diff --git a/dotnet/AipsCore/Application/Abstract/UserContext/ITokenProvider.cs b/dotnet/AipsCore/Application/Abstract/UserContext/ITokenProvider.cs index 7839f5f..26932bf 100644 --- a/dotnet/AipsCore/Application/Abstract/UserContext/ITokenProvider.cs +++ b/dotnet/AipsCore/Application/Abstract/UserContext/ITokenProvider.cs @@ -1,8 +1,9 @@ using AipsCore.Domain.Models.User; +using AipsCore.Domain.Models.User.External; namespace AipsCore.Application.Abstract.UserContext; public interface ITokenProvider { - string Generate(User user, IList roles); + string Generate(User user, IList roles); } \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Authentication/Token.cs b/dotnet/AipsCore/Application/Authentication/Token.cs deleted file mode 100644 index 85915f3..0000000 --- a/dotnet/AipsCore/Application/Authentication/Token.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace AipsCore.Application.Authentication; - -public record Token(string Value); \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Authentication/IAuthService.cs b/dotnet/AipsCore/Application/Common/Authentication/IAuthService.cs new file mode 100644 index 0000000..f5c3215 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Authentication/IAuthService.cs @@ -0,0 +1,9 @@ +using AipsCore.Domain.Models.User; + +namespace AipsCore.Application.Common.Authentication; + +public interface IAuthService +{ + Task SignUpWithPasswordAsync(User user, string password, CancellationToken cancellationToken = default); + Task LoginWithEmailAndPasswordAsync(string email, string password, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Authentication/LoginResult.cs b/dotnet/AipsCore/Application/Common/Authentication/LoginResult.cs new file mode 100644 index 0000000..16e22c4 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Authentication/LoginResult.cs @@ -0,0 +1,6 @@ +using AipsCore.Domain.Models.User; +using AipsCore.Domain.Models.User.External; + +namespace AipsCore.Application.Common.Authentication; + +public record LoginResult(User User, IList Roles); \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Common/Authentication/Token.cs b/dotnet/AipsCore/Application/Common/Authentication/Token.cs new file mode 100644 index 0000000..18d5af1 --- /dev/null +++ b/dotnet/AipsCore/Application/Common/Authentication/Token.cs @@ -0,0 +1,3 @@ +namespace AipsCore.Application.Common.Authentication; + +public record Token(string Value); \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommand.cs b/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommand.cs index 3a2312d..216eb8c 100644 --- a/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommand.cs +++ b/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommand.cs @@ -1,5 +1,5 @@ using AipsCore.Application.Abstract.Command; -using AipsCore.Application.Authentication; +using AipsCore.Application.Common.Authentication; namespace AipsCore.Application.Models.User.Command.LogIn; diff --git a/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommandHandler.cs b/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommandHandler.cs index 2316407..eebaa7d 100644 --- a/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommandHandler.cs +++ b/dotnet/AipsCore/Application/Models/User/Command/LogIn/LogInUserCommandHandler.cs @@ -1,6 +1,6 @@ using AipsCore.Application.Abstract.Command; using AipsCore.Application.Abstract.UserContext; -using AipsCore.Application.Authentication; +using AipsCore.Application.Common.Authentication; using AipsCore.Domain.Abstract; using AipsCore.Domain.Models.User.External; @@ -10,18 +10,18 @@ public class LogInUserCommandHandler : ICommandHandler { private readonly IUserRepository _userRepository; private readonly ITokenProvider _tokenProvider; - private readonly IUnitOfWork _unitOfWork; - - public LogInUserCommandHandler(IUserRepository userRepository, ITokenProvider tokenProvider, IUnitOfWork unitOfWork) + private readonly IAuthService _authService; + + public LogInUserCommandHandler(IUserRepository userRepository, ITokenProvider tokenProvider, IAuthService authService) { _userRepository = userRepository; _tokenProvider = tokenProvider; - _unitOfWork = unitOfWork; + _authService = authService; } public async Task Handle(LogInUserCommand command, CancellationToken cancellationToken = default) { - var loginResult = await _userRepository.LoginWithEmailAndPasswordAsync(command.Email, command.Password, cancellationToken); + var loginResult = await _authService.LoginWithEmailAndPasswordAsync(command.Email, command.Password, cancellationToken); return new Token(_tokenProvider.Generate(loginResult.User, loginResult.Roles)); } diff --git a/dotnet/AipsCore/Application/Models/User/Command/SignUp/SignUpUserCommandHandler.cs b/dotnet/AipsCore/Application/Models/User/Command/SignUp/SignUpUserCommandHandler.cs index 722d811..925c22f 100644 --- a/dotnet/AipsCore/Application/Models/User/Command/SignUp/SignUpUserCommandHandler.cs +++ b/dotnet/AipsCore/Application/Models/User/Command/SignUp/SignUpUserCommandHandler.cs @@ -1,4 +1,5 @@ using AipsCore.Application.Abstract.Command; +using AipsCore.Application.Common.Authentication; using AipsCore.Domain.Abstract; using AipsCore.Domain.Models.User.External; using AipsCore.Domain.Models.User.ValueObjects; @@ -8,19 +9,19 @@ namespace AipsCore.Application.Models.User.Command.SignUp; public class SignUpUserCommandHandler : ICommandHandler { private readonly IUserRepository _userRepository; - private readonly IUnitOfWork _unitOfWork; - - public SignUpUserCommandHandler(IUserRepository userRepository, IUnitOfWork unitOfWork) + private readonly IAuthService _authService; + + public SignUpUserCommandHandler(IUserRepository userRepository, IAuthService authService) { _userRepository = userRepository; - _unitOfWork = unitOfWork; + _authService = authService; } public async Task Handle(SignUpUserCommand command, CancellationToken cancellationToken = default) { var user = Domain.Models.User.User.Create(command.Email, command.Username); - await _userRepository.SignUpWithPasswordAsync(user, command.Password, cancellationToken); + await _authService.SignUpWithPasswordAsync(user, command.Password, cancellationToken); return user.Id; } diff --git a/dotnet/AipsCore/Domain/Models/User/External/IUserRepository.cs b/dotnet/AipsCore/Domain/Models/User/External/IUserRepository.cs index edc3107..7a9f154 100644 --- a/dotnet/AipsCore/Domain/Models/User/External/IUserRepository.cs +++ b/dotnet/AipsCore/Domain/Models/User/External/IUserRepository.cs @@ -5,6 +5,5 @@ namespace AipsCore.Domain.Models.User.External; public interface IUserRepository : IAbstractRepository { - Task SignUpWithPasswordAsync(User user, string password, CancellationToken cancellationToken = default); - Task LoginWithEmailAndPasswordAsync(string email, string password, CancellationToken cancellationToken = default); + } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/User/External/LoginResult.cs b/dotnet/AipsCore/Domain/Models/User/External/LoginResult.cs deleted file mode 100644 index 3988a20..0000000 --- a/dotnet/AipsCore/Domain/Models/User/External/LoginResult.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace AipsCore.Domain.Models.User.External; - -public record LoginResult(User User, IList Roles); \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/User/External/UserRole.cs b/dotnet/AipsCore/Domain/Models/User/External/UserRole.cs index de0a13e..5712d3d 100644 --- a/dotnet/AipsCore/Domain/Models/User/External/UserRole.cs +++ b/dotnet/AipsCore/Domain/Models/User/External/UserRole.cs @@ -17,10 +17,8 @@ public record UserRole public static IEnumerable All() => [User, Admin]; - public static UserRole FromString(string name) + public static UserRole? FromString(string name) { - var role = All().FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - - return role ?? throw new ValidationException(UserErrors.RoleDoesNotExist(name)); + return All().FirstOrDefault(r => r.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/User/Options/UserOptionsDefaults.Auth.cs b/dotnet/AipsCore/Domain/Models/User/Options/UserOptionsDefaults.Auth.cs new file mode 100644 index 0000000..959ce5e --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/User/Options/UserOptionsDefaults.Auth.cs @@ -0,0 +1,12 @@ +namespace AipsCore.Domain.Models.User.Options; + +public static partial class UserOptionsDefaults +{ + public const int PasswordRequiredLength = 8; + public const bool PasswordRequireDigit = true; + public const bool PasswordRequireLowercase = true; + public const bool PasswordRequireUppercase = true; + public const bool PasswordRequireNonAlphanumeric = true; + + public const bool UserRequireUniqueEmail = true; +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/User/Validation/UserErrors.cs b/dotnet/AipsCore/Domain/Models/User/Validation/UserErrors.cs index 62affaa..7675aa1 100644 --- a/dotnet/AipsCore/Domain/Models/User/Validation/UserErrors.cs +++ b/dotnet/AipsCore/Domain/Models/User/Validation/UserErrors.cs @@ -7,18 +7,10 @@ namespace AipsCore.Domain.Models.User.Validation; public class UserErrors : AbstractErrors { - public static ValidationError LoginErrorUserNotFoundByEmail(string email) + public static ValidationError InvalidCredentials() { - string code = "login_error_user_not_found_by_email"; - string message = $"User with email '{email}' not found"; - - return CreateValidationError(code, message); - } - - public static ValidationError LoginErrorIncorrectPassword() - { - string code = "login_error_incorrect_password"; - string message = $"Incorrect password provided"; + string code = "invalid_credentials"; + string message = "Invalid credentials"; return CreateValidationError(code, message); } diff --git a/dotnet/AipsCore/Infrastructure/DI/Configuration/ConfigurationEnvExtensions.cs b/dotnet/AipsCore/Infrastructure/DI/Configuration/ConfigurationEnvExtensions.cs index 69d8ec1..8c56742 100644 --- a/dotnet/AipsCore/Infrastructure/DI/Configuration/ConfigurationEnvExtensions.cs +++ b/dotnet/AipsCore/Infrastructure/DI/Configuration/ConfigurationEnvExtensions.cs @@ -5,21 +5,66 @@ namespace AipsCore.Infrastructure.DI.Configuration; public static class ConfigurationEnvExtensions { private const string DbConnStringKey = "DB_CONN_STRING"; + + private const string JwtIssuer = "JWT_ISSUER"; + private const string JwtAudience = "JWT_AUDIENCE"; + private const string JwtKey = "JWT_KEY"; + private const string JwtExpirationMinutes = "JWT_EXPIRATION_MINUTES"; - public static string GetEnvConnectionString(this IConfiguration configuration) + extension(IConfiguration configuration) { - return configuration.GetEnvForSure(DbConnStringKey); - } - - private static string GetEnvForSure(this IConfiguration configuration, string key) - { - var value = configuration[key]; - - if (value is null) + public string GetEnvConnectionString() { - throw new ConfigurationException(key); + return configuration.GetEnvForSure(DbConnStringKey); + } + + public string GetEnvJwtIssuer() + { + return configuration.GetEnvForSure(JwtIssuer); + } + + public string GetEnvJwtAudience() + { + return configuration.GetEnvForSure(JwtAudience); + } + + public string GetEnvJwtKey() + { + return configuration.GetEnvForSure(JwtKey); + } + + public int GetEnvJwtExpirationMinutes() + { + return configuration.GetEnvInt(configuration.GetEnvOrDefault(JwtExpirationMinutes, "60")); + } + + private string GetEnvForSure(string key) + { + var value = configuration[key]; + + if (value is null) + { + throw new ConfigurationException(key); + } + + return value; } - return value; + private string GetEnvOrDefault(string key, string defaultValue) + { + return configuration.GetValue(key, defaultValue); + } + + private int GetEnvInt(string value) + { + if (int.TryParse(value, out var result)) + { + return result; + } + else + { + throw new ConfigurationException($"Value '{value}' is not a valid integer."); + } + } } } \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/DI/StartupExtensions.cs b/dotnet/AipsCore/Infrastructure/DI/StartupExtensions.cs new file mode 100644 index 0000000..0a7265e --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/DI/StartupExtensions.cs @@ -0,0 +1,16 @@ +using AipsCore.Infrastructure.Persistence.Db; +using Microsoft.Extensions.DependencyInjection; + +namespace AipsCore.Infrastructure.DI; + +public static class StartupExtensions +{ + public static async Task InitializeInfrastructureAsync(this IServiceProvider services) + { + using var scope = services.CreateScope(); + + var serviceProvider = scope.ServiceProvider; + + await DbInitializer.SeedRolesAsync(serviceProvider); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs b/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs index f051b43..e3e5ea1 100644 --- a/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs +++ b/dotnet/AipsCore/Infrastructure/DI/UserContextRegistrationExtension.cs @@ -1,5 +1,8 @@ using System.Text; using AipsCore.Application.Abstract.UserContext; +using AipsCore.Application.Common.Authentication; +using AipsCore.Domain.Models.User.Options; +using AipsCore.Infrastructure.DI.Configuration; using AipsCore.Infrastructure.Persistence.Authentication; using AipsCore.Infrastructure.Persistence.Db; using AipsCore.Infrastructure.Persistence.User; @@ -17,10 +20,10 @@ public static class UserContextRegistrationExtension { var jwtSettings = new JwtSettings { - Issuer = configuration["JWT_ISSUER"]!, - Audience = configuration["JWT_AUDIENCE"]!, - Key = configuration["JWT_KEY"]!, - ExpirationMinutes = int.Parse(configuration["JWT_EXPIRATION_MINUTES"] ?? "60") + Issuer = configuration.GetEnvJwtIssuer(), + Audience = configuration.GetEnvJwtAudience(), + Key = configuration.GetEnvJwtKey(), + ExpirationMinutes = configuration.GetEnvJwtExpirationMinutes() }; services.AddSingleton(jwtSettings); @@ -29,13 +32,13 @@ public static class UserContextRegistrationExtension services.AddIdentityCore(options => { - options.Password.RequiredLength = 8; - options.Password.RequireDigit = true; - options.Password.RequireLowercase = true; - options.Password.RequireUppercase = true; - options.Password.RequireNonAlphanumeric = true; + options.Password.RequiredLength = UserOptionsDefaults.PasswordRequiredLength; + options.Password.RequireDigit = UserOptionsDefaults.PasswordRequireDigit; + options.Password.RequireLowercase = UserOptionsDefaults.PasswordRequireLowercase; + options.Password.RequireUppercase = UserOptionsDefaults.PasswordRequireUppercase; + options.Password.RequireNonAlphanumeric = UserOptionsDefaults.PasswordRequireNonAlphanumeric; - options.User.RequireUniqueEmail = true; + options.User.RequireUniqueEmail = UserOptionsDefaults.UserRequireUniqueEmail; }) .AddRoles>() .AddEntityFrameworkStores() @@ -62,6 +65,7 @@ public static class UserContextRegistrationExtension services.AddTransient(); services.AddTransient(); + services.AddTransient(); return services; } diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Authentication/EfAuthService.cs b/dotnet/AipsCore/Infrastructure/Persistence/Authentication/EfAuthService.cs new file mode 100644 index 0000000..700bf42 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/Authentication/EfAuthService.cs @@ -0,0 +1,64 @@ +using AipsCore.Application.Common.Authentication; +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.User.External; +using AipsCore.Domain.Models.User.Validation; +using AipsCore.Infrastructure.Persistence.User.Mappers; +using Microsoft.AspNetCore.Identity; + +namespace AipsCore.Infrastructure.Persistence.Authentication; + +public class EfAuthService : IAuthService +{ + private readonly UserManager _userManager; + + public EfAuthService(UserManager userManager) + { + _userManager = userManager; + } + + public async Task SignUpWithPasswordAsync(Domain.Models.User.User user, string password, CancellationToken cancellationToken = default) + { + var entity = user.MapToEntity(); + + var result = await _userManager.CreateAsync(entity, password); + + if (!result.Succeeded) + { + var errors = string.Join(", ", result.Errors.Select(e => e.Description)); + throw new Exception($"User registration failed: {errors}"); + } + + await _userManager.AddToRoleAsync(entity, UserRole.User.Name); + } + + public async Task LoginWithEmailAndPasswordAsync(string email, string password, CancellationToken cancellationToken = default) + { + var entity = await _userManager.FindByEmailAsync(email); + + if (entity is null) + { + throw new ValidationException(UserErrors.InvalidCredentials()); + } + + var isPasswordValid = await _userManager.CheckPasswordAsync(entity, password); + + if (!isPasswordValid) + { + throw new ValidationException(UserErrors.InvalidCredentials()); + } + + var roles = new List(); + var rolesNames = await _userManager.GetRolesAsync(entity); + + foreach (var roleName in rolesNames) + { + var role = UserRole.FromString(roleName); + + if (role is null) throw new Exception($"Role {roleName} not found"); + + roles.Add(role); + } + + return new LoginResult(entity.MapToModel(), roles); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Authentication/JwtTokenProvider.cs b/dotnet/AipsCore/Infrastructure/Persistence/Authentication/JwtTokenProvider.cs index c80883b..2ccc79e 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Authentication/JwtTokenProvider.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Authentication/JwtTokenProvider.cs @@ -2,6 +2,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Models.User.External; using Microsoft.IdentityModel.Tokens; namespace AipsCore.Infrastructure.Persistence.Authentication; @@ -15,7 +16,7 @@ public class JwtTokenProvider : ITokenProvider _jwtSettings = jwtSettings; } - public string Generate(Domain.Models.User.User user, IList roles) + public string Generate(Domain.Models.User.User user, IList roles) { var claims = new List { @@ -25,7 +26,7 @@ public class JwtTokenProvider : ITokenProvider foreach (var role in roles) { - claims.Add(new Claim(ClaimTypes.Role, role)); + claims.Add(new Claim(ClaimTypes.Role, role.Name)); } var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key)); diff --git a/dotnet/AipsCore/Infrastructure/Persistence/User/Mappers/UserMappers.cs b/dotnet/AipsCore/Infrastructure/Persistence/User/Mappers/UserMappers.cs new file mode 100644 index 0000000..8cfe7b4 --- /dev/null +++ b/dotnet/AipsCore/Infrastructure/Persistence/User/Mappers/UserMappers.cs @@ -0,0 +1,39 @@ +namespace AipsCore.Infrastructure.Persistence.User.Mappers; + +public static class UserMappers +{ + public static Domain.Models.User.User MapToModel(this User entity) + { + return Domain.Models.User.User.Create( + entity.Id.ToString(), + entity.Email!, + entity.UserName!, + entity.CreatedAt, + entity.DeletedAt + ); + } + + public static User MapToEntity(this Domain.Models.User.User model) + { + return new User + { + Id = new Guid(model.Id.IdValue), + Email = model.Email.EmailValue, + NormalizedEmail = model.Email.EmailValue.ToUpperInvariant(), + UserName = model.Username.UsernameValue, + NormalizedUserName = model.Username.UsernameValue.ToUpperInvariant(), + CreatedAt = model.CreatedAt.CreatedAtValue, + DeletedAt = model.DeletedAt.DeletedAtValue + }; + } + + public static void UpdateEntity(this User entity, Domain.Models.User.User model) + { + entity.Email = model.Email.EmailValue; + entity.NormalizedEmail = model.Email.EmailValue.ToUpperInvariant(); + entity.UserName = model.Username.UsernameValue; + entity.NormalizedUserName = model.Username.UsernameValue.ToUpperInvariant(); + entity.CreatedAt = model.CreatedAt.CreatedAtValue; + entity.DeletedAt = model.DeletedAt.DeletedAtValue; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/User/UserRepository.cs b/dotnet/AipsCore/Infrastructure/Persistence/User/UserRepository.cs index 525a36d..b650917 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/User/UserRepository.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/User/UserRepository.cs @@ -4,88 +4,31 @@ using AipsCore.Domain.Models.User.Validation; using AipsCore.Domain.Models.User.ValueObjects; using AipsCore.Infrastructure.Persistence.Abstract; using AipsCore.Infrastructure.Persistence.Db; +using AipsCore.Infrastructure.Persistence.User.Mappers; using Microsoft.AspNetCore.Identity; namespace AipsCore.Infrastructure.Persistence.User; public class UserRepository : AbstractRepository, IUserRepository { - private readonly UserManager _userManager; - public UserRepository(AipsDbContext context, UserManager userManager) : base(context) { - _userManager = userManager; + } protected override Domain.Models.User.User MapToModel(User entity) { - return Domain.Models.User.User.Create( - entity.Id.ToString(), - entity.Email, - entity.UserName, - entity.CreatedAt, - entity.DeletedAt - ); + return entity.MapToModel(); } protected override User MapToEntity(Domain.Models.User.User model) { - return new User - { - Id = new Guid(model.Id.IdValue), - Email = model.Email.EmailValue, - NormalizedEmail = model.Email.EmailValue.ToUpperInvariant(), - UserName = model.Username.UsernameValue, - NormalizedUserName = model.Username.UsernameValue.ToUpperInvariant(), - CreatedAt = model.CreatedAt.CreatedAtValue, - DeletedAt = model.DeletedAt.DeletedAtValue - }; + return model.MapToEntity(); } protected override void UpdateEntity(User entity, Domain.Models.User.User model) { - entity.Email = model.Email.EmailValue; - entity.NormalizedEmail = model.Email.EmailValue.ToUpperInvariant(); - entity.UserName = model.Username.UsernameValue; - entity.NormalizedUserName = model.Username.UsernameValue.ToUpperInvariant(); - entity.CreatedAt = model.CreatedAt.CreatedAtValue; - entity.DeletedAt = model.DeletedAt.DeletedAtValue; - } - - public async Task SignUpWithPasswordAsync(Domain.Models.User.User user, string password, CancellationToken cancellationToken = default) - { - var entity = MapToEntity(user); - - var result = await _userManager.CreateAsync(entity, password); - - if (!result.Succeeded) - { - var errors = string.Join(", ", result.Errors.Select(e => e.Description)); - throw new Exception($"User registration failed: {errors}"); - } - - await _userManager.AddToRoleAsync(entity, UserRole.User.Name); - } - - public async Task LoginWithEmailAndPasswordAsync(string email, string password, CancellationToken cancellationToken = default) - { - var entity = await _userManager.FindByEmailAsync(email); - - if (entity is null) - { - throw new ValidationException(UserErrors.LoginErrorUserNotFoundByEmail(email)); - } - - var isPasswordValid = await _userManager.CheckPasswordAsync(entity, password); - - if (!isPasswordValid) - { - throw new ValidationException(UserErrors.LoginErrorIncorrectPassword()); - } - - var roles = await _userManager.GetRolesAsync(entity); - - return new LoginResult(MapToModel(entity), roles); + entity.UpdateEntity(model); } } \ No newline at end of file diff --git a/dotnet/AipsWebApi/Controllers/UserController.cs b/dotnet/AipsWebApi/Controllers/UserController.cs index 1987388..746887f 100644 --- a/dotnet/AipsWebApi/Controllers/UserController.cs +++ b/dotnet/AipsWebApi/Controllers/UserController.cs @@ -1,5 +1,5 @@ using AipsCore.Application.Abstract; -using AipsCore.Application.Authentication; +using AipsCore.Application.Common.Authentication; using AipsCore.Application.Models.User.Command.LogIn; using AipsCore.Application.Models.User.Command.SignUp; using AipsCore.Application.Models.User.Query.GetUser; diff --git a/dotnet/AipsWebApi/Program.cs b/dotnet/AipsWebApi/Program.cs index 42ebe7c..3629922 100644 --- a/dotnet/AipsWebApi/Program.cs +++ b/dotnet/AipsWebApi/Program.cs @@ -16,10 +16,7 @@ builder.Services.AddAips(builder.Configuration); var app = builder.Build(); -using (var scope = app.Services.CreateScope()) -{ - await DbInitializer.SeedRolesAsync(scope.ServiceProvider); -} +await app.Services.InitializeInfrastructureAsync(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment())