вход переехал на доверенный заголовок Authelia вместо собственного OIDC
- пришедшего называет заголовок Remote-User от прокси, и верят ему только с адреса из перечня trusted_proxies; своего входа у сервиса не осталось — ни корня /auth, ни кук, ни срока сессии, ни секрета клиента в конфиге и в базе - учётная запись заводится первым обращением: EnsureUser в пакете хранилища, шаг схемы 202608220001 с колонкой provider_login и снятыми правилами users - cmd/oidcstub заменён на cmd/devtools с подкомандой proxy; заодно закрыт унаследованный DL3066 — пользователь образа назван числом
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
## Критерии приёмки
|
||||
|
||||
Дословно из записи задачи `trusted-header-login`. Файл задачи закрытие удалит —
|
||||
критерии обязаны его пережить.
|
||||
|
||||
1. Обращение к адресу приложения с заголовком от доверенного источника идёт от
|
||||
имени учётной записи, заведённой при первом таком обращении, а повторное с тем
|
||||
же значением попадает в ту же запись. Оракул — тест обработчика: два запроса
|
||||
подряд, в хранилище одна запись пользователя.
|
||||
2. Тот же заголовок с недоверенного адреса даёт `401`, а не вход под названным
|
||||
именем. Оракул — тест: запрос с адресом источника вне перечня доверенных.
|
||||
3. Собственные адреса входа хранилища сессии не выдают и учётную запись не
|
||||
меняют. Оракул — тест по перечню адресов под `/api/collections/users/`: каждый
|
||||
отвечает отказом.
|
||||
4. Механики OIDC в дереве не осталось: корня `/auth`, кук входа,
|
||||
`ApplyProviderSettings`, `cmd/oidcstub` и секрета клиента в конфиге. Оракул —
|
||||
поиск по этим именам плюс зелёный `task gate`.
|
||||
5. Разграничение записей по владельцу работает как прежде: чужая запись
|
||||
неотличима от несуществующей. Оракул — существующие тесты владельца остаются
|
||||
зелёными.
|
||||
|
||||
## Рубрика ревью дизайна
|
||||
|
||||
Двенадцать свойств, по которым судится узел этого рода — слой узнавания
|
||||
предъявителя плюс заведение учётной записи первым обращением. Порождены до
|
||||
чтения дизайна. Приёмка идёт по одному списку: эти пункты наравне с критериями
|
||||
постановки выше.
|
||||
|
||||
1. Доверие ограничено источником, и источник берётся у соединения, а не у
|
||||
пересылаемого значения; настройка проверяется на старте, небезопасного
|
||||
умолчания нет.
|
||||
2. Заголовок узнаётся однозначно: более одного значения не даёт «первое
|
||||
попавшееся».
|
||||
3. Вырожденное значение — пустое, пробельное, сверх предела длины, с
|
||||
управляющими знаками — не узнаёт никого и не заводит ничего.
|
||||
4. Ключ учётной записи стабилен, и цена его нестабильности названа в обе стороны
|
||||
— и смена, и переиспользование. Выборка по ключу имеет свой индекс.
|
||||
5. Заведение первым обращением идемпотентно и устойчиво к конкуренции; отказ
|
||||
уникальности не по ключевой колонке имеет назначенный исход.
|
||||
6. Результат остаётся функцией уже произошедшего: ключевая колонка неизменяема
|
||||
после заведения.
|
||||
7. Второго способа стать этим человеком нет, а при двух предъявленных удостоверениях
|
||||
победитель назначен нормой, а не порядком слоёв.
|
||||
8. Закрытие поверхности не отменяет законного пути заведения записи самим
|
||||
сервисом.
|
||||
9. Значение заголовка — недоверенный вход на всём пути: не в журнал, не в ответ,
|
||||
не в метку метрики, и в хранилище параметром, а не подстановкой в фильтр.
|
||||
10. Охват слоя назван, перечень открытых адресов закрыт, посторонний заголовок
|
||||
ответа открытых адресов не меняет.
|
||||
11. Срок узнавания назван: всё, что переживает запрос, либо отсутствует, либо
|
||||
имеет назначенный срок, и этот срок и есть задержка отзыва.
|
||||
12. Исход узнавания наблюдаем владельцем и неразличим для отправителя.
|
||||
|
||||
## 1. Схема хранилища
|
||||
|
||||
- [x] 1.1 Новый шаг схемы `202608220001_trusted_header_login.go`: колонка
|
||||
`provider_login` в коллекции `users` с уникальным индексом, почта
|
||||
переведена в необязательную, `OAuth2.Enabled = false` и
|
||||
`OAuth2.Providers = nil`. Применённый `202608120001_oidc_login` не
|
||||
трогается.
|
||||
- [x] 1.2 Тот же шаг снимает **все** правила доступа коллекции `users` в пустое —
|
||||
`ListRule`, `ViewRule`, `CreateRule`, `UpdateRule`, `DeleteRule`. Сегодня
|
||||
четыре из них библиотечные (`id = @request.auth.id`), и правка своей записи
|
||||
открыта: это путь захвата чужого имени через ключевую колонку.
|
||||
- [x] 1.3 Шаг зарегистрирован в `migrations.go`, имя ключевой колонки объявлено
|
||||
константой рядом с именами коллекций.
|
||||
- [x] 1.4 `schema_test.go` сторожит `users` наравне с шестью коллекциями,
|
||||
которые он уже проверяет: все пять правил пусты.
|
||||
- [x] 1.5 Откат шага возвращает то, что было до него, кроме открытого создания
|
||||
записи и открытой правки, и не падает на проверке коллекции.
|
||||
- [x] 1.6 `docs/database.md` описывает новую колонку, снятые настройки и снятые
|
||||
правила доступа.
|
||||
|
||||
## 2. Настройка
|
||||
|
||||
- [x] 2.1 Секция входа конфига переписана: адреса провайдера, идентификатор и
|
||||
секрет клиента, адрес возврата и признак защищённой куки убраны, перечень
|
||||
доверенных адресов заведён: секция `[auth]` остаётся под своим именем, в
|
||||
ней один ключ `trusted_proxies` (решение человека 2026-08-22).
|
||||
- [x] 2.2 Проверка настройки на старте: пустой перечень и нечитаемая строка
|
||||
роняют старт с именем ключа.
|
||||
- [x] 2.3 `config.example.toml` переписан вместе с секцией, раздел про локальный
|
||||
вход заменён на подставной прокси.
|
||||
|
||||
## 3. Узнавание пришедшего
|
||||
|
||||
- [x] 3.1 Слой узнавания в транспорте HTTP: читает заголовок, судит адрес пира по
|
||||
перечню, ставит учётную запись предъявителя только когда её ещё нет.
|
||||
Вешается корневым, но действует на объявленной области — корень приложения
|
||||
плюс адрес выдачи файлового токена; область выводится из перечня адресного
|
||||
пространства, а не пишется вторым списком.
|
||||
- [x] 3.2 Приём значения заголовка: пустое и пробельное не узнают никого, более
|
||||
одного значения не узнаёт никого, предел длины и отказ на управляющие
|
||||
знаки, сравнение точное. Значение уходит в хранилище параметром, а не
|
||||
подстановкой в текст фильтра.
|
||||
- [x] 3.3 Поиск и заведение учётной записи — **методом пакета хранилища**, а не
|
||||
куском в транспорте: у правила один дом, и `api-tokens` возьмёт его же.
|
||||
Имя и почта берутся только при заведении, найденная запись не
|
||||
переписывается; отказ уникальности по ключевой колонке ведёт к повторному
|
||||
поиску, по любой другой — к заведению записи без почты.
|
||||
- [x] 3.4 Отказ хранилища при узнавании кончается отказом сервиса, а не
|
||||
молчаливым проходом неузнанным.
|
||||
- [x] 3.5 Журнал: исходы «заголовок с недоверенного адреса» и «учётная запись
|
||||
заведена» — с адресом пира и идентификатором записи, без значения
|
||||
заголовка; перечень доверенных адресов называется строкой при подъёме.
|
||||
- [x] 3.6 `ApplyProviderSettings`, `ProviderSettings` и `ProviderName` убраны из
|
||||
пакета хранилища; `SessionDuration` убран вместе со сроком сессии.
|
||||
|
||||
## 4. Снос механики OIDC
|
||||
|
||||
- [x] 4.1 `internal/controller/http/auth.go` удалён целиком вместе с корнем
|
||||
`/auth` в перечне адресного пространства.
|
||||
- [x] 4.2 `SessionFromCookie` и `BlockSessionRefresh` удалены; места их привязки
|
||||
переписаны на новый слой.
|
||||
- [x] 4.3 `cmd/oidcstub` удалён.
|
||||
- [x] 4.4 Приложение: адрес, которым экран уводил ко входу, убран; неузнавание
|
||||
показывается строкой и отличается от прочей неудачи. Юнит-тесты приложения
|
||||
обновлены.
|
||||
|
||||
## 5. Способ представиться без прокси
|
||||
|
||||
- [x] 5.1 Заведён `cmd/devtools` с подкомандой `proxy`: слушает свой порт,
|
||||
ставит заголовок, переправляет запрос сервису. Подкоманду `admin` заводит
|
||||
задача `dev-run-task`. В образ пакет не едет: строка сборки `Dockerfile`
|
||||
называет точку входа поимённо.
|
||||
- [x] 5.2 `CLAUDE.md`, раздел «Команды» и раздел «Запреты» — про локальный вход
|
||||
без прокси; `README.md`, если он про это говорит.
|
||||
|
||||
## 6. Проверки
|
||||
|
||||
- [x] 6.1 Тест: первое обращение с доверенного адреса заводит учётную запись,
|
||||
второе с тем же значением попадает в ту же — в хранилище одна запись
|
||||
(критерий 1).
|
||||
- [x] 6.2 Тест: тот же заголовок с недоверенного адреса даёт `401`, и учётной
|
||||
записи не появляется (критерий 2).
|
||||
- [x] 6.3 Тест по перечню собственных адресов входа хранилища под
|
||||
`/api/collections/users/`: каждый отвечает отказом и учётной записи не
|
||||
меняет (критерий 3).
|
||||
- [x] 6.4 Тест: годный собственный токен побеждает заголовок, а протухший
|
||||
узнаванию не мешает.
|
||||
- [x] 6.4a Тест: узнанный не правит свою запись в коллекции пользователей
|
||||
запросом к хранилищу и не перечисляет коллекцию — путь захвата чужого имени
|
||||
закрыт.
|
||||
- [x] 6.4b Тест: пустой заголовок, два значения одного заголовка и значение
|
||||
сверх предела длины не узнают никого и записи не заводят.
|
||||
- [x] 6.4c Тест: два одновременных первых обращения одним значением дают одну
|
||||
запись.
|
||||
- [x] 6.4d Тест: занятый адрес почты не мешает завести запись — она заводится
|
||||
без почты.
|
||||
- [x] 6.4e Тест: запрос с заголовком на `GET /health` учётной записи не заводит.
|
||||
- [x] 6.4f Тест: прежние адреса под корнем `/auth` отдают разметку приложения.
|
||||
- [x] 6.5 Тест: значение заголовка не попадает в журнал.
|
||||
- [x] 6.6 Тест: ответ на успешный запрос не ставит браузеру куки.
|
||||
- [x] 6.7 Тест: токен файла выдаётся узнанному по заголовку, и путь к файлу
|
||||
записи проходит целиком.
|
||||
- [x] 6.8 Существующие тесты владельца и разграничения переписаны на новый способ
|
||||
представиться и остаются зелёными (критерий 5).
|
||||
- [x] 6.9 Тесты входа, обмена кода, куки, продления и выхода удалены вместе с
|
||||
предметом.
|
||||
|
||||
## 7. Документы и закрытие
|
||||
|
||||
- [x] 7.1 `docs/security.md`: периметр, недоверенный вход, разграничение доступа;
|
||||
четвёртый сдвиг про секрет в базе снят; требование к контуру — прокси
|
||||
**ставит** заголовок, а не пропускает пришедший — названо поимённо; там же
|
||||
цена переиспользования логина у провайдера.
|
||||
- [x] 7.2 `CLAUDE.md`: изъятие из инварианта о секрете снято.
|
||||
- [x] 7.3 `docs/architecture.md` приведён в соответствие; в «Единые точки
|
||||
проекта» добавлена строка про дом правила узнавания предъявителя.
|
||||
- [x] 7.3a `docs/passport.md`: строки про сессию OIDC у потребителей и в границе
|
||||
«Управление учётными записями» — учётную запись сервис не заводит по своей
|
||||
воле, а зеркалит имя, названное провайдером.
|
||||
- [x] 7.4 Поиск по `oidc`, `transcriber_session`, `transcriber_login`,
|
||||
`ApplyProviderSettings`, `/auth/` в дереве не находит живого кода
|
||||
(критерий 4).
|
||||
- [x] 7.5 `task gate` зелёный, кроме унаследованного `hadolint DL3066` — отказ
|
||||
воспроизводится на чистом `origin/master`, объявлен долгом в `CLAUDE.md`
|
||||
(критерий 4).
|
||||
|
||||
## 8. Отработка ревью кода
|
||||
|
||||
- [x] 8.1 Слой узнавания переехал за ограничитель частоты: отвергнутый запрос
|
||||
больше не заводит учётной записи.
|
||||
- [x] 8.2 Заведение учётной записи пишется в журнал; два значения заголовка —
|
||||
предупреждением, а не отладочным уровнем.
|
||||
- [x] 8.3 Имя и почта принимаются, а не кладутся как есть: длинное имя больше не
|
||||
запирает человека вечным отказом сервиса.
|
||||
- [x] 8.4 Отказ уникальности судится по коду, а не по имени колонки: негодная
|
||||
почта отличима от занятой.
|
||||
- [x] 8.5 Предел логина считается в знаках, а не в байтах.
|
||||
- [x] 8.6 Уникальный индекс по ключу сделан частичным — как соседний индекс
|
||||
почты; подъём на непустой базе больше не роняет накатку.
|
||||
- [x] 8.7 Ключ учётной записи закрыт от правки рукой в панели модельным хуком.
|
||||
- [x] 8.8 Хранилищу назван заголовок адреса спрашивающего: бюджет ограничителя
|
||||
перестал быть общим на весь сервис.
|
||||
- [x] 8.9 Приложение показывает текст сервера, своего словаря под коды ответа не
|
||||
заводит; текст `401` больше не зовёт ко входу, которого нет.
|
||||
- [x] 8.10 Оснастка берёт имена заголовков константами транспорта.
|
||||
- [x] 8.11 Три записи конвенций приведены к действительности; словарь полей
|
||||
журнала пополнен, изъятие про вывод оснастки записано.
|
||||
- [x] 8.12 Порог отката образа назван в «Эксплуатации»; требование к контуру про
|
||||
`X-Forwarded-For` и цена ширины перечня — в модели угроз.
|
||||
- [x] 8.13 Образец конфига сужен до адреса прокси.
|
||||
- [x] 7.6 Поведенческая проверка: сервис поднят локально, приложение открывается,
|
||||
учётная запись заводится первым обращением, запрос без заголовка получает
|
||||
отказ.
|
||||
Reference in New Issue
Block a user