using MaksIT.Core.Abstractions.Domain; using MaksIT.Core.Security; using MaksIT.Results; namespace MaksIT.LetsEncryptServer.Domain; public class User( Guid id, string name ) : DomainDocumentBase(id) { public string Name { get; private set; } = name; public string Salt { get; private set; } = string.Empty; public string Hash { get; private set; } = string.Empty; public List JwtTokens { get; private set; } = []; public DateTime LastLogin { get; private set; } public User( string name ) : this( Guid.NewGuid(), name ) { } /// /// Change user name /// /// /// /// public User SetName(string newName) { Name = newName; return this; } /// /// For persistence /// /// /// /// public User SetSaltedHash(string salt, string hash) { Salt = salt; Hash = hash; return this; } /// /// /// /// /// public User SetJwtTokens(List tokens) { JwtTokens = tokens; return this; } public User SetLastLogin() { SetLastLogin(DateTime.UtcNow); return this; } public User SetLastLogin(DateTime dateTime) { LastLogin = dateTime; return this; } #region Password Management /// /// Set or change password (returns this for chaining) /// /// /// /// public Result SetPassword(string password, string pepper) { if (!PasswordHasher.TryCreateSaltedHash(password, pepper, out var saltedHash, out var errorMessage)) return Result.InternalServerError(null, errorMessage); Salt = saltedHash.Value.Salt; Hash = saltedHash.Value.Hash; return Result.Ok(this); } /// /// Validate password /// /// /// /// public Result ValidatePassword(string password, string pepper) { if (PasswordHasher.TryValidateHash(password, Salt, Hash, pepper, out var isValid, out var errorMessage)) { if (isValid) return Result.Ok(); return Result.Unauthorized("Invalid password."); } return Result.InternalServerError(null, errorMessage); } /// /// Reset password to a new value (returns this for chaining) /// /// /// /// public Result ResetPassword(string newPassword, string pepper) => SetPassword(newPassword, pepper); #endregion #region JWT Token Management public User UpsertJwtToken(JwtToken token) { var existing = JwtTokens.FirstOrDefault(t => t.Id == token.Id); if (existing != null) JwtTokens.Remove(existing); JwtTokens.Add(token); return this; } public User UpsertJwtTokens(List tokens) { foreach (var token in tokens) UpsertJwtToken(token); return this; } public Result RemoveJwtToken(Guid tokenId) { var token = JwtTokens.FirstOrDefault(t => t.Id == tokenId); if (token == null) return Result.NotFound(null, "JWT token not found."); JwtTokens.Remove(token); return Result.Ok(this); } public Result RemoveJwtToken(string token) { var tokenDomain = JwtTokens.FirstOrDefault(t => t.Token == token); if (tokenDomain == null) return Result.NotFound(null, "JWT token not found."); JwtTokens.Remove(tokenDomain); return Result.Ok(this); } public Result RemoveJwtTokens(List tokenIds) { foreach (var tokenId in tokenIds) { var removeTokenResult = RemoveJwtToken(tokenId); if (!removeTokenResult.IsSuccess) return removeTokenResult; } return Result.Ok(this); } public User RemoveRevokedJwtTokens() { JwtTokens = JwtTokens.Where(t => !t.IsRevoked).ToList(); return this; } public User RevokeAllJwtTokens() { JwtTokens = []; return this; } #endregion }