Php система прав пользователя

Управление привилегиями пользователя на основе ролей

Существуют различные подходы к управлению доступом к информации, каждый из них имеет свои плюсы и минусы. Например, использование битовой маски очень эффективно, но ограничено 32 или 64 уровнями (количеством бит в формате целого числа). А подход на основе списка доступа (ACL) позволяет назначать уровни доступа объектам, но с операциями могут возникнуть сложности.

В данной статье описывается реализация доступа на основе ролей (role based access control — RBAC). RBAC — модель системы разграничения прав доступа, в которой роли используются для выполнения различных задач, а разрешения на выполнение операций привязываются к ролям. Пользователь может получать одну или несколько ролей, которые ограничивают его полномочия на действия в системе.

Обратной стороной использования модели RBAC является то, что при плохом управлении ролями и разрешениями вся система доступа превращается в хаотичную массу. В быстро меняющейся бизнес среде может возникнуть необходимость в специальном операторе, который будет заниматься только отслеживанием назначений соответствующих ролей новым сотрудникам и удалений полномочий у тех, кто покинул компанию или сменил позицию в структуре. Определение новых ролей для возникающих рабочих мест, ревизия и удаление полномочий требуют постоянного контроля. Ошибка в управлении полномочиями может сделать брешь в системе безопасности.

Мы обсудим создание необходимых таблиц баз данных. Затем создадим два файла с классами: Role.php — для выполнения определенных задач по управлению ролями, и PrivilegedUser.php — для расширения класса пользователя. Затем приведем несколько примеров, как код может быть интегрирован в ваше приложение. Управление ролями и пользователями весьма похожи, поэтому у вас появится система для идентификации пользователей.

Читайте также:  Css стилизация кнопки отправить

База данных

Нужно 4 таблицы для хранения ролей и информации о разрешениях: таблица roles содержит ID и имя роли, таблица permissions хранит ID и описание разрешения, таблица role_perm содержит соответствие ролей и разрешений, а таблица user_role содержит соответсвие ролей и пользователей.

С помощью такой схемы можно иметь неограниченное количество ролей и разрешений, а каждому пользователю может быть назначено любое число ролей.

Вот запрос CREATE TABLE для создания структуры в базе данных:

CREATE TABLE roles ( role_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, role_name VARCHAR(50) NOT NULL, PRIMARY KEY (role_id) ); CREATE TABLE permissions ( perm_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, perm_desc VARCHAR(50) NOT NULL, PRIMARY KEY (perm_id) ); CREATE TABLE role_perm ( role_id INTEGER UNSIGNED NOT NULL, perm_id INTEGER UNSIGNED NOT NULL, FOREIGN KEY (role_id) REFERENCES roles(role_id), FOREIGN KEY (perm_id) REFERENCES permissions(perm_id) ); CREATE TABLE user_role ( user_id INTEGER UNSIGNED NOT NULL, role_id INTEGER UNSIGNED NOT NULL, FOREIGN KEY (user_id) REFERENCES users(user_id), FOREIGN KEY (role_id) REFERENCES roles(role_id) );

Обратите внимание, что последняя таблица user_role ссылается на таблицу users , которая определяется в другом месте. В нашем случае мы предполагаем, что user_id является основным ключом в таблице users .

Никаких изменений для таблицы users не требуется, так как информация о правах доступа хранится отдельно. В отличие от других систем RBAC, в нашем случае пользователь не имеет никакой роли по умолчанию. Он просто не получает никаких полномочий. В классе PrivilegedUser возможно сделать обнаружение пользователей без ролей и выдачу роли без полномочий в таком случае. Или также можно сделать запрос SQL для копирования ID пользователей и инициализации их нужной ролью по умолчанию.

Класс Role

Основная цель класса Role — вернуть объект роли, который содержит список соответствующих полномочий. Таким образом можно просто проверить наличие нужных полномочий без дополнительных запросов SQL.

Вот код класса, который содержится в файле Role.php :

permissions = array(); > // Возвращаем объект роли с соответствующими полномочиями public static function getRolePerms($role_id) < $role = new Role(); $sql = "SELECT t2.perm_desc FROM role_perm as t1 JOIN permissions as t2 ON t1.perm_id = t2.perm_id WHERE t1.role_id = :role_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":role_id" => $role_id)); while($row = $sth->fetch(PDO::FETCH_ASSOC)) < $role->permissions[$row["perm_desc"]] = true; > return $role; > // Проверка установленных полномочий public function hasPerm($permission) < return isset($this->permissions[$permission]); > >

Метод getRolePerms() создает новый объект Role на основе заданного ID роли. Затем использует выражение JOIN для комбинации таблиц role_perm и perm_desc . Каждое разрешение, связанное с ролью, хранится в виде описания в качестве ключа и значения true . Метод hasPerm() принимает описание разрешения и возвращает значение из текущего объекта.

Класс PrivilegedUser

С помощью нового класса, который дополняет существующий класс пользователя, можно использовать существующий код управления пользователями, дополняя его новыми методами для работы с привилегиями.

Код класса в файле PrivilegedUser.php :

 // Изменяем метод класса User public static function getByUsername($username) < $sql = "SELECT * FROM users WHERE username = :username"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":username" => $username)); $result = $sth->fetchAll(); if (!empty($result)) < $privUser = new PrivilegedUser(); $privUser->user_id = $result[0]["user_id"]; $privUser->username = $username; $privUser->password = $result[0]["password"]; $privUser->email_addr = $result[0]["email_addr"]; $privUser->initRoles(); return $privUser; > else < return false; >> // Наполняем объект roles соответствующими разрешениями protected function initRoles() < $this->roles = array(); $sql = "SELECT t1.role_id, t2.role_name FROM user_role as t1 JOIN roles as t2 ON t1.role_id = t2.role_id WHERE t1.user_id = :user_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->execute(array(":user_id" => $this->user_id)); while($row = $sth->fetch(PDO::FETCH_ASSOC)) < $this->roles[$row["role_name"]] = Role::getRolePerms($row["role_id"]); > > // Проверяем, обладет ли пользователь нужными разрешениями public function hasPrivilege($perm) < foreach ($this->roles as $role) < if ($role->hasPerm($perm)) < return true; >> return false; > >

Метод getByUsername() возвращает объект, заполненный информацией о соответствующем пользователе. Метод почти идентичен тому, что обычно используется в класса пользователей, но его нужно изменить, чтобы добавить объект для ролей.

Метод initRoles() использует выражение JOIN для комбинирования таблиц user_role и roles для сбора информации о ролях для текущего пользователя. Каждая роль наполняется соответствующими разрешениями с помощью метода класса Role Role::getRolePerms() .

Метод hasPrivilege() получает описание разрешения и возвращает результат проверки его наличия у текущего пользователя.

С помощью описанных двух классов проверка привилегий пользователя будет выполняться очень просто:

 if ($u->hasPrivilege("thisPermission")) < // выполняем действие >

Здесь имя пользователя хранится в активной сессии, и для него создается новый объект PrivilegedUser , для которого вызывается метод hasPrivilege() . В зависимости от информации, хранящейся в базе данных, объект может выглядеть следующим образом:

object(PrivilegedUser)#3 (2) < ["roles":"PrivilegedUser":private]=>array(1) < ["Admin"]=>object(Role)#5 (1) < ["permissions":protected]=>array(4) < ["addUser"]=>bool(true) ["editUser"]=>bool(true) ["deleteUser"]=>bool(true) ["editRoles"]=>bool(true) > > > ["fields":"User":private]=> array(4) < ["user_id"]=>string(1) "2" ["username"]=>string(7) "mpsinas" ["password"]=>bool(false) ["email_addr"]=>string(0) "" > >

Организационные вопросы

Одним из преимуществ использования ООП для RBAC является возможность простого разделения логики и сервисных функций. Например, можно добавить следующие методы к классу Role для управления ролями (добавление, удаление, и так далее):

// Вставить новую роль public static function insertRole($role_name) < $sql = "INSERT INTO roles (role_name) VALUES (:role_name)"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":role_name" => $role_name)); > // Вставить массив ролей для заданного ID пользователя public static function insertUserRoles($user_id, $roles) < $sql = "INSERT INTO user_role (user_id, role_id) VALUES (:user_id, :role_id)"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->bindParam(":user_id", $user_id, PDO::PARAM_STR); $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT); foreach ($roles as $role_id) < $sth->execute(); > return true; > // удалить массив ролей и все связи public static function deleteRoles($roles) < $sql = "DELETE t1, t2, t3 FROM roles as t1 JOIN user_role as t2 on t1.role_id = t2.role_id JOIN role_perm as t3 on t1.role_id = t3.role_id WHERE t1.role_id = :role_id"; $sth = $GLOBALS["DB"]->prepare($sql); $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT); foreach ($roles as $role_id) < $sth->execute(); > return true; > // Удалить все роли для заданного пользователя public static function deleteUserRoles($user_id) < $sql = "DELETE FROM user_role WHERE user_id = :user_id"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":user_id" => $user_id)); >

Также можно добавить подобные методы для класса PrivilegedUser :

// Проверка, обладает ли пользователь заданной ролью public function hasRole($role_name) < return isset($this->roles[$role_name]); > // Вставляем новое разрешение в роль public static function insertPerm($role_id, $perm_id) < $sql = "INSERT INTO role_perm (role_id, perm_id) VALUES (:role_id, :perm_id)"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(array(":role_id" => $role_id, ":perm_id" => $perm_id)); > // Удаляем все разрешения из роли public static function deletePerms() < $sql = "TRUNCATE role_perm"; $sth = $GLOBALS["DB"]->prepare($sql); return $sth->execute(); >

Разрешения очень тесно связаны с логикой приложения, поэтому они требуют достаточно сложных процедур добавления и удаления. А роли можно легко модифицировать, создавать и удалять. с помощью интерфейса администратора.

Заключение

В данном уроке мы рассмотрели базу для системы управления доступом на основе использования ролей. Достаточно простой код позволяет построить гибкую масштабируемую систему разграничения прав пользователей. Однако должно внимание надо уделять управлению пользовательским полномочиями на этапе использования, иначе гибкость может привести к хаосу и неразберихе.

Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: phpmaster.com/role-based-access-control-in-php/
Перевел: Сергей Фастунов
Урок создан: 9 Мая 2012
Просмотров: 56156
Правила перепечатки

5 последних уроков рубрики «PHP»

Фильтрация данных с помощью zend-filter

Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.

Контекстное экранирование с помощью zend-escaper

Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.

Подключение Zend модулей к Expressive

Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.

Совет: отправка информации в Google Analytics через API

Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.

Подборка PHP песочниц

Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.

Источник

Оцените статью