using System.Security.Claims; using adas_core.Application.Exceptions; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; namespace adas_core.Authentication.Attributes; public class PermissionAuthorizeAttribute(string source, string? resourceIdHeader = null) : AuthorizeAttribute, IAsyncAuthorizationFilter { /// /// Performs asynchronous authorization by validating the user's roles, username, and source header against the permission service. /// For the Panel source, it verifies that at least one of the user's roles grants access to the panel; for other sources, it requires a resource identifier header (such as DisplayId or UnitId) and checks access to the corresponding resource. /// If any required value is missing or no role grants the needed permission, the response is set to . /// /// The for the current authorization request, providing access to the HTTP context, services, and the result to set when authorization fails. /// Thrown when the cannot be resolved from the request services. public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { // Obtener el servicio de permisos. var permissionService = context.HttpContext.RequestServices.GetService() ?? throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); var user = context.HttpContext.User; // Obtener el header que indica la fuente. var headerSource = context.HttpContext.Request.Headers[source].FirstOrDefault(); // Obtener el identificador del usuario var username = user.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; // Obtiene la lista de roles (valores) del usuario. var userRoles = user.Claims .Where(c => c.Type == ClaimTypes.Role) .Select(c => c.Value) .ToList(); // Procesa cada rol: si contiene 2 guiones bajos, elimina el tercer segmento. var processedRoles = userRoles.Select(role => { var parts = role.Split('_'); return parts.Length == 2 ? parts[0] : role; }).ToList(); // Validar que se tenga al menos un rol, username y headerSource. if (!processedRoles.Any() || string.IsNullOrEmpty(username) || string.IsNullOrEmpty(headerSource)) { context.Result = new ForbidResult(); return; } // Parseamos el header para obtener la fuente. var source1 = (PermissionEnum.SourcePermissionsEnum)Enum.Parse(typeof(PermissionEnum.SourcePermissionsEnum), headerSource); var hasPermission = false; if (source1 == PermissionEnum.SourcePermissionsEnum.Panel) { // Si la fuente es PANEL, verificamos que alguno de los roles tenga acceso. foreach (var roleStr in processedRoles) { if (!Enum.TryParse(typeof(PermissionEnum.RolesType), roleStr, out var roleObj)) continue; var role = (PermissionEnum.RolesType)roleObj; if (!await permissionService.HasAccessToPanel(username, role, source1)) continue; hasPermission = true; break; } if (!hasPermission) context.Result = new ForbidResult(); } else if (resourceIdHeader != null) { // Para otras fuentes, se requiere un header que indique el resourceId. var resourceId = context.HttpContext.Request.Headers[resourceIdHeader].FirstOrDefault(); if (string.IsNullOrEmpty(resourceId)) { context.Result = new ForbidResult(); return; } // Verificar que al menos uno de los roles tenga acceso al recurso. foreach (var roleStr in processedRoles) if (Enum.TryParse(typeof(PermissionEnum.RolesType), roleStr, out var roleObj)) { var role = (PermissionEnum.RolesType)roleObj; hasPermission = resourceIdHeader switch { "DisplayId" => await permissionService.HasAccessToDisplay(username, role, source1, resourceId), "UnitId" => await permissionService.HasAccessToUnit(username, role, source1, resourceId), _ => false }; if (hasPermission) break; } if (!hasPermission) context.Result = new ForbidResult(); } } }