Phase C, Stage C-1 (ветка feature/custom-roles): таблица roles (name, label, base_roles JSON, is_builtin) + засев встроенных. auth.effectiveRoles(role) — кастомная роль наследует base_roles (какие встроенные гейты проходит); встроенные — быстрый путь без БД. requireRole() теперь проверяет пересечение allowed с effectiveRoles → 111 существующих гейтов не задеты (встроенные ведут себя как прежде). Дизайн: PHASE_C_DESIGN.md. Тест effectiveRoles 5/5; полный backend pass. ВАЖНО (обнаружено): users.role в канон-схеме имеет CHECK (admin/teacher/student/ free_student), безопасно пересобрать users (FK от многих таблиц, миграции в txn) нельзя → присвоение кастомной роли пользователю пойдёт через users.custom_role (C-2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.4 KiB
Phase C — кастомные роли / делегирование / пер-классовый скоуп (дизайн)
Составлен 2026-06-03 (Opus). Это архитектурная фаза — отдельная ветка + согласование модели ДО реализации. Здесь — факты по коду и развилка, чтобы выбрать подход.
Факты (проверено по коду/БД)
users.role—TEXT NOT NULL DEFAULT 'student', без CHECK → новое значение роли можно хранить без пересборки таблицы. В ходу: admin, student, teacher (free_student зеркалит student в коде).role_permissions.role— CHECKIN ('teacher','student','free_student')→ конфиг дефолтов для новой роли требует миграции-пересборки (SQLite не ALTER-ит CHECK).requireRole('...')— 111 вызовов в 24 файлах, строки ролей захардкожены. Любая новая роль не пройдёт ни один такой гейт, пока её явно не добавить в нужные списки.registry.byRole()строит UI только для teacher/student;requirePermissionчитает права live из БД (user → role → дефолт реестра) — это уже гибко и НЕ зависит от requireRole.
Суть проблемы
«Способности» выражены двумя разными механизмами: грубый requireRole (кто вообще в разделе) + тонкий requirePermission (конкретное действие). Кастомные роли упираются в requireRole: он не дата-управляемый.
Варианты (по возрастанию объёма/риска)
Вариант 1 — Узкий слой: роль «Родитель» (read-only) ★ минимум риска
Уже есть инфраструктура parentAuth + parent_links (отдельный токен, привязка к ученику). Доделать
родительский доступ как «роль» для просмотра прогресса/оценок ребёнка — без вторжения в requireRole
(родитель ходит через parentAuth, не через requireRole). Объём: малый. Польза: конкретная и частая.
Вариант 2 — Курируемые доп-роли (например «методист/завуч», «классрук», «ассистент») ★ среднее
- Миграция: расширить CHECK
role_permissions.role(пересборка) на новые роли + засеять их дефолты. registry: добавить роли вroles[]нужных ключей +byRoleдля UI.- requireRole точечно: добавить новую роль в те из 111 вызовов, где она должна иметь доступ (например «методист» = доступ к аналитике/вопросам, но не к админ-настройкам).
- UI вкладки «Доступ · роли»: секции для новых ролей. Объём: средний (зависит от числа гейтов на роль). Риск: средний (точечные правки auth).
Вариант 3 — Полностью произвольные роли (admin создаёт роли в UI) ★ крупное, рискованное
Нужен слой capabilities: заменить requireRole на проверку способностей роли (данные в БД), т.е.
переписать ~111 точек на requireCapability(...) + UI конструктора ролей + резолвер. Это недели работы
и затрагивает аутентификацию целиком. Делать только при реальной потребности в произвольных ролях.
C10 / C11 (отдельные подпункты)
- C10 делегирование учителю — дать учителю менять часть студенческих прав в рамках его классов.
Технически = пер-классовый скоуп (C11) + расширение прав
/class/:id/bulkна учителя-владельца. - C11 пер-классовый скоуп прав — правило права на уровне класса (как content_access). Резолвер начинает учитывать ещё один слой (class-override между user и role). Средне-крупно.
Рекомендация
Начать с Варианта 1 (роль «Родитель», read-only) — конкретная польза, минимум риска, не трогает requireRole. Затем, если нужно, Вариант 2 для одной конкретной роли (скажем «методист»). Вариант 3 и C11 — только под явный запрос (большой рефактор аутентификации).
Реализация — на ветке
feature/custom-roles. Сначала выбрать вариант/конкретную роль.