I am trying to create a permissions system in an ASP.NET MVC Application. I have been learning the newest Identity framework - here are my requirements:
- A set of Hierarchical Roles for each set of functionality. So, for instance, there might be the following roles:
- Inherit
- Reader
- Editor
- Manager
- Administrator
- Each user would have one of those roles for each module (e.g. Events, Pages, etc.)
- Users can be members of a security group. Each security group can be assigned a role directly and then all users in that group (who have not been explicitly assigned that permission) will inherit that role.
- Multi-tenant site: each user has a set of sites which they are a member of. In the context of each site, they have a complete set of permissions which can be assigned by the site admin.
Through extending ASP.NET Identity, is it going to be possible for me to accomplish all of this? Or should I be building something custom from the ground-up?
9,999 times out of 10,000 implementing your own authentication system is the wrong way to go. Anything is easier than that, and it's a deceptively difficult thing to do right. ASP.NET Identity is actually pretty customizable, as it was created specifically for that purpose. You might need to do quite a bit to bootstrap your custom requirements fully, but it'll almost certainly be quicker and more secure using ASP.NET Identity.
UPDATE
UserManager
's constructor takes an implementation ofIUserStore
. When working with Entity Framework, you typically just feed itMicrosoft.AspNet.Identity.EntityFramework.UserStore
, but this is your tie in point for extensibility. So, you can simply subclassUserStore
and then override something likeGetRolesAsync
to do whatever custom role logic you need to implement. Then you'd just feedUserManager
your subclass.Following steps can solve your problem
Some good examples of the same are below
Hope this solves your problem
In version 1.0 of ASP.NET Membership, the
IRole
interface must have astring
primary key. However in version 2.0, released March 2014, they added anIRole<TKey>
that allows you to specify a role's primary key, as long asTKey
implementsIEquatable<TKey>
.That said, out of the box MVC integration points for authorization still depend on roles being ID'd by a single string. So if you are going to do authorization there, via attributes like Authorize, then you may need to write your own custom authorization attributes.
One way to achieve hierarchical roles would be to handle it in your application instead of in the model. I assume by hierarchical, you mean that Administrators have all the privileges of Managers, Managers have all the same privileges as Editors, and so on. You could achieve this by adding users to multiple roles, instead of having to walk through a modeled role hierarchy. Something like a db trigger could do it, but you can model it as a business rule in code too. Then if you restrict a page to Editor, Admins & Mgrs would have access to it as well.
Another way would be to just authorize certain actions for multiple roles:
I disagree though that you would want to id roles on a composite key. If you want to protect MVC actions using the Authorize attribute, the ID of the role needs to be a constant value, like a string literal, int or Enum value, etc. If you keyed role on more than one of its properties, the number of properties you need to set on the attribute multiplies by the number of values in each component of the id. You would have Manager/SiteA, Manager/SiteB, and so on.
Instead, it sounds like it might be a good idea to just add properties to the gerund that tracks users in roles (the in-between table in a many-to-many relationship). To do this, you wouldn't be able to simply override and extend methods in the UserManager class as @Chris Pratt suggested. But that doesn't mean you have to throw out the baby with the bathwater. You can still use Microsoft.AspNet.Identity for authentication, and just write your own methods for role management, augmenting them to take an additional parameter:
Given the above, say your URL's look something like this:
...you could write a custom authorization attribute that checks the URL and authorizes the principal both against the role name in the attribute and the siteId in the URL. To get access to the db from the attribute, if you are using IoC for EntityFramework, you can property inject an instance of your DbContext (or whatever interface you have wrapping it).