POST /api/profile/switch called invalidate_models_cache(), whose
test-isolation contract also unlinks models_cache.<profile>.json. On a
switch nothing about the sources changed, so the delete only forced a
full cold rebuild (live provider fetch_models HTTPS calls, ~490k deepcopy
calls) on the very next /api/models — several seconds on every switch.
The disk cache is already keyed per profile and rejected on read when
_models_cache_source_fingerprint() differs, so it is safe to keep. Add a
delete_disk keyword (default True — every existing caller keeps the old
behaviour) and have the switch route pass delete_disk=False.
Regression tests cover: memory-only drop preserves the disk file, default
call still unlinks it, the next get_available_models() serves the disk
snapshot without a live rebuild, and the switch route opts out. Two
existing zero-arg stubs of invalidate_models_cache accept **kwargs now.
Two Codex adversarial-security-gate findings on the batch:
1. #7331 CORE (backward-compat break): making '|' an unconditional trusted-groups
separator would re-split an existing deployment whose group NAME legitimately
contains a literal '|' (e.g. 'unprivileged|admins' read as one group), silently
changing its profile binding. Pipe splitting is now OPT-IN via
HERMES_WEBUI_TRUSTED_GROUPS_PIPE_SEPARATOR; comma/newline stay the default.
Tests: literal-pipe preserved by default, pipe splits only with the flag set.
2. #6772 follow-up (my own hardening introduced it): rebuilding the origin from
parsed parts dropped the brackets around an IPv6 host, so 'http://[::1]:8787'
became 'http://::1:8787' and never matched the browser's bracketed
clientDataJSON.origin -> passkey login over IPv6 would fail. Re-bracket IPv6
literals. Added rp_context regression tests (Origin-first, port preserved,
IPv6 bracketed, malformed->Host fallback, userinfo stripped).
Per review feedback on #7331: cover comma, pipe, newline, mixed
separators, repeated/adjacent separators, and surrounding whitespace
directly against _trusted_groups_header_value(), in addition to the
existing end-to-end bound_profile regression test.
Authentik's proxy provider on this deployment joins multiple group
names with "|" instead of "," (e.g. "admins|developpeur"). The
comma-only split in _trusted_groups_header_value() treated that whole
string as a single unmatched group name, silently dropping the
session to the unbound "default" profile even for identities that
are legitimate members of a mapped group. Single-group identities were
never affected since there was no separator to parse.
Accept comma, pipe, and newline as equivalent separators so the parser
works regardless of which the outpost happens to send.
* feat(#3351): add trusted-header auth core
* fix(#3351): reconcile trusted sessions per request
* fix(#3351): preserve valid trusted group mappings
* fix(#3351): defer trusted logout reconciliation until after csrf
* fix(auth): clear _pending_set_cookies in reset_trusted_auth_request_state (keep-alive cookie cross-request leak)
Codex gate CORE finding: a Set-Cookie queued in request N but not flushed
survived onto the reused HTTP/1.1 handler and could be emitted by request N+1
— after trusted-identity rotation on logout this could overwrite a valid login
cookie and 401 the user. Reset the queue at the per-request boundary + add a
keep-alive regression test.
* docs(.env.example): document trusted-header auth / reverse-proxy SSO env vars
HERMES_WEBUI_TRUSTED_AUTH_HEADER + TRUSTED_PROXY_CIDRS (with exact-proxy-IP
security warning) + optional groups-header/JSON group-profile-map/logout-url.
* Release exp-v0.52.43: trusted-header auth / reverse-proxy SSO (#5568, #3351) + keep-alive cookie fix + docs
---------
Co-authored-by: Rod Boev <rod.boev@gmail.com>
Co-authored-by: nesquena-hermes <agent@nesquena-hermes>