1

I'm working on a project in which some users can be in the role AdminReader. Those users can see everything, but will not be able to save/edit any data.

I know I can do it this way:

public JsonResult ChangeStatus(int? id)
{
    // AdminReader validation
    if (base.User.isAdminReader)
    {
        return Json(new
        {
            Message = "You don't have privileges to alter data.",
            Success = false,
        }, JsonRequestBehavior.AllowGet);
    }

    // Function code

But I don't want to insert the above code inside all project functions.

I thought I could decorate my methods like we use [HttpGet]. I've also read this SO post.

Then I dropped the idea.

But then I found about Exception Handler Attribute and a logging action filter.

Is it possible to somehow combine the public void OnActionExecuting(ActionExecutingContext filterContext) with my AdminReader validation?

I don't know if it is the right way to go about my problem. Also, I'm not sure it could work really. What's the best practice in this situation?

Any suggestion is welcome, thanks in advance.

Graham
  • 7,431
  • 18
  • 59
  • 84
Washington Guedes
  • 4,254
  • 3
  • 30
  • 56
  • Do you want to restrict an entire set of controllers so they can only be accessed by one role? – BillRuhl Feb 20 '17 at 16:25
  • @BillRuhl. Suppose a table list with a button to change status on each line (this is my example case). The `AdminReader` can see the table list (a `PartialViewResult` in the controller), but can not `ChangeStatus` (a `JsonResult` in the same controller). It applies to almost every controller, be it a list with button to change status, or a modal with a button to edit values. The view is accessible, but the method shouldn't be – Washington Guedes Feb 20 '17 at 16:44
  • 2
    I would try @if (User.Identity.IsAuthenticated && !User.IsInRole("AdminReader")){ //render button} in the view itself instead of in the controller. In other words if the user's role is "AdiminReader" then do not render the button.. – BillRuhl Feb 20 '17 at 16:57

2 Answers2

0

There are many ways to do this.

Yes, it's true that attributes are just metadata. However, the MVC framework has code in it that recognizes certain metadata and performs actions on it. Examples include the two attributes you mentioned (ActionFilters and ExceptionFilters), there's also AuthorizationFilters, which may be what you actually want.

AuthorizationFilters run before ActionFilters, near the start of the MVC pipeline, which allows them to block access before the page actually renders. But, if you don't need that, you can just use this point to do specific things before the page renders.

However, having said that, you are still going to need to have code on each page that controls what the user can and can't do based on their role. There is no magic way around that. Whenever you want to control what a user can do on a page based on access, you need code that does that in each section where control is required.

It's not clear from your example what you are trying to do, since the return value from a page is typically the HTML to render, but it looks like you want to return some kind of status message. I don't see how that can be replicated to all pages, since the pages themselves need to render.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • The `AdminReader` is able to access the: views, partials, table lists, modals, everything, but they can not save, edit nor `ChangeStatus` (as in my question example). – Washington Guedes Feb 20 '17 at 16:36
0

I'm not entirely sure I understood your question, so sorry if this is off: but if you wanted to perform your AdminReader logic, you could write your own custom attribute like below:

 public class AccessDeniedAuthorizeAttribute : AuthorizeAttribute
 {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
            if (filterContext.Result is HttpUnauthorizedResult)
            {
                // Perform your unauthorized action here.
            }
        }
}

And then throw the attribute on any method where it applies (or you could throw it on the entire Controller class, if it applied to everything). Like so:

// The RoleSettings is a class of constants I defined that just contain strings
[AccessDeniedAuthorize(Roles = RoleSettings.AdminRole]
[HttpPost]
public ActionResult MyEditMethod()
{
   // Perform actions if they are in the AdminRole
   // If not authorized, it will do whatever you defined above in the 
   // AccessDeniedAuthorizeAttribute
}
joshmcode
  • 3,471
  • 1
  • 35
  • 50