illusiard / yii2-entity-acl
Unix-like ACL + JSON conditions (allow/deny) for entities and records in Yii2
Package info
github.com/Illusiard/yii2-entity-acl
Type:yii2-extension
pkg:composer/illusiard/yii2-entity-acl
Requires
- php: >=8.5
- yiisoft/yii2: ^2.0.55
Requires (Dev)
- phpunit/phpunit: ^13.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-18 09:42:00 UTC
README
ACL-система для Yii2 с unix-подобной моделью прав и расширяемыми условиями доступа.
Пакет предназначен для использования совместно с yii2-base-entity-system,
но не имеет жёсткой зависимости от него и может применяться самостоятельно.
Основная цель — дать прозрачную, расширяемую и предсказуемую модель контроля доступа, подходящую для сложных бизнес-сценариев (CRM, SaaS, back-office системы).
Требования
- PHP 8.5+
- Yii 2.0.55+
Основные принципы
-
Unix-like модель прав
- owner / group / other
- битовые флаги операций
- отдельные правила для сущности и для конкретной записи
-
Allow / Deny conditions
- условия могут как запрещать, так и разрешать доступ
- deny всегда имеет приоритет
- allow может разрешить доступ даже если базовые права запрещают
-
Расширяемость
- subject (кто) — через
SubjectResolverInterface - when (условия) — через handler’ы условий
- storage — через
AclStorageInterface
- subject (кто) — через
-
Детерминированный порядок принятия решений
- без неявной магии
- поведение зафиксировано тестами
Операции и флаги
Используются следующие битовые флаги операций:
| Операция | Флаг |
|---|---|
| list | 1 |
| read | 2 |
| create | 4 |
| update | 8 |
| delete | 16 |
| restore | 32 |
| permanentDelete | 64 |
Флаги комбинируются через побитовое OR.
- delete — логическое удаление / деактивация / soft delete
- restore — восстановление после мягкого удаления
- permanentDelete — физическое удаление записи
Таблицы
bes_acl_record
Хранит базовые права доступа.
Используется одна таблица:
- для прав на сущность (
record_id IS NULL) - для прав на конкретную запись (
record_id = <id>)
Основные поля:
entityrecord_id(NULL = правило для сущности)owner_flagsgroup_flagsother_flagsowner_id(varchar(64), поддерживает UUID)group_id(varchar(64), поддерживает UUID)priority
bes_acl_condition
Хранит дополнительные условия доступа.
Основные поля:
entityrecord_id(NULL = условие для сущности)effect(allow/deny)ops_masksubject_jsonwhen_jsonpriorityenabled
Порядок принятия решения
Для запроса доступа выполняются шаги:
-
Определение операции
- операция переводится в битовую маску
-
Базовое правило (
bes_acl_record)- сначала ищется правило для конкретной записи
- если нет — правило для сущности
- определяется сегмент: owner / group / other
- проверяются флаги
-
Conditions (
bes_acl_condition)- выбираются все условия для операции
- применяются record-level и entity-level условия
- проверяются
subjectиwhen - если сработал deny → доступ запрещён
- если сработал allow → доступ разрешён
-
Итог
- allow condition
- иначе base allow
- иначе deny (по умолчанию)
Deny всегда имеет приоритет над allow и base-правами.
Subject (кто)
Subject описывает, к кому применяется условие.
Пример subject_json:
{
"userId": 1,
"groupId": 10
}
Поддерживаемые ключи (по умолчанию):
userIdgroupIdownerId
AccessRequest::$userId, а также идентификаторы владельца и группы принимают int|string.
Разрешение этих значений выполняется через SubjectResolverInterface.
SubjectResolverInterface
interface SubjectResolverInterface { /** @return int|string|list<int|string>|null */ public function resolveGroupId(int|string $userId, array $context = []): int|string|array|null; public function resolveOwnerId(AccessRequest $req): int|string|null; }
Дефолтная реализация (ContextSubjectResolver) читает значения из AccessRequest->context:
groupIdsилиgroup_ids— список групп;groupIdилиgroup_id— одна группа либо список для обратной совместимости.
Имя resolveGroupId() сохранено для совместимости; метод может вернуть один идентификатор или список.
При проверке группового ACL и условий достаточно совпадения любого идентификатора из списка.
When (когда)
When описывает условия времени/контекста, при которых правило применяется.
Пример when_json:
[
{ "type": "betweenHours", "from": 9, "to": 17 }
]
Поддержка условий реализована через handler’ы:
interface ConditionHandlerInterface { public function supports(string $type): bool; public function evaluate(array $payload, AccessRequest $req, ConditionEngine $engine): bool; }
Ссылки на другие условия
Допускается ссылка на другие conditions:
[
{ "condition": [12, 15] }
]
Все указанные условия должны выполниться (логика AND).
Кеширование
DbAclStorage использует in-memory кеш на время запроса:
- ACL записи
- список conditions
- conditions по id
Это позволяет вызывать can() много раз без повторных запросов к БД.
Использование
use illusiard\entity_acl\models\dto\AccessRequest; use illusiard\entity_acl\AclService; $req = new AccessRequest( userId: '01995d22-6bc8-72e4-92b7-8bca0e75f01f', entity: 'post', operation: 'read', recordId: '10', context: [ 'groupIds' => [ '01995d3b-4d4a-710b-9f84-f08f05bd6f07', '01995d62-f585-710a-a688-c1da35cd47d9', ], 'ownerId' => '01995d22-6bc8-72e4-92b7-8bca0e75f01f', ] ); $allowed = AclService::instance() ->getPolicy() ->can($req);
Тесты
Логика ACL полностью покрыта unit-тестами:
- порядок применения правил
- приоритет deny / allow
- entity-level vs record-level
- subject-resolver
- conditions
Тесты являются частью спецификации поведения.
Статус
Пакет предоставляет стабильное ядро ACL. UI, редакторы правил и визуализация намеренно не входят в состав и должны реализовываться на уровне клиентского кода или других пакетов.
Лицензия
BSD 3-Clause