Permission systems
(Programming,Blog Software)
For a long time I’ve struggled with permissions systems. I looked at other people’s systems and coded a few that I’ve used. But I’ve never been fully satisfied with any of my login systems. And I’d really like to resolve the problem once and for all.
Requirements
So what do I need for a general login/permission system which would work in most applications? Let’s see…
- The system should, as far as possible, be a “one size fits all” system, workable on both small are large sites.
- Could operate using a database or not. Preferably, without, though, since every page load would otherwise involve a database lookup, which would slow page loads quite a bit, and chew CPU cycles unnecessarily.
- Should use PHP sessions. I believe this has a slight security advantage over straight cookies.
- Passwords should be stored encrypted and be unable to be decrypted, much like the *nix password system.
- Should allow for setting specific permissions for object/pages, but should allow those definitions to be as user-definable as possible.
Some of these requirements could be altered based on the preference of the programmer. For example, some may balk at the idea of avoiding the use of a database, and consider a page-load database-lookup unimportant. That’s fine. Similarly, with PHP sessions. Some will argue that regular encrypted cookies are a better choice. That’s fine, too. Again choices like that are less important. The above simply reflect my opinions and my needs.
Permissions
The most important question to me is how permissions get applied, assigned and stored. For example, I’ve seen a lot of systems where permissions are assigned in a one-dimensional way. That is, each user is assigned a rank in the permissions scheme. So each user fits somewhere in a one-dimensional array of permissions (and some users may share the same rank). This type of permission scheme works in a lot of applications, like the Mantis bug tracker, where there are six levels of user. Each type of user allows certain types of access.
This kind of a one-dimensional system may work for very limited sites, but not for more complex ones. As an example, the “Philosopher’s Stone” software which runs my business has hundreds of pages with all kinds of different functionality. I’ve got pages for receivables, payables, payroll, customers, inventory, invoicing, job management, sales orders, statistics, mailing lists, etc. I may want to give certain people access to invoicing, but not payroll. Some people would have access to sales orders, but not mailing lists. Etc. Permissions in this case are not one-dimensional. They’re two-dimensional at least. So a permissions system which uses a one-dimensional user permission hierarchy just wouldn’t work in my business.
For the sake of simplicity, let’s say that each user would have something like a “permission string” or “permission number” stored with their username and password, which would represent what they have permission to access. That’s a pretty general but reasonable assumption.
Now, for a site like mine, I’d like to designate various “objects” and “actions” which would go along with them. For example, I’d like to say that someone would be able to view customer records and edit them, but not to delete them. That same person might have permission to add, delete and edit inventory items. Someone else might have entirely different permissions on an entirely different set of objects.
Someone with a very small site might simply want permissions to be equivalent to “access to everything” and “access to nothing”. Someone with a medium sized site might want to specify permissions on a per-page basis. Where I might give “add customer” permissions, they’d simply have permissions set on the “add_customer.php” page.
So we’d like to build in the flexibility for the programmer to define what their “permissions string” or “permissions number” represents. But this brings up another point. How long or how big must/should our permission gizmo be? If we store it as a number, we’re generally limited to 32 or 64 bits, depending on the server architecture. Assuming worst-case scenario, that we need to represent list, edit, add and delete permissions on our objects, each object would need at least two bits to represent the range of permissions for it. That means we can only represent 16 or 32 objects in our permission numbers. That limitation wouldn’t work on my site. So for the sake of flexibility, it seems clear an integer wouldn’t work well for permissions. That means we’ll need to store permissions as a string.
The next question is how long a string? Well, any limitation we put on the permissions string would create a problem for someone somewhere down the line. So what if we simply make the string of variable length, based on the programmer’s preference and needs? Moreover, how about if we specify that the string could grow as the programmer added pages and functionality? That’s nice and flexible, right?
Practical Application
So let’s recap. Some way or another, we’re going to store various information about the user. When they register, we’re going to ask them for their username, their password, their email address, maybe their phone number or whatever. Most of this stuff will get stored in the clear. But the password will get encrypted and the result will be stored. When the user logs in, they’ll be asked for their password, and that will be encrypted. The result will be compared with the encrypted string we’ve already stored for them. If the passwords match, the user will be logged in. The process of logging in the user will cause various information (username, user ID, encrypted password, email address, etc.) to be stored in a session, itself perhaps encrypted. All pretty standard.
Somewhere along the line, the user will be assigned a permission string. This could be a default permission string, in the case of a small site. Or an administrator could come along and determine what permissions that user should have. Either way, the permission string gets saved with the user record, and retrieved when the user logs in. Each page load compares whatever permissions are needed for a page with those available for that user. This means there probably needs to be a call to some sort of “permissions” function at the top of each permission-restricted file, which specifies the permissions needed to access that file. This function would look up the logged in user and check to ensure that their permissions string permits access. If not, they get an error message and get routed elsewhere. Otherwise, we load the rest of the page.
How to Implement
Now how do we actually implement this marvelous, cosmic permissions string concept in the real world? We want the programmer to be able to define permissions simply and as extensively as possible. Let’s assume we’re using objects and capabilities as previously described. In the case of Grotblog, there are seven types of objects, as follows:
- Posts
- Comments
- Topics
- Authors
- Categories
- Links
- Configuration
And there are four types of capabilities, as follows:
- List/View
- Add
- Edit
- Delete
Given the objects above, our permissions string would be, nominally, seven “slots” long. Each “slot” would represent one or a combination of the above capabilities. If we assign successive powers of two to the capabilities, and AND them together to create combinations of capabilities, each capability combination can be represented as a single hexadecimal digit from 0 to F. So our “slots” can be one character wide and our string can be seven characters wide.
But let’s say we have a combination of capabilities which can only be represented with three characters or digits. Then our “slots” for each object would be three characters wide. And given seven object types, our permissions string would have to be 7 * 3 characters long, or 21 characters. We don’t need this here, but keep it in mind for later.
So let’s take a typical user of the Grotblog system, and examine what their permissions string might look like. Given a permissions string which looks like this: A2531F0. Let’s assume that our object slots start with Configuration on the far left and Posts on the far right and assign each successive slot in turn, here’s what our user can do:
- Delete and add configurations
- Add links
- Edit and list links
- Add and list categories
- List authors
- List, add, edit and delete comments
- Nothing with posts
If I were setting this system up for Grotblog, I’d probably have the information to drive this system in a file somewhere, which I’d call in as part of the startup process. It might look like this:
define('O_CONFIG', 0); define('O_LINKS', 1); define('O_CATEGORIES', 2); define('O_AUTHORS', 3); define('O_TOPICS', 4); define('O_COMMENTS', 5); define('O_POSTS', 6); define('C_LST', 1); define('C_ADD', 2); define('C_EDT', 4); define('C_DEL', 8); define('P_STRING_LENGTH', 7); define('P_SLOT_WIDTH', 1);
At the top of the add_post.php, I’d probably put a call like this:
allowed(O_POST, C_ADD);
The allowed() function would check the permissions string of the logged in user to see if slot 6 (posts) contained a hexadecimal digit whose value could be masked by 2 (add) to see if it allowed adding a post. If not, it would branch to some other page, with the message that the user doesn’t have permission to do this.
You’ll notice that we also defined the permission string length and the slot width as part of our define() statements. This would allow the allowed() function the maximum flexibility in determining how to read the permissions strings.
This whole system could be wrapped in a permissions class or somesuch, where all the login and permission checking routines are gathered together.
Applicability
I mentioned earlier the Mantis user hierarchy of six user levels. The above system could work with Mantis, though I doubt they’d want to use it. It could even scale down to the “all-or-nothing” website, where the users are either superusers or nobodies. Whether a programmer would want to use a system like this is another matter. The point is that it could scale both up and down.
Actual Code
There are still a few minor issues to be worked out with this system, but once worked out, it will probably be added to Grotblog version 1.7. If you want to look at a real world implementation of the code, look there.