feat(api): PUT /api/v1/users/{id}/permisos/overrides + excepciones domain + ExceptionFilter [UDT-009]
This commit is contained in:
@@ -60,6 +60,7 @@ public static class DependencyInjection
|
||||
|
||||
// Usuarios/Permisos (UDT-009)
|
||||
services.AddScoped<ICommandHandler<GetUsuarioPermisosQuery, UsuarioPermisosDto>, GetUsuarioPermisosQueryHandler>();
|
||||
services.AddScoped<ICommandHandler<UpdateUsuarioPermisosOverridesCommand, UsuarioPermisosDto>, UpdateUsuarioPermisosOverridesCommandHandler>();
|
||||
|
||||
// FluentValidation validators (scans entire Application assembly)
|
||||
services.AddValidatorsFromAssemblyContaining<LoginCommandValidator>();
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace SIGCM2.Application.Usuarios.Permisos;
|
||||
|
||||
/// <summary>UDT-009: Command to replace the grant/deny override sets for a usuario.</summary>
|
||||
public sealed record UpdateUsuarioPermisosOverridesCommand(
|
||||
int Id,
|
||||
IReadOnlyList<string> Grant,
|
||||
IReadOnlyList<string> Deny);
|
||||
@@ -0,0 +1,81 @@
|
||||
using SIGCM2.Application.Abstractions;
|
||||
using SIGCM2.Application.Abstractions.Persistence;
|
||||
using SIGCM2.Application.Common;
|
||||
using SIGCM2.Domain.Exceptions;
|
||||
|
||||
namespace SIGCM2.Application.Usuarios.Permisos;
|
||||
|
||||
/// <summary>
|
||||
/// UDT-009: Handles PUT /api/v1/users/{id}/permisos/overrides.
|
||||
/// Validates overlap and catalog existence, persists new overrides, returns updated effective set.
|
||||
/// </summary>
|
||||
public sealed class UpdateUsuarioPermisosOverridesCommandHandler
|
||||
: ICommandHandler<UpdateUsuarioPermisosOverridesCommand, UsuarioPermisosDto>
|
||||
{
|
||||
private readonly IUsuarioRepository _usuarioRepo;
|
||||
private readonly IRolPermisoRepository _rolPermisoRepo;
|
||||
private readonly IPermisoRepository _permisoRepo;
|
||||
|
||||
public UpdateUsuarioPermisosOverridesCommandHandler(
|
||||
IUsuarioRepository usuarioRepo,
|
||||
IRolPermisoRepository rolPermisoRepo,
|
||||
IPermisoRepository permisoRepo)
|
||||
{
|
||||
_usuarioRepo = usuarioRepo;
|
||||
_rolPermisoRepo = rolPermisoRepo;
|
||||
_permisoRepo = permisoRepo;
|
||||
}
|
||||
|
||||
public async Task<UsuarioPermisosDto> Handle(UpdateUsuarioPermisosOverridesCommand command)
|
||||
{
|
||||
var grant = command.Grant ?? [];
|
||||
var deny = command.Deny ?? [];
|
||||
|
||||
// 1. Overlap check — grant ∩ deny → 400
|
||||
var overlap = grant.Intersect(deny, StringComparer.Ordinal).ToArray();
|
||||
if (overlap.Length > 0)
|
||||
throw new GrantDenyOverlapException(overlap);
|
||||
|
||||
// 2. Catalog existence check
|
||||
var allCodes = grant.Concat(deny).Distinct(StringComparer.Ordinal).ToArray();
|
||||
if (allCodes.Length > 0)
|
||||
{
|
||||
var existentes = await _permisoRepo.GetByCodigosAsync(allCodes);
|
||||
var existSet = existentes.Select(p => p.Codigo).ToHashSet(StringComparer.Ordinal);
|
||||
var faltantes = allCodes.Where(c => !existSet.Contains(c)).ToArray();
|
||||
if (faltantes.Length > 0)
|
||||
throw new InvalidPermisoCodesException(faltantes);
|
||||
}
|
||||
|
||||
// 3. Load usuario
|
||||
var usuario = await _usuarioRepo.GetByIdAsync(command.Id)
|
||||
?? throw new UsuarioNotFoundException(command.Id);
|
||||
|
||||
// 4. Persist — use WithPermisosJson to get updated FechaModificacion
|
||||
var newOverrides = new PermisosOverride(grant, deny);
|
||||
var updated = usuario.WithPermisosJson(newOverrides.ToJson());
|
||||
await _usuarioRepo.UpdatePermisosJsonAsync(
|
||||
updated.Id,
|
||||
updated.PermisosJson,
|
||||
updated.FechaModificacion!.Value);
|
||||
|
||||
// 5. Return updated effective set
|
||||
var rolPermisoEntities = await _rolPermisoRepo.GetByRolCodigoAsync(updated.Rol);
|
||||
var rolPermisos = rolPermisoEntities
|
||||
.Select(p => p.Codigo)
|
||||
.OrderBy(c => c, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
var effective = PermisoResolver.Resolve(rolPermisos, newOverrides)
|
||||
.OrderBy(c => c, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
return new UsuarioPermisosDto(
|
||||
UsuarioId: updated.Id,
|
||||
Rol: updated.Rol,
|
||||
RolPermisos: rolPermisos,
|
||||
Grant: grant,
|
||||
Deny: deny,
|
||||
Effective: effective);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user