Today I want to tell you about the organization of access rights to users (on your site) using bit masks. Perhaps you have already known it, probably not, but anyway I want to give you this information. Why bit masks – easy, because this is and fast and easy way to recognize user’s possibilities. In our system, I have identified six possible actions (it will keep 6 bits, each bit – one of the possible actions). There are next actions: Read, Create, Edit Own, Delete Own, Edit Any and Delete Any (moderator’s / admin’s actions). It can be applied to everything – blogs, news articles, photos and more. Ok, lets start.
Here are a little of theory about Bitwise Operators:
| Example | Name | Result |
$a & $b | And | Bits that are set in both $a and $b are set. |
$a | $b | Or (inclusive or) | Bits that are set in either $a or $b are set. |
$a ^ $b | Xor (exclusive or) | Bits that are set in $a or $b but not both are set. |
~ $a | Not | Bits that are set in $a are not set, and vice versa. |
$a << $b | Shift left | Shift the bits of $a $b steps to the left (each step means “multiply by two”) |
$a >> $b | Shift right | Shift the bits of $a $b steps to the right (each step means “divide by two”) |
Now – download the source files and lets start coding !
Step 1. HTML
Our demo uses 3 html template files:
main_page.html
04 | <title>Access Control with Bit Masks</title> |
05 | <link href="css/main.css" rel="stylesheet" type="text/css" /> |
09 | <h2>Access Control with Bit Masks</h2> |
12 | <div class="container"> |
This is very easy layout, isn’t it?. Next one template file:
02 | <h3>Access control demonstration</h3> |
03 | <p>You can use next usernames "User", "Writer", "Moderator" and "Admin" and password "password" to login in system. Each of them have own set of possibilities.</p> |
06 | <form class="login_form" action="index.php" method="post"> |
08 | <label>Username:</label><input type="text" name="username"> |
09 | <label>Password:</label><input type="password" name="password"> |
10 | <input type="submit" name="LogIn" value="Login"> |
Another one easy template for login form. Next one template file:
03 | <h3>Your bit mask:</h3> |
05 | <h3>Your possibilities:</h3> |
06 | <div>{possibilities}</div> |
09 | <a href="index.php?logout">Log Out</a> |
This is template where we will display our possibilities and link to logout.
Step 2. CSS
css/main.css
This file contains several styles of our page layout, no need to publish it today.
Step 3. PHP
Now, lets review our main functionality:
index.php
003 | define('CAN_READ', 1 << 0); |
004 | define('CAN_CREATE', 1 << 1); |
005 | define('CAN_EDIT_OWN', 1 << 2); |
006 | define('CAN_DELETE_OWN', 1 << 3); |
007 | define('CAN_EDIT_ANY', 1 << 4); |
008 | define('CAN_DELETE_ANY', 1 << 5); |
010 | $oSimpleAccessSystem = new SimpleAccessSystem(); |
011 | $sLoginForm = $oSimpleAccessSystem->getLoginBox(); |
012 | echo strtr(file_get_contents('main_page.html'), array('{form}' => $sLoginForm)); |
014 | class SimpleAccessSystem { |
018 | function SimpleAccessSystem() { |
021 | $sUserPerm = CAN_READ; |
022 | $sWriterPerm = CAN_READ | CAN_CREATE | CAN_EDIT_OWN | CAN_DELETE_OWN; |
023 | $sModeratorPerm = CAN_READ | CAN_EDIT_ANY | CAN_DELETE_ANY; |
024 | $sAdminPerm = CAN_READ | CAN_CREATE | CAN_EDIT_OWN | CAN_DELETE_OWN | CAN_EDIT_ANY | CAN_DELETE_ANY; |
026 | $this->aMembers = array( |
027 | 'User' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sUserPerm), |
028 | 'Writer' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sWriterPerm), |
029 | 'Moderator' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sModeratorPerm), |
030 | 'Admin' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sAdminPerm) |
034 | function getLoginBox() { |
035 | if (isset($_GET['logout'])) { |
036 | if (isset($_SESSION['member_name']) && isset($_SESSION['member_pass'])) |
037 | $this->performLogout(); |
039 | if ($_POST && $_POST['username'] && $_POST['password']) { |
040 | if ($this->checkLogin($_POST['username'], $_POST['password'], false)) { |
041 | $this->performLogin($_POST['username'], $_POST['password']); |
042 | header( "Location:{$_SERVER['REQUEST_URI']}" ); |
046 | require_once('login_form.html'); |
047 | $sLoginForm = ob_get_clean(); |
048 | return $sLoginForm . '<h2>Username or Password is incorrect</h2>'; |
051 | if (isset($_SESSION['member_name']) && $_SESSION['member_name'] && $_SESSION['member_pass']) { |
052 | if ($this->checkLogin($_SESSION['member_name'], $_SESSION['member_pass'])) { |
053 | $sRule = $this->aMembers[$_SESSION['member_name']]['rule']; |
055 | $sPermissions .= $this->isCanRead($sRule); |
056 | $sPermissions .= $this->isCanCreate($sRule); |
057 | $sPermissions .= $this->isCanEdit($sRule); |
058 | $sPermissions .= $this->isCanEditAny($sRule); |
059 | $sPermissions .= $this->isCanDelete($sRule); |
060 | $sPermissions .= $this->isCanDeleteAny($sRule); |
062 | require_once('logout_form.html'); |
063 | $sLogoutForm = ob_get_clean(); |
064 | $sLogoutForm = str_replace('{name}', $_SESSION['member_name'], $sLogoutForm); |
065 | $sLogoutForm = str_replace('{bit_mask}', $sRule, $sLogoutForm); |
066 | $sLogoutForm = str_replace('{possibilities}', $sPermissions, $sLogoutForm); |
072 | require_once('login_form.html'); |
073 | $sLoginForm = ob_get_clean(); |
078 | function isCanRead($sRule) { |
079 | return ($sRule & CAN_READ) ? 'You can Read<br />' : ''; |
081 | function isCanCreate($sRule) { |
082 | return ($sRule & CAN_CREATE) ? 'You can Create<br />' : ''; |
084 | function isCanEdit($sRule) { |
085 | return ($sRule & CAN_EDIT_OWN) ? 'You can Edit<br />' : ''; |
087 | function isCanEditAny($sRule) { |
088 | return ($sRule & CAN_EDIT_ANY) ? 'You can Edit anything<br />' : ''; |
090 | function isCanDelete($sRule) { |
091 | return ($sRule & CAN_DELETE_OWN) ? 'You can Delete<br />' : ''; |
093 | function isCanDeleteAny($sRule) { |
094 | return ($sRule & CAN_DELETE_ANY) ? 'You can Delete anything<br />' : ''; |
097 | function performLogin($sName, $sPass) { |
098 | $this->performLogout(); |
099 | $sSalt = $this->aMembers[$sName]['salt']; |
100 | $sPass = sha1(md5($sPass) . $sSalt); |
101 | $_SESSION['member_name'] = $sName; |
102 | $_SESSION['member_pass'] = $sPass; |
105 | function performLogout() { |
106 | unset($_SESSION['member_name']); |
107 | unset($_SESSION['member_pass']); |
110 | function checkLogin($sName, $sPass, $isHash = true) { |
111 | if (isset($this->aMembers[$sName])) { |
113 | $sSalt = $this->aMembers[$sName]['salt']; |
114 | $sPass = sha1(md5($sPass) . $sSalt); |
116 | return ($sPass == $this->aMembers[$sName]['hash']); |
First, we define the constants for access rights (bit mask). Further, when we enumerate users on the system – we grant them different sets of rights (using the logical operator | or). Keep in mind that I am not forcing you to keep users in the same array, in your case, your users can easily be in the database. And, in this case, you can grant them their right in the database itself. Further, I have added extra check functions, so that we can understand if user can perform a certain action.
In such functions we use logic operator & (and). Most of these checks uses single bit check. If you want to make multiple check, as example – lets create function to check if member can read and create. Then this function will looks like this:
1 | function isCanReadCreate($sRule) { |
2 | return ($sRule & (CAN_READ | CAN_CREATE)); |
This function will return us True or False.
Conclusion
I hope that it was interesting for you to remember how works bitwise and logical operands. If you have any good ideas you would like to share, be sure to write us as well. Good luck!