Reader Freddy requested a tutorial about implementing bitwise permissions in real application. This tutorial will cover that. It’s very simple tutorial and I tried to make it as simple as possible.
Folder Structure Top
Folder structure is very simple. We only have 3 files db.php, user.php and index.php. db.php will have database connection, user.php will have simple class that will help us working with users and index.php will be our main file.
Database Structure Top
We’ll need database to. We’ll have 3 tables. bw_users, bw_actions and bw_groups.
bw_users Top
This table is for our users. It has 3 fields, id, name and group. id is primary key, name is users name and group if foreign key for group that links to bw_groups table (id filed).
CREATE TABLE IF NOT EXISTS `bw_users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `uname` varchar(25) COLLATE utf8_bin NOT NULL, `group` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `group` (`group`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=5 ;
bw_actions Top
This table will have all available actions. We’ll have 3 fields, id, name and priv. id is primary key, name is name of the action and we’ll use it to choose action from table that is requested from user and priv is used to determine what privilege is required to execute that action.
CREATE TABLE IF NOT EXISTS `bw_actions` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(25) COLLATE utf8_bin NOT NULL, `priv` int(10) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=7 ;
bw_groups Top
This table will have all groups. We’ll have 3 fields, id, name and priv. id is primary key, name is name of the group and priv is privilege that certain group has.
CREATE TABLE IF NOT EXISTS `bw_groups` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(25) COLLATE utf8_bin NOT NULL, `priv` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=6 ;
Now we just need to link bw_users to bw_groups.
ALTER TABLE `bw_users` ADD CONSTRAINT `bw_users_ibfk_1` FOREIGN KEY (`group`) REFERENCES `bw_groups` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
Table Data Top
I did not create any interface that we will use for creating actions, grups and users but using my tutorial on bitwise permissions i created them manually. Each action has it’s own unique privilege that has to be some number in base 2 (1, 2, 4, 16…) and why you can read on a link above. Groups have some bits set or unset from those actions depending on groups requirements. For example guest will only have permission to read but not to edit, publish or delete.
INSERT INTO `bw_actions` (`id`, `name`, `priv`) VALUES (1, 'view', 1), (2, 'edit', 2), (5, 'publish', 4), (6, 'delete', 8); INSERT INTO `bw_groups` (`id`, `name`, `priv`) VALUES (1, 'guest', 1), (3, 'editor', 3), (4, 'publisher', 5), (5, 'administrator', 15); INSERT INTO `bw_users` (`id`, `uname`, `group`) VALUES (1, 'user1', 1), (2, 'user2', 3), (3, 'user3', 4), (4, 'user4', 5);
db.php Top
This file is elementary PHP and I’ll not explain it.
$con = mysql_connect('localhost', 'root', '');
mysql_select_db('tutoriali', $con);
user.php Top
Now we will create our user class. First we’ll have two variables, $_priv and $_group. What they are for you can tell from their names. Then we’ll have some “useless” constructor.
class user
{
private $_priv;
private $_group;
public function __construct()
{ }
}
Next we’ll have two methods for setting values and two for restoring them (setters and getters). Nothing special. Note that in setPriv we cast passed variable to integer. Why? Reason is very simple. When we get it from database it’s a string but we need integer. We can’t even have float type. That’s because ‘1′, 1 and 1.0 are not the same thing. ‘1′ is binary coded as 0..0110001 and as integer that is 49 (49 is 1 in ASCII table) and that is not 1 that we need. 1.0 is one but has different way of coding than integers because they have wider range of numbers they can display.
public function getGroup()
{
return $this->_group;
}
public function setGroup($group)
{
$this->_group = $group;
}
public function getPriv()
{
return $this->_priv;
}
public function setPriv($priv)
{
$this->_priv = (int) $priv;
}
Now we have only one more method and that will check if user can do something. Not that here we cast **$action to integer again and use ‘&’ to check if user can or can not execute that action.
public function can($action)
{
return (int) $action & $this->getPriv();
}
Main Page Top
Now that we have our class we just need to put this all together. First we’ll include our files in index.php and create two arrays that will have all available users and actions. Next we check if parameters from $_GET are set and valid. After that we have a list of all available users and actions to make our testing easier. After that we just pull our data from database, pass required elements to user class and then we echo message about users permission (can or can not execute action).
require_once './db.php';
require_once './user.php';
$actions = array('view', 'edit', 'publish', 'delete');
$users = array('user1', 'user2', 'user3', 'user4');
$action = (isset($_GET['action']) && in_array($_GET['action'], $actions)) ? $_GET['action'] : 'view';
$user = (isset($_GET['user']) && in_array($_GET['user'], $users)) ? $_GET['user'] : 'user1';
?>
<table>
<thead>
<tr>
< ?php foreach($actions as $tmpAction) : ?>
<th>< ?php echo $tmpAction; ?></th>
< ?php endforeach; ?>
</tr>
</thead>
<tbody>
< ?php foreach($users as $tmpUser) : ?>
<tr>
< ?php foreach($actions as $tmpAction) : ?>
<td><a href="index.php?user=<?php echo $tmpUser; ?>&action=< ?php echo $tmpAction; ?>">< ?php echo $tmpUser; ?></a></td>
< ?php endforeach; ?>
</tr>
< ?php endforeach; ?>
</tbody>
</table>
< ?php
$sql = 'SELECT `bw_groups`.`priv`, `bw_groups`.`name` '
. 'FROM `bw_users` '
. 'JOIN `bw_groups` '
. 'ON `bw_users`.`group` = `bw_groups`.`id` '
. "WHERE `bw_users`.`uname` = '%s' "
. 'LIMIT 1';
$result = mysql_query(sprintf($sql, $user), $con);
if (!$result)
die('Error.');
$temp = mysql_fetch_object($result);
$userClass = new user();
$userClass->setGroup($temp->name);
$userClass->setPriv($temp->priv);
$sql = 'SELECT `priv` '
. 'FROM `bw_actions` '
. "WHERE `name` = '%s'";
$result = mysql_query(sprintf($sql, $action), $con);
if (!$result)
die('Error.');
$temp = mysql_fetch_object($result);
printf(
'User %s in group %s %s %s.',
$user,
$userClass->getGroup(),
$userClass->can($temp->priv) ? 'can' : "can't",
$action
);
Conclusion Top
This is just simple way that show you how you can use bitwise permissions in your application. You can view demo here or download source code here.