73 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			73 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using DysonNetwork.Shared.Proto;
 | 
						|
using Grpc.Core;
 | 
						|
using Microsoft.AspNetCore.Http;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
 | 
						|
namespace DysonNetwork.Shared.Auth
 | 
						|
{
 | 
						|
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
 | 
						|
    public class RequiredPermissionAttribute(string area, string key) : Attribute
 | 
						|
    {
 | 
						|
        public string Area { get; set; } = area;
 | 
						|
        public string Key { get; } = key;
 | 
						|
    }
 | 
						|
 | 
						|
    public class PermissionMiddleware(RequestDelegate next)
 | 
						|
    {
 | 
						|
        public async Task InvokeAsync(HttpContext httpContext, PermissionService.PermissionServiceClient permissionService, ILogger<PermissionMiddleware> logger)
 | 
						|
        {
 | 
						|
            var endpoint = httpContext.GetEndpoint();
 | 
						|
 | 
						|
            var attr = endpoint?.Metadata
 | 
						|
                .OfType<RequiredPermissionAttribute>()
 | 
						|
                .FirstOrDefault();
 | 
						|
 | 
						|
            if (attr != null)
 | 
						|
            {
 | 
						|
                if (httpContext.Items["CurrentUser"] is not Account currentUser)
 | 
						|
                {
 | 
						|
                    httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
 | 
						|
                    await httpContext.Response.WriteAsync("Unauthorized");
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                // Assuming Account proto has a bool field 'is_superuser' which is generated as 'IsSuperuser'
 | 
						|
                if (currentUser.IsSuperuser)
 | 
						|
                {
 | 
						|
                    // Bypass the permission check for performance
 | 
						|
                    await next(httpContext);
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                var actor = $"user:{currentUser.Id}";
 | 
						|
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    var permResp = await permissionService.HasPermissionAsync(new HasPermissionRequest
 | 
						|
                    {
 | 
						|
                        Actor = actor,
 | 
						|
                        Area = attr.Area,
 | 
						|
                        Key = attr.Key
 | 
						|
                    });
 | 
						|
 | 
						|
                    if (!permResp.HasPermission)
 | 
						|
                    {
 | 
						|
                        httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
 | 
						|
                        await httpContext.Response.WriteAsync($"Permission {attr.Area}/{attr.Key} was required.");
 | 
						|
                        return;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                catch (RpcException ex)
 | 
						|
                {
 | 
						|
                    logger.LogError(ex, "gRPC call to PermissionService failed while checking permission {Area}/{Key} for actor {Actor}", attr.Area, attr.Key, actor);
 | 
						|
                    httpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
 | 
						|
                    await httpContext.Response.WriteAsync("Error checking permissions.");
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            await next(httpContext);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |