Iceshrimp.NET/Iceshrimp.Backend/Core/Middleware/AuthorizationMiddleware.cs
Laura Hausmann 705e061f74
[backend/asp] Refactor middleware stack
This commit splits the request pipeline conditionally instead of invoking every middleware in the stack.

It also simplifies middleware instantiation by using runtime discovery, allowing for Plugins to add Middleware.
2024-11-18 19:02:44 +01:00

50 lines
No EOL
2.1 KiB
C#

using Iceshrimp.Backend.Controllers.Mastodon.Attributes;
using Iceshrimp.Backend.Core.Extensions;
using Iceshrimp.Backend.Core.Helpers;
namespace Iceshrimp.Backend.Core.Middleware;
public class AuthorizationMiddleware(RequestDelegate next) : ConditionalMiddleware<AuthorizeAttribute>
{
public async Task InvokeAsync(HttpContext ctx)
{
var endpoint = ctx.GetEndpoint();
var attribute = GetAttributeOrFail(ctx);
ctx.Response.Headers.CacheControl = "private, no-store";
var isMastodon = endpoint?.Metadata.GetMetadata<MastodonApiControllerAttribute>() != null;
if (isMastodon)
{
var token = ctx.GetOauthToken();
if (token is not { Active: true })
throw GracefulException.Unauthorized("This method requires an authenticated user");
if (attribute.Scopes.Length > 0 &&
attribute.Scopes.Except(MastodonOauthHelpers.ExpandScopes(token.Scopes)).Any())
throw GracefulException.Forbidden("This action is outside the authorized scopes");
if (attribute.AdminRole && !token.User.IsAdmin)
throw GracefulException.Forbidden("This action is outside the authorized scopes");
if (attribute.ModeratorRole && token.User is { IsAdmin: false, IsModerator: false })
throw GracefulException.Forbidden("This action is outside the authorized scopes");
}
else
{
var session = ctx.GetSession();
if (session is not { Active: true })
throw GracefulException.Unauthorized("This method requires an authenticated user");
if (attribute.AdminRole && !session.User.IsAdmin)
throw GracefulException.Forbidden("This action is outside the authorized scopes");
if (attribute.ModeratorRole && session.User is { IsAdmin: false, IsModerator: false })
throw GracefulException.Forbidden("This action is outside the authorized scopes");
}
await next(ctx);
}
}
public class AuthorizeAttribute(params string[] scopes) : Attribute
{
public readonly bool AdminRole = scopes.Contains("role:admin");
public readonly bool ModeratorRole = scopes.Contains("role:moderator");
public readonly string[] Scopes = scopes.Where(p => !p.StartsWith("role:")).ToArray();
}