From 4488b680f9f8a87f54dbefdbfa9e08a4ab6cc2a4 Mon Sep 17 00:00:00 2001 From: WarriorsSami Date: Thu, 20 Jun 2024 23:11:35 +0300 Subject: [PATCH] fix(quetzalcoatl): configure testing environment for ci pipeline --- .github/workflows/quetzalcoatl-auth-ci.yaml | 2 +- quetzalcoatl-auth/.env.template | 3 +- .../Api/Features/Auth/Login/Endpoint.cs | 54 ++-- .../Features/Auth/RefreshToken/Endpoint.cs | 14 +- .../Api/Features/Auth/RefreshToken/Mappers.cs | 2 +- .../Api/Features/Auth/Register/Endpoint.cs | 54 ++-- .../Api/Features/Auth/Register/Validators.cs | 23 +- .../Core/ApplicationUserExtensions.cs | 6 +- .../Api/Features/Core/JwtExtensions.cs | 8 +- .../Api/Features/Core/LinqExtensions.cs | 2 +- .../Api/Features/Users/Delete/Endpoint.cs | 3 +- .../Api/Features/Users/Get/Mappers.cs | 6 +- .../Api/Features/Users/GetAll/Endpoint.cs | 28 ++- .../Api/Features/Users/GetAll/Mappers.cs | 6 +- .../Api/Features/Users/Roles/Add/Endpoint.cs | 24 +- .../Api/Features/Users/Roles/Add/Models.cs | 2 +- .../Api/Features/Users/Roles/Add/Summary.cs | 8 +- .../Features/Users/Roles/Remove/Endpoint.cs | 24 +- .../Api/Features/Users/Roles/Remove/Models.cs | 2 +- .../Features/Users/Roles/Remove/Summary.cs | 8 +- .../Api/Features/Users/Update/Endpoint.cs | 7 +- .../Api/Features/Users/Update/Mappers.cs | 6 +- .../Api/Features/Users/Update/Validators.cs | 15 +- quetzalcoatl-auth/Api/Usings.cs | 2 +- .../Features/Users/CreateUser/Handler.cs | 3 +- .../Bootstrapper/Bootstrapper.csproj | 1 + .../Extensions/ServiceCollectionExtensions.cs | 4 +- quetzalcoatl-auth/Bootstrapper/Program.cs | 78 ++---- .../Domain/Consts/ApplicationUserConsts.cs | 9 +- .../Domain/Consts/SystemConsts.cs | 2 +- .../Infrastructure/ApplicationDbContext.cs | 3 +- ...230315154826_AddGuidAsPKForIdentityUser.cs | 232 ++++++++---------- ...319104138_AddProfileImageToIdentityUser.cs | 13 +- .../20230509173144_AddRefreshTokenEntity.cs | 15 +- ...20230525105824_UpdateRefreshTokenEntity.cs | 19 +- ..._RemoveRedundantFieldsFromRefreshTokens.cs | 28 +-- .../Triggers/DeleteStaleRefreshTokens.cs | 4 +- .../Features/Auth/RegisterEndpointTests.cs | 50 ++-- .../Api/Features/Users/DeleteEndpointTests.cs | 12 +- .../Api/Features/Users/UpdateEndpointTests.cs | 70 +++--- 40 files changed, 425 insertions(+), 427 deletions(-) diff --git a/.github/workflows/quetzalcoatl-auth-ci.yaml b/.github/workflows/quetzalcoatl-auth-ci.yaml index 4ac5551..8435d7b 100644 --- a/.github/workflows/quetzalcoatl-auth-ci.yaml +++ b/.github/workflows/quetzalcoatl-auth-ci.yaml @@ -23,4 +23,4 @@ jobs: cd quetzalcoatl-auth dotnet build --no-restore - name: Test - run: dotnet test "quetzalcoatl-auth/Tests.Integration/Tests.Integration.csproj" \ No newline at end of file + run: dotnet test -e ASPNETCORE_ENVIRONMENT=Testing "quetzalcoatl-auth/Tests.Integration/Tests.Integration.csproj" \ No newline at end of file diff --git a/quetzalcoatl-auth/.env.template b/quetzalcoatl-auth/.env.template index 55b0184..18cee60 100644 --- a/quetzalcoatl-auth/.env.template +++ b/quetzalcoatl-auth/.env.template @@ -1,5 +1,6 @@ -ASPNETCORE_ENVIRONMENT=Development +ASPNETCORE_ENVIRONMENT=Testing ASPNETCORE_URLS=http://+:5210 +DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false JwtConfig__SecretKey="z7F+ut_aphaxeja0&ba*p9spew!4fe0rAFRO5HestitIKOv5nistlz3b=+edu1aP" JwtConfig__JwtAccessTokenLifeTime="0:01:00:00.0" diff --git a/quetzalcoatl-auth/Api/Features/Auth/Login/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Auth/Login/Endpoint.cs index 31d2cbe..4862a4d 100644 --- a/quetzalcoatl-auth/Api/Features/Auth/Login/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Auth/Login/Endpoint.cs @@ -47,39 +47,29 @@ public override async Task HandleAsync(LoginUserRequest req, CancellationToken c } ); - HttpContext - .Response - .Cookies - .Append( - CookieAuthenticationDefaults.CookiePrefix + "AccessToken", - tokenResponse.AccessToken, - new CookieOptions - { - HttpOnly = true, - SameSite = SameSiteMode.None, - Secure = true, - Expires = DateTimeOffset - .UtcNow - .AddTicks(_jwtConfig.JwtAccessTokenLifetime.Ticks) - } - ); + HttpContext.Response.Cookies.Append( + CookieAuthenticationDefaults.CookiePrefix + "AccessToken", + tokenResponse.AccessToken, + new CookieOptions + { + HttpOnly = true, + SameSite = SameSiteMode.None, + Secure = true, + Expires = DateTimeOffset.UtcNow.AddTicks(_jwtConfig.JwtAccessTokenLifetime.Ticks) + } + ); - HttpContext - .Response - .Cookies - .Append( - CookieAuthenticationDefaults.CookiePrefix + "RefreshToken", - tokenResponse.RefreshToken, - new CookieOptions - { - HttpOnly = true, - SameSite = SameSiteMode.None, - Secure = true, - Expires = DateTimeOffset - .UtcNow - .AddTicks(_jwtConfig.JwtRefreshTokenLifetime.Ticks) - } - ); + HttpContext.Response.Cookies.Append( + CookieAuthenticationDefaults.CookiePrefix + "RefreshToken", + tokenResponse.RefreshToken, + new CookieOptions + { + HttpOnly = true, + SameSite = SameSiteMode.None, + Secure = true, + Expires = DateTimeOffset.UtcNow.AddTicks(_jwtConfig.JwtRefreshTokenLifetime.Ticks) + } + ); await SendOkAsync( response: new UserTokenResponse diff --git a/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Endpoint.cs index b442c3c..827acd7 100644 --- a/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Endpoint.cs @@ -60,16 +60,12 @@ public override async Task PersistTokenAsync(UserTokenResponse response) public override async Task RefreshRequestValidationAsync(UserTokenRequest req) { - _logger.LogInformation( - "Validating the refresh token for user {UserId}", - req.UserId - ); + _logger.LogInformation("Validating the refresh token for user {UserId}", req.UserId); - var storedRefreshToken = await _tokenRepository.GetRefreshTokenAsync( - rt => - rt.Token == Guid.Parse(req.RefreshToken) - && rt.UserId == Guid.Parse(req.UserId) - && rt.ExpiryDate > DateTime.UtcNow + var storedRefreshToken = await _tokenRepository.GetRefreshTokenAsync(rt => + rt.Token == Guid.Parse(req.RefreshToken) + && rt.UserId == Guid.Parse(req.UserId) + && rt.ExpiryDate > DateTime.UtcNow ); if (storedRefreshToken is null) diff --git a/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Mappers.cs b/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Mappers.cs index f320b07..308d885 100644 --- a/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Mappers.cs +++ b/quetzalcoatl-auth/Api/Features/Auth/RefreshToken/Mappers.cs @@ -10,4 +10,4 @@ public UserTokenResponseToRefreshTokenEntityProfile() .ForMember(dest => dest.ExpiryDate, opt => opt.MapFrom(src => src.RefreshExpiry)) .ForMember(dest => dest.CreationDate, opt => opt.MapFrom(_ => DateTime.UtcNow)); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Auth/Register/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Auth/Register/Endpoint.cs index d614939..979cb33 100644 --- a/quetzalcoatl-auth/Api/Features/Auth/Register/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Auth/Register/Endpoint.cs @@ -46,39 +46,29 @@ public override async Task HandleAsync(RegisterUserRequest req, CancellationToke } ); - HttpContext - .Response - .Cookies - .Append( - CookieAuthenticationDefaults.CookiePrefix + "AccessToken", - tokenResponse.AccessToken, - new CookieOptions - { - HttpOnly = true, - SameSite = SameSiteMode.None, - Secure = true, - Expires = DateTimeOffset - .UtcNow - .AddTicks(_jwtConfig.JwtAccessTokenLifetime.Ticks) - } - ); + HttpContext.Response.Cookies.Append( + CookieAuthenticationDefaults.CookiePrefix + "AccessToken", + tokenResponse.AccessToken, + new CookieOptions + { + HttpOnly = true, + SameSite = SameSiteMode.None, + Secure = true, + Expires = DateTimeOffset.UtcNow.AddTicks(_jwtConfig.JwtAccessTokenLifetime.Ticks) + } + ); - HttpContext - .Response - .Cookies - .Append( - CookieAuthenticationDefaults.CookiePrefix + "RefreshToken", - tokenResponse.RefreshToken, - new CookieOptions - { - HttpOnly = true, - SameSite = SameSiteMode.None, - Secure = true, - Expires = DateTimeOffset - .UtcNow - .AddTicks(_jwtConfig.JwtRefreshTokenLifetime.Ticks) - } - ); + HttpContext.Response.Cookies.Append( + CookieAuthenticationDefaults.CookiePrefix + "RefreshToken", + tokenResponse.RefreshToken, + new CookieOptions + { + HttpOnly = true, + SameSite = SameSiteMode.None, + Secure = true, + Expires = DateTimeOffset.UtcNow.AddTicks(_jwtConfig.JwtRefreshTokenLifetime.Ticks) + } + ); await SendCreatedAtAsync( endpointName: $"/api/users/{user.Id}", diff --git a/quetzalcoatl-auth/Api/Features/Auth/Register/Validators.cs b/quetzalcoatl-auth/Api/Features/Auth/Register/Validators.cs index 23fdae4..2c3d3b4 100644 --- a/quetzalcoatl-auth/Api/Features/Auth/Register/Validators.cs +++ b/quetzalcoatl-auth/Api/Features/Auth/Register/Validators.cs @@ -8,7 +8,9 @@ public Validator() .NotEmpty() .WithMessage("Username is required") .MinimumLength(ApplicationUserConsts.UsernameMinLength) - .WithMessage($"Username must be at least {ApplicationUserConsts.UsernameMinLength} characters long"); + .WithMessage( + $"Username must be at least {ApplicationUserConsts.UsernameMinLength} characters long" + ); RuleFor(x => x.Email) .NotEmpty() @@ -20,9 +22,13 @@ public Validator() .NotEmpty() .WithMessage("Password is required") .MinimumLength(ApplicationUserConsts.PasswordMinLength) - .WithMessage($"Password must be at least {ApplicationUserConsts.PasswordMinLength} characters long") + .WithMessage( + $"Password must be at least {ApplicationUserConsts.PasswordMinLength} characters long" + ) .MaximumLength(ApplicationUserConsts.PasswordMaxLength) - .WithMessage($"Password must be at most {ApplicationUserConsts.PasswordMaxLength} characters long") + .WithMessage( + $"Password must be at most {ApplicationUserConsts.PasswordMaxLength} characters long" + ) .Matches(ApplicationUserConsts.PasswordRegex) .WithMessage( "Password must contain at least one uppercase letter, one lowercase letter, one number and one special character" @@ -30,12 +36,16 @@ public Validator() RuleFor(x => x.Fullname) .MaximumLength(ApplicationUserConsts.FullnameMaxLength) - .WithMessage($"Fullname must be at most {ApplicationUserConsts.FullnameMaxLength} characters long") + .WithMessage( + $"Fullname must be at most {ApplicationUserConsts.FullnameMaxLength} characters long" + ) .When(x => !string.IsNullOrWhiteSpace(x.Fullname)); RuleFor(x => x.Bio) .MaximumLength(ApplicationUserConsts.BioMaxLength) - .WithMessage($"Bio must be at most {ApplicationUserConsts.BioMaxLength} characters long") + .WithMessage( + $"Bio must be at most {ApplicationUserConsts.BioMaxLength} characters long" + ) .When(x => !string.IsNullOrWhiteSpace(x.Bio)); RuleFor(x => x.ProfilePicture) @@ -47,7 +57,8 @@ public Validator() .When(x => x.ProfilePicture is not null); } - private static bool IsAllowedSize(long length) => length <= ApplicationUserConsts.ProfilePictureMaxLength; + private static bool IsAllowedSize(long length) => + length <= ApplicationUserConsts.ProfilePictureMaxLength; private static bool IsAllowedType(string contentType) => ApplicationUserConsts.AllowedProfilePictureTypes.Contains(contentType); diff --git a/quetzalcoatl-auth/Api/Features/Core/ApplicationUserExtensions.cs b/quetzalcoatl-auth/Api/Features/Core/ApplicationUserExtensions.cs index 06df51b..3df1d9f 100644 --- a/quetzalcoatl-auth/Api/Features/Core/ApplicationUserExtensions.cs +++ b/quetzalcoatl-auth/Api/Features/Core/ApplicationUserExtensions.cs @@ -26,7 +26,7 @@ public static class SortUsersByExtensions public static IEnumerable SortUsers( this IEnumerable query, SortUsersBy sortBy - ) + ) { return sortBy switch { @@ -39,7 +39,7 @@ SortUsersBy sortBy public static IAsyncEnumerable SortUsers( this IAsyncEnumerable query, SortUsersBy sortBy - ) + ) { return sortBy switch { @@ -48,4 +48,4 @@ SortUsersBy sortBy _ => query.OrderBy(user => user.Username), }; } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Core/JwtExtensions.cs b/quetzalcoatl-auth/Api/Features/Core/JwtExtensions.cs index 7da210d..2a2d4fe 100644 --- a/quetzalcoatl-auth/Api/Features/Core/JwtExtensions.cs +++ b/quetzalcoatl-auth/Api/Features/Core/JwtExtensions.cs @@ -26,9 +26,9 @@ out var validatedToken private static bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken) { return (validatedToken is JwtSecurityToken jwtSecurityToken) - && jwtSecurityToken - .Header - .Alg - .Equals(SecurityAlgorithms.HmacSha256, StringComparison.InvariantCultureIgnoreCase); + && jwtSecurityToken.Header.Alg.Equals( + SecurityAlgorithms.HmacSha256, + StringComparison.InvariantCultureIgnoreCase + ); } } diff --git a/quetzalcoatl-auth/Api/Features/Core/LinqExtensions.cs b/quetzalcoatl-auth/Api/Features/Core/LinqExtensions.cs index 956b458..3e9c799 100644 --- a/quetzalcoatl-auth/Api/Features/Core/LinqExtensions.cs +++ b/quetzalcoatl-auth/Api/Features/Core/LinqExtensions.cs @@ -11,4 +11,4 @@ public static IAsyncEnumerable Paginate(IAsyncEnumerable query, int pag { return query.Skip((page - 1) * pageSize).Take(pageSize); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Delete/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Users/Delete/Endpoint.cs index 85c7d48..336e67c 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Delete/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Delete/Endpoint.cs @@ -39,8 +39,7 @@ public override async Task HandleAsync(DeleteUserRequest req, CancellationToken if (!result.Succeeded) { var errors = result - .Errors - .Select(e => e.Description) + .Errors.Select(e => e.Description) .Aggregate("Identity Errors: ", (a, b) => $"{a}, {b}"); _logger.LogWarning( diff --git a/quetzalcoatl-auth/Api/Features/Users/Get/Mappers.cs b/quetzalcoatl-auth/Api/Features/Users/Get/Mappers.cs index 146169f..7a190b9 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Get/Mappers.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Get/Mappers.cs @@ -27,9 +27,9 @@ public ApplicationUserToGetUserResponseProfile() .ForMember( dest => dest.ProfilePictureId, opt => - opt.MapFrom( - src => src.ProfilePicture != null ? src.ProfilePicture!.Id : null - ) + opt.MapFrom(src => + src.ProfilePicture != null ? src.ProfilePicture!.Id : null + ) ); } } diff --git a/quetzalcoatl-auth/Api/Features/Users/GetAll/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Users/GetAll/Endpoint.cs index ff3175d..74db2d7 100644 --- a/quetzalcoatl-auth/Api/Features/Users/GetAll/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Users/GetAll/Endpoint.cs @@ -29,20 +29,23 @@ public override async Task HandleAsync(GetAllUsersRequest req, CancellationToken { _logger.LogInformation("Getting all users"); - var users = _userManager.Users.AsAsyncEnumerable().SelectAwait(async user => - { - var userDto = _mapper.Map(user); - userDto.Roles = await _userManager.GetRolesAsync(user); - return userDto; - }).AsAsyncEnumerable(); - + var users = _userManager + .Users.AsAsyncEnumerable() + .SelectAwait(async user => + { + var userDto = _mapper.Map(user); + userDto.Roles = await _userManager.GetRolesAsync(user); + return userDto; + }) + .AsAsyncEnumerable(); + if (!string.IsNullOrWhiteSpace(req.Username)) { users = users.Where(user => user.Username.Contains(req.Username)); } var totalCount = await users.CountAsync(cancellationToken: ct); - + if (req.SortBy is not null) { users = users.SortUsers(req.SortBy.Value); @@ -50,6 +53,13 @@ public override async Task HandleAsync(GetAllUsersRequest req, CancellationToken users = LinqExtensions.Paginate(users, req.Page ?? 1, req.PageSize ?? 10); - await SendOkAsync(response: new GetAllUsersResponse { Users = users.ToEnumerable(), TotalCount = totalCount }, ct); + await SendOkAsync( + response: new GetAllUsersResponse + { + Users = users.ToEnumerable(), + TotalCount = totalCount + }, + ct + ); } } diff --git a/quetzalcoatl-auth/Api/Features/Users/GetAll/Mappers.cs b/quetzalcoatl-auth/Api/Features/Users/GetAll/Mappers.cs index c7d6681..d961313 100644 --- a/quetzalcoatl-auth/Api/Features/Users/GetAll/Mappers.cs +++ b/quetzalcoatl-auth/Api/Features/Users/GetAll/Mappers.cs @@ -27,9 +27,9 @@ public ApplicationUserToUserDtoProfile() .ForMember( dest => dest.ProfilePictureId, opt => - opt.MapFrom( - src => src.ProfilePicture != null ? src.ProfilePicture!.Id : null - ) + opt.MapFrom(src => + src.ProfilePicture != null ? src.ProfilePicture!.Id : null + ) ); } } diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Endpoint.cs index 5496750..d794467 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Endpoint.cs @@ -26,7 +26,11 @@ public override void Configure() public override async Task HandleAsync(AddRoleRequest req, CancellationToken ct) { - _logger.LogInformation("Adding role {Role} to user with id {Id}", req.Role.ToString(), req.Id.ToString()); + _logger.LogInformation( + "Adding role {Role} to user with id {Id}", + req.Role.ToString(), + req.Id.ToString() + ); if (!Enum.TryParse(req.Role, out var role)) { @@ -47,7 +51,11 @@ public override async Task HandleAsync(AddRoleRequest req, CancellationToken ct) if (await _userManager.IsInRoleAsync(user, role.ToString())) { - _logger.LogWarning("User with id {Id} already has role {Role}", req.Id.ToString(), role.ToString()); + _logger.LogWarning( + "User with id {Id} already has role {Role}", + req.Id.ToString(), + role.ToString() + ); var errors = $"User with id {req.Id.ToString()} already has role {role}"; AddError(errors); } @@ -58,11 +66,15 @@ public override async Task HandleAsync(AddRoleRequest req, CancellationToken ct) if (!result.Succeeded) { var errors = result - .Errors - .Select(e => e.Description) + .Errors.Select(e => e.Description) .Aggregate("Identity Errors: ", (a, b) => $"{a}, {b}"); - _logger.LogWarning("Failed to add role {Role} to user with id {Id}: {errors}", role.ToString(), req.Id.ToString(), errors); + _logger.LogWarning( + "Failed to add role {Role} to user with id {Id}: {errors}", + role.ToString(), + req.Id.ToString(), + errors + ); AddError(errors); } ThrowIfAnyErrors(); @@ -73,4 +85,4 @@ public override async Task HandleAsync(AddRoleRequest req, CancellationToken ct) await SendOkAsync(response, ct); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Models.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Models.cs index 1b6dfb8..1cd64a0 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Models.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Models.cs @@ -6,4 +6,4 @@ public class AddRoleRequest public Guid Id { get; set; } = Guid.Empty; public string Role { get; set; } = string.Empty; -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Summary.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Summary.cs index 5ee06b9..cb0ad30 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Summary.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Add/Summary.cs @@ -6,7 +6,11 @@ public AddRoleSummary() { Summary = "Add a role to a user"; Description = "Add a role to a user by id"; - ExampleRequest = new AddRoleRequest { Id = Guid.NewGuid(), Role = ApplicationRole.Proposer.ToString() }; + ExampleRequest = new AddRoleRequest + { + Id = Guid.NewGuid(), + Role = ApplicationRole.Proposer.ToString() + }; Response( 200, "Role added successfully", @@ -25,4 +29,4 @@ public AddRoleSummary() Response(401, "Unauthorized access"); Response(500, "Internal server error"); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Endpoint.cs index 0c9d832..c9250e6 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Endpoint.cs @@ -26,7 +26,11 @@ public override void Configure() public override async Task HandleAsync(RemoveRoleRequest req, CancellationToken ct) { - _logger.LogInformation("Removing role {Role} from user with id {Id}", req.Role.ToString(), req.Id.ToString()); + _logger.LogInformation( + "Removing role {Role} from user with id {Id}", + req.Role.ToString(), + req.Id.ToString() + ); if (!Enum.TryParse(req.Role, out var role)) { @@ -47,7 +51,11 @@ public override async Task HandleAsync(RemoveRoleRequest req, CancellationToken if (!await _userManager.IsInRoleAsync(user, role.ToString())) { - _logger.LogWarning("User with id {Id} does not have role {Role}", req.Id.ToString(), role.ToString()); + _logger.LogWarning( + "User with id {Id} does not have role {Role}", + req.Id.ToString(), + role.ToString() + ); var errors = $"User with id {req.Id.ToString()} does not have role {role}"; AddError(errors); } @@ -58,11 +66,15 @@ public override async Task HandleAsync(RemoveRoleRequest req, CancellationToken if (!result.Succeeded) { var errors = result - .Errors - .Select(e => e.Description) + .Errors.Select(e => e.Description) .Aggregate("Identity Errors: ", (a, b) => $"{a}, {b}"); - _logger.LogWarning("Failed to remove role {Role} from user with id {Id}: {errors}", role.ToString(), req.Id.ToString(), errors); + _logger.LogWarning( + "Failed to remove role {Role} from user with id {Id}: {errors}", + role.ToString(), + req.Id.ToString(), + errors + ); AddError(errors); } ThrowIfAnyErrors(); @@ -73,4 +85,4 @@ public override async Task HandleAsync(RemoveRoleRequest req, CancellationToken await SendOkAsync(response, ct); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Models.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Models.cs index 1380e28..227bed2 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Models.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Models.cs @@ -6,4 +6,4 @@ public class RemoveRoleRequest public Guid Id { get; set; } = Guid.Empty; public string Role { get; set; } = string.Empty; -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Summary.cs b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Summary.cs index 23e5d85..c0e34a7 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Summary.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Roles/Remove/Summary.cs @@ -6,7 +6,11 @@ public RemoveRoleSummary() { Summary = "Remove a role from a user"; Description = "Remove a role from a user by id"; - ExampleRequest = new RemoveRoleRequest { Id = Guid.NewGuid(), Role = ApplicationRole.Proposer.ToString() }; + ExampleRequest = new RemoveRoleRequest + { + Id = Guid.NewGuid(), + Role = ApplicationRole.Proposer.ToString() + }; Response( 200, "Role removed successfully", @@ -25,4 +29,4 @@ public RemoveRoleSummary() Response(401, "Unauthorized access"); Response(500, "Internal server error"); } -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Api/Features/Users/Update/Endpoint.cs b/quetzalcoatl-auth/Api/Features/Users/Update/Endpoint.cs index 89d6473..5c93f03 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Update/Endpoint.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Update/Endpoint.cs @@ -28,8 +28,8 @@ public override async Task HandleAsync(UpdateUserRequest req, CancellationToken { _logger.LogInformation("Updating user with id {Id}", req.Id.ToString()); - var subClaim = User.Claims - .Where(c => c.Type == ClaimTypes.NameIdentifier) + var subClaim = User + .Claims.Where(c => c.Type == ClaimTypes.NameIdentifier) .Select(c => c.Value) .FirstOrDefault(); @@ -52,8 +52,7 @@ public override async Task HandleAsync(UpdateUserRequest req, CancellationToken if (!result.Succeeded) { var errors = result - .Errors - .Select(e => e.Description) + .Errors.Select(e => e.Description) .Aggregate("Identity Errors: ", (a, b) => $"{a}, {b}"); _logger.LogWarning( diff --git a/quetzalcoatl-auth/Api/Features/Users/Update/Mappers.cs b/quetzalcoatl-auth/Api/Features/Users/Update/Mappers.cs index 2dae3e8..a176cc1 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Update/Mappers.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Update/Mappers.cs @@ -61,9 +61,9 @@ public ApplicationUserToUpdateUserResponseProfile() .ForMember( dest => dest.ProfilePictureId, opt => - opt.MapFrom( - src => src.ProfilePicture != null ? src.ProfilePicture!.Id : null - ) + opt.MapFrom(src => + src.ProfilePicture != null ? src.ProfilePicture!.Id : null + ) ); } } diff --git a/quetzalcoatl-auth/Api/Features/Users/Update/Validators.cs b/quetzalcoatl-auth/Api/Features/Users/Update/Validators.cs index 44614df..f6bf1bb 100644 --- a/quetzalcoatl-auth/Api/Features/Users/Update/Validators.cs +++ b/quetzalcoatl-auth/Api/Features/Users/Update/Validators.cs @@ -8,7 +8,9 @@ public Validator() RuleFor(x => x.Username) .MinimumLength(ApplicationUserConsts.UsernameMinLength) - .WithMessage($"Username must be at least {ApplicationUserConsts.UsernameMinLength} characters long") + .WithMessage( + $"Username must be at least {ApplicationUserConsts.UsernameMinLength} characters long" + ) .When(x => !x.Username.IsNullOrEmpty()); RuleFor(x => x.Email) @@ -18,12 +20,16 @@ public Validator() RuleFor(x => x.Fullname) .MaximumLength(ApplicationUserConsts.FullnameMaxLength) - .WithMessage($"Fullname must be at most {ApplicationUserConsts.FullnameMaxLength} characters long") + .WithMessage( + $"Fullname must be at most {ApplicationUserConsts.FullnameMaxLength} characters long" + ) .When(x => !string.IsNullOrWhiteSpace(x.Fullname)); RuleFor(x => x.Bio) .MaximumLength(ApplicationUserConsts.BioMaxLength) - .WithMessage($"Bio must be at most {ApplicationUserConsts.BioMaxLength} characters long") + .WithMessage( + $"Bio must be at most {ApplicationUserConsts.BioMaxLength} characters long" + ) .When(x => !string.IsNullOrWhiteSpace(x.Bio)); RuleFor(x => x.ProfilePicture) @@ -35,7 +41,8 @@ public Validator() .When(x => x.ProfilePicture is not null); } - private static bool IsAllowedSize(long length) => length <= ApplicationUserConsts.ProfilePictureMaxLength; + private static bool IsAllowedSize(long length) => + length <= ApplicationUserConsts.ProfilePictureMaxLength; private static bool IsAllowedType(string contentType) => ApplicationUserConsts.AllowedProfilePictureTypes.Contains(contentType); diff --git a/quetzalcoatl-auth/Api/Usings.cs b/quetzalcoatl-auth/Api/Usings.cs index cd5ed68..02b0b22 100644 --- a/quetzalcoatl-auth/Api/Usings.cs +++ b/quetzalcoatl-auth/Api/Usings.cs @@ -7,6 +7,7 @@ global using Application.Features.Users.ValidateUserCredentials; global using AutoMapper; global using Domain.Configs; +global using Domain.Consts; global using Domain.Entities; global using Domain.Interfaces; global using FastEndpoints; @@ -21,4 +22,3 @@ global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Tokens; global using IMapper = AutoMapper.IMapper; -global using Domain.Consts; diff --git a/quetzalcoatl-auth/Application/Features/Users/CreateUser/Handler.cs b/quetzalcoatl-auth/Application/Features/Users/CreateUser/Handler.cs index b82cedd..798d22d 100644 --- a/quetzalcoatl-auth/Application/Features/Users/CreateUser/Handler.cs +++ b/quetzalcoatl-auth/Application/Features/Users/CreateUser/Handler.cs @@ -30,8 +30,7 @@ public override async Task ExecuteAsync( if (!result.Succeeded) { var errors = result - .Errors - .Select(e => e.Description) + .Errors.Select(e => e.Description) .Aggregate("Identity Errors: ", (a, b) => $"{a}, {b}"); _logger.LogError( diff --git a/quetzalcoatl-auth/Bootstrapper/Bootstrapper.csproj b/quetzalcoatl-auth/Bootstrapper/Bootstrapper.csproj index e8d2536..315916c 100644 --- a/quetzalcoatl-auth/Bootstrapper/Bootstrapper.csproj +++ b/quetzalcoatl-auth/Bootstrapper/Bootstrapper.csproj @@ -27,6 +27,7 @@ all + diff --git a/quetzalcoatl-auth/Bootstrapper/Extensions/ServiceCollectionExtensions.cs b/quetzalcoatl-auth/Bootstrapper/Extensions/ServiceCollectionExtensions.cs index d899933..6c21087 100644 --- a/quetzalcoatl-auth/Bootstrapper/Extensions/ServiceCollectionExtensions.cs +++ b/quetzalcoatl-auth/Bootstrapper/Extensions/ServiceCollectionExtensions.cs @@ -5,8 +5,8 @@ public static class ServiceCollectionExtensions public static void RemoveDbContext(this IServiceCollection services) where T : DbContext { - var descriptor = services.SingleOrDefault( - d => d.ServiceType == typeof(DbContextOptions) + var descriptor = services.SingleOrDefault(d => + d.ServiceType == typeof(DbContextOptions) ); if (descriptor != null) services.Remove(descriptor); diff --git a/quetzalcoatl-auth/Bootstrapper/Program.cs b/quetzalcoatl-auth/Bootstrapper/Program.cs index add1d9b..7bfacc7 100644 --- a/quetzalcoatl-auth/Bootstrapper/Program.cs +++ b/quetzalcoatl-auth/Bootstrapper/Program.cs @@ -3,12 +3,9 @@ using DotNetEnv.Configuration; Log.Logger = new LoggerConfiguration() - .MinimumLevel - .Override("Microsoft", LogEventLevel.Information) - .Enrich - .FromLogContext() - .WriteTo - .Console() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() .CreateLogger(); try @@ -19,11 +16,9 @@ if (builder.Environment.IsEnvironment(SystemConsts.TestingEnvironment)) { - _ = builder.Configuration - .AddDotNetEnv(".env.template", LoadOptions.TraversePath()) - .Build(); + _ = builder.Configuration.AddDotNetEnv(".env.template", LoadOptions.TraversePath()).Build(); } - + builder.Services.Configure(builder.Configuration.GetSection(nameof(JwtConfig))); builder.Services.Configure(builder.Configuration.GetSection(nameof(AdminConfig))); @@ -39,43 +34,26 @@ }; var dsnConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"); - if (builder.Environment.IsEnvironment(SystemConsts.TestingEnvironment)) - { - builder.Services.AddEntityFrameworkInMemoryDatabase(); - } - else + if (!builder.Environment.IsEnvironment(SystemConsts.TestingEnvironment)) { - builder.Services - .AddHealthChecks() - .AddSqlServer(dsnConnectionString!); + builder.Services.AddHealthChecks().AddSqlServer(dsnConnectionString!); } - builder - .Host - .UseSerilog( - (context, services, configuration) => - configuration - .ReadFrom - .Configuration(context.Configuration) - .ReadFrom - .Services(services) - ); + builder.Host.UseSerilog( + (context, services, configuration) => + configuration.ReadFrom.Configuration(context.Configuration).ReadFrom.Services(services) + ); builder - .Services - .AddDbContext(options => + .Services.AddDbContext(options => { - if (builder.Environment.IsEnvironment(SystemConsts.TestingEnvironment)) - { - options.UseInMemoryDatabase("InMemoryDbForTesting"); - } - else + if (!builder.Environment.IsEnvironment(SystemConsts.TestingEnvironment)) { options.UseSqlServer(dsnConnectionString); } - options.UseTriggers( - triggerOptions => triggerOptions.AddTrigger() + options.UseTriggers(triggerOptions => + triggerOptions.AddTrigger() ); }) .AddScoped() @@ -94,8 +72,7 @@ var corsOrigins = builder.Configuration.GetSection("AllowedOrigins").Value?.Split(';'); Log.Information("Allowed origins: {CorsOrigins}", corsOrigins); builder - .Services - .AddCors(options => + .Services.AddCors(options => { options.AddDefaultPolicy(corsPolicyBuilder => { @@ -108,23 +85,19 @@ .AllowCredentials(); }); }) - .AddFastEndpoints(options => - { - options.DisableAutoDiscovery = true; - options.Assemblies = - [ - typeof(IApiMarker).Assembly, - typeof(IApplicationMarker).Assembly - ]; - }) .AddSingleton(tokenValidationParameters) .AddAuthenticationJwtBearer(opt => { opt.SigningKey = jwtConfig.SecretKey; }) .AddAuthorization() - .AddAutoMapper(typeof(IApiMarker), typeof(IApplicationMarker)); - + .AddAutoMapper(typeof(IApiMarker), typeof(IApplicationMarker)) + .AddFastEndpoints(options => + { + options.DisableAutoDiscovery = true; + options.Assemblies = [typeof(IApiMarker).Assembly, typeof(IApplicationMarker).Assembly]; + }); + var app = builder.Build(); if (app.Environment.IsDevelopment()) @@ -136,7 +109,10 @@ { app.MapHealthChecks( "/_health", - new HealthCheckOptions { ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, } + new HealthCheckOptions + { + ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse, + } ) .RequireHost("*:5210"); } diff --git a/quetzalcoatl-auth/Domain/Consts/ApplicationUserConsts.cs b/quetzalcoatl-auth/Domain/Consts/ApplicationUserConsts.cs index ebd3b1a..b50474b 100644 --- a/quetzalcoatl-auth/Domain/Consts/ApplicationUserConsts.cs +++ b/quetzalcoatl-auth/Domain/Consts/ApplicationUserConsts.cs @@ -9,5 +9,10 @@ public static class ApplicationUserConsts public const int BioMaxLength = 300; public const int ProfilePictureMaxLength = 10_000_000; public const string PasswordRegex = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{6,20}$"; - public static readonly string[] AllowedProfilePictureTypes = { "image/png", "image/jpeg", "image/jpg" }; -} \ No newline at end of file + public static readonly string[] AllowedProfilePictureTypes = + { + "image/png", + "image/jpeg", + "image/jpg" + }; +} diff --git a/quetzalcoatl-auth/Domain/Consts/SystemConsts.cs b/quetzalcoatl-auth/Domain/Consts/SystemConsts.cs index 85dc8f3..88402bb 100644 --- a/quetzalcoatl-auth/Domain/Consts/SystemConsts.cs +++ b/quetzalcoatl-auth/Domain/Consts/SystemConsts.cs @@ -3,4 +3,4 @@ namespace Domain.Consts; public static class SystemConsts { public const string TestingEnvironment = "TESTING"; -} \ No newline at end of file +} diff --git a/quetzalcoatl-auth/Infrastructure/ApplicationDbContext.cs b/quetzalcoatl-auth/Infrastructure/ApplicationDbContext.cs index 767f298..346d8e9 100644 --- a/quetzalcoatl-auth/Infrastructure/ApplicationDbContext.cs +++ b/quetzalcoatl-auth/Infrastructure/ApplicationDbContext.cs @@ -26,7 +26,8 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); - if (optionsBuilder.IsConfigured) return; + if (optionsBuilder.IsConfigured) + return; var connectionString = Environment.GetEnvironmentVariable("QUETZALCOATL_DSN"); optionsBuilder.UseSqlServer(connectionString!); diff --git a/quetzalcoatl-auth/Infrastructure/Migrations/20230315154826_AddGuidAsPKForIdentityUser.cs b/quetzalcoatl-auth/Infrastructure/Migrations/20230315154826_AddGuidAsPKForIdentityUser.cs index 3e1da52..b937e98 100644 --- a/quetzalcoatl-auth/Infrastructure/Migrations/20230315154826_AddGuidAsPKForIdentityUser.cs +++ b/quetzalcoatl-auth/Infrastructure/Migrations/20230315154826_AddGuidAsPKForIdentityUser.cs @@ -13,29 +13,25 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "AspNetRoles", - columns: table => - new - { - Id = table.Column( - type: "uniqueidentifier", - nullable: false, - defaultValueSql: "newsequentialid()" - ), - Name = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - NormalizedName = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - ConcurrencyStamp = table.Column( - type: "nvarchar(max)", - nullable: true - ) - }, + columns: table => new + { + Id = table.Column( + type: "uniqueidentifier", + nullable: false, + defaultValueSql: "newsequentialid()" + ), + Name = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + NormalizedName = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, constraints: table => { table.PrimaryKey("PK_AspNetRoles", x => x.Id); @@ -44,51 +40,47 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetUsers", - columns: table => - new - { - Id = table.Column( - type: "uniqueidentifier", - nullable: false, - defaultValueSql: "newsequentialid()" - ), - UserName = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - NormalizedUserName = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - Email = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - NormalizedEmail = table.Column( - type: "nvarchar(256)", - maxLength: 256, - nullable: true - ), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column( - type: "nvarchar(max)", - nullable: true - ), - PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column( - type: "datetimeoffset", - nullable: true - ), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, + columns: table => new + { + Id = table.Column( + type: "uniqueidentifier", + nullable: false, + defaultValueSql: "newsequentialid()" + ), + UserName = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + NormalizedUserName = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + Email = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + NormalizedEmail = table.Column( + type: "nvarchar(256)", + maxLength: 256, + nullable: true + ), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column( + type: "datetimeoffset", + nullable: true + ), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); @@ -97,16 +89,15 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetRoleClaims", - columns: table => - new - { - Id = table - .Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, + columns: table => new + { + Id = table + .Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, constraints: table => { table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); @@ -122,16 +113,15 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetUserClaims", - columns: table => - new - { - Id = table - .Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, + columns: table => new + { + Id = table + .Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, constraints: table => { table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); @@ -147,20 +137,16 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetUserLogins", - columns: table => - new - { - LoginProvider = table.Column( - type: "nvarchar(450)", - nullable: false - ), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column( - type: "nvarchar(max)", - nullable: true - ), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column( + type: "nvarchar(max)", + nullable: true + ), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, constraints: table => { table.PrimaryKey( @@ -179,12 +165,11 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetUserRoles", - columns: table => - new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, constraints: table => { table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); @@ -207,28 +192,23 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateTable( name: "AspNetUserTokens", - columns: table => - new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column( - type: "nvarchar(450)", - nullable: false - ), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, constraints: table => { table.PrimaryKey( "PK_AspNetUserTokens", - x => - new - { - x.UserId, - x.LoginProvider, - x.Name - } + x => new + { + x.UserId, + x.LoginProvider, + x.Name + } ); table.ForeignKey( name: "FK_AspNetUserTokens_AspNetUsers_UserId", diff --git a/quetzalcoatl-auth/Infrastructure/Migrations/20230319104138_AddProfileImageToIdentityUser.cs b/quetzalcoatl-auth/Infrastructure/Migrations/20230319104138_AddProfileImageToIdentityUser.cs index d0e1071..a1f3296 100644 --- a/quetzalcoatl-auth/Infrastructure/Migrations/20230319104138_AddProfileImageToIdentityUser.cs +++ b/quetzalcoatl-auth/Infrastructure/Migrations/20230319104138_AddProfileImageToIdentityUser.cs @@ -13,13 +13,12 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Pictures", - columns: table => - new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Data = table.Column(type: "varbinary(max)", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Data = table.Column(type: "varbinary(max)", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, constraints: table => { table.PrimaryKey("PK_Pictures", x => x.Id); diff --git a/quetzalcoatl-auth/Infrastructure/Migrations/20230509173144_AddRefreshTokenEntity.cs b/quetzalcoatl-auth/Infrastructure/Migrations/20230509173144_AddRefreshTokenEntity.cs index 5fef439..a0e4044 100644 --- a/quetzalcoatl-auth/Infrastructure/Migrations/20230509173144_AddRefreshTokenEntity.cs +++ b/quetzalcoatl-auth/Infrastructure/Migrations/20230509173144_AddRefreshTokenEntity.cs @@ -13,14 +13,13 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "RefreshTokens", - columns: table => - new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Token = table.Column(type: "nvarchar(max)", nullable: false), - ExpiryDate = table.Column(type: "datetime2", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Token = table.Column(type: "nvarchar(max)", nullable: false), + ExpiryDate = table.Column(type: "datetime2", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, constraints: table => { table.PrimaryKey("PK_RefreshTokens", x => x.Id); diff --git a/quetzalcoatl-auth/Infrastructure/Migrations/20230525105824_UpdateRefreshTokenEntity.cs b/quetzalcoatl-auth/Infrastructure/Migrations/20230525105824_UpdateRefreshTokenEntity.cs index fb438f1..f057092 100644 --- a/quetzalcoatl-auth/Infrastructure/Migrations/20230525105824_UpdateRefreshTokenEntity.cs +++ b/quetzalcoatl-auth/Infrastructure/Migrations/20230525105824_UpdateRefreshTokenEntity.cs @@ -13,16 +13,15 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "RefreshTokens", - columns: table => - new - { - Token = table.Column(type: "uniqueidentifier", nullable: false), - ExpiryDate = table.Column(type: "datetime2", nullable: false), - IsUsed = table.Column(type: "bit", nullable: false), - IsInvalidated = table.Column(type: "bit", nullable: false), - Jti = table.Column(type: "uniqueidentifier", nullable: false), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, + columns: table => new + { + Token = table.Column(type: "uniqueidentifier", nullable: false), + ExpiryDate = table.Column(type: "datetime2", nullable: false), + IsUsed = table.Column(type: "bit", nullable: false), + IsInvalidated = table.Column(type: "bit", nullable: false), + Jti = table.Column(type: "uniqueidentifier", nullable: false), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, constraints: table => { table.PrimaryKey("PK_RefreshTokens", x => x.Token); diff --git a/quetzalcoatl-auth/Infrastructure/Migrations/20231201120945_RemoveRedundantFieldsFromRefreshTokens.cs b/quetzalcoatl-auth/Infrastructure/Migrations/20231201120945_RemoveRedundantFieldsFromRefreshTokens.cs index a84ea53..24aeddb 100644 --- a/quetzalcoatl-auth/Infrastructure/Migrations/20231201120945_RemoveRedundantFieldsFromRefreshTokens.cs +++ b/quetzalcoatl-auth/Infrastructure/Migrations/20231201120945_RemoveRedundantFieldsFromRefreshTokens.cs @@ -11,49 +11,45 @@ public partial class RemoveRedundantFieldsFromRefreshTokens : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.DropPrimaryKey( - name: "PK_RefreshTokens", - table: "RefreshTokens"); + migrationBuilder.DropPrimaryKey(name: "PK_RefreshTokens", table: "RefreshTokens"); - migrationBuilder.DropColumn( - name: "Jti", - table: "RefreshTokens"); + migrationBuilder.DropColumn(name: "Jti", table: "RefreshTokens"); - migrationBuilder.DropColumn( - name: "IsUsed", - table: "RefreshTokens"); + migrationBuilder.DropColumn(name: "IsUsed", table: "RefreshTokens"); migrationBuilder.AddPrimaryKey( name: "PK_RefreshTokens", table: "RefreshTokens", - column: "Token"); + column: "Token" + ); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.DropPrimaryKey( - name: "PK_RefreshTokens", - table: "RefreshTokens"); + migrationBuilder.DropPrimaryKey(name: "PK_RefreshTokens", table: "RefreshTokens"); migrationBuilder.AddColumn( name: "Jti", table: "RefreshTokens", type: "uniqueidentifier", nullable: false, - defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + defaultValue: new Guid("00000000-0000-0000-0000-000000000000") + ); migrationBuilder.AddColumn( name: "IsUsed", table: "RefreshTokens", type: "bit", nullable: false, - defaultValue: false); + defaultValue: false + ); migrationBuilder.AddPrimaryKey( name: "PK_RefreshTokens", table: "RefreshTokens", - columns: new[] { "Token", "Jti" }); + columns: new[] { "Token", "Jti" } + ); } } } diff --git a/quetzalcoatl-auth/Infrastructure/Triggers/DeleteStaleRefreshTokens.cs b/quetzalcoatl-auth/Infrastructure/Triggers/DeleteStaleRefreshTokens.cs index 81e3503..e2b0c51 100644 --- a/quetzalcoatl-auth/Infrastructure/Triggers/DeleteStaleRefreshTokens.cs +++ b/quetzalcoatl-auth/Infrastructure/Triggers/DeleteStaleRefreshTokens.cs @@ -17,8 +17,8 @@ CancellationToken cancellationToken { if (context.ChangeType is ChangeType.Added or ChangeType.Modified) { - await _tokenRepository.DeleteRefreshTokenAsync( - token => token.IsInvalidated || token.ExpiryDate < DateTime.UtcNow + await _tokenRepository.DeleteRefreshTokenAsync(token => + token.IsInvalidated || token.ExpiryDate < DateTime.UtcNow ); } } diff --git a/quetzalcoatl-auth/Tests.Integration/Api/Features/Auth/RegisterEndpointTests.cs b/quetzalcoatl-auth/Tests.Integration/Api/Features/Auth/RegisterEndpointTests.cs index 71cad99..941812c 100644 --- a/quetzalcoatl-auth/Tests.Integration/Api/Features/Auth/RegisterEndpointTests.cs +++ b/quetzalcoatl-auth/Tests.Integration/Api/Features/Auth/RegisterEndpointTests.cs @@ -39,13 +39,13 @@ public async Task GivenValidUser_WhenRegistering_ThenReturnsCreated() "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -110,13 +110,13 @@ public async Task GivenInvalidUser_WhenRegistering_ThenReturnsBadRequest() "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -147,16 +147,24 @@ public async Task GivenInvalidUser_WhenRegistering_ThenReturnsBadRequest() response.Headers.TryGetValues("Set-Cookie", out _).Should().BeFalse(); var formatters = new MediaTypeFormatterCollection(); - formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); - formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/problem+json")); - + formatters.JsonFormatter.SupportedMediaTypes.Add( + new MediaTypeHeaderValue("application/json") + ); + formatters.JsonFormatter.SupportedMediaTypes.Add( + new MediaTypeHeaderValue("application/problem+json") + ); + var result = await response.Content.ReadAsAsync(formatters: formatters); result.Should().NotBeNull(); - result!.Errors.Keys.Select(r => r.ToUpper()) - .Should().Contain(nameof(request.Password).ToUpper()); - result.Errors.Keys.Select(r => r.ToUpper()) - .Should().Contain(nameof(request.ProfilePicture).ToUpper()); + result! + .Errors.Keys.Select(r => r.ToUpper()) + .Should() + .Contain(nameof(request.Password).ToUpper()); + result + .Errors.Keys.Select(r => r.ToUpper()) + .Should() + .Contain(nameof(request.ProfilePicture).ToUpper()); #endregion } diff --git a/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/DeleteEndpointTests.cs b/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/DeleteEndpointTests.cs index 75d1598..4b03052 100644 --- a/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/DeleteEndpointTests.cs +++ b/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/DeleteEndpointTests.cs @@ -133,9 +133,9 @@ public async Task GivenAuthorizedUserAndIdOfNonExistingUser_WhenDeletingUser_The using var scope = _apiWebFactory.Services.CreateScope(); var userManager = scope.ServiceProvider.GetRequiredService>(); - var roleManager = scope - .ServiceProvider - .GetRequiredService>>(); + var roleManager = scope.ServiceProvider.GetRequiredService< + RoleManager> + >(); await roleManager.CreateAsync(new IdentityRole(ApplicationRole.Admin.ToString())); @@ -205,9 +205,9 @@ public async Task GivenAuthorizedUser_WhenDeletingUser_ThenReturnsNoContent() using var scope = _apiWebFactory.Services.CreateScope(); var userManager = scope.ServiceProvider.GetRequiredService>(); - var roleManager = scope - .ServiceProvider - .GetRequiredService>>(); + var roleManager = scope.ServiceProvider.GetRequiredService< + RoleManager> + >(); await roleManager.CreateAsync(new IdentityRole(ApplicationRole.Admin.ToString())); diff --git a/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/UpdateEndpointTests.cs b/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/UpdateEndpointTests.cs index 0f2584b..e15ae38 100644 --- a/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/UpdateEndpointTests.cs +++ b/quetzalcoatl-auth/Tests.Integration/Api/Features/Users/UpdateEndpointTests.cs @@ -47,13 +47,13 @@ public async Task GivenAnonymousUser_WhenUpdatingUser_ThenReturnsUnauthorized() "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -128,13 +128,13 @@ public async Task GivenAuthorizedUserAndInvalidRequest_WhenUpdatingUser_ThenRetu "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -229,13 +229,13 @@ public async Task GivenAuthorizedUserAndRequestForUpdatingOtherUserThanSelf_When "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -331,13 +331,13 @@ public async Task GivenAuthorizedUserAndValidRequestForPartialUpdate_WhenUpdatin "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent(); @@ -439,13 +439,13 @@ public async Task GivenAuthorizedUserAndValidRequest_WhenUpdatingUser_ThenReturn "demo.jpg" ); - _client - .DefaultRequestHeaders - .Accept - .Add(new MediaTypeWithQualityHeaderValue("application/json")); - _client - .DefaultRequestHeaders - .TryAddWithoutValidation("Content-Type", "multipart/form-data"); + _client.DefaultRequestHeaders.Accept.Add( + new MediaTypeWithQualityHeaderValue("application/json") + ); + _client.DefaultRequestHeaders.TryAddWithoutValidation( + "Content-Type", + "multipart/form-data" + ); var requestForm = new MultipartFormDataContent();