Home > Net >  Override the [Authorize(Roles = "xxx")]
Override the [Authorize(Roles = "xxx")]

Time:01-12

I have an API controller

[ApiController]
public class DataController : ControllerBase
{
    public ActionResult Get1()
    {}

    [Authorize]
    public ActionResult Get2()
    {}

    [Authorize(Role="xxx")]
    public ActionResult Get3()
    {}
}

In my Startup I've specified:

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder().AddRequirements(new DataRequirement()).Build();
    options.FallbackPolicy = new AuthorizationPolicyBuilder().AddRequirements(new DataRequirement()).Build();
});

Which takes care of the Get1 (fallback) and Get2 (default).

But how can I customize a Policy handling this decoration [Authorize(Role="xxx")]?

I actually don't want to use the Role-parameter, but rather something like [Authorize(RoleSuffix="_yyy")] coz I can't enter the full AdGroup name, only the last part. And that's why I need to customize this.

Requirements:

  • I don't want to use a FilterAttribute, which works great eg: [DoAuthorize("_xxx")]
  • I can't change in AD and put groups in groups etc

The closest thing I've come up with is to specify a Policy in the decoration [Authorize(Policy = "policy")] but how can I specify a suffix-parameter to this policy? I don't want to create 1 policy per suffix. I would like to specify the suffix at the method.

The reason I need to customize a lot here: I have a few special groups that should have access to everything, and a few based on the group's suffix, and one basic default for methods with no suffix specified.

Any ideas here?

CodePudding user response:

Check Parameterized authorize attribute example.

Here is an example of how you can implement this for your scenario:

public class SuffixAuthorizeAttribute : AuthorizeAttribute
{
    const string POLICY_PREFIX = "RoleSuffix";

    public SuffixAuthorizeAttribute(string suffix) => Suffix = suffix;

    public string Suffix
    {
        get
        {
            return Policy == null ? string.Empty : Policy[POLICY_PREFIX.Length..];
        }
        set
        {
            Policy = $"{POLICY_PREFIX}{value}";
        }
    }
}

public class SuffixPolicyProvider : IAuthorizationPolicyProvider
{
    const string POLICY_PREFIX = "RoleSuffix";

    private DefaultAuthorizationPolicyProvider BackupPolicyProvider { get; }

    public SuffixPolicyProvider(IOptions<AuthorizationOptions> options)
    {
        BackupPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        => BackupPolicyProvider.GetDefaultPolicyAsync();

    public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
        => BackupPolicyProvider.GetFallbackPolicyAsync();

    public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
    {
        if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase))
        {
            var suffix = policyName[POLICY_PREFIX.Length..];

            var policy = new AuthorizationPolicyBuilder();

            policy.RequireAssertion(
              context => context.User.HasClaim(c => c.Type == ClaimTypes.Role && c.Value.EndsWith(suffix)));

            return Task.FromResult(policy?.Build());
        }

        return BackupPolicyProvider.GetPolicyAsync(policyName);
    }
}

Register the custom IAuthorizationPolicyProvider:

builder.Services.AddSingleton<IAuthorizationPolicyProvider, SuffixPolicyProvider>();

Use like this:

[SuffixAuthorize("_yyy")]
public ActionResult Get3()
{
}

this is the equivalent of doing: [Authorize(Policy = "RoleSuffix_yyy")]. Only difference is you don't have to create a policy for each suffix because the policy is created "dynamically" from the custom IAuthorizationPolicyProvider.

  • Related