improvements, fixes, etc...

This commit is contained in:
jozsef.b@aycode.com 2024-04-24 17:58:38 +02:00
parent 7360291242
commit cba3b9f8d6
4 changed files with 50 additions and 27 deletions

View File

@ -8,8 +8,10 @@ namespace AyCode.Core.Helpers
{ {
public class PasswordHasher public class PasswordHasher
{ {
public static string HashPassword(string password, string? dynamicSalt = null) public static string HashPassword(string? password, string? dynamicSalt = null)
{ {
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentNullException(nameof(password));
// Generate a random salt // Generate a random salt
var salt = new byte[16]; var salt = new byte[16];
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(salt); using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(salt);
@ -29,6 +31,9 @@ namespace AyCode.Core.Helpers
// Extract the salt and hashed password from the combined hash // Extract the salt and hashed password from the combined hash
var parts = hashedPassword.Split('$'); var parts = hashedPassword.Split('$');
if (parts.Length != 5) return false;
var salt = Convert.FromBase64String(parts[3].Replace("salt=", string.Empty)); var salt = Convert.FromBase64String(parts[3].Replace("salt=", string.Empty));
var storedHash = parts[4].Replace("hash=", string.Empty); var storedHash = parts[4].Replace("hash=", string.Empty);

View File

@ -21,7 +21,7 @@ namespace AyCode.Database.Tests.Users
protected TUser AcBase_GetUserById_ReturnsUser_WhenUserExists(string userIdString) protected TUser AcBase_GetUserById_ReturnsUser_WhenUserExists(string userIdString)
{ {
var userId = Guid.Parse(userIdString); var userId = Guid.Parse(userIdString);
var user = Dal.GetUserById(userId); var user = Dal.GetUserById(userId, false);
Assert.IsNotNull(user, "User is null"); Assert.IsNotNull(user, "User is null");
Assert.IsNotNull(user.Profile, "Profile is null"); Assert.IsNotNull(user.Profile, "Profile is null");
@ -34,7 +34,7 @@ namespace AyCode.Database.Tests.Users
protected TUser AcBase_GetUserByEmail_ReturnsUser_WhenUserExists(string email) protected TUser AcBase_GetUserByEmail_ReturnsUser_WhenUserExists(string email)
{ {
var user = Dal.GetUserByEmail(email); var user = Dal.GetUserByEmail(email, false);
Assert.IsNotNull(user, "User is null"); Assert.IsNotNull(user, "User is null");
Assert.IsNotNull(user.Profile, "Profile is null"); Assert.IsNotNull(user.Profile, "Profile is null");
@ -49,7 +49,7 @@ namespace AyCode.Database.Tests.Users
{ {
TUser? user = null; TUser? user = null;
user = await Dal.GetUserByEmailAsync(email).ConfigureAwait(false); user = await Dal.GetUserByEmailAsync(email, false).ConfigureAwait(false);
//user = await Dal.SessionAsync(ctx => ctx.Users.FirstOrDefault(x => x.EmailAddress == email)).ConfigureAwait(false); //user = await Dal.SessionAsync(ctx => ctx.Users.FirstOrDefault(x => x.EmailAddress == email)).ConfigureAwait(false);

View File

@ -27,11 +27,11 @@ namespace AyCode.Database.DataLayers.Users
where TUserToServiceProvider : class, IAcUserToServiceProviderBase where TUserToServiceProvider : class, IAcUserToServiceProviderBase
where TProfileAddress : class, IAcAddress where TProfileAddress : class, IAcAddress
{ {
public TUser? GetUserById(Guid userId) => Session(x => x.GetUserById(userId)); public TUser? GetUserById(Guid userId, bool onlyConfirmed) => Session(x => x.GetUserById(userId, onlyConfirmed));
public Task<TUser?> GetUserByIdAsync(Guid userId) => SessionAsync(x => x.GetUserById(userId)); public Task<TUser?> GetUserByIdAsync(Guid userId, bool onlyConfirmed) => SessionAsync(x => x.GetUserById(userId, onlyConfirmed));
public TUser? GetUserByEmail(string? email) => Session(x => x.GetUserByEmail(email)); public TUser? GetUserByEmail(string? email, bool onlyConfirmed) => Session(x => x.GetUserByEmail(email, onlyConfirmed));
public Task<TUser?> GetUserByEmailAsync(string? email) => SessionAsync(x => x.GetUserByEmail(email)); public Task<TUser?> GetUserByEmailAsync(string? email, bool onlyConfirmed) => SessionAsync(x => x.GetUserByEmail(email, onlyConfirmed));
public Task<bool> AddUserAsync(TUser user) public Task<bool> AddUserAsync(TUser user)
{ {
@ -72,18 +72,19 @@ namespace AyCode.Database.DataLayers.Users
profile.Address = address; profile.Address = address;
profile.AddressId = address.Id; profile.AddressId = address.Id;
return profile; return profile;
} }
public Task<bool> RemoveUserAsync(TUser? user) => TransactionAsync(ctx => RemoveUserTransactionBody(user, ctx)); public Task<bool> RemoveUserAsync(TUser? user) => TransactionAsync(ctx => RemoveUserTransactionBody(user, ctx));
public Task<bool> RemoveUserAsync(Guid userId) => TransactionAsync(ctx => public Task<bool> RemoveUserAsync(Guid userId) => TransactionAsync(ctx =>
{ {
var user = ctx.GetUserById(userId); var user = ctx.GetUserById(userId, false);
return RemoveUserTransactionBody(user, ctx); return RemoveUserTransactionBody(user, ctx);
}); });
protected bool RemoveUserTransactionBody(TUser? user, TDbContext ctx) protected virtual bool RemoveUserTransactionBody(TUser? user, TDbContext ctx)
{ {
if (user == null) return false; if (user == null) return false;
@ -106,15 +107,24 @@ namespace AyCode.Database.DataLayers.Users
var result = Transaction(ctx => var result = Transaction(ctx =>
{ {
user = ctx.GetUserByEmail(email); try
if (user is { EmailConfirmed: true } && string.Equals(user.EmailAddress, email, StringComparison.CurrentCultureIgnoreCase)
&& PasswordHasher.VerifyPassword(password, user.Password, PasswordHasher.GenerateDynamicSalt(user.Id)))
{ {
if (ctx.UpdateJwtRefreshToken(user, refreshToken)) return true; //var passwordHash = PasswordHasher.HashPassword(password, PasswordHasher.GenerateDynamicSalt(userId));
errorCodeInner = AcErrorCode.RefreshTokenUpdateError; user = ctx.GetUserByEmail(email, true);
if (user is { EmailConfirmed: true } && string.Equals(user.EmailAddress, email, StringComparison.CurrentCultureIgnoreCase)
&& PasswordHasher.VerifyPassword(password, user.Password, PasswordHasher.GenerateDynamicSalt(user.Id)))
{
if (ctx.UpdateJwtRefreshToken(user, refreshToken)) return true;
errorCodeInner = AcErrorCode.RefreshTokenUpdateError;
}
else errorCodeInner = AcErrorCode.WrongLoginData;
}
catch (Exception ex)
{
errorCodeInner = AcErrorCode.UnknownError;
} }
else errorCodeInner = AcErrorCode.WrongLoginData;
return false; return false;
}); });

View File

@ -9,23 +9,31 @@ namespace AyCode.Database.DbSets.Users;
public static class AcUserDbSetExtensions public static class AcUserDbSetExtensions
{ {
public static TUser? GetUserById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId) where TUser : class, IAcUserBase public static TUser? AuthenticateUser<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, string? passwordHash, bool onlyConfirmed) where TUser : class, IAcUserBase
=> ctx.GetUsersById(userId).FirstOrDefault();
public static TUser? GetUserByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email) where TUser : class, IAcUserBase
{ {
return string.IsNullOrWhiteSpace(email) ? null : ctx.GetUsersByEmail(email).FirstOrDefault(); if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(passwordHash))
return null;
return ctx.GetUsersByEmail(email, onlyConfirmed).SingleOrDefault(u => u.Password == passwordHash);
} }
public static IQueryable<TUser> GetUsersById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId) where TUser : class, IAcUserBase public static TUser? GetUserById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId, bool onlyConfirmed) where TUser : class, IAcUserBase
=> ctx.Users.Where(u => u.Id == userId); => ctx.GetUsersById(userId, onlyConfirmed).FirstOrDefault();
public static IQueryable<TUser> GetUsersByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string email) where TUser : class, IAcUserBase public static TUser? GetUserByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string? email, bool onlyConfirmed) where TUser : class, IAcUserBase
{
return string.IsNullOrWhiteSpace(email) ? null : ctx.GetUsersByEmail(email, onlyConfirmed).FirstOrDefault();
}
public static IQueryable<TUser> GetUsersById<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId, bool onlyConfirmed) where TUser : class, IAcUserBase
=> ctx.Users.Where(u => u.Id == userId && (!onlyConfirmed || u.EmailConfirmed));
public static IQueryable<TUser> GetUsersByEmail<TUser>(this IAcUserDbSetBase<TUser> ctx, string email, bool onlyConfirmed) where TUser : class, IAcUserBase
{ {
Logger.Info($"GetUserByEmail: {email}"); Logger.Info($"GetUserByEmail: {email}");
var emailLower = email.ToLower(); var emailLower = email.ToLower();
return ctx.Users.Where(u => u.EmailAddress == emailLower); return ctx.Users.Where(u => u.EmailAddress == emailLower && (!onlyConfirmed || u.EmailConfirmed));
} }
public static bool AddUser<TUser>(this IAcUserDbSetBase<TUser> ctx, TUser user) where TUser : class, IAcUserBase public static bool AddUser<TUser>(this IAcUserDbSetBase<TUser> ctx, TUser user) where TUser : class, IAcUserBase
@ -42,7 +50,7 @@ public static class AcUserDbSetExtensions
public static bool RemoveUser<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId) where TUser : class, IAcUserBase public static bool RemoveUser<TUser>(this IAcUserDbSetBase<TUser> ctx, Guid userId) where TUser : class, IAcUserBase
{ {
var user = ctx.GetUserById(userId); var user = ctx.GetUserById(userId, false);
if (user == null) return false; if (user == null) return false;
@ -65,7 +73,7 @@ public static class AcUserDbSetExtensions
if (string.IsNullOrWhiteSpace(refreshToken)) return null; if (string.IsNullOrWhiteSpace(refreshToken)) return null;
var existingUser = ctx.GetUserByEmail(email); var existingUser = ctx.GetUserByEmail(email, true);
if (existingUser == null) return null; if (existingUser == null) return null;
existingUser.RefreshToken = refreshToken; existingUser.RefreshToken = refreshToken;