вход переехал на доверенный заголовок 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,361 @@
|
||||
## Context
|
||||
|
||||
Сегодня сервис проводит вход сам: адрес `/auth/login` уводит человека к
|
||||
Authelia, положив состояние и проверочный код PKCE в куку `transcriber_login`;
|
||||
`/auth/callback` сверяет состояние и меняет принесённый код на сессию
|
||||
внутрипроцессным запросом к собственному адресу хранилища; сессия уезжает кукой
|
||||
`transcriber_session` и живёт семь суток. Секрет клиента ради этого обмена лежит
|
||||
в конфиге и приводится к настройкам коллекции пользователей при каждом подъёме.
|
||||
|
||||
Обратный прокси контура (`files/caddyproxy/Caddyfile.template` в
|
||||
`pet-project-server`) уже спрашивает Authelia через `forward_auth` и копирует
|
||||
ответ в заголовки `Remote-User`, `Remote-Groups`, `Remote-Email`, `Remote-Name`
|
||||
трём соседним сервисам. Правила для этого сервиса там нет: он не выложен.
|
||||
Контейнер портов наружу не публикует.
|
||||
|
||||
Проект на стройке: данных на сервере нет, совместимость не требуется. Применённый
|
||||
шаг схемы по-прежнему не переписывается.
|
||||
|
||||
**Что измерено пробником** на временной базе (прогон 2026-08-22, программа
|
||||
удалена):
|
||||
|
||||
- коллекция `users` требует непустых `email` и `password`; поле `email`
|
||||
переводится в необязательное шагом схемы, `password` — нет ни при каком
|
||||
значении признака;
|
||||
- уникальность почты держится **частичным** индексом (`WHERE email != ''`),
|
||||
поэтому запись без почты законна и второй такой же не мешает;
|
||||
- своя колонка с уникальным индексом отвергает вторую запись с тем же значением
|
||||
и находится поиском по значению;
|
||||
- запись, заведённая так, выдаёт файловый токен — то есть годится как учётная
|
||||
запись предъявителя во всём, что делает сервис.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Узнавать пришедшего заголовком доверенного источника и никак иначе.
|
||||
- Убрать собственный протокол входа целиком, вместе с куками, состоянием, PKCE,
|
||||
обменом кода и выходом.
|
||||
- Убрать секрет клиента из конфига и из базы.
|
||||
- Оставить рабочим путь к файлу записи: предъявиться, взять у хранилища короткий
|
||||
файловый токен, пройти по ссылке.
|
||||
- Оставить рабочим вход владельца в панель его собственным паролем.
|
||||
- Дать способ представиться на машине без прокси.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Второй уровень доступа по `Remote-Groups`: его потребителя — страницы расхода
|
||||
— ещё нет.
|
||||
- Личные токены для программ: их заводит `api-tokens`.
|
||||
- Правило обратного прокси и правило Authelia для домена сервиса: они живут в
|
||||
`pet-project-server`, и эта работа их не пишет и не проверяет.
|
||||
- Приведение пути к канонической форме (обход панели подменой знака): барьер
|
||||
переезжает на домен, и правило на литерал пути перестаёт быть единственным, но
|
||||
сама канонизация — не предмет этой работы.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Р1. Доверие определяется адресом пира соединения
|
||||
|
||||
Заголовку верят, когда запрос пришёл с адреса из объявленного перечня. Адрес
|
||||
берётся у самого соединения (`RemoteAddr`), а не из пересылаемых заголовков.
|
||||
|
||||
Отвергнуто:
|
||||
|
||||
- **`X-Forwarded-For` и его родня.** Значение целиком задаёт тот, кто шлёт
|
||||
запрос. Барьер, который подделывается той же строкой, что и обходится, не
|
||||
барьер вовсе.
|
||||
- **Готовое разрешение адреса у библиотеки** (`RealIP`). Оно читает
|
||||
пересылаемый заголовок, когда настроен список доверенных прокси, а сам список
|
||||
живёт в настройках хранилища — то есть в базе, а не в конфиге. Условие доверия
|
||||
оказалось бы в месте, которое правят руками в панели.
|
||||
- **«Контейнер портов не публикует, значит доверяем всякому».** Условие верное,
|
||||
но непроверяемое отсюда: правка раскладки контейнера открыла бы сервис молча,
|
||||
и оракула у второго критерия приёмки не было бы вовсе.
|
||||
|
||||
### Р2. Слой действует на объявленной области, а не на всём роутере
|
||||
|
||||
Слой вешается корневым — иначе к нужным адресам его не привязать, — но узнаёт
|
||||
пришедшего **только на объявленной области**: корень приложения плюс адрес,
|
||||
которым хранилище выдаёт короткий файловый токен. Область берётся из перечня
|
||||
адресного пространства, а не пишется вторым списком; адрес файлового токена
|
||||
добавляется к нему поимённо. Устроено это ровно так же, как сегодня устроен
|
||||
запрет продления сессии: корневая привязка и проверка пути внутри.
|
||||
|
||||
Учётная запись ставится **только когда её ещё нет** — то есть после того, как
|
||||
библиотека прочитала предъявленный токен и никого не нашла. Предъявленным
|
||||
считается токен, который библиотека **прочитала успешно**: протухший и негодный
|
||||
предъявленными не считаются, и такой запрос узнаётся заголовком.
|
||||
|
||||
**Первая редакция вешала узнавание на весь роутер, и это была дыра.** Прежняя
|
||||
спека держала обратное требование — «область действия слоя MUST быть ограничена
|
||||
адресами приложения… часть поверхности хранилища защищена сегодня ровно тем, что
|
||||
браузер заголовка сам не шлёт, и расширение слоя на всё сняло бы эту защиту
|
||||
молча», — а первая редакция снимала это требование вместе с кукой и замены ему не
|
||||
давала. Сложение с Р5 (ключ учётной записи переезжает в обычную колонку
|
||||
пользователей) давало путь захвата: узнанный человек шлёт
|
||||
`PATCH /api/collections/users/records/{свой id}` и ставит себе ключом чужое имя,
|
||||
а первое обращение настоящего владельца этого имени попадает в **его** учётную
|
||||
запись — вместе со всем архивом. Правило правки у коллекции пользователей
|
||||
библиотечное и разрешает править свою запись; **проверено пробником**: у
|
||||
коллекции `users` `ListRule`, `ViewRule`, `UpdateRule` и `DeleteRule` равны
|
||||
`id = @request.auth.id`, а применённый шаг `202608120001_oidc_login` трогает
|
||||
только правило создания.
|
||||
|
||||
Второе следствие сужения: узнавание больше не срабатывает на пробе здоровья, на
|
||||
метриках и на каждом ресурсе сборщика. Иначе всякий запрос за картинкой стоил бы
|
||||
обращения к базе, а первый такой запрос с новым именем — записи в неё.
|
||||
|
||||
Отвергнуто: **вешать под корнем приложения и завести отдельный путь к файлу**.
|
||||
Второй способ добраться до файла — второй дом одного правила, и разошлись бы они
|
||||
на первой же правке разграничения.
|
||||
|
||||
### Р2а. Поверхность коллекции пользователей закрывается схемой
|
||||
|
||||
Новый шаг схемы снимает у коллекции пользователей все правила доступа —
|
||||
перечисление, чтение, создание, правку и удаление, — оставляя их пустыми, то есть
|
||||
«только владелец панели». Наш код читает и заводит запись мимо правил, панель
|
||||
работает суперпользователем, своих экранов профиля сервис не заводит.
|
||||
|
||||
Это второй барьер поверх Р2, и он нужен именно вторым. Прежняя спека сама
|
||||
называла защиту, державшуюся на том, что браузер не шлёт заголовка,
|
||||
**случайной** — и была права; область слоя это та же случайность, только с
|
||||
другой стороны. Сверку берёт на себя проверка схемы: коллекция пользователей
|
||||
дописывается к шести, которые она уже сторожит, — сегодня она единственная из
|
||||
семи проверяется только умолчаниями библиотеки.
|
||||
|
||||
Отсюда же норма, которой в первой редакции не было вовсе: **ключ учётной записи
|
||||
не правится ничем, кроме заведения самим сервисом.** Правило доступа закрывает
|
||||
путь снаружи, норма закрывает и правку рукой в панели: переписанный ключ отдаёт
|
||||
архив следующему, кто придёт с этим именем, и вернуть его будет нечем.
|
||||
|
||||
### Р3. Сессия не выдаётся никакая
|
||||
|
||||
Сервис не выдаёт браузеру ни куки, ни токена. Каждый запрос узнаётся заново, по
|
||||
заголовку, который прокси поставил, сходив к Authelia.
|
||||
|
||||
Это и есть выгода задачи: отзыв доступа перестаёт ждать. Пока сервис выдавал
|
||||
значение, живущее семь суток, отозванный у провайдера человек работал до
|
||||
истечения этого значения, и другого канала отзыва не было.
|
||||
|
||||
Отвергнуто: **выдавать сессию хранилища по первому запросу с заголовком** — куку
|
||||
ставить, дальше верить ей. Дешевле в работе (слой срабатывал бы раз в неделю, а
|
||||
не на каждом запросе), но возвращает ровно то, что задача убирает: значение,
|
||||
переживающее отзыв. Семь суток вернулись бы вместе с ним.
|
||||
|
||||
Цена принятого решения: поиск учётной записи по значению заголовка идёт на
|
||||
**каждом** запросе к сервису. Уникальный индекс делает это одним обращением к
|
||||
базе; сервисом пользуются единицы человек, и замера здесь не требуется.
|
||||
|
||||
### Р4. Имена заголовков нормативны, настраивается перечень доверенных адресов
|
||||
|
||||
Имена `Remote-User`, `Remote-Name`, `Remote-Email` записываются в спеку и
|
||||
живут константами — как жило имя куки сессии, и по той же причине: смена имени
|
||||
молча перестаёт узнавать всех, а тест, который сам ставит и сам читает своё имя,
|
||||
этого не замечает. Контур уже пишет эти имена трём соседним сервисам.
|
||||
|
||||
Настройкой приходит одно — перечень адресов, чьему заголовку верят. Он
|
||||
непустой обязателен: пустой перечень значит «не верить никому», то есть сервис,
|
||||
поднявшийся никого не узнающим, и молчать об этом старт не вправе.
|
||||
|
||||
Отвергнуто: **имена заголовков ключами конфига**. Второе место, где их можно
|
||||
написать неверно, а выгоды нет: контур один и пишет одно.
|
||||
|
||||
Имена ключей конфига — необратимое. **Решено человеком на чекпоинте
|
||||
2026-08-22:** секция `[auth]` остаётся под своим именем, в ней один ключ —
|
||||
`trusted_proxies`. Довод: секция сохраняет смысл «кому мы верим на входе», а
|
||||
проверка настроек на старте остаётся там же, где была.
|
||||
|
||||
### Р5. Ключ учётной записи — своя колонка
|
||||
|
||||
Значение `Remote-User` ложится в свою колонку коллекции пользователей,
|
||||
уникальную. По ней запись ищется и по ней же заводится, если её ещё нет.
|
||||
|
||||
Отвергнуто:
|
||||
|
||||
- **Адрес почты ключом.** Authelia не обязана его отдавать, человек его меняет,
|
||||
а первый вход с чужим адресом достался бы чужой записи — ровно тот захват,
|
||||
ради которого прежний шаг схемы закрывал создание записи.
|
||||
- **Таблица внешних учётных записей библиотеки.** Она принадлежит механике
|
||||
OAuth2, которую эта работа убирает целиком.
|
||||
- **Не брать `Remote-Email` вовсе.** Довод за: поля с личными сведениями,
|
||||
у которого нет ни одного потребителя, в периметре быть не должно — ключом
|
||||
почта не служит, в журнал и в ответ ей ходу нет, а ради неё шаг схемы ещё и
|
||||
делает колонку необязательной. Отвергнуто потому, что потребитель назван и
|
||||
ждёт своей очереди: задача `email-notification` шлёт готовый текст на адрес из
|
||||
учётной записи, и строка про это стоит в модели угроз, раздел «Куда уходит
|
||||
содержимое записи». Взять адрес заголовком в тот день будет нечем — заголовок
|
||||
приходит с запросом человека, а рассылка идёт из конвейера.
|
||||
|
||||
### Р5а. Цена ключа названа в обе стороны
|
||||
|
||||
Переименование у провайдера заводит новую учётную запись — это первая половина, и
|
||||
она была названа сразу. Вторая: **логин переиспользуем**. Человек, которому
|
||||
выдали логин ушедшего, при первом же обращении попадает в **существующую** запись
|
||||
и получает весь её архив — голосовые записи семьи, расшифровки, тексты, то есть
|
||||
самое чувствительное, что у сервиса есть.
|
||||
|
||||
Записывается это в спеку и повторяется в модели угроз, а не чинится: неизменяемого
|
||||
признака заголовок не приносит, и не допускать переиспользования логинов — работа
|
||||
провайдера, а не сервиса. Дефект был бы в умолчании — в том, что риск не назван, и
|
||||
следующий читатель счёл бы вопрос закрытым абзацем про переименование.
|
||||
|
||||
Третья сторона той же цены: у осиротевшей записи прежнего человека остаётся архив,
|
||||
который нечем ни слить с новой, ни убрать — владелец записи назначается один раз и
|
||||
не меняется, а учётная запись с записями не удаляется по норме `storage`.
|
||||
|
||||
Имя колонки — `provider_login`, и оно говорит о происхождении значения: это логин
|
||||
человека **у провайдера**, а не наш идентификатор. Имя уезжает шагом схемы и
|
||||
потому не переписывается.
|
||||
|
||||
Заведение записи: имя — из `Remote-Name`, почта — из `Remote-Email`, если тот
|
||||
пришёл. Почта перестаёт быть обязательной (шаг схемы), пароль записи
|
||||
обязателен всегда — ей ставится случайный, употребить его нельзя, потому что
|
||||
вход по паролю у коллекции выключен.
|
||||
|
||||
Найденную запись повторное обращение **не переписывает**: имя и почта берутся
|
||||
только при заведении. Иначе каждый запрос был бы записью в базу, а правка имени
|
||||
в Authelia переписывала бы карточку человека молча, посреди его работы.
|
||||
|
||||
**Значение принимается, а не берётся как есть.** Пустое и состоящее из пробельных
|
||||
знаков не узнаёт никого и записи не заводит: прокси штатно шлёт пустой заголовок
|
||||
там, где никого не назвал, и по букве «первое обращение с новым значением» все
|
||||
неназванные собрались бы в одну общую учётную запись с общим архивом. По той же
|
||||
причине отвергается запрос, несущий **более одного** значения `Remote-User`:
|
||||
прокси, настроенный добавлять заголовок вместо замены, оставляет присланный
|
||||
анонимом рядом со своим, и умолчание «берём первый» отдаёт вход анониму. Сверх
|
||||
того — предел длины и отказ на управляющие знаки, а в хранилище значение уходит
|
||||
параметром, а не подстановкой в текст фильтра. Класс у проекта уже был: хвост
|
||||
имени отправителя, уехавший меткой метрики, чинился приведением на входе, а не
|
||||
запретом на выходе.
|
||||
|
||||
Сравнение при поиске — точное, знак в знак. Приведение регистра завело бы правило,
|
||||
которого нет у провайдера: считает ли Authelia `admin` и `Admin` одним человеком,
|
||||
сервису неизвестно, а угаданное правило склеило бы двух разных людей молча.
|
||||
|
||||
**Два отказа уникальности различаются.** По ключевой колонке — гонка двух первых
|
||||
обращений одним именем: код повторяет поиск и продолжает. По любой другой —
|
||||
почта, пришедшая от провайдера, уже занята другой учётной записью (общий
|
||||
почтовый ящик, семья, группа) — запись заводится **без почты**, потому что почта
|
||||
необязательна, и остаётся строка журнала с идентификатором записи. Без этого
|
||||
разреза второй человек с той же почтой не завёлся бы никогда: повторный поиск по
|
||||
имени снова ничего не нашёл бы, и исход выродился бы либо в цикл, либо в вечный
|
||||
отказ без внятной причины.
|
||||
|
||||
**Отказ хранилища при узнавании — отказ сервиса, а не «вас не узнали».** Молчаливый
|
||||
проход неузнанным показал бы человеку отказ входа там, где легла база.
|
||||
|
||||
### Р6. Заголовок с недоверенного адреса просто не действует
|
||||
|
||||
Слой при этом отказа не выдаёт. Отказ приходит там, где приходил и раньше, —
|
||||
требованием учётной записи под корнем приложения, кодом `401`.
|
||||
|
||||
Отвергнуто: **отвечать отказом прямо в слое**. Проба здоровья, метрики и
|
||||
разметка приложения открыты анонимно по спеке; отказ в слое закрыл бы их
|
||||
всякому, кто пришлёт заголовок, — то есть чужая строка в запросе гасила бы
|
||||
наблюдение за сервисом.
|
||||
|
||||
### Р7. Значение заголовка в журнал не идёт
|
||||
|
||||
Пишется факт и исход — «заголовок пришёл с недоверенного адреса», «учётная
|
||||
запись заведена», — с идентификатором записи, но без значения. Довод тот же,
|
||||
каким причина отказа провайдера приводилась к перечню известных: значением
|
||||
целиком распоряжается тот, кто шлёт запрос, а с недоверенного адреса это ещё и
|
||||
аноним. Плюс само имя принадлежит человеку — наравне с адресом почты, которому
|
||||
спека уже запрещает попадать в журнал.
|
||||
|
||||
### Р7а. У правила «найти или завести человека» есть дом, и он не в транспорте
|
||||
|
||||
Правило — поиск по ключу, заведение при отсутствии, приём значения, разрез двух
|
||||
отказов уникальности, «найденную не переписывать» — живёт **методом пакета
|
||||
хранилища**, а слой транспорта только читает заголовок, судит адрес пира и зовёт
|
||||
метод. Дом объявляется строкой в перечне единых точек проекта.
|
||||
|
||||
Причина в следующей задаче, а не в чистоте. `api-tokens` заводит **второй** способ
|
||||
представиться и в своей записи прямо говорит: «два способа представиться и один
|
||||
владелец». Правило, уложенное куском в слой транспорта, придётся тогда либо
|
||||
продублировать вторым куском — и он разойдётся с первым на первой же правке, —
|
||||
либо вытаскивать задним числом. Стоимость следующего изменения здесь считается:
|
||||
при вынесенном правиле новый способ представиться стоит одного нового слоя.
|
||||
|
||||
Отвергнуто: **контракт в `internal/contract` с реализацией в адаптере**. Дороже на
|
||||
интерфейс, у которого сегодня одна реализация и второго потребителя вне HTTP не
|
||||
предвидится; правила направления зависимостей транспорту знать адаптер хранилища
|
||||
разрешают.
|
||||
|
||||
### Р8. Способ представиться без прокси — развилка
|
||||
|
||||
Подставной провайдер уходит вместе с протоколом, и его место пусто. На это место
|
||||
опирается `dev-run-task`, которая обещает адрес, по которому открывают
|
||||
приложение. Варианты:
|
||||
|
||||
| Способ | Что даёт | Цена |
|
||||
| --- | --- | --- |
|
||||
| **Один main-пакет оснастки с подкомандами** — `cmd/devtools proxy` ставит заголовок и переправляет запрос сервису, `cmd/devtools admin` заводит владельца панели | Приложение открывается браузером обычным образом; сервис остаётся без единой ветки для разработчика; строка исключения в сборке образа остаётся одна навсегда | Соседняя задача `dev-run-task` планировала свой пакет `cmd/devadmin` — её придётся переформулировать под подкоманду |
|
||||
| **Отдельный main-пакет на каждый инструмент** — как было с подставным провайдером | Ничего не надо согласовывать с соседней задачей | Каждый инструмент стоит четырёх мест: строка сборки образа, «Деплой» в устройстве, «Команды» в памятке, `README`. Забытая строка сборки тихо кладёт инструмент разработчика в боевой образ, а инструментов сразу становится два |
|
||||
| **Контейнер с прокси вместо своего кода** — контур и так стоит на Caddy, docker и так требование к машине разработчика | Каталог точек входа не растёт ни на один пакет | Ещё один контейнер в локальном прогоне и конфиг прокси, который никто не проверяет; отладка «почему не узнан» уходит в чужой контейнер |
|
||||
| **Ключ конфига, подставляющий имя** | Один процесс вместо двух | В боевом коде появляется ветка, пускающая без всякой проверки; от беды её отделяет только правильность конфига на сервере. Сверх того — **прямо противоречит требованию дельты**: сервис узнаёт пришедшего по заголовку с доверенного адреса и никак иначе, значит цена включает переоткрытие спеки |
|
||||
|
||||
**Решено человеком на чекпоинте 2026-08-22:** первый вариант — один пакет
|
||||
оснастки `cmd/devtools` с подкомандами. Эта работа заводит пакет и подкоманду
|
||||
`proxy`; подкоманду `admin` заводит задача `dev-run-task`, и её запись надо
|
||||
переформулировать — она планировала свой отдельный пакет. Строка исключения в
|
||||
сборке образа остаётся одна навсегда.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Весь барьер держится на том, что прокси заголовок перезаписывает, а не
|
||||
пропускает пришедший.** Запрос от анонима приходит к сервису с адреса прокси,
|
||||
то есть с доверенного; если прокси не заменит `Remote-User` своим значением, а
|
||||
оставит присланный, сервис пустит кого угодно под любым именем. Проверить это
|
||||
репозиторием нечем — правило живёт в `pet-project-server`. → Требование к
|
||||
контуру называется поимённо в модели угроз, и выкладку запускает человек.
|
||||
- **Правило Authelia на домен сервиса не заведено.** Тогда прокси заголовка не
|
||||
ставит вовсе, сервис никого не узнаёт, и все адреса приложения отвечают `401`.
|
||||
→ Отказ громкий и одинаковый для всех, а перечень доверенных адресов сервис
|
||||
называет строкой журнала при подъёме.
|
||||
- **Переименование пользователя в Authelia заводит новую учётную запись**, и
|
||||
прежние записи остаются у прежней. → Принятая цена, названная в постановке;
|
||||
записывается в спеку, а не обходится.
|
||||
- **Два первых обращения одним именем одновременно.** Оба не найдут записи и оба
|
||||
примутся её заводить. → Уникальный индекс отвергает второго; код на отказ
|
||||
уникальности повторяет поиск, а не падает.
|
||||
- **Учётная запись заводится анонимом, если прокси настроен неверно.** Прежде
|
||||
запись заводилась только успешным входом у провайдера. → Тот же риск, что и
|
||||
первый, и то же смягчение: заводит её обращение с доверенного адреса, а
|
||||
доверенный адрес — это прокси.
|
||||
- **Перечень доверенных адресов, накрывающий весь интернет**, читается как
|
||||
подсеть и стартует молча. Старт роняется на пустом и на нечитаемом перечне, но
|
||||
«доверять всякому» остаётся достижимым настройкой — тем самым, что дизайн
|
||||
отверг решением. → Смягчения барьером нет намеренно: порог «эта подсеть
|
||||
слишком широка» обходится двумя половинками той же подсети, то есть был бы
|
||||
барьером на вид. Наблюдаемость вместо барьера: перечень называется строкой
|
||||
журнала при подъёме.
|
||||
- **Логин у провайдера переиспользуем**, и новый его владелец получает архив
|
||||
прежнего. → Записывается ценой в спеку и в модель угроз; не допускать
|
||||
переиспользования — работа провайдера. Подробно — Р5а.
|
||||
- **Панель владельца остаётся вне разграничения.** Она и была вне его; барьер
|
||||
переезжает с правила на литерал пути (обходимого подменой знака) на домен,
|
||||
закрытый Authelia. → Обход `/%5f/` перестаёт существовать, но канонизация пути
|
||||
этой работой не делается, и в модели угроз это надо переписать, а не вычеркнуть.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Стройка: на сервере данных нет, выкладка пойдёт с чистого листа, переносить
|
||||
нечего.
|
||||
|
||||
1. Новый шаг схемы — колонка ключа `provider_login` с уникальным индексом,
|
||||
необязательная почта, снятые настройки OAuth2 и **все правила доступа
|
||||
коллекции пользователей, снятые в пустое**. Применённый
|
||||
`202608120001_oidc_login` не трогается.
|
||||
2. Конфиг: секция входа переписывается, образец — вместе с ней.
|
||||
3. Контур: правило прокси и правило Authelia для домена сервиса заводятся в
|
||||
`pet-project-server`. Это делает человек, и до этого сервис никого не узнаёт.
|
||||
|
||||
Откат: шаг схемы возвращает то, что было до него, кроме открытого создания
|
||||
записи — его не возвращает и прежний шаг, по той же причине.
|
||||
|
||||
## Open Questions
|
||||
|
||||
Открытых не осталось: оба вопроса закрыты человеком на чекпоинте 2026-08-22 —
|
||||
имя ключа настроек (Р4) и способ представиться без прокси (Р8).
|
||||
Reference in New Issue
Block a user