Puedes utilizar la clase de .NET OperationAuthorizationRequirement la cual nos facilita registrar todos los posibles permisos que maneja tu aplicación. Creamos una clase Operaciones en nuestra carpeta Core y agregamos los tipos de permisos que manejará nuestra aplicación.
Operaciones.cs
publicstaticclassOperaciones{publicstaticOperationAuthorizationRequirement Crear =new OperationAuthorizationRequirement { Name ="Crear" };publicstaticOperationAuthorizationRequirement Modificar =new OperationAuthorizationRequirement { Name ="Modificar" };publicstaticOperationAuthorizationRequirement Borrar =new OperationAuthorizationRequirement { Name ="Borrar" };publicstaticOperationAuthorizationRequirement Consultar =new OperationAuthorizationRequirement { Name ="Consultar" };}
Creamos una clase PermisoDTO el cual nos indica que tabla deseamos validar, y también indicamos una variable SoloAdministradores en el cual si esta en true esta tabla sólo es accesible a los usuarios con el rol de Administrador.
PermisoDTO.cs
namespaceCaducaRest.DTO;/// <summary>/// Permite validar los permisos de cada servicio/// </summary>publicclassPermisoDTO{ /// <summary> /// Nombre de la tabla a validar /// </summary>publicstring Tabla { get; set; } /// <summary> /// Si esta en true se requiere permiso de Administrador /// </summary>publicbool EsAdministrador { get; set; } =false;}
Creamos nuestra clase RolTablaPermisoDAO la cual nos va a ayudar a validar que el usuario tenga acceso a la tabla de acuerdo a su rol, para la operación a realizar
Para nuestras reglas personalizadas es necesario crear una clase que herede de AuthorizationHandler, la cual recibe como objetos genéricos un objeto de la clase OperationAuthorizationRequirement y cualquier otro dato adicional en nuestro caso es nuestra clase PermisoDTO. Agregamos una clase llamada PermisoEditHandler en nuestra carpeta Core
PermisoEditHandler.cs
usingSystem;usingSystem.Security.Claims;usingSystem.Threading.Tasks;usingCaducaRest.DTO;usingCaducaRest.Models;usingCaducaRest.DAO;usingCaducaRest.Resources;usingMicrosoft.AspNetCore.Authorization;usingMicrosoft.AspNetCore.Authorization.Infrastructure;namespaceCaducaRest.Core{publicclassPermisoEditHandler:AuthorizationHandler <OperationAuthorizationRequirement,PermisoDTO> {privatereadonlyCaducaContext contexto;privatereadonlyLocService _localizer;publicPermisoEditHandler(CaducaContext context,LocService localizer) { contexto = context; _localizer = localizer; }protectedoverrideTaskHandleRequirementAsync(AuthorizationHandlerContext context,OperationAuthorizationRequirement operacion,PermisoDTO recurso) {var usuarioId =Convert.ToInt32(context.User .FindFirst(c =>c.Type==ClaimTypes.Sid).Value); //Se revisa si en el token se incluye el claim con el // id del usuarioif (!context.User.HasClaim(c =>c.Type==ClaimTypes.Sid))context.Fail();RolDAO rolDAO =newRolDAO(contexto, _localizer);bool esAdministrador =rolDAO.EsAdministrador(usuarioId); //Si el recurso requiere un usuario administrador // se valida que el usuario sea de tipo administrador // si no es asi, mandar errorif (recurso.RequiereAdministrador&&!esAdministrador)context.Fail();else { if (!esAdministrador) { //Se revisa si el usuario tiene autorización para realizar la acciónRolTablaPermisoDAO rolTablaPermisoDAO =newRolTablaPermisoDAO(contexto, _localizer);if (!rolTablaPermisoDAO.TienePermiso(usuarioId,recurso.Tabla,operacion.Name))context.Fail();elsecontext.Succeed(operacion); }context.Succeed(operacion); }returnTask.CompletedTask; } }}
En nuestro archivo CategoriasController agregamos un objeto de la clase PermisoDTO indicando la tabla Categoria, y le indicamos que este servicio no requiere de un usuario Administrador.
Agregamos al archivo de recursos los siguientes mensajes de error:
Clave
Valor
ForbiddenUpdate
No tienes permiso para actualizar {0}. Por favor solicita el acceso.
ForbiddenAdd
No tienes permiso para agregar {0}. Por favor solicita el acceso.
ForbiddenDelete
No tienes permiso para borrar {0}. Por favor solicita el acceso.
En nuestro servicio get, agregamos la validación
CategoriasController.cs
publicclassCategoriasController:ControllerBase{publicCategoriasController(CaducaContext context,LocService localizer,IAuthorizationService authorizationService) { //Código permisoDTO =newPermisoDTO { Tabla ="Categoria", RequiereAdministrador =false }; } [HttpGet] [Authorize(Roles ="Administrador, Vendedor")]publicasyncTask<List<Categoria>> GetCategoriaAsync() { //Agregamos nuestra validación personalizadavar authorizationResult =await _authorizationService .AuthorizeAsync(User, permisoDTO,Operaciones.Consultar); //Si el resultado no fue exitoso regresamos una lista vaciaif (!authorizationResult.Succeeded)returnnewList<Categoria>();returnawaitcategoriaDAO.ObtenerTodoAsync(); } [HttpPut("{id}")] [Authorize(Roles ="Administrador")]publicasyncTask<IActionResult> PutCategoria([FromRoute] int id, [FromBody] Categoria categoria) {var authorizationResult =await _authorizationService .AuthorizeAsync(User, permisoDTO,Operaciones.Modificar); //Si el resultado no fue exitoso regresamos errorif (!authorizationResult.Succeeded)returnStatusCode(403,String.Format(this.localizacion.GetLocalizedHtmlString("ForbiddenUpdate"),"La categoría")); //Código } [HttpPost] [Authorize(Roles ="Administrador")]publicasyncTask<IActionResult> PostCategoria( [FromBody] Categoria categoria) {var authorizationResult =await _authorizationService .AuthorizeAsync(User, permisoDTO,Operaciones.Crear); //Si el resultado no fue exitoso regresamos una lista vaciaif (!authorizationResult.Succeeded)returnStatusCode(403,String.Format(this.localizacion.GetLocalizedHtmlString("ForbiddenAdd"),"La categoría")); //Código } [HttpDelete("{id}")] [Authorize(Roles ="Administrador")]publicasyncTask<IActionResult> DeleteCategoria([FromRoute] int id) {var authorizationResult =await _authorizationService .AuthorizeAsync(User, permisoDTO,Operaciones.Borrar); //Si el resultado no fue exitoso regresamos una lista vaciaif (!authorizationResult.Succeeded)returnStatusCode(403,String.Format(this.localizacion.GetLocalizedHtmlString("ForbiddenDelete"),"La categoría")); //Código}