Статьи

Контроль доступа с битовыми масками


Сегодня я хочу рассказать вам об организации прав доступа пользователей (на вашем сайте) с помощью битовых масок.
Возможно, вы уже знаете это, возможно, нет, но в любом случае я хочу дать вам эту информацию. Почему битовые маски? — легко, потому что это быстрый и простой способ распознать возможные действия пользователя. В нашей системе я определил шесть возможных действий (в нем будет 6 битов, где каждый бит является одним из возможных действий). Это следующие действия: чтение, создание, редактирование своего, удаление собственного, редактирование любого и удаление любого (действия модератора / администратора). Этот метод может применяться ко всему — блогам, новостным статьям, фотографиям и многому другому. Хорошо, давайте начнем.

Вот небольшая теория о побитовых операторах:

пример название Результат
$ a & $ b И Биты, которые установлены как в $ a, так и в $ b, установлены.
$ a | $ б Или (включительно или) Биты, которые установлены в $ a или $ b, установлены.
$ a ^ $ b Xor (эксклюзив или) Биты, которые установлены в $ a или $ b, но не оба установлены.
~ $ a Не Биты, которые установлены в $ a, не устанавливаются, и наоборот.
$ a << $ b Сдвиг влево Сдвиньте биты $ a $ b шагов влево (каждый шаг означает «умножить на два»)
$ a >> $ b Сдвиг вправо Сдвиньте биты шагов $ a $ b вправо (каждый шаг означает «разделить на два»)

Live Demo

скачать в упаковке


Теперь загрузите исходные файлы и начните кодировать!


Шаг 1. HTML

Наше демо использует 3 html файла шаблона:

main_page.html

<!DOCTYPE html>
<html lang="en" >
<head>
    <title>Access Control with Bit Masks</title>
    <link href="css/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <header>
        <h2>Access Control with Bit Masks</h2>
        <a href="http://www.script-tutorials.com/access-control-with-bit-masks/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
    </header>
    <div class="container">
        {form}
    </div>
</body>
</html>

Это очень простой макет, не так ли? Следующий файл шаблона:

login_form.html

<div class="column">
    <h3>Access control demonstration</h3>
    <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>
</div>
<div class="column">
    <form class="login_form" action="index.php" method="post">
        <h3>Log In</h3>
        <label>Username:</label><input type="text" name="username">
        <label>Password:</label><input type="password" name="password">
        <input type="submit" name="LogIn" value="Login">
    </form>
</div>

Еще один простой шаблон для формы входа. Следующий файл шаблона:

logout_form.html

<div class="column">
    <h3>Hello {name}</h3>
    <h3>Your bit mask:</h3>
    <div>{bit_mask}</div>
    <h3>Your possibilities:</h3>
    <div>{possibilities}</div>
</div>
<div class="column">
    <a href="index.php?logout">Log Out</a>
</div>

Это шаблон, в котором мы будем отображать возможности пользователя и ссылку на выход из системы.

Шаг 2. CSS

CSS / main.css

Этот файл содержит несколько стилей нашего макета страницы, нет необходимости публиковать его сегодня.

Шаг 3. PHP

Теперь давайте рассмотрим наш основной функционал:

index.php

<?php

// define bit mask for access rights
define('CAN_READ', 1 << 0);   // 000001
define('CAN_CREATE', 1 << 1); // 000010
define('CAN_EDIT_OWN', 1 << 2);   // 000100
define('CAN_DELETE_OWN', 1 << 3); // 001000
define('CAN_EDIT_ANY', 1 << 4);   // 010000
define('CAN_DELETE_ANY', 1 << 5); // 100000

// login system init and generation code
$oSimpleAccessSystem = new SimpleAccessSystem();
$sLoginForm = $oSimpleAccessSystem->getLoginBox();
echo strtr(file_get_contents('main_page.html'), array('{form}' => $sLoginForm));

// class SimpleAccessSystem
class SimpleAccessSystem {

    // variables
    var $aMembers; // Existed members array

    // constructor
    function SimpleAccessSystem() {
        session_start();

        // different sets of permissions
        $sUserPerm = CAN_READ;
        $sWriterPerm = CAN_READ | CAN_CREATE | CAN_EDIT_OWN | CAN_DELETE_OWN;
        $sModeratorPerm = CAN_READ | CAN_EDIT_ANY | CAN_DELETE_ANY;
        $sAdminPerm = CAN_READ | CAN_CREATE | CAN_EDIT_OWN | CAN_DELETE_OWN | CAN_EDIT_ANY | CAN_DELETE_ANY;

        /* hash = sha1(md5('password') . 'testing'); */
        $this->aMembers = array(
            'User' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sUserPerm),
            'Writer' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sWriterPerm),
            'Moderator' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sModeratorPerm),
            'Admin' => array('hash' => 'b88c654d6c68fc37f4dda1d29935235eea9a845b', 'salt' => 'testing', 'rule' => $sAdminPerm)
        );
    }

    // get login box function
    function getLoginBox() {
        if (isset($_GET['logout'])) { // logout processing
            if (isset($_SESSION['member_name']) && isset($_SESSION['member_pass']))
                $this->performLogout();
        }

        if ($_POST && $_POST['username'] && $_POST['password']) { // login processing
            if ($this->checkLogin($_POST['username'], $_POST['password'], false)) { // successful login
                $this->performLogin($_POST['username'], $_POST['password']);
                header( "Location:{$_SERVER['REQUEST_URI']}" );
                exit;
            } else { // wrong login
                ob_start(); // get template of Login form
                require_once('login_form.html');
                $sLoginForm = ob_get_clean();
                return $sLoginForm . '<h2>Username or Password is incorrect</h2>';
            }
        } else { // in case if we already logged (on refresh page):
            if (isset($_SESSION['member_name']) && $_SESSION['member_name'] && $_SESSION['member_pass']) {
                if ($this->checkLogin($_SESSION['member_name'], $_SESSION['member_pass'])) {
                    $sRule = $this->aMembers[$_SESSION['member_name']]['rule'];
                    $sPermissions = '';
                    $sPermissions .= $this->isCanRead($sRule);
                    $sPermissions .= $this->isCanCreate($sRule);
                    $sPermissions .= $this->isCanEdit($sRule);
                    $sPermissions .= $this->isCanEditAny($sRule);
                    $sPermissions .= $this->isCanDelete($sRule);
                    $sPermissions .= $this->isCanDeleteAny($sRule);

                    ob_start(); // get template of Logout form
                    require_once('logout_form.html');
                    $sLogoutForm = ob_get_clean();
                    $sLogoutForm = str_replace('{name}', $_SESSION['member_name'], $sLogoutForm);
                    $sLogoutForm = str_replace('{bit_mask}', $sRule, $sLogoutForm);
                    $sLogoutForm = str_replace('{possibilities}', $sPermissions, $sLogoutForm);
                    return $sLogoutForm;
                }
            }

            // otherwise - draw login form
            ob_start();
            require_once('login_form.html');
            $sLoginForm = ob_get_clean();
            return $sLoginForm;
        }
    }

    // check functions
    function isCanRead($sRule) {
        return ($sRule & CAN_READ) ? 'You can Read<br />' : '';
    }
    function isCanCreate($sRule) {
        return ($sRule & CAN_CREATE) ? 'You can Create<br />' : '';
    }
    function isCanEdit($sRule) {
        return ($sRule & CAN_EDIT_OWN) ? 'You can Edit<br />' : '';
    }
    function isCanEditAny($sRule) {
        return ($sRule & CAN_EDIT_ANY) ? 'You can Edit anything<br />' : '';
    }
    function isCanDelete($sRule) {
        return ($sRule & CAN_DELETE_OWN) ? 'You can Delete<br />' : '';
    }
    function isCanDeleteAny($sRule) {
        return ($sRule & CAN_DELETE_ANY) ? 'You can Delete anything<br />' : '';
    }

    // perform login
    function performLogin($sName, $sPass) {
        $this->performLogout();

        $sSalt = $this->aMembers[$sName]['salt'];
        $sPass = sha1(md5($sPass) . $sSalt);

        $_SESSION['member_name'] = $sName;
        $_SESSION['member_pass'] = $sPass;
    }

    // perform logout
    function performLogout() {
        unset($_SESSION['member_name']);
        unset($_SESSION['member_pass']);
    }

    // check login
    function checkLogin($sName, $sPass, $isHash = true) {
        if (isset($this->aMembers[$sName])) {
            if (! $isHash) {
                $sSalt = $this->aMembers[$sName]['salt'];
                $sPass = sha1(md5($sPass) . $sSalt);
            }
            return ($sPass == $this->aMembers[$sName]['hash']);
        }
        return false;
    }
}

Сначала мы определяем константы для прав доступа (битовая маска). Далее, когда мы перечисляем пользователей в системе — мы предоставляем им разные наборы прав (используя логический оператор | или). Имейте в виду, что я не заставляю вас держать пользователей в одном массиве, в вашем случае ваши пользователи могут легко находиться в базе данных. И, в этом случае, вы можете предоставить им их право в самой базе данных. Кроме того, я добавил дополнительные функции проверки, чтобы мы могли понять, может ли пользователь выполнить определенное действие.

В таких функциях мы используем логический оператор & (и). В большинстве этих проверок используется проверка одного бита. Если вы хотите сделать несколько проверок, например — давайте создадим функцию, чтобы проверить, может ли член читать и создавать. Тогда эта функция будет выглядеть так:

function isCanReadCreate($sRule) {
    return ($sRule & (CAN_READ | CAN_CREATE));
}

Эта функция вернет True или False.


Live Demo

скачать в архиве


Вывод

Надеюсь, вам было интересно вспомнить, как работают побитовые и логические операнды. Если у вас есть хорошие идеи, которыми вы хотели бы поделиться, обязательно напишите нам. Удачи!

 

Источник: http://www.script-tutorials.com/access-control-with-bit-masks/