cabloy 5.1.159 → 5.1.160

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/package.json +1 -1
  4. package/repo-docs/.vitepress/config.mjs +11 -0
  5. package/repo-docs/backend/department-management.md +98 -0
  6. package/repo-docs/backend/menu-authorization.md +124 -0
  7. package/repo-docs/backend/rbac-authorization.md +117 -0
  8. package/repo-docs/backend/resource-field-update.md +2 -2
  9. package/repo-docs/backend/role-management.md +92 -0
  10. package/repo-docs/backend/shared-rbac-architecture.md +339 -0
  11. package/repo-docs/backend/user-management.md +92 -0
  12. package/repo-docs/frontend/model-resource-owner-pattern.md +2 -0
  13. package/repo-docs/frontend/permission-formscene-action-visibility-guide.md +5 -3
  14. package/repo-docs/frontend/table-action-visibility-permission-flow-guide.md +2 -0
  15. package/repo-docs/frontend/table-guide.md +75 -22
  16. package/repo-docs/frontend/table-resource-crud-cookbook.md +25 -4
  17. package/repo-docs/frontend/zova-table-source-reading-map.md +54 -19
  18. package/repo-docs/frontend/zova-table-under-the-hood.md +70 -39
  19. package/test-results/.last-run.json +20 -2
  20. package/test-results/a-commerce-ATP-ADDR-01-aut-11dea-ss-through-Web-self-service/error-context.md +238 -0
  21. package/test-results/a-commerce-ATP-SPC-01-Coup-ef380-mantic-Admin-field-controls/error-context.md +240 -0
  22. package/test-results/a-commerce-ATP-SPC-02-Cate-9f120-on-and-publication-controls/error-context.md +238 -0
  23. package/test-results/a-commerce-ATP-SPC-02-Prod-5f92a-on-and-publication-controls/error-context.md +238 -0
  24. package/test-results/a-commerce-ATP-SPC-02-SKU--2e61e-ency-and-lifecycle-controls/error-context.md +238 -0
  25. package/test-results/a-commerce-ATP-SPC-04-Stoc-fa895--readonly-and-mutation-free/error-context.md +235 -0
  26. package/test-results/a-commerce-ATP-SPC-05-syst-fb406-e-without-mutation-controls/error-context.md +238 -0
  27. package/test-results/a-commerce-Commerce-sessio-4e963-ie-selects-raw-server-theme/error-context.md +226 -0
  28. package/test-results/a-commerce-Commerce-theme--062fe--without-hydration-mismatch/error-context.md +229 -0
  29. package/test-results/a-commerce-PayPal-browser--8672c-t-or-open-an-awaiting-order/error-context.md +238 -0
  30. package/test-results/a-commerce-Payment-callbac-f56c2--reconciles-after-hydration/error-context.md +238 -0
  31. package/test-results/a-commerce-Payment-cancell-3fc74-ified-provider-confirmation/error-context.md +238 -0
  32. package/test-results/a-commerce-Phase-50-60-aut-71d61--observes-operator-shipment/error-context.md +238 -0
  33. package/test-results/a-commerce-Phase-60-custom-407cd-ecutes-a-whole-order-refund/error-context.md +238 -0
  34. package/test-results/cabloy-basic-ATP-BASIC-TAB-e7253-and-pins-configured-columns/error-context.md +240 -0
  35. package/test-results/markdown-ATP-SPC-02-Produc-9d5c2-link-toolbar-edits-Markdown/error-context.md +211 -0
  36. package/test-results/markdown-ATP-SPC-02-Produc-9d6ea-t-editor-and-saves-Markdown/error-context.md +238 -0
  37. package/vona/packages-cli/cli/package.json +1 -1
  38. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
  39. package/vona/packages-cli/cli-set-api/package.json +1 -1
  40. package/vona/pnpm-lock.yaml +97 -11
  41. package/zova/pnpm-lock.yaml +16 -16
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.159
1
+ 5.1.160
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.160
4
+
5
+ ### Features
6
+
7
+ - Update feature capabilities
8
+
9
+ ### Improvements
10
+
11
+ - Add documentation explaining shared RBAC architecture
12
+ - Add system management guides
13
+ - Document ZovaRender.column
14
+
3
15
  ## 5.1.159
4
16
 
5
17
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.159",
3
+ "version": "5.1.160",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -287,6 +287,17 @@ export default defineConfig({
287
287
  { text: 'Validation Guide', link: '/backend/validation-guide' },
288
288
  ],
289
289
  },
290
+ {
291
+ text: 'System Management',
292
+ items: [
293
+ { text: 'User Management', link: '/backend/user-management' },
294
+ { text: 'Role Management', link: '/backend/role-management' },
295
+ { text: 'Department Management', link: '/backend/department-management' },
296
+ { text: 'Shared RBAC Architecture', link: '/backend/shared-rbac-architecture' },
297
+ { text: 'RBAC Authorization', link: '/backend/rbac-authorization' },
298
+ { text: 'Menu Authorization', link: '/backend/menu-authorization' },
299
+ ],
300
+ },
290
301
  {
291
302
  text: 'Application Basics',
292
303
  items: [
@@ -0,0 +1,98 @@
1
+ # Department Management
2
+
3
+ Department Management maintains an instance's organizational hierarchy and its user memberships. Departments are not roles and are not tenant boundaries. They are trusted organizational facts that can later participate in Department-scoped RBAC decisions.
4
+
5
+ > <Badge type="warning" text="Start specimen" /> The examples below are grounded in Cabloy Start's `admin-department` module. The Start tree/list page is one UI implementation; the hierarchy and membership invariants are the portable operational model.
6
+
7
+ ## Department and membership model
8
+
9
+ A Department represents a node in an ordered hierarchy:
10
+
11
+ | Department fact | Meaning |
12
+ | --------------- | ---------------------------------------------------------------- |
13
+ | `parentId` | The parent organizational unit, or the root |
14
+ | `sortOrder` | Order among sibling Departments |
15
+ | `enabled` | Whether the Department is active for operations and policy scope |
16
+ | `managerId` | The user currently designated as its manager |
17
+
18
+ A Department membership relates one user to one Department and carries its own facts:
19
+
20
+ | Membership fact | Meaning |
21
+ | --------------- | ------------------------------------------------ |
22
+ | `position` | Membership-specific organizational position |
23
+ | `enabled` | Whether this membership is active |
24
+ | `primary` | The user's primary enabled Department membership |
25
+
26
+ A user may hold more than one Department membership. This is intentionally different from role membership: roles describe access groupings, while Departments describe organizational placement.
27
+
28
+ ## Tree administration
29
+
30
+ The Start management surface supports Department CRUD, parent moves, sibling reordering, activation changes, manager assignment, and membership management.
31
+
32
+ Keep these hierarchy invariants in the service layer:
33
+
34
+ 1. moving a Department must not create a cycle;
35
+ 2. a move must leave sibling ordering coherent;
36
+ 3. names must satisfy the configured sibling uniqueness rule;
37
+ 4. operations that would leave active children or invalid organizational relationships must be rejected; and
38
+ 5. tree changes must be serialized where concurrent ordering updates could conflict.
39
+
40
+ The Start service uses a lock around ordered-sibling operations and verifies ancestry before a move. A frontend tree is useful for navigation, but it must not be the only place enforcing those rules.
41
+
42
+ ## Membership and manager rules
43
+
44
+ Membership administration should preserve clear, durable facts:
45
+
46
+ - a duplicate membership for the same user and Department is invalid;
47
+ - at most one **enabled** membership is primary for a user;
48
+ - a Department manager must be an enabled membership of that Department; and
49
+ - before disabling or deleting a manager's membership, assign a replacement manager or clear the manager relationship according to the operation's explicit policy.
50
+
51
+ These constraints explain why Department membership is managed as its own record rather than as an array embedded casually in a user profile.
52
+
53
+ ## Relationship to RBAC
54
+
55
+ Department data becomes authorization input only when a protected action uses a Department-based scope.
56
+
57
+ ```text
58
+ Department tree and enabled memberships
59
+ → RBAC policy resolution
60
+ → server-derived Department/user terms
61
+ → scoped query or entry check
62
+ ```
63
+
64
+ For example, [RBAC Authorization](/backend/rbac-authorization) can use the current user's enabled primary Department, all enabled Department memberships, descendants of a Department, or explicit custom Departments. The Department service must invalidate relevant policy state after topology or membership changes so future requests do not evaluate a stale organizational scope.
65
+
66
+ > [!NOTE]
67
+ > The Vona instance remains the tenancy boundary. Department hierarchy is an organizational model inside that instance; it must not be used as a substitute for cross-instance isolation.
68
+
69
+ ## Source specimen
70
+
71
+ Representative Start sources:
72
+
73
+ - `vona/src/suite/cabloy-admin/modules/admin-department/src/controller/department.ts` — protected tree, membership, manager, move, reorder, and lifecycle endpoints.
74
+ - `vona/src/suite/cabloy-admin/modules/admin-department/src/service/department.ts` — hierarchy/membership validation, manager constraints, ordering, and RBAC invalidation.
75
+ - `vona/src/suite/cabloy-admin/modules/admin-department/src/entity/department.tsx` and `entity/departmentMembership.tsx` — persisted Department and membership facts.
76
+
77
+ ## Common mistakes
78
+
79
+ - **Using Departments as roles.** Use [Role Management](/backend/role-management) for access-group membership.
80
+ - **Trusting a client-supplied owner or Department in a scoped API.** RBAC must derive and check authoritative values on the server.
81
+ - **Allowing a manager membership to disappear silently.** Enforce replacement/clearance rules in the mutation service.
82
+ - **Using Department hierarchy as a tenant boundary.** Vona instance scoping remains mandatory.
83
+
84
+ ## Verification checklist
85
+
86
+ 1. Create root and child Departments, then verify ordering and parent relationships.
87
+ 2. Attempt an ancestor/descendant cycle and confirm it is rejected.
88
+ 3. Create multiple memberships for a user and verify the enabled-primary invariant.
89
+ 4. Attempt to disable or remove a manager's membership and confirm the configured manager safeguard applies.
90
+ 5. Change a Department/membership relevant to a scoped grant, then verify the next RBAC evaluation reflects the new topology.
91
+
92
+ ## Read next
93
+
94
+ - [User Management](/backend/user-management)
95
+ - [Role Management](/backend/role-management)
96
+ - [RBAC Authorization](/backend/rbac-authorization)
97
+ - [ORM Guide](/backend/orm-guide)
98
+ - [Transaction Guide](/backend/transaction-guide)
@@ -0,0 +1,124 @@
1
+ # Menu Authorization
2
+
3
+ Menu Authorization controls **navigation disclosure**: which SSR menu leaves are returned to a caller for the active site. It does not grant controller, API, page, Resource, action, or data-scope access.
4
+
5
+ > [!WARNING]
6
+ > A menu association is never an authorization grant. Keep backend guards and RBAC policy on the protected route even when the menu is visible. See [RBAC Authorization](/backend/rbac-authorization) for action/data authority and [Menu Guide](/backend/menu-guide) for the shared SSR retrieval model.
7
+
8
+ > <Badge type="warning" text="Start specimen" /> The dynamic role-menu administration examples below come from Cabloy Start's `admin-menu` module. Its SSR-site names, role editor, and refresh behavior are implementation details, not Basic UI guarantees.
9
+
10
+ ## Static menu eligibility
11
+
12
+ An SSR menu item can declare `roles` metadata:
13
+
14
+ ```typescript
15
+ @SsrMenu({
16
+ item: {
17
+ title: $locale('Operations'),
18
+ link: 'presetResource',
19
+ roles: ['systemAdmin'],
20
+ },
21
+ })
22
+ ```
23
+
24
+ The declaration determines whether a leaf participates in role-aware disclosure:
25
+
26
+ | `roles` declaration | Static visibility | Dynamic role-menu configuration |
27
+ | ------------------- | ----------------------------------------------- | ---------------------------------------------------- |
28
+ | omitted | Public to the menu audience | Not configurable |
29
+ | `[]` | No static role makes it visible | Required for role-based visibility |
30
+ | nonempty names | Visible to a caller with any matching role name | Can additionally be disclosed through an association |
31
+
32
+ The `roles` declaration is server-only metadata. It is removed from the public menu DTO and is not a frontend substitute for route authorization.
33
+
34
+ ## Role-to-menu associations
35
+
36
+ A persisted association identifies a configurable SSR menu leaf for a role:
37
+
38
+ ```text
39
+ roleId + ssrSiteName + ssrMenuName
40
+ ```
41
+
42
+ The management service must validate all three parts against live menu metadata:
43
+
44
+ - the role exists;
45
+ - the target is a real site-bound leaf, not a group; and
46
+ - the leaf declares `roles`, making it eligible for dynamic configuration.
47
+
48
+ Public leaves and structural groups are intentionally not configurable. For keyed items, the runtime derives a stable menu-leaf identity from the menu declaration and item key.
49
+
50
+ ## Effective request-time visibility
51
+
52
+ At retrieval time, Cabloy prepares menu structure for the current SSR site and locale, then filters it for the caller. The effective result is additive:
53
+
54
+ ```text
55
+ static role-name visibility
56
+ OR
57
+ persisted association held by any current role
58
+ → visible leaf in the SSR menu response
59
+ ```
60
+
61
+ The system removes empty groups after leaf filtering and does not expose the private declaration metadata to the consumer.
62
+
63
+ This union is about discoverability only. It must not be confused with frontend site admission or with Vona controller policy:
64
+
65
+ ```text
66
+ siteIds → may enter a frontend site
67
+ menu authorization → may discover a navigation destination
68
+ RBAC / Passport → may execute a backend action
69
+ RBAC data scope → may affect this persisted row
70
+ ```
71
+
72
+ ## Administration workflow
73
+
74
+ Role Management is normally the operator's entry point for configuring both action policy and navigation disclosure:
75
+
76
+ ```text
77
+ choose a role
78
+ → configure RBAC grants for API/resource authority
79
+ → configure eligible SSR menu leaves for disclosure
80
+ → refresh affected current subjects
81
+ → verify direct API access independently
82
+ ```
83
+
84
+ > <Badge type="warning" text="Start specimen" /> Cabloy Start registers the System Management group as `start-siteadmin:systemManagement` and presents a role-menu tree editor in the role detail. These names and layout are not a cross-edition contract. The stable contract is an SSR menu group with independently authorized backend operations.
85
+
86
+ ## Freshness after a menu-policy change
87
+
88
+ Start records a separate menu-visibility revision when associations change. Its frontend model invalidates role-menu state and refreshes the application when the edited role belongs to the current Passport subject.
89
+
90
+ This refresh matters because the frontend menu query is intentionally keyed by stable site/public-path/locale inputs, not by every role identity detail. It remains a UX freshness concern: route, Resource, controller, and API guards must still re-evaluate independently.
91
+
92
+ ## Source specimen
93
+
94
+ Representative Start sources:
95
+
96
+ - `vona/src/suite/cabloy-admin/modules/admin-menu/src/service/roleMenu.ts` — target validation, serialized role updates, association mutation, and revision changes.
97
+ - `vona/src/suite/cabloy-admin/modules/admin-menu/src/bean/eventListener.menuVisibilityResolver.ts` — merging static visibility with current-role associations.
98
+ - `vona/src/suite/cabloy-start/modules/start-siteadmin/src/bean/ssrMenuGroup.systemManagement.ts` — Start-specific System Management group registration.
99
+ - `zova/src/suite/cabloy-admin/modules/admin-menu/src/model/roleMenu.ts` — Start client freshness behavior.
100
+
101
+ The current Start role-menu editor protects `systemAdmin` from ordinary editing. Treat that as a Start UI safeguard and resolve any edition-specific policy rule from the active implementation/specification; do not infer an implicit administrative-menu bypass.
102
+
103
+ ## Common mistakes
104
+
105
+ - **Using menu visibility to secure an API.** Always use a backend Passport/RBAC guard.
106
+ - **Making public menu leaves configurable.** Only leaves that explicitly declare `roles` have dynamic role-menu eligibility.
107
+ - **Expecting menu association to admit a role to a site.** Site admission is a separate `siteIds` decision.
108
+ - **Assuming a menu refresh proves authorization.** Exercise the actual protected request and data-scope path.
109
+
110
+ ## Verification checklist
111
+
112
+ 1. Verify omitted, empty, and nonempty `roles` declarations produce the expected static/dynamic eligibility.
113
+ 2. Associate an eligible leaf with a role and verify a current holder receives the menu after refresh.
114
+ 3. Remove the association and verify the leaf disappears when no static role rule still exposes it.
115
+ 4. Attempt to configure a group or public leaf and confirm server validation rejects it.
116
+ 5. Attempt the destination's protected API with and without the appropriate backend grant; menu visibility must not change the result.
117
+
118
+ ## Read next
119
+
120
+ - [Menu Guide](/backend/menu-guide)
121
+ - [Role Management](/backend/role-management)
122
+ - [RBAC Authorization](/backend/rbac-authorization)
123
+ - [Navigation Guards Guide](/frontend/navigation-guards-guide)
124
+ - [Admin Resource and Web Self-Service](/fullstack/admin-resource-and-web-self-service)
@@ -0,0 +1,117 @@
1
+ # RBAC Authorization
2
+
3
+ RBAC Authorization decides whether a backend action is allowed and, for scoped actions, which persisted data the caller may reach. It is server-enforced action and data authorization.
4
+
5
+ Read [Shared RBAC Architecture](/backend/shared-rbac-architecture) first for the shared Basic/Start `a-rbac` runtime, Resource permission projection, frontend matcher, SSR, and freshness model. This page focuses on the Cabloy Start operational policy specimen: roles, grants, Departments, and administration workflow.
6
+
7
+ > [!WARNING]
8
+ > RBAC is not authentication, SSR menu disclosure, or a frontend-button rule. A hidden action may improve UX, but every direct Vona request must pass the backend guard and, when applicable, the server-side data-scope check.
9
+
10
+ > <Badge type="warning" text="Start specimen" /> This guide uses Cabloy Start's `admin-rbac` configuration module and `a-rbac` runtime path. Editions can compose the administration UI differently without weakening the backend decision model.
11
+
12
+ ## The protected-action model
13
+
14
+ A controller opts an action into dynamic RBAC with `@Passport.rbac(...)` metadata. A representative scoped action looks like this:
15
+
16
+ ```typescript
17
+ @Web.get()
18
+ @Passport.rbac({ dataScope: true })
19
+ async select(...) {
20
+ return await this.scope.service.record.select({
21
+ where: this.ctx.rbacScopeCurrent.where(where),
22
+ });
23
+ }
24
+ ```
25
+
26
+ The exact decorator options depend on the action contract. The important rule is that protected controllers consume the resulting request-local scope instead of treating guard admission as a complete row-level decision.
27
+
28
+ The runtime chain is:
29
+
30
+ ```text
31
+ cataloged route action
32
+ → current Passport role IDs
33
+ → enabled role/action grants
34
+ → allowed policy terms
35
+ → typed scope operations in the controller
36
+ → ORM query filter or persisted-entry check
37
+ ```
38
+
39
+ For updates, views, and deletes, use the appropriate scope operation such as `checkEntry(...)` or `checkEntries(...)`. For creates, derive trusted owner/Department values with `ownerValues()` rather than accepting values that can widen a client request.
40
+
41
+ ## Role, action, and scope grants
42
+
43
+ A grant binds a role to a stable catalog action and a compatible data scope:
44
+
45
+ ```text
46
+ roleId + actionKey + dataScope + enabled
47
+ ```
48
+
49
+ The catalog comes from routes decorated with `@Passport.rbac(...)`; ordinary routes do not become dynamic RBAC actions merely because they are visible in a UI. The Start catalog also supports controlled action inheritance, validated centrally to reject missing targets and cycles.
50
+
51
+ Multiple enabled grants held through the current user's roles combine as a union. A caller-provided query filter remains additionally constrained by the server scope, rather than replacing it.
52
+
53
+ ## Data scopes
54
+
55
+ The Start specimen supports these five scopes:
56
+
57
+ | Scope | Effective data boundary |
58
+ | ----------------------------- | -------------------------------------------------------------- |
59
+ | `all` | All records within the active Vona instance |
60
+ | `customDepartments` | Records belonging to explicitly configured enabled Departments |
61
+ | `ownDepartment` | Records in the caller's enabled Department scope |
62
+ | `ownDepartmentAndDescendants` | Caller Department scope plus enabled descendants |
63
+ | `mine` | Records owned by the current user |
64
+
65
+ `all` dominates narrower terms. Department-aware terms depend on enabled memberships and enabled Department topology; descendant expansion must be cycle-safe. A non-data-scoped action accepts only the scope compatible with its catalog definition.
66
+
67
+ Departments are inputs to the policy, not alternative authorization stores. Read [Department Management](/backend/department-management) before designing Department-scoped grants.
68
+
69
+ ## Runtime authority and frontend projections
70
+
71
+ Resource permissions are derived from the same guards to support frontend action visibility. They are UX projections, not a second grant database and never the final authority:
72
+
73
+ ```text
74
+ frontend permission projection → UX decision
75
+ backend RBAC guard/scope → authoritative decision
76
+ ```
77
+
78
+ For the shared guard simulation, projection contract, matcher semantics, SSR behavior, and active-tab freshness limitation, read [Shared RBAC Architecture](/backend/shared-rbac-architecture). This distinction prevents stale browser state, crafted requests, and hidden-button assumptions from becoming security vulnerabilities.
79
+
80
+ ## Policy freshness
81
+
82
+ A grant change, role-membership change, Department-membership change, or Department-topology change can affect later policy resolution. The Start implementation emits `a-rbac:policyInvalidated`, advances policy revision state, and clears dependent permission caches after the relevant transaction commits.
83
+
84
+ Treat this as a dependency graph: source mutations must refresh every warmed policy or permission projection that depends on them. Do not hand-patch a browser cache while leaving the server policy stale.
85
+
86
+ ## System-administrator safeguards
87
+
88
+ The Start grant service/editor excludes `systemAdmin` from ordinary grant management, and its scope adapter treats that subject as unrestricted for the RBAC decision path. These are explicit Start operational safeguards, not a guarantee that every Cabloy edition exposes the same UI or management rule.
89
+
90
+ Whatever the edition, protect administrator transitions deliberately and ensure the final backend decision remains explicit, testable, and server-owned.
91
+
92
+ ## Source specimen
93
+
94
+ Representative Start sources:
95
+
96
+ - `vona/src/suite/cabloy-admin/modules/admin-rbac/src/lib/rbacPolicy.ts` — scope compatibility and action inheritance rules.
97
+ - `vona/src/suite/cabloy-admin/modules/admin-rbac/src/service/rbacPolicy.ts` — role/grant lookup and Department/user-term resolution.
98
+ - `vona/src/suite/cabloy-admin/modules/admin-rbac/src/service/rbacGrant.ts` — mutation validation and invalidation.
99
+ - `vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/bean/guard.rbac.ts` and `bean.rbacScope.ts` — guard-time decision and typed scope consumption.
100
+
101
+ ## Verification checklist
102
+
103
+ 1. Confirm an unauthenticated or ungranted caller is denied for every opted-in action.
104
+ 2. Test each supported scope with persisted data, not only a frontend visibility assertion.
105
+ 3. Verify forged owner/Department values cannot widen create, update, view, delete, or bulk operations.
106
+ 4. Grant two scopes through multiple roles and confirm their permitted terms combine as expected.
107
+ 5. Change a grant, role membership, or Department topology and verify subsequent server decisions and frontend projections refresh.
108
+ 6. Verify a visible menu or button never makes an ungranted direct API request succeed.
109
+
110
+ ## Read next
111
+
112
+ - [Shared RBAC Architecture](/backend/shared-rbac-architecture)
113
+ - [Role Management](/backend/role-management)
114
+ - [Department Management](/backend/department-management)
115
+ - [Menu Authorization](/backend/menu-authorization)
116
+ - [Controller AOP Guide](/backend/controller-aop-guide)
117
+ - [Permission FormScene and Action Visibility Guide](/frontend/permission-formscene-action-visibility-guide)
@@ -113,7 +113,7 @@ A resource list can search and order a relation by a human-readable column from
113
113
  studentId: TableIdentity;
114
114
  ```
115
115
 
116
- `table` and `joinOn` identify the relation, while `originalName` resolves both the `studentId` filter and order key to `trainingStudent.name`. `_includesI_` makes the filter a case-insensitive partial-name match. `ZovaRender.column({ enableSorting: true })` exposes the sortable table column; the query pipeline uses the same field metadata to add the join and rewrite `orders: [['studentId', 'asc']]` to the related display column.
116
+ `table` and `joinOn` identify the relation, while `originalName` resolves both the `studentId` filter and order key to `trainingStudent.name`. `_includesI_` makes the filter a case-insensitive partial-name match. `ZovaRender.column({ enableSorting: true })` is the shared Cabloy Basic/Start frontend eligibility declaration for a sortable table header; it does not by itself authorize or resolve the backend order key. The order schema and query metadata remain authoritative, and the query pipeline uses the same field metadata to add the join and rewrite `orders: [['studentId', 'asc']]` to the related display column. For the complete table-column contract and resource-page sort path, see [Table Guide](/frontend/table-guide#configure-physical-columns-with-zovarendercolumn).
117
117
 
118
118
  Use `innerJoin` deliberately when the relation is required and unmatched rows should not participate in this filter/order path. For an optional relation whose unmatched rows must remain visible, choose the join behavior explicitly instead of copying the required-relation example.
119
119
 
@@ -154,7 +154,7 @@ A custom backend-rendered field normally needs both sides of the frontend pair:
154
154
 
155
155
  A plain frontend component is not sufficient for backend table-cell metadata. Reuse the closest shared renderer’s option/data flow and keep the customization focused.
156
156
 
157
- For public implementation patterns, see [Form Guide](/frontend/form-guide), [TableCell Cookbook](/frontend/table-cell-cookbook#pattern-7-backend-contract-to-frontend-cell-handoff), and the Basic-only [Custom Form/Table Renderers tutorial](/fullstack/tutorial-4-custom-level-renderers).
157
+ For public implementation patterns, see [Form Guide](/frontend/form-guide), [TableCell Authoring Cookbook](/frontend/table-cell-cookbook#pattern-7-backend-contract-to-frontend-cell-handoff), and the Basic-only [Custom Form/Table Renderers tutorial](/fullstack/tutorial-4-custom-level-renderers).
158
158
 
159
159
  ## Follow the contract loop
160
160
 
@@ -0,0 +1,92 @@
1
+ # Role Management
2
+
3
+ Role Management defines reusable access groupings and assigns them to users. A role is the common subject for several access-related systems, but those systems remain independent:
4
+
5
+ | Role relationship | What it controls | Does not control |
6
+ | ---------------------- | ------------------------------------------- | ------------------------ |
7
+ | `siteIds` | Admission to a frontend SSR site | Individual API actions |
8
+ | RBAC grants | Backend action and data-scope authorization | Menu disclosure |
9
+ | Role-menu associations | Navigation disclosure | Controller or API access |
10
+
11
+ > <Badge type="warning" text="Start specimen" /> The Cabloy Start `admin-role` module is the source specimen. Its role-detail tabs are an edition-specific UI composition; the three policy domains above are the durable model.
12
+
13
+ ## Role lifecycle
14
+
15
+ The Start role controller supports role CRUD and user-role membership management. The role service validates role names and validates configured `siteIds` against enabled SSR sites.
16
+
17
+ A role should therefore be created with a clear operational purpose:
18
+
19
+ ```text
20
+ Role name and title
21
+ → eligible frontend sites
22
+ → users who hold the role
23
+ → action/data grants, when needed
24
+ → navigation disclosure, when needed
25
+ ```
26
+
27
+ Creating a role or assigning it to a user does not automatically create RBAC grants or menu assignments. Configure those policy domains deliberately after the role exists.
28
+
29
+ ## Assign roles to users
30
+
31
+ A user can hold multiple roles. Ordinary membership replacement belongs to Role Management because it changes an access grouping, even though the affected user is visible in User Management.
32
+
33
+ The Start specimen preserves an existing `systemAdmin` membership during ordinary replacement and excludes it from the usual candidate flow. Dedicated protected operations handle system-administrator transitions instead.
34
+
35
+ > [!WARNING]
36
+ > Treat built-in and administrator roles as protected operational identities. Do not implement a generic role form that can rename or delete them, and do not make the last usable system administrator removable or disableable.
37
+
38
+ ## Site admission is a coarse boundary
39
+
40
+ A role's `siteIds` determines whether a current role may enter a frontend site. It is a coarse admission check, not proof that the role may execute every action once it is inside.
41
+
42
+ ```text
43
+ site admission
44
+ → menu disclosure
45
+ → frontend action visibility
46
+ → backend API guard
47
+ → data-scope enforcement
48
+ ```
49
+
50
+ Each later layer remains responsible for its own decision. In particular, a role admitted to an Admin site still needs a passing backend guard for every protected request.
51
+
52
+ ## Role as the configuration hub
53
+
54
+ The Start Role Resource embeds two separate editors in its entry view:
55
+
56
+ - **Resource Permissions** configures action/data policy through `admin-rbac`.
57
+ - **Menu Authorization** configures SSR navigation disclosure through `admin-menu`.
58
+
59
+ This is a useful operator workflow, but not a claim that every Cabloy edition must use the same route, tabs, component identifiers, or UI library. The important design is that the editors update different persistence and runtime-policy paths.
60
+
61
+ ## Source specimen
62
+
63
+ Representative Start sources:
64
+
65
+ - `vona/src/suite/cabloy-admin/modules/admin-role/src/controller/role.ts` — protected role and membership endpoints.
66
+ - `vona/src/suite/cabloy-admin/modules/admin-role/src/service/role.ts` — name/site validation, built-in-role safeguards, membership replacement, and policy invalidation.
67
+ - `vona/src/suite/cabloy-admin/modules/admin-role/src/dto/roleView.tsx` — edition-specific composition of the two authorization editors.
68
+
69
+ Role changes that affect memberships must invalidate dependent RBAC policy state. The next request must resolve current roles and policy rather than relying on an old browser projection.
70
+
71
+ ## Common mistakes
72
+
73
+ - **Using a role name as a substitute for every action grant.** Use [RBAC Authorization](/backend/rbac-authorization) for protected resource/API actions.
74
+ - **Treating menu assignment as API authorization.** A role-menu association only changes discoverability; see [Menu Authorization](/backend/menu-authorization).
75
+ - **Putting Department facts on a role.** Departments describe organizational membership and supply some RBAC scopes; see [Department Management](/backend/department-management).
76
+ - **Changing role membership from a client-only cache.** Membership mutation must pass through backend validation and invalidation.
77
+
78
+ ## Verification checklist
79
+
80
+ 1. Confirm built-in roles cannot be deleted or renamed.
81
+ 2. Confirm only valid enabled SSR sites may be assigned to a role.
82
+ 3. Assign and remove an ordinary role, then verify the user's next access evaluation uses the new membership.
83
+ 4. Verify system-administrator changes use the edition's dedicated safeguards.
84
+ 5. Verify RBAC and menu assignments remain separate before and after role edits.
85
+
86
+ ## Read next
87
+
88
+ - [User Management](/backend/user-management)
89
+ - [RBAC Authorization](/backend/rbac-authorization)
90
+ - [Menu Authorization](/backend/menu-authorization)
91
+ - [Menu Guide](/backend/menu-guide)
92
+ - [User Access Guide](/backend/user-access-guide)