Policy based authorization / Reusable authorization handler

Viewed 198

Is it possible to reuse authorization handlers? Following the documentation on: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-5.0#why-would-i-want-multiple-handlers-for-a-requirement

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

I would like to reuse this logic for example to create 'WarehouseEntryRequirement' that would allow access with BadgeId or CEO claim. Is there a better way than copy paste the
public class BadgeEntryHandler : AuthorizationHandler and change it to: public class BadgeEntryHandler : AuthorizationHandler

What is the best approach to let the Superuser access any endpoint but allowing further authorization process for non-superusers ?

Thanks in advance

1 Answers

This could be done in a generic fashion as per below.

    public interface IRequireClaim
    {
        string RequiredClaim { get; }
    }

    public class BuildingEntryRequirement : IAuthorizationRequirement, IRequireClaim
    {
        public string RequiredClaim { get => "BadgeId"; }
    }

    public class WarehouseEntryRequirement : IAuthorizationRequirement, IRequireClaim
    {
        public string RequiredClaim { get => "CEOROLE"; }
    }

    public class EntryHandler<T> : AuthorizationHandler<T> where T : IAuthorizationRequirement, IRequireClaim
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                       T requirement)
        {
            if (context.User.HasClaim(c => c.Type == requirement.RequiredClaim &&
                                           c.Issuer == "http://microsoftsecurity"))
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }       
    }

    public class BuildingEntryHandler : EntryHandler<BuildingEntryRequirement> { }

    public class WarehouseEntryHandler : EntryHandler<WarehouseEntryRequirement> { }

Alternatively, it could be done with the func style as below.

options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" &&
                 c.Issuer == "https://microsoftsecurity")));

options.AddPolicy("CEOEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "CEOROLE" &&
                 c.Issuer == "https://microsoftsecurity")));

The commonalities could then be extracted out into relevant methods as appropriate.

Related