In ASP.NET Core 6 , if I want my own way of authorizing, it seems that I should implement an IAuthorizationRequirement
and a handler that inherits from AuthorizationHandler<T>
for that requirement, then use it via policy. I did that just fine and it works.
I'd like to have some users who can still be authorized by role or be authorized based on other requirements.
So therefore, I want to retain the default roles-based authorization but in my handler in some cases alter the roles before authorization-proper happens. Can I somehow compose it--perhaps call the default roles-based handler from my handler? Or would I need to re-implement everything the default handler does--and if so, how?
None of the examples I can find seem to attempt this--they all seem to implement their own thing from scratch, not paying attention to roles at all.
So how do I extend the default Roles-based authorization without having to implement my own full handler from scratch?
CodePudding user response:
I'd like to have some users who can still be authorized by role but have additional restrictions based on such requirements.
Role-based authorization could work together with Policy-based Authorization
Add the Authorize
Attribute like:[Authorize(Role="SomeRole",Policy="SomePolicy")]
or regist the policy like:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdministratorRoleWithSomeOtherRequirement",policy
=>
{
policy.RequireRole("Administrator"));
policy.AddRequirement(new SomeRequirement(input));
});
options.AddPolicy("AnotherPolicy",policy
=>
{
policy.RequireRole("Administrator","OtherRoles"));
policy.AddRequirement(new OtherRequirement(input));
});
});
CodePudding user response:
I wanted to do this with a new policy, but I could not figure out how, as even if I inherit my new AuthorizationRequirement from RolesAuthorizationRequirement
, and then implement everything needed to replace the default handler, I'd still have to provide the roles to the requirement when I AddPolicy in Program.cs, which I don't have.
Luckily, my need does not require any additional data in a new AuthorizationRequirement class, so I can forgo that and just totally override the default handler. So, in Program.cs:
builder.Services
.AddAuthorization()
.AddSingleton<IAuthorizationHandler, NonproductionAuthorizeHandler>() // note this makes NonproductionAuthorizeHandler override the default handler
...
And the new handler:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
namespace MyNamespace.Server.Helpers;
/// <summary>
/// Handles authorization to allow access to attribute-specified roles in any environment,
/// as well as special logic that need not be specified in
/// any AuthorizationAttribute (which may appear on any controller or controller method).
/// </summary>
public class SpecialLogicAuthorizeHandler : AuthorizationHandler<RolesAuthorizationRequirement>
{
private readonly IWebHostEnvironment environment;
public SpecialLogicAuthorizeHandler(/* optional injected dependencies */) {}
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
RolesAuthorizationRequirement requirement)
{
if (requirement.AllowedRoles.Any(context.User.IsInRole) || IsAllowedBySpecialLogic())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
private bool IsAllowedBySpecialLogic(){
// whatever you want here
}
}
With that, it is always effective for all AuthorizationAttributes and no policy is needed.
[Authorize(Roles = "MyRole1")]
[HttpGet]
public async Task<string> Get(){
...
}
/// the user can access the above if they satisfy the special logic OR if they are in MyRole1.