Reputation: 629
Is it possible to bypass the authorization role check on a controller, but enforce the role check on an action? I've spent a bit of time researching this and everything I find shows how to implement an AllowAnonymousAttribute. I'm currently using the AllowAnonymousAttribute and it works great for completely bypassing authorization for an action. That isn't what I want. I have a controller that requires certain roles. When a particular action is requested I want to skip the roles at the controller level and just verify user has the roles designated on the action.
Here's some code:
[Authorize(Roles="Administrator")]
public class MembersController : ViewApiController<MemberView>
{
// a list of actions....
[Authorize(Roles="ApiUser")]
[HttpPost]
public void AutoPayPost([FromBody] List<AutoPayModel> autoPayList)
{
//....
}
}
The problem is I want users with just the 'ApiUser' role to have access to the 'AutoPayPost' action. I realize I can remove the class level authorize attribute, then add it to every action method on my controller, minus the 'AutoPayPost' action. I would like to avoid this because several of my controllers inherit from a base class that provides a long list of actions that require the 'Administrative' role. Because of that I would have to override every base action, add the Authorize attribute to the overridden method, then delegate the call back to the base class. This WILL work but if I later decide to add functionality to the base class I'll have to remember to go back to the MembersController and override the new methods, add the attribute etc...
It would be great if the end result looked like this:
[Authorize(Roles="Administrator")]
public class MembersController : ViewApiController<MemberView>
{
// a list of actions....
[Authorize(Roles="ApiUser", IgnoreControllerRoles=true)]
[HttpPost]
public void AutoPayPost([FromBody] List<AutoPayModel> autoPayList)
{
//....
}
}
Upvotes: 2
Views: 1585
Reputation: 3199
If I understand you correctly, you could implement a custom ByPassControllerChecksAttribute (it is for decorating methods that you want to allow "passthrough" access to), then in your LogonAuthorizeAttribute retrieve the action method being called by this request and check if its custom attribute collection has an instance of ByPassControllerChecksAttribute. If it does, run the code that checks if the user is allowed access to the method, otherwise run the code that checks if the user is allowed access to the controller. Of course if you have just one method and the name is known not to change, you can bypass the extra attribute and just check for the name, but of course the first method is much better.
EDIT
If your LogonAuthorizeAttribute inherits from AuthorizeAttribute then you can override the AuthorizeCore method which returns a boolean (true meaning the user is authorized, false otherwise). In this method you can have something along the following pseudocode:
if(CheckIfMethodHasByPassAttribute()){
return CheckIfUserIsAllowedToRunThisMethod();
}
return CheckIfUserIsAllowedToRunThisController();
The method CheckIfUserIsAllowedToRunThisMethod would have whatever checks you need to do to determine if a user is allowed to run this method, while the CheckIfUserIsAllowedToRunThisController would have the code to check if a user is allowed access to the controller in general (which I assume is already in you LogonAuthorizeAttribute)
Upvotes: 0
Reputation: 6839
Do something like this, where you will check if the roles/users are in the roles and then deny any of them.
public class ByPassAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string[] roles = this.Roles.Split(',');
string[] users = this.Users.Split(',');
foreach (var r in roles)
{
if (httpContext.User.IsInRole(r.Trim()))
return false;
}
foreach (var u in users)
{
if (httpContext.User.Identity.Name.Equals(u))
return false;
}
return base.AuthorizeCore(httpContext);
}
}
And then decore your controller/action like this:
[ByPassAuthorize(Roles = "Admin,test,testint", Users = "Tester")]
public ActionResult Edit(int id = 0)
{
FooModel foomodel = db.FooModels.Find(id);
if (foomodel == null)
{
return HttpNotFound();
}
return View(foomodel);
}
Hope its help you!
Upvotes: 1