разграничение прав доступа php

Создание ролей пользователей на сайте

Привет друзья. Хочу поговорить с вами о методе разграничения прав пользователей на сайте, а точнее о ролях пользователей.

Роль пользователя это совокупность прав и разрешений выданные той или иной роли описанной в информационной системе.

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

Самые распространённые роли это:

И так друзья, что мы сможем делать с помощью ролей на нашем сайте? А все что угодно. Например вы можете показывать какую-то часть контента не зарегистрированному пользователю. Если пользователь прошёл регистрацию и авторизовался в системе, то его привилегии увеличиваются в плане доступа к контенту и он уже может просматривать более подробный контент вашего ресурса (например фотогалерею, оставлять комментарии и т.д). Все зависит от вашей фантазии и того что хотите показать пользователю или на оборот спрятать от него.

И так, создадим таблицы в нашей базе данных. У меня это таблицы:

В таблице bez_reg в конце добавляем поле role, где собственно и будет привязка пользователя к роли.

разграничение прав доступа php. Смотреть фото разграничение прав доступа php. Смотреть картинку разграничение прав доступа php. Картинка про разграничение прав доступа php. Фото разграничение прав доступа php

Далее создаем таблицу bez_role, где напишим названия наших ролей:

разграничение прав доступа php. Смотреть фото разграничение прав доступа php. Смотреть картинку разграничение прав доступа php. Картинка про разграничение прав доступа php. Фото разграничение прав доступа php

Данный трех ролей нам будет достаточно, чтобы система ролей работала. Создаем третью таблицу bez_contnet, здесь будет храниться контент и роли пользователей которым можно просматривать данный контент.

разграничение прав доступа php. Смотреть фото разграничение прав доступа php. Смотреть картинку разграничение прав доступа php. Картинка про разграничение прав доступа php. Фото разграничение прав доступа php

С таблицами разобрались, теперь нужно нам сделать себя администраторами т.к. по идее при импорте базы данных у вас должна быть учетная запись по умолчанию с ролью администратор, но так как мы делаем все сами, то нам не лень залезть в базу и сделать себя админом )).

Регистрируемся через форму регистрации, активируем свой аккаунт изменив поле status = 1 и делаем себя администратором установив в поле role = 1.

разграничение прав доступа php. Смотреть фото разграничение прав доступа php. Смотреть картинку разграничение прав доступа php. Картинка про разграничение прав доступа php. Фото разграничение прав доступа php

Далее заполняете таблицу bez_contnet, произвольным текстом, при этом в поле role через запятую указываете те роли которым разрешен доступ на просмотр данного контента. У меня администратор может просматривать все статьи, модератор только первую статью, пользователь только вторую статью.

С созданием нужных таблиц разобрались, теперь нужно немного поправить скрипт авторизации. Для это переходим в папку ./scripts/auth/auth.php ищим запрос к базе данных

Заменяем на новый запрос к базе данных

Далее добавляем нужные сессионые переменные для работы с авторизированным пользователем.

Источник

Способы разграничения доступов к файлам при помощи php+mysql+apache

Задача по разграничению доступа к файлам, которые хранятся на диске довольно редка, но она может возникнуть при написании: online-магазина, который торгует файлами или файлового сервера вроде rapidshare.de. В данной статье я рассмотрю 3-и способа разграничения доступа при помощи php, mysql и специальных модулей веб сервера apache.

Способ #1: использование символьных ссылок

Это самый простой на мой взгляд способ, он не требует установки каких-то дополнительных модулей apache, но в то же время, в чистом виде, это наименее гибкий метод и работать он будет только на сервере под управлением *nix. Темнеменее это метод прекрасно подойдет для файлового хостинга.
Что нужно сделать:
1. Создадим две директории, в одной из которых будут храниться все файлы, доступ к которым нужно ограничить, а вторая пока будет пуста. Для примере я создал у себя директории: members и free;

3. Для примера создадим в директории members файл test.html. Если обратиться к этому файлу через http то получим Forbidden, т.е. пользователь даже имея прямую ссылку на этот файл не сможет его скачать;

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

Способ #2: использование модуля mod_auth_mysql

1. Скорее всего в базе данных вашего сайта уже есть таблица, в которой хранится список зарегистрированных пользователей сайта, в этом случае вам нужно будет добавить в эту таблицу поле в котором вы будете хранить md5 хеши паролей (если конечно вы их так уже не храните). Но т.к. у меня всего этого нет то я создам таблицу с нуля:
CREATE TABLE users (
id int(11) unsigned not null auto_increment primary key,
login CHAR(50) NOT NULL,
password CHAR(50) NOT NULL,
unique key login_idx (login)
)
Как видно из структуры таблицы поле login должно быть уникальным и не должно содержать значение NULL;

Способ #3: использование модуля mod_auth_cookie_mysql

Этот модуль по своей функциональности очень похож на модуль mod_auth_mysql. Но всеже этот модуль имеет 2-а существенных отличия:
1. Он может брать информацию для авторизации из cookie пользователя;
2. В cookie не фигурирует логин/пароль пользователя, что сводит на нет возможность их кражи.
Скачать модуль и ознакомиться с его документацией можно здесь.

# Активируем модуль mod_auth_cookie_mysql
AuthCookieSql on

# Обязательные параметры для подключения к базе данных
AuthCookieSql_DBhost
AuthCookieSql_DBuser
AuthCookieSql_DBpassword
AuthCookieSql_DBName

# Имя таблицы, в которой хранятся сессии пользователей. Обязательный параметр
AuthCookieSql_DBtable users_sessions

# Названия полей в таблице. Обязательные
AuthCookieSql_SessnameField cookie_name
AuthCookieSql_SessvalField cookie_value
AuthCookieSql_UsernameField login

# Имя cookie переменной, из которой будет взято значение для поиска в базе.
# Опциональный параметр, если он не указан то поиск совпадений в базе
# будет осуществляться для всех cookie пременных установленных на данный момент
AuthCookieSql_CookieName AuthorizationCookie

# Поле таблицы, в котором хранится время окончания действия cookie.
# Параметр опциональный, если не указан то время действия бесконечно.
AuthCookieSql_ExpiryField expire

# Поле таблицы, в котором хранится удаленный IP пользователя.
# Опциональный параметр, если он не указан то IP не проверяется.
CookieAuth_RemoteIPField ip

# Проверяем найдено ли совпадение в базе
require valid-user

3. После успешной авторизации пользователя на сайте сделаем следующие действия:

// Подготавливаем дату окончания действия cookie
$expire = time() + 60 * 60; // cookie будет действовать 1 час

После этих действий мы можем редиректить пользователя на любой файл, находящийся в каталоге members.

Вот собственно и все. На данный момент это все известные мне способы разграничения прав доступа к файлам под управлением веб сервера apache. Если кто-то знает другие — поделитесь ссылкой и я с удовольствием о них почитаю.

Источник

Система разделения прав доступа в веб-приложении

В этой статье мы пройдём с вами полный цикл от идеи, проектирования БД, написания PHP-Кода, и завершающей оптимизации. Постараюсь рассказать обо всем, как можно проще. Использовать для примеров буду PHP и Mysql. Заодно потренирую новичков :).

В этой статье я коснусь вопросов:
1. Идея ACL
2. Проектирование БД
3. Нормализация БД
4. Рефакторинг кода
5. Оптимизация рабочего кода

Статья является ответом на Бинарное распределение прав доступа в CMS. Пока автором пишется практическая часть, я хочу предоставить мой вариант, который я использую довольно давно.
То, что я сейчас расскажу, похоже на ACL.

Упрощенное описание идеи

Права доступа принадлежат ко всем объектам, к которым необходимо их применять.
Если рассматривать пример простой страницы новостей (которую мы с вами здесь напишем), то права доступа должны иметь:
1) Основная страница новостей — глобальные права доступа, означающие «создание новой новости», «модерирование новостей», «просмотр самой страницы».
2) Каждая новость — возможности «редактировать автором новости» или «не оставлять комментарии».

Система прав доступа состоит из:
+ или

Group — это набор имен, которые из себя представляют:
1) Права конкретного пользователя (например ‘User1’, ‘User2’. ). Например используется для личных сообщений, к которым этот пользователь имеет доступ или для допуска редактирования только его сообщений на сайте.
2) Группы приватных страниц (или групп пользователей), к которым необходимо дать права на определенные действия. (например, администраторами, супермодераторами и др.)
3) Дополнительные свойства. (Например, флаг — переключатель режимов)

Action — набор действий, которые пользователи с имеющимся могут делать. В нашей системе новостей можно использовать:
N — добавлять новую тему
D — удалять тему
E — редактировать тему
V — видеть тему
C — оставлять комментарий
B — удалять комментарий

± означает давать пользователю с такими правами или не давать (приоритетно) доступ к действию. Например: Users+VC, Users-C = Users+V.

Теперь рассмотрим пример прав доступа, для простого сайта новостей:
Объект MainNewsPage:
Users+VC, Moderator+NEDB, Admin+NEDB
Объект NewsMessage:
User1+ED (в принципе не нужно, если добавлять могут только модераторы)
Users-C (можно использовать, если нет желания оставлять комментарии)
Объект NewsComment:
User2+B (а здесь необходимо, так как комментарий может оставлять любой пользователь, но не все могут удалять их)

Упростим систему, для понимания идеи компьютером

Для начала, определим Базы данных, для работы с правами объектов.

Так как у нас получается список нескольких прав, то можно начать с такой БД:
RightsID — идентификатор списка прав.
Group — название группы.
Sign — знак группы.
Action — название действия.

Нормализация таблиц. Добавляем права пользователя.

У пользователя, который будет просматривать нашу страницу, должны иметься права, которыми он владеет. Исходя из них, мы сможем знать, имеет ли пользователь право на действие.
Например: User2, Users, Moderator.

Для этого определим таблицу прав:
RightsID — идентификатор списка прав пользователя.
Group — название группы, в которой пользователь состоит.

Пример:

IDRightsIDGroup
110User1
210Users
310Moderator

Теперь приведем обе наши таблицы к нормальной форме. (wiki)

В результате ID ключи отсеются, и мы получим 3 таблицы:
rights_action — права объекта
RightsID: integer (pk) — идентификатор списка прав.
GroupID: integer (pk) — название группы.
Sign: tinyint (1) — знак группы.
Action: enum (pk) — название действия.
rights_group — права пользователя
RightsID: integer (pk) — идентификатор списка прав пользователя.
GroupID: integer (pk) — идентификатор группы, в которой пользователь состоит.
rights_names — названия групп
GroupID: integer (pk) — идентификатор группы.
name — название группы.

Primary key ‘ID’ мы заменили на другие ключи, состоящие в некоторых случаях из нескольких полей таблицы.
Знак группы теперь 0 (+) или 1 (-), потому что так нам будет проще к ним обращаться.
Идентификатор GroupID прямиком указывает на название в rights_names.
На самом деле таблица rights_names является в нашем случае аппендиксом, который не будет использоваться для выявления прав на необходимое действие. Эта таблица теперь служит лишь для «Очеловечивания» результатов.

Пример, что у нас получилось:

rights_name
GroupIDname
10Users
11Moderator
12Admin
1001User1
1002User2
1003User3
rights_group
RightsIDGroupID
11001
110
111
rights_action
RightsIDGroupIDSignAction
100100message_view
100100comment_create
100110message_create
100110message_edit
100110message_delete
100110comment_delete
100120message_create
100120message_edit
100120message_delete
100120comment_delete
10110010message_edit
10110010message_delete
101101comment_create

Стало менее наглядно — для человека. Компьютеру, который оперирует числами, стало намного проще обращаться с таблицами.
Теперь мы можем добавить права любому объекту в таблице на любое действие. Действия теперь записываются в таблицу в виде ENUM (поле ‘action’), что упрощает понимание и разработку проектов. Само действие как string и может называться, как угодно.

`rights_group` должна быть привязана к пользователям и говорит о тех правах, которыми пользователь обладает.
`rights_action` должна быть привязана к объектам и говорит, с каким правами, какие действия пользователь может выполнять.

Например (для нашего сайта новостей):

news_page (параметры основной страница с новостями)
PageIDRightsIDName
1100Страница новостей
news_message (сообщения на странице новостей)
MsgIDPageIDRightsIDHeaderMessage
11101Ура, мы на главной.Но это только начало, дальше, когда мы ближе подберемся к администрации хабра.
21101Новости последней неделиНе смотря на наше стройное шествие по главной, похоже планы обломались.

Разработка библиотеки работы с правами доступа

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

Алгоритм наших действий при проверке возможности действия:
1) Берем из БД выборку прав по необходимому объекту (объектам). (100: Users+VC, Moderator+NEDB, Admin+NEDB)
2) Выбираем необходимые нам действия (action). (V: Users+V)
3) Сравниваем права доступа пользователя и нашей выборки. (Users, User1 Users+)
4) Если результатов нет, тогда возвращаем false.
5) Если результаты есть, но состоят из минусов, возвращаем false. Иначе возвращаем true.

Ещё один момент, который стоит заметить: права доступа от parent (страницы новостей) переходят к child (в данном случае, к сообщению). То есть, если указать на странице +’message_view’, то все сообщения автоматически будут с такими правами (чтения). Это обстоятельство мы будем использовать и проверять в пункте 1 нашего алгоритма.

Пункт 2.
С выбором необходимого нам действия, тоже всё просто:
SELECT * FROM `rights_action` WHERE `action`= ‘message_view’

Пункт 3.
А вот здесь придется объединить несколько SELECTов. Сначала выбираем права доступа пользователя, а затем сравниваем их с необходимыми нам. Если объединить эти действия в одно, получится:
SELECT * FROM `rights_action` WHERE `GroupID` IN ( SELECT `GroupID` FROM `rights_group` WHERE `RightsID` = 1)

Пункт 1-3.
Теперь всё вместе одним сложным запросом:

SELECT * FROM `rights_action` WHERE `RightsID` IN (100, 101) AND `action`= ‘message_view’ AND `GroupID` IN ( SELECT `GroupID` FROM `rights_group` WHERE `RightsID` = 1)

Пункт 1-5.
Вставим это в PHP реализацию и заодно добавим проверку:

if (!$result)
return false ;

Это лишь начало, первый шаг.

Создаём класс работы с правами доступа

Что нам необходимо?
Разработаем пример работы с нашим классом.
1) Во-первых, необходимо указать права пользователя, с правами которого нам надо работать. А сам класс необходимо привязать к конкретному пользователю.
2) Добавление в класс child-прав объектов с дополнением свойств.
3) Проверка доступа по различным действиям (action).

Замечания и мысли:
Права пользователя можно указать при создании класса в __construct.
При добавлении новых свойств, чтобы не терялись свойства в классе, необходимо будет делать новый класс (клонировать старый с добавлением свойств).

Давайте попробуем это всё реализовать:

$UserRights = new Rights($CurrentUser->rightsID);

Рассмотрим добавление права объекта для проверки:

Напомню, что портить объект нам не нужно, потому что мы будем к нему (parent) добавлять права от разных сообщений (child).

Теперь перепишем нашу функцию check, введя её в класс, и посмотрим что получилось:

if (!$result)
return false ;

То, что мы сейчас с вами проделали (переделали функцию в класс, сделав её удобнее в использовании и универсальнее) называется Рефакторинг. (wiki)

Сейчас этот класс можно использовать вот так:

//Создаём права пользователя
$UserRights = new Rights($CurrentUser->rightsID);

//Проверяем, может ли пользователь просматривать страницу?
if ($PageRights->check( ‘messages_view’ )) <
//Да, может. Но что делать с сообщениями?

//И проверяем на читаемость
if ($MsgRights->check( ‘messages_view’ )) <
//И если оно читается, проверяем можем ли мы редактировать сообщения?
if ($MsgRights->check( ‘messages_edit’ ))
$msg->editable_flag = 1;
//А удалять сообщения?
if ($MsgRights->check( ‘messages_delete’ ))
$msg->delete_flag = 1;

где $CurrentUser — структура пользователя, который смотрит страницу.
$MainPage — структура страницы, который смотрит пользователь.
$MainPage->Messages — массив сообщений, которые выводятся на странице.
Структуры, предварительно, были считаны из БД.

Оптимизация

Качеством и функциональностью библиотеки мы с вами довольны, но возникает вопрос производительности.

Первое, что бросается в глаза — это при каждой проверке с новым ‘action’, происходит не хилый SQL запрос. Давайте попробуем это исправить.

Для начала посмотрим, что от запроса к запросу не меняется и оптимизируем это.

function __construct($grp) <
$result=mysql_query( «SELECT `group_rights`.groupID FROM `group_rights` WHERE `group_rights`.rightsID=$grp» );

Уже стало легче, но все равно происходит поиск по всей БД каждый запрос. Как нам от этого можно избавиться? Вероятнее всего, создать предварительный результат, зависящий только от action — потому что выборка `RightsID` и `GroupID` остается неизменной.

Когда добавляется группа объектов, считываем все результаты из БД в массив, который будет зависеть лишь от значений ‘action’.

SELECT * FROM `rights_action` WHERE `RightsID` IN (. ) AND `GroupID` IN (. )

Далее, уже перебором по каждому ‘action’ в массиве, ищем необходимый элемент. При этом запросов в БД больше нет — до следующего объекта с новыми правами.

В результате оптимизации, наш класс будет выглядеть вот так:

if ($tmp) <
return (array_search(0,$tmp)!==FALSE);
>
return false ;
>

function __construct($grp) <
$result=mysql_query( «SELECT `group_rights`.groupID FROM `group_rights` WHERE `group_rights`.rightsID=$grp» );

Можно ли ещё быстрее?

Да, можно.
1) Если учитывать, например пустые группы прав у сообщений (child), которые не поменяют нашу уже используемую временную таблицу. В этом случае мы можем использовать её, не создавая заново. А для проверки, нам необходимо добавить лишь ещё один SELECT count(*) FROM `action_rights` WHERE `GroupID` =…, который пройдётся по индексу и вернет результат.

2) Правильно расставить индексы в таблицах `action_rights` и `group_rights`.
Тут я не уверен. Эксперты меня надеюсь, поправят. Лично сделал PK — ‘rightsID’, ‘action’, ‘groupID’, INDEX — ‘groupID’, ‘rightsID’

4) Использовать кеш. Но это уже другая история 🙂

Работающий пример

Я думаю, что уже достаточно на сегодня кода. Вот как это работает:
работающий пример — извиняюсь за не наглядность.
test.php (рабочий пример) — здесь используются мои библиотеки, которые работают с SQL БД, не удивляйтесь. Уверен, что разберетесь.
rights.php — наша библиотека.

Расширяемость

Любые новые действия, которые вы будете использовать в вашем проекте добавляются в ‘action’ ENUM.

Если вы не хотите быть привязанным к конкретным действиям и добавлять их в реальном времени, то стоит заменить ‘action’ ENUM на integer и создать ещё одну таблицу соответствий actionID с action_name. (как мы сделали это с названиями Групп)

Источник

Реализация механизма разграничения прав доступа к админ-части

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

Что ж, сама по себе данная тема довольно грузная, и требует определённого времени на анализ и постанувку задачи.

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

Давайте с самого начала обсудим архитектуру модульной системы на выбранной нами псевдо-системе.

Все модули представлены ввиде подключаемых к главному документу (индекс-файлу) вставок. Запрос модуля происходит из строки запроса QUERY_STRING, и название подключаемого модуля передаётся в качестве аргумента act. В некотором месте индекса файла происходит изъятие и обработка данного параметра. После, если у пользователя достаточно прав для доступа к модулю в контексте чтения, происходит проверка существования указанного в строке запроса модуля, и если таковой существует, то происходит его подключение к индекс файлу.

Для воплощения данного механизма мы будет проверять значение переменной строки запроса `do`, которая обрабатывается в самом модуле и носит информацию о том, к какому разделу модуля необходимо предоставить доступ пользовалю.

Значение do буду фиксированными, данная переменная будет принимать следующие значения:

В целом, этот список можно увеличить, при этом всё зависит лишь только от масштабов проекта и его потребностей в функционале.

Теперь непосредственно о модулях. Кроме физического существования некоторого модуля в контексте файловой системы проекта, модуль так же должен быть добавлен в особую таблицу БД, которая будет содержать информацию о всех существующих модулях в системе. Добавление и изменение данных данной таблицы, обычно, производится непосредственно в контексте модулей, а то есть во время их инсталяции в системе. Однако это уже углубление в принципы посмотроения расширяемых систем, о чём мы как-то в другой раз поговорим, и посему, мы ограничимся ручным обновлением и добавлением данных о модулях.

Кроме модулей у нас будут ещё две таблицы, а именно таблица в которой будут хранится данные относительно профилей прав доступа и таблица с информацией о пользователях непосредственно.

Что ж, давайте рассмотрим эту особую структуру. Она будет следующей: [ module_indefier : [0 | 1]+ \: [0 | 1]+ \;] *

То есть идёт список из пар: имя модуля «:» права чтения «,» права записи «;». При этом данная метка обновляется в момент внесения изменений о правах доступа пользователя к системе. Если в системе появляется информация о модуле, который не вошёл в данную метку, то стоит просто произвести процедуру редактирования, и данные сохранятся автоматически.

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

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

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

Далее описан класс для внедрения функций проверки прав доступа пользователей к модулям системы.

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

Функция secure::getUserId()

Функция secure::getUserSecurityAccess($id)

На выходе данная функция возвращает идентификатор профиля безопасности текущего пользователя в системе.

Функция secure::checkUserPermission($module,$act))

Производится запрос к БД, относительно прав пользователя на произведение действий чтения/записи в контексте переданного в качестве параметра модуля.

Процедура авторизации будет выглядеть ввиде внесения личных данных пользователя (логин и пароль) в специальную форму, после отправки которой произойдёт обработка данных, переданных пользователем, по-методу функции checkAuthData(), и, в случае корректности данных, будет произведено сохранение данных о пользователе ввиде куки записи на период установленный пользователем, либо в отсутствии заданного значение на период по-умолчанию.

Я не привожу форму для отправки, так как это не часть теории программирования, указав лишь идентификаторы полей.

Вот, в целом и всё. Осталось лишь пробовать, экспериментировать, ошибатся и находить решение, что я всецело и оставляю вам.

С уважением Карпенко Кирилл, глава IT-отдела ИНПП.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *