docs: закрытие продуктовых рисков и новый LDD baseline
This commit is contained in:
@@ -60,8 +60,8 @@
|
||||
|
||||
**Разбор имени группы:** `^(.+)-(\d)(\d+)(БО|МО)$` → `stream` (буквы/дефисы), `course` (одна цифра),
|
||||
`number` (остальные цифры), `degree`. Примеры: `ПИ-11БО` → ПИ/1/1/БО; `ИВТ-ТОП-12БО` → ИВТ-ТОП/1/2/БО;
|
||||
`ПИ-101БО` → курс 1, номер 01 (ведущие нули допустимы). Разбор выполняется при создании/импорте,
|
||||
далее живём по полям; имя храним для отображения/выгрузок.
|
||||
`ПИ-101БО` → курс 1, номер 1. `number` — числовой; ведущий ноль не является отдельным значением.
|
||||
Разбор выполняется при создании/импорте, далее живём по полям; имя храним для отображения/выгрузок.
|
||||
|
||||
---
|
||||
|
||||
@@ -227,7 +227,29 @@ CREATE INDEX audit_events_action_idx ON identity.audit_events (action);
|
||||
```
|
||||
Внешних ключей нет: событие должно переживать удаление субъекта. Запись идёт
|
||||
из сервисов через `POST /api/v1/internal/audit` (`X-Internal-Token`), чтение —
|
||||
`GET /api/v1/audit` и `/admin/audit` только для админа.
|
||||
`GET /api/v1/audit?page=&limit=&action=` и `/admin/audit` только для админа.
|
||||
Страница аудита поддерживает `page` (с нуля) и `limit` (максимум 500), в UI —
|
||||
переход «Показать ещё».
|
||||
|
||||
**V11 — refresh-токены (добавлена при закрытии рисков аутентификации)**
|
||||
```sql
|
||||
CREATE TABLE identity.refresh_tokens (
|
||||
id uuid PRIMARY KEY,
|
||||
account_id uuid NOT NULL REFERENCES identity.accounts(id) ON DELETE CASCADE,
|
||||
token_hash varchar(64) NOT NULL UNIQUE,
|
||||
issued_at timestamptz NOT NULL,
|
||||
expires_at timestamptz NOT NULL,
|
||||
revoked_at timestamptz,
|
||||
replaced_by uuid
|
||||
);
|
||||
CREATE INDEX refresh_tokens_account_idx ON identity.refresh_tokens (account_id);
|
||||
CREATE INDEX refresh_tokens_expires_idx ON identity.refresh_tokens (expires_at);
|
||||
```
|
||||
Хранится только SHA-256-хеш raw-токена. Ротация при `POST /api/v1/auth/refresh`
|
||||
помечает старый токен заменённым; повторное использование заменённого токена
|
||||
трактуется как кража и отзывает все сессии аккаунта. `logout` и смена пароля
|
||||
отзывают refresh; отчисление, выпуск, архив и закрытая группа не дают
|
||||
обновить сессию.
|
||||
|
||||
### academic
|
||||
|
||||
@@ -400,8 +422,30 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
и защиты. Свой профиль студент открывает без записи в аудит, чтобы не
|
||||
засорять журнал и не нагружать identity на горячем пути. Админ смотрит
|
||||
журнал на `/admin/audit` с фильтром по действию; время отображается в МСК.
|
||||
`membership_history` и `promotion_runs/items` остаются доменной историей и
|
||||
дополняют общий журнал.
|
||||
Журнал читается страницами (`page`/`limit`, «Показать ещё»), а не только
|
||||
последними N событиями. `membership_history` и `promotion_runs/items`
|
||||
остаются доменной историей и дополняют общий журнал.
|
||||
|
||||
### 5.3. Аутентификация, сессии и отзыв доступа
|
||||
|
||||
- **Access-токен** — JWT на 15 минут (`LMS_ACCESS_TOKEN_TTL`), роли
|
||||
`ADMIN/TEACHER/STUDENT`, без списка курсов/групп внутри (скоуп проверяется
|
||||
сервисами на каждом запросе).
|
||||
- **Refresh-токен** — 7 дней (`LMS_REFRESH_TOKEN_TTL`), хранится хешем в
|
||||
`identity.refresh_tokens` (`V11`), ротируется при каждом обновлении.
|
||||
Повторное использование старого токена отзывает все активные сессии
|
||||
аккаунта; выход, смена пароля, отчисление/выпуск/архив и закрытая группа
|
||||
обновление запрещают.
|
||||
- **Прямой API-клиент** обязан обновлять пару через
|
||||
`POST /api/v1/auth/refresh`; `POST /api/v1/auth/logout` отзывает refresh.
|
||||
- **Web-сессия** — 7 дней sliding (`LMS_SESSION_TIMEOUT`), cookie `LMS_SESSION`
|
||||
(`HttpOnly`, `SameSite=Lax`, `Max-Age` обновляется на каждом ответе).
|
||||
Access-токен автоматически обновляется за 2 минуты до истечения, поэтому
|
||||
активная вкладка не требует повторного входа каждые 15 минут; при выходе
|
||||
refresh отзывается.
|
||||
- **Ограничение:** абсолютного лимита жизни сессии нет — при непрерывной
|
||||
активности она продлевается. Если политика потребует принудительный
|
||||
повторный вход раз в N дней, добавляется отдельный абсолютный `max-age`.
|
||||
|
||||
---
|
||||
|
||||
@@ -410,10 +454,13 @@ V13 идемпотентна: если старая feature-миграция V10
|
||||
- Аудитории: `ALL` (в т.ч. **неавторизованные**), `STUDENTS`, `TEACHERS`; глобальные публикует только админ.
|
||||
- Нужны **публичные маршруты** для `ALL` без аутентификации. `STUDENTS`/`TEACHERS`/курсовые материалы
|
||||
анонимно недоступны ни при каких условиях.
|
||||
- **Активы (риск):** материалы ссылаются на файлы (`/api/v1/assets/**`). Для публичных глав активы должны
|
||||
быть доступны, для приватных — нет. Решение: видимость актива наследуется от главы, и маршрут выдачи
|
||||
активов обязан это проверять (либо отдельный публичный префикс для `ALL`). Без этого публичные материалы
|
||||
будут ломаться, а приватные — утекать.
|
||||
- **Активы (реализовано):** видимость актива наследуется от статьи → раздела →
|
||||
главы, маршрут `/api/v1/content/assets/{name}` проверяет доступ. Файл без
|
||||
связи со статьёй не публичен: аноним/студент/преподаватель получают `404`,
|
||||
админ может скачать файл и получить список таких файлов через
|
||||
`GET /api/v1/content/legacy-assets` для привязки или удаления. Старые
|
||||
внешние ссылки на несвязанные файлы у обычных пользователей перестают
|
||||
работать — это осознанное secure-by-default поведение.
|
||||
- Поисковая индексация: для `ALL` — по умолчанию разрешена; для остального — закрыть (robots/noindex).
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user