Plan the focused role and permission implementation
Why: The approved scope spans Backend authority, RDM project enforcement, and Frontend configuration and needs exact contracts and verification boundaries. Evidence: The plan was checked against every specification section, fixed interface names, failure cases, and placeholder rules. Impact: Implementation is limited to one effective role, system and project authorization, and the requested configuration views. Refs: Focused role and permission system spec
This commit is contained in:
@@ -0,0 +1,393 @@
|
||||
# Focused Role and Permission System Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Deliver the requested single-effective-role position model and enforce system, role, and per-project permissions from one server-side action catalog.
|
||||
|
||||
**Architecture:** Backend owns roles, the single active user binding, the permission catalog, system grants, and role defaults/caps. RDM consumes that public authority while retaining project membership, project overrides, creator ownership, and existing document duties. Frontend uses Backend role APIs for the single-role and matrix views and uses RDM only for project-scoped configuration.
|
||||
|
||||
**Tech Stack:** Go 1.27, SQLite, PostgreSQL, HTTP JSON APIs, compiled extension Host contracts, React 19, TypeScript, Vitest, Playwright.
|
||||
|
||||
**Spec:** `docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- One user has at most one effective role; one role may be assigned to many users.
|
||||
- Role and user stable IDs never change when display data changes.
|
||||
- System administrators do not gain implicit access to project business content.
|
||||
- A project creator can manage only the project they created.
|
||||
- Unknown actions, missing providers, stale versions, disabled accounts, and disabled roles fail closed.
|
||||
- Role defaults never exceed role caps; project allows never exceed role or platform caps.
|
||||
- UI configuration uses a draft, preview, and atomic batch commit; hidden rows do not lose draft edits.
|
||||
- Account recovery, multi-role bindings, Agent permissions, workflow redesign, production migration, and deployment remain outside this plan.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- Reassigning a user while two administrators submit concurrently must leave exactly one active binding and return one conflict.
|
||||
- Disabling a role between preview and commit must reject the commit without partial writes.
|
||||
- A project creator switching from project A to B must not retain A-derived management authority in B.
|
||||
- A filtered matrix batch must contain explicit action IDs and must not apply to hidden or newly registered actions.
|
||||
- RDM provider absence or version mismatch must deny authorization and must not fall back to the legacy local role table.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Backend action catalog and role schema
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/catalog.go`
|
||||
- Create: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Modify: `Agent-System-Backend/internal/store/migrate.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_migration_test.go`
|
||||
- Test: `Agent-System-Backend/internal/authz/catalog_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `authz.Descriptor`, `authz.DescriptorByID(string) (Descriptor, bool)`, `store.RoleRecord`, `store.RoleBinding`, and schema v35 tables.
|
||||
- Consumes: existing `users(id, owner, enabled, state_version)` and migration transaction machinery.
|
||||
|
||||
- [ ] **Step 1: Add failing catalog tests**
|
||||
|
||||
```go
|
||||
func TestCatalogRejectsUnknownAndProtectsManagementActions(t *testing.T) {
|
||||
if _, ok := DescriptorByID("invented.action"); ok { t.Fatal("unknown action registered") }
|
||||
got, ok := DescriptorByID("system.roles.manage")
|
||||
if !ok || got.Scope != ScopeSystem || !got.Protected { t.Fatalf("descriptor=%+v", got) }
|
||||
create, ok := DescriptorByID("project.create")
|
||||
if !ok || create.Scope != ScopeSystem || create.Protected { t.Fatalf("project.create=%+v", create) }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused test and confirm the missing package failure**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz -run TestCatalogRejectsUnknownAndProtectsManagementActions -count=1`
|
||||
Expected: FAIL because `internal/authz` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the fixed action catalog**
|
||||
|
||||
```go
|
||||
type Scope string
|
||||
const ( ScopeSystem Scope = "system"; ScopeProject Scope = "project" )
|
||||
type Descriptor struct { ActionID, Label, Description, Group string; Scope Scope; Protected bool }
|
||||
func DescriptorByID(id string) (Descriptor, bool)
|
||||
func Catalog() []Descriptor
|
||||
```
|
||||
|
||||
Register only the system and project actions listed in the spec. Return copies so callers cannot mutate the registry.
|
||||
|
||||
- [ ] **Step 4: Add schema v35 and migration tests for both dialects**
|
||||
|
||||
Create `roles`, `role_permissions`, `user_role_bindings`, `system_grants`, `authority_state`, and `policy_receipts`. Enforce one active binding per user with a unique `user_id` key; preserve inactive history through versioned replacement rows in the same transaction. Use the existing boolean representation per dialect and foreign keys to `users(id)`.
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestRolePolicyMigration|TestMigrate' -count=1`
|
||||
Expected: PASS on SQLite; the existing PG migration suite later verifies PostgreSQL.
|
||||
|
||||
- [ ] **Step 5: Commit the schema and catalog**
|
||||
|
||||
```bash
|
||||
git add internal/authz/catalog.go internal/authz/catalog_test.go internal/store/role_policy.go internal/store/role_policy_migration_test.go internal/store/migrate.go
|
||||
git commit -m "Add the role permission catalog and schema"
|
||||
```
|
||||
|
||||
### Task 2: Atomic role, binding, and permission store
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/internal/store/role_policy.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_test.go`
|
||||
- Test: `Agent-System-Backend/internal/store/role_policy_pg_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `authz.DescriptorByID`, current account state, store transaction helpers.
|
||||
- Produces:
|
||||
- `CreateRole(actor string, in RoleCreate) (RoleRecord, error)`
|
||||
- `PatchRole(actor, roleID string, expected int64, patch RolePatch) (RoleRecord, error)`
|
||||
- `ReplaceRolePermissions(actor, roleID string, expected int64, changes []RolePermissionChange, key string) (PolicyReceipt, error)`
|
||||
- `AssignUserRole(actor, userID, roleID string, expectedState int64, key string) (RoleBinding, error)`
|
||||
- `RoleForUser(userID string) (RoleBindingView, error)`
|
||||
- `ReplaceSystemGrants(actor, userID string, expectedEpoch int64, actions []string, key string) (PolicyReceipt, error)`
|
||||
|
||||
- [ ] **Step 1: Write failing transaction and concurrency tests**
|
||||
|
||||
```go
|
||||
func TestAssignUserRoleKeepsOneEffectiveBinding(t *testing.T) {
|
||||
first := mustAssign(t, st, admin, userID, engineerID, 1, "assign-1")
|
||||
second := mustAssign(t, st, admin, userID, reviewerID, first.UserStateVersion, "assign-2")
|
||||
if second.RoleID != reviewerID { t.Fatalf("binding=%+v", second) }
|
||||
if got := countActiveBindings(t, st, userID); got != 1 { t.Fatalf("active=%d", got) }
|
||||
}
|
||||
```
|
||||
|
||||
Also test two concurrent replacements with the same expected state, duplicate idempotency keys, disabled users, disabled roles, unknown actions, `allowed=true/capAllowed=false`, stale role versions, a multi-change batch containing one invalid item, and rejection when a system-grant batch removes the final effective `system.permissions.manage` holder.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm missing methods fail**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/store -run 'TestAssignUserRole|TestReplaceRolePermissions' -count=1`
|
||||
Expected: FAIL with undefined store methods.
|
||||
|
||||
- [ ] **Step 3: Implement transaction-scoped mutations**
|
||||
|
||||
Lock the actor, target account, role, and `authority_state` in the existing account-before-policy lock order. Validate the complete batch before any write. Increment `role_version` and `authority_epoch` once per successful transaction and persist one `policy_receipts` row per idempotency key.
|
||||
|
||||
- [ ] **Step 4: Verify SQLite and PostgreSQL behavior**
|
||||
|
||||
Run SQLite: `GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Run PostgreSQL through the isolated PG harness after it creates the mode-0600 DSN file: `AGENT_SYSTEM_TEST_PG_DSN_FILE=/tmp/iam-u4-role-policy-pg-dsn GOWORK=off go test ./internal/store -run 'Role|Policy|Binding' -count=1`
|
||||
Expected: both pass with identical conflict and rollback assertions.
|
||||
|
||||
- [ ] **Step 5: Commit the store service**
|
||||
|
||||
```bash
|
||||
git add internal/store/role_policy.go internal/store/role_policy_test.go internal/store/role_policy_pg_test.go
|
||||
git commit -m "Add atomic role and binding policies"
|
||||
```
|
||||
|
||||
### Task 3: Backend authorization service and HTTP API
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Backend/internal/authz/service.go`
|
||||
- Create: `Agent-System-Backend/internal/server/roles.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/server.go`
|
||||
- Modify: `Agent-System-Backend/internal/server/accounts.go`
|
||||
- Test: `Agent-System-Backend/internal/server/roles_test.go`
|
||||
- Test: `Agent-System-Backend/internal/server/role_authorization_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 2 store methods and existing authenticated actor resolution.
|
||||
- Produces: the HTTP endpoints in the spec and `Service.AuthorizeSystem(actor, actionID string) Decision`.
|
||||
|
||||
- [ ] **Step 1: Write failing authorization and HTTP tests**
|
||||
|
||||
```go
|
||||
func TestRoleManagerCannotGrantProtectedPermission(t *testing.T) {
|
||||
cookie := loginAs(t, h, "role-manager")
|
||||
rec := do(h, "PUT", "/api/admin/roles/"+roleID+"/permissions",
|
||||
`{"expectedRoleVersion":1,"idempotencyKey":"p1","changes":[{"actionId":"system.permissions.manage","allowed":true,"capAllowed":true}]}`, cookie)
|
||||
if rec.Code != http.StatusForbidden { t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String()) }
|
||||
}
|
||||
```
|
||||
|
||||
Cover role description create/edit, exact and fuzzy role search, status/action filtering, pagination, one role assigned to two users, user reassignment, stale versions, fixed-ID batches, disabled role assignment, system-grant listing/replacement, final permission-manager protection, and account profile fields remaining outside authorization.
|
||||
|
||||
- [ ] **Step 2: Run tests and confirm routes are absent**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/server -run 'Role|PermissionCatalog|UserRole' -count=1`
|
||||
Expected: FAIL with 404 or missing handlers.
|
||||
|
||||
- [ ] **Step 3: Implement service authorization and handlers**
|
||||
|
||||
Protected actions require explicit `system_grants`; `project.create` may also use the current enabled role permission. Provide `GET/PUT /api/admin/users/{userId}/system-grants` with expected epoch and idempotency key. Replace `requireAdmin` only for the new role endpoints in this task; existing unrelated admin endpoints remain unchanged until their explicit action is wired in Task 4.
|
||||
|
||||
- [ ] **Step 4: Wire account management actions without changing recovery**
|
||||
|
||||
Map account list/create/profile/state handlers to `system.users.view/create/edit/disable`. Do not touch recovery routes or credential behavior. Preserve first-admin bootstrap as the protected root recovery path.
|
||||
|
||||
- [ ] **Step 5: Run server and full Backend tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/authz ./internal/server ./internal/store -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 6: Commit the Backend API**
|
||||
|
||||
```bash
|
||||
git add internal/authz/service.go internal/server/roles.go internal/server/server.go internal/server/accounts.go internal/server/roles_test.go internal/server/role_authorization_test.go
|
||||
git commit -m "Expose role and system permission APIs"
|
||||
```
|
||||
|
||||
### Task 4: Public role provider for RDM
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Backend/pkg/extension/extension.go`
|
||||
- Modify: `Agent-System-Backend/internal/app/hostcaps.go`
|
||||
- Test: `Agent-System-Backend/internal/app/role_provider_test.go`
|
||||
- Create: `Agent-System-RDM/internal/perms/role_provider.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/role_provider_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces public `extension.RolePolicyProvider` methods `RolePolicyForActor(actor string)`, `RolePolicyForUser(userID string)`, and `CheckRoleAction(userID, actionID string)` returning versioned, non-secret snapshots.
|
||||
- Consumes the actual request actor from the trusted Host; caller-supplied actor fields never select authority.
|
||||
|
||||
- [ ] **Step 1: Add provider contract tests on both sides**
|
||||
|
||||
Backend tests verify stable user IDs, enabled state, role version, action default/cap, authority epoch, and rejection when the Host has no authenticated actor. RDM tests verify provider missing, wrong version, disabled role, and unknown action all deny without consulting the legacy tables.
|
||||
|
||||
- [ ] **Step 2: Run the contract tests red**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./internal/app -run RoleProvider -count=1`
|
||||
Run RDM: `GOWORK=off go test ./internal/rdmapp -run RoleProvider -count=1`
|
||||
Expected: FAIL with missing public provider.
|
||||
|
||||
- [ ] **Step 3: Implement the public interface and RDM adapter**
|
||||
|
||||
The provider returns only role and permission metadata needed for authorization. It must not return user contact fields. RDM's evaluator must use the provider whenever a role binding exists; absence is a denial after migration readiness is enabled.
|
||||
|
||||
- [ ] **Step 4: Run both component suites**
|
||||
|
||||
Run Backend: `GOWORK=off go test ./... -count=1`
|
||||
Run RDM with the reviewed Backend module overlay during development: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS; overlay evidence remains SELF until a trusted Backend tag is published.
|
||||
|
||||
- [ ] **Step 5: Commit each repository separately**
|
||||
|
||||
Backend commit: `Expose the role policy provider`.
|
||||
RDM commit: `Consume the Backend role authority`.
|
||||
|
||||
### Task 5: RDM migration adapter and creator-only project management
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-RDM/internal/perms/role_migration.go`
|
||||
- Modify: `Agent-System-RDM/internal/perms/roles.go`
|
||||
- Modify: `Agent-System-RDM/internal/rdmapp/perms_api.go`
|
||||
- Modify: `Agent-System-RDM/plugins/rdm/plugin.go`
|
||||
- Test: `Agent-System-RDM/internal/perms/role_migration_test.go`
|
||||
- Test: `Agent-System-RDM/internal/rdmapp/project_creator_permissions_test.go`
|
||||
- Test: `Agent-System-RDM/plugins/rdm/roles_test.go`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 4 provider and Backend import endpoint/command.
|
||||
- Produces a versioned export manifest for existing RDM roles and a compatibility forwarding layer for `rdm.role.*` writes.
|
||||
|
||||
- [ ] **Step 1: Write migration and cross-project denial tests**
|
||||
|
||||
```go
|
||||
func TestCreatorCannotManageAnotherCreatorsProject(t *testing.T) {
|
||||
projectA := createProjectAs(t, "alice")
|
||||
projectB := createProjectAs(t, "bob")
|
||||
requireAllowed(t, setProjectPermission("alice", projectA, "carol", ActDocRead, "allow"))
|
||||
requireDenied(t, setProjectPermission("alice", projectB, "carol", ActDocRead, "allow"))
|
||||
}
|
||||
```
|
||||
|
||||
Also assert role IDs, descriptions, bindings, defaults, caps, disabled state, and counts survive export/import; once provider readiness is set, legacy local writes and fallback reads are rejected.
|
||||
|
||||
- [ ] **Step 2: Implement export and forwarding without production migration**
|
||||
|
||||
Add dry-run export and compare functions. Forward `rdm.role.create/action/assign/disable/list` to Backend through the trusted Host. Keep old tables read-only for verification; do not delete them.
|
||||
|
||||
- [ ] **Step 3: Tighten project management to immutable creator ownership**
|
||||
|
||||
For member, permission, and duty configuration, load the actual project and compare its immutable `created_by` to the authenticated actor before applying role and platform caps. Do not add delegated project-manager behavior.
|
||||
|
||||
- [ ] **Step 4: Run RDM tests**
|
||||
|
||||
Run: `GOWORK=off go test ./internal/perms ./internal/rdmapp ./plugins/rdm -count=1`
|
||||
Run: `GOWORK=off go test ./... -count=1`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Commit the RDM consumer**
|
||||
|
||||
```bash
|
||||
git add internal/perms/role_migration.go internal/perms/roles.go internal/rdmapp/perms_api.go plugins/rdm/plugin.go internal/perms/role_migration_test.go internal/rdmapp/project_creator_permissions_test.go plugins/rdm/roles_test.go
|
||||
git commit -m "Move RDM role writes to the Backend authority"
|
||||
```
|
||||
|
||||
### Task 6: Frontend role API and shared draft model
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Frontend/src/api.ts`
|
||||
- Create: `Agent-System-Frontend/src/rolePermissions.ts`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Backend HTTP endpoints from Task 3.
|
||||
- Produces `RoleRecord`, `PermissionDescriptor`, `RolePermissionDraft`, `mergeRoleDraft`, `visiblePatch`, and typed API methods.
|
||||
|
||||
- [ ] **Step 1: Write failing draft-model tests**
|
||||
|
||||
Test that filtered rows retain edits, `visiblePatch` includes only explicit action IDs, single-role and matrix edits produce identical changes, stale server refresh does not replace a newer draft generation, and `allowed=true/capAllowed=false` is rejected before submit.
|
||||
|
||||
- [ ] **Step 2: Implement the typed API and pure draft helpers**
|
||||
|
||||
```ts
|
||||
export type RolePermissionValue = { allowed: boolean; capAllowed: boolean }
|
||||
export type RolePermissionDraft = Record<string, RolePermissionValue>
|
||||
export function visiblePatch(draft: RolePermissionDraft, visibleActionIds: string[]): RolePermissionChange[]
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run tests and build**
|
||||
|
||||
Run: `npx vitest run test/role-permissions.test.ts`
|
||||
Run: `npm run build`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Commit the client contract**
|
||||
|
||||
```bash
|
||||
git add src/api.ts src/rolePermissions.ts test/role-permissions.test.ts
|
||||
git commit -m "Add the role permission client model"
|
||||
```
|
||||
|
||||
### Task 7: Single-role and matrix configuration UI
|
||||
|
||||
**Files:**
|
||||
- Create: `Agent-System-Frontend/src/components/RolePermissionsPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AdminZone.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/components/AccountUsersPanel.tsx`
|
||||
- Modify: `Agent-System-Frontend/src/styles.css`
|
||||
- Test: `Agent-System-Frontend/test/role-permissions-ui.test.tsx`
|
||||
- Test: `Agent-System-Frontend/test/design-guard.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes Task 6 API and draft helpers plus existing `@ui` components and tokens.
|
||||
- Produces the “角色与权限” system-management section and one-role assignment in user details.
|
||||
|
||||
- [ ] **Step 1: Re-read Dev `DESIGN.md` and add failing interaction tests**
|
||||
|
||||
Cover exact/fuzzy role search, state filtering, description edit, binding count, single-role edit, matrix edit, grouped paging, hidden-row draft retention, diff preview, conflict preservation, one user role replacement, system-grant editing with final-manager protection, and a late response discarded after switching roles.
|
||||
|
||||
- [ ] **Step 2: Implement the panel using existing design tokens**
|
||||
|
||||
Use `Collapsible` from `@ui`; consume `@ui/tokens.css` and `@ui/motion.css`; do not add raw colors, private keyframes, `<details>`, or inline font sizes. Keep one request sequence owner for list/detail/preview/commit results.
|
||||
|
||||
- [ ] **Step 3: Replace the RDM role admin entry**
|
||||
|
||||
Change the section label from “RDM 角色权限” to “角色与权限” and bind it to Backend APIs. Keep RDM project permission and document-duty components in project settings.
|
||||
|
||||
- [ ] **Step 4: Add the user role selector**
|
||||
|
||||
Display the current role and explicit system grants in account details. Allow role replacement only when the server advertises `system.roles.assign`; submit stable `userId`, `roleId`, expected state version, and idempotency key. Allow system-grant editing only with `system.permissions.manage`, using the server epoch and fixed action IDs.
|
||||
|
||||
- [ ] **Step 5: Run Frontend and design checks**
|
||||
|
||||
Run: `npm test`
|
||||
Run: `npm run build`
|
||||
Run paired UI: `npm test` in `Agent-System-UI`.
|
||||
Expected: all pass with no design-guard regression.
|
||||
|
||||
- [ ] **Step 6: Commit the UI**
|
||||
|
||||
```bash
|
||||
git add src/components/RolePermissionsPanel.tsx src/components/AdminZone.tsx src/components/AccountUsersPanel.tsx src/styles.css test/role-permissions-ui.test.tsx
|
||||
git commit -m "Add role and permission configuration views"
|
||||
```
|
||||
|
||||
### Task 8: Fixed-combination verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `Agent-System-Dev/modules/backend`
|
||||
- Modify: `Agent-System-Dev/modules/rdm`
|
||||
- Modify: `Agent-System-Dev/modules/frontend`
|
||||
- Modify: `Agent-System-Dev/modules/ui`
|
||||
- Test evidence: `Agent-System-Dev/.task/feature-iam-u4-role-authority.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes reviewed component commits and trusted published tags where required.
|
||||
- Produces one fixed Dev combination and requirement-to-evidence map.
|
||||
|
||||
- [ ] **Step 1: Run component verification on exact commits**
|
||||
|
||||
Backend: full SQLite, isolated PostgreSQL, migration rollback/retry, build.
|
||||
RDM: full tests/build against the reviewed Backend public version.
|
||||
Frontend/UI: full tests/build and design guards.
|
||||
|
||||
- [ ] **Step 2: Run a real browser scenario on an isolated database**
|
||||
|
||||
Create two ordinary users, one “软件工程师” role with a description, assign both users, replace one user's role, configure `project.create`, create projects A and B under different creators, and verify A's creator cannot manage B. Verify an unassigned system administrator cannot read either project's business content. Exercise exact/fuzzy search, filters, single-role edit, matrix edit, preview, conflict, and draft retention.
|
||||
|
||||
- [ ] **Step 3: Run the Dev combination**
|
||||
|
||||
Run: `make test`
|
||||
Expected: PASS on the exact component SHAs recorded by `make evidence`.
|
||||
|
||||
- [ ] **Step 4: Record remaining boundaries**
|
||||
|
||||
Mark production migration, deployment, account recovery, multi-role, Agent permissions, and workflow changes `NOT_RUN / out of scope`. Do not claim them from component tests.
|
||||
@@ -85,6 +85,8 @@ Backend 成为角色和系统权限的唯一写入权威,提供版本化接口
|
||||
- `PUT /api/admin/roles/{roleId}/permissions`,批量提交固定 actionId 列表和 `expectedRoleVersion`
|
||||
- `PUT /api/admin/users/{userId}/role`,替换该用户唯一有效角色,使用用户状态版本和幂等键
|
||||
- `GET /api/admin/users/{userId}/role`
|
||||
- `GET /api/admin/users/{userId}/system-grants`
|
||||
- `PUT /api/admin/users/{userId}/system-grants`,批量提交明确系统动作并使用 `expectedAuthorityEpoch` 和幂等键
|
||||
- `GET /api/authz/explain`,仅解释调用者自己或其有权管理的对象
|
||||
|
||||
批量权限修改在单库事务内全部成功或全部失败;任一未知动作、越权、越上限或版本冲突不得部分写入。响应返回新版本、实际差异和审计回执标识。
|
||||
@@ -102,7 +104,7 @@ RDM 通过公开、固定版本的 Backend 角色读取/判定契约消费角
|
||||
- 过滤隐藏的单元格不丢失草稿;全选只作用于当前明确显示的固定 actionId 集合。
|
||||
- 提交前显示差异;冲突保留草稿并要求刷新,不逐格即时写库。
|
||||
|
||||
用户详情显示当前角色,并允许有 `system.roles.assign` 权限的操作者从有效角色中选择。用户可选资料继续用于展示、搜索和辨识,不参与权限计算。
|
||||
用户详情显示当前角色和明确系统授权来源,并允许有 `system.roles.assign` 权限的操作者从有效角色中选择。有 `system.permissions.manage` 权限的操作者可以编辑目标用户的系统授权,但不能撤销最后一个有效权限管理者。用户可选资料继续用于展示、搜索和辨识,不参与权限计算。
|
||||
|
||||
项目设置继续使用现有成员和权限入口,显示角色默认、角色上限、项目覆盖及最终允许/拒绝原因。项目创建者无法切换到其他项目后沿用原项目管理权。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user