удалён вход Telegram, владелец записи стал обязателен в схеме

- убраны клиент бота, транспорт обновлений, отправитель сообщений, сборка
  входа при старте, секция настроек и зависимость go-telegram-bot-api; из
  конвейера ушла доставка ответа отправителю — исход виден опросом готовности.
  Колонки адресата и значение источника остались в схеме: применённые шаги не
  переписываются
- шаг 202608140003 запрещает пустого владельца у аудиозаписи и у файла;
  существующие строки он не проверяет, и это принято сознательно — искать их
  надо запросом до выкладки
- ревью нашло два пред-существующих дефекта, оба закрыты: пустой второй ответ
  распознавателя стирал сохранённую расшифровку, а пустая расшифровка перестала
  быть заметной вместе с убранной доставкой. Попутно поднят golang.org/x/image
  до v0.45.0 — красный шаг vulns, воспроизводился и на чистом master
This commit is contained in:
av
2026-08-15 07:24:35 +03:00
parent 97ceb7bb69
commit 8f7c3a057a
74 changed files with 2453 additions and 2733 deletions
+23 -30
View File
@@ -10,12 +10,10 @@ OIDC, чем предъявляется сессия, что её прекращ
только на вопрос «узнан ли пришедший», но и на «чьё он смотрит». Запись из веба
принадлежит тому, кто её принёс, и чужая неотличима от несуществующей.
Записи, принятые ботом, владельца не имеют вовсе и по API не достаются никому:
связи чата Telegram с учётной записью приложения сервис не ведёт, её заводит
отдельная задача.
Вход из Telegram эта capability не нормирует: бот проверяет отправителя своим
белым списком, и с учётной записью приложения тот список не связан.
Записи без владельца у сервиса не бывает: колонка владельца пустого значения не
принимает, и норму эту держит capability `storage`. Прежде такие записи заводил
вход Telegram — связи чата с учётной записью сервис не вёл, — и 2026-08-14 вход
убран вместе с этим исключением.
## Requirements
### Requirement: Вход через внешнего провайдера
@@ -350,13 +348,14 @@ MUST не делать. Кто допущен, определяет правил
### Requirement: У записи есть владелец, и чужую ей не отдают
Сервис SHALL заводить у каждой записи, принятой **по HTTP**, — владельца, то
есть учётную запись, от имени которой запись принята, — и MUST отдавать данные
такой записи только её владельцу. Владелец назначается один раз, при приёме, и
MUST не меняться у записи, у которой владелец есть: совместного доступа, ролей и
передачи записи другому сервис не знает. Оговорка не случайна — назначить
владельца записи, у которой его нет, вправе задача, заводящая связь чата
Telegram с учётной записью.
Сервис SHALL заводить у каждой принятой записи владельца — учётную запись, от
имени которой запись принята, — и MUST отдавать данные такой записи только её
владельцу. Запись без владельца MUST не заводиться ничем — ни приёмом, ни конвейером, ни
рукой в панели: колонка владельца пустого значения не принимает, и норму эту
держит capability `storage`.
Владелец назначается один раз, при приёме, и MUST не меняться: совместного
доступа, ролей и передачи записи другому сервис не знает.
Владелец MUST браться из предъявленной сессии и ниоткуда больше. Владелец,
пришедший полем запроса, дал бы всякому вошедшему право завести запись на чужое
@@ -368,16 +367,12 @@ Telegram с учётной записью.
что разграничение прячет. Каким именно ответом это выражено, нормирует
capability `intake`: там живёт адрес опроса, и держатель нормы обязан быть один.
Пустой владелец MUST не совпадать ни с одной записью — ни со своей, ни с чужой,
ни с ничьей. Правило записано со стороны **спрашивающего**, а не со стороны
записи: обязательность владельца, которую держит одна лишь подпись метода, пустую
строку пропускает, и первый же вызывающий без учётной записи получил бы ровно
множество записей без владельца, то есть все записи бота.
Записи, принятые из Telegram, владельца не имеют: связи чата с учётной записью
приложения сервис не ведёт. Такая запись MUST не доставаться по API никому —
ответ на неё тот же, что и на несуществующую, — а её расшифровка уезжает
отправителю в чат, как и прежде.
Пустой владелец MUST не совпадать ни с одной записью. Правило записано со стороны
**спрашивающего** и остаётся в силе, хотя записей без владельца в хранилище
больше нет: спрашивающий с пустым владельцем — это вызов, у которого нет учётной
записи, и отвечать ему надо отказом, а не выборкой. Держится оно отдельно от
схемы намеренно: схема запрещает **заводить** ничью запись, а это правило
запрещает **спрашивать** ничьим именем, и одно другое не заменяет.
#### Scenario: Своя запись доступна
@@ -396,15 +391,13 @@ capability `intake`: там живёт адрес опроса, и держат
- **WHEN** запрос на приём записи несёт своё значение владельца
- **THEN** владельцем принятой записи становится предъявитель сессии
#### Scenario: Запись из Telegram не достаётся по API
#### Scenario: Ничью запись завести нечем
- **GIVEN** запись принята ботом
- **WHEN** её состояние спрашивает вошедший человек
- **THEN** ответ тот же, что и на неизвестный идентификатор
- **WHEN** запись пытаются завести с пустым владельцем
- **THEN** хранилище её не сохраняет
#### Scenario: Пустой владелец не открывает ничего
- **GIVEN** заведены три задачи: своя, чужая и принятая ботом
- **GIVEN** заведены две записи: своя и чужая
- **WHEN** состояние каждой спрашивают с пустым владельцем
- **THEN** ответ на все три тот же, что и на неизвестный идентификатор
- **THEN** ответ на обе тот же, что и на неизвестный идентификатор