- шаг схемы закрывает поверхность, которую хранилище приносит открытой: собственную регистрацию, вход по паролю и одноразовый код — без этого закрытие приёма обходилось двумя запросами - продление сессии выключено, срок семь суток: иначе отзыв доступа у провайдера до сервиса не доходит никогда - файл записи отдаётся вошедшему по токену файла — пересмотр ADR-2026-08-12-file-link-open-but-not-logged
5.4 KiB
MODIFIED Requirements
Requirement: Файл отдаётся ссылкой
Сервис SHALL отдавать файл записи ссылкой, которую строит хранилище по самой записи, и только узнанному отправителю. Поле файла MUST быть помечено защищённым: без этого ссылка открывает запись любому, кто её знает, и знание ссылки становится правом. Отданный файл MUST совпадать с принятым по длине.
Одной пометки мало: защищённый файл судится коротким токеном файла, который узнанный отправитель берёт у хранилища, предъявив сессию, — и правилом просмотра коллекции. Правило MUST пускать всякого узнанного: незаданное означает «только владелец панели», и тогда файла не получит и вошедший. Сужения по владельцу здесь нет — его заводит отдельная задача.
Отсюда порядок для потребителя: сессия → токен файла → ссылка с этим токеном. Браузер с одной лишь кукой файла не получит, и это свойство хранилища, а не недосмотр.
Ссылка на несуществующую запись MUST отвечать отказом, а не пустым файлом.
Ссылка сама по себе и есть право пройти по ней, и потому она MUST не попадать ни в журнал, ни в метку метрики, ни в ответ отправителю. Имя, под которым файл лёг в хранилище, из журнала выводимо быть не должно: журнал уезжает в собранные логи, откуда строку не убрать, и оттуда ссылка на чужую запись работала бы бессрочно.
Защищённое поле сужает это право, но не отменяет запрета: право пройти теперь требует ещё и сессии, а строка журнала со ссылкой по-прежнему собирала бы половину ключа.
Отсюда требование к отказам: сообщение об отказе хранилища MUST не выходить за пределы хранилища дословно. Отказ чтения и отказ укладки называют ключ файла целиком, а отказ выгрузки во внешнее хранилище — полный адрес объекта; и то и другое кончается в журнале и собирает ссылку не хуже успешного пути.
Конвейер расшифровки этим не затронут: он читает файл из файловой системы хранилища, а не по ссылке.
Что именно журнал приёма пишет ради прослеживаемости, нормирует capability
intake.
Scenario: Файл забирают по ссылке
- GIVEN запись принята и её файл лежит в хранилище
- AND забирающий предъявил сессию и взял по ней токен файла
- WHEN ссылку на файл запрашивают с этим токеном
- THEN приходит тот же файл, и его длина совпадает с длиной принятого
Scenario: Без сессии файл не отдаётся
- GIVEN запись принята и её файл лежит в хранилище
- WHEN ссылку на файл запрашивают без сессии
- THEN приходит отказ, а содержимого записи в ответе нет
Scenario: Ссылка ведёт в никуда
- WHEN запрашивают ссылку на запись, которой нет
- THEN приходит отказ, а не пустой ответ
Scenario: По журналу ссылку не собрать
- GIVEN запись принята и прошла конвейер
- WHEN читают журнал сервиса целиком
- THEN имени, под которым файл лёг в хранилище, в нём нет
Scenario: Отказ чтения файла не называет его ключ
- GIVEN файл записи не читается из хранилища
- WHEN шаг конвейера берётся за эту запись и отказывает
- THEN отказ называет запись её идентификатором и не несёт имени файла
Scenario: Конвейер читает файл без сессии
- GIVEN запись принята и ждёт расшифровки
- WHEN шаг конвейера берётся за неё
- THEN файл читается из файловой системы хранилища и шаг проходит