Refactor JWT token generation and update password handling logic; add exception handling behavior
This commit is contained in:
@@ -7,11 +7,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.2.2" />
|
||||
<PackageReference Include="Mapster" Version="7.3.0" />
|
||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.11" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.10" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CMSMicroservice.Domain\CMSMicroservice.Domain.csproj" />
|
||||
|
||||
@@ -11,12 +11,30 @@ public class ValidationException : Exception
|
||||
}
|
||||
|
||||
public ValidationException(IEnumerable<ValidationFailure> failures)
|
||||
: this()
|
||||
: base(BuildMessage(failures, out var errors))
|
||||
{
|
||||
Errors = failures
|
||||
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
|
||||
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
public IDictionary<string, string[]> Errors { get; }
|
||||
|
||||
private static string BuildMessage(IEnumerable<ValidationFailure> failures, out IDictionary<string, string[]> errors)
|
||||
{
|
||||
var list = failures?.Where(f => !string.IsNullOrWhiteSpace(f.ErrorMessage)).ToList() ?? new List<ValidationFailure>();
|
||||
|
||||
errors = list
|
||||
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
|
||||
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
|
||||
|
||||
var items = list
|
||||
.Select(f => string.IsNullOrWhiteSpace(f.PropertyName)
|
||||
? f.ErrorMessage
|
||||
: $"{f.PropertyName}: {f.ErrorMessage}")
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
||||
.ToArray();
|
||||
|
||||
return items.Length > 0
|
||||
? string.Join(" | ", items)
|
||||
: "One or more validation failures have occurred.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace CMSMicroservice.Application.Common.Interfaces;
|
||||
public interface IGenerateJwtToken
|
||||
{
|
||||
Task<string> GenerateJwtToken(User user);
|
||||
Task<string> GenerateJwtToken(User user,int? expiryDays=null);
|
||||
}
|
||||
|
||||
@@ -3,15 +3,50 @@ namespace CMSMicroservice.Application.UserCQ.Commands.SetPasswordForUser;
|
||||
public class SetPasswordForUserCommandHandler : IRequestHandler<SetPasswordForUserCommand, Unit>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IHashService _hashService;
|
||||
|
||||
public SetPasswordForUserCommandHandler(IApplicationDbContext context)
|
||||
public SetPasswordForUserCommandHandler(IApplicationDbContext context, IHashService hashService)
|
||||
{
|
||||
_context = context;
|
||||
_hashService = hashService;
|
||||
}
|
||||
|
||||
public async Task<Unit> Handle(SetPasswordForUserCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
//TODO: Implement your business logic
|
||||
return new Unit();
|
||||
// basic validations
|
||||
if (!string.Equals(request.NewPassword, request.ConfirmPassword, StringComparison.Ordinal))
|
||||
{
|
||||
throw new CMSMicroservice.Application.Common.Exceptions.ValidationException(new[]
|
||||
{
|
||||
new FluentValidation.Results.ValidationFailure(nameof(request.ConfirmPassword), "کلمه عبور و تایید آن یکسان نیستند.")
|
||||
});
|
||||
}
|
||||
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken)
|
||||
?? throw new NotFoundException(nameof(User), request.UserId);
|
||||
|
||||
var hasExistingPassword = !string.IsNullOrWhiteSpace(user.HashPassword);
|
||||
if (hasExistingPassword)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.CurrentPassword))
|
||||
{
|
||||
throw new CMSMicroservice.Application.Common.Exceptions.ValidationException(new[]
|
||||
{
|
||||
new FluentValidation.Results.ValidationFailure(nameof(request.CurrentPassword), "کلمه عبور فعلی الزامی است.")
|
||||
});
|
||||
}
|
||||
if (!_hashService.VerifyPassword(request.CurrentPassword, user.HashPassword))
|
||||
{
|
||||
throw new UnauthorizedAccessException("کلمه عبور فعلی نادرست است.");
|
||||
}
|
||||
}
|
||||
|
||||
// set new password (PBKDF2)
|
||||
user.HashPassword = _hashService.HashPassword(request.NewPassword);
|
||||
_context.Users.Update(user);
|
||||
user.AddDomainEvent(new CMSMicroservice.Domain.Events.SetPasswordForUserEvent(user));
|
||||
await _context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return Unit.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using CMSMicroservice.Domain.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace CMSMicroservice.Application.UserCQ.EventHandlers;
|
||||
namespace CMSMicroservice.Application.UserCQ.EventHandlers.SetPasswordForUserEventHandlers;
|
||||
|
||||
public class SetPasswordForUserEventHandler : INotificationHandler<SetPasswordForUserEvent>
|
||||
{
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
namespace CMSMicroservice.Application.UserCQ.Queries.AdminGetJwtToken;
|
||||
|
||||
public class AdminGetJwtTokenQueryHandler : IRequestHandler<AdminGetJwtTokenQuery, AdminGetJwtTokenResponseDto>
|
||||
{
|
||||
private readonly IApplicationDbContext _context;
|
||||
private readonly IGenerateJwtToken _generateJwt;
|
||||
private readonly IHashService _hashService;
|
||||
|
||||
public AdminGetJwtTokenQueryHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt, IHashService hashService)
|
||||
public AdminGetJwtTokenQueryHandler(IApplicationDbContext context, IGenerateJwtToken generateJwt,
|
||||
IHashService hashService)
|
||||
{
|
||||
_context = context;
|
||||
_generateJwt = generateJwt;
|
||||
_hashService = hashService;
|
||||
}
|
||||
|
||||
public async Task<AdminGetJwtTokenResponseDto> Handle(AdminGetJwtTokenQuery request, CancellationToken cancellationToken)
|
||||
public async Task<AdminGetJwtTokenResponseDto> Handle(AdminGetJwtTokenQuery request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await _context.Users
|
||||
.Include(u => u.UserRoles)
|
||||
@@ -21,14 +24,16 @@ public class AdminGetJwtTokenQueryHandler : IRequestHandler<AdminGetJwtTokenQuer
|
||||
if (user == null)
|
||||
throw new Exception("Invalid username or password.");
|
||||
|
||||
if (user.UserRoles.All(a => a.RoleId != 2))
|
||||
throw new Exception("You do not have permission to do that.");
|
||||
|
||||
// verify password (supports PBKDF2 or legacy SHA-256)
|
||||
if (!_hashService.VerifyPassword(request.Password, user.HashPassword))
|
||||
throw new Exception("Invalid username or password.");
|
||||
|
||||
return new AdminGetJwtTokenResponseDto()
|
||||
{
|
||||
Token = await _generateJwt.GenerateJwtToken(user),
|
||||
Token = await _generateJwt.GenerateJwtToken(user,15),
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user