Skip to content

test(auth): add trust-mode acceptance suite (no-DB, multi-worker, config-flip) - #6753

Open
jonpspri wants to merge 2 commits into
feat/5904-trust-token-mintingfrom
feat/5905-trust-mode-acceptance-suite
Open

test(auth): add trust-mode acceptance suite (no-DB, multi-worker, config-flip)#6753
jonpspri wants to merge 2 commits into
feat/5904-trust-token-mintingfrom
feat/5905-trust-mode-acceptance-suite

Conversation

@jonpspri

@jonpspri jonpspri commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Six acceptance suites proving trust mode end to end. Suites: no-DB proof; multi-worker revocation; config-flip; UAID propagation; overage-degraded; cross-gateway UAID.

(a) NO-DB PROOF — CI-eligible, asserts on executed SQL (never a grep): a SQLAlchemy before_cursor_execute listener recorded 300 statements across 60 authenticated trust-mode requests through get_current_user (real revocation check, group resolver, roles lookup; only the JWT verifier patched) — external_group_mappingsx60, rolesx60, token_revocationsx60, email_teamsx120 — and ZERO statements touching email_users, while a live user row exists for the caller (a read would be observable). A vacuous-pass guard asserts the statements list is non-empty and every request resolved a VirtualPrincipal. The wall-clock p99 latency comparison originally in this suite was removed before merge: unit tests do not run benchmarks.

(b) MULTI-WORKER — tests/live_gateway/test_trust_mode_multi_worker.py (e2e + skip_no_gateway, modeled on the multi-instance runner): mint with known jti, 200 on both workers, revoke on A via logout, 401 on B within the 30s negative-cache window, unrevoked control passes. No live stack exists in this worktree — both skip dimensions verified firing; live execution happens in the Epic 2 gate's stack.

(c) CONFIG-FLIP — full db -> jwt-trust -> db round-trip in one process: the trusted marker 401s when OFF with the user-lookup seam tripwired (proving it never enters the default funnel); legacy tokens keep default semantics under trust mode, including the is_active kill-switch (disabled user -> 401).

(d) UAID — forwarded bearer verified verbatim with claims intact; opaque session tokens and UAID_FORWARD_AUTH=false NOT forwarded; UAID_ALLOWED_DOMAINS fail-closed unchanged. Doc section added (docs/security/uaid-cross-gateway-auth.md).

(e) OVERAGE-DEGRADED — all three Entra marker shapes under proceed_without_groups: authenticated with token_teams=[], team-visibility agents 404 (A2AAgentNotFound at the router), public agents 200, WARNING carrying the oid.

(f) CROSS-GATEWAY — two divergent mapping tables; the same forwarded payload resolves caller-team-x on the calling gateway and remote-team-a on the remote — membership does not transfer; the remote re-evaluates against its own table; Team-B agent -> 404, public -> 200.

Tested with: uv run pytest tests -k "trust_mode" -q — 54 passed, 1 skipped (suite (b) skip verified by direct invocation); make ruff — all checks passed; make test — 23381 passed, 879 skipped, 2 xfailed.

Acceptance criteria of #5905 are met. Risk to existing users: none — tests and documentation only.

Stack: B.13 of epic #5885 (base: #6751).

Closes #5905

@jonpspri
jonpspri added this pull request to stack #6729 September 10, 2026 04:12
@jonpspri
jonpspri marked this pull request as ready for review September 10, 2026 04:12
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch 2 times, most recently from 52d17b6 to 3f7e101 Compare September 10, 2026 08:06
@jonpspri
jonpspri removed this pull request from stack #6729 September 12, 2026 08:50
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from 3f7e101 to cdf9d95 Compare September 12, 2026 09:07
@jonpspri
jonpspri added this pull request to stack #6798 September 12, 2026 09:08
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch 3 times, most recently from c5a19c2 to dd5819f Compare September 12, 2026 09:52
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from dd5819f to a889dc6 Compare September 12, 2026 16:45
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from a889dc6 to fe18af6 Compare September 12, 2026 17:20
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from fe18af6 to f97ea15 Compare September 12, 2026 17:35
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from f97ea15 to 44b049d Compare September 12, 2026 17:54
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from 44b049d to 94ae757 Compare September 12, 2026 18:09
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from 94ae757 to d6baa29 Compare September 12, 2026 18:33
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from d6baa29 to 5c0251b Compare September 12, 2026 18:52
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from 5c0251b to c9d486a Compare September 12, 2026 19:12
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch 2 times, most recently from 50f3af9 to 91adb65 Compare September 12, 2026 20:26
…fig-flip)

Suites: no-DB proof; multi-worker revocation; config-flip; UAID propagation; overage-degraded; cross-gateway UAID.
Signed-off-by: Jonathan Springer <jps@s390x.com>
…the real ingress

Finding F4: the dispatch matrix called _maybe_verify_external() directly
with a mocked verify_external_idp_token, and the group-role merge tests
patched verify_jwt_token_cached and derived permissions manually, so
neither exercised the real authentication path. With the ingress
dispatch (#6750) and decorator forwarding (#6749) landed, convert both.

test_token_dispatch_matrix.py: the external rows drive
get_current_user()/verify_credentials_cached() with externally-signed
RS256 tokens minted from the local OIDC issuer key; only the OIDC
discovery/JWKS network fetch is mocked. Wraps-spies preserve the
consulted/not-consulted assertions while the real dispatch runs, and the
fail-closed row uses a token signed with a key the JWKS stub does not
serve.

test_trust_role_merge.py: tokens flow through get_current_user ->
get_current_user_with_permissions -> check_permission_inline with the
real PermissionService (restoring the real class over the autouse
always-allow mock); manual roles-table permission derivation is removed.
Adds a boundary row pinning that a resolved role without permissions
still denies.

Signed-off-by: Jonathan Springer <jps@s390x.com>
@jonpspri
jonpspri force-pushed the feat/5905-trust-mode-acceptance-suite branch from 925f8c2 to b53c5c6 Compare September 12, 2026 21:11
@jonpspri

Copy link
Copy Markdown
Collaborator Author

Requirement note (remediation) — real-path tests

The dispatch matrix and the role-merge flow now drive the real ingress end to end. The tests mock the JWKS fetch only.

Proof: with the ingress dispatch removed, 9 of 16 converted tests fail. The old patched tests caught none. See the note on #5905.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trust-mode acceptance suite - no-DB proof, multi-worker revocation, config-flip, UAID

1 participant