у записи появился владелец: чужую больше не отдают
- колонка `owner` связью с `users` в обеих коллекциях новым шагом схемы `202608140001`; чтение задачи сужено владельцем, и чужая, ничья и несуществующая дают один ответ; правило просмотра файлов сужено им же - приём по HTTP берёт владельца из сессии, а предъявителя без учётной записи пользователя отвергает до чтения тела: позже пришлось бы убирать уложенный файл, а уборки файлов сервис не умеет. Выборка воркера владельцем не сужается - удаление учётной записи с записями отвергается стражем, и вешает его сама сборка хранилища: сборка, забывшая его позвать, теряла защиту молча
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
## Why
|
||||
|
||||
Сегодня знание идентификатора задачи и есть право её читать: всякий вошедший
|
||||
видит любую расшифровку по её идентификатору — свою, соседа, чью угодно. Паспорт
|
||||
обещает приглашённому пользователю обратное: «каждый видит только свои записи».
|
||||
|
||||
Разграничение стоит первым в очереди не само по себе. На владельце записи стоят
|
||||
список записей, дедупликация, удаление, учёт расхода и квота — всё, что проект
|
||||
собирается делать дальше. Заводить их поверх общей кучи записей значит переделать
|
||||
каждое из них потом.
|
||||
|
||||
## What Changes
|
||||
|
||||
- У записи появляется **владелец** — учётная запись, от имени которой её
|
||||
завели. Назначается он один раз, при приёме, и больше не меняется.
|
||||
- **Опрос готовности отдаёт только свои записи.** Чужая запись по её
|
||||
идентификатору отвечает «не найдено» — тем же ответом, что и несуществующая.
|
||||
Отдельного «доступ запрещён» нет намеренно: по нему перебирается список
|
||||
заведённых задач.
|
||||
- **Приём по HTTP заводит владельца** из предъявленной сессии. Приём без сессии
|
||||
отказывал и раньше, и это не меняется.
|
||||
- **Конвейер владельцем не сужается**: расшифровка идёт для всех записей подряд,
|
||||
как и прежде. Владелец решает, кому запись показывать, а не кому её считать.
|
||||
- **BREAKING** для содержимого базы: колонка владельца добавляется шагом схемы, и
|
||||
записи, заведённые до него, владельца не получают. Данные не переносим —
|
||||
проект заводится с чистого листа, так решено рамками задачи.
|
||||
|
||||
Записи, пришедшие ботом, владельца здесь не получают: связи чата Telegram с
|
||||
учётной записью приложения ещё нет, её заводит следующая задача. Что это значит
|
||||
для обязательности колонки — развилка, вынесенная в design.md и на чекпоинт.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
Новых нет: разграничение доступа — это поведение уже заведённой `access`, а не
|
||||
новый домен.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `access`: появляется владелец записи и правило «чужая запись неотличима от
|
||||
несуществующей». Сегодня спека прямо говорит обратное — «разграничения записей
|
||||
по владельцу здесь нет».
|
||||
- `intake`: приём назначает владельца принятой записи, опрос готовности сужается
|
||||
владельцем. Обе строки спеки, обещающие обратное, снимаются.
|
||||
- `pipeline`: записывается то, что до сих пор было умолчанием, — выборка задачи
|
||||
воркером владельцем не сужается.
|
||||
- `storage`: у задачи в схеме появляется колонка владельца, и заводится она без
|
||||
умолчания.
|
||||
|
||||
## Impact
|
||||
|
||||
- схема хранилища: новый шаг с колонкой владельца в таблице задач; `docs/database.md`;
|
||||
- `internal/entity`, `internal/contract`: у задачи появляется владелец, чтение
|
||||
сужается им;
|
||||
- `internal/adapter/repo/pocketbase`: четыре места правки колонок очереди —
|
||||
`applyToRecord`, `recordToJob`, `acquireColumns`, `acquiredRow`;
|
||||
- `internal/service`: оба метода заведения задачи;
|
||||
- `internal/controller/http`: опрос готовности и приём;
|
||||
- публичный контракт `GET /api/status/{id}`: чужая запись начинает отвечать
|
||||
`404` там, где прежде отдавала содержимое. Форма ответа не меняется.
|
||||
Reference in New Issue
Block a user