using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Suspectus.Gandalf.Palantir.Abstractions; using Suspectus.Gandalf.Palantir.Data.Entities.Security; namespace Suspectus.Gandalf.Palantir.Security; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] public class GrAuthorizeAttribute : Attribute, IAuthorizationFilter { public required string[] Authorities { get; init; } public required AuthorityType Type { get; init; } public required string ParameterName { get; init; } public bool AllRequired { get; init; } = true; public void OnAuthorization(AuthorizationFilterContext context) { if(IsAnonymousAllowed(context)) return; var invoker = (Invoker)context.HttpContext.User; if (!invoker.IsAuthenticated) { HandleResponse(context, "One does not simply access this endpoint not authenticated.", StatusCodes.Status401Unauthorized); return; } var id = context.HttpContext.Request.RouteValues.GetValueOrDefault(ParameterName)?.ToString(); if (id is null) { HandleResponse(context, "One does not simply cause a internal server error.", StatusCodes.Status500InternalServerError); return; } var missingAuthorities = Type switch { AuthorityType.Tenant => Authorities.Where(x => !(invoker.TenantAuthorityDictionary.GetValueOrDefault(id)?.Contains(x) ?? false)).ToArray(), AuthorityType.App => Authorities.Where(x => !(invoker.AppAuthorityDictionary.GetValueOrDefault(id)?.Contains(x) ?? false)).ToArray(), _ => Authorities }; if (missingAuthorities.Length == 0) return; var typeName = Type == AuthorityType.App ? "app" : "tenant"; HandleResponse(context, $"One does not simply access this endpoint without {typeName} authorization.", StatusCodes.Status403Forbidden, missingAuthorities); } private static void HandleResponse(AuthorizationFilterContext context, string message, int statusCode, string[]? missingAuthorities = null) { context.Result = missingAuthorities is null ? new JsonResult(new { StatusCode = statusCode, Message = message }) { StatusCode = statusCode } : new JsonResult(new { StatusCode = statusCode, Message = message, MissingAuthorities = missingAuthorities }) { StatusCode = statusCode }; } private static bool IsAnonymousAllowed(AuthorizationFilterContext context) { return context.ActionDescriptor.EndpointMetadata.OfType().Any(); } }