- пришедшего называет заголовок Remote-User от прокси, и верят ему только с адреса из перечня trusted_proxies; своего входа у сервиса не осталось — ни корня /auth, ни кук, ни срока сессии, ни секрета клиента в конфиге и в базе - учётная запись заводится первым обращением: EnsureUser в пакете хранилища, шаг схемы 202608220001 с колонкой provider_login и снятыми правилами users - cmd/oidcstub заменён на cmd/devtools с подкомандой proxy; заодно закрыт унаследованный DL3066 — пользователь образа назван числом
10 KiB
MODIFIED Requirements
Requirement: Файл отдаётся ссылкой
Сервис SHALL отдавать файл записи ссылкой, которую строит хранилище по самой записи, и только узнанному отправителю. Поле файла MUST быть помечено защищённым: без этого ссылка открывает запись любому, кто её знает, и знание ссылки становится правом. Отданный файл MUST совпадать с принятым по длине.
Одной пометки мало: защищённый файл судится коротким токеном файла, который узнанный отправитель берёт у хранилища, — и правилом просмотра коллекции. Правило MUST пускать только владельца файла: незаданное означает «только владелец панели», и тогда файла не получит и узнанный, а прежнее «всякий узнанный» отдавало чужое аудио тому, кто знает идентификатор записи.
Токен файла хранилище выдаёт на предъявителя, а не на файл, и о файле при выдаче не спрашивает. Значит владельца судит переход по ссылке, а не выдача токена: отказ наступает там, и требовать его от выдачи значит требовать механизма, которого нет.
Отсюда порядок для потребителя: узнавание → токен файла → ссылка с этим токеном. Адрес выдачи токена лежит в пространстве хранилища, и узнавание по заголовку MUST на нём работать — иначе файл записи недостижим для браузера вовсе. Одного заголовка при этом мало: без токена ссылка файла не отдаёт, и это свойство хранилища, а не недосмотр.
Конвейер расшифровки этим не затронут: он читает файл из файловой системы хранилища, а не по ссылке.
Ссылка на несуществующую запись MUST отвечать отказом, а не пустым файлом.
Ссылка сама по себе и есть право пройти по ней, и потому она MUST не попадать ни в журнал, ни в метку метрики, ни в ответ отправителю. Имя, под которым файл лёг в хранилище, из журнала выводимо быть не должно: журнал уезжает в собранные логи, откуда строку не убрать, и оттуда ссылка на чужую запись работала бы бессрочно.
Защищённое поле сужает это право, но не отменяет запрета: право пройти теперь требует ещё и узнавания, а строка журнала со ссылкой по-прежнему собирала бы половину ключа.
Отсюда требование к отказам: сообщение об отказе хранилища MUST не выходить за пределы хранилища дословно. Отказ чтения и отказ укладки называют ключ файла целиком, а отказ выгрузки во внешнее хранилище — полный адрес объекта; и то и другое кончается в журнале и собирает ссылку не хуже успешного пути.
Что именно журнал приёма пишет ради прослеживаемости, нормирует capability
intake.
Scenario: Файл забирают по ссылке
- GIVEN запись принята и её файл лежит в хранилище
- AND забирающий узнан и взял токен файла
- WHEN ссылку на файл запрашивают с этим токеном
- THEN приходит тот же файл, и его длина совпадает с длиной принятого
Scenario: Неузнанному файл не отдаётся
- GIVEN запись принята и её файл лежит в хранилище
- WHEN ссылку на файл запрашивают неузнанным
- THEN приходит отказ, а содержимого записи в ответе нет
Scenario: Токен файла выдаётся узнанному по заголовку
- GIVEN запрос идёт с доверенного адреса с заголовком
Remote-User - WHEN он просит у хранилища токен файла
- THEN токен выдаётся
Scenario: Конвейер читает файл без узнавания
- GIVEN запись принята и ждёт расшифровки
- WHEN шаг конвейера берётся за неё
- THEN файл читается из файловой системы хранилища и шаг проходит
Scenario: Ссылка ведёт в никуда
- WHEN запрашивают ссылку на запись, которой нет
- THEN приходит отказ, а не пустой ответ
Scenario: По журналу ссылку не собрать
- GIVEN запись принята и прошла конвейер
- WHEN читают журнал сервиса целиком
- THEN имени, под которым файл лёг в хранилище, в нём нет
Scenario: Отказ чтения файла не называет его ключ
- GIVEN файл записи не читается из хранилища
- WHEN шаг конвейера берётся за эту запись и отказывает
- THEN отказ называет запись её идентификатором и не несёт имени файла
Requirement: Файл записи сужается владельцем наравне с задачей
Хранилище SHALL держать владельца и у файла записи — той же связью с учётной записью, — и правило просмотра файлов MUST пускать к файлу только его владельца.
Владелец файла MUST назначаться при приёме, из узнанного предъявителя, а колонка файла MUST не допускать пустого значения наравне с колонкой записи. Прежде пустое значение оставалось у файлов, заведённых конвейером для записи без владельца; таких записей больше не заводится, и разное правило у записи и у её файла читалось бы как недосмотр.
Файл, заведённый шагом конвейера, — приведённую копию заводит именно он — MUST получать владельца своей записи. Иного источника владельца у файла нет, и шаг, оставивший его пустым, упрётся в отказ сохранения: запись накопит отказы и остановится признаком на первом же приведении.
Ссылки на файлы у записи две — на принятую копию и на приведённую, — и обе живут до конца, но владелец файла MUST по-прежнему лежать своей колонкой, а не выводиться через запись: файл переживает свою запись, и заведённый шагом до сохранения записи он остаётся с владельцем и без ссылки.
Отказ наступает на переходе по ссылке, а не на выдаче токена файла: токен хранилище выдаёт на предъявителя, а не на файл, и о файле при выдаче не спрашивает вовсе. Требовать отказа при выдаче значит требовать механизма, которого нет, — а проверка, написанная под такое требование, зеленела бы, не касаясь пути, по которому аудио и уходит.
Scenario: Чужой файл не отдаётся
- GIVEN запись принята одним узнанным
- WHEN другой узнанный идёт по ссылке на файл этой записи со своим токеном
- THEN содержимого он не получает
Scenario: Свой файл отдаётся
- GIVEN человек принял запись
- WHEN он идёт по ссылке на файл своей записи со своим токеном
- THEN содержимое отдаётся
Scenario: Файл без владельца не сохраняется
- GIVEN сервис поднят
- WHEN файл записи пытаются сохранить с пустым владельцем
- THEN хранилище его не сохраняет
Scenario: Приведённая копия получает владельца записи
- GIVEN запись с владельцем дошла до приведения
- WHEN шаг заводит приведённую копию файла
- THEN владельцем копии стоит владелец записи
- AND шаг завершается без отказа