How to Combine/Compose Authorization Handlers in ASP.Net Core?

Viewed 1722

How do I re-use AuthorizationHandlers to compose a composite requirement of the two handlers?

  • RequirementA with one Handler IsAllowedAccessToA : AuthorizationHandler<RequirementA>
  • RequirementB with one Handler IsAllowedAccessToB : AuthorizationHandler<RequirementB>
  • RequirementA_OR_B where if it meets IsAllowedAccessToA or IsAllowedAccessToB it succeeds

I have resources that are only accessible to RequirementA and the same for RequirementB. I also have resources that are available to A or B.

I can't figure out how to do this without duplicating IsAllowedAccessToA and IsAllowedAccessToB handlers

This article helps but is not exactly the same use case.

4 Answers

try this:

you're requirement class is like this:

public class PermissionRequirement : IAuthorizationRequirement
{
    public PermissionRequirement(string permission)
    {
        this.Permission = permission;
    }

    public string Permission { get; }
}

and the handler should be like this:

public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly IPermissionProvider _permissionProvider;

    private readonly IUserProvider _userProvider;

    public PermissionAuthorizationHandler(IPermissionProvider permissionProvider, IUserProvider userProvider)
    {
        // permissionProvider is a class that has a function called hasClaim, with bool return value that takes user id and claim as input arguments and realize weather the user id has access to the controller or not
        this._permissionProvider = permissionProvider;

        // userProvider, return the id of current user
        this._userProvider = userProvider;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        var hasClaim= await this._permissionProvider.HasClaim(this._userProvider.GetUserId(), requirement.Permission)
                                .ConfigureAwait(false);

        if (hasClaim) context.Succeed(requirement);
        else
            context.Fail();
    }
}

Actually, the article you point to gives all the answers. The only thing for you to do is defining the handlers in such a way that they can be used for different requirements, i.e., make them generic. Later, you will just register them as services using different type parameters for every requirement.

So your handlers will look something like:

public class IsAllowedAccessToA<T>
    : AuthorizationHandler<T> where T : IAuthorizationRequirement
{
}

And you will register them to use for the composite requirement like:

services.AddSingleton<IAuthorizationHandler, IsAllowedAccessToA<RequirementA_OR_B>>();
services.AddSingleton<IAuthorizationHandler, IsAllowedAccessToB<RequirementA_OR_B>>();

This should do what you need.

Related