Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IAuthorizationPolicyProvider: can I use multiple policy providers or I have to consider all cases inside one general policy provider?

In my program I want to

  1. Check if users are registered in a certain users table for all possible actions.
  2. Just for certain actions, I want also to check if the user has the appropriate CRUD rights.

I check the 1st condition in startup with the following code:

services.AddAuthorization(options =>
{
    // This policy checks if a user is registered in our Users table.
    options.AddPolicy(
        "UserIsRegistered",
        new AuthorizationPolicyBuilder()
            .AddRequirements(new RegistrationRequirement())
            .Build());
});

I also add

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers().RequireAuthorization("UserIsRegistered");
});

and I define the RegistrationRequirement and the RegistrationAuthorizationHandler accordingly.

For the 2nd condition, I created an attribute CrudAuthorizeAttribute with its CrudAuthorizationHandler. Since I have one different police for each possible Crud action, I created a CrudPolicyProvider.

Then I registered everything in Startup:

services.AddTransient<IAuthorizationHandler, RegistrationAuthorizationHandler>();
services.AddTransient<IAuthorizationHandler, CrudAuthorizationHandler>();
services.AddTransient<IAuthorizationPolicyProvider, CrudPolicyProvider>();

Now I see when I am checking the 1st condition I'm getting the policy from CrudPolicyProvider but I expect this policy generator to provide policies only in the 2nd condition.

Therefore:

  • Is there any way to specify that CrudPolicyProvider has to be used only with the CrudAttribute and nowhere else?
  • More generally, can I define two policy providers (as I can do with IAuthorizationHandler)
services.AddTransient<IAuthorizationPolicyProvider, FirstPolicyProvider>();
services.AddTransient<IAuthorizationPolicyProvider, SecondPolicyProvider>();

and use each one only when required?

  • If the answer is no, does it mean that I have to define a single GeneralPolicyProvider and inside this provider check which policies I have to provide each time depending on the situation I am? (something like using the POLICY_PREFIX here?).

Thanks!

like image 374
xavier Avatar asked Oct 12 '25 02:10

xavier


1 Answers

can I define two policy providers (as I can do with IAuthorizationHandler)and use each one only when required

You could. But the two separated policy providers won't be activated correspondingly. You might have multiple policy providers, but only one of them will be used at the same time. Also there's no magic when using IAuthorizationPolicyProvider.

define a single GeneralPolicyProvider .... check which policies I have to provide each time depending on the situation I am? (something like using the POLICY_PREFIX here?).

Yes. But you don't have to use Policy_Prefix everywhere. In that way you'll repeat yourself too many times.

A better way is to inject a IHttpContextAccessor service into GeneralPolicyProvider, such that you can check the current EndPoint/HttpContext at run-time, and then you can resolve the target Policy Provider dynamically.

An implementation looks like:

public class GenericPolicyProvider : IAuthorizationPolicyProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private AuthorizationOptions _authZOpts { get; }
    public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
    private IAuthorizationPolicyProvider _fstPolicyProvider  {get;set;}
    private IAuthorizationPolicyProvider _sndPolicyProvider  {get;set;}

    public GenericPolicyProvider(IHttpContextAccessor httpContextAccessor, IOptions<AuthorizationOptions> options)
    {
        this._httpContextAccessor = httpContextAccessor;
        this._authZOpts = options.Value;
        this.FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
        this._fstPolicyProvider = new FirstPolicyProvider(options.Value);
        this._sndPolicyProvider = new SecondPolicyProvider(options.Value);
    }

    // use the target provider to provide policy
    public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
    {
        var targetPolicyProvider = this.GetPolicyProvider(policyName);
        return targetPolicyProvider.GetPolicyAsync(policyName);
    }

    // get the target provider dynamically
    private IAuthorizationPolicyProvider GetPolicyProvider(string policyName)
    {

        var httpContext = this._httpContextAccessor.HttpContext;
        if(httpContext==null) throw new Exception("HttpContext must not be null");
        // now you get the HttpContext
        //    check HttpContext to determine which policy provider should be used
        // ...
        // or check endpoint,e.g. get a mark filter by endpoint.Metadata.GetMetadata<...>()
        var endpoint = httpContext.GetEndpoint();
        var someMarker = endpoint.Metadata.GetMetadata<SomeMarker>();
        // in short, resolve the policy provider dynamically:
        if(shouldUseFirstPolicyProvider())
            return this._fstPolicyProvider;
        else if(shouldUseSecondPolicyProvider())
            return this._sndPolicyProvider;
        else 
           return this.FallbackPolicyProvider;
    }
    ...
}

Finally, don't forget to register this GenericPolicyProvider in Startup.

like image 74
itminus Avatar answered Oct 16 '25 05:10

itminus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!