Skip to content

Commit 9573cf5

Browse files
authored
Merge pull request #127 from samuelzedec/develop
refactor: reestruturação da arquitetura de autenticação e serviços
2 parents 4341726 + 7790741 commit 9573cf5

File tree

138 files changed

+4335
-2330
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

138 files changed

+4335
-2330
lines changed

.editorconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ indent_size = 2
1111
# C# files
1212
[*.cs]
1313
dotnet_diagnostic.CS8019.severity = error
14+
# dotnet_diagnostic.IDE0005.severity = error
1415

1516
# Migrations and auto-generated code
1617
[**/Migrations/*.cs]

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Riber - Changelog
22

3+
## v4.1.0 - 26/10/2025
4+
5+
- **NOVO**: Sistema modular de serviços de autenticação com separação de responsabilidades
6+
- Adiciona `IAuthenticationService` para operações de login e autenticação
7+
- Adiciona `IRoleManagementService` para gerenciamento de roles
8+
- Adiciona `IUserManagementService` para gerenciamento de usuários
9+
- Adiciona `IUserQueryService` para consultas de usuários
10+
- Implementa serviços de Identity: `AuthenticationService`, `RoleManagementService`, `UserManagementService`, `UserQueryService`, `UserMappingService` e `UserCreationService`
11+
- **NOVO**: `EmptyResult` para operações sem retorno de valor
12+
- **REFATORAÇÃO**: Reorganização das interfaces de serviços para pasta `Authentication`
13+
- **REFATORAÇÃO**: Atualização de toda a aplicação (handlers, controllers, middlewares) para usar novos serviços
14+
- **REFATORAÇÃO**: Melhorias no `PermissionDataService` e repositórios
15+
- Remove serviços obsoletos: `IAuthService`, `AuthService`, `UserCreationService` (versão antiga)
16+
- Remove `SpecificationExtension` não utilizada
17+
- Atualiza todos os testes para refletir as mudanças arquiteturais
18+
19+
---
20+
321
# v4.0.1 - 22/10/2025
422

523
- **CORREÇÃO**: Mudança no Job de limpeza de imagens na Bucket

Directory.Build.props

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Companies>Samuel Zedec</Companies>
55
<Copyright>Copyright © $([System.DateTime]::Now.Year)</Copyright>
66
<Description>Sistema de gestão financeira para um lanchonete local</Description>
7-
<Version>4.0.1</Version>
7+
<Version>4.1.0</Version>
88
</PropertyGroup>
99

1010
<!-- Repositório e Documentação -->
@@ -18,6 +18,7 @@
1818
<TargetFramework>net9.0</TargetFramework>
1919
<ImplicitUsings>enable</ImplicitUsings>
2020
<Nullable>enable</Nullable>
21+
<InvariantGlobalization>true</InvariantGlobalization>
2122
</PropertyGroup>
2223

2324
<!-- Configurações para análise de código do projeto -->

analyze.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ SONAR_BEGIN_ARGS=(
6262
**/Identity/**,\
6363
**/Common/Api/**,\
6464
**/Requests/**,\
65-
**/AuthService.cs"
65+
**/UserManagementService.cs/**,\
66+
**/UserMappingService.cs/**"
6667
)
6768

6869
# Adicionar /o: apenas se for SonarCloud

src/Riber.Api/Common/Api/BuilderExtension.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Microsoft.AspNetCore.Http.Timeouts;
99
using Microsoft.AspNetCore.Identity;
1010
using Microsoft.AspNetCore.Mvc;
11+
using Microsoft.AspNetCore.Server.Kestrel.Core;
1112
using Microsoft.IdentityModel.Tokens;
1213
using Microsoft.OpenApi.Models;
1314
using Riber.Api.Authorizations.Permissions;
@@ -43,7 +44,13 @@ private static void AddMiddleware(this WebApplicationBuilder builder)
4344

4445
private static void AddConfigurations(this WebApplicationBuilder builder)
4546
{
46-
builder.WebHost.ConfigureKestrel(options => options.AddServerHeader = false);
47+
builder.WebHost.ConfigureKestrel(options =>
48+
{
49+
options.AddServerHeader = false;
50+
options.ConfigureEndpointDefaults(endpoint
51+
=> endpoint.Protocols = HttpProtocols.Http1AndHttp2AndHttp3);
52+
});
53+
4754
builder.Configuration.AddEnvironmentVariables();
4855
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
4956
builder.Services.AddProblemDetails();
@@ -196,7 +203,8 @@ private static void AddJsonConfiguration(this WebApplicationBuilder builder)
196203
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.Never;
197204
});
198205

199-
builder.Services.AddControllers()
206+
builder.Services
207+
.AddControllers()
200208
.AddJsonOptions(options =>
201209
{
202210
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

src/Riber.Api/Common/Api/GlobalExceptionHandler.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Riber.Api.Extensions;
33
using Riber.Application.Exceptions;
44
using Riber.Domain.Exceptions;
5+
using System.Net;
56
using Layer = Riber.Application.Exceptions;
67

78
namespace Riber.Api.Common.Api;
@@ -20,18 +21,18 @@ public async ValueTask<bool> TryHandleAsync(
2021
logger.LogError(exception, "Exception occurred: {ExceptionType} - {Message}",
2122
exception.GetType().Name, exception.Message);
2223

23-
(int statusCode, string message, Dictionary<string, string[]> details) = MapExceptionToError(exception);
24+
(HttpStatusCode statusCode, string message, Dictionary<string, string[]> details) = MapExceptionToError(exception);
2425
await httpContext.WriteErrorResponse(statusCode, message, details);
2526
return true;
2627
}
2728

28-
private static (int StatusCode, string Message, Dictionary<string, string[]> Details) MapExceptionToError(Exception exception)
29+
private static (HttpStatusCode StatusCode, string Message, Dictionary<string, string[]> Details) MapExceptionToError(Exception exception)
2930
=> exception switch
3031
{
3132
RequestTimeoutException timeoutEx => (timeoutEx.Code, timeoutEx.Message, []),
32-
ValidationException validationEx => (StatusCodes.Status400BadRequest, string.Empty, validationEx.Details),
33+
ValidationException validationEx => (HttpStatusCode.BadRequest, string.Empty, validationEx.Details),
3334
Layer.ApplicationException applicationEx => (applicationEx.Code, applicationEx.Message, []),
34-
DomainException domainEx => (StatusCodes.Status422UnprocessableEntity, domainEx.Message, []),
35-
_ => (StatusCodes.Status500InternalServerError, "Erro inesperado no servidor.", [])
35+
DomainException domainEx => (HttpStatusCode.UnprocessableContent, domainEx.Message, []),
36+
_ => (HttpStatusCode.InternalServerError, "Erro inesperado no servidor.", [])
3637
};
3738
}

src/Riber.Api/Controllers/AuthController.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNetCore.Authorization;
44
using Microsoft.AspNetCore.Http.Timeouts;
55
using Microsoft.AspNetCore.Mvc;
6+
using Riber.Api.Extensions;
67
using Riber.Application.Common;
78
using Riber.Application.Features.Auths.Commands.Login;
89
using Riber.Application.Features.Auths.Commands.Logout;
@@ -29,7 +30,7 @@ public async Task<IActionResult> Login(
2930
CancellationToken cancellationToken)
3031
{
3132
var response = await mediator.Send(command, cancellationToken);
32-
return Ok(response);
33+
return response.ToHttpResult();
3334
}
3435

3536
[HttpGet("permissions")]
@@ -38,7 +39,7 @@ public async Task<IActionResult> Login(
3839
public async Task<IActionResult> GetPermissionsByAuthenticatedUser(CancellationToken cancellationToken)
3940
{
4041
var response = await mediator.Send(new GetPermissionsQuery(), cancellationToken);
41-
return Ok(response);
42+
return response.ToHttpResult();
4243
}
4344

4445
[HttpGet("refresh")]
@@ -48,7 +49,7 @@ public async Task<IActionResult> GetPermissionsByAuthenticatedUser(CancellationT
4849
public async Task<IActionResult> GetRefreshToken(CancellationToken cancellationToken)
4950
{
5051
var response = await mediator.Send(new GetRefreshTokenQuery(), cancellationToken);
51-
return Ok(response);
52+
return response.ToHttpResult();
5253
}
5354

5455
[HttpPost("logout")]
@@ -57,6 +58,6 @@ public async Task<IActionResult> GetRefreshToken(CancellationToken cancellationT
5758
public async Task<IActionResult> Logout(CancellationToken cancellationToken)
5859
{
5960
var response = await mediator.Send(new LogoutCommand(), cancellationToken);
60-
return Ok(response);
61+
return response.ToHttpResult();
6162
}
6263
}

src/Riber.Api/Controllers/CompanyController.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using Microsoft.AspNetCore.Http.Timeouts;
55
using Microsoft.AspNetCore.Mvc;
66
using Riber.Api.Attributes;
7+
using Riber.Api.Extensions;
8+
using Riber.Api.Requests.Company;
79
using Riber.Application.Common;
810
using Riber.Application.Features.Companies.Commands.CreateCompanyWithAdmin;
911
using Riber.Application.Features.Companies.Commands.UpdateCompany;
@@ -29,19 +31,20 @@ public async Task<IActionResult> CreateCompany(
2931
CancellationToken cancellationToken)
3032
{
3133
var response = await mediator.Send(withAdminCommand, cancellationToken);
32-
return Created($"/api/company/{response.Value?.CompanyId}", response);
34+
return response.ToHttpResult($"/api/company/{response.Value?.CompanyId}");
3335
}
34-
35-
[HttpPut]
36+
37+
[HttpPut("{id:guid}")]
3638
[ProducesResponseType<Result<UpdateCompanyCommandResponse>>(StatusCodes.Status200OK)]
3739
[RequirePermission(PermissionsSettings.Companies.Read, PermissionsSettings.Companies.Update)]
38-
[RequestTimeout("standard")]
40+
[RequestTimeout("standard")]
3941
public async Task<IActionResult> UpdateCompany(
40-
[FromBody] UpdateCompanyCommand command,
42+
[FromRoute] Guid id,
43+
[FromBody] UpdateCompanyRequest request,
4144
CancellationToken cancellationToken)
4245
{
43-
var response = await mediator.Send(command, cancellationToken);
44-
return Ok(response);
46+
var response = await mediator.Send(request.ToCommand(id), cancellationToken);
47+
return response.ToHttpResult();
4548
}
4649

4750
[HttpGet("{id:guid}")]
@@ -53,6 +56,6 @@ public async Task<IActionResult> GetCompanyById(
5356
CancellationToken cancellationToken)
5457
{
5558
var response = await mediator.Send(new GetCompanyByIdQuery(id), cancellationToken);
56-
return Ok(response);
59+
return response.ToHttpResult();
5760
}
5861
}

src/Riber.Api/Controllers/ProductCategoryController.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.AspNetCore.Http.Timeouts;
55
using Microsoft.AspNetCore.Mvc;
66
using Riber.Api.Attributes;
7+
using Riber.Api.Extensions;
78
using Riber.Application.Common;
89
using Riber.Application.Features.ProductCategories.Commands;
910
using Riber.Infrastructure.Settings;
@@ -27,6 +28,6 @@ public async Task<IActionResult> CreateProductCategory(
2728
CancellationToken cancellationToken)
2829
{
2930
var response = await mediator.Send(request, cancellationToken);
30-
return Ok(response);
31+
return response.ToHttpResult($"api/product-category/{response.Value?.ProductCategoryId}");
3132
}
3233
}
Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,36 @@
1-
// using Mediator;
2-
// using Microsoft.AspNetCore.Authorization;
3-
// using Microsoft.AspNetCore.Http.Timeouts;
4-
// using Microsoft.AspNetCore.Mvc;
5-
// using Riber.Api.Attributes;
6-
// using Riber.Api.Requests;
7-
// using Riber.Application.Common;
8-
// using Riber.Application.Features.Products.Commands;
9-
// using Riber.Infrastructure.Settings;
10-
//
11-
// namespace Riber.Api.Controllers;
12-
//
13-
// [ApiController]
14-
// [Authorize]
15-
// [Route("api/v{version:apiVersion}/product")]
16-
// [ApiVersion("1.0")]
17-
// [Produces("application/json")]
18-
// [Consumes("application/json", "multipart/form-data")]
19-
// public sealed class ProductController(IMediator mediator) : ControllerBase
20-
// {
21-
// [HttpPost]
22-
// [RequestSizeLimit(3_145_728)]
23-
// [RequestFormLimits(MultipartBodyLengthLimit = 3_145_728)]
24-
// [RequirePermission(PermissionsSettings.Products.Create)]
25-
// [RequestTimeout("standard")]
26-
// [ProducesResponseType<Result<CreateProductCommandResponse>>(StatusCodes.Status201Created)]
27-
// public async Task<IActionResult> CreateProduct(
28-
// [FromForm] CreateProductRequest request,
29-
// CancellationToken cancellationToken)
30-
// {
31-
// var response = await mediator.Send(new CreateProductCommand(
32-
// Name: request.Name,
33-
// Description: request.Description,
34-
// Price: request.Price,
35-
// CategoryId: request.CategoryId,
36-
// ImageStream: request.Image?.OpenReadStream(),
37-
// ImageName: request.Image?.FileName ?? string.Empty,
38-
// ImageContent: request.Image?.ContentType ?? string.Empty
39-
// ), cancellationToken);
40-
// return Created($"api/product/{response.Value.ProductId}", response);
41-
// }
42-
// }
1+
using Asp.Versioning;
2+
using Mediator;
3+
using Microsoft.AspNetCore.Authorization;
4+
using Microsoft.AspNetCore.Http.Timeouts;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Riber.Api.Attributes;
7+
using Riber.Api.Extensions;
8+
using Riber.Api.Requests.Product;
9+
using Riber.Application.Common;
10+
using Riber.Application.Features.Products.Commands;
11+
using Riber.Infrastructure.Settings;
12+
13+
namespace Riber.Api.Controllers;
14+
15+
[ApiController]
16+
[Authorize]
17+
[Route("api/v{version:apiVersion}/product")]
18+
[ApiVersion("1.0")]
19+
[Produces("application/json")]
20+
[Consumes("application/json", "multipart/form-data")]
21+
public sealed class ProductController(IMediator mediator) : ControllerBase
22+
{
23+
[HttpPost]
24+
[RequestSizeLimit(3_145_728)]
25+
[RequestFormLimits(MultipartBodyLengthLimit = 3_145_728)]
26+
[RequirePermission(PermissionsSettings.Products.Create)]
27+
[RequestTimeout("standard")]
28+
[ProducesResponseType<Result<CreateProductCommandResponse>>(StatusCodes.Status201Created)]
29+
public async Task<IActionResult> CreateProduct(
30+
[FromForm] CreateProductRequest request,
31+
CancellationToken cancellationToken)
32+
{
33+
var response = await mediator.Send(request.ToCommand(), cancellationToken);
34+
return response.ToHttpResult($"/api/product/{response.Value?.ProductId}");
35+
}
36+
}

0 commit comments

Comments
 (0)