Repository navigation
Commit 69df290
fix(app-shell): the permission matrix's object rows show exactly what the runtime enforces for every grant shape (#27051)
Fixes #27010
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:权限集编辑器(Studio 访问、Setup
权限集)的对象行,改为显示运行时对该对象实际生效的对象授权;继承而来的授权用与字段行相同的灰色虚线框标出,并有提示和图例说明来源。在这类行上勾选、选记录范围或用行快捷按钮时,写入的对象条目从该行显示的内容开始。“只看已授权”、已授权对象计数、委派管理员的配套授权列表按同一读法。
- 为什么改:对象行对部分授权形态的显示与运行时执行不一致,管理员按屏幕操作可能在看不到的情况下改变实际权限(复现细节按维护者指示不公开)。
- 风险与回滚:只改界面(app-shell、i18n 文案);运行时与 spec
均未改动,运行时一侧只新增一个固定现有行为的测试。回滚即还原本 PR。可见变化:继承授权的对象行显示为灰色虚线框。
- 先看:`permission-slice.ts` 的 `objectRuleCell` /
`objectRuleSeed`(显示与写入起点的唯一来源);`PermissionMatrixEditor.tsx` 的
`objectEditSeed` 各调用点与 `ObjectRuleBox`;`PermissionAdvancedFacets.tsx` 的
`companionRow`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/views/metadata-admin/permission-slice.objectGrant-27010.test.ts`,预期
17 passed。
- 需要你做的:无,席位落地。
## What changes
The permission matrix's object rows show exactly what the runtime
enforces for every grant shape.
- **One reading, one source**
(`ui/packages/app-shell/src/views/metadata-admin/permission-slice.ts`):
- `objectRuleCell(objects, objectName, accessDefault)` is what an object
row draws: the object entry the runtime applies, resolved the way
`resolveObjectPermission` (`@objectstack/plugin-security`, ADR-0066 D2)
resolves it, with the object's `access.default` read from the catalog
the rows are listed from. `inherited` marks a row whose grant comes from
outside its own entry.
- `objectRuleSeed` is what an edit of a row starts from: the drawn
entry, as a fresh copy.
- `scopePermissionSet` keeps, beside the package's own rows, the one
entry the rows need for display; `mergePermissionSlice` still takes
everything outside the package's objects from the fresh base (pinned).
- **The editor** (`PermissionMatrixEditor.tsx`): both renderings (the
routed page, which is also the Setup catalog's editor, and the Studio
Access Objects tab) draw each row through `objectRuleCell`, box by box
in `ObjectRuleBox`. An inherited box wears the grey dashed look
inherited field rows already use (now one shared class), with a tooltip
and a legend line; the row's scope pickers are marked the same way.
Every row edit (a box, a scope pick, a row shortcut) and every field-row
edit under it starts from the drawn entry (`objectEditSeed`,
`drawnObjectPerm`). "Only granted" and the granted-object count read the
rows as drawn.
- **Delegated-admin companions** (`PermissionAdvancedFacets.tsx`): the
companion list and its one-click grant read a companion object's grant
through the same `objectRuleCell`, with the posture the editor passes
from its catalog (`objectAccessDefault`).
- **Strings** (`@object-ui/i18n`, `studio`):
`perm.object.inherited.legend` and `perm.object.inherited.tip`, en and
zh.
- **Runtime: no change.** The editor follows `resolveObjectPermission`.
The runtime half is pinned by a new test,
`packages/plugins/plugin-security/src/object-grant-wildcard.test.ts`,
whose table is the editor pin's table.
## Tests
- `permission-slice.objectGrant-27010.test.ts` (17): every row of the
table against the runtime's verdicts, the posture default, the seed, and
the slice and merge.
- `PermissionMatrixEditor.objectGrantInherited-27010.test.tsx` (20): the
rendered boxes, pickers, legend, filter and count on both renderings,
the companion wiring, and the saved payload after a click, a scope pick,
a row shortcut, a field click and a package-door autosave.
- `PermissionAdvancedFacets.companionWildcard-27010.test.tsx` (4): the
companion marks and the one-click grant.
- `object-grant-wildcard.test.ts` in plugin-security (20): the same
table through a real `ObjectQL` over SQLite with the real
`SecurityPlugin` middleware, posture read from the registered object;
refusals asserted on `code` + `status`, a written update read back from
storage.
- Ablation (one-time, not kept, at `60ed127c`): `objectRuleCell`'s
inherited branch mutated through `scripts/ablation-replace.mjs` (anchor
hit 1 → 0, blob `8ade369f` → `87539ee9`); 25 of the 41 new editor-side
cases went red; restored to the HEAD blob with `git diff HEAD` empty. No
rebuild was needed: the tests import the module by relative path.
## Acceptance notes
- **Surveyed surfaces.** The Setup permission-set editor is this
component and is covered. The other surfaces that draw a set's object
grants were surveyed; the findings went to the coordinator in the
private report.
- **Not in this PR (product decision, reported to the coordinator).**
The matrix has no control to view or edit the entry inherited rows draw
from, nor one to return an edited row to inheriting. Neither is needed
for this card's acceptance.
- **File surface.** The claim named the object rows and
`permission-slice.ts`. This PR also changes
`PermissionAdvancedFacets.tsx` (same directory, same reading) and adds a
test-only file in plugin-security, as the dispatch asked.
- `@object-ui/app-shell` is private; the changeset names it with
`@object-ui/console` (which ships it) and `@object-ui/i18n`, as the
other app-shell changesets do.
- `test:repo` (the vitest `repo` project) was not run locally: no
package in this diff declares it, and CI's `Test Core` runs it.
- `origin/main` moved four commits past the base during this work; none
touches a file in this diff, so the branch was not merged. The merge
queue tests the combined tree.
- Docs drift (noted, not filed):
`content/docs/permissions/administrator-guide.mdx` Step 5's object-grid
bullet still lists Restore and Purge, both retired, and omits Export.
## Verification
All readings at `d779ee6d` (this branch, base `a90163d4`), unless a line
says otherwise.
<details><summary>Builds and type-checks</summary>
- `pnpm turbo run build --concurrency=2
--filter='@object-ui/app-shell^...'
--filter='@objectstack/plugin-security^...'`: exit 0 (63 tasks).
- `pnpm --filter @object-ui/app-shell run type-check` (`tsc --noEmit`,
then `tsc -p tsconfig.test.json`, whose file list carries the three new
test files): exit 0.
- `pnpm --filter @object-ui/i18n run type-check`: exit 0.
- `pnpm --filter @objectstack/plugin-security run typecheck` (with
`check:test-typecheck`): exit 0.
</details>
<details><summary>Tests</summary>
- `pnpm --dir ui exec vitest run --maxWorkers=2` over metadata-admin
`PermissionMatrixEditor*`, `permission-slice*`,
`PermissionAdvancedFacets*`, `previews/PermissionPreview*`,
`previews/adminPreviews.i18n-10862-s2.test.tsx`,
`PositionPermissionSetsSection.test.tsx`,
`studio-design/StudioDesignSurface.accessTabs-12456.test.tsx` and
`@object-ui/i18n`'s `studio-namespace.test.ts`: Test Files 51 passed
(51), Tests 429 passed (429), exit 0.
- `pnpm --filter @objectstack/plugin-security exec vitest run
--maxWorkers=2 src/object-grant-wildcard.test.ts`: Test Files 1 passed
(1), Tests 20 passed (20), exit 0.
- Declared narrowing: the rest of the app-shell suite is CI's (`Test
UI`).
</details>
<details><summary>Gates</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived 66 families for the
10-path change set. All 66 ran locally. 64 exited 0 on the first run;
`check:dual-build-cjs-loads` and `check:i18n` exited 3 (prerequisite not
met: unbuilt `dist/`) and exited 0 once their prerequisite builds ran.
`--ran`: "66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN".
- `ui/package.json` gates `check:i18n-keys`, `check:i18n-drift`,
`check:i18n-dead-keys`, `check:i18n-designer-parity`,
`check:designer-field-key-parity`, `check:new-line-citations` (0 new
citations) and `check:control-bytes`: exit 0 each.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01J8VVAkZns5rNnK2rtszzbQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2713377 commit 69df290
10 files changed
Lines changed: 1018 additions & 41 deletions
File tree
- .changeset
- packages/plugins/plugin-security/src
- ui/packages
- app-shell/src/views/metadata-admin
- i18n/src/namespaces/studio
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
0 commit comments