2023-04-04 15:41:12 +02:00
|
|
|
using System.ComponentModel.DataAnnotations;
|
2023-08-28 12:14:16 +02:00
|
|
|
using System.Globalization;
|
2021-02-17 11:50:19 +01:00
|
|
|
using System.Security.Claims;
|
2020-05-21 16:33:24 +10:00
|
|
|
using System.Security.Principal;
|
2020-12-01 17:24:23 +11:00
|
|
|
using Microsoft.AspNetCore.Http;
|
2020-05-14 22:21:19 +01:00
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Microsoft.Extensions.Options;
|
2023-03-29 08:14:47 +02:00
|
|
|
using Umbraco.Cms.Core;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
2021-02-26 16:37:34 +01:00
|
|
|
using Umbraco.Cms.Core.Events;
|
2023-03-29 08:14:47 +02:00
|
|
|
using Umbraco.Cms.Core.Models;
|
|
|
|
|
using Umbraco.Cms.Core.Models.Membership;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Net;
|
2021-05-11 14:33:49 +02:00
|
|
|
using Umbraco.Cms.Core.Notifications;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core.Security;
|
2023-03-29 08:14:47 +02:00
|
|
|
using Umbraco.Cms.Core.Services.OperationStatus;
|
2021-02-26 16:37:34 +01:00
|
|
|
using Umbraco.Cms.Infrastructure.Security;
|
2020-05-21 16:33:24 +10:00
|
|
|
using Umbraco.Extensions;
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
namespace Umbraco.Cms.Web.Common.Security;
|
|
|
|
|
|
|
|
|
|
public class BackOfficeUserManager : UmbracoUserManager<BackOfficeIdentityUser, UserPasswordConfigurationSettings>,
|
2023-03-29 08:14:47 +02:00
|
|
|
IBackOfficeUserManager,
|
2023-04-04 15:41:12 +02:00
|
|
|
ICoreBackOfficeUserManager
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
private readonly IBackOfficeUserPasswordChecker _backOfficeUserPasswordChecker;
|
2023-03-29 08:14:47 +02:00
|
|
|
private readonly GlobalSettings _globalSettings;
|
2022-05-09 09:39:46 +02:00
|
|
|
private readonly IEventAggregator _eventAggregator;
|
|
|
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
|
|
|
|
|
|
|
|
public BackOfficeUserManager(
|
|
|
|
|
IIpResolver ipResolver,
|
|
|
|
|
IUserStore<BackOfficeIdentityUser> store,
|
|
|
|
|
IOptions<BackOfficeIdentityOptions> optionsAccessor,
|
|
|
|
|
IPasswordHasher<BackOfficeIdentityUser> passwordHasher,
|
|
|
|
|
IEnumerable<IUserValidator<BackOfficeIdentityUser>> userValidators,
|
|
|
|
|
IEnumerable<IPasswordValidator<BackOfficeIdentityUser>> passwordValidators,
|
|
|
|
|
BackOfficeErrorDescriber errors,
|
|
|
|
|
IServiceProvider services,
|
|
|
|
|
IHttpContextAccessor httpContextAccessor,
|
|
|
|
|
ILogger<UserManager<BackOfficeIdentityUser>> logger,
|
|
|
|
|
IOptions<UserPasswordConfigurationSettings> passwordConfiguration,
|
|
|
|
|
IEventAggregator eventAggregator,
|
2023-03-29 08:14:47 +02:00
|
|
|
IBackOfficeUserPasswordChecker backOfficeUserPasswordChecker,
|
|
|
|
|
IOptions<GlobalSettings> globalSettings)
|
2022-05-09 09:39:46 +02:00
|
|
|
: base(
|
|
|
|
|
ipResolver,
|
|
|
|
|
store,
|
|
|
|
|
optionsAccessor,
|
|
|
|
|
passwordHasher,
|
|
|
|
|
userValidators,
|
|
|
|
|
passwordValidators,
|
|
|
|
|
errors,
|
|
|
|
|
services,
|
|
|
|
|
logger,
|
|
|
|
|
passwordConfiguration)
|
|
|
|
|
{
|
|
|
|
|
_httpContextAccessor = httpContextAccessor;
|
|
|
|
|
_eventAggregator = eventAggregator;
|
|
|
|
|
_backOfficeUserPasswordChecker = backOfficeUserPasswordChecker;
|
2023-03-29 08:14:47 +02:00
|
|
|
_globalSettings = globalSettings.Value;
|
2022-05-09 09:39:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Override to check the user approval value as well as the user lock out date, by default this only checks the user's
|
|
|
|
|
/// locked out date
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="user">The user</param>
|
|
|
|
|
/// <returns>True if the user is locked out, else false</returns>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// In the ASP.NET Identity world, there is only one value for being locked out, in Umbraco we have 2 so when checking
|
|
|
|
|
/// this for Umbraco we need to check both values
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public override async Task<bool> IsLockedOutAsync(BackOfficeIdentityUser user)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
if (user == null)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
throw new ArgumentNullException(nameof(user));
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
if (user.IsApproved == false)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
return true;
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
2020-06-22 10:08:08 +02:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return await base.IsLockedOutAsync(user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override async Task<IdentityResult> AccessFailedAsync(BackOfficeIdentityUser user)
|
|
|
|
|
{
|
|
|
|
|
IdentityResult result = await base.AccessFailedAsync(user);
|
2022-04-19 08:33:03 +02:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
// Slightly confusing: this will return a Success if we successfully update the AccessFailed count
|
|
|
|
|
if (result.Succeeded)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
NotifyLoginFailed(_httpContextAccessor.HttpContext?.User, user.Id);
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2020-12-03 20:30:35 +11:00
|
|
|
|
2022-09-19 16:37:24 +02:00
|
|
|
public override async Task<IdentityResult> ChangePasswordWithResetAsync(string userId, string token, string newPassword)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
|
|
|
|
IdentityResult result = await base.ChangePasswordWithResetAsync(userId, token, newPassword);
|
|
|
|
|
if (result.Succeeded)
|
|
|
|
|
{
|
|
|
|
|
NotifyPasswordReset(_httpContextAccessor.HttpContext?.User, userId);
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
2020-06-22 10:08:08 +02:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-19 16:37:24 +02:00
|
|
|
public override async Task<IdentityResult> ChangePasswordAsync(BackOfficeIdentityUser user, string currentPassword, string newPassword)
|
2022-05-09 09:39:46 +02:00
|
|
|
{
|
|
|
|
|
IdentityResult result = await base.ChangePasswordAsync(user, currentPassword, newPassword);
|
|
|
|
|
if (result.Succeeded)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
NotifyPasswordChanged(_httpContextAccessor.HttpContext?.User, user.Id);
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public override async Task<IdentityResult> SetLockoutEndDateAsync(
|
|
|
|
|
BackOfficeIdentityUser user,
|
|
|
|
|
DateTimeOffset? lockoutEnd)
|
|
|
|
|
{
|
|
|
|
|
if (user == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(user));
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
|
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
IdentityResult result = await base.SetLockoutEndDateAsync(user, lockoutEnd);
|
2020-12-03 20:30:35 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
// The way we unlock is by setting the lockoutEnd date to the current datetime
|
|
|
|
|
if (result.Succeeded && lockoutEnd > DateTimeOffset.UtcNow)
|
|
|
|
|
{
|
|
|
|
|
NotifyAccountLocked(_httpContextAccessor.HttpContext?.User, user.Id);
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
2022-05-09 09:39:46 +02:00
|
|
|
else
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
NotifyAccountUnlocked(_httpContextAccessor.HttpContext?.User, user.Id);
|
2020-12-03 20:30:35 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
// Resets the login attempt fails back to 0 when unlock is clicked
|
|
|
|
|
await ResetAccessFailedCountAsync(user);
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
Member password roll forward (#10138)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 15:45:35 +10:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2023-03-29 08:14:47 +02:00
|
|
|
public async Task<Attempt<UserUnlockResult, UserOperationStatus>> UnlockUser(IUser user)
|
|
|
|
|
{
|
|
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString());
|
|
|
|
|
|
|
|
|
|
if (identityUser is null)
|
|
|
|
|
{
|
2023-04-04 15:41:12 +02:00
|
|
|
return Attempt.FailWithStatus(UserOperationStatus.UserNotFound, new UserUnlockResult());
|
2023-03-29 08:14:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IdentityResult result = await SetLockoutEndDateAsync(identityUser, DateTimeOffset.Now.AddMinutes(-1));
|
|
|
|
|
|
|
|
|
|
return result.Succeeded
|
|
|
|
|
? Attempt.SucceedWithStatus(UserOperationStatus.Success, new UserUnlockResult())
|
2023-04-04 15:41:12 +02:00
|
|
|
: Attempt.FailWithStatus(UserOperationStatus.UnknownFailure, new UserUnlockResult { Error = new ValidationResult(result.Errors.ToErrorMessage()) });
|
2023-03-29 08:14:47 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public override async Task<IdentityResult> ResetAccessFailedCountAsync(BackOfficeIdentityUser user)
|
|
|
|
|
{
|
|
|
|
|
IdentityResult result = await base.ResetAccessFailedCountAsync(user);
|
2020-12-03 20:30:35 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
// notify now that it's reset
|
|
|
|
|
NotifyResetAccessFailedCount(_httpContextAccessor.HttpContext?.User, user.Id);
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void NotifyForgotPasswordRequested(IPrincipal currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserForgotPasswordRequestedNotification(ip, userId, currentUserId));
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyForgotPasswordChanged(IPrincipal currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserForgotPasswordChangedNotification(ip, userId, currentUserId));
|
|
|
|
|
|
|
|
|
|
public SignOutSuccessResult NotifyLogoutSuccess(IPrincipal currentUser, string? userId)
|
|
|
|
|
{
|
|
|
|
|
UserLogoutSuccessNotification notification = Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserLogoutSuccessNotification(ip, userId, currentUserId));
|
|
|
|
|
|
|
|
|
|
return new SignOutSuccessResult { SignOutRedirectUrl = notification.SignOutRedirectUrl };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void NotifyAccountLocked(IPrincipal? currentUser, string? userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserLockedNotification(ip, userId, currentUserId));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Override to allow checking the password via the <see cref="IBackOfficeUserPasswordChecker" /> if one is configured
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="store"></param>
|
|
|
|
|
/// <param name="user"></param>
|
|
|
|
|
/// <param name="password"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected override async Task<PasswordVerificationResult> VerifyPasswordAsync(
|
|
|
|
|
IUserPasswordStore<BackOfficeIdentityUser> store,
|
|
|
|
|
BackOfficeIdentityUser user,
|
|
|
|
|
string password)
|
|
|
|
|
{
|
|
|
|
|
if (user.HasIdentity == false)
|
2020-05-14 22:21:19 +01:00
|
|
|
{
|
2022-05-09 09:39:46 +02:00
|
|
|
return PasswordVerificationResult.Failed;
|
2020-10-23 14:18:53 +11:00
|
|
|
}
|
2020-12-03 20:30:35 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
BackOfficeUserPasswordCheckerResult result =
|
|
|
|
|
await _backOfficeUserPasswordChecker.CheckPasswordAsync(user, password);
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
// if the result indicates to not fallback to the default, then return true if the credentials are valid
|
|
|
|
|
if (result != BackOfficeUserPasswordCheckerResult.FallbackToDefaultChecker)
|
|
|
|
|
{
|
|
|
|
|
return result == BackOfficeUserPasswordCheckerResult.ValidCredentials
|
|
|
|
|
? PasswordVerificationResult.Success
|
|
|
|
|
: PasswordVerificationResult.Failed;
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
return await base.VerifyPasswordAsync(store, user, password);
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
private string GetCurrentUserId(IPrincipal? currentUser)
|
|
|
|
|
{
|
|
|
|
|
ClaimsIdentity? umbIdentity = currentUser?.GetUmbracoIdentity();
|
|
|
|
|
var currentUserId = umbIdentity?.GetUserId<string>() ?? Core.Constants.Security.SuperUserIdAsString;
|
|
|
|
|
return currentUserId;
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyAccountUnlocked(IPrincipal? currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserUnlockedNotification(ip, userId, currentUserId));
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyLoginFailed(IPrincipal? currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserLoginFailedNotification(ip, userId, currentUserId));
|
2020-10-23 14:18:53 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyLoginRequiresVerification(IPrincipal currentUser, string? userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserLoginRequiresVerificationNotification(ip, userId, currentUserId));
|
2020-10-23 14:18:53 +11:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyLoginSuccess(IPrincipal currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserLoginSuccessNotification(ip, userId, currentUserId));
|
2021-02-26 16:37:34 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyPasswordChanged(IPrincipal? currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserPasswordChangedNotification(ip, userId, currentUserId));
|
2020-05-21 16:33:24 +10:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyPasswordReset(IPrincipal? currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserPasswordResetNotification(ip, userId, currentUserId));
|
2021-03-01 20:31:04 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
public void NotifyResetAccessFailedCount(IPrincipal? currentUser, string userId) => Notify(
|
|
|
|
|
currentUser,
|
|
|
|
|
(currentUserId, ip) => new UserResetAccessFailedCountNotification(ip, userId, currentUserId));
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
private T Notify<T>(IPrincipal? currentUser, Func<string, string, T> createNotification)
|
|
|
|
|
where T : INotification
|
|
|
|
|
{
|
|
|
|
|
var currentUserId = GetCurrentUserId(currentUser);
|
|
|
|
|
var ip = IpResolver.GetCurrentRequestIpAddress();
|
2020-05-14 22:21:19 +01:00
|
|
|
|
2022-05-09 09:39:46 +02:00
|
|
|
T notification = createNotification(currentUserId, ip);
|
|
|
|
|
_eventAggregator.Publish(notification);
|
|
|
|
|
return notification;
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|
2023-03-29 08:14:47 +02:00
|
|
|
|
|
|
|
|
public async Task<IdentityCreationResult> CreateForInvite(UserCreateModel createModel)
|
|
|
|
|
{
|
|
|
|
|
var identityUser = BackOfficeIdentityUser.CreateNew(
|
|
|
|
|
_globalSettings,
|
|
|
|
|
createModel.UserName,
|
|
|
|
|
createModel.Email,
|
|
|
|
|
_globalSettings.DefaultUILanguage);
|
|
|
|
|
|
|
|
|
|
identityUser.Name = createModel.Name;
|
|
|
|
|
|
|
|
|
|
IdentityResult created = await CreateAsync(identityUser);
|
|
|
|
|
|
|
|
|
|
return created.Succeeded
|
|
|
|
|
? new IdentityCreationResult { Succeded = true }
|
|
|
|
|
: IdentityCreationResult.Fail(created.Errors.ToErrorMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IdentityCreationResult> CreateAsync(UserCreateModel createModel)
|
|
|
|
|
{
|
|
|
|
|
var identityUser = BackOfficeIdentityUser.CreateNew(
|
|
|
|
|
_globalSettings,
|
|
|
|
|
createModel.UserName,
|
|
|
|
|
createModel.Email,
|
2024-02-29 10:40:48 +01:00
|
|
|
_globalSettings.DefaultUILanguage,
|
|
|
|
|
createModel.Name,
|
|
|
|
|
createModel.Id);
|
2023-03-29 08:14:47 +02:00
|
|
|
|
|
|
|
|
IdentityResult created = await CreateAsync(identityUser);
|
|
|
|
|
|
|
|
|
|
if (created.Succeeded is false)
|
|
|
|
|
{
|
|
|
|
|
return IdentityCreationResult.Fail(created.Errors.ToErrorMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var password = GeneratePassword();
|
|
|
|
|
|
|
|
|
|
IdentityResult passwordAdded = await AddPasswordAsync(identityUser, password);
|
|
|
|
|
if (passwordAdded.Succeeded is false)
|
|
|
|
|
{
|
|
|
|
|
return IdentityCreationResult.Fail(passwordAdded.Errors.ToErrorMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new IdentityCreationResult { Succeded = true, InitialPassword = password };
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-28 12:14:16 +02:00
|
|
|
public async Task<Attempt<string, UserOperationStatus>> GeneratePasswordResetTokenAsync(IUser user)
|
|
|
|
|
{
|
|
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString());
|
|
|
|
|
|
|
|
|
|
if (identityUser is null)
|
|
|
|
|
{
|
|
|
|
|
return Attempt.FailWithStatus(UserOperationStatus.UserNotFound, string.Empty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var token = await GeneratePasswordResetTokenAsync(identityUser);
|
|
|
|
|
|
|
|
|
|
return Attempt.SucceedWithStatus(UserOperationStatus.Success, token);
|
|
|
|
|
}
|
2023-03-29 08:14:47 +02:00
|
|
|
public async Task<Attempt<string, UserOperationStatus>> GenerateEmailConfirmationTokenAsync(IUser user)
|
|
|
|
|
{
|
|
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString());
|
|
|
|
|
|
|
|
|
|
if (identityUser is null)
|
|
|
|
|
{
|
2023-04-04 15:41:12 +02:00
|
|
|
return Attempt.FailWithStatus(UserOperationStatus.UserNotFound, string.Empty);
|
2023-03-29 08:14:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var token = await GenerateEmailConfirmationTokenAsync(identityUser);
|
|
|
|
|
|
|
|
|
|
return Attempt.SucceedWithStatus(UserOperationStatus.Success, token);
|
|
|
|
|
}
|
2023-06-05 08:42:29 +02:00
|
|
|
|
|
|
|
|
public async Task<Attempt<ICollection<IIdentityUserLogin>, UserOperationStatus>> GetLoginsAsync(IUser user)
|
|
|
|
|
{
|
|
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString());
|
|
|
|
|
if (identityUser is null)
|
|
|
|
|
{
|
|
|
|
|
return Attempt.FailWithStatus<ICollection<IIdentityUserLogin>, UserOperationStatus>(UserOperationStatus.UserNotFound, Array.Empty<IIdentityUserLogin>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Attempt.SucceedWithStatus(UserOperationStatus.Success, identityUser.Logins);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 12:42:52 +02:00
|
|
|
public async Task<bool> IsEmailConfirmationTokenValidAsync(IUser user, string token)
|
|
|
|
|
{
|
2023-08-28 12:14:16 +02:00
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString(CultureInfo.InvariantCulture));
|
2023-07-05 12:42:52 +02:00
|
|
|
|
|
|
|
|
if (identityUser != null && await VerifyUserTokenAsync(identityUser, Options.Tokens.EmailConfirmationTokenProvider, ConfirmEmailTokenPurpose, token).ConfigureAwait(false))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-08-28 12:14:16 +02:00
|
|
|
|
|
|
|
|
public async Task<bool> IsResetPasswordTokenValidAsync(IUser user, string token)
|
|
|
|
|
{
|
|
|
|
|
BackOfficeIdentityUser? identityUser = await FindByIdAsync(user.Id.ToString(CultureInfo.InvariantCulture));
|
|
|
|
|
|
|
|
|
|
if (identityUser != null && await VerifyUserTokenAsync(identityUser, Options.Tokens.PasswordResetTokenProvider, ResetPasswordTokenPurpose, token).ConfigureAwait(false))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-05-14 22:21:19 +01:00
|
|
|
}
|