Backend/e-suite.Modules.UserManager/e-suite.Modules.UserManager/UserManager.cs

821 lines
32 KiB
C#

using e_suite.API.Common.exceptions;
using e_suite.API.Common.extensions;
using e_suite.API.Common.models;
using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Database.Core.Models;
using e_suite.Database.Core.Tables.UserManager;
using e_suite.Modules.UserManager.Facade;
using e_suite.Modules.UserManager.Services;
using e_suite.Nuget.PasswordHasher;
using e_suite.Utilities.Pagination;
using eSuite.Core.Clock;
using eSuite.Core.MailService;
using eSuite.Core.Miscellaneous;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Text;
using System.Text.Json;
using e_suite.Modules.UserManager.Extensions;
using IUserManager = e_suite.API.Common.IUserManager;
using System.Data;
namespace e_suite.Modules.UserManager;
public partial class UserManager : IUserManager
{
private readonly IConfiguration _configuration;
private readonly IPasswordHasher<IPassword> _passwordHasher;
private readonly ITwoFactorAuthenticator _twoFactorAuthenticator;
private readonly IJwtService _jwtService;
private readonly IMailService _mailService;
private readonly IRandomNumberGenerator _randomNumberGenerator;
private readonly IClock _clock;
private readonly IUserManagerRepository _userManagerRepository;
private readonly IDomainRepository _domainRepository;
public UserManager(IConfiguration configuration, IPasswordHasher<IPassword> passwordHasher, ITwoFactorAuthenticator twoFactorAuthenticator, IJwtService jwtService, IMailService mailService, IRandomNumberGenerator randomNumberGenerator, IClock clock, IUserManagerRepository userManagerRepository, IDomainRepository domainRepository)
{
_configuration = configuration;
_passwordHasher = passwordHasher;
_twoFactorAuthenticator = twoFactorAuthenticator;
_jwtService = jwtService;
_mailService = mailService;
_randomNumberGenerator = randomNumberGenerator;
_clock = clock;
_userManagerRepository = userManagerRepository;
_domainRepository = domainRepository;
}
private void HashPassword(User user, string password)
{
user.Password = _passwordHasher.HashPassword(user, password);
}
private string GetEmailUserActionUrl(User existingUser, EmailUserAction emailConfirmation)
{
var emailToConfirmationToken = new EmailActionToken
{
Email = existingUser.Email,
EmailActionType = emailConfirmation.EmailActionType,
Token = emailConfirmation.Token
};
var jsonString = JsonSerializer.Serialize(emailToConfirmationToken);
var encodedBase64String = Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonString));
var baseUrl = _configuration.GetConfigValue("BASE_URL", "baseUrl", "http://localhost:3000");
var confirmEmail = $"{baseUrl?.Trim().TrimEnd('/')}/emailuseraction/{encodedBase64String}";
return confirmEmail;
}
private async Task SendEmailUserAction(AuditUserDetails auditUserDetails, User user, EmailUserActionType type,
CancellationToken cancellationToken)
{
if (user == null)
throw new Exception("User not found");
if (!user.Active)
throw new NotFoundException("Active User not found");
var emailUserAction = await CreateEmailUserAction(auditUserDetails, user, type, cancellationToken);
var emailUserActionUrl = GetEmailUserActionUrl(user, emailUserAction);
await SentEmailRequest(user, emailUserActionUrl, type, cancellationToken);
}
private async Task<EmailUserAction> CreateEmailUserAction(AuditUserDetails auditUserDetails, User existingUser,
EmailUserActionType type, CancellationToken cancellationToken)
{
var emailTimeout = GetEmailTimeoutHours();
var emailConfirmation = new EmailUserAction
{
Created = _clock.GetNow,
Expires = _clock.GetNow.AddHours(emailTimeout),
EmailActionType = type,
Token = Guid.NewGuid(),
UserId = existingUser.Id
};
await _userManagerRepository.AddEmailUserAction(auditUserDetails, emailConfirmation, cancellationToken);
return emailConfirmation;
}
private int GetEmailTimeoutHours()
{
return _configuration.GetValue("Smtp:EmailTimeoutHours", 48);
}
private async Task SentEmailRequest(User user, string emailUserActionUrl, EmailUserActionType type, CancellationToken cancellationToken)
{
var emailRequest = new MailRequest();
emailRequest.To.Add(user);
emailRequest.EmailType = GetMailType(type);
emailRequest.Parameters.Add("url", emailUserActionUrl);
await _mailService.RequestEMailAsync(emailRequest, cancellationToken);
}
private async Task SendEmailRequest(User user, MailType mailType, CancellationToken cancellationToken)
{
var emailRequest = new MailRequest();
emailRequest.To.Add(user);
emailRequest.EmailType = mailType;
await _mailService.RequestEMailAsync(emailRequest, cancellationToken);
}
private static MailType GetMailType(EmailUserActionType type)
{
return type switch
{
EmailUserActionType.ConfirmEmailAddress => MailType.ConfirmEmailAddress,
EmailUserActionType.DisableAuthenticator => MailType.DisableAuthenticator,
EmailUserActionType.PasswordReset => MailType.PasswordReset,
_ => throw new ArgumentException(message: $"{type} cannot be translated to and EmailType.", paramName: nameof(type))
};
}
public async Task<LoginResponse> Login(Login userLogin, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserByEmail(userLogin.Email, cancellationToken);
if (user == null)
{
return new LoginResponse
{
Result = LoginResult.Failed
};
}
var result = _passwordHasher.VerifyHashedPassword(user, user.Password, userLogin.Password);
if (result == PasswordVerificationResult.Failed)
{
return new LoginResponse
{
Result = LoginResult.Failed
};
}
var auditUserDetails = new AuditUserDetails
{
UserId = user.Id,
UserDisplayName = user.DisplayName
};
if (result == PasswordVerificationResult.SuccessRehashNeeded)
{
HashPassword(user, userLogin.Password);
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
}
if (userLogin.RequestTfaRemoval)
{
await SendEmailUserAction(auditUserDetails, user, EmailUserActionType.DisableAuthenticator, cancellationToken);
return new LoginResponse
{
Result = LoginResult.TwoFactorAuthenticationRemovalRequested
};
}
if (user.UsingTwoFactorAuthentication)
{
if (string.IsNullOrWhiteSpace(userLogin.SecurityCode))
{
return new LoginResponse
{
Result = LoginResult.TwoFactorAuthenticationCodeRequired
};
}
var tfaResult =
_twoFactorAuthenticator.ValidateTwoFactorPIN(user.TwoFactorAuthenticationKey, userLogin.SecurityCode);
if (!tfaResult)
{
return new LoginResponse
{
Result = LoginResult.TwoFactorAuthenticationCodeIncorrect
};
}
}
return CreateLoginResponse(user);
}
public async Task<LoginResponse> LoginSso(long ssoId, string ssoUserId, CancellationToken cancellationToken = default!)
{
var user = await _userManagerRepository.GetUserSsoId(ssoId, ssoUserId, cancellationToken) ?? await _userManagerRepository.GetUserByDomainSsoId(ssoId, ssoUserId, cancellationToken);
return CreateLoginResponse(user);
}
private LoginResponse CreateLoginResponse(User? user)
{
if (user == null)
{
return new LoginResponse
{
Result = LoginResult.Failed
};
}
if (!user.Active)
{
return new LoginResponse
{
Result = LoginResult.Failed
};
}
if (!user.EmailConfirmed)
{
return new LoginResponse
{
Result = LoginResult.EmailNotConfirmed
};
}
var token = _jwtService.GenerateSecurityToken(user);
return new LoginResponse
{
Result = LoginResult.Success,
Token = token
};
}
public async Task CreateUser(AuditUserDetails auditUserDetails, UserRegistration userRegistration, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync(async () =>
{
userRegistration.Email = userRegistration.Email.Trim();
ValidateEmail(userRegistration.Email);
await CheckForExistingUserByEmail(userRegistration.Email, cancellationToken);
var currentUser = await _userManagerRepository.GetUserById(new GeneralIdRef
{
Id = auditUserDetails.UserId
}, cancellationToken);
var domainId = currentUser!.DomainId;
if (userRegistration.DomainId != null )
{
var domain = await _userManagerRepository.GetDomainById(userRegistration.DomainId, cancellationToken)
?? throw new NotFoundException("unable to find domain");
domainId = domain.Id;
}
var user = new User
{
FirstName = userRegistration.FirstName.Trim(),
MiddleNames = userRegistration.MiddleNames.Trim(),
LastName = userRegistration.LastName.Trim(),
Email = userRegistration.Email,
EmailConfirmed = false,
DomainId = domainId
};
DisableTwoFactorAuthenticationForUser(user);
var password = _randomNumberGenerator.GetRandomString(10);
HashPassword(user, password);
await _userManagerRepository.AddUser(auditUserDetails, user, cancellationToken);
await SendEmailUserAction(auditUserDetails, user, EmailUserActionType.ConfirmEmailAddress, cancellationToken);
});
}
private async Task CheckForExistingUserByEmail(string emailAddress, CancellationToken cancellationToken)
{
var existingUser = await _userManagerRepository.GetUserByEmail(emailAddress, cancellationToken);
if (existingUser != null)
throw new ExistsException("User Already Exists");
}
private static void ValidateEmail(string email)
{
if (!(new EmailAddressAttribute()).IsValid(email))
throw new ArgumentException($"{email} is not a valid email address");
}
private void DisableTwoFactorAuthenticationForUser(User user)
{
user.UsingTwoFactorAuthentication = false;
user.TwoFactorAuthenticationKey = GenerateTwoFactorAuthenticationKey();
}
private string GenerateTwoFactorAuthenticationKey()
{
return _randomNumberGenerator.GetRandomString(_configuration.GetValue<int>("twoFactorAuthentication:keySize"));
}
public async Task<LoginResponse> RefreshToken(string email, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserByEmail(email, cancellationToken);
return RefreshToken(user);
}
public async Task<LoginResponse> RefreshToken(IGeneralIdRef id, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserById(id, cancellationToken);
return RefreshToken(user);
}
private LoginResponse RefreshToken(User? user)
{
return CreateLoginResponse(user);
}
public async Task ForgotPassword(string email, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync( async () =>
{
var user = await GetUserByEmailAsync(email, cancellationToken);
if (!user.EmailConfirmed) {
throw new EmailNotConfirmedException("Email not confirmed");
}
var auditUserDetails = new AuditUserDetails
{
UserId = user.Id,
UserDisplayName = user.DisplayName
};
await SendEmailUserAction(auditUserDetails, user, EmailUserActionType.PasswordReset, cancellationToken);
});
}
public async Task<User> CompleteEmailAction(EmailActionToken token, CancellationToken cancellationToken)
{
return await _userManagerRepository.TransactionAsync(async () =>
{
var emailUserAction = await _userManagerRepository.GetEmailUserAction(token.Token, cancellationToken)
?? throw new TokenInvalidException("Your email token is invalid");
if ((emailUserAction.EmailActionType == EmailUserActionType.ConfirmEmailAddress ||
emailUserAction.EmailActionType == EmailUserActionType.PasswordReset))
CheckPasswordStrength(token.Password);
var user = emailUserAction.User;
if (!user.Email.Equals(token.Email, StringComparison.CurrentCultureIgnoreCase))
throw new InvalidEmailException("Your email is invalid");
switch (emailUserAction.EmailActionType)
{
case EmailUserActionType.ConfirmEmailAddress:
throw new ArgumentException($"EmailActionType {emailUserAction.EmailActionType} not supported, use the UI instead.");
case EmailUserActionType.DisableAuthenticator:
DisableTwoFactorAuthenticationForUser(user);
break;
case EmailUserActionType.PasswordReset:
AddPasswordHash(user,token.Password);
break;
default:
throw new ArgumentException($"EmailActionType {emailUserAction.EmailActionType} not implemented");
}
var auditUserDetails = new AuditUserDetails
{
UserId = user.Id,
UserDisplayName = user.DisplayName
};
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
await _userManagerRepository.DeleteEmailUserAction(auditUserDetails, emailUserAction, cancellationToken);
if (emailUserAction.EmailActionType == EmailUserActionType.PasswordReset) {
await SendEmailRequest(user, MailType.PasswordResetCompleted, cancellationToken);
}
return user;
});
}
public async Task DeactivateUser(AuditUserDetails auditUserDetails, string email, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync( async () =>
{
var user = await GetUserByEmailAsync(email, cancellationToken);
await DeactivateUser(auditUserDetails, user, cancellationToken);
});
}
public async Task DeactivateUser(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync(async () =>
{
var user = await _userManagerRepository.GetUserById(generalIdRef, cancellationToken)
?? throw new NotFoundException("Unable to find user");
await DeactivateUser(auditUserDetails, user, cancellationToken);
});
}
private async Task DeactivateUser(AuditUserDetails auditUserDetails, User user, CancellationToken cancellationToken)
{
if (!user.Active)
throw new InvalidOperationException("User already deactivated");
if (user.Id == auditUserDetails.UserId)
throw new InvalidOperationException("You can't delete yourself");
user.Active = false;
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
}
public async Task<UserProfile> GetProfile(string email, CancellationToken cancellationToken)
{
var applicationName = _configuration.GetValue<string>("applicationName");
if (string.IsNullOrWhiteSpace(applicationName))
{
throw new SystemException("applicationName has not been set");
}
var user = await GetUserByEmailAsync(email, cancellationToken);
var setupInfo = _twoFactorAuthenticator.GenerateSetupCode(applicationName, user.Email, user.TwoFactorAuthenticationKey, false);
var twoFactorAuthenticationSettings = new TwoFactorAuthenticationSettings
{
QrCodeImageUrl = setupInfo.QrCodeSetupImageUrl,
ManualEntrySetupCode = setupInfo.ManualEntryKey
};
var ssoProviders = GetSsoProvidersForUser();
var userProfile = new UserProfile
{
FirstName = user.FirstName,
MiddleNames = user.MiddleNames,
LastName = user.LastName,
Email = user.Email,
UsingTwoFactorAuthentication = user.UsingTwoFactorAuthentication,
TwoFactorAuthenticationSettings = twoFactorAuthenticationSettings,
Created = user.Created,
DomainSsoProviderId = user.Domain.SsoProviderId,
SsoProviderId = user.SsoProviderId,
SsoSubject = user.SsoSubject,
SsoProviders = ssoProviders
.ToDictionary(x => x.Id, x => x.Name)
};
return userProfile;
}
private IEnumerable<SsoProvider> GetSsoProvidersForUser()
{
var ssoProviders = _userManagerRepository.GetSsoProviders().Where(x => x.Deleted == false && x.IsPublic == true);
return ssoProviders;
}
public async Task UpdateProfile(AuditUserDetails auditUserDetails, string email, UpdatedUserProfile userProfile, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync(async () =>
{
var user = await GetUserByEmailAsync(email, cancellationToken);
userProfile.Email = userProfile.Email.Trim();
if (!string.IsNullOrWhiteSpace(userProfile.Email))
{
ValidateEmail(userProfile.Email);
if (user.Email != userProfile.Email)
{
await CheckForExistingUserByEmail(userProfile.Email, cancellationToken);
user.EmailConfirmed = false;
user.Email = userProfile.Email;
}
}
user.FirstName = userProfile.FirstName;
user.MiddleNames = userProfile.MiddleNames;
user.LastName = userProfile.LastName;
user.Email = userProfile.Email;
await SetInternalAuthenticationDetails(user, userProfile, true, cancellationToken);
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
if (!user.EmailConfirmed)
{
await SendEmailUserAction(auditUserDetails, user, EmailUserActionType.ConfirmEmailAddress, cancellationToken);
}
});
}
private async Task SetInternalAuthenticationDetails(
User user,
UpdatedUserProfile userProfile,
bool sendEmail,
CancellationToken cancellationToken
)
{
if (!string.IsNullOrWhiteSpace(userProfile.Password))
{
HashPassword(user, userProfile.Password);
if (sendEmail)
await SendEmailRequest(user, MailType.PasswordResetCompleted, cancellationToken);
}
if (userProfile.UsingTwoFactorAuthentication != user.UsingTwoFactorAuthentication)
{
if (userProfile.UsingTwoFactorAuthentication)
{
if (!string.IsNullOrWhiteSpace(userProfile.SecurityCode))
{
if (_twoFactorAuthenticator.ValidateTwoFactorPIN(user.TwoFactorAuthenticationKey,
userProfile.SecurityCode))
user.UsingTwoFactorAuthentication = true;
}
}
else
DisableTwoFactorAuthenticationForUser(user);
}
}
public async Task EditUser(AuditUserDetails auditUserDetails, EditUser user, CancellationToken cancellationToken)
{
await _userManagerRepository.TransactionAsync(async () =>
{
var editUser = await _userManagerRepository.GetUserById(user.Id! , cancellationToken)
?? throw new NotFoundException("unable to find user");
if (!editUser.Active)
throw new DeletedRowInaccessibleException("This user is inactive so cannot be modified. You will need to create the user again.");
var userDomain = await _userManagerRepository.GetDomainById(user.Domain, cancellationToken)
?? throw new NotFoundException("unable to find domain");
editUser.FirstName = user.FirstName;
editUser.MiddleNames = user.MiddleNames;
editUser.LastName = user.LastName;
editUser.Email = user.Email;
editUser.DomainId = userDomain.Id;
editUser.Domain = userDomain;
await _userManagerRepository.EditUser(auditUserDetails, editUser, cancellationToken);
});
}
public async Task<IPaginatedData<GetUser>> GetUsersAsync(Paging paging, CancellationToken cancellationToken)
{
var users = _userManagerRepository.GetUsers().Where(x => x.Active == true);
var paginatedData = await PaginatedData.Paginate(users, paging, KeySelector, FilterSelector, cancellationToken);
var paginatedResult = new PaginatedData<GetUser>
{
Count = paginatedData.Count,
Page = paginatedData.Page,
PageSize = paginatedData.PageSize,
Data = paginatedData.Data.Select(MapUser)
};
return paginatedResult;
}
public async Task<GetUser?> GetUserAsync(GeneralIdRef generalIdRef, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserById(generalIdRef, cancellationToken)
?? throw new NotFoundException("User not found");
return MapUser(user);
}
public async Task<User> GetUserByEmailAsync(string email, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserByEmail(email, cancellationToken)
?? throw new NotFoundException("User not found");
return user;
}
private GetUser MapUser(User user)
{
var getUser = new GetUser
{
Id = user.Id,
Guid = user.Guid,
FirstName = user.FirstName,
LastName = user.LastName,
MiddleNames = user.MiddleNames,
DisplayName = user.DisplayName,
Email = user.Email,
Created = user.Created,
LastUpdated = user.LastUpdated,
Domain = new GeneralIdRef
{
Id = user.Domain.Id,
Guid = user.Domain.Guid
},
DomainName = user.Domain.Name,
EmailConfirmed = user.EmailConfirmed
};
return getUser;
}
private Expression<Func<User, bool>> FilterSelector(string? key, string value)
{
return key?.ToLowerInvariant() switch
{
"firstname" => x => x.FirstName.Contains(value),
"lastname" => x => x.LastName.Contains(value),
"middlenames" => x => x.MiddleNames.Contains(value),
"displayname" => x => ( x.FirstName + " " + x.MiddleNames + " " + x.LastName ).Contains(value),
"domainname" => x => x.Domain.Name.Contains(value),
"created" => x => x.Created.ToString().Contains(value),
"lastupdated" => x => x.LastUpdated.ToString().Contains(value),
"email" => x => x.Email.Contains(value),
_ => x => x.Email.Contains(value)
};
}
private Expression<Func<User, object>> KeySelector(string? sortKey)
{
return sortKey?.ToLowerInvariant() switch
{
"firstname" => x => x.FirstName,
"lastname" => x => x.LastName,
"middlenames" => x => x.MiddleNames,
"displayname" => x => x.FirstName + " " + x.MiddleNames + " " + x.LastName,
"domainname" => x => x.Domain.Name,
"created" => x => x.Created,
"lastupdated" => x => x.LastUpdated,
"email" => x => x.Email,
_ => x => x.Email
};
}
private void AddPasswordHash(User user,string tokenPass)
{
if (string.IsNullOrWhiteSpace(tokenPass))
throw new ArgumentException("Invalid password supplied");
HashPassword(user, tokenPass);
}
private static void CheckPasswordStrength(string password)
{
const int minPasswordLength = 12;
const string symbols = "~`! @#$%^&*()_+={[}|\\:;\"'<,->.?/";
if (password.Length < minPasswordLength)
throw new WeakPasswordException($"Password must be at least {minPasswordLength} characters");
if (!password.Contains(symbols.ToCharArray()))
throw new WeakPasswordException($"Password must contain at least one symbol: {symbols}");
if (!password.Any(char.IsDigit))
throw new WeakPasswordException($"Password must contain at least one number");
if (!password.Any(char.IsUpper))
throw new WeakPasswordException($"Password must contain at least one upper case character");
if (!password.Any(char.IsLower))
throw new WeakPasswordException($"Password must contain at least one lower case character");
}
public async Task ResendConfirmEmail(
AuditUserDetails auditUserDetails,
GeneralIdRef generalIdRef,
CancellationToken cancellationToken
)
{
var user = await _userManagerRepository.GetUserById(generalIdRef, cancellationToken)
?? throw new NotFoundException("User not found");
await SendEmailUserAction(auditUserDetails, user, EmailUserActionType.ConfirmEmailAddress, cancellationToken);
}
public async Task<string> GetCurrentEmailActionUrl( string emailAddress, EmailUserActionType emailUserActionType, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserByEmail(emailAddress, cancellationToken)
?? throw new NotFoundException("User not found");
var emailUserAction = await _userManagerRepository.GetCurrentEmailUserAction(user.Id, emailUserActionType, cancellationToken)
?? throw new NotFoundException("Email action not found");
var emailUserActionUrl = GetEmailUserActionUrl(user, emailUserAction);
return emailUserActionUrl;
}
public async Task<SsoProvider?> GetSsoProviderForEmail(string loginEmail, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserByEmail(loginEmail, cancellationToken);
if (user?.DomainId != null)
{
var domain =
await _domainRepository.GetDomainById(new GeneralIdRef { Id = user.DomainId }, cancellationToken);
if (domain?.SsoProvider != null)
return domain.SsoProvider;
}
return user?.SsoProvider;
}
public async Task<SsoProvider?> GetSsoProviderById(long ssoProviderId, CancellationToken cancellationToken)
{
var ssoProvider = await _userManagerRepository.GetSsoProviderById(ssoProviderId, cancellationToken);
return ssoProvider;
}
public async Task LinkSsoProfileToUser(AuditUserDetails auditUserDetails, User user, long ssoId, string ssoUserId, bool setEmailConfirmed, CancellationToken cancellationToken)
{
if (user == null)
throw new NullReferenceException();
if (user.Domain.SsoProviderId == null)
{
var ssoProvider = await _userManagerRepository.GetSsoProviderById(ssoId, cancellationToken) ?? throw new NotFoundException("SSO Provider not found");
user.SsoProviderId = ssoProvider.Id;
}
user.SsoSubject = ssoUserId;
if (setEmailConfirmed)
user.EmailConfirmed = true;
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
}
public async Task TurnOfSsoForUser(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserById(generalIdRef, cancellationToken) ?? throw new NotFoundException("User not found");
user.SsoProviderId = null;
user.SsoSubject = string.Empty;
user.SsoProvider = null;
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
}
public async Task<Guid> CreateSingleUseGuid(
AuditUserDetails auditUserDetails,
GeneralIdRef generalIdRef,
CancellationToken cancellationToken
)
{
var user = await _userManagerRepository.GetUserById(generalIdRef, cancellationToken) ?? throw new NotFoundException("User not found");
var singleUseGuid = new SingleUseGuid
{
UserId = user.Id,
User = user,
Guid = Guid.NewGuid(),
Expires = _clock.GetNow.AddHours(GetEmailTimeoutHours())
};
await _userManagerRepository.SaveSingleUseGuidForUser(singleUseGuid, cancellationToken);
return singleUseGuid.Guid;
}
public async Task<User?> GetUserWithSingleUseGuid(Guid guid, CancellationToken cancellationToken)
{
var user = await _userManagerRepository.GetUserBySingleUseGuid(guid, cancellationToken);
await _userManagerRepository.DeleteSingleUseGuid(guid, cancellationToken);
return user;
}
public async Task SetAuthentication(
AuditUserDetails auditUserDetails,
UserAuthenticationDetails userAuthenticationDetails,
bool setEmailConfirmed,
CancellationToken cancellationToken
)
{
await _userManagerRepository.TransactionAsync(async () =>
{
var user = await _userManagerRepository.GetUserById(userAuthenticationDetails.Id, cancellationToken)
?? throw new NotFoundException("User not found");
var userProfile = new UpdatedUserProfile
{
Password = userAuthenticationDetails.Password,
SecurityCode = userAuthenticationDetails.SecurityCode,
UsingTwoFactorAuthentication = userAuthenticationDetails.UsingTwoFactorAuthentication
};
await SetInternalAuthenticationDetails(user, userProfile, false, cancellationToken);
if (setEmailConfirmed)
user.EmailConfirmed = true;
await _userManagerRepository.EditUser(auditUserDetails, user, cancellationToken);
});
}
}