вход переехал на доверенный заголовок Authelia вместо собственного OIDC
- пришедшего называет заголовок Remote-User от прокси, и верят ему только с адреса из перечня trusted_proxies; своего входа у сервиса не осталось — ни корня /auth, ни кук, ни срока сессии, ни секрета клиента в конфиге и в базе - учётная запись заводится первым обращением: EnsureUser в пакете хранилища, шаг схемы 202608220001 с колонкой provider_login и снятыми правилами users - cmd/oidcstub заменён на cmd/devtools с подкомандой proxy; заодно закрыт унаследованный DL3066 — пользователь образа назван числом
This commit is contained in:
+39
-10
@@ -271,15 +271,33 @@ capability, и третий смысл развёл бы одно слово п
|
||||
- **Файл отдаётся ссылкой** `/api/files/<коллекция>/<запись>/<имя>`. Поле файла
|
||||
помечено защищённым шагом `202608120001`, а правило просмотра коллекции
|
||||
пускает всякого вошедшего: пройти по ссылке можно только с коротким токеном
|
||||
файла, который берут по сессии. Прежнее решение — «право прочитать запись даёт
|
||||
файла, который берёт узнанный. Прежнее решение — «право прочитать запись даёт
|
||||
знание её идентификатора» — отменено задачей `oidc-login` 2026-08-12. Имя файла
|
||||
в хранилище **в журнал не пишется** по-прежнему: оно последняя часть ссылки.
|
||||
- **Коллекция `users`** заводится самой библиотекой, а шаг `202608120001` её
|
||||
сужает: создание записи разрешено только контексту обмена OIDC
|
||||
(`@request.context = "oauth2"`), вход по паролю и одноразовый код выключены.
|
||||
Без этого сужения закрытие API обходится двумя запросами — завести себе
|
||||
запись и войти паролем. Продление сессии закрыто слоем в приложении, а не
|
||||
настройкой коллекции: библиотека выдаёт сессию продлеваемой всегда.
|
||||
- **Коллекция `users`** заводится самой библиотекой, а два наших шага её сужают.
|
||||
`202608120001` выключил вход по паролю и одноразовый код; `202608220001`
|
||||
довершил: снял настройки OAuth2 и **все пять правил доступа** — перечисление,
|
||||
чтение, создание, правку и удаление, — оставив их пустыми, что у хранилища
|
||||
означает «только владелец панели».
|
||||
|
||||
Правку и удаление умолчание библиотеки открывало владельцу записи
|
||||
(`id = @request.auth.id`), и до переезда входа это ничему не мешало: слой
|
||||
предъявления жил под корнем приложения, и браузер до поверхности хранилища не
|
||||
дотягивался. С узнаванием по заголовку она достижима, а ключ учётной записи
|
||||
лежит теперь обычной колонкой — правка своей записи была бы присвоением чужого
|
||||
имени. Наш код читает и заводит запись мимо правил, панель работает
|
||||
суперпользователем, своих экранов профиля сервис не заводит.
|
||||
|
||||
- **Ключ учётной записи — колонка `provider_login`** с уникальным индексом,
|
||||
заведена шагом `202608220001`. В ней логин человека **у провайдера** — то
|
||||
значение, которым его называет обратный прокси заголовком. По нему запись
|
||||
ищется и по нему же заводится при первом обращении.
|
||||
|
||||
Почта в той же коллекции переведена в необязательную тем же шагом: провайдер
|
||||
не обязан её приносить, а ключом она не служит. Уникальность почты держится
|
||||
**частичным** индексом (`WHERE email != ''`), поэтому записи без почты
|
||||
уживаются друг с другом; уникальность логина — обычным, поэтому двух записей с
|
||||
пустым ключом схема не примет вовсе.
|
||||
- **Захват записи — один запрос с `RETURNING`**, мимо записей коллекции.
|
||||
`app.DB()` направляет всё, кроме выборок, в пул с единственным соединением,
|
||||
поэтому захваты выстраиваются в очередь. Порядок выборки — по времени
|
||||
@@ -324,6 +342,14 @@ capability, и третий смысл развёл бы одно слово п
|
||||
| Умолчание размера страницы списка | 30 | `controller/http.DefaultPageLimit` | столько помещается на экран телефона без прокрутки в два экрана |
|
||||
| Потолок размера страницы списка | 100 | `controller/http.MaxPageLimit` | против того, чтобы попросить весь архив одним запросом и тем обойти постраничность её же параметром |
|
||||
| Ограничитель частоты под `/app/` | 120 запросов за 60 секунд | `controller/http.appRateMaxRequests`, `appRateWindowSec` | сервисом пользуются единицы человек; бюджет считается по адресу спрашивающего, а не по учётной записи |
|
||||
|
||||
**Адрес спрашивающего берётся из `X-Forwarded-For`, и это назначается кодом при
|
||||
подъёме** — `controller/http.ApplyTrustedProxyHeaders`. Без этого хранилище
|
||||
ключует счётчик адресом пира, а пир с переездом входа на заголовок всегда один и
|
||||
тот же — обратный прокси; бюджет тогда становится общим на весь сервис, и восемь
|
||||
одновременно открытых карточек выбирают его целиком. Требование к контуру,
|
||||
которое отсюда следует, записано в [security.md](security.md), «Периметр»:
|
||||
`X-Forwarded-For` прокси обязан перезаписывать, а не дописывать.
|
||||
| Доля бюджета под опрос карточки | 1/8 | `controller/http.pollBudgetShare` | опрос идёт не один: в ту же секунду приложение листает список и грузит новую запись. Из этой доли **выводится** объявляемая частота опроса, и своей константы у неё нет |
|
||||
| Потолок длины имени файла отправителя | 255 знаков | `entity.MaxOriginalFilenameLen` | предел длины имени в распространённых файловых системах: длиннее системный диалог выбора файла не даёт |
|
||||
| Потолок длины расширения | 32 знака | `service/transcribe.go`, `maxExtLen` | сторож от патологии, а не перечень: расширения известных форматов укладываются в пять знаков, а `x.` с четырьмястами знаками роняет заведение временного файла |
|
||||
@@ -339,9 +365,12 @@ capability, и третий смысл развёл бы одно слово п
|
||||
| Качество кодирования vorbis | `-q:a 4` | `adapter/converter/ffmpeg/ffmpeg.go` | — |
|
||||
| Жизнь приглашения завести владельца панели | 30 минут | умолчание PocketBase | — |
|
||||
| Потолок размера одной записи | 8 ГиБ | `entity.MaxRecordSize` | расчётный потолок в шесть часов с запасом на видео |
|
||||
| Срок жизни сессии | нормирует [access](../openspec/specs/access/spec.md) | `pbrepo.SessionDuration`, ставится при подъёме | решение владельца 2026-08-12; умолчание библиотеки никем не выбрано, и спека прямо запрещает его применять |
|
||||
| Потолок времени на вход у провайдера | 10 минут | `controller/http/auth.go` | дольше носитель состояния не нужен |
|
||||
| Таймаут обмена кода у провайдера | 15 секунд | там же | молчащий провайдер иначе держит обработчик возврата открытым |
|
||||
| Предел длины логина у провайдера | 255 знаков | `pbrepo.MaxProviderLoginLength` и колонка `provider_login` | значение приходит заголовком, то есть задаётся тем, кто шлёт запрос; число то же, что у имени в умолчании библиотеки |
|
||||
|
||||
Три числа отсюда ушли 2026-08-22 вместе с собственным входом: срок жизни сессии,
|
||||
потолок времени на вход у провайдера и таймаут обмена кода. Сессия не выдаётся
|
||||
вовсе, обменивать код не на что, а отзыв доступа судит провайдер на каждом
|
||||
запросе — задержке, которую измерял срок сессии, теперь неоткуда взяться.
|
||||
|
||||
**У сторожа простоя есть второй потолок, и он не тот, что в настройке.** Предел
|
||||
простоя проверяется в момент захвата, а захват не выдаёт запись, чей срок
|
||||
|
||||
Reference in New Issue
Block a user