From e5abcc94f97bde8553e33d7e8b5ef5fccf457120 Mon Sep 17 00:00:00 2001 From: Felipe Mattioli Date: Sun, 14 Jul 2024 05:43:14 -0300 Subject: [PATCH] FEAT: Adding new logic to store user attributes --- .../appsettings.Development.json | 17 +++--- .../Users/CreateUserCommandHandler.cs | 13 ++++- .../Commands/Users/LoginUserCommandHandler.cs | 2 +- .../Mappers/UserMapper.cs | 9 +-- .../Requests/User/AddUserRequest.cs | 2 +- .../Requests/User/AttributesRequest.cs | 4 -- .../Entities/Attributes.cs | 8 --- .../TokenManager.Domain/Entities/User.cs | 4 +- .../Interfaces/IUserRepository.cs | 2 +- .../Repositories/UserRepository.cs | 5 +- .../Handlers/CreateUserCommandHandlerTests.cs | 56 ++++++++++++++++--- 11 files changed, 77 insertions(+), 45 deletions(-) delete mode 100644 src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AttributesRequest.cs delete mode 100644 src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/Attributes.cs diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Api/appsettings.Development.json b/src/Feijuca.Keycloak.TokenManager/TokenManager.Api/appsettings.Development.json index 26404ae..3aa24f1 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Api/appsettings.Development.json +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Api/appsettings.Development.json @@ -3,14 +3,15 @@ "AuthSettings": { "Realms": [ { - "Name": "", - "Audience": "", - "Issuer": "" + "Name": "smartconsig", + "Audience": "smartconsig-api", + "Issuer": "https://services-keycloak.ul0sru.easypanel.host/realms/smartconsig" } ], - "ClientId": "", - "Resource": "", - "AuthServerUrl": "", + "ClientSecret": "qSGxtu0CFOmZ6Yzr5ntPK2iXppmKeerS", + "ClientId": "smartconsig-api", + "Resource": "smartconsig-api", + "AuthServerUrl": "https://services-keycloak.ul0sru.easypanel.host", "PolicyName": "TokenManager", "Roles": [ "TokenManager" @@ -19,6 +20,6 @@ "tokenmanager-write", "tokenmanager-read" ] - } + } } -} +} \ No newline at end of file diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs index 0f6d0fc..7db518a 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/CreateUserCommandHandler.cs @@ -13,11 +13,13 @@ public class CreateUserCommandHandler(IUserRepository userRepository) : IRequest public async Task Handle(CreateUserCommand request, CancellationToken cancellationToken) { + AddTenantToRequest(request); var accessTokenResult = await _userRepository.GetAccessTokenAsync(request.Tenant); if (accessTokenResult.IsSuccess) { var user = request.AddUserRequest.ToDomain(); - var(IsSuccessStatusCode, contentRequest) = await _userRepository.CreateNewUserAsync(user); + + var (IsSuccessStatusCode, contentRequest) = await _userRepository.CreateNewUserAsync(request.Tenant, user); if (IsSuccessStatusCode) { await SetUserPasswordAsync(user); @@ -25,10 +27,15 @@ public async Task Handle(CreateUserCommand request, CancellationToken ca } UserErrors.SetTechnicalMessage(contentRequest); - return Result.Failure(UserErrors.TokenGenerationError); + return Result.Failure(UserErrors.WrongPasswordDefinition); } - return Result.Failure(UserErrors.InvalidUserNameOrPasswordError); + return Result.Failure(UserErrors.TokenGenerationError); + } + + private static void AddTenantToRequest(CreateUserCommand request) + { + request.AddUserRequest.Attributes.Add("tenant", [request.Tenant]); } private async Task SetUserPasswordAsync(User user) diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/LoginUserCommandHandler.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/LoginUserCommandHandler.cs index 1e79aa5..736ee2f 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/LoginUserCommandHandler.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Commands/Users/LoginUserCommandHandler.cs @@ -12,7 +12,7 @@ public class LoginUserCommandHandler(IUserRepository userRepository) : IRequestH public async Task> Handle(LoginUserCommand request, CancellationToken cancellationToken) { - var user = request.LoginUser.ToDomain(request.Tenant); + var user = request.LoginUser.ToDomain(); var tokenDetailsResult = await _userRepository.LoginAsync(request.Tenant, user); if (tokenDetailsResult.IsSuccess) { diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs index 8bfea2b..6586588 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Mappers/UserMapper.cs @@ -7,15 +7,10 @@ namespace TokenManager.Application.Services.Mappers public static class UserMapper { public static User ToDomain(this AddUserRequest userRequest) - { - var attributes = userRequest.Attributes!.ToDomain(); - return new User(userRequest.Username!, userRequest.Password, userRequest.Email!, userRequest.FirstName!, userRequest.LastName!, attributes); + { + return new User(userRequest.Username!, userRequest.Password, userRequest.Email!, userRequest.FirstName!, userRequest.LastName!, userRequest.Attributes); } - public static Attributes ToDomain(this AttributesRequest attributes) - { - return new Attributes(attributes.Tenant, attributes.UserAttributes); - } public static User ToDomain(this LoginUserRequest loginUserRequest) { diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs index 76cc9df..d3ee000 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AddUserRequest.cs @@ -2,5 +2,5 @@ namespace TokenManager.Application.Services.Requests.User { - public record AddUserRequest(string Username, string Password, string Email, string FirstName, string LastName, AttributesRequest? Attributes); + public record AddUserRequest(string Username, string Password, string Email, string FirstName, string LastName, Dictionary Attributes); } diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AttributesRequest.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AttributesRequest.cs deleted file mode 100644 index da339d0..0000000 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Application.Services/Requests/User/AttributesRequest.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace TokenManager.Application.Services.Requests.User -{ - public record AttributesRequest(string Tenant, Dictionary UserAttributes); -} diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/Attributes.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/Attributes.cs deleted file mode 100644 index e413ae3..0000000 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/Attributes.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace TokenManager.Domain.Entities -{ - public class Attributes(string Tenant, Dictionary UserAttributes) - { - public Dictionary UserAttributes = UserAttributes; - public string? Tenant { get; set; } = Tenant; - } -} diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs index 260db5e..16b7479 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Entities/User.cs @@ -19,7 +19,7 @@ public class User public int NotBefore { get; set; } public long CreatedTimestamp { get; set; } public Access? Access { get; set; } - public Attributes? Attributes { get; set; } + public Dictionary? Attributes { get; set; } public User() { @@ -32,7 +32,7 @@ public User(string userName, string password) Password = password; } - public User(string userName, string password, string email, string firstName, string lastName, Attributes attributes) + public User(string userName, string password, string email, string firstName, string lastName, Dictionary attributes) { Enabled = true; EmailVerified = true; diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs index 3d4ed54..4ec2993 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Domain/Interfaces/IUserRepository.cs @@ -7,7 +7,7 @@ public interface IUserRepository { Task> GetAccessTokenAsync(string tenant); Task> LoginAsync(string tenant, User user); - Task<(bool result, string content)> CreateNewUserAsync(User user); + Task<(bool result, string content)> CreateNewUserAsync(string tenant, User user); Task> GetUserAsync(string userName); Task ResetPasswordAsync(string userId, string password); Task SendEmailVerificationAsync(string userId); diff --git a/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs b/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs index bb9b4eb..9f198a2 100644 --- a/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs +++ b/src/Feijuca.Keycloak.TokenManager/TokenManager.Infra.Data/Repositories/UserRepository.cs @@ -1,5 +1,4 @@ using Contracts.Common; -using Feijuca.Keycloak.MultiTenancy.Services; using Flurl; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -63,9 +62,9 @@ public async Task> GetAccessTokenAsync(string tenant) return Result.Failure(UserErrors.TokenGenerationError); } - public async Task<(bool result, string content)> CreateNewUserAsync(User user) + public async Task<(bool result, string content)> CreateNewUserAsync(string tenant, User user) { - SetBaseUrlUserAction(user!.Attributes!.Tenant!); + SetBaseUrlUserAction(tenant); var json = JsonConvert.SerializeObject(user, Settings); var httpContent = new StringContent(json, Encoding.UTF8, "application/json"); diff --git a/tests/TokenManager.UnitTests/Handlers/CreateUserCommandHandlerTests.cs b/tests/TokenManager.UnitTests/Handlers/CreateUserCommandHandlerTests.cs index aba8c8a..1ff055d 100644 --- a/tests/TokenManager.UnitTests/Handlers/CreateUserCommandHandlerTests.cs +++ b/tests/TokenManager.UnitTests/Handlers/CreateUserCommandHandlerTests.cs @@ -35,7 +35,7 @@ public async Task HandleWhenInformAValidUser_ShouldCreateANewUserAndReturnsTrue( (bool result, string content) createUserResult = (true, "Success message"); _userRepositoryMock - .Setup(x => x.CreateNewUserAsync(It.IsAny())) + .Setup(x => x.CreateNewUserAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(createUserResult); var user = _autoFixture.Create(); @@ -53,26 +53,37 @@ public async Task HandleWhenInformAValidUser_ShouldCreateANewUserAndReturnsTrue( var resultHandle = await _createUserCommandHandler.Handle(createUserCommand, CancellationToken.None); //Assert - _userRepositoryMock - .Verify(x => x.GetAccessTokenAsync(It.IsAny()), Times.Once); - resultHandle.IsSuccess .Should() .Be(true); + + _userRepositoryMock + .Verify(x => x.GetAccessTokenAsync(It.IsAny()), Times.Once); + + _userRepositoryMock + .Verify(x => x.CreateNewUserAsync(It.IsAny(), It.IsAny()), Times.Once); + + _userRepositoryMock + .Verify(x => x.GetUserAsync(It.IsAny()), Times.Once); + + _userRepositoryMock + .Verify(x => x.ResetPasswordAsync(It.IsAny(), It.IsAny()), Times.Once); + + _userRepositoryMock.VerifyNoOtherCalls(); } [Fact] - public async Task HandleWhenInformAInvalidUser_ShouldNotCreateANewUserAndReturnsFalse() + public async Task HandleWhenInformAInvalidConfig_ShouldNotCreateANewUserAndReturnsFalse() { // Arrange var createUserCommand = _autoFixture.Create(); var errorMessage = _autoFixture.Create(); UserErrors.SetTechnicalMessage(errorMessage); - var result = Result.Failure(UserErrors.TokenGenerationError); + var result = Result.Failure(UserErrors.TokenGenerationError); _userRepositoryMock - .Setup(x => x.GetAccessTokenAsync(It.IsAny()), Times.Once) + .Setup(x => x.GetAccessTokenAsync(It.IsAny())) .ReturnsAsync(result); //Act @@ -91,5 +102,36 @@ public async Task HandleWhenInformAInvalidUser_ShouldNotCreateANewUserAndReturns .Should() .Contain(result.Error.Code); } + + [Fact] + public async Task HandleWhenInformAInvalidUser_ShouldNotCreateANewUserAndReturnsFalse() + { + // Arrange + var createUserCommand = _autoFixture.Create(); + + var errorMessage = _autoFixture.Create(); + UserErrors.SetTechnicalMessage(errorMessage); + var successResult = Result.Success(_autoFixture.Create()); + + + _userRepositoryMock + .Setup(x => x.GetAccessTokenAsync(It.IsAny())) + .ReturnsAsync(successResult); + + _userRepositoryMock + .Setup(x => x.CreateNewUserAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync((false, "Some error")); + //Act + var resultHandle = await _createUserCommandHandler.Handle(createUserCommand, CancellationToken.None); + + //Assert + resultHandle.IsSuccess + .Should() + .Be(false); + + resultHandle.Error.Description + .Should() + .Contain(UserErrors.WrongPasswordDefinition.Description); + } } }