Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Глава 20. Проверка доступа к объекту

20.1. От модели к конкретному запросу

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

Теперь появляется конкретный запрос:

User A
    ↓
READ
    ↓
Object X

Система должна ответить:

ALLOW

или:

DENY

Но этот ответ не возникает непосредственно из одной записи о пользователе.

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

В общем случае цепочка выглядит так:

Subject
   ↓
Relevant facts
   ↓
Context
   ↓
Access grounds
   ↓
Rules
   ↓
Effective permission
   ↓
Object authorization

И только после этого можно перейти к данным объекта.


20.2. Эффективного разрешения недостаточно

Предположим, пользователь имеет:

READ

Это ещё не означает, что он может читать любой объект.

Нужно знать область действия этого права.

Например:

READ in Project A

не означает:

READ everywhere

Поэтому проверка должна сопоставлять две стороны:

permission side

и:

object side

Одна сторона говорит:

Какие права есть у субъекта в данной области?

Другая:

В каких областях и с какими правами объект доступен?

Только их пересечение позволяет принять решение.


20.3. Публикация находится на стороне объекта

Публикация объекта — это не разрешение пользователя.

Она сообщает:

В какой области объект опубликован и какие права допускает эта публикация?

Например:

Object X
   ↓ published in
Project P

Это ещё не означает:

User A → READ → Object X

Нужно дополнительно установить, имеет ли пользователь эффективное разрешение в Project P.

Таким образом:

Object publication
        +
Subject effective permission
        ↓
Object authorization

20.4. Владелец — отдельное основание

Владелец объекта представляет особый случай.

В текущей модели Guardian право владельца проверяется независимо от effective_user_permission.

Концептуально:

OWNER scope
    ↓
owner_id = current user
    ↓
permission mask
    ↓
required action

Это важно.

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

Тем самым сохраняется различие:

Creator ≠ Owner ≠ Effective permission

Создатель может передать владение.

После этого право должно следовать за владельцем, а не за created_by.


20.5. Обычная ветка проверки

Для обычного доступа текущая модель Guardian использует две проекции:

object_scope
        +
effective_user_permission

Связь выполняется по:

company_id
user_id
scope_type
scope_id

а затем проверяется пересечение масок.

Упрощённо:

User permission
        &
Object publication ceiling
        &
Required permission
        != 0

То есть:

(eup.permission_mask
 &
 os.permission_mask
 &
 required_mask) != 0

Это конкретное правило Guardian, а не универсальная формула всех систем доступа.


20.6. Упрощённый Guardian-псевдокод

Текущую проверку можно представить так:

function hasObjectPermission(objectId, requiredMask):

    identity = currentIdentity()

    if exists OWNER scope where
           scope.objectId == objectId
       and scope.companyId == identity.companyId
       and scope.scopeType == OWNER
       and scope.scopeId == identity.userId
       and (scope.permissionMask & requiredMask) != 0:

        return ALLOW


    if exists objectScope where
           objectScope.objectId == objectId
       and objectScope.scopeType != OWNER
       and exists effectiveUserPermission where
           effectiveUserPermission.companyId
               == objectScope.companyId
       and effectiveUserPermission.userId
               == identity.userId
       and effectiveUserPermission.scopeType
               == objectScope.scopeType
       and effectiveUserPermission.scopeId
               == objectScope.scopeId
       and (
           effectiveUserPermission.permissionMask
           &
           objectScope.permissionMask
           &
           requiredMask
       ) != 0:

        return ALLOW

    return DENY

Это не буквальная копия SQL.

Это сокращённое представление его архитектурной логики.


20.7. Где здесь группа

На этом месте легко сделать неправильный вывод.

Можно предположить:

hasObjectPermission
    ↓
effective_user_group
    ↓
check membership

Но текущая модель Guardian устроена иначе.

effective_user_group используется раньше.

Он участвует в построении эффективных разрешений, в частности в обработке Scope Barrier и расширении прав по иерархии.

После построения:

effective_user_permission

runtime-проверка объекта использует уже этот результат.

Поэтому:

effective_user_group

не является непосредственным runtime-фильтром в has_object_permission().


20.8. Почему это разделение важно

Если выполнять всю логику отношений во время каждого запроса, система должна каждый раз заново определять:

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

Это дорого и усложняет проверку.

В Guardian большая часть этой работы происходит заранее.

Поэтому запрос видит уже подготовленные:

effective_user_permission
object_scope

а не весь граф исходных отношений.


20.9. Объектная авторизация ещё не является доступом к данным

Даже если:

has_object_permission(...) = ALLOW

это не означает:

можно читать любые физические строки,
относящиеся к объекту

Логический объект и физические данные могут иметь разные границы.

Один объект может быть представлен:

table A
table B
table C

или несколькими строками одной таблицы.

Поэтому нужна ещё одна граница:

Object authorization
        ↓
Data enforcement

20.10. Главный вывод

Проверка доступа к объекту — это не поиск пользователя в ACL.

Это сопоставление:

Subject effective permission
        ×
Object security scope
        ×
Requested action

В Guardian:

effective_user_permission
        +
object_scope
        ↓
has_object_permission()

При этом владелец проверяется отдельной веткой.

Но даже положительное решение на уровне объекта не отменяет последующую защиту физических данных.