Snippets: User Permissions with Bitmask

Snippets: User Permissions with Bitmask

·

5 min read

Sometimes user permissions can become a complex mess that is hard to manage well in our code. One way to manage those permissions using an integer value is with a bitmask.

A bitmask is just computer jargon for an integer that represents a different value. It evaluates to a true/false using bitwise operators, which compare value A against value B. This is a 1000 foot view of bitmasks and what they represent. We could go way into the weeds with binary and bits that are on or off, but for this overview, we need a basic understanding.

Setting Up The Permissions

Let's say you have a forum where some users have varied levels of permissions they can perform. You would assign those values an integer, which is always double the value that came before it. Doubling the numbers ensures our integer value can only be created by combining one set of numbers.

$perms = [
    "read"    => 1,
    "write"   => 2,
    "delete"  => 4,
    "suspend" => 8,
    "ban"     => 16,
]

In the example above you can see there is only one way to get to 13 or 7. Now when we perform a bitwise operation on this integer we know if a user can ban users or delete posts. If you wanted a field to be true, or the user has permissions, you add that number to their permission value. If we wanted a user who could only read and write to the forum their permission integer would be 3 and that is what we would store in the database. Our permissions table then becomes something like the below example.

iduserperms
01233
1abc11

It becomes easy to create and maintain a complex permission schema using this technique.

Checking For Permissions

Below is a basic example in PHP showing how you would check for user permissions using a bitmask. Keep in mind this just validates the permission, it does not set the permission values or update them, just runs the bitwise operation. You can view this code here and run it in your browser here

/**
 * User permissions with Bitmask
 */
class UserPerms
{
    public function __construct()
    {
        // Set our permissions and their value.
        $this->permissions = [
            'read'    => 1,
            'write'   => 2,
            'delete'  => 4, // Values are doubled with each entry, this way there is only 1 possible way to have a 4 or 6
            'suspend' => 8,
            'ban'     => 16,
        ];
    }

    /**
     * Check if the user can perform an action
     *
     * @param string $action     The action to perform, ie read
     * @param integer $userPerms The user permission integer from the DB (1, 3, 6, etc)
     * @return boolean
     */
    public function hasPerm($action, $userPerms)
    {
        foreach ($this->permissions as $perm => $mask) {
            if ($action == $perm) {
                $ans = $userPerms & $mask;
                return boolval($ans); 
            }
        }
    }
}

Expanding The Idea

For really complex permissions systems, you can break them down into smaller chunks. For instance, you can break it down by forum topic and store the integer values a bit differently. You could turn this into a string you store like 11.0.23.3 where each decimal place represents a different forum topic or area. When parsing you would just break the string up by the . and process the correct position to check the permissions.