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.
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?
@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;
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!
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?
aha ok, now I understand
Pretty sweet man. Thanks for explaining.
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
@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?
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
typo: …at least TWO templates…
@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?
Hmm… Ok the comments widget screwed with that reply slightly…
I meant to say:
<% if (user.HasPermission(Permissions.Something)) { %>
// Generate Html here
<% } %>
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%>
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.
>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
How do you call the GetUser method from the session object?
var user = filterContext.HttpContext.Session.GetUser();
@Michael: I defined an extension method on the Session object. So doing Session.GetUser() is exactly the same as (User)Session["User"].
what is the user.HasPermissions method?
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
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.
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