Permission-based access in ASP.NET MVC

I recently read an interesting blog about implementing Role-based access in MVC using custom attributes.  I have implemented a similar strategy for Permission-based access by using the session object.

Storing the permissions

I usually define a user’s permissions as an enumeration and then simply store this as an integer in the database.  You might need permissions to be defined at a group or role-level, but in most cases this will do what is required.  YAGNI!

[Flags]
public enum Permissions
{
    View                 = (1 << 0),
    Add                  = (1 << 1),
    Edit                 = (1 << 2),
    Delete               = (1 << 3),
    Admin                = (View | Add | Edit | Delete)
}

This is the set of permissions I’m going to use for specifying access rights on my actions.

Store the user object in the session

We’re going to need access to the current user’s permissions if we want to determine if the current user has the necessary permissions to perform a certain action.  I find the easiest thing to do is to simply store the current user in the session.  I’ve had some lively debates around this – the best argument against this being that any changes to the user object will cause all users to lose their sessions.  While this is a valid argument, I prefer doing the simplest thing that could possibly work.  YAGNI!

public ActionResult Authenticate(string username, string password)
{
    var user = authenticationService.Authenticate(username, password);
    Session["User"] = user;
                
    return RedirectToAction("Somewhere", "Else");   
}

Create a custom attribute for authorizing access

The last step is to create a custom attribute for authorizing access to our actions.

public class PermissionsAttribute : ActionFilterAttribute
{
    private readonly Permissions required;

    public PermissionsAttribute(Permissions required)
    {
        this.required = required;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var user = filterContext.HttpContext.Session.GetUser();
        if (user == null)
        {
            //send them off to the login page
            var url = new UrlHelper(filterContext.RequestContext);
            var loginUrl = url.Content("~/Home/Login"); 
            filterContext.HttpContext.Response.Redirect(loginUrl, true);    
        }
        else
        {
            if (!user.HasPermissions(required))
            {
                throw new AuthenticationException("You do not have the necessary permission to perform this action");
            }
        }
    }
}

And that’s all we need.  Now we can specify the necessary permissions on each controller action.

[Permissions(Permissions.View)]
public ActionResult Index()
{
    // ...
}

Happy coding.

Tags: C#, MVC

  1. Doron says:

    Interesting take on permissions Jaco. I do like the simplicity of it as opposed to roles based systems.

    There does remain an issue that your permissions need to be fairly static to use this approach and any changes to the available permissions will require a new deployment but nonetheless an interesting way of handling things.

    So if I wanted to assign a user both "Add" and "Edit" permissions for example, what would I need to do?

    Would the database itself simply contain an Int field in the Users table to hold the permission value?

  2. Jaco Pretorius says:

    @Doron: Thanks, this is indeed a pretty static approach to permissions. There are 2 considerations around that – firstly, it's very difficult to create a system where adding new types of permissions wouldn't require any code changes. But code changes are pretty easy in any case right? That's pretty much what we're doing every day. Secondly – for most applications role based systems might be overkill and this simplistic approach should do fine.

    Yes, I store the permissions as a single Int field in the database.

    If you wanted to assign both "Add" and "Edit" permissions to the user you would do something like

    user.Permissions = Permissions.Add | Permissions.Edit;

  3. Doron says:

    Thanks Jaco, my question is more about what you would actually be storing in the db as the permission value in the case when the user has both Add and Edit permissions?

    Excellent posts dude!

  4. Jaco Pretorius says:

    Ah ok – for this you will have to put your binary thinking cap on.

    First you need to take a look at how I've defined the different enum values and understand what's going on with the bit shifting.

    For example,

    View = (1 << 0),
    Add = (1 << 1),
    Edit = (1 << 2)

    is the same as saying

    View = 1,
    Add = 2,
    Edit = 4

    Which in binary terms mean

    View = 001,
    Add = 010,
    Edit = 100

    So every single enum value corresponds to a single bit in the integer value (an integer value in C# being composed of 32 bits). So if the corresponding bit is set this means we have the permission, otherwise we don't.

    So to get back to your original question,

    Permissions.Add | Permissions.Edit

    is the same as (in binary terms)

    010 | 100

    which is (again in binary)

    110

    which is 6 in decimal. So we will store the integer value 6 in the database indicating we have both the "Add" and "Edit" permissions.

    Make sense?

  5. Doron says:

    aha ok, now I understand :)

    Pretty sweet man. Thanks for explaining.

  6. Anonymous says:

    Hi,

    I like your approach!

    I have to implement somthing like you are suggesting. But my idea was to hide controls in the page if the user has no rights to perform the command.

    So I'll avoid the user to click a button and receive back the message.

    Do you got my point? Any clue?

    Cabbi

  7. Jaco Pretorius says:

    @Cabbi: Do you mean you will (for example) disable a button if the user doesn't have the permission to perform that action? In this case you would need to disable the button in the view AND do the check on the server-side. (Remember the user can always manipulate the DOM in the browser)

    Did I understand you correctly?

  8. Anonymous says:

    Yes, you did!

    I'll disable or better, hide controls if user has no rights.

    The today's idea is to use/develop a PermissionPanel with at least to Templates:
    GrantedPanel & DeniedPanel. So the developer can hide controls if user has no grant and put something else on the page (if needed!)

    Cabbi

  9. Anonymous says:

    typo: …at least TWO templates…

  10. Jaco Pretorius says:

    @Cabbi: Ok cool – so I guess you'll just do something like

    <% if (user.HasPermission(Permissions.Something)) { %>

    <% } %>

    Or you can render different templates based on the permissions – but that seems like more work?

  11. Jaco Pretorius says:

    Hmm… Ok the comments widget screwed with that reply slightly…

    I meant to say:

    <% if (user.HasPermission(Permissions.Something)) { %>

    // Generate Html here

    <% } %>

  12. Anonymous says:

    OK, here my very first & simple PermissionPanel implementation:

    [Flags]
    public enum Permission
    {
    View = (1<<0),
    Add = (1<<1)|View,
    Edit = (1<<2)|View,
    Delete = (1<<3)|View,
    Admin = (1<<4)|View|Add|Edit|Delete
    }

    public class PermissionPanel: System.Web.UI.WebControls.Panel
    {
    private Permission m_Permission;

    public Permission Permission
    {
    get { return m_Permission; }
    set { m_Permission = value; }
    }

    protected override void OnPreRender(EventArgs e)
    {
    base.OnPreRender(e);

    Visible = [--check page's user permission --]
    }
    }

    Within the ASP.Net page I place my panel like:

    <%ExCtrl:PermissionPanel ID="ctrl_PermissionPanel" runat="server"
    Permission="Edit"%>
    <%asp:ImageButton ID="ctrl_EditBtn" runat="server"
    CommandName="Edit"
    SkinID="edit"
    ToolTip="Edit Item"/%>
    <%/ExCtrl:PermissionPanel%>

  13. Jaco Pretorius says:

    Oh ok, you're doing Webforms, not MVC. This would work I guess, you just need to check the HTML you're generating – there's no real point in sending all that HTML to the browser if it's going to be invisible – it's better to simply not render the control.

  14. Anonymous says:

    >Oh ok, you're doing Webforms, not MVC

    yes… sorry for the mistake. I'm quite new to webDevelopment! :)

    Well, if the Panel is set to invisible, I supposte the panel content is not rendered (i.e. no HTML is generated). I'll check it out!

    Thanks,
    Cabbi

  15. Michael Mofokeng says:

    How do you call the GetUser method from the session object?
    var user = filterContext.HttpContext.Session.GetUser();

  16. @Michael: I defined an extension method on the Session object. So doing Session.GetUser() is exactly the same as (User)Session["User"].

  17. Superartsy says:

    what is the user.HasPermissions method?

  18. It’s a method on the user object that takes the permissions enum and returns a boolean indicating if the user has the specified permission

  19. Nenotlep says:

    Most excellent way of doing permissions! I’ll try to implement this and tie it together with a role-based system so that roles have the permissions and users have the roles. If anyone has done this before, I’d love some pointers. I’m really surprised that membership doesn’t actually have any kind of permission system that ties into the role system.

  20. Jhonatan says:

    What if user A has “Edit” access to view X, having 10 fields. However, there’s one field “RestrictedField” which he should not be able to edit. It seems to me your approach doesn’t cover this situation. I’ve been looking for a solution to this, having implemented it on ASP.Net webforms. The problem with MVC is I can’t iterate through “view controls” to make them visible or editable based on user permissions dynamically. I asked this on stackoverflow http://stackoverflow.com/questions/12164937/how-to-control-access-to-forms-fields-on-a-asp-net-mvc-3-view

  21. Thanks a lot. This gave me a basic idea for using permissions in my app.

  22. MartinC says:

    Hi. I need to implement a ActionFilterAttribute [POST] ActionResult () in the controller. The problem is that I try to “redirect” to a page if validation failed … But it does not work. Validation runs, but then returns to the ActionResult () next line and finally when the view is returned, only then “redirected” to the page listed in the validation. Ultimately what I need is to stop the ActionResult () statements and “redirect” to the page listed in the validation. I tried OnActionExecuting () and OnActionExecuted () but does not work any

    I need to ….
        filterContext.HttpContext.Response.Redirect (loginUrl, true);
    Run away, “redirecting” the page indicated

    My code:

    [HelperSomeValidations("")]
    [HttpPost]
    public ActionResult Create(Pais pais)
    {
    try
    {
    PaisBLL.saveNew(pais);
    }
    catch (Exception ex)
    {
    ViewBag.error = ex;
    return View(“Error”);
    }
    return RedirectToAction(“Index”);
    }

    public class HelperSomeValidations : ActionFilterAttribute
    {

    public HelperSomeValidations(String permiso)
    {
    this.permiso = permiso;
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    var user = filterContext.HttpContext.Session["coco"];
    if (user == null) //validates if the user just login
    {
    //send them off to the login page
    var url = new UrlHelper(filterContext.RequestContext);
    var loginUrl = url.Content(“~/Usuario/Login”);
    filterContext.HttpContext.Response.Redirect(loginUrl, true);
    }
    else
    {
    if (permission != “”)
    {
    //does some validations with “permission”
    }
    }
    }
    }

    Thks!

  23. Hi Martin, It’s been a very long time since I’ve worked in ASP.NET, I suggest you ask the question on StackOverflow, you might have better luck there. Sorry!