Search by

illusiard / yii2-entity-acl

Illusiard

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

Statistics

Installs: 11

Dependents: 1

Suggesters: 0

Stars: 0

v1.4.0 2026-09-20 09:14 UTC

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+

Основные принципы

  1. Unix-like модель прав

    • owner / group / other
    • битовые флаги операций
    • отдельные правила для сущности и для конкретной записи
  2. Allow / Deny conditions

    • условия могут как запрещать, так и разрешать доступ
    • deny всегда имеет приоритет
    • allow может разрешить доступ даже если базовые права запрещают
  3. Расширяемость

    • subject (кто) — через SubjectResolverInterface
    • when (условия) — через handler’ы условий
    • storage — через AclStorageInterface
  4. Детерминированный порядок принятия решений

    • без неявной магии
    • поведение зафиксировано тестами

Операции и флаги

Используются следующие битовые флаги операций:

Операция Флаг
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>)

Основные поля:

  • entity
  • record_id (NULL = правило для сущности)
  • owner_flags
  • group_flags
  • other_flags
  • owner_id (varchar(64), поддерживает UUID)
  • group_id (varchar(64), поддерживает UUID)
  • priority

bes_acl_condition

Хранит дополнительные условия доступа.

Основные поля:

  • entity
  • record_id (NULL = условие для сущности)
  • effect (allow / deny)
  • ops_mask
  • subject_json
  • when_json
  • priority
  • enabled

Порядок принятия решения

Для запроса доступа выполняются шаги:

  1. Определение операции

    • операция переводится в битовую маску
  2. Базовое правило (bes_acl_record)

    • сначала ищется правило для конкретной записи
    • если нет — правило для сущности
    • определяется сегмент: owner / group / other
    • проверяются флаги
  3. Conditions (bes_acl_condition)

    • выбираются все условия для операции
    • применяются record-level и entity-level условия
    • проверяются subject и when
    • если сработал deny → доступ запрещён
    • если сработал allow → доступ разрешён
  4. Итог

    • allow condition
    • иначе base allow
    • иначе deny (по умолчанию)

Deny всегда имеет приоритет над allow и base-правами.

Subject (кто)

Subject описывает, к кому применяется условие.

Пример subject_json:

{
  "userId": 1,
  "groupId": 10
}

Поддерживаемые ключи (по умолчанию):

  • userId
  • groupId
  • ownerId

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