62 lines
2.7 KiB
C#

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<AllowAnonymousAttribute>().Any();
}
}