diff options
Diffstat (limited to 'server/src/Services')
| -rw-r--r-- | server/src/Services/MailService.cs | 49 | ||||
| -rw-r--r-- | server/src/Services/PasswordResetService.cs | 115 | ||||
| -rw-r--r-- | server/src/Services/UserService.cs | 50 | ||||
| -rw-r--r-- | server/src/Services/VaultService.cs | 158 |
4 files changed, 0 insertions, 372 deletions
diff --git a/server/src/Services/MailService.cs b/server/src/Services/MailService.cs deleted file mode 100644 index c08cb84..0000000 --- a/server/src/Services/MailService.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace IOL.GreatOffice.Api.Services; - -public class MailService -{ - private readonly ILogger<MailService> _logger; - private static string _emailHost; - private static int _emailPort; - private static string _emailUser; - private static string _emailPassword; - - public MailService(VaultService vaultService, ILogger<MailService> logger) { - var configuration = vaultService.GetCurrentAppConfiguration(); - _logger = logger; - _emailHost = configuration.SMTP_HOST; - _emailPort = Convert.ToInt32(configuration.SMTP_PORT); - _emailUser = configuration.SMTP_USER; - _emailPassword = configuration.SMTP_PASSWORD; - } - - /// <summary> - /// Send an email. - /// </summary> - /// <param name="message"></param> - public void SendMail(MailMessage message) { - using var smtpClient = new SmtpClient { - Host = _emailHost, - EnableSsl = _emailPort == 587, - Port = _emailPort, - Credentials = new NetworkCredential { - UserName = _emailUser, - Password = _emailPassword, - } - }; - var configurationString = JsonSerializer.Serialize(new { - smtpClient.Host, - smtpClient.EnableSsl, - smtpClient.Port, - UserName = _emailUser.HasValue() ? "**REDACTED**" : "**MISSING**", - Password = _emailPassword.HasValue() ? "**REDACTED**" : "**MISSING**", - }, - new JsonSerializerOptions { - WriteIndented = true - }); - - _logger.LogDebug("SmtpClient was instansiated with the following configuration\n" + configurationString); - - smtpClient.Send(message); - } -}
\ No newline at end of file diff --git a/server/src/Services/PasswordResetService.cs b/server/src/Services/PasswordResetService.cs deleted file mode 100644 index 1b4f147..0000000 --- a/server/src/Services/PasswordResetService.cs +++ /dev/null @@ -1,115 +0,0 @@ -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 diff --git a/server/src/Services/UserService.cs b/server/src/Services/UserService.cs deleted file mode 100644 index 6db663a..0000000 --- a/server/src/Services/UserService.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace IOL.GreatOffice.Api.Services; - -public class UserService -{ - private readonly PasswordResetService _passwordResetService; - - /// <summary> - /// Provides methods to perform common operations on user data. - /// </summary> - /// <param name="passwordResetService"></param> - public UserService(PasswordResetService passwordResetService) { - _passwordResetService = passwordResetService; - } - - /// <summary> - /// Log in a user. - /// </summary> - /// <param name="httpContext"></param> - /// <param name="user"></param> - /// <param name="persist"></param> - public async Task LogInUser(HttpContext httpContext, User user, bool persist = false) { - var claims = new List<Claim> { - new(AppClaims.USER_ID, user.Id.ToString()), - new(AppClaims.NAME, user.Username), - }; - - var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); - var principal = new ClaimsPrincipal(identity); - var authenticationProperties = new AuthenticationProperties { - AllowRefresh = true, - IssuedUtc = DateTimeOffset.UtcNow, - }; - - if (persist) { - authenticationProperties.ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(6); - authenticationProperties.IsPersistent = true; - } - - await httpContext.SignInAsync(principal, authenticationProperties); - await _passwordResetService.DeleteRequestsForUserAsync(user.Id); - } - - /// <summary> - /// Log out a user. - /// </summary> - /// <param name="httpContext"></param> - public async Task LogOutUser(HttpContext httpContext) { - await httpContext.SignOutAsync(); - } -} diff --git a/server/src/Services/VaultService.cs b/server/src/Services/VaultService.cs deleted file mode 100644 index 732911a..0000000 --- a/server/src/Services/VaultService.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Microsoft.Extensions.Caching.Memory; - -namespace IOL.GreatOffice.Api.Services; - -public class VaultService -{ - private readonly HttpClient _client; - private readonly IMemoryCache _cache; - private readonly IConfiguration _configuration; - private readonly ILogger<VaultService> _logger; - private int CACHE_TTL { get; set; } - - public VaultService(HttpClient client, IConfiguration configuration, IMemoryCache cache, ILogger<VaultService> logger) { - var token = configuration.GetValue<string>(AppEnvironmentVariables.VAULT_TOKEN); - var vaultUrl = configuration.GetValue<string>(AppEnvironmentVariables.VAULT_URL); - CACHE_TTL = configuration.GetValue(AppEnvironmentVariables.VAULT_CACHE_TTL, 60 * 60 * 12); - if (token.IsNullOrWhiteSpace()) throw new ApplicationException("VAULT_TOKEN is empty"); - if (vaultUrl.IsNullOrWhiteSpace()) throw new ApplicationException("VAULT_URL is empty"); - client.DefaultRequestHeaders.Add("X-Vault-Token", token); - client.BaseAddress = new Uri(vaultUrl); - _client = client; - _cache = cache; - _configuration = configuration; - _logger = logger; - } - - public static object Data { get; set; } - - public T Get<T>(string path) { - var result = _cache.GetOrCreate(AppConstants.VAULT_CACHE_KEY, - cacheEntry => { - cacheEntry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(CACHE_TTL); - var getSecretResponse = _client.GetFromJsonAsync<GetSecretResponse<T>>("/v1/kv/data/" + path).Result; - - if (getSecretResponse == null) { - return default; - } - - Log.Debug("Setting new Vault cache, " - + new { - PATH = path, - CACHE_TTL, - Data = JsonSerializer.Serialize(getSecretResponse.Data.Data) - }); - return getSecretResponse.Data.Data ?? default; - }); - return result; - } - - public T Refresh<T>(string path) { - _cache.Remove(AppConstants.VAULT_CACHE_KEY); - CACHE_TTL = _configuration.GetValue(AppEnvironmentVariables.VAULT_CACHE_TTL, 60 * 60 * 12); - return Get<T>(path); - } - - public async Task<RenewTokenResponse> RenewTokenAsync<T>(string token) { - var response = await _client.PostAsJsonAsync("v1/auth/token/renew", - new { - Token = token - }); - if (response.IsSuccessStatusCode) { - return await response.Content.ReadFromJsonAsync<RenewTokenResponse>(); - } - - return default; - } - - public AppConfiguration GetCurrentAppConfiguration() { - var path = _configuration.GetValue<string>(AppEnvironmentVariables.MAIN_CONFIG_SHEET); - var result = Get<AppConfiguration>(path); - var overwrites = new { - DB_HOST = _configuration.GetValue("OVERWRITE_DB_HOST", string.Empty), - DB_PORT = _configuration.GetValue("OVERWRITE_DB_PORT", string.Empty), - DB_USER = _configuration.GetValue("OVERWRITE_DB_USER", string.Empty), - DB_PASSWORD = _configuration.GetValue("OVERWRITE_DB_PASSWORD", string.Empty), - DB_NAME = _configuration.GetValue("OVERWRITE_DB_NAME", string.Empty), - }; - if (overwrites.DB_HOST.HasValue()) { - _logger.LogInformation("OVERWRITE_DB_HOST is specified, using it's value: " + overwrites.DB_HOST); - result.DB_HOST = overwrites.DB_HOST; - } - - if (overwrites.DB_PORT.HasValue()) { - _logger.LogInformation("OVERWRITE_DB_PORT is specified, using it's value: " + overwrites.DB_PORT); - result.DB_PORT = overwrites.DB_PORT; - } - - if (overwrites.DB_USER.HasValue()) { - _logger.LogInformation("OVERWRITE_DB_USER is specified, using it's value: " + overwrites.DB_USER); - result.DB_USER = overwrites.DB_USER; - } - - if (overwrites.DB_PASSWORD.HasValue()) { - _logger.LogInformation("OVERWRITE_DB_PASSWORD is specified, using it's value: " + "(redacted)"); - result.DB_PASSWORD = overwrites.DB_PASSWORD; - } - - if (overwrites.DB_NAME.HasValue()) { - _logger.LogInformation("OVERWRITE_DB_NAME is specified, using it's value: " + overwrites.DB_NAME); - result.DB_NAME = overwrites.DB_NAME; - } - - return result; - } - - public AppConfiguration RefreshCurrentAppConfiguration() { - var path = _configuration.GetValue<string>(AppEnvironmentVariables.MAIN_CONFIG_SHEET); - return Refresh<AppConfiguration>(path); - } - - public class RenewTokenResponse - { - public Guid RequestId { get; set; } - public string LeaseId { get; set; } - public bool Renewable { get; set; } - public long LeaseDuration { get; set; } - public object Data { get; set; } - public object WrapInfo { get; set; } - public List<string> Warnings { get; set; } - public Auth Auth { get; set; } - } - - public class Auth - { - public string ClientToken { get; set; } - public string Accessor { get; set; } - public List<string> Policies { get; set; } - public List<string> TokenPolicies { get; set; } - public object Metadata { get; set; } - public long LeaseDuration { get; set; } - public bool Renewable { get; set; } - public string EntityId { get; set; } - public string TokenType { get; set; } - public bool Orphan { get; set; } - public object MfaRequirement { get; set; } - public long NumUses { get; set; } - } - - public class GetSecretResponse<T> - { - public VaultSecret<T> Data { get; set; } - } - - public class VaultSecret<T> - { - public T Data { get; set; } - public VaultSecretMetadata Metadata { get; set; } - } - - public class VaultSecretMetadata - { - public DateTimeOffset CreatedTime { get; set; } - public object CustomMetadata { get; set; } - public string DeletionTime { get; set; } - public bool Destroyed { get; set; } - public long Version { get; set; } - } -} |
