Глава 40. SaaS
SaaS (Software as a Service, программное обеспечение как услуга) особенно хорошо показывает, почему один и тот же пользователь не обязательно принадлежит только одному контексту.
В простой многопользовательской системе можно представить модель:
Company
↓
Users
↓
Resources
Кажется естественным считать организацию основной областью доступа.
Пользователь принадлежит компании.
Ресурс принадлежит компании.
Значит, пользователь может работать с ресурсом.
Но реальная SaaS-система быстро добавляет другие отношения:
-
пользователь участвует в нескольких организациях;
-
внутри организации у него разные роли;
-
один ресурс может быть связан с проектом;
-
доступ может быть выдан непосредственно;
-
существуют внешние пользователи;
-
есть делегирование;
-
разные действия требуют разных прав;
-
некоторые ресурсы являются общими между организациями;
-
физические данные должны быть изолированы.
Поэтому tenant является важным измерением контекста, но не всей моделью доступа.
40.1 Tenant задаёт границу, но не готовое разрешение
Рассмотрим SaaS-систему с двумя организациями:
Company A
Company B
Пользователь:
User U
может состоять в обеих:
User U → Company A
User U → Company B
При этом его роль может отличаться:
Company A → Admin
Company B → Viewer
Поэтому вопрос:
"What can User U do?"
снова недостаточно точен.
Нужно спросить:
What can User U do
to Object O
within Company C?
Компания определяет область, в которой должно интерпретироваться отношение пользователя.
Но сама по себе принадлежность к компании ещё не является разрешением.
40.2 Один пользователь — несколько организаций
Это один из принципиальных случаев SaaS.
Пользователь может работать:
User U
├── Company A
├── Company B
└── Company C
При этом:
Company A → Admin
Company B → Editor
Company C → Viewer
Одна и та же операция:
UPDATE Object O
может иметь разные результаты в зависимости от того, к какой организации относится объект.
Поэтому роль нельзя хранить только как свойство пользователя:
User.role = EDITOR
Такое представление потеряло бы организационный контекст.
Точнее:
User
↓
Role
↓
Company
Роль становится отношением пользователя к определённой области.
40.3 Tenant и объект должны рассматриваться вместе
Пусть:
Object O1 → Company A
Object O2 → Company B
и:
User U ∈ Company A
User U ∈ Company B
Сам факт, что пользователь состоит в обеих организациях, не означает:
User U → same permissions → O1 and O2
Для каждого объекта нужно определить соответствующее отношение.
Например:
Company A:
User U → Admin → O1
Company B:
User U → Viewer → O2
Таким образом, tenant является частью контекста конкретного решения:
Context(User U, UPDATE, O1)
и:
Context(User U, UPDATE, O2)
могут содержать разные отношения.
40.4 Изоляция tenant — отдельный уровень безопасности
В SaaS есть важное требование, которого может не быть в обычной корпоративной системе:
данные одной организации не должны случайно стать доступны другой.
Это уже не только вопрос роли.
Даже если пользователь является администратором внутри:
Company A
это не должно автоматически означать доступ к:
Company B
Получается независимая граница:
Tenant boundary
Она ограничивает пространство, внутри которого вообще может рассматриваться обычная модель доступа.
Можно представить это так:
Tenant isolation
↓
Access model
↓
Effective permission
Изоляция клиента и authorization внутри клиента связаны, но это не одно и то же.
40.5 Одна организация — несколько уровней доступа
Даже внутри одного tenant простого правила:
User ∈ Company
→ access
обычно недостаточно.
Пусть есть:
Company A
├── Project P1
├── Project P2
└── Finance
Пользователь:
User U
может быть:
Admin → Company A
или:
Manager → Project P1
или:
Viewer → Project P2
Каждая роль действует в собственной области.
Поэтому даже внутри одного tenant контекст может иметь несколько измерений:
Tenant
+
Project
+
Role
+
Object
+
Action
40.6 Ресурс может быть связан с несколькими областями
Предположим, SaaS-система управляет документами.
Документ:
Document D
связан с:
Company A
Project P1
Но пользователь может получить доступ к нему через разные основания:
Owner
Project membership
Direct grant
Company role
Это не означает, что все основания одинаковы.
Например:
Company role → READ
Project role → UPDATE
Owner → MANAGE
Эффективное разрешение становится результатом их совместного применения.
SaaS здесь ничем принципиально не отличается от предыдущих примеров.
Меняется только предметная семантика отношений.
40.7 Общие ресурсы нарушают простую модель tenant
Теперь рассмотрим более сложный случай.
Некоторые данные могут быть общими для нескольких организаций:
Company A ─┐
├── Shared Resource
Company B ─┘
Например, это может быть:
-
общий каталог;
-
шаблон;
-
библиотека;
-
публичный внутри платформы справочник;
-
совместный проект.
Тогда утверждение:
Object → exactly one Company
может быть неверным для конкретного класса объектов.
Появляется другой вопрос:
как именно объект связан с организациями и какая семантика у этой связи?
Это снова возвращает нас к различию:
Tenant
≠
universal object scope
Для одних объектов tenant является жёсткой границей.
Для других — одной из областей публикации или совместного использования.
40.8 Прямой доступ не должен ломать tenant isolation
Пусть пользователь получил прямое разрешение:
User U
↓
READ
↓
Object O
Если O принадлежит другой организации, нельзя автоматически считать прямое разрешение достаточным.
Сначала должна быть определена семантика межорганизационного доступа.
Например:
Company A
↓
B2B relationship
↓
Company B
и только затем:
User U
↓
delegated access
↓
Object O
Такой доступ уже содержит несколько отношений.
Получается:
User
↓
Company A
↓
B2B relationship
↓
Company B
↓
Object
Сам по себе прямой grant не должен молча отменять организационную границу.
40.9 B2B показывает, что субъект тоже может быть составным
В межорганизационном SaaS действие может выполняться не просто:
User → Object
а:
User
↓
Company A
↓
B2B relationship
↓
Company B
↓
Object
При этом важно различать:
-
пользователя;
-
организацию, от имени которой он действует;
-
технический сервис;
-
конкретный объект;
-
отношение между организациями.
Например, сотрудник компании A может иметь право работать с данными компании B только в рамках определённого договора или проекта.
Тогда организация становится не просто tenant boundary, а участником отношения.
Это ещё один случай, когда:
Subject
нельзя свести к одному идентификатору пользователя.
40.10 В SaaS особенно важен уровень действия
Пусть пользователь имеет доступ к объекту:
Document D
Это ещё не означает одинаковое право на все операции.
Например:
READ
UPDATE
SHARE
EXPORT
DELETE
могут иметь разные правила.
Пользователь может:
READ → YES
UPDATE → YES
SHARE → NO
EXPORT → NO
Поэтому проверка:
User has access to Document D
слишком груба.
Нужно проверять:
Can(User, Action, Object, Context)?
Это особенно важно в SaaS, где операции вроде SHARE или EXPORT могут создавать новый поток данных за пределы исходной организации.
40.11 Изменение роли может изменить доступ сразу к множеству объектов
Предположим, пользователь:
User U
был:
Viewer → Company A
и стал:
Editor → Company A
Объекты при этом не изменились.
Но эффективные права пользователя изменились сразу для большого количества объектов.
То есть:
Role change
↓
Security model change
↓
Effective permissions change
↓
Access changes
Это тот же механизм распространения изменения, который мы рассматривали раньше.
SaaS просто делает его особенно заметным из-за большого количества объектов и пользователей.
40.12 Физическая изоляция должна соответствовать логической
Tenant isolation имеет ещё один уровень.
Допустим, приложение проверило:
User U belongs to Company A
Но затем выполнило SQL:
SELECT *
FROM documents;
без ограничения по tenant.
Логическая проверка ничего не спасает.
Физический путь к данным должен сохранять ту же границу.
Концептуально:
Tenant context
↓
Authorization
↓
Data enforcement
↓
Rows of Company A
Поэтому в SaaS особенно естественно использовать механизмы ограничения данных на уровне базы или другого физического хранилища.
Но это всё равно не превращает механизм физической изоляции в полную модель authorization.
40.13 Один запрос может работать сразу с несколькими tenant
Иногда пользовательский интерфейс позволяет администратору или оператору работать сразу с несколькими организациями.
Например:
Company A
Company B
Company C
Тогда запрос:
"Show all open incidents"
может охватывать несколько tenant.
Но это не означает, что tenant boundary исчезает.
Наоборот, результат должен сохранять происхождение каждой записи:
Incident 1 → Company A
Incident 2 → Company B
Incident 3 → Company C
И для каждой организации должны применяться соответствующие правила.
Это важный пример того, почему:
tenant_id
не всегда достаточно как единственного security attribute.
Нужен контекст конкретного действия.
40.14 Массовые запросы снова делают физическое enforcement критичным
SaaS-система постоянно выполняет запросы вида:
all documents
all projects
all users
all invoices
all tasks
Нельзя полагаться только на то, что каждый отдельный объект будет проверен после извлечения.
Например:
SELECT *
FROM invoices
WHERE ...
должен физически возвращать только те строки, которые находятся в допустимом пространстве данных.
Именно здесь хорошо разделяются два уровня:
Authorization
→ может ли субъект выполнять действие?
Data enforcement
→ какие физические данные могут участвовать в операции?
Для SaaS это различие особенно важно, потому что ошибка в одном фильтре может привести к смешению данных разных организаций.
40.15 Кэш и фоновые процессы тоже должны знать о границе tenant
SaaS редко ограничивается синхронным HTTP-запросом.
Есть:
cache
background jobs
exports
reports
notifications
search indexes
integrations
Каждый из этих компонентов может получить данные.
Поэтому tenant isolation должна сохраняться не только в основном API.
Например, кэшировать:
"all invoices"
без учёта tenant нельзя.
Нужна семантика вроде:
CacheKey
=
Tenant
+
Resource
+
Query
Но и это лишь техническое следствие более общего принципа:
производное представление данных не должно терять security boundary исходной модели.
40.16 SaaS показывает разницу между tenant и context особенно хорошо
Если бы контекст был равен tenant, достаточно было бы написать:
Context = Company A
Но для конкретного запроса этого мало.
Например:
User U
Company A
Project P
Role Manager
Object O
Action APPROVE
State Review
Все эти факты могут быть релевантны одновременно.
Поэтому:
Tenant ⊂ Context
если tenant вообще является частью конкретного решения.
Но:
Tenant ≠ Context
Tenant задаёт одну границу.
Контекст объединяет все релевантные для данного решения факты и отношения.
40.17 Что происходит при удалении пользователя из tenant
Предположим:
User U ∈ Company A
отношение удаляется.
Документы компании не меняются.
Проекты не меняются.
Но пользователь должен потерять соответствующие права.
Это снова показывает:
Relationship change
↓
Security projection change
↓
Access change
Причём если пользователь состоит в нескольких организациях, удаление из Company A не должно автоматически менять его доступ к Company B.
Изменение имеет область влияния.
Это одна из причин, по которой security facts полезно строить с явной областью применимости.
40.18 SaaS не требует одной универсальной модели доступа
Как и в предыдущих системах, не всякий SaaS требует сложной модели.
Для простого продукта может быть достаточно:
Tenant
+
Role
+
Permission
Если требования ограничиваются изоляцией клиентов и несколькими ролями, этого может быть вполне достаточно.
Но если появляются:
multiple organizations per user
project roles
groups
direct grants
B2B access
delegation
shared resources
object ownership
fine-grained actions
модель отношений становится необходимой.
Поэтому архитектура должна следовать требованиям предметной области, а не наоборот.
40.19 Главный вывод
SaaS показывает особенно ясно, что организационная граница и модель доступа — разные уровни.
Tenant отвечает на важный вопрос:
к какой области изоляции относится этот запрос или данные?
Но он не отвечает сам по себе:
что именно может делать этот пользователь с конкретным объектом?
Для этого нужны другие отношения:
User
↓
Tenant
↓
Role
↓
Project / Group
↓
Object
↓
Action
В межорганизационном доступе цепочка может стать ещё длиннее:
User
↓
Company A
↓
B2B relationship
↓
Company B
↓
Object
А физическая модель должна сохранить те же границы:
Access context
↓
Authorization
↓
Data enforcement
↓
Tenant-safe data
Таким образом, SaaS добавляет к нашей модели особенно важное измерение — границу организации.
Но эта граница не заменяет контекст.
Она является одним из его возможных элементов.
Именно поэтому даже в многопользовательской SaaS-системе недостаточно сказать:
User belongs to tenant
Нужно определить:
what relationship
to which object
for which action
within which tenant
under which conditions
И только после этого возникает эффективное разрешение.
Следующая глава переносит ту же модель на межсервисный и B2B-доступ. Там субъектом действия может быть уже не только пользователь, а организация, сервис или делегированный участник, а границы доверия проходят между несколькими системами.