Repository navigation
Commit d36d4ad
feat(plugin-auth): a SCIM Credentials Setup entry while SCIM is wired (#31222)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XYauubomxzQiKW9cdVCunsZ)_
Fixes #31123
Clause-②: yes (widening)
Metadata-protocol: none
## 维护者速读
- **改了什么**:开启 SCIM 的部署里,Setup → 访问控制 下多出一个「SCIM Credentials」菜单项,打开 SCIM
连接凭据列表(上面就是已上线的「签发凭据 / 吊销凭据」按钮)。没开 SCIM 时不出现。
- **为什么改**:签发和吊销按钮已经上线,但菜单里找不到入口,管理员只能手敲 URL 才能配置 Okta / Entra ID 的 SCIM
对接。
- **风险与回滚**:只加一个菜单项和六种语言的标签,权限沿用 SSO 菜单项(平台管理员),签发/吊销接口自身的权限不变;回滚即
revert 本 PR。
-
**先看**:`packages/plugins/plugin-auth/src/auth-plugin.ts`(菜单项与开关,复用签发接口的同一个
SCIM
判定);`packages/plugins/plugin-auth/src/scim-credentials-setup-nav.test.ts`;`packages/platform-objects/src/apps/translations/en.ts`。
- **两分钟验证**:`pnpm --filter @objectstack/plugin-auth exec vitest run
src/scim-credentials-setup-nav.test.ts`,预期 6 个用例全绿(开 SCIM 有入口、关 SCIM
无入口)。
- **需要你做的**:无,席位落地。
## What changes
- **plugin-auth** builds its Setup > Access Control items from the two
capabilities it owns. "SSO Providers" is unchanged (still only while
`isSsoWired()`). A new item `nav_scim_credentials`, "SCIM Credentials",
opens `sys_scim_connection_credential` (the object PR #31119's Mint /
Revoke Credential actions live on), with icon `key-round` and the SSO
entry's gate, `requiredPermissions: ['manage_platform_settings']`. It is
contributed only while `this.authManager.isScimWired()` is true. That is
the method the `/admin/scim/mint-credential` door's `isScimEnabled` dep
calls before it refuses 503, so there is one spelling of "SCIM wired"
and the menu cannot show an entry whose Mint button can only fail. The
routes keep their own platform-admin gate, so the entry widens nothing.
- The contribution object is still spread in only when at least one item
is wired, so a deployment with neither SSO nor SCIM registers no
`navigationContributions` from plugin-auth, as before. Both items share
the one existing contribution (group `group_access_control`, priority
250); SCIM sorts after SSO.
- **platform-objects**: the label
`apps.setup.navigation.nav_scim_credentials` in all six Setup bundles
(en "SCIM Credentials", zh-CN "SCIM 凭据", ja-JP "SCIM 資格情報", es-ES
"Credenciales SCIM", zh-Hant "SCIM 憑證", ar "بيانات اعتماد SCIM"), each
the short form of the object's own translated `pluralLabel`. The five
non-English locales record the en source hash (`2d94a32c33ca2957`) so a
later source edit marks them stale. The id joins
`CONDITIONAL_SETUP_NAV_IDS` in `setup-nav-dead-key-tombstone.test.ts`,
because `check:app-nav-i18n` boots one composition and never merges a
gated-off entry (the same reason `nav_sso_providers` is listed there).
- Clause-② measured: the bundle ships in platform-objects' `dist`
(`files: ["dist", …]`) and the key is new, so `yes (widening)`; the
claim's seat reading `no` is re-declared as the order asked. Changeset
`.changeset/31123-scim-credentials-setup-entry.md`, patch for both
packages.
## Tests
- New
`packages/plugins/plugin-auth/src/scim-credentials-setup-nav.test.ts`
reads the manifest `init()` registers: the entry with SCIM on via
config, with SCIM on via `OS_SCIM_ENABLED`, absent with SCIM off (and no
other entry in its place), absent when the manager's `isScimWired()`
answers false although the config says on, the SSO + SCIM order, and SSO
alone unchanged.
- Red first on `origin/main` `658d84f5`: 4 failed, 2 passed (the two
absence cases already hold on main).
- Ablation of the negative pins: replacing
`this.authManager.isScimWired()` with an always-true marker turned 3
cases red ("is absent when SCIM is not wired…", "asks the manager the
same question…", "SSO alone…"), restored with blob == HEAD and `git diff
HEAD` empty. Removing the ja-JP label turned the tombstone pin red
(`expected [ 'nav_scim_credentials' ] to deeply equal []`), restored the
same way.
## Acceptance notes
- Wording "SCIM Credentials" is the card's. The other locales shorten
the object's `pluralLabel` the same way English does.
- Comments outside this claim's file surface still say plugin-auth's
"only nav contribution (`nav_sso_providers`)" is conditional:
`packages/cli/scripts/check-app-nav-i18n.mjs` header,
`packages/cli/test/platform-page-i18n-parity.test.ts`,
`packages/qa/dogfood/test/platform-app-object-entry-views.test.ts`.
Their reasoning still holds (every plugin-auth nav entry is
conditional); only the count is now two. Not edited here.
- `check:app-nav-i18n` is not in the derived list and has no reverse
direction, so the new bundle key draws no orphan verdict; not run
locally.
## Verification
All local readings are at head `bb431ed4`.
- `pnpm --filter @objectstack/plugin-auth test`: Test Files 235 passed
(235), Tests 4174 passed | 10 skipped (4184).
- `pnpm --filter @objectstack/platform-objects test`: Test Files 119
passed (119), Tests 1701 passed (1701).
- `pnpm --filter @objectstack/plugin-auth typecheck` (tsc, examples
tsconfig, `check:test-typecheck`) and `pnpm --filter
@objectstack/platform-objects typecheck`: exit 0.
- `eslint --no-inline-config packages/plugins/plugin-auth/src
packages/platform-objects/src`: exit 0.
- Derived gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` gives 68 families; all 68 ran with exit 0.
`--ran` reconciliation: "68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN".
<details><summary>Three gates that first answered exit 3 (prerequisite
not met), and their re-runs</summary>
- `node scripts/check-plugin-teardown-shape.mjs --self-test` needed the
shallow clone deepened (`git fetch --depth=1 origin 621a487`); re-run
exit 0, "48 cases pass".
- `pnpm check:dual-build-cjs-loads` and `pnpm check:i18n` needed a full
build (`turbo run build`, 120/120 tasks); re-run exit 0: "116 published
require entry point(s) across 71 package(s) load" and
"check-i18n-bundles: OK (10 package(s) — all bundles in sync, no
undeclared authoring keys)".
</details>
Not run locally, left to CI: `test:repo`, the dogfood battery (no `!` in
the title and no `(narrowing)` arm), and `check:app-nav-i18n` (not
derived; see Acceptance notes).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01FEkQ5kkU448zCPf5uHdBMT
---
_Generated by [Claude
Code](https://claude.ai/code/session_01FEkQ5kkU448zCPf5uHdBMT)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent df22adc commit d36d4ad
15 files changed
Lines changed: 209 additions & 18 deletions
File tree
- .changeset
- packages
- platform-objects/src/apps/translations
- plugins/plugin-auth/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| 103 | + | |
103 | 104 | | |
104 | 105 | | |
105 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
74 | 77 | | |
75 | 78 | | |
76 | 79 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
112 | 119 | | |
113 | 120 | | |
114 | 121 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
74 | 77 | | |
75 | 78 | | |
76 | 79 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
74 | 77 | | |
75 | 78 | | |
76 | 79 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
190 | 190 | | |
191 | 191 | | |
192 | 192 | | |
193 | | - | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
194 | 196 | | |
195 | 197 | | |
196 | 198 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
| |||
0 commit comments