Repository navigation
feat(plugin-auth): the SSO Providers form sets, shows and clears the groups claim - #31755
Conversation
…ups claim Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
… client secret; pins for the form groups claim Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
…s-claim actions Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
… in ten locales; changeset names the update-provider fix Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
…unts Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 44 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 11 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 24 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 96c5dbba8cc7814cb4020ab9e86d514cc361189b && git checkout 96c5dbba8cc7814cb4020ab9e86d514cc361189b
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6be71abc34cdd7adcc6057d121479ba5a11724e3 26b9e907ba78ffdd6ddfb7fe1bf0cdd690f4c2e6 && git checkout -B drift-repro 6be71abc34cdd7adcc6057d121479ba5a11724e3 && git merge --no-ff 26b9e907ba78ffdd6ddfb7fe1bf0cdd690f4c2e6
node scripts/docs-audit/affected-docs.mjs --json 6be71abc34cdd7adcc6057d121479ba5a11724e3
|
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi 席位 ACCEPT · head
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 42562448270 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
…unts The platform-admin standing sweep admits get-groups-claim and set-groups-claim (ADR-0068 gate first; the probe provider is unknown, so the admin lands on 404 and nothing moves). The non-admin refusal sweep records them in the objectstack-gate bucket with the same probe bodies. Claude-Session: https://claude.ai/code/session_01ADHkmQznRNHwfSHVJJUCj2 Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi 席位 ACCEPT (delta) · head
|
Requested by Steve · project thread
Fixes #31666
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
/sso/register完全相同:mapping.extraFields.groups。oidcConfig的 update-provider 对密钥存于sys_secret的 OIDC 提供方一律返回 400)。回滚即 revert 本 PR。packages/plugins/plugin-auth/src/register-sso-provider.ts末尾的两个桥,以及objectql-adapter.ts里update回显的密钥注入。pnpm --filter @objectstack/plugin-auth exec vitest run src/sso-oidc-refusal-cause.test.ts src/register-sso-provider.test.ts src/admin-sso-bridge-gate.test.ts。What changes
The Setup → SSO Providers form is metadata (
sys_sso_provideractions with flat params that post to plugin-auth/admin/sso/*bridges), not console code, so the whole change sits in plugin-auth and platform-objects. Nothing underui/orpackages/spec/**changes.mapping.extraFields.groups, exactly wherePOST /api/v1/auth/sso/registerstores it. Blank stores noextraFields, as today.POST /api/v1/auth/admin/sso/get-groups-claim, which reads the stored row (system read,oidc_config/saml_configrecovered) and answers only{ providerId, groupsClaim }.POST /api/v1/auth/admin/sso/set-groups-claim. It merges the claim into the stored mapping (every other member kept verbatim), writes through the vendor's own/sso/update-provider, and reads the claim back. A blank claim deletesextraFields.groups(and an emptiedextraFields), so the provider signs users in exactly like one saved without it.gateAdminfirst, the platform-admin gate of the other/admin/ssobridges. A refused vendor update answers its status with the standard code for it and a fixed sentence; the vendor's body is never relayed, so no other config member (client secret, certificate, endpoints, other mapping members) can reach an answer.@better-auth/ssoreads the provider for/sso/update-providerthroughlockSSOProviderRow, which is an adapterupdate. That echo did not run the read half (injectClientSecretOnRead), so any update-provider carrying anoidcConfigfor a provider whose secret is held insys_secretwas refused 400 "clientSecret is required". The adapter'supdatenow carries the secret and the IdP-initiated column into its echo like every other provider read.content/docs/permissions/sso.mdxupdated, translations for ten locales.sso-group-projection.ts) is untouched.New public route names (
/admin/sso/get-groups-claim,/admin/sso/set-groups-claim) and action names (show_sso_groups_claim,edit_sso_groups_claim) follow the/admin/sso/*and*_sso_*house spelling; they are the seat's call. No new error code: 400 VALIDATION_ERROR / INVALID_REQUEST, 404 RESOURCE_NOT_FOUND, 401/403 from the existing gate.Why
Since #31657 an SSO sign-in grants the permission sets mapped to the provider's IdP groups, read from
mapping.extraFields.groups. Only the vendor register API could set that member (documented by #31664); an administrator working in Setup had no way to configure it.Verification
Clause-② measured: on base
POST /api/v1/auth/admin/sso/set-groups-claimandget-groups-claimare not mounted and the register forms carry no groups param; on head both routes answer a platform admin 200 and the register forms accept the new param. A widening only: no request accepted on main is now refused.Tests (all import
src):sso-oidc-refusal-cause.test.ts, new describe: a form save with a claim stores the register door's mapping and the next sign-in grants the mapped set (the feat(plugin-auth): an SSO sign-in grants the permission set its IdP group is mapped to #31657 pin shape). CONTROL: a blank claim writes nothing and grants nothing. CONTROL: Show on a register-door row answersgroups, then a clear removes it. Round trip on a rich mapping (register-door row and a row this action changed): every other member kept verbatim.register-sso-provider.test.ts: both register forms store the claim where the register door does (compared to a direct/sso/registerrow), CONTROL without it; the bridges' merge, clear, 404 and 400; a no-leak pin (client secret, certificate, client id, IdP URL, other mapping members never appear in any answer, including a vendor refusal carrying them; data keys are exactlyproviderIdandgroupsClaim).admin-sso-bridge-gate.test.ts: for both routes, anonymous 401 and member / org-admin / position-named-admin 403 with neither the store read nor the vendor called; platform admin 200.sso-client-secret-at-rest.test.ts③b: an update-provider carrying onlyoidcConfig.mappingis accepted and the secret still reads back.Ablations (each restored,
git diff HEADempty afterwards):expected 200 to be 401/403).update→ ③b and the round trip red (clientSecret is required …, 400).Runs: plugin-auth full suite 4431 passed, 10 skipped, 1 red (the route-ledger pin, which hand-lists the mounts); that file re-run green (10/10) after the pin named the two routes; platform-objects 120 files / 1933 tests green; typecheck green for both packages; lint (
eslint --no-inline-config) clean on both packages;check:i18n,check:i18n-coverageandcheck:auth-mount-ledgerOK; everydispatch-gates --commandscommand run and reconciled with--ran. Dogfood, run locally under the verify lock after the sweep fix: the whole@objectstack/dogfoodpackage 440 files passed, 3 skipped; 3653 tests passed, 41 skipped; 0 failed. The five admin suites together 5/5 files, 34/34 tests. Control: without the new entries the non-admin sweep is red at:618with the queue's signature.Forced test edits (existing tests changed because the surface grew):
sso-oidc-refusal-cause.test.ts:bootGroupEnginenow registers the auth object extensions andsys_secretand sets a fake crypto provider, so the harness holds a client secret the way production does.platform-objects.test.ts: two rows in the action table (show_sso_groups_claim,edit_sso_groups_claim).platform-admin-affordance-standing.test.ts: the same two actions on the floor list.auth-route-ledger.conformance.test.ts: the hand-written objectstack-mount pin names the two new routes.packages/qa/dogfood/test/admin-platform-admin-standing.dogfood.test.ts: the two routes joinADMITTED(ADR-0068 gate first) with probe bodies; the admin reaches the handler and gets 404 for the unknown probe provider, nothing moves.packages/qa/dogfood/test/admin-route-nonadmin-refusal.dogfood.test.ts: the two routes join theobjectstack-gatebucket with the same probe bodies (anonymous 401, member 403; admin measured 404 RESOURCE_NOT_FOUND).Both dogfood sweeps derive the served
/admin/route set and refuse an unclassified mount; PR CI does not run the dogfood shards, so the merge queue caught them (run 42562448270, Dogfood 3/3; then Dogfood 1/3). Census ofpackages/qa/dogfood/test: only these two files derive the/admin/set.admin-credential-lifecycle,admin-identity-audit-trailandadmin-ledger-decision-metadataname fixed routes and need no registration.Out of scope, noted: ADR-0011's "Not assessed" list could name the two new actions (governed surface, left alone); the cloud auth proxy must mount the two new routes to serve them there.
Human Minimum
packages/specholds no runtime logic.pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.## 维护者速读section is Chinese by the dispatch order.)Generated by Claude Code