docs: видимость активов и итоговые проверки
This commit is contained in:
@@ -141,8 +141,10 @@ flowchart TD
|
|||||||
|
|
||||||
- Роли: `ADMIN`, `STUDENT`; конвертер ролей читает claim `role` как `ROLE_<role>`.
|
- Роли: `ADMIN`, `STUDENT`; конвертер ролей читает claim `role` как `ROLE_<role>`.
|
||||||
- Публичные пути: `/actuator/**`, `/api/v1/auth/{health,setup,login,jwks.json}`,
|
- Публичные пути: `/actuator/**`, `/api/v1/auth/{health,setup,login,jwks.json}`,
|
||||||
`/api/v1/content/tree`, `/api/v1/content/assets/**`,
|
`/api/v1/content/tree`, `/api/v1/content/{chapter}/{section}/{article}`.
|
||||||
`/api/v1/content/{chapter}/{section}/{article}`.
|
`/api/v1/content/assets/**` gateway пропускает без JWT (legacy/публичные
|
||||||
|
`ALL`-активы), но academic проверяет видимость статьи и для курсовых или
|
||||||
|
ролевых активов требует токен.
|
||||||
- RSA-ключи identity хранятся в таблице `settings` и публикуются в JWKS.
|
- RSA-ключи identity хранятся в таблице `settings` и публикуются в JWKS.
|
||||||
|
|
||||||
## 5. Рендеринг страниц и защита форм (web)
|
## 5. Рендеринг страниц и защита форм (web)
|
||||||
|
|||||||
@@ -125,8 +125,10 @@ printf("Ответ: 42\n");
|
|||||||
* Главы, которых нет в архиве совсем, не затрагиваются — их нужно удалить вручную.
|
* Главы, которых нет в архиве совсем, не затрагиваются — их нужно удалить вручную.
|
||||||
* Минимальный архив (например, только один раздел + `assets/`) безопасно импортировать
|
* Минимальный архив (например, только один раздел + `assets/`) безопасно импортировать
|
||||||
в режиме merge: остальные материалы останутся как есть.
|
в режиме merge: остальные материалы останутся как есть.
|
||||||
* Ссылки вида `assets/имя` автоматически заменяются на публичный адрес
|
* Ссылки вида `assets/имя` автоматически заменяются на адрес
|
||||||
`/api/v1/content/assets/...`; сами файлы складываются в хранилище.
|
`/api/v1/content/assets/...`; сами файлы складываются в хранилище. Для
|
||||||
|
курсовых статей этот адрес требует сессию пользователя курса, для публичных
|
||||||
|
`ALL`-материалов и старых несвязанных файлов остаётся публичным.
|
||||||
|
|
||||||
## Публикация и скрытие
|
## Публикация и скрытие
|
||||||
|
|
||||||
|
|||||||
@@ -272,6 +272,9 @@ CREATE UNIQUE INDEX chapters_slug_course_uidx ON chapters (course_id, slug) WHER
|
|||||||
Признак видимости актив наследует от статьи→раздела→главы (course/audience). Требуется проверка доступа в
|
Признак видимости актив наследует от статьи→раздела→главы (course/audience). Требуется проверка доступа в
|
||||||
маршруте выдачи активов (см. §6). Отдельных колонок достаточно опционально; минимум — корректная авторизация.
|
маршруте выдачи активов (см. §6). Отдельных колонок достаточно опционально; минимум — корректная авторизация.
|
||||||
|
|
||||||
|
> В реализации backfill связей вынесен в `V12__asset_links_backfill.sql`, а
|
||||||
|
> проверка доступа — в `/api/v1/content/assets/{name}` (см. [15](15-stage5-course-materials.md)).
|
||||||
|
|
||||||
**V10 — уникальное название курса (реальный файл `V10__course_title_unique.sql`)**
|
**V10 — уникальное название курса (реальный файл `V10__course_title_unique.sql`)**
|
||||||
```sql
|
```sql
|
||||||
-- Защита от дублей курса при конкурентном bootstrap и повторном создании.
|
-- Защита от дублей курса при конкурентном bootstrap и повторном создании.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Статус:** план согласован, этапы 0–5 выполнены. Проверка этапа 6 выполнена
|
**Статус:** план согласован, этапы 0–5 выполнены. Проверка этапа 6 выполнена
|
||||||
(тесты, upgrade миграций, security, deploy-runbook), но этап 6 закрыт не
|
(тесты, upgrade миграций, security, deploy-runbook), но этап 6 закрыт не
|
||||||
полностью: остаются права преподавателя на запись, видимость активов и CI —
|
полностью: остаются права преподавателя на запись и CI —
|
||||||
см. § 9.1 и [16-stage6-report.md](16-stage6-report.md).
|
см. § 9.1 и [16-stage6-report.md](16-stage6-report.md).
|
||||||
**Основа:** [07-groups-courses.md](07-groups-courses.md).
|
**Основа:** [07-groups-courses.md](07-groups-courses.md).
|
||||||
**Дата решений:** 2026-10-23.
|
**Дата решений:** 2026-10-23.
|
||||||
@@ -205,12 +205,12 @@
|
|||||||
|
|
||||||
Номера уточняются при реализации, чтобы не конфликтовать с максимумами master
|
Номера уточняются при реализации, чтобы не конфликтовать с максимумами master
|
||||||
(identity V3, **academic V5**, progress V12). Максимумы этой ветки:
|
(identity V3, **academic V5**, progress V12). Максимумы этой ветки:
|
||||||
identity V9, **academic V11**, progress V13 (+V14 при необходимости аудита).
|
identity V9, **academic V12**, progress V13 (+V14 при необходимости аудита).
|
||||||
|
|
||||||
| Сервис | Миграции | Содержание |
|
| Сервис | Миграции | Содержание |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| identity | V4–V9 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status` |
|
| identity | V4–V9 | роли, поля групп, статусы студентов, `membership_history`, `promotion_runs/items`, `accounts.status` |
|
||||||
| academic | V6–V11 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `SET NOT NULL` — post-bootstrap шаг вне Flyway |
|
| academic | V6–V12 + post-bootstrap step | курсы, преподаватели, группы курса, `labs.course_id` (nullable), материалы курса/аудитории, частичные индексы, активы; `V10` — уникальный индекс `lower(title)` по курсам; `V11` — backfill `audience='ALL'` для глобальных глав; `V12` — backfill связей `article_assets` из markdown-ссылок; `SET NOT NULL` — post-bootstrap шаг вне Flyway |
|
||||||
| progress | V13 (+ V14 при необходимости аудита) | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит скачиваний — этап 6 |
|
| progress | V13 (+ V14 при необходимости аудита) | `grade_comments.author_account_id`, `lab_grades.updated_by_account_id`, индекс `lab_grades(lab_id)`; аудит скачиваний — этап 6 |
|
||||||
|
|
||||||
### Переход со старой feature-миграции V10 на V13
|
### Переход со старой feature-миграции V10 на V13
|
||||||
@@ -288,7 +288,9 @@ identity V9, **academic V11**, progress V13 (+V14 при необходимос
|
|||||||
- **Межсервисная авторизация.** Самый сложный участок; ADR до кода.
|
- **Межсервисная авторизация.** Самый сложный участок; ADR до кода.
|
||||||
- **Перенос данных.** Нужны preflight и копия БД; группы с нестандартными именами
|
- **Перенос данных.** Нужны preflight и копия БД; группы с нестандартными именами
|
||||||
и дубли номеров лаб могут заблокировать `NOT NULL`.
|
и дубли номеров лаб могут заблокировать `NOT NULL`.
|
||||||
- **Активы.** Сейчас публичны и не связаны со статьями; нужна миграция связей.
|
- **Legacy-активы.** Связанные активы наследуют видимость статьи (миграция `V12`),
|
||||||
|
но файлы без markdown-ссылки остаются публичными; нужна инвентаризация и
|
||||||
|
привязка или удаление.
|
||||||
- **JWT 30 дней.** Средний вариант блокировки оставляет остаточный риск для
|
- **JWT 30 дней.** Средний вариант блокировки оставляет остаточный риск для
|
||||||
прямого API; фиксируем в документации.
|
прямого API; фиксируем в документации.
|
||||||
- **Комментарии без автора.** Старые записи останутся с «автор не указан».
|
- **Комментарии без автора.** Старые записи останутся с «автор не указан».
|
||||||
@@ -320,16 +322,17 @@ identity V9, **academic V11**, progress V13 (+V14 при необходимос
|
|||||||
Отчёт: [15-stage5-course-materials.md](15-stage5-course-materials.md).
|
Отчёт: [15-stage5-course-materials.md](15-stage5-course-materials.md).
|
||||||
- **Этап 6 — проверка выполнена, закрыт не полностью.** Проверены полный
|
- **Этап 6 — проверка выполнена, закрыт не полностью.** Проверены полный
|
||||||
прогон тестов/линтеров, upgrade миграций с master-схемы, security-матрица,
|
прогон тестов/линтеров, upgrade миграций с master-схемы, security-матрица,
|
||||||
gateway и deploy-runbook. Отчёт: [16-stage6-report.md](16-stage6-report.md).
|
gateway, видимость активов и deploy-runbook. Отчёт: [16-stage6-report.md](16-stage6-report.md).
|
||||||
Не закрыты: права преподавателя на запись, видимость активов, аудит
|
Не закрыты: права преподавателя на запись, аудит скачиваний и CI. Остальные
|
||||||
скачиваний и CI. Остальные пункты hardening (индексы, backup/restore,
|
пункты hardening (индексы, backup/restore, документация, smoke) описаны в
|
||||||
документация, smoke) описаны в отчёте и чек-листе.
|
отчёте и чек-листе.
|
||||||
|
|
||||||
### 9.1. Известные пробелы после этапа 6
|
### 9.1. Известные пробелы после этапа 6
|
||||||
|
|
||||||
- **Активы публичны.** `/api/v1/content/assets/{name}` не проверяет курс и
|
- **Legacy-активы без привязки.** Маршрут `/api/v1/content/assets/{name}`
|
||||||
статью; нужна проверка `article_assets → article → section → chapter` и
|
проверяет `article_assets → article → section → chapter`, а `V12`
|
||||||
visibility-aware маршруты.
|
восстанавливает связи из markdown. Файлы без ссылок в статьях остаются
|
||||||
|
публичными; нужна инвентаризация и привязка или удаление.
|
||||||
- **Преподаватель — в основном read-only.** Авторство лабораторных/материалов,
|
- **Преподаватель — в основном read-only.** Авторство лабораторных/материалов,
|
||||||
распределение, оценки, комментарии и защита остаются admin-only в UI/API;
|
распределение, оценки, комментарии и защита остаются admin-only в UI/API;
|
||||||
ТЗ ожидает преподавательский self-service.
|
ТЗ ожидает преподавательский self-service.
|
||||||
|
|||||||
@@ -11,10 +11,11 @@
|
|||||||
- Миграции:
|
- Миграции:
|
||||||
- identity `V4–V9`: роли, группы-когорты, статусы студентов, повышение,
|
- identity `V4–V9`: роли, группы-когорты, статусы студентов, повышение,
|
||||||
история членства, статусы аккаунтов;
|
история членства, статусы аккаунтов;
|
||||||
- academic `V6–V11`: курсы, преподаватели, группы курса, `labs.course_id`,
|
- academic `V6–V12`: курсы, преподаватели, группы курса, `labs.course_id`,
|
||||||
материалы курса/аудитории, индексы активов; `V10` (уникальный индекс
|
материалы курса/аудитории, индексы активов; `V10` (уникальный индекс
|
||||||
`lower(title)` по курсам) добавлена на этапе 2, после этого отчёта;
|
`lower(title)` по курсам) добавлена на этапе 2, после этого отчёта;
|
||||||
`V11` backfill-ом проставляет `audience='ALL'` глобальным главам;
|
`V11` backfill-ом проставляет `audience='ALL'` глобальным главам;
|
||||||
|
`V12` backfill-ом восстанавливает связи `article_assets` из markdown-ссылок;
|
||||||
- progress `V13`: автор комментария и последнего изменения оценки.
|
- progress `V13`: автор комментария и последнего изменения оценки.
|
||||||
- Preflight-скрипты:
|
- Preflight-скрипты:
|
||||||
- `deploy/preflight/groups-courses.sql` — до миграций;
|
- `deploy/preflight/groups-courses.sql` — до миграций;
|
||||||
|
|||||||
@@ -74,6 +74,8 @@
|
|||||||
- `V9__article_assets_indexes.sql` — индекс `article_assets(article_id)`.
|
- `V9__article_assets_indexes.sql` — индекс `article_assets(article_id)`.
|
||||||
- `V10__course_title_unique.sql` — уникальный индекс `lower(title)` по курсам.
|
- `V10__course_title_unique.sql` — уникальный индекс `lower(title)` по курсам.
|
||||||
- `V11__global_audience_all.sql` — backfill `audience='ALL'` для глобальных глав.
|
- `V11__global_audience_all.sql` — backfill `audience='ALL'` для глобальных глав.
|
||||||
|
- `V12__asset_links_backfill.sql` — backfill связей `article_assets` из
|
||||||
|
markdown-ссылок для видимости активов.
|
||||||
|
|
||||||
## 2. API
|
## 2. API
|
||||||
|
|
||||||
@@ -137,8 +139,11 @@
|
|||||||
- **Материалы курса и аудитории.** Схема `chapters.course_id/audience` готова
|
- **Материалы курса и аудитории.** Схема `chapters.course_id/audience` готова
|
||||||
(`V8`), но URL `/learn/course/{course_no}/...` и проверка аудитории ещё не
|
(`V8`), но URL `/learn/course/{course_no}/...` и проверка аудитории ещё не
|
||||||
реализованы: читалка материалов пока работает по прежним глобальным правилам.
|
реализованы: читалка материалов пока работает по прежним глобальным правилам.
|
||||||
- **Активы.** `V9` добавляет только индекс; наследуемая видимость активов
|
- **Активы — закрыто после этапа 5.** `V9` добавил индекс;
|
||||||
от статьи → раздела → главы и проверка доступа при выдаче файлов — этап 3.
|
`V12__asset_links_backfill.sql` восстановил связи `article_assets` из
|
||||||
|
markdown-ссылок, а `/api/v1/content/assets/{name}` проверяет наследуемую
|
||||||
|
видимость статьи → раздела → главы. Остаточный случай — файлы без ссылок
|
||||||
|
(см. [15](15-stage5-course-materials.md)).
|
||||||
- **Проверка аккаунтов преподавателей.** Academic принимает UUID без обращения
|
- **Проверка аккаунтов преподавателей.** Academic принимает UUID без обращения
|
||||||
к identity: не проверяются существование аккаунта, роль `TEACHER` и статус
|
к identity: не проверяются существование аккаунта, роль `TEACHER` и статус
|
||||||
`ACTIVE`. Нужна внутренняя проверка identity при назначении.
|
`ACTIVE`. Нужна внутренняя проверка identity при назначении.
|
||||||
|
|||||||
@@ -24,15 +24,32 @@
|
|||||||
Миграция `V11__global_audience_all.sql` backfill-ом проставляет `ALL` старым
|
Миграция `V11__global_audience_all.sql` backfill-ом проставляет `ALL` старым
|
||||||
глобальным главам; курсовые главы остаются с `audience = NULL`.
|
глобальным главам; курсовые главы остаются с `audience = NULL`.
|
||||||
|
|
||||||
## Остающийся разрыв: активы
|
## Видимость активов (закрыто после этапа 5)
|
||||||
|
|
||||||
Публичный прокси активов `/api/v1/content/assets/{name}` и прокси web
|
Разрыв этапа 5 закрыт: активы привязаны к статьям и наследуют их видимость.
|
||||||
`/api/v1/content/assets/{name}` **не изменялись**. Активы по-прежнему не привязаны
|
|
||||||
к главе/статье и не наследуют курсовую видимость. Для этапа 6 нужно:
|
|
||||||
|
|
||||||
- связать `article_assets` со статьёй/главой и проверять доступ по токену;
|
- Добавлены entity/repository `article_assets`; импорт контента сохраняет
|
||||||
- отдавать картинки курсовых статей только авторизованным пользователям курса;
|
связи для файлов, на которые ссылаются статьи.
|
||||||
- предусмотреть отдельный безопасный путь для публичных `ALL`-материалов.
|
- Миграция `V12__asset_links_backfill.sql` backfill-ом восстанавливает связи
|
||||||
|
для уже загруженных статей: ищет в `articles.body_md` ссылки вида
|
||||||
|
`/api/v1/content/assets/{name}`, создаёт строки `article_assets` с MIME по
|
||||||
|
расширению и `size = 0`. Повторный запуск не создаёт дубликатов.
|
||||||
|
- Публичный API `/api/v1/content/assets/{name}` проверяет доступ по всем
|
||||||
|
статьям, к которым привязан файл (достаточно одной видимой):
|
||||||
|
- актив без связей в `article_assets` — legacy-файл, публичен как раньше;
|
||||||
|
- глобальная опубликованная статья `ALL` — публична, в том числе анонимно;
|
||||||
|
- глобальные `STUDENTS`/`TEACHERS` — только с JWT и соответствующей ролью;
|
||||||
|
- курсовой актив — только с JWT и доступом к курсу: админ видит любой курс,
|
||||||
|
преподаватель — свой (включая скрытые материалы), студент — только
|
||||||
|
опубликованные материалы своего курса. Скрытый актив для студента отдаёт
|
||||||
|
`404` (не раскрывает существование), чужой курс — `403`.
|
||||||
|
- Web-прокси `/api/v1/content/assets/{name}` передаёт Bearer-токен из web-сессии,
|
||||||
|
поэтому защищённые картинки на страницах `/learn/...` открываются, а на
|
||||||
|
публичных страницах прокси остаётся анонимным.
|
||||||
|
|
||||||
В рамках этапа 5 это осознанно не реализуется, чтобы не ломать существующие
|
**Остаточный риск — legacy-активы без привязки.** `V12` закрывает только файлы,
|
||||||
публичные ссылки на изображения.
|
на которые есть ссылки в markdown. Если файл был загружен, но ни одна статья на
|
||||||
|
него не ссылается (осиротевший или удалённый из текста актив), он остаётся
|
||||||
|
публичным. Нужна инвентаризация таких файлов и привязка к статье либо удаление;
|
||||||
|
до этого поведение сохраняется осознанно, чтобы не ломать старые публичные
|
||||||
|
ссылки.
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
**Дата:** 2026-10-27.
|
**Дата:** 2026-10-27.
|
||||||
**Ветка:** `feature/groups-courses`.
|
**Ветка:** `feature/groups-courses`.
|
||||||
**Статус:** проверка этапа 6 выполнена; этап 6 закрыт **не полностью**.
|
**Статус:** проверка этапа 6 выполнена; этап 6 закрыт **не полностью**.
|
||||||
Миграции, security-матрица, upgrade-тест и deploy-runbook проверены. Открытыми
|
Миграции, security-матрица, upgrade-тест, видимость активов и deploy-runbook
|
||||||
остаются права преподавателя на запись, видимость активов, аудит и CI — см.
|
проверены. Открытыми остаются права преподавателя на запись, аудит и CI — см.
|
||||||
раздел 4.
|
раздел 4.
|
||||||
|
|
||||||
## 1. Итоговый объём этапов 0–5
|
## 1. Итоговый объём этапов 0–5
|
||||||
|
|
||||||
| Этап | Что реализовано | Отчёт |
|
| Этап | Что реализовано | Отчёт |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 0 | Схема и контракты: identity `V4–V9`, academic `V6–V11`, progress `V13`; preflight-скрипты; ADR; контракт профиля студента курса | [10](10-stage0-report.md) |
|
| 0 | Схема и контракты: identity `V4–V9`, academic `V6–V12`, progress `V13`; preflight-скрипты; ADR; контракт профиля студента курса | [10](10-stage0-report.md) |
|
||||||
| 1 | Роли `ADMIN/TEACHER/STUDENT`, группы-когорты и парсер имени, статусы студентов и аккаунтов, `membership_history`, импорт v2 | [11](11-stage1-report.md) |
|
| 1 | Роли `ADMIN/TEACHER/STUDENT`, группы-когорты и парсер имени, статусы студентов и аккаунтов, `membership_history`, импорт v2 | [11](11-stage1-report.md) |
|
||||||
| 2 | Курсы, `course_no` для браузерных URL, история преподавателей и групп, bootstrap курса C, привязка лабораторных к курсу, копирование лабораторных, админ-UI | [12](12-stage2-report.md) |
|
| 2 | Курсы, `course_no` для браузерных URL, история преподавателей и групп, bootstrap курса C, привязка лабораторных к курсу, копирование лабораторных, админ-UI | [12](12-stage2-report.md) |
|
||||||
| 3 | Аккаунты преподавателей, курсовая область видимости, внутренние API identity с `LMS_INTERNAL_TOKEN`, кабинет преподавателя, профиль студента курса и скачивание всех попыток | [13](13-stage3-report.md) |
|
| 3 | Аккаунты преподавателей, курсовая область видимости, внутренние API identity с `LMS_INTERNAL_TOKEN`, кабинет преподавателя, профиль студента курса и скачивание всех попыток | [13](13-stage3-report.md) |
|
||||||
@@ -26,6 +26,9 @@
|
|||||||
- преподаватель видит только активные назначенные курсы; студент — только курсы
|
- преподаватель видит только активные назначенные курсы; студент — только курсы
|
||||||
активной группы; админ — все курсы;
|
активной группы; админ — все курсы;
|
||||||
- студент видит только опубликованные оценки и комментарии;
|
- студент видит только опубликованные оценки и комментарии;
|
||||||
|
- активы наследуют видимость статьи → раздела → главы: курсовые требуют доступа
|
||||||
|
к курсу, глобальные — аудитории, а старые файлы без привязки остаются
|
||||||
|
публичными (см. раздел 4);
|
||||||
- вход закрыт для отчисленных, выпущенных и архивных студентов, а также для
|
- вход закрыт для отчисленных, выпущенных и архивных студентов, а также для
|
||||||
архивных аккаунтов; web дополнительно перепроверяет статус;
|
архивных аккаунтов; web дополнительно перепроверяет статус;
|
||||||
- повышение и жизненный цикл пишут историю; применение повышения идемпотентно.
|
- повышение и жизненный цикл пишут историю; применение повышения идемпотентно.
|
||||||
@@ -42,6 +45,10 @@
|
|||||||
|
|
||||||
Результат: `BUILD SUCCESSFUL`, код возврата `0`, все модули зелёные.
|
Результат: `BUILD SUCCESSFUL`, код возврата `0`, все модули зелёные.
|
||||||
|
|
||||||
|
Счётчики включают новые тесты видимости активов: `academic-service` +9
|
||||||
|
(`ContentAssetAccessIntegrationTest`, импорт активов), `web` +2
|
||||||
|
(`ApiClientContentAssetTest`). Итог — **183 теста, 0 падений**.
|
||||||
|
|
||||||
Для получения актуальных счётчиков тесты запущены принудительно:
|
Для получения актуальных счётчиков тесты запущены принудительно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -51,11 +58,11 @@
|
|||||||
| Модуль | Тестов | Failures | Errors | Skipped |
|
| Модуль | Тестов | Failures | Errors | Skipped |
|
||||||
|---|---:|---:|---:|---:|
|
|---|---:|---:|---:|---:|
|
||||||
| identity-service | 62 | 0 | 0 | 0 |
|
| identity-service | 62 | 0 | 0 | 0 |
|
||||||
| academic-service | 53 | 0 | 0 | 0 |
|
| academic-service | 62 | 0 | 0 | 0 |
|
||||||
| web | 40 | 0 | 0 | 0 |
|
| web | 42 | 0 | 0 | 0 |
|
||||||
| progress-service | 12 | 0 | 0 | 0 |
|
| progress-service | 12 | 0 | 0 | 0 |
|
||||||
| markdown | 5 | 0 | 0 | 0 |
|
| markdown | 5 | 0 | 0 | 0 |
|
||||||
| **Итого** | **172** | **0** | **0** | **0** |
|
| **Итого** | **183** | **0** | **0** | **0** |
|
||||||
|
|
||||||
`ktlintCheck` и `detekt` прошли без замечаний. Отдельных unit-тестов у gateway
|
`ktlintCheck` и `detekt` прошли без замечаний. Отдельных unit-тестов у gateway
|
||||||
и `common-http` в текущем дереве нет; маршрутизация gateway проверена
|
и `common-http` в текущем дереве нет; маршрутизация gateway проверена
|
||||||
@@ -73,7 +80,7 @@
|
|||||||
статья/актив, оценка, комментарий и попытка сдачи.
|
статья/актив, оценка, комментарий и попытка сдачи.
|
||||||
3. Поверх master применены миграции ветки:
|
3. Поверх master применены миграции ветки:
|
||||||
- identity `V4–V9`;
|
- identity `V4–V9`;
|
||||||
- academic `V6–V11`;
|
- academic `V6–V12`;
|
||||||
- progress `V13`.
|
- progress `V13`.
|
||||||
4. Выполнены postflight-запросы и повторное применение `V13`.
|
4. Выполнены postflight-запросы и повторное применение `V13`.
|
||||||
|
|
||||||
@@ -87,7 +94,10 @@
|
|||||||
- **academic:** добавлены `labs.course_id`, `chapters.course_id/audience`,
|
- **academic:** добавлены `labs.course_id`, `chapters.course_id/audience`,
|
||||||
индексы `labs_number_course_uidx`, `chapters_slug_global_uidx`,
|
индексы `labs_number_course_uidx`, `chapters_slug_global_uidx`,
|
||||||
`chapters_slug_course_uidx`, `article_assets_article_idx`,
|
`chapters_slug_course_uidx`, `article_assets_article_idx`,
|
||||||
`courses_title_lower_uidx`. Курс C миграциями не создаётся — его создаёт
|
`courses_title_lower_uidx`. `V12__asset_links_backfill.sql`
|
||||||
|
восстанавливает связи `article_assets` из markdown-ссылок
|
||||||
|
`/api/v1/content/assets/{name}`; файлы без ссылок остаются несвязанными.
|
||||||
|
Курс C миграциями не создаётся — его создаёт
|
||||||
bootstrap после входа админа, поэтому `labs_without_course` до bootstrap
|
bootstrap после входа админа, поэтому `labs_without_course` до bootstrap
|
||||||
ожидаемо больше нуля.
|
ожидаемо больше нуля.
|
||||||
- **progress:** `grade_comments.author_account_id` и
|
- **progress:** `grade_comments.author_account_id` и
|
||||||
@@ -106,13 +116,20 @@
|
|||||||
|
|
||||||
### 2.3. Security-проверки
|
### 2.3. Security-проверки
|
||||||
|
|
||||||
Проверено автоматическими тестами (полный прогон 172 теста) и разбором
|
Проверено автоматическими тестами (полный прогон 183 теста) и разбором
|
||||||
конфигурации gateway.
|
конфигурации gateway.
|
||||||
|
|
||||||
| Сценарий | Ожидание | Факт |
|
| Сценарий | Ожидание | Факт |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Аноним → курс/материалы курса | нет доступа | `401` на `/api/v1/courses/{id}/content/tree`; `/api/v1/courses/by-no/{no}` — `401` |
|
| Аноним → курс/материалы курса | нет доступа | `401` на `/api/v1/courses/{id}/content/tree`; `/api/v1/courses/by-no/{no}` — `401` |
|
||||||
| Аноним → публичные материалы | только глобальные `ALL` | публичное дерево содержит только глобальные главы с `audience = ALL`; курсовая глава через `/api/v1/content/...` — `404` |
|
| Аноним → публичные материалы | только глобальные `ALL` | публичное дерево содержит только глобальные главы с `audience = ALL`; курсовая глава через `/api/v1/content/...` — `404` |
|
||||||
|
| Аноним → актив глобальной `ALL`-статьи | публично | `200` |
|
||||||
|
| Аноним → курсовой/`STUDENTS`/`TEACHERS` актив | `404` | `404`, существование файла не раскрывается |
|
||||||
|
| Студент своего курса → актив опубликованной статьи | доступ | `200`; скрытый курсовой актив — `404` |
|
||||||
|
| Студент/преподаватель чужого курса → курсовой актив | `403` | `403` |
|
||||||
|
| Преподаватель своего курса → скрытый курсовой актив | доступ | `200`; админ видит любой актив |
|
||||||
|
| Глобальный `STUDENTS`-актив → преподаватель | `403` | `403`; студент получает `200` |
|
||||||
|
| Legacy-актив без связи со статьёй | публично | `200`; остаточный риск, раздел 4 |
|
||||||
| Админ → курс, профиль, скачивание | полный доступ | доступ к любому курсу и студенту, скачивание любой попытки курса |
|
| Админ → курс, профиль, скачивание | полный доступ | доступ к любому курсу и студенту, скачивание любой попытки курса |
|
||||||
| Преподаватель своего курса | курс, группы, профиль, материалы | видит опубликованные и скрытые материалы, всех студентов активной группы курса, неопубликованные оценки и все попытки |
|
| Преподаватель своего курса | курс, группы, профиль, материалы | видит опубликованные и скрытые материалы, всех студентов активной группы курса, неопубликованные оценки и все попытки |
|
||||||
| Преподаватель чужого курса | `403` | `403` на материалы, курс по `course_no`, состав группы и профиль |
|
| Преподаватель чужого курса | `403` | `403` на материалы, курс по `course_no`, состав группы и профиль |
|
||||||
@@ -124,8 +141,8 @@
|
|||||||
| Gateway → заголовки личности | клиент не подменяет | `X-Account-Id/Login/Role` удаляются и проставляются только после проверки JWT |
|
| Gateway → заголовки личности | клиент не подменяет | `X-Account-Id/Login/Role` удаляются и проставляются только после проверки JWT |
|
||||||
| Web-сессия | быстрый отзыв доступа | статус перепроверяется не реже чем раз в 45 секунд; `active=false` или `401/403` инвалидируют сессию; недоступность identity не разлогинивает всех |
|
| Web-сессия | быстрый отзыв доступа | статус перепроверяется не реже чем раз в 45 секунд; `active=false` или `401/403` инвалидируют сессию; недоступность identity не разлогинивает всех |
|
||||||
|
|
||||||
Остаточные риски перечислены в разделе 4: публичные активы и 30-дневный JWT
|
Остаточные риски перечислены в разделе 4: legacy-активы без привязки,
|
||||||
для прямого API.
|
права преподавателя на запись и 30-дневный JWT для прямого API.
|
||||||
|
|
||||||
## 3. Чек-лист деплоя
|
## 3. Чек-лист деплоя
|
||||||
|
|
||||||
@@ -147,7 +164,7 @@
|
|||||||
1. **Flyway.** `./deploy/deploy.sh up` собирает образы и запускает
|
1. **Flyway.** `./deploy/deploy.sh up` собирает образы и запускает
|
||||||
`postgres → identity → academic → progress → gateway → web`. Миграции
|
`postgres → identity → academic → progress → gateway → web`. Миграции
|
||||||
применяются автоматически при старте сервисов:
|
применяются автоматически при старте сервисов:
|
||||||
identity `V4–V9`, academic `V6–V11`, progress `V13`.
|
identity `V4–V9`, academic `V6–V12`, progress `V13`.
|
||||||
Отдельный ручной запуск Flyway не нужен.
|
Отдельный ручной запуск Flyway не нужен.
|
||||||
2. **Bootstrap курса C.** После входа админа открыть `/admin/courses`.
|
2. **Bootstrap курса C.** После входа админа открыть `/admin/courses`.
|
||||||
Пока курсов нет, показан блок первичной настройки. Bootstrap идемпотентно
|
Пока курсов нет, показан блок первичной настройки. Bootstrap идемпотентно
|
||||||
@@ -193,6 +210,9 @@
|
|||||||
материалы; видны только опубликованные оценки и комментарии.
|
материалы; видны только опубликованные оценки и комментарии.
|
||||||
- [ ] **Материалы курса:** админ и преподаватель видят скрытые статьи, студент —
|
- [ ] **Материалы курса:** админ и преподаватель видят скрытые статьи, студент —
|
||||||
только опубликованные; чужой курс даёт `403`, аноним уходит на `/login`.
|
только опубликованные; чужой курс даёт `403`, аноним уходит на `/login`.
|
||||||
|
- [ ] **Активы:** картинки курсовой статьи открываются у студента своего курса
|
||||||
|
и преподавателя; чужой курс — `403`, скрытая картинка у студента — `404`,
|
||||||
|
публичная `ALL` — без входа; старый файл без привязки — публичен.
|
||||||
- [ ] **Копирование лабораторной:** админ копирует лабораторную в другой курс;
|
- [ ] **Копирование лабораторной:** админ копирует лабораторную в другой курс;
|
||||||
создаётся черновик с уровнями и вариантами; оценки, сдачи, защиты и
|
создаётся черновик с уровнями и вариантами; оценки, сдачи, защиты и
|
||||||
назначения не копируются; занятый номер даёт `409`.
|
назначения не копируются; занятый номер даёт `409`.
|
||||||
@@ -206,13 +226,13 @@
|
|||||||
|
|
||||||
## 4. Известные пробелы и follow-up
|
## 4. Известные пробелы и follow-up
|
||||||
|
|
||||||
1. **Активы всё ещё публичны.** Прокси `/api/v1/content/assets/{name}` (и
|
1. **Legacy-активы без привязки.** Видимость связанных активов реализована:
|
||||||
web-прокси) не проверяет курс/статью: `article_assets` не связаны с
|
`V12` восстанавливает `article_assets` по markdown-ссылкам, а маршрут
|
||||||
видимостью материала. Курсовые картинки может получить любой, кто знает
|
`/api/v1/content/assets/{name}` проверяет курс/аудиторию и публикацию.
|
||||||
имя файла. Нужно использовать связь `article_assets → article → section →
|
Остаточный случай — файлы, на которые нет ссылок ни в одной статье: они
|
||||||
chapter` для проверки видимости и сделать visibility-aware маршруты,
|
остаются публичными. Нужна инвентаризация и привязка либо удаление таких
|
||||||
отдельный публичный путь для `ALL`. Осознанно
|
файлов; до этого fallback сохраняется, чтобы не ломать старые ссылки.
|
||||||
вынесено из этапа 5, зафиксировано в [15](15-stage5-course-materials.md).
|
Детали — в [15](15-stage5-course-materials.md).
|
||||||
2. **Права преподавателя в основном read-only.** Кабинет преподавателя даёт
|
2. **Права преподавателя в основном read-only.** Кабинет преподавателя даёт
|
||||||
обзор курса, групп, лабораторных и профилей. Авторство лабораторных и
|
обзор курса, групп, лабораторных и профилей. Авторство лабораторных и
|
||||||
материалов, распределение вариантов, выставление оценок, комментарии,
|
материалов, распределение вариантов, выставление оценок, комментарии,
|
||||||
@@ -249,9 +269,9 @@
|
|||||||
authoring лабораторных/материалов, распределение вариантов, выставление
|
authoring лабораторных/материалов, распределение вариантов, выставление
|
||||||
оценок и комментариев, коды и действия защиты; закрыть negative-тестами
|
оценок и комментариев, коды и действия защиты; закрыть negative-тестами
|
||||||
«свой/чужой курс». Это закрывает основной разрыв с ТЗ.
|
«свой/чужой курс». Это закрывает основной разрыв с ТЗ.
|
||||||
2. **Видимость активов.** Использовать связь `article_assets → article →
|
2. **Инвентаризация legacy-активов.** Найти файлы без строк в `article_assets`,
|
||||||
section → chapter`, проверять курс и публикацию при выдаче, оставить
|
привязать их к статьям или удалить; после этого можно убрать публичный
|
||||||
отдельный публичный маршрут только для `ALL`.
|
fallback для несвязанных файлов.
|
||||||
3. **UI авторства контента.** Выбор курса и аудитории при создании глав/
|
3. **UI авторства контента.** Выбор курса и аудитории при создании глав/
|
||||||
статей, чтобы преподаватель и админ не работали только через импорт;
|
статей, чтобы преподаватель и админ не работали только через импорт;
|
||||||
сейчас глобальные главы создаются с `ALL`, курсовые — без аудитории.
|
сейчас глобальные главы создаются с `ALL`, курсовые — без аудитории.
|
||||||
@@ -263,9 +283,9 @@
|
|||||||
|
|
||||||
## 6. Итог
|
## 6. Итог
|
||||||
|
|
||||||
Этапы 0–5 реализованы и проверены. Security-матрица, upgrade-тест миграций и
|
Этапы 0–5 реализованы и проверены. Security-матрица, upgrade-тест миграций,
|
||||||
deploy-runbook этапа 6 зелёные; отчёт и чек-лист передачи готовы. Этап 6
|
видимость активов и deploy-runbook этапа 6 зелёные; отчёт и чек-лист передачи
|
||||||
**нельзя считать полностью завершённым** до закрытия прав преподавателя,
|
готовы. Этап 6 **нельзя считать полностью завершённым** до закрытия прав
|
||||||
видимости активов и CI. До этого релиз допустим только с пониманием, что
|
преподавателя и CI. Видимость связанных активов реализована; публичными
|
||||||
преподаватели работают в режиме просмотра, а активы курсовых материалов
|
остаются только старые файлы без привязки к статье (см. раздел 4), а
|
||||||
остаются публичными.
|
преподаватели пока работают в режиме просмотра.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Preview Mermaid Support) и в IntelliJ IDEA (плагин Mermaid).
|
|||||||
| [12-stage2-report.md](12-stage2-report.md) | Отчёт этапа 2: курсы, bootstrap курса C, привязка и копирование лабораторных, миграция V10 |
|
| [12-stage2-report.md](12-stage2-report.md) | Отчёт этапа 2: курсы, bootstrap курса C, привязка и копирование лабораторных, миграция V10 |
|
||||||
| [13-stage3-report.md](13-stage3-report.md) | Отчёт этапа 3: преподаватели, доступ к курсам, внутренние API, кабинет и профиль студента курса |
|
| [13-stage3-report.md](13-stage3-report.md) | Отчёт этапа 3: преподаватели, доступ к курсам, внутренние API, кабинет и профиль студента курса |
|
||||||
| [14-stage4-report.md](14-stage4-report.md) | Отчёт этапа 4: повышение, академотпуск, отчисление, архив, восстановление и удаление |
|
| [14-stage4-report.md](14-stage4-report.md) | Отчёт этапа 4: повышение, академотпуск, отчисление, архив, восстановление и удаление |
|
||||||
| [15-stage5-course-materials.md](15-stage5-course-materials.md) | Отчёт этапа 5: курсовые материалы, публичные `ALL`-главы, оставшийся разрыв по активам |
|
| [15-stage5-course-materials.md](15-stage5-course-materials.md) | Отчёт этапа 5: курсовые материалы, публичные `ALL`-главы, видимость активов и legacy-файлы без привязки |
|
||||||
| [16-stage6-report.md](16-stage6-report.md) | Отчёт этапа 6: проверка, upgrade-тест миграций, security-матрица, чек-лист деплоя и оставшиеся пробелы |
|
| [16-stage6-report.md](16-stage6-report.md) | Отчёт этапа 6: проверка, upgrade-тест миграций, security-матрица, чек-лист деплоя и оставшиеся пробелы |
|
||||||
| [ADR 0001](adr/0001-groups-courses-foundation.md) | Ключевые архитектурные решения: курс C, миграции, доступ, статусы, повышение |
|
| [ADR 0001](adr/0001-groups-courses-foundation.md) | Ключевые архитектурные решения: курс C, миграции, доступ, статусы, повышение |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user