у записи появился владелец: чужую больше не отдают
- колонка `owner` связью с `users` в обеих коллекциях новым шагом схемы `202608140001`; чтение задачи сужено владельцем, и чужая, ничья и несуществующая дают один ответ; правило просмотра файлов сужено им же - приём по HTTP берёт владельца из сессии, а предъявителя без учётной записи пользователя отвергает до чтения тела: позже пришлось бы убирать уложенный файл, а уборки файлов сервис не умеет. Выборка воркера владельцем не сужается - удаление учётной записи с записями отвергается стражем, и вешает его сама сборка хранилища: сборка, забывшая его позвать, теряла защиту молча
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
## Критерии приёмки задачи
|
||||
|
||||
Дословно из `tasks/items/record-ownership.md`. Файл задачи закрытие удалит —
|
||||
критерии обязаны его пережить.
|
||||
|
||||
- Запрос чужой записи по её идентификатору возвращает «не найдено», а не
|
||||
содержимое и не «доступ запрещён». Оракул — тест: две сессии, задача первой
|
||||
запрашивается второй, ответ 404 и пустое тело.
|
||||
*Расхождение:* «пустое тело» разошлось с нетронутым сценарием спеки, где ответ
|
||||
по неизвестному идентификатору несёт сообщение о ненайденной задаче. Читается
|
||||
как «то же тело, что и у неизвестного идентификатора, и без состояния и текста
|
||||
расшифровки» — иначе реализация по букве критерия молча снимет действующее
|
||||
требование о сообщении.
|
||||
- Запись, заведённая из веба, принадлежит вошедшему. Оракул — тест приёма по
|
||||
HTTP со сверкой колонки владельца.
|
||||
- Выборка воркера владельцем **не** сужается: конвейер обрабатывает записи всех.
|
||||
Оракул — тест: задачи двух владельцев проходят конвейер одним воркером.
|
||||
- База заводится с чистого листа, колонка владельца обязательна и без умолчания.
|
||||
Оракул — прогон миграций на пустой базе и попытка вставки без владельца.
|
||||
|
||||
**Четвёртый критерий разошёлся с рамками той же задачи** и правится решением
|
||||
человека на чекпоинте: рамки запрещают назначать владельца записям из Telegram, а
|
||||
обязательная колонка такую запись вставить не даст. Разобрано в `design.md`,
|
||||
«Open Questions»; ниже план написан по рекомендованному способу — колонка без
|
||||
умолчания, допускающая пустое значение, обязательность приёма по HTTP держит код.
|
||||
|
||||
## Рубрика ревью дизайна — приёмочные критерии
|
||||
|
||||
Порождена проходом `rubric` до чтения артефактов. Приёмка судится по одному
|
||||
списку: этот блок и блок выше.
|
||||
|
||||
1. Чужая запись неотличима от несуществующей на всех наблюдаемых осях: тот же
|
||||
код, то же тело, та же форма ответа.
|
||||
2. Сужение живёт в одном месте пути чтения и обязательно к употреблению: второго
|
||||
читающего входа, у которого сужение можно не позвать, нет.
|
||||
3. Пустой владелец на входе чтения не совпадает ни с одной записью — своей,
|
||||
чужой и ничьей.
|
||||
4. Записи без владельца недостижимы сужённым путём никому, и недостижимость
|
||||
выведена из правила, а не из того, что таких записей мало.
|
||||
5. Владелец назначается сервером из сессии: поле владельца, пришедшее запросом,
|
||||
на результат не влияет.
|
||||
6. Владелец появляется в той же операции, что и запись; отказ по отсутствию
|
||||
владельца не оставляет ни задачи, ни файла, и способ этого назван.
|
||||
7. Неизменность владельца держит механизм, а не обещание: назван каждый путь,
|
||||
которым владельца можно переписать, и что его удерживает.
|
||||
8. Колонка прочитана всеми четырьмя местами правки колонок очереди плюс шагом
|
||||
схемы.
|
||||
9. Воркер владельцем не сужается, и это записано нормой, а не оставлено
|
||||
умолчанию.
|
||||
10. Судьба записи при исчезновении владельца определена.
|
||||
11. Шаг схемы: одно представление «владельца нет», откат не требует переписывания
|
||||
применённого шага.
|
||||
12. Проверка владельца и чтение состояния берутся из одного чтения записи, а не
|
||||
двумя раздельными.
|
||||
13. Ответ отправителю адресуется по источнику записи, а не по владельцу: запись
|
||||
без владельца получает свой ответ в чат.
|
||||
14. Ни ответ, ни журнал не выдают того, что прячет разграничение; различение
|
||||
чужой и несуществующей в журнале допустимо.
|
||||
|
||||
## 1. Схема хранилища
|
||||
|
||||
- [x] 1.1 Новый шаг схемы `202608140001_record_owner.go`: колонка `owner` в
|
||||
таблице задач связью с коллекцией `users`, без умолчания, пустое значение
|
||||
допустимо, каскадное удаление выключено. Имя колонки — `owner`, поле
|
||||
сущности — `OwnerID`; имена названы здесь, потому что разойтись им есть где
|
||||
— семь мест плюс подпись метода
|
||||
- [x] 1.2 Тем же шагом — колонка `owner` в таблице файлов, теми же свойствами;
|
||||
правило просмотра коллекции файлов сужается владельцем вместо прежнего
|
||||
«всякий вошедший»
|
||||
- [x] 1.3 Тем же шагом — запрет удаления учётной записи, у которой остались
|
||||
задачи: отказ с причиной, а не снятие ссылки
|
||||
- [x] 1.4 Прежние шаги схемы не тронуты — проверяется шагом гейта `migrations`
|
||||
- [x] 1.5 `docs/database.md`: строки колонок в обеих таблицах, правило выборки по
|
||||
владельцу, новое правило просмотра файлов и запрет удаления учётной записи
|
||||
|
||||
## 2. Сущность и контракт
|
||||
|
||||
- [x] 2.1 `internal/entity`: у задачи расшифровки появляется владелец
|
||||
- [x] 2.2 `internal/contract`: читающий метод репозитория задач принимает
|
||||
владельца; второго читающего метода не заводится
|
||||
|
||||
## 3. Хранилище задач
|
||||
|
||||
- [x] 3.1 `applyToRecord` кладёт владельца при заведении
|
||||
- [x] 3.2 `recordToJob` читает владельца
|
||||
- [x] 3.3 `acquireColumns` и `acquiredRow` читают колонку владельца
|
||||
- [x] 3.4 `applyOwnedByPipeline` владельца **не** трогает
|
||||
- [x] 3.5 Чтение задачи сужено владельцем: задача другого владельца и задача без
|
||||
владельца отдают ту же ошибку, что и несуществующая
|
||||
|
||||
## 4. Приём и опрос
|
||||
|
||||
- [x] 4.1 `internal/service`: метод заведения задачи из веба принимает владельца
|
||||
и отказывает при пустом; метод заведения из Telegram владельца не
|
||||
назначает. Владелец кладётся и на файл, заводимый при приёме
|
||||
- [x] 4.2 `internal/controller/http`: приём берёт владельца из предъявленной
|
||||
сессии
|
||||
- [x] 4.3 `internal/controller/http`: опрос готовности передаёт владельца в
|
||||
хранилище и отвечает `404` с прежним телом на чужую и на ничью задачу
|
||||
|
||||
## 5. Проверки
|
||||
|
||||
- [x] 5.1 Тест: две сессии, задача первой запрашивается второй — `404`, тело без
|
||||
состояния и текста
|
||||
- [x] 5.2 Тест: приём по HTTP заводит задачу с владельцем-предъявителем
|
||||
- [x] 5.3 Тест: приём по HTTP без узнанной учётной записи задачи не заводит
|
||||
- [x] 5.4 Тест: задачи двух владельцев и задача без владельца проходят конвейер
|
||||
одним воркером
|
||||
- [x] 5.5 Тест: шаг конвейера, сохраняющий результат, владельца не затирает
|
||||
- [x] 5.6 Тест: миграции на пустой базе заводят колонку без умолчания
|
||||
- [x] 5.7 Тест: чтение с пустым владельцем не отдаёт ни своей, ни чужой, ни
|
||||
ничьей задачи
|
||||
- [x] 5.8 Тест: сессия без учётной записи пользователя получает `403` на приёме,
|
||||
файла и задачи не заводится — узнан, но не запись коллекции пользователей
|
||||
- [x] 5.9 Тест: вошедший просит токен чужого файла — отказ; своего — успех
|
||||
- [x] 5.10 Тест: удаление учётной записи с задачами отвергается, без задач —
|
||||
проходит
|
||||
- [x] 5.11 `task gate` зелёный
|
||||
|
||||
## 6. Архивация
|
||||
|
||||
- [ ] 6.1 На архивации выправить `## Purpose` спеки `access`: преамбула
|
||||
переживает слияние дельт дословно и сегодня утверждает, что разграничения
|
||||
по владельцу нет. Валидатор преамбулу не судит — вспомнить об этом больше
|
||||
некому
|
||||
- [ ] 6.2 Сверить `docs/security.md`, `docs/passport.md` и **`docs/architecture.md`**
|
||||
(строка «Разграничения записей по владельцу здесь нет»): все три обещают
|
||||
закрытие разграничением именно этой задачей. Адрес в `architecture.md`
|
||||
назван отдельно — его нашло ревью, и без него обзор архитектуры отправлял
|
||||
бы следующего читателя чинить уже закрытое
|
||||
Reference in New Issue
Block a user