diff options
| author | ivarlovlie <git@ivarlovlie.no> | 2022-09-29 16:45:43 +0200 |
|---|---|---|
| committer | ivarlovlie <git@ivarlovlie.no> | 2022-09-29 16:45:43 +0200 |
| commit | 5857fde284b22e885843a992479ee6cf68fd8048 (patch) | |
| tree | e4b1f35931cec30aec2ad6bf664f8a8cea4abad9 /server/src/Services/PasswordResetService.cs | |
| parent | 4a581314cede75241d66336cecf2a45fb7288cd4 (diff) | |
| download | greatoffice-5857fde284b22e885843a992479ee6cf68fd8048.tar.xz greatoffice-5857fde284b22e885843a992479ee6cf68fd8048.zip | |
refactor: Rename ForgotPasswordService to PasswordResetService internally
Diffstat (limited to 'server/src/Services/PasswordResetService.cs')
| -rw-r--r-- | server/src/Services/PasswordResetService.cs | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/server/src/Services/PasswordResetService.cs b/server/src/Services/PasswordResetService.cs new file mode 100644 index 0000000..1b4f147 --- /dev/null +++ b/server/src/Services/PasswordResetService.cs @@ -0,0 +1,115 @@ +namespace IOL.GreatOffice.Api.Services; + +public class PasswordResetService +{ + private readonly AppDbContext _context; + private readonly MailService _mailService; + private readonly AppConfiguration _configuration; + private readonly ILogger<PasswordResetService> _logger; + + + public PasswordResetService( + AppDbContext context, + VaultService vaultService, + ILogger<PasswordResetService> logger, + MailService mailService + ) { + _context = context; + _configuration = vaultService.GetCurrentAppConfiguration(); + _logger = logger; + _mailService = mailService; + } + + public async Task<ForgotPasswordRequest> GetRequestAsync(Guid id, CancellationToken cancellationToken = default) { + var request = await _context.ForgotPasswordRequests + .Include(c => c.User) + .SingleOrDefaultAsync(c => c.Id == id, cancellationToken); + if (request == default) { + return default; + } + + _logger.LogInformation($"Found password reset request for user: {request.User.Username}, expires at {request.ExpirationDate} (in {request.ExpirationDate.Subtract(AppDateTime.UtcNow).Minutes} minutes)."); + return request; + } + + public async Task<bool> FullFillRequestAsync(Guid id, string newPassword, CancellationToken cancellationToken = default) { + var request = await GetRequestAsync(id, cancellationToken); + if (request == default) { + throw new ForgotPasswordRequestNotFoundException("Request with id: " + id + " was not found"); + } + + var user = _context.Users.SingleOrDefault(c => c.Id == request.User.Id); + if (user == default) { + throw new UserNotFoundException("User with id: " + request.User.Id + " was not found"); + } + + user.HashAndSetPassword(newPassword); + _context.Users.Update(user); + await _context.SaveChangesAsync(cancellationToken); + _logger.LogInformation($"Fullfilled password reset request for user: {request.User.Username}"); + await DeleteRequestsForUserAsync(user.Id, cancellationToken); + return true; + } + + + public async Task AddRequestAsync(User user, TimeZoneInfo requestTz, CancellationToken cancellationToken = default) { + await DeleteRequestsForUserAsync(user.Id, cancellationToken); + var request = new ForgotPasswordRequest(user); + _context.ForgotPasswordRequests.Add(request); + await _context.SaveChangesAsync(cancellationToken); + var portalUrl = _configuration.PORTAL_URL; + var emailFromAddress = _configuration.EMAIL_FROM_ADDRESS; + var emailFromDisplayName = _configuration.EMAIL_FROM_DISPLAY_NAME; + var zonedExpirationDate = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(request.ExpirationDate, requestTz.Id); + var message = new MailMessage { + From = new MailAddress(emailFromAddress, emailFromDisplayName), + To = { + new MailAddress(user.Username) + }, + Subject = "Reset password - Greatoffice", + Body = @$" +Hi {user.Username} + +Go to the following link to set a new password. + +{portalUrl}/reset-password/{request.Id} + +The link expires at {zonedExpirationDate:yyyy-MM-dd hh:mm}. +If you did not request a password reset, no action is required. +" + }; + +#pragma warning disable 4014 + Task.Run(() => { +#pragma warning restore 4014 + _mailService.SendMail(message); + _logger.LogInformation($"Added password reset request for user: {request.User.Username}, expires in {request.ExpirationDate.Subtract(AppDateTime.UtcNow)}."); + }, + cancellationToken); + } + + public async Task DeleteRequestsForUserAsync(Guid userId, CancellationToken cancellationToken = default) { + var requestsToRemove = _context.ForgotPasswordRequests.Where(c => c.UserId == userId).ToList(); + if (!requestsToRemove.Any()) return; + _context.ForgotPasswordRequests.RemoveRange(requestsToRemove); + await _context.SaveChangesAsync(cancellationToken); + _logger.LogInformation($"Deleted {requestsToRemove.Count} password reset requests for user: {userId}."); + } + + + public async Task DeleteStaleRequestsAsync(CancellationToken cancellationToken = default) { + var deleteCount = 0; + foreach (var request in _context.ForgotPasswordRequests.Where(c => c.IsExpired)) { + if (!request.IsExpired) { + continue; + } + + _context.ForgotPasswordRequests.Remove(request); + deleteCount++; + _logger.LogInformation($"Marking password reset request with id: {request.Id} for deletion, expiration date was {request.ExpirationDate}."); + } + + await _context.SaveChangesAsync(cancellationToken); + _logger.LogInformation($"Deleted {deleteCount} stale password reset requests."); + } +}
\ No newline at end of file |
