I am trying to find a way to imperatively authorize resources in an asp.net razor pages application. So far, I have followed the guide found in this link: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-8.0. But, I am running into a problem where I want to be able to reuse authentication logic for various different requirements, without having to define a new class which inherits that specific requirement.
For example, I have a location object that I want to be viewable under the following conditions:
- If the user is of the role Admin or Master, or
- If the user is a part of the Zone which the Location is also a part of.
I have successfully built a requirement, and two handlers to implement this logic as follows:
public class IsAdminOrHigherHandler : AuthorizationHandler<AccessLocationRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
AccessLocationRequirement requirement)
{
if (context.User.IsInRole(SD.Roles.Admin.ToString()) || context.User.IsInRole(SD.Roles.Master.ToString()))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class InLocationsZoneHandler : AuthorizationHandler<AccessLocationRequirement, Location>
{
private readonly IUserService _userService;
public InLocationsZoneHandler(IUserService userService)
{
_userService = userService;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
AccessLocationRequirement requirement,
Location resource)
{
User? user = await _userService.GetUserFromContextAsync();
if (user == null)
{
context.Fail();
return;
}
if (user.Zones.Any(z => z.Id == resource.Zone.Id))
{
context.Succeed(requirement);
return;
}
}
}
I then register a policy that takes this requirement:
options.AddPolicy("CanAccessLocation",
policy => policy.AddRequirements(new AccessLocationRequirement()));
This works great, but my question is that when I want to define a new policy for defining rules for editing a location, I want to avoid rewriting this logic just because the requirement has changed. For example, my condition for editing a location are as follows:
- If the user is Admin or Master, or
- If the user is a part of the Zone which the Location is also a part of AND they have a role of ZoneManager.
As we can see, to edit a location, you may fulfill the requirement simply by fulfilling the logic laid out in the IsAdminOrHigherHandler
method, but because the IsAdminOrHigherHandler
inherits from AuthorizationHandler<AccessLocationRequirement>
and not AuthorizationHandler<EditLocationRequirement>
, we would have to rewrite this method.
How can I share Authorization Handler logic for different Requirements? Or is there a better method?
You could have
BaseRequirement
which will be inherited by yourAccessLocationRequirement
andEditLocationRequirement
. Then yourIsAdminOrHigherHandler
would impelement theBaseRequirement
.That way your every requirement would inherit from your base which handler holds logic for Admin and Master roles.
Something like this:
Then you gonna have:
and your other requirement:
Then your handler code would be:
And just register handlers and policy:
and just add your policies with requirements.