Модуль авторизации для phact-cmf основанный на роутах.
- подключить модуль и заменить стандартный компонент auth в
settings.php
:
return [
'modules' => [
'Authorization',
...
],
'components' => [
'auth' => [
'class' => \Modules\Authorization\Components\Authorization::class,
],
...
],
...
]
- Создать таблицы в бд:
php www/index.php Base db
. - Заполнить таблицы значениями по-умолчанию:
php www/index.php Authorization init
.
Модуль является надстройкой над стандартным модулем auth(entication) и использует его модель User
.
User
получает новое полеgroups
(М2М). Меняется функционал поляis_superuser
(см. "Значения по-умолчанию" п.3.)
Модуль добавляет следующие модели:
Group
- группы выполняют функции ролей в терминологии RBAC. Являются по сути набором разрешений.Permission
- если упростить, разрешения являются таблицей связи(М2М) между группой и роутом с дополнительным полемparams
(параметры роута). Но на самом деле являются центральным элементом в управление доступами и используются как самостоятельная модель.Route
- базовая единица доступа, используемая в модуле. Модель имеет всего 2 поля:name
- название роута вида[название неймспейса]:[краткое название роута]
('main:index').label
- человекочитаемое название роута.
После выполнения команды Authorization init
будут созданы:
- Стандартные роуты:
- Роуты
admin:login
иmanage:login
, необходимые для аутентификации в админках. - Общие роуты для каждого неймспейса(модуля) вида
[название модуля]:*
, например:
- Роуты
admin:*
manage:*
main:*
meta:*
editor:*
...
-
Стандартные и системные группы:
- guest - Базовая системная группа, которая будет содержать минимальный необходимый функционал для работы с сайтом. Любой пользователь, включая не аутентифицированных автоматически считается членом этой группы. Эта группа имеет доступ ко всем перечесленным в п.1. роутам, кроме
admin:*
иmanage:*
. - staff - По-умолчанию имеет доступ к 2 стандартным роутам:
admin:*
иmanage:*
. - admin - Системная группа. Автоматически получает доступ ко всем возможным роутам, включая создаваемые в будущем, даже если связи группа-роут не указаны явно.
- guest - Базовая системная группа, которая будет содержать минимальный необходимый функционал для работы с сайтом. Любой пользователь, включая не аутентифицированных автоматически считается членом этой группы. Эта группа имеет доступ ко всем перечесленным в п.1. роутам, кроме
-
Пользователи. Модуль не создает новых пользователей, но использует существующих для предоставления стандартных доступов:
- все существующие пользователи становятся членами группы staff.
- все пользователи с флагом
is_superuser
становятся членами группы admin. Сам флагis_superuser
в дальнейшем будет являться синонимом наличия группы admin у пользователя.
Модуль подписывается на событие application.beforeRunController
и автоматически проверяет доступ пользователя к текущему роуту. В случае неудачной авторизации выполняется следующий порядок:
0. В лог кладется предупреждение о неудачной попытке доступа.
- Генерируется событие вида
authorization.403.{$namespace}
, где$namespace
- неймспейс текущего роута(обычно равен названию модуля). Это событие позволяет каждому конкретному модулю самостоятельно обработать неудачную авторизацию(показать сообщение об ошибке/архивировать/выполнить переадресацию и т.д.). - Если на событие из п.1. никто не отреагировал, герерируется событие
authorization.403
, которое может использоваться для указания стандартных "глобальных" дейтсвий при ошибке авторизации. - Если событие из п.2. также было проигнорировано - модуль положит в лог сообщение об ошибке авторизации, вернет 403й http-код и остановит выполнение приложения.
События из п.1 и п.2. передают такой же набор параметров, что и событие
application.beforeRunController
.
Стандартных действий модуля из предыдущего параграфа достаточно для предотвращения несанкционированного доступа к любому роуту. Если же требуются дополнительные действия на основе авторизации(например, шаблонизация интерфейса), можно воспользоваться главным методом модуля:
bool User::hasPermission(string $routeName, array $params = null)
Метод принимает:
$routeName
- название роута(см. "Общие сведения"Route->name
)$params
- необазательный аргумент. Ассоциативный массив параметров роута. Служит для уточнения объектов к которым требуется получить доступ на основе параметров роута. Например, для роутаadmin:update
вида/update/{:module}/{:admin}/{:pk}
самый подробный запрос на проверку доступа будет выглядеть так:
Phact::app()->user->hasPermission('admin:update', [
'module' => 'main',
'admin' => 'PostAdmin',
'pk' => 3,
]);
В массиве параметров необязетльно указывать все параметры или можно указать пустое значение для параметра, который вас не интересует.
Метод возвращает true
, если пользователь имеет доступ к указанным роуту с параметрами, иначе false
.
В процессе проверки доступа будут сравниваться указанные роут и его параметры с оными у имеющихся у пользователя разрешений(все разрешения всех групп, в которых состоит пользователь). Разрешение имеющееся у пользователя должно быть >=
запрошенному. Процедура сравнения разрешений:
- Сравнение роутов:
- Считается, что общий роут
'admin:*' > 'admin:update'
. В таком случае метод немедленно возвращаетtrue
. - Роуты равны, если равны их названия:
'admin:update' = 'admin:update'
. Если роуты равны - выполняется сравнение параметров.
- Считается, что общий роут
- Сравнение параметров:
- Каждое разрешение может иметь перечисление из нескольких значений для каждого параметра роута. В таком случае список разрешеных значений будет представлен в виде подмассива:
[ 'module' => [ 'admin', 'main', ], 'admin' => 'PostAdmin', 'pk' => 3, ]
- любой параметр может быть пропущем, в таком случае пустое значение олицетворяет весь спектр возможных значений. Например, доступ ко всем админкам из модулей
admin
иmain
:[ 'module' => [ 'admin', 'main', ], 'admin' => '', ]
- при сравнение параметров требуется, чтобы имеющиеся "покрывали" запрошенные. Т.е. чтобы в массиве перечисленных разрешенных параметров присутствовало значение параметра, указанное во входном массиве функции
hasPermission
или чтобы параметры, имеющиеся у пользователя были пустыми.
- Каждое разрешение может иметь перечисление из нескольких значений для каждого параметра роута. В таком случае список разрешеных значений будет представлен в виде подмассива:
Дано: пользователь имеет разрешение к роуту admin:update
с параметрами:
[
'module' => [
'admin',
'main',
],
'admin' => '',
'pk' => ['4', '5'],
]
Доступ к редактирование всех моделей из модулей admin
и main
с ключами 4
или 5
.
Тесты:
Phact::app()->user->hasPermission('admin:update') == false
//
каждый не уточненный параметр олицетворяет все возможные значения
Phact::app()->user->hasPermission('admin:update', [
'module' => '',
'admin' => 'asdasd',
'pk' => '4',
]) == false
//
значения указанные для модуля предполагает любое возможное, когда доступ разрешен к 2 конкретным значениям
Phact::app()->user->hasPermission('admin:update', [
'module' => 'editor',
'admin' => '',
'pk' => '4',
]) == false
//
значения указанные для модуля отсутствует в списке разрешенных
Phact::app()->user->hasPermission('admin:update', [
'module' => 'main',
'admin' => 'asdasd',
'pk' => '4',
]) == true
//
значения указанные для модуля и ключа присутствуют в имеющихся перечислениях, значение параметра admin может быть любым
Phact::app()->user->hasPermission('admin:update', [
'module' => 'main',
'admin' => '',
'pk' => '4',
]) == true
Phact::app()->user->hasPermission('manage:update', [
'module' => 'main',
'pk' => '4',
]) == true
Phact::app()->user->hasPermission('admin:update', [
'module' => 'main',
'admin' => '',
]) == false
В панели администратора настраиваются связи User - Group
и Group - Permission
.
При настройке разрешений можно отдельно подключать списки значений для каждого параметра роута.
Списки привязываются к имени параметра внутри своего модуля, т.е. списки для параметра module
в модуле Main
и Manage
необходимо настраивать отдельно. Так же каждый список может указать список конкретных роутов, к которым он может применяться.
Для настройки списков используется класс Authorization\RouteParamsList\RouteParamsList
, от которого необходимо унаследовать свой класс списков и положить в неймспейс вида Modules\[ModuleName]\RouteParamsList\[ListName]
. В минимальной конфигурации такой класс должен реализовать метод initList()
, который должен сгенерировать ассоциативный массив. Ключи этого массива будут использоваться для хранения данных в бд, а значения могут быть человекочитаемыми.
Значения параметров без подключенных списков можно перечислять разделяя их символом |
.