feat(api): PUT /api/v1/users/{id}/permisos/overrides + excepciones domain + ExceptionFilter [UDT-009]

This commit is contained in:
2026-04-15 21:43:38 -03:00
parent 47323302cc
commit 7d4dc4d2bb
7 changed files with 179 additions and 0 deletions

View File

@@ -246,6 +246,30 @@ public sealed class UsuariosController : ControllerBase
return Ok(MapToPermisosResponse(result));
}
/// <summary>
/// Replaces the grant/deny override sets for a usuario.
/// Requires administracion:usuarios:gestionar.
/// </summary>
[HttpPut("{id:int}/permisos/overrides")]
[RequirePermission("administracion:usuarios:gestionar")]
[ProducesResponseType(typeof(UsuarioPermisosResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> UpdatePermisosOverrides(
[FromRoute] int id,
[FromBody] UpdatePermisosOverridesRequest request)
{
var command = new UpdateUsuarioPermisosOverridesCommand(
Id: id,
Grant: request.Grant ?? [],
Deny: request.Deny ?? []);
var result = await _dispatcher.Send<UpdateUsuarioPermisosOverridesCommand, UsuarioPermisosDto>(command);
return Ok(MapToPermisosResponse(result));
}
private static UsuarioPermisosResponse MapToPermisosResponse(UsuarioPermisosDto dto)
=> new(
RolPermisos: dto.RolPermisos,
@@ -266,6 +290,11 @@ public sealed record PermisosOverridesShape(
IReadOnlyList<string> Grant,
IReadOnlyList<string> Deny);
/// <summary>UDT-009: PUT permisos/overrides request body.</summary>
public sealed record UpdatePermisosOverridesRequest(
IReadOnlyList<string>? Grant,
IReadOnlyList<string>? Deny);
/// <summary>Create user request body — nullable to catch missing field scenarios.</summary>
public sealed record CreateUsuarioRequest(
string? Username,

View File

@@ -169,6 +169,35 @@ public sealed class ExceptionFilter : IExceptionFilter
context.ExceptionHandled = true;
break;
// UDT-009: permiso override validation errors
case InvalidPermisoCodesException ipce:
context.Result = new ObjectResult(new Microsoft.AspNetCore.Mvc.ProblemDetails
{
Type = "about:blank",
Title = "invalid-permiso-codes",
Status = 400,
Extensions = { ["invalidCodes"] = ipce.InvalidCodes }
})
{
StatusCode = StatusCodes.Status400BadRequest
};
context.ExceptionHandled = true;
break;
case GrantDenyOverlapException gdoe:
context.Result = new ObjectResult(new Microsoft.AspNetCore.Mvc.ProblemDetails
{
Type = "about:blank",
Title = "grant-deny-overlap",
Status = 400,
Extensions = { ["overlap"] = gdoe.Overlap }
})
{
StatusCode = StatusCodes.Status400BadRequest
};
context.ExceptionHandled = true;
break;
case ValidationException validationEx:
var errors = validationEx.Errors
.GroupBy(e => e.PropertyName)