🔑 RBAC и роли
Back-office - ролевая модель с матрицей прав. Кабинет - изоляция по компании (RBAC-права внутри кабинета не проверяются, роль membership'а формальная). Определения:
backoffice/app/rbac.py(роли+права),backoffice/app/auth.py(проверка),cabinet/app/auth.py(изоляция).
Роли back-office
| Роль | Смысл | Права |
|---|---|---|
ceo |
владелец | все (wildcard *, включая управление операторами users.*) |
operator |
операционист | клиенты/счета/платежи/FX/лимиты/карты/тарифы + AML raise/screen |
compliance |
комплаенс-офицер | ревью AML/Risk/KYC/SAR + скрининг (владеет комплаенсом) |
viewer |
наблюдатель | только чтение (*.view) |
Присваиваемые в UI роли: ceo, operator, compliance, viewer. Управление
операторами (создать/сменить роль/блок/локаут/сброс пароля) - CEO-only, с self-guard
и last-CEO-guard.
Матрица прав (полная)
| Право | ceo | operator | compliance | viewer |
|---|---|---|---|---|
| clients.view | ✅ | ✅ | ✅ | ✅ |
| clients.manage | ✅ | ✅ | - | - |
| kyc.view | ✅ | ✅ | ✅ | ✅ |
| kyc.manage | ✅ | - | ✅ | - |
| accounts.view | ✅ | ✅ | - | ✅ |
| accounts.manage | ✅ | ✅ | - | - |
| ledger.view | ✅ | ✅ | ✅ | ✅ |
| payments.view | ✅ | ✅ | ✅ | ✅ |
| payments.approve | ✅ | ✅ | - | - |
| fx.view | ✅ | ✅ | ✅ | ✅ |
| fx.manage | ✅ | ✅ | - | - |
| audit.view | ✅ | ✅ | ✅ | ✅ |
| reports.view | ✅ | ✅ | ✅ | ✅ |
| limits.view | ✅ | ✅ | ✅ | ✅ |
| limits.manage | ✅ | ✅ | - | - |
| messages.view | ✅ | ✅ | ✅ | - |
| messages.send | ✅ | ✅ | ✅ | - |
| aml.view | ✅ | ✅ | ✅ | ✅ |
| aml.raise | ✅ | ✅ | ✅ | - |
| aml.review | ✅ | - | ✅ | - |
| aml.screen | ✅ | ✅ | ✅ | - |
| risk.view | ✅ | ✅ | ✅ | ✅ |
| risk.manage | ✅ | - | ✅ | - |
| sanctions.view | ✅ | ✅ | ✅ | ✅ |
| cards.view | ✅ | ✅ | ✅ | ✅ |
| cards.manage | ✅ | ✅ | - | - |
| cards.reveal | ✅ | ✅ | - | - |
| fees.view | ✅ | ✅ | ✅ | ✅ |
| fees.manage | ✅ | ✅ | - | - |
| revenue.view | ✅ | ✅ | ✅ | ✅ |
| accounting.view | ✅ | ✅ | ✅ | ✅ |
| accounting.manage | ✅ | ✅ | - | - |
| sar.view | ✅ | ✅ | ✅ | ✅ |
| sar.manage | ✅ | - | ✅ | - |
📌 Разделение обязанностей: ревью AML (aml.review), risk-override (risk.manage),
управление KYC (kyc.manage) и владение SAR (sar.manage) - у compliance, НЕ у
operator. Operator двигает операции, compliance проверяет. CEO может всё.
Механизм проверки прав
Право проверяется зависимостью requires("<permission>") на роуте: она достаёт
текущего оператора из сессии и вызывает operator_can(role, permission). Логика:
роль с wildcard * (CEO) → всё разрешено; иначе право должно быть в явном наборе
роли. Иначе - HTTP 403. Роль хранится в Operator.role.
Сессия и аутентификация оператора
Подписанная cookie-сессия (capital_session, TimestampSigner), sliding-таймаут
(обновляется на каждом авторизованном запросе). Пароли - bcrypt. Локаут: 5 неудач →
блок на 15 мин (failed_login_attempts/locked_until). Опциональная 2FA (TOTP):
opt-in, при включённом TOTP логин двухшаговый (short-lived pending-токен → verify);
CEO без 2FA не блокируется.
Изоляция кабинета (client_id / company_id)
Кабинет - хард-изоляция по компании, а не роли. Сессия несёт пару
(user_id, company_id). Зависимость get_current_company одним запросом проверяет,
что активная company_id есть в membership'ах пользователя (join Membership↔
Company); чужой company_id в сессии → 401. Неактивная компания → 403. Это
linchpin безопасности кабинета (cabinet/app/auth.py, район строки 220). Все
эндпоинты берут компанию через эту зависимость и фильтруют выборки по company.id
в SQL. Роль в Membership (default owner) присутствует, но внутри кабинета не
проверяется - все члены компании имеют равный доступ к её данным.
CSRF (оба приложения)
Синхронайзер-токен на всех мутирующих запросах (POST/PUT/DELETE) через зависимость
verify_csrf: сверяет cookie-токен с токеном из заголовка X-CSRF-Token или
form-поля csrf_token (constant-time compare). Безопасные методы (GET/HEAD/OPTIONS/
TRACE) пропускаются. Cookie: capital_csrf (back-office) / capital_cab_csrf
(кабинет).
Сводка
| Аспект | Back-office | Кабинет |
|---|---|---|
| Идентичность | Operator (login+пароль) | CabinetUser (email+пароль) + Membership |
| Сессия | ID оператора | пара (user_id, company_id) |
| Роли | ceo/operator/compliance/viewer | owner (не проверяется) |
| Модель прав | роль → набор прав, requires() |
нет (равный доступ по membership) |
| Изоляция | однотенантно | хард по company_id на каждом запросе |
| CSRF | синхронайзер-токен | синхронайзер-токен |