I would like to require one policy for all actions on a controller, and I would like to also require a second policy for all calls to HTTP "edit methods" (POST, PUT, PATCH, and DELETE). That is, the edit methods should require both policies. Due to implementation requirements, and also a desire to keep the code DRY, I need the latter policy to be applied at the controller level, not duplicated on all the action methods.
As a simple example, I have a PeopleController, and I also have two permissions, implemented as Policies, ViewPeople and EditPeople. Right now I have:
[Authorize("ViewPeople")]
public class PeopleController : Controller { }
How do I go about adding the EditPeople policy/permission such that it "stacks" and only applies to the edit verbs?
I've run into two problems which both seem to be a real pain:
- You can't have more than one AuthorizeAttribute or more than one Policy specified within the AuthorizeAttribute, AFAIK.
- You can't access the Request in a custom AuthorizationHandler, so I can't check the HttpMethod to check it.
I tried working around the former with a custom Requirement and AuthorizationHandler, like so:
public class ViewEditRolesRequirement : IAuthorizationRequirement
{
public ViewEditRolesRequirement(Roles[] editRoles, Roles[] viewRoles)
=> (EditRoles, ViewRoles) = (editRoles, viewRoles);
public Roles[] EditRoles { get; }
public Roles[] ViewRoles { get; }
}
public class ViewEditRolesHandler : AuthorizationHandler<ViewEditRolesRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ViewEditRolesRequirement requirement)
{
if (context.User != null)
{
var canView = requirement.ViewRoles.Any(r => context.User.IsInRole(r.ToString()));
var canEdit = requirement.EditRoles.Any(r => context.User.IsInRole(r.ToString()));
if (context. // Wait, why can't I get to the bloody HttpRequest??
}
return Task.CompletedTask;
}
}
... but I got as far as if (context. before I realized that I didn't have access to the request object.
Is my only choice to override the OnActionExecuting method in the controller and do my authorization there? I assume that's frowned upon, at the very least?