A bitfield permission system

Posted by lec** on Saturday, February 16 2008 @ 15:21:12 GMT        
I'm assuming you wish to make a website that will allow users to log in to use it's features. The most important thing in coding it is to distinguish one visitor from the next, and give each visitor permission to do something in accordance with the status the visitor has identified themselves as having (by mapping the login name and password with a user in the database, and then further to an appropriate rank with set permissions). Each member may have access to their personal control panel, but guests should not. Likewise, users with administrative privileges might be able to access special protected pages of their own, or be shown special admin controls which no other visitors should be able to see.

The problem emerges when your website grows, and you require an ever larger number of these distinctions: you may want some members or groups of members to be able to post comments on other users, let them rate other users more than once a day, prevent some members from sending private messages, or even use these permissions to determine which usergroups are counted as "staff" for staff lists or statistics. If you had a table "usergroups" in the database, you should undersand that using the ID of the usergroup you are checking for is a bad choice - if you ever need to add additional usergroups, it will reqire a complete re-code of many scripts.

<?php 
// check if user is an administrator
if ($user["usergroup"] == 4)
{
// ...
}
?>
]]>

As you can see, if you later were to split existing administrators into "Support Staff" and "Management", you would have to go through every piece of code and update it -- aack!

An good way might be to have many (compared to the method I'll be describing below, too many) columns in the usergroups database table named like "can_comment_other_users" and then a 1/0 or yes/no value in them. However this is not optimal, as you will end up with a lot of columns, making your table messy, as well as inefficient (should you choose to use yes/no). There is a way to store a huge amount of these permission values in only a single field, without too many complications (once you understand and get the hang of it, it's positively easy) and this is known as "bit fields".

So what are bitfields? They are just numbers that you compare on a binary level with other, fixed numbers (powers of two) to find out whether a certain something should be done with the certain something in question. I am actually aware how very unilluminating that sentence was, so give me a second to clarify. We will be using powers of two from 20 to 2n (1, 2, 4, 8, 16, 32, 64, 128, 256 ... 2n) to create numbers that can be analysed later to see which permissions they contain.

For example, 1 will represent the permission to view the website. 2 will represent whether the visitor can view member profiles. 4 will be for posting in topics, and 8 will be for locking and unlocking topics. Now, you want administrators to be able to preform all these tasks, so you'll add up 1, 2, 4 and 8 for them - 15. That is their permission value. For members, it would be the first three without the permission to lock topics - 7. Guests will have 3, and banned members can't do anything, so it's 0 for them.

You've just given all these member groups their own permissions. Now how do you compare the permissions to see if the current user can, for example, lock a topic? Well first get the permission value (e.g. from the database) and then use the bitwise "and" operator to compare the two values. It is often written simply as an ampersand (&) in most languages. For example (Python):

# Check to see if the user's permissions (in this example, an administrator's) 
# allow for locking topics, and print yes/no accoring to that:
if 15 & 8:
print "yes"
else:
print "no"


Or, in PHP:

<?php 
// do permission check
if (15 & 8)
{
print "yes";
}
else
{
print "no";
}
?>
]]>

The above will print "yes". Why? If you are unfamiliar with the binary number system and the bitwise and operator, let me explain what is happening. First of all, all bitwise operators (AND, OR, NOT, XOR, RSHIFT, LSHIFT) work with numbers on a binary level. That means that firstly the numbers are converted to binary: 15 is written as 1111 in binary, and 8 is 1000. The AND operation (a.k.a. logical conjuction) compares each digit of the number in binary with the digit of other number in the same position, and if they are both true (1) - the operation returns true. Otherwise it's false (0) - 1 AND 1 is 1, 1 AND 0 is 0, 0 AND 1 is 0, 0 AND 0 is also 0. Let's see:

 
 1111
 1000 &
 ----
 1000
 
 


So, 1 AND 0 are 0, and 1 AND 1 are 1. Therfore, the bitwise operation 1111 & 1000 returns 1000, or in decimal, 15 & 8 return 8, which is a non-false value, thus the if (15 & 8) part evaluates as true, and the code prints yes. If you need more help on mastering this binary comparison, read the wikipedia article on it (binary number system, bitwise comparison).

Simply said, if a permission was 1 + 4 + 16 + 256, which is 277, 277 & 16 would be true because 16 was one of the numbers you added up. Likewise, 277 & 32 would be false, since 32 was not added to the permission value.

Now you know this, you can check the other usergroups can be done: 7 & 8 = 0 (members can't lock topics), 3 & 8 = 0 (neither can guests), 0 & 8 = 0 (of course, neither can banned members). Try calculating this for the other permissions listed above for a little practice.

The next step is to make it easier for yourself by mapping these powers of two to more revealing names of permissions. The best way is probably to use a dictionary, hash or associative array, or other key-value programming structure. Here are examples for PHP and Python:

<?php 
$bitfields = array(
'canviewsite' => 1,
'canpostonprofiles' => 2,
'canpostintopics' => 4,
'canlock' => 8,
);
?>
1, 'canpostonprofiles' => 2, 'canpostintopics' => 4, 'canlock' => 8, ); ?> ]]>

bitfields = { 
"canviewsite" : 1,
"canpostonprofiles" : 2,
"canpostintopics" : 4,
"canlock" : 8,
}

Now it's positively easy to use them in your code. Let's assume the variable "permissions" is the value for "permissions" in the datbase:

<?php 
if ($permissions & $bitfields['canviewsite'])
{
// display the website, this is not a banned user
echo "welcome";
}
?>
]]>

if permissions & bitfields["canviewsite"]: 
# display the website to the person
print "welcome"

See, it's positively easy. From this point, you can create new permissions easily. Just write a new key into the array, hash or whatever and give it the value of the next power of two (16 in the above case). Then just add that number to the permissions number for all users that you wish to grant the permission to! It's easy, clean, fun and efficient.
lec**

lec's avatar
Nov 14 2013 @ 18:36:34
I agree, this is rather crude. Though since 2008 when I wrote this, I haven't improved much upon it.

Another approach I've started to use is to have a hierarchy of permissions stored in a table that refer to actions on other tables (view, create, update, delete). This gets messy rather quickly as well.
Navarr^

Navarr's avatar
Jun 02 2010 @ 21:11:57
Fantastic.. I was trying to figure out just how exactly these worked months ago and today while working with Kat on Route50 she said that you wrote an article on bitfields awhile back (your permission system is awful, by the way.. there must be a better way).

So here it is, I'm going to bookmark it and look back on it for future reference, time and time again, until I inherently understand bitfields.
lec**

lec's avatar
Aug 28 2009 @ 09:14:36
Indeed. However, neither PHP nor Python inherently support that, and you don't really feel the difference, unlike with C++
SpaceMan

SpaceMan's avatar
Aug 28 2009 @ 04:45:14
It's a C/C++/Java technique. It will be better if there is a binary notation in the programming languages. (in some of them, the notation is 0b1010)
Nick^

Nick's avatar
Jun 27 2009 @ 06:21:09
You might also like to point out that this doesn't necessarily have to be used for permissions, I'm using it for a cookie-based site options feature :D
Nick^

Nick's avatar
Feb 24 2008 @ 07:41:22
Ahh... I get it now! I am so using this! xD
Conventional Login

Don't have an account? You may want to create one.

OpenID Login
OpenID login and registration is usable, but not finished.
What is OpenID?
Search

(advanced search)
Site Stats
  Total members: 108
  Latest member: adamthephantump
  Members currently online: 0
  Most online: 5 - Aug 28, 2009 (21:49)
  Front page hits: 87933
Developer info
  Site version: 3.5 Alpha
  12 queries - 4 templates
Under the Spotlight
Collide Site
Collide make fabulously dreamy electronic-industrial music, they're one of my favourite bands! Give them a chance to take control of your life - myspace | youtube - "Euphoria".

Collide Site - Hits: 4478

5/5 (2) | Rate this site?