ASP.NET MVC Fine Grained Identity & Access Control - Part 1
In this series I will explain how I designed a generic claims based access control system ASP.NET MVC 5 with Identity 2.0.Part 2: ASP.NET MVC Fine Grained Identity & Access Control - Part 2
The method I explain here will be useful for applications with moderate complexity, which would require an end user (an admin) to define which resources a particular user has access to. Since I could not find any existing projects with what I want, I bootstrapped my own. I realized that a lot of people want to build something like this, but there are no available resources. Also, please, not that, there is a lot of room for improvement. I will be updating the github repo as I develop the blog post series.
This post targets moderate .NET skill sets. If you have no idea what a claim is, you should first go through that. And see how role based access works. And then come back to claims. Understand the difference and then come back to this post. Also, its nice if you do know what logic gates are. Most of the time, associations help us understand stuff better. Think gates!
Jump right into code, if you know what you're doing. (I don't comment every line, I try to use self explanatory code. If you don't understand, shoot me a message.)
This is a 5 part series where I explain in detail how I achieved fine grained access control in ASP.NET MVC 5 with Identity 2.0.
3. Lets do some coding - TODO
4. Let the user pick who has access to what - TODO
5. F5 - TODO
GitHub Repo: ClaimsAuth
I'll try to keep this post simple and short.
We have different kinds of users. (Employees, Agents, 3rd Party, Guest and so on.) Following are the (not finalized) requirements and approaches taken to visualize the end result.
- The UI should change according to the logged in user type.
- I need to be able to use this on both MVC and WebAPI end points.
- Admins are not allowed to manually add routes/permission sets/ACLs. It should be system generated. Admins can only SELECT who has access to what. This is to eliminate human error. Imagine 100000 end points.
- Users belong to one or more groups (Super Admin, Admin, Agent, Client etc.). Using the existing Role based model as a user group based model with claims, we can do some magic.
- Super admins are on god mode. They can create lower level users/groups/assign permissions etc.
- Administrators has access to almost everywhere and can create lower level users, but they can't assign permissions. Super admins can limit permissions of admins.
- Other groups can see only what they are given.
- Users can have special permissions. For example, Everyone in Agent group is denied access to updating a hotel, but a special agent who might also be the owner of a hotel can be given specific access to updating only their hotel.
- Entire access control system runs on MVC area-controller-action sets. This authorization part is designed for a large complex system. Therefore we design the system hierarchically, where no one has no access (including super admins) and we gradually define which parts the groups/users has access to. And we give super admins and admins exclusive access through a claim. Access to everywhere is denied by default.
- If a group is given access to an area, that group can access any controller/action within that group
- If a group is given access to a controller, any action in the controller can be accessed.
- If only an action level permission is given, the obvious thing happens, which is, the group has no access to anything else in that controller.
- Groups doesn't have any special order, and if any of the groups the user belongs to has access to a required resource, user can access it. i.e., one group can deny access to Chat section but, another group, can grant it. Since, the user is in both groups, he gets to access chat section.
- A system only grows. In order to maintain security, except for admins every new feature on the system is locked down to less fortunate users, unless super admin gives access to them.
- Entire ACL is cached so the lookups can be faster.
- I assume, that you know what C# is, and how to use Visual Studio 2012+.
- I also assume that, you are here, because you want to know how to do something like this in your own/office/whatever project
- I also assume you have moderate level of experience in C#
- Another assumption is that, you know what claims are, what access control is, what system security is. Why we need security,
- I also assume you know the difference between authentication and authorization.
- You need to have an basic understanding of how the ASP.NET Identity framework works.
- Also, you need to know SQL
- And, you need to know how to work with EF (Entity Framework!)
- Any previous experience in role based access management is a definite plus
- If you are here because the title sounded cool, thanks, read on. You might learn something!
I am not a fan of re-inventing the wheel, so I'll be piggybacking on the existing ASP.NET identity tables. And add the minimum no of tables I want, to get the job done. Not a fan of too-many-tables-than-necessary either.
I'm gonna call every AREA-CONTROLLER-ACTION trio as resources in my system. WebAPI included. Area itself is a resource. Controller itself is a resource. Action itself is a resource. Any combination of them, is also a resource. I'll auto generate everything from the system itself.
I have a multiple applications using the same database, so I need to know which user belongs to which application. This is the "Application" table.
I also have different user types. An 'enum' in the system, just to make my life easier. This is the "Application User Type" table.
There are parts of the system, that everyone MUST have access to. Like Login and Logout. Yeah, this can deny access to logout and login too. So, lets have a place for them. I'll call it "Resource Global Permissions". I like to have a "Allow All" bit field in this table so that I can quickly switch off if I change my mind about login, or whatever.
Then there are every other resource, which can be given or denied access to. We'll put them in "Resource Role Permission" table. We'll assign roles to resources here. Also have a "Allow" bit field, so its easier to control if I ever change my mind about a user.
User Groups/Roles will be defined in "AspNetRoles" table. (Generated)
We'll use the existing "Asp Net User Claims" table to store our claims. I'm not going to use a seperate table to store claims. There will not be redundancies because this table stores only special claims. Such as exclusive access claim for super admins. Allowed hotel ID for an agent, etc.
Rest of the tables you can read about on here..
|Initial database diagram|
AspNetUserLogins=> Not used here!
Now, we only have to add 5 more tables:
Even though I call it "Groups" instead of "Roles" I use the term Role here just to keep things not confusing.
I'm not going to define table structures here. You can find them in the source.
- All the fields with "ID" in it are Ints
- Every field with 'Is' in it are bits
- 'Active' is a bit field.
- Every field which sounds like a boolean field, is a bit field.
- Every field which sounds like a text field is a nvarchar field with appropriate lenghts.
- CustomerID and EmployeeID in AspNetUsers table are there to differentiate between Agents and Employees. (You may not need that in yours)
- Application User Types are "Generic", "Employee", "Agent" and "Supplier" etc.
On part 2, I'll start the coding part so we can make this DB design really do some magic.