UDT-005: Gestión de Permisos (RBAC) — catálogo + asignación rol↔permisos #9
@@ -16,13 +16,16 @@ public sealed class PermisosController : ControllerBase
|
|||||||
{
|
{
|
||||||
private readonly IDispatcher _dispatcher;
|
private readonly IDispatcher _dispatcher;
|
||||||
private readonly IValidator<AssignPermisosToRolCommand> _assignValidator;
|
private readonly IValidator<AssignPermisosToRolCommand> _assignValidator;
|
||||||
|
private readonly IValidator<GetRolPermisosQuery> _getRolPermisosValidator;
|
||||||
|
|
||||||
public PermisosController(
|
public PermisosController(
|
||||||
IDispatcher dispatcher,
|
IDispatcher dispatcher,
|
||||||
IValidator<AssignPermisosToRolCommand> assignValidator)
|
IValidator<AssignPermisosToRolCommand> assignValidator,
|
||||||
|
IValidator<GetRolPermisosQuery> getRolPermisosValidator)
|
||||||
{
|
{
|
||||||
_dispatcher = dispatcher;
|
_dispatcher = dispatcher;
|
||||||
_assignValidator = assignValidator;
|
_assignValidator = assignValidator;
|
||||||
|
_getRolPermisosValidator = getRolPermisosValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Lists all permisos in the canonical catalog. Requires admin role.</summary>
|
/// <summary>Lists all permisos in the canonical catalog. Requires admin role.</summary>
|
||||||
@@ -39,13 +42,23 @@ public sealed class PermisosController : ControllerBase
|
|||||||
/// <summary>Gets all permisos assigned to a rol. Requires admin role.</summary>
|
/// <summary>Gets all permisos assigned to a rol. Requires admin role.</summary>
|
||||||
[HttpGet("roles/{codigo}/permisos")]
|
[HttpGet("roles/{codigo}/permisos")]
|
||||||
[ProducesResponseType(typeof(IReadOnlyList<PermisoDto>), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(IReadOnlyList<PermisoDto>), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public async Task<IActionResult> GetRolPermisos(string codigo)
|
public async Task<IActionResult> GetRolPermisos(string codigo)
|
||||||
{
|
{
|
||||||
var result = await _dispatcher.Send<GetRolPermisosQuery, IReadOnlyList<PermisoDto>>(
|
var query = new GetRolPermisosQuery(codigo);
|
||||||
new GetRolPermisosQuery(codigo));
|
var validation = await _getRolPermisosValidator.ValidateAsync(query);
|
||||||
|
if (!validation.IsValid)
|
||||||
|
{
|
||||||
|
var errors = validation.Errors
|
||||||
|
.GroupBy(e => e.PropertyName)
|
||||||
|
.ToDictionary(g => g.Key, g => g.Select(e => e.ErrorMessage).ToArray());
|
||||||
|
return BadRequest(new { errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _dispatcher.Send<GetRolPermisosQuery, IReadOnlyList<PermisoDto>>(query);
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
|
||||||
|
namespace SIGCM2.Application.Permisos.GetByRol;
|
||||||
|
|
||||||
|
public sealed class GetRolPermisosQueryValidator : AbstractValidator<GetRolPermisosQuery>
|
||||||
|
{
|
||||||
|
public GetRolPermisosQueryValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.RolCodigo)
|
||||||
|
.NotEmpty().WithMessage("El código del rol es requerido.")
|
||||||
|
.Matches(@"^[a-z][a-z0-9_]*$")
|
||||||
|
.WithMessage("El código del rol debe empezar con una letra minúscula y contener solo minúsculas, dígitos o guion bajo.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -250,6 +250,17 @@ public sealed class PermisosEndpointTests : IAsyncLifetime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetRolPermisos_InvalidCodigoFormat_Returns400()
|
||||||
|
{
|
||||||
|
var token = await GetBearerTokenAsync(AdminUsername, AdminPassword);
|
||||||
|
// "ROL-INVALIDO" no matchea ^[a-z][a-z0-9_]*$ (tiene guion y mayúsculas)
|
||||||
|
using var req = BuildRequest(HttpMethod.Get, "/api/v1/roles/ROL-INVALIDO/permisos", bearerToken: token);
|
||||||
|
var resp = await _client.SendAsync(req);
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
// ── PUT /api/v1/roles/{codigo}/permisos ──────────────────────────────────
|
// ── PUT /api/v1/roles/{codigo}/permisos ──────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using FluentValidation;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using SIGCM2.Application.Abstractions.Persistence;
|
using SIGCM2.Application.Abstractions.Persistence;
|
||||||
using SIGCM2.Application.Permisos.GetByRol;
|
using SIGCM2.Application.Permisos.GetByRol;
|
||||||
@@ -89,3 +90,33 @@ public class GetRolPermisosQueryHandlerTests
|
|||||||
Assert.Equal(18, result.Count);
|
Assert.Equal(18, result.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class GetRolPermisosQueryValidatorTests
|
||||||
|
{
|
||||||
|
private readonly IValidator<GetRolPermisosQuery> _validator =
|
||||||
|
new GetRolPermisosQueryValidator();
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("ROL-INVALIDO")]
|
||||||
|
[InlineData("ROL:INVALIDO")]
|
||||||
|
[InlineData("123abc")]
|
||||||
|
[InlineData("UPPER")]
|
||||||
|
[InlineData("con espacio")]
|
||||||
|
[InlineData("")]
|
||||||
|
public async Task Validate_InvalidCodigoFormat_ReturnsInvalid(string codigo)
|
||||||
|
{
|
||||||
|
var result = await _validator.ValidateAsync(new GetRolPermisosQuery(codigo));
|
||||||
|
Assert.False(result.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("admin")]
|
||||||
|
[InlineData("cajero")]
|
||||||
|
[InlineData("rol_valido")]
|
||||||
|
[InlineData("abc123")]
|
||||||
|
public async Task Validate_ValidCodigoFormat_ReturnsValid(string codigo)
|
||||||
|
{
|
||||||
|
var result = await _validator.ValidateAsync(new GetRolPermisosQuery(codigo));
|
||||||
|
Assert.True(result.IsValid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user