- разведка storage-without-pocketbase: шесть ролей библиотеки в этом коде, отпавший довод перевода, шесть модулей достижимы только через неё - ADR-2026-08-22-storage-without-pocketbase заменяет три записи: перевод в PocketBase, очередь коллекцией, файл за защищённым полем - задача storage-without-pocketbase встала первой строкой плана стройки
58 lines
4.8 KiB
Markdown
58 lines
4.8 KiB
Markdown
# Файл записи закрыт защищённым полем и отдаётся вошедшему по токену файла
|
||
|
||
- **Дата:** 2026-08-12
|
||
- **Источник:** [../../openspec/changes/archive/2026-08-12-oidc-login/design.md](../../openspec/changes/archive/2026-08-12-oidc-login/design.md),
|
||
раздел «Что изменило ревью кода», плюс отчёт триажа
|
||
[../../openspec/changes/archive/2026-08-12-oidc-login/review/report.md](../../openspec/changes/archive/2026-08-12-oidc-login/review/report.md),
|
||
пункт 3
|
||
- **Статус:** заменено на [ADR-2026-08-22-storage-without-pocketbase](ADR-2026-08-22-storage-without-pocketbase.md)
|
||
|
||
## Решение
|
||
|
||
Поле файла в хранилище **помечается защищённым**, а правило просмотра коллекции
|
||
файлов пускает всякого узнанного. Ссылка `/api/files/<коллекция>/<запись>/<имя>`
|
||
перестаёт быть правом пройти по ней: нужен короткий токен файла, который берут,
|
||
предъявив сессию.
|
||
|
||
Запись заменяет [ADR-2026-08-12-file-link-open-but-not-logged](ADR-2026-08-12-file-link-open-but-not-logged.md).
|
||
|
||
## Почему
|
||
|
||
Прежнее решение было обусловленным и само назвало условие своего пересмотра:
|
||
|
||
> Решение действует до разграничения доступа: задачи `oidc-login` и
|
||
> `record-ownership` меняют условие, и тогда пометку стоит пересмотреть новой
|
||
> записью.
|
||
|
||
Условие наступило. Прежний довод — «право прочитать задачу даёт знание её
|
||
идентификатора, и файл встаёт вровень с `GET /api/status/:id`» — держался на том,
|
||
что опрос готовности открыт анонимно. Этот change закрывает опрос за вход, и
|
||
файл, оставшийся открытым, стал бы единственным анонимным путём к содержимому
|
||
записи — самому чувствительному, что есть у проекта.
|
||
|
||
Вторая половина прежнего решения остаётся в силе: имя файла в журнал по-прежнему
|
||
не пишется. Защищённое поле сужает право пройти, но не отменяет запрета —
|
||
строка журнала со ссылкой собирала бы половину ключа.
|
||
|
||
Пометки самой по себе оказалось мало, и это выяснило ревью кода прогоном:
|
||
защищённый файл судится **и** токеном, **и** правилом просмотра коллекции, а
|
||
незаданное правило означает «только владелец панели». Файл не получал ни аноним,
|
||
ни вошедший — сценарий спеки не исполнялся вовсе. Правило назначено тем же шагом
|
||
схемы.
|
||
|
||
## Последствия
|
||
|
||
- `+` содержимое записи перестало быть доступным по одному знанию ссылки; после
|
||
закрытия API это был последний анонимный путь к нему.
|
||
- `+` условие, названное прежней записью, отработало как задумано: решение
|
||
пересмотрено записью, а не молча.
|
||
- `−` ссылка усложнилась для потребителя: браузер с одной кукой файла не
|
||
получает, нужен порядок «сессия → токен файла → ссылка». Будущее приложение
|
||
обязано этот шаг делать, и задача про прослушивание записи начинается с него.
|
||
- `−` разграничения по владельцу нет: токен файла берёт всякий вошедший, и по
|
||
ссылке он получит **любую** запись, а не только свою. Сужение приносит
|
||
`record-ownership`; до неё круг сузился с «кто угодно из интернета» до «кто
|
||
угодно из вошедших», и это меньше, чем кажется.
|
||
- `−` отзыва у выданного токена нет, как не было у ссылки; смягчает только его
|
||
короткий срок.
|