Warning: This post contains ugly code and half-ideas.
I have a confession. I’ve never used the built in ASP.NET Membership framework. Early on, it was a pain trying to figure out how to get it integrated with your own database, and later on, I was so used to rolling my own that I didn’t worry about it. A little more than a year ago, though, I realized this was pretty foolish, so I decided to take another look at it.
Unfortunately, what I found didn’t suite me. It may work for some applications, but not for what I’m building. I needed something with some finer control. Here are a few things I wanted that it didn’t have:
- Ability to combine webforms and windows authentication (some users from each)
- Ability to store passwords using the bcrypt hashing algorithm (for future safety).
- Finely tuned permissions that only give access to a subset of resources in a resource type.
The last one is especially important. On the basic applications you see in demos, it’s not important at all. If you have a blog, most people have read access, few people have write access. For blog demos, users that have read-write access to only a subset of blog posts doesn’t come up very often. But this is a very common issue in every single web app I’ve ever developed. Having an “EditBlogEntry” role doesn’t cut it. That role needs to be able to check to make sure that some user can edit a specific blog entry.
Recently, I’ve set out to tackle that problem.
What I’m working on
It became apparent to me pretty quickly that to get something like this to work, each role would need to be able to execute some unique code to be able to run the logic to insure that a user can access what they’re trying to access. I would call these “Permissions.” Permissions would eventually be able to be assigned to both individual users and groups of users, but to begin with, to keep it simple, only users.
Here are some of the requirements:
- Users (and groups) can have many permissions (many-t0-many in the db)
- Permissions will have unique checking code for each one (therefore, each permission is a different class).
- A filter will be per-controller or per-method (in MVC) and will know which permission that method or controller requires (it will be a string, most likely).
- The filter will create an instance of the permission, pass in the values needed and then execute some function to find out if the user is authorized.
I started writing some code. One issue I realized right away: EF expects one table per class. With each permission being a different class, and wanting an arbitrary number of classes, this would be a problem. Instead, I would make all permissions inherit from an interface and store only certain information from the class in the database.
Currently, here is the website permission:
When the applications starts up, it reflects over itself to find all classes that implement IWebsitePermission, and it inserts a record into the Permissions Database (if it doesn’t already exist) for that permission. The database stores the Permission Name, Option, Whether it has an option, description, and System.Type.
To give you an idea how this permission is implemented, here is a partial implementation that checks to see if a user can edit a specific webpage:
Okay, there is a lot of bad code in this file. Ini my defense, for now I’m just trying to get this idea to work. Then I’ll work on fixing the rest. Above all, the entity framework code in the snippet should be outlawed. And there’s no exception checking!
So far, I’m pleased with how this method is turning out. It seems like I’m on the right path, but I’ve got some major architectural issues:
- Where should the permissions be hosted? In the Models namespace? In the Website namespace?
- Is there a better way to pass in the request information and logged in user information?
- How is the best way to break up permission access? Does there need to be a permission for add/edit/delete, and every sub modication (e.g. add version to webpage)
I have a long ways to go here, but this idea has been floating in my head for a few years now, so it’s nice to start making some real progress.