Модель администрирования capabilities
Модель администрирования capabilities
Статус документа
Этот документ фиксирует операционную модель, по которой команда администрирует доступы в KSAILab после перехода на Keycloak.
Его цель: снять двусмысленность между IAM, platform permissions и frontend visibility.
По состоянию на 2026-03-11 backend auth foundation уже landed, поэтому ниже описывается не только target-state, но и текущий runtime baseline.
По состоянию на 2026-04-28 backend MR
ksailab-app-backend!30
добавил обязательную boundary-синхронизацию для учебных групп, access groups, question bank/test content scope и файлового доступа. Сводный публичный контракт находится в access-abac-boundaries.md.
Короткий ответ
Где хранится каталог возможностей
Каталог capabilities хранится как версионируемый продуктовый контракт в двух местах:
- в backend как capability registry;
- в документации как canonical human-readable catalog.
Новые capability keys не создаются из UI и не заводятся вручную в Keycloak.
Где capabilities администрируются
Повседневное администрирование выполняется в platform UI, а не в Keycloak:
- админ собирает bundles;
- назначает bundles пользователям и группам;
- делает точечные allow/deny overrides;
- проверяет итоговый effective access.
Где хранятся назначения прав
Рабочие назначения прав хранятся в БД платформы:
- bundles;
- assignments;
- overrides;
- связи между bundle и capability.
Для чего нужен Keycloak
Keycloak хранит IAM-контур:
- identity;
- credentials;
- login/logout endpoints и SSO session lifecycle;
- realm roles;
- clients;
- protocol mappers;
- service account
ksailab-admin-service.
Важно: ksailab-admin-service нужен backend для provisioning и lifecycle-операций. Это не human admin.
Keycloak не является основным хранилищем полного каталога platform permissions.
Keycloak также не является владельцем canonical browser session bootstrap для KsaiLab UI: эту роль выполняет backend через /api/v1/auth/login, /api/v1/auth/callback и GET /api/v1/auth/me.
Принцип разделения ответственности
| Зона | Где живёт | Кто администрирует | Как часто меняется |
|---|---|---|---|
| Пользователь, пароль, login/logout endpoints | Keycloak | IAM/platform admin | По жизненному циклу аккаунтов |
Базовые realm roles admin, teacher, student | Keycloak | IAM/platform admin | Редко |
| Capability definitions | Backend + docs | Разработчики | При добавлении новых фич |
| Permission bundles | Платформа БД | Platform admin | Часто |
| Assignments user/group -> bundle | Платформа БД | Platform admin | Часто |
| Allow/deny overrides | Платформа БД | Platform admin | Точечно |
| Resource-scoped ABAC | Backend | Разработчики | При развитии доменной модели |
Runtime baseline после backend MR !10
Сейчас в runtime уже подтверждено:
- canonical browser login entry идёт через
GET /api/v1/auth/login; - canonical browser callback идёт через
GET /api/v1/auth/callback; - canonical browser logout идёт через
POST /api/v1/auth/logout; - frontend bootstrap идёт через
GET /api/v1/auth/me; - backend уже разрешает runtime access через
access_assignment_mode, а итоговый access context считает по моделиprincipal + access snapshot + bundles + overrides; - local
POST /api/v1/auth/loginиPOST /api/v1/auth/refreshбольше не являются рабочим auth flow и возвращают410 Gone; - onboarding orchestration вынесен в отдельный
POST /api/v1/onboarding/*контур; - минимальный capability baseline уже существует в коде;
- отдельный access-group backend layer уже landed под
/api/v1/access-groups, аunassignedbootstrap-only state уже считается валидным runtime-состоянием.
Это нужно читать как один непрерывный контракт:
- frontend инициирует browser login только через backend entrypoint;
- backend владеет code exchange и browser auth session;
- frontend потребляет только уже поднятый bootstrap-контекст через
/auth/me.
Следующая logout/session wave добавляет сюда ещё четыре жёстких правила:
- browser cookie = opaque session key only;
- auth session и access/refresh/id tokens = server-side only;
Redis= target storage для browser auth session;local app logoutиfull platform logout= разные уровни одного контракта.
Текущие runtime capability keys
На сейчас backend уже отдаёт:
auth.bootstrap.readonboarding.teacher.createonboarding.group.createonboarding.group.assign_teacheronboarding.student.bulk_createonboarding.credentials.consume
Текущие runtime bundle identifiers
Сейчас backend возвращает минимальную bootstrap summary через технические bundle identifiers:
role:adminrole:teacherrole:student
Это не отменяет более богатую целевую модель bundles вроде admin.base или admin.permissions, но важно не путать текущий runtime baseline с будущим административным UX.
Эти identifiers также нужно читать как read-only runtime markers для immutable system roles, а не как признак того, что системные роли уже стали редактируемой сущностью.
Текущий runtime access snapshot
GET /api/v1/auth/me теперь публикует отдельный access block:
modeпоказывает, идёт ли runtime черезlegacy_role,access_groupилиunassigned;stateфиксирует текущее состояние выдачи прикладного доступа;groups,assigned_rolesиassigned_bundle_keysописывают runtime membership/access assignment отдельно от coarse realm role;unassigned/no accessсчитается штатным bootstrap-only состоянием identity до выдачи доступа.
Read-only permissions source of truth wave
Первая delivery-wave для permission administration deliberately вводит отдельный backend read layer без CRUD:
GET /api/v1/permissions/catalog;GET /api/v1/permissions/role-mappings;GET /api/v1/permissions/bundles.
Этот слой code-backed и нужен, чтобы frontend Permissions -> Roles и Permissions -> Groups перестали читать docs или локальные массивы как runtime source of truth.
Важно:
/auth/meостаётся bootstrap endpoint только для current principal;- новый
/api/v1/permissions/*слой не является generic/capabilitiesbootstrap endpoint; - role mappings и starter bundles в этой волне ещё не требуют отдельной DB-модели;
- assignments, overrides и write-оркестрация остаются follow-up треком;
permissions/groupsне является write-экраном для/api/v1/access-groups.
Current runtime boundaries for this wave
Прежде чем обсуждать целевую административную модель, текущий runtime нужно читать так:
- coarse system roles
admin,teacher,studentостаются code-backed и immutable для текущего и ближайшего write-flow; - editable custom roles считаются отдельной будущей сущностью и не должны документироваться как уже landed CRUD по системным ролям;
Permissions -> Groupsв этой wave остаётся snapshot/read-only surface и не подменяет ни рабочий контурEducation -> Groups, ни отдельный access-group CRUD под/api/v1/access-groups;Education -> Groupsнужно описывать только как учебные группы/roster/onboarding на/api/v1/groups/management*, а не как permission-side access assignment;permissions/groupsне может быть fallback CRUD для access groups: это read-only registry/snapshot, который показывает starter bundles и composition;- identity может существовать без прикладного доступа, а
unassigned/no accessнужно читать как валидное состояние до membership assignment; - учебный контент, question bank, tests и files теперь требуют resource-scope checks поверх role/capability: отсутствие active learning/access scope должно приводить к
403; - direct bundles/grants в этой wave допустимо описывать только внутри явного single-role scope, без обещания multi-role/custom-role orchestration;
- lifecycle для Keycloak-managed human accounts сейчас должен описываться как
disable/offboarding, а не как обычный delete; - tenant-aware правило для
organization_idуже публикуется backend как явный runtime contract:b2c-> optional,b2b/b2g-> required.
Целевая административная модель
Ниже описан target state для permission-side write model. Он не должен читаться как уже landed CRUD текущего backend read-only слоя.
1. Capability Definition
Capability definition — это стабильный ключ вида domain.resource.action.
Примеры:
education.courses.readeducation.courses.managepermissions.users.managemonitoring.logs.read
Definition включает:
- ключ;
- display name;
- описание;
- тип (
globalилиresource-aware); - backend enforcement note;
- frontend usage note;
- suggested starter bundles.
Capability definition добавляется только через релиз и одновременно фиксируется:
- в backend registry;
- в
permission-catalog.md; - во frontend permission matrix, если capability влияет на экран или действие.
2. Permission Bundle
Bundle — это рабочий административный набор capabilities.
Именно bundles должны быть основной единицей повседневного администрирования, потому что они:
- быстрее поддерживаются, чем ручные назначения по одному permission;
- уменьшают хаос;
- позволяют быстро включать новую фичу без правок Keycloak.
Рекомендуемый продуктовый стартовый набор bundles:
admin.baseadmin.permissionsadmin.monitoringteacher.basestudent.base
Эти bundle names нужно трактовать как target administrative design для платформенного UI.
3. Assignment
Assignment связывает subject с bundle.
Subject может быть:
- пользователем;
- access group;
- в будущем организационным контекстом или workspace.
Основной путь выдачи прав:
- realm role даёт базовую классификацию пользователя;
- platform admin назначает membership в access group или другой явный access container;
- access container добавляет bundles, direct grants и explicit role context только в рамках разрешённого single-role scope;
- backend рассчитывает effective capabilities;
- frontend получает готовый capability set через
GET /api/v1/auth/me.
Важно: этот шаг 5 не означает, что frontend владеет permissions. Он только потребляет backend-calculated result.
4. Overrides
Overrides нужны как исключение, а не как основная модель.
Разрешены два вида:
allow overridedeny override
Приоритет:
deny overrideallow override- bundle grants
- starter mapping from realm role
Это правило нужно считать canonical precedence для всей платформы.
Как это администрируется в platform UI
Раздел Permissions
Целевой административный UX должен опираться на уже существующие frontend-разделы:
Permissions -> UsersPermissions -> GroupsPermissions -> Roles
Permissions -> Users
Экран должен покрывать:
- просмотр пользователя;
- его realm role;
- список назначенных bundles;
- effective capabilities;
- точечные allow/deny overrides;
- создание teacher accounts;
- запуск one-time credential handoff;
- lifecycle actions через отдельный backend credential/admin contract.
Текущий frontend runtime уже пришёл к этой модели частично:
- teacher account provisioning идёт через onboarding contract;
- временный пароль раскрывается только через one-time credential receipt flow;
- каталог пользователей и safe lifecycle actions читаются из backend directory API;
- обычный delete Keycloak-managed human accounts intentionally unavailable; текущий операторский путь — disable/offboarding;
- follow-up password changes больше не живут в admin UI и уходят в self-service
Settings -> Securityflow; - assignments, effective capabilities, allow/deny overrides и admin credential lifecycle ещё ждут отдельный backend permission contract.
Settings -> Security
Self-service безопасность не должна смешиваться с административным CRUD раздела Permissions.
Текущий frontend contract здесь такой:
Settings -> Securityоткрывает только backend-owned account security entrypointGET /api/v1/auth/account/security;- frontend не вызывает legacy
/users/password/*endpoints как рабочую модель; - delete-account и admin reset-password остаются явно unavailable, пока backend не отдаст отдельный lifecycle contract.
Permissions -> Groups
В текущей wave этот экран нужно трактовать как snapshot/read-only surface permission-side metadata, а не как скрытый CRUD второй модели групп.
Он должен покрывать:
- просмотр starter bundle catalog и capability composition;
- просмотр starter role mappings и boundary notes для текущей permission model;
- честное указание, что runtime access-group lifecycle уже живёт отдельно под
/api/v1/access-groups; - переход в
Education -> Groups, если нужно управлять учебными группами и roster lifecycle.
Текущий frontend runtime здесь сознательно ограничен:
- экран больше не использует моковые группы, fake counters или локальный CRUD;
- собственный bundle catalog уже приходит из backend read layer;
- создание учебных групп и roster actions остаются в
Education -> Groups; - отдельный access-group CRUD уже живёт под
/api/v1/access-groups, но dedicated permission-side write UI для него остаётся follow-up треком; - если следующая wave добавит write-сущность в permission-side model, она должна быть названа явно как
access assignment,bundle assignment,grants groupилиaccess profile, а не как ещё одна безымяннаяgroup.
Permissions -> Roles
Этот экран в текущей wave нужно читать в двух слоях:
- immutable system roles
admin,teacher,studentкак runtime/read-only baseline; - отдельный future custom-role layer как follow-up write model.
Он должен покрывать:
- аудит code-backed system role mappings и их starter bundles;
- просмотр базового набора capabilities по role family;
- аудит, какие capabilities приходят из role starter mapping, а какие из bundle/override;
- честный shell для будущих custom roles без обещания CRUD по системным ролям.
Текущий frontend runtime здесь тоже уже подготовлен к следующей волне:
- список, detail route и create route уже разведены как отдельные экраны;
- список показывает runtime baseline
role:admin,role:teacher,role:studentи документированные starter bundles / scope groups; - backend read-model теперь поставляет этот snapshot через
GET /api/v1/permissions/role-mappings,GET /api/v1/permissions/bundlesиGET /api/v1/permissions/catalog; - create и edit intentionally не притворяются рабочим CRUD и не являются editor-ом системных ролей, пока backend role mapping orchestration и отдельный custom-role write contract ещё не отдали явный контракт.
При этом onboarding первой версии раскладывается так:
Permissions -> Users— teacher accounts;Education -> Groups— создание groups;Education -> Groups -> roster— bulk onboarding студентов.
Это важно, потому что student provisioning в KSAILab привязан к group membership, а не к абстрактной пользовательской карточке.
Tenant-aware organization policy
organization_id нельзя трактовать как локальную догадку фронта или как вечно optional поле по умолчанию.
Для этой wave canonical формулировка такая:
- в
b2cсценарияхorganization_idможет отсутствовать; - в
b2bиb2gсценарияхorganization_idдолжен быть required; - backend остаётся владельцем tenant mode и requiredness rule для create/update flows;
- backend уже публикует tenant policy через
contractsblock в/api/v1/permissions/*, а frontend/docs должны читать именно этот contract вместо локальных догадок.
Как быстро добавлять новую capability
Целевой рабочий сценарий должен занимать один релиз и несколько минут админской настройки после него.
Добавление новой фичи
- Разработчик добавляет capability key в backend registry.
- Разработчик обновляет
permission-catalog.md. - Разработчик добавляет backend policy check.
- Если capability влияет на UI, обновляется
frontend-permission-matrix.md. - После релиза и после landing permission-side write model platform admin обновляет bundle/access-group composition в явном access admin surface.
- Пользователи получают новый доступ через существующие assignments или access-group membership.
Это и есть модель релиз + UI-настройка.
Удаление capability
Безопасный порядок:
- отметить capability как
deprecatedв каталоге; - убрать её из bundles;
- удалить обращения из frontend;
- удалить backend policy usage;
- только после этого удалять definition.
Что меняется без релиза, а что нет
Без релиза можно
Ниже описан target operating mode после того, как permission-side write surface будет явно landed. Это не нужно читать как уже существующий CRUD текущего read-only runtime.
- создать новый bundle;
- выдать или снять bundle;
- сделать точечный override;
- поменять composition существующих bundles;
- изменить access-group membership или другое явное access assignment для уже существующего access container;
- отключить или включить пользователя через platform UI.
Для первой версии отдельно фиксируется:
- manual onboarding выполняет только
admin; - учётки teacher и student не создаются преподавателями;
- временный пароль выдаётся через
credential_receiptи одноразовый consume flow; - обычное добавление новой platform feature не требует правок в Keycloak.
- starter mappings для immutable system roles остаются code-backed и не должны трактоваться как admin-editable без релиза.
Требует релиза
- новый capability key;
- новая backend policy;
- новая resource-scoped ABAC логика;
- новая frontend feature, если у неё ещё нет capability contract;
- изменения в auth flow;
- изменения клиентов, realm roles или mapper contract в Keycloak.
Как быстро настраивать Keycloak
Основной принцип
Keycloak нужно настраивать как Realm as Code, а не вручную через GUI как единственный способ работы.
Это означает:
- realm/bootstrap/provisioning config хранится в
keycloak-scripts-ksailab; - clients, roles и mappers заводятся через bootstrap/import;
- ручные правки в UI допустимы только для диагностики и экстренного администрирования.
Отдельные repo boundaries для Keycloak-контура:
ci-pipelines/pipelines-ksailab— source of truth для pipeline logic;infra-keycloak-ksailab— физический deployment Keycloak;keycloak-theme-ksailab— login theme и branding.
Поддерживаемый scripting path для scripts repo — только Python. PowerShell fallback больше не считается supported operational path.
Что обязательно должно быть описано as code
- realm;
- clients
ksailab-frontend,ksailab-backend,ksailab-admin-service; - redirect URIs;
- logout URIs и post-logout redirect contract;
- realm roles
admin,teacher,student; - protocol mappers для базовых claims и roles;
- service account permissions для backend orchestration.
При этом ownership clients нужно описывать явно:
ksailab-frontendможет оставаться transition browser-login client, если runtime ещё опирается на него под backend initiation;ksailab-backendне нужно путать с canonical browser-login client, пока он остаётся backend-facing audience/bearer contract;- target architecture предпочитает отдельный backend browser-auth client, чтобы убрать двусмысленность ownership.
Временная политика работы через Keycloak GUI
До полной стабилизации realm as code допускается использовать Keycloak GUI, но только как вспомогательный инструмент:
- для первичного исследования настроек realm/client;
- для smoke-валидации после деплоя;
- для аварийного unblock, если нужно быстро подтвердить гипотезу.
При этом действуют жёсткие правила:
- Ручная GUI-правка не становится канонической сама по себе.
- Любое рабочее изменение должно быть перенесено в
keycloak-scripts-ksailabв том же delivery cycle. - Если GUI-изменение влияет на backend/frontend contract, это отдельно фиксируется в docs и связанных issue.
- Долгоживущая конфигурация, существующая только в GUI, считается техническим долгом.
Отдельная boundary по human admin bootstrap:
- первый human admin
platform-adminсоздаётся через отдельный operator flow в Keycloak; - backend runtime может только проверять presence и синхронизировать профиль при login;
ksailab-admin-serviceне должен описываться как human admin surrogate.
Когда Keycloak реально нужно менять
Правки в Keycloak нужны, если меняется:
- login flow;
- client configuration;
- redirect URI policy;
- post-logout redirect policy;
- realm roles;
- federation;
- SSO/integration topology;
- claims или mapper contract.
Для обычного добавления новой platform feature Keycloak менять не нужно. Для обычного изменения bundles, assignments, overrides или capability rollout Keycloak также не должен становиться точкой ежедневного администрирования.
Что должен рассчитывать backend
Backend должен возвращать в GET /api/v1/auth/me уже готовый effective access context:
principal;classification;realm_roles;access;effective_capabilities;bundles.
Backend остаётся единственным источником истины по effective permissions, потому что только он видит:
- realm roles из IAM;
- access-group membership и текущий
access_assignment_mode; - bundles и assignments из БД платформы;
- overrides;
- ABAC-контекст ресурса.
Отдельно:
/auth/meне является прямым чтением Keycloak token payload для UI;/auth/meне заменяет backend session ownership;/auth/meне отменяет обязательную backend ABAC-проверку реального действия.
Критерии правильной модели
Модель считается операционно удачной, если:
- Keycloak не превращается в хранилище сотен platform permissions;
- новые capability keys появляются только через контролируемый релиз;
- админ может за минуты выдать новую фичу через bundle composition;
- frontend строит меню и экраны по
GET /api/v1/auth/me; - backend централизованно проверяет доступ и не полагается на UI.
Cross-repo references and explicit follow-up
Repo-local backend truth для текущей волны:
- backend-ksailab:
docs/keycloak-onboarding-provisioning-flow.md - backend-ksailab:
docs/auth-capability-bootstrap-contract.md
Frontend runtime constraints для этой wave:
Явные follow-up треки, которые нельзя описывать как уже landed:
- полноценный custom-role CRUD и metadata contract;
- отдельная permission-side write model для access assignments / bundle assignments;
- dedicated frontend/admin orchestration поверх уже landed
/api/v1/access-groups; - dedicated
/coursesAPI и UI-contract, который заменит текущую topic-based course-like модель; - расширение текущего published tenant contract на будущие permission-side write surfaces.