openxiangda 1.0.136 → 1.0.138
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.
- package/lib/cli.js +687 -5
- package/lib/design-gates.js +16 -3
- package/openxiangda-skills/references/best-practices.md +18 -7
- package/openxiangda-skills/references/component-guide.md +7 -1
- package/openxiangda-skills/references/permission-design-patterns.md +95 -5
- package/openxiangda-skills/references/permissions-settings.md +165 -0
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +87 -0
- package/package.json +1 -1
- package/packages/sdk/dist/{ProcessPreview-Ci8_UsbN.d.mts → ProcessPreview-CZJP8n3e.d.mts} +6 -0
- package/packages/sdk/dist/{ProcessPreview-Ci8_UsbN.d.ts → ProcessPreview-CZJP8n3e.d.ts} +6 -0
- package/packages/sdk/dist/components/index.cjs +143 -16
- package/packages/sdk/dist/components/index.cjs.map +1 -1
- package/packages/sdk/dist/components/index.d.mts +8 -2
- package/packages/sdk/dist/components/index.d.ts +8 -2
- package/packages/sdk/dist/components/index.mjs +143 -16
- package/packages/sdk/dist/components/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/index.cjs +169 -25
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +1 -1
- package/packages/sdk/dist/runtime/index.d.ts +1 -1
- package/packages/sdk/dist/runtime/index.mjs +169 -25
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +26 -9
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +13 -3
- package/packages/sdk/dist/runtime/react.d.ts +13 -3
- package/packages/sdk/dist/runtime/react.mjs +26 -9
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
package/lib/design-gates.js
CHANGED
|
@@ -113,23 +113,31 @@ const DESIGN_GATE_TOPICS = [
|
|
|
113
113
|
{
|
|
114
114
|
code: 'permissions',
|
|
115
115
|
title: '账号 / 角色 / 页面权限 / 表单数据范围',
|
|
116
|
-
triggers: ['权限', '角色', 'RBAC', '账号权限', '平台账号', '组织账号', '角色治理', '数据范围', '只看自己', '部门数据', '页面权限', '字段权限', '查询参数权限'],
|
|
116
|
+
triggers: ['权限', '角色', 'RBAC', '账号权限', '平台账号', '组织账号', '角色治理', '数据范围', '业务范围', 'scope_policy', '只看自己', '部门数据', '页面权限', '字段权限', '查询参数权限'],
|
|
117
117
|
mustAsk: [
|
|
118
118
|
'权限模式选择:managed-platform-account、existing-platform-user-assignment、static-role-permission、query-param-context 中哪一种,为什么',
|
|
119
119
|
'账号来源是什么:应用创建平台账号/部门、选择已有平台用户、固定角色成员,还是仅使用低风险查询参数上下文',
|
|
120
120
|
'是否创建或维护平台账号/部门;如果是,哪些 App Function/organization SDK 负责创建、更新、重置密码和同步审计',
|
|
121
121
|
'角色成员如何维护:角色分配表、平台角色手工维护、静态 roles manifest、还是公开访问 externalRoleCodes',
|
|
122
|
+
'业务范围是否来自应用表单:用户/角色到客户、项目、区域、门店、学院、班级等授权关系是否需要 scopeDimensions、scopeGrantSources、dataScopePolicies',
|
|
123
|
+
'业务范围数量是否超过少量固定枚举;如果会增长到几十/上百个对象,是否采用 scope_policy 而不是为每个对象建角色/权限组',
|
|
122
124
|
'哪些角色可以设置应用角色、分配角色成员、给角色分配接口权限;这些角色是否需要 `app:role:manage`',
|
|
123
125
|
'哪些角色可以维护页面权限组、表单权限组、组织账号;是否需要 `app:page-permission-group:manage`、`app:form-permission-group:manage`、`app:organization:manage`',
|
|
124
126
|
'业务范围字段是什么,哪些可见字段需要派生隐藏 scalar key 供表单权限条件匹配',
|
|
125
|
-
'
|
|
127
|
+
'每个角色的 action matrix 是什么:view/create/edit/delete/export/import/change_records/workflow 是否分别授权',
|
|
128
|
+
'导出/导入是否独立授权;导出范围是否与 view 范围不同,导入是否还需要逐行 create 范围校验',
|
|
129
|
+
'create 是否需要 scope_policy 校验提交数据,防止新增到未授权业务范围',
|
|
130
|
+
'页面/菜单/路由、表单 actions/fieldAccessPolicy/dataPermission/scope_policy 的权限矩阵如何落到 resources',
|
|
126
131
|
'查询参数是否参与授权;如果参与,只能做上下文、筛选或 ticket 输入,敏感数据必须由 public-access grant、角色、表单权限组或 App Function 校验',
|
|
127
132
|
'验收时需要模拟哪些角色、账号状态、业务范围、查询参数篡改和拒绝场景',
|
|
128
133
|
],
|
|
129
134
|
recommendedDefaults: [
|
|
130
135
|
'正式后台优先选择 managed-platform-account;如果平台账号已存在,选择 existing-platform-user-assignment;固定内部门户选择 static-role-permission',
|
|
131
136
|
'query-param-context 仅用于低风险页面上下文、筛选条件或公开 ticket 输入,不作为敏感数据授权依据',
|
|
132
|
-
'角色写 src/resources/roles,页面组写 permissions/page-groups,表单组写 permissions/form-groups,显式声明
|
|
137
|
+
'角色写 src/resources/roles,页面组写 permissions/page-groups,表单组写 permissions/form-groups,显式声明 actions/dataPermission/fieldAccessPolicy;平台内部仍兼容存储为 operations',
|
|
138
|
+
'复杂业务范围优先写 permissions/scope-dimensions、permissions/scope-grant-sources、permissions/data-scope-policies,并在表单权限组或 data-view 权限组使用 dataPermission.type=scope_policy',
|
|
139
|
+
'scope_policy 只表达数据范围,actions 表达操作能力;按钮隐藏只是 UX,后端 action check 才是权威',
|
|
140
|
+
'scope_policy 默认语义是个人授权 + 当前应用角色授权;多维 rules 显式 AND;空授权集合拒绝;管理员绕过但可审计',
|
|
133
141
|
'能管理角色或给别人分配角色的应用角色必须在 roles manifest 声明 apiPermissionCodes;至少包含 app:role:manage,按需加入 app:page-permission-group:manage、app:form-permission-group:manage、app:organization:manage',
|
|
134
142
|
'由校区管理员等委托管理员创建的新角色,如果还具备继续管理账号/角色/权限的能力,也必须同步声明并发布对应 apiPermissionCodes',
|
|
135
143
|
'账号治理闭环使用 organization_unit、system_account、role_assignment 等业务表,加 roles、page groups、form groups、sync App Functions 和 PermissionBoundary',
|
|
@@ -140,7 +148,11 @@ const DESIGN_GATE_TOPICS = [
|
|
|
140
148
|
'不能只做前端权限隐藏,必须有后端角色、表单权限组、public-access grant 或 App Function 校验',
|
|
141
149
|
'查询参数不能作为敏感授权依据,只能作为上下文、筛选或 ticket 输入',
|
|
142
150
|
'只配菜单可见,不配后端数据权限',
|
|
151
|
+
'只隐藏按钮但没有表单权限组 actions 或 App Function 服务端校验',
|
|
143
152
|
'为了省事授予全部数据再在前端过滤',
|
|
153
|
+
'为每个客户/项目/区域/学院/班级/门店创建一个角色或一个权限组',
|
|
154
|
+
'把应用表单里的业务范围强行同步成平台部门、平台角色或大量权限组',
|
|
155
|
+
'用数据冗余字段 + 成百上千个权限组表达动态业务授权',
|
|
144
156
|
'在页面里硬编码角色、账号、部门或权限范围',
|
|
145
157
|
'只给用户挂业务角色但没有给该角色绑定角色设置接口权限,导致后续新增账号/角色时运行时报无权限',
|
|
146
158
|
'让普通管理员创建带管理能力的新角色,但没有同时发布新角色的 apiPermissionCodes',
|
|
@@ -150,6 +162,7 @@ const DESIGN_GATE_TOPICS = [
|
|
|
150
162
|
],
|
|
151
163
|
acceptance: [
|
|
152
164
|
'permission audit 输出无高危缺口',
|
|
165
|
+
'业务范围超过少量枚举时,resource validate/permission audit 不再出现权限组爆炸 warning,scope explain 能说明用户、当前角色、命中授权、缓存状态和最终策略',
|
|
153
166
|
'设计文档写明权限模式、账号来源、角色成员来源和权限矩阵',
|
|
154
167
|
'角色资源中声明了管理型角色的 apiPermissionCodes,并说明由谁首次授予',
|
|
155
168
|
'内部角色允许项可用,未授权项拒绝',
|
|
@@ -47,8 +47,9 @@ Use a normal form plus:
|
|
|
47
47
|
- a `status` field
|
|
48
48
|
- user-facing responsibility fields such as personnel, department, enum, or
|
|
49
49
|
SDK-backed select fields
|
|
50
|
-
- hidden permission scope keys
|
|
51
|
-
|
|
50
|
+
- hidden permission scope keys only for small fixed data-permission conditions;
|
|
51
|
+
for dynamic customer/project/region/store/college/class-like scopes, use
|
|
52
|
+
business authorization forms plus `scope_policy`
|
|
52
53
|
- an action log form
|
|
53
54
|
- a pure state machine in `domain/<feature>/state-machine.ts`
|
|
54
55
|
- a service method that changes status and writes the log in one path
|
|
@@ -133,24 +134,34 @@ For apps with managed accounts or dynamic roles:
|
|
|
133
134
|
`searchFieldId`, and a reasonable `pageSize` so typing in the dropdown
|
|
134
135
|
triggers remote search
|
|
135
136
|
4. Add hidden derived scope keys only when platform form permission conditions
|
|
136
|
-
require scalar matching, for example:
|
|
137
|
+
require scalar matching in a small fixed app, for example:
|
|
137
138
|
- `collegeScopeKey`
|
|
138
139
|
- `classScopeKey`
|
|
139
140
|
- `ownerDeptScopeKey`
|
|
140
141
|
- `ownerUserScopeKey`
|
|
141
142
|
5. Create page permission groups for entry visibility.
|
|
142
|
-
6.
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
6. For dynamic business ranges, declare
|
|
144
|
+
`permissions/scope-dimensions`, `permissions/scope-grant-sources`, and
|
|
145
|
+
`permissions/data-scope-policies`, then reference the policy from form
|
|
146
|
+
permission groups or Data View permission groups with
|
|
147
|
+
`dataPermission.type = "scope_policy"`.
|
|
148
|
+
7. Use condition-based data permissions only when the scope values are a small,
|
|
149
|
+
fixed set or map cleanly to platform organization/departments.
|
|
150
|
+
8. For delegated administrators, add role API permissions in
|
|
145
151
|
`src/resources/roles/<code>.json` with `apiPermissionCodes`, for example
|
|
146
152
|
`app:role:manage`, `app:page-permission-group:manage`,
|
|
147
153
|
`app:form-permission-group:manage`, and `app:organization:manage`.
|
|
148
|
-
|
|
154
|
+
9. Put sensitive writes behind App Functions with server-side role/scope checks.
|
|
149
155
|
|
|
150
156
|
Frontend button hiding is only user experience. It is not permission control.
|
|
151
157
|
Every sensitive action must still be protected by platform role/form permission
|
|
152
158
|
groups or backend-side JS_CODE checks.
|
|
153
159
|
|
|
160
|
+
Do not generate one role or one permission group per customer, project, region,
|
|
161
|
+
store, college, class, or other maintained business object. That configuration
|
|
162
|
+
will not scale and should be replaced with a form-maintained authorization
|
|
163
|
+
relationship and `scope_policy`.
|
|
164
|
+
|
|
154
165
|
Query parameters are not permission control either. They may prefill context,
|
|
155
166
|
select filters, or carry a signed/expiring ticket, but tampering with a query
|
|
156
167
|
parameter must not expand sensitive data access.
|
|
@@ -64,7 +64,7 @@ AI 在实现成熟交互前必须先判断是否已有可靠组件或库:
|
|
|
64
64
|
| 富文本编辑 | `EditorField` | 含图片上传集成 | 完整工具栏、平台图床、粘贴清洗 |
|
|
65
65
|
| 数字签名 | `DigitalSignatureField` | 平台级签名 | 签名采集、验证、归档 |
|
|
66
66
|
| 地理位置 | `LocationField` | 接入地图服务 | 定位、地图选点、地址解析 |
|
|
67
|
-
| 数据管理列表 | `DataManagementList` | 接入数据查询 API |
|
|
67
|
+
| 数据管理列表 | `DataManagementList` | 接入数据查询 API | 分页、筛选、导出、批量操作、字段权限、后端 action summary 按钮控制 |
|
|
68
68
|
|
|
69
69
|
### 示例
|
|
70
70
|
|
|
@@ -89,6 +89,12 @@ OSS 浏览器直传所需的 CORS 规则在 `src/resources/storage/<code>.json`
|
|
|
89
89
|
|
|
90
90
|
业务应用只能从 `openxiangda`、`openxiangda/runtime`、`openxiangda/runtime/react` 等公开入口导入组件和 SDK。不要为了减小包体积去引用 `openxiangda/packages/sdk/dist/...` 内部文件;React SPA 模板已提供 vendor chunk 分组,SDK 会把 `antd-mobile`、`dayjs` 等大型 UI 依赖交给应用构建器按路由拆分。
|
|
91
91
|
|
|
92
|
+
`DataManagementList` 默认 `permissionMode="auto"`,进入页面会读取表单 action
|
|
93
|
+
summary,并用 `can.create/export/import/delete/workflow/change_records` 隐藏新增、
|
|
94
|
+
导入、导出、删除、流程等按钮。`readonly=true` 仍会强制关闭新增、编辑、删除和
|
|
95
|
+
导入。`actionOverrides` 只能关闭按钮,不能扩大后端权限;敏感授权必须依赖后端
|
|
96
|
+
form actions、`scope_policy` 或 App Function 服务端校验。
|
|
97
|
+
|
|
92
98
|
> ✅ 一句话原则:**这些场景看到了,直接抄上表,不要自己写。**
|
|
93
99
|
|
|
94
100
|
---
|
|
@@ -27,8 +27,8 @@ Required design:
|
|
|
27
27
|
scalar scope keys, platform role member sync state.
|
|
28
28
|
- `src/resources/roles/*.json`: stable app role codes.
|
|
29
29
|
- `src/resources/permissions/page-groups/*.json`: menu, route, and page access.
|
|
30
|
-
- `src/resources/permissions/form-groups/*.json`:
|
|
31
|
-
|
|
30
|
+
- `src/resources/permissions/form-groups/*.json`: `actions`, data scope or
|
|
31
|
+
`dataPermission.type=scope_policy`, and `fieldAccessPolicy`.
|
|
32
32
|
- `apiPermissionCodes` on roles that are allowed to manage roles, role members,
|
|
33
33
|
permission groups, or organization accounts.
|
|
34
34
|
- Sync App Functions or trusted JS_CODE: sync organization units, accounts, and
|
|
@@ -49,11 +49,15 @@ Required design:
|
|
|
49
49
|
|
|
50
50
|
- Role assignment form with `UserSelectField` / department fields for members.
|
|
51
51
|
- Business scope fields such as region, department, project, customer, class, or
|
|
52
|
-
college
|
|
53
|
-
|
|
52
|
+
college. When the scope object count can grow past a few fixed values, model
|
|
53
|
+
the authorization as `scopeDimensions`, `scopeGrantSources`, and
|
|
54
|
+
`dataScopePolicies` instead of creating one role or permission group per
|
|
55
|
+
object.
|
|
54
56
|
- Role sync App Function or automation only updates app role members, not
|
|
55
57
|
account records.
|
|
56
|
-
- Roles, page groups, and form groups remain the
|
|
58
|
+
- Roles, page groups, and form groups remain the RBAC enforcement layer.
|
|
59
|
+
`scope_policy` becomes the data-range enforcement layer for dynamic business
|
|
60
|
+
objects.
|
|
57
61
|
|
|
58
62
|
Use this when HR/IT or the platform admin owns account lifecycle.
|
|
59
63
|
|
|
@@ -109,6 +113,9 @@ organization_unit
|
|
|
109
113
|
-> src/resources/roles
|
|
110
114
|
-> permissions/page-groups
|
|
111
115
|
-> permissions/form-groups
|
|
116
|
+
-> permissions/scope-dimensions
|
|
117
|
+
-> permissions/scope-grant-sources
|
|
118
|
+
-> permissions/data-scope-policies
|
|
112
119
|
-> sync App Functions / JS_CODE
|
|
113
120
|
-> PermissionBoundary for display
|
|
114
121
|
-> backend role/scope checks for sensitive writes
|
|
@@ -119,6 +126,81 @@ Pages may show or hide navigation with `useAppMenus`, `useCanAccessRoute`, and
|
|
|
119
126
|
platform permission group, public-access grant, or App Function role/scope
|
|
120
127
|
check.
|
|
121
128
|
|
|
129
|
+
## Business Scope Authorization
|
|
130
|
+
|
|
131
|
+
Use this layer for application-maintained authorization relationships:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
authorization form rows
|
|
135
|
+
-> scope grant source sync
|
|
136
|
+
-> materialized effective grants
|
|
137
|
+
-> Redis summary cache
|
|
138
|
+
-> form/data-view dataPermission.type=scope_policy
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The platform keeps RBAC and data range separate:
|
|
142
|
+
|
|
143
|
+
- Roles decide who can enter pages, submit/view forms, use operations, and
|
|
144
|
+
maintain permissions.
|
|
145
|
+
- Form `actions` decide what a role can do: `view`, `create`, `edit`, `delete`,
|
|
146
|
+
`export`, `import`, `change_records`, and `workflow`.
|
|
147
|
+
- Data View `actions` decide query capabilities: `query`, `stats`, `export`,
|
|
148
|
+
and `refresh`.
|
|
149
|
+
- Field access remains `fieldAccessPolicy`.
|
|
150
|
+
- Business scope policies decide which rows or submitted scope values a matched
|
|
151
|
+
role/user can access for the current action.
|
|
152
|
+
- App Functions must still check role/scope server-side before sensitive
|
|
153
|
+
writes.
|
|
154
|
+
|
|
155
|
+
Default semantics:
|
|
156
|
+
|
|
157
|
+
- Effective grants are personal grants plus current app role grants.
|
|
158
|
+
- Multi-dimension policies are `AND` by default.
|
|
159
|
+
- Empty grants deny.
|
|
160
|
+
- Admin bypass remains available, but use `openxiangda scope explain` and audit
|
|
161
|
+
logs to make bypass behavior visible.
|
|
162
|
+
- Authorization source rows should include an explicit status/enabled field and
|
|
163
|
+
grant sources should use `filterJson` or `enabledField` so drafts, disabled
|
|
164
|
+
rows, or rejected assignments are not materialized.
|
|
165
|
+
- Source forms must have a sync path. `sync: true` on the manifest runs during
|
|
166
|
+
publish; runtime changes need an automation/App Function or platform API call
|
|
167
|
+
that resyncs the affected grant source after create/update/delete.
|
|
168
|
+
- Policy target fields should normally be hidden scalar keys. If targeting a
|
|
169
|
+
JSONB option/person/department field directly, declare `valuePath: "value"` or
|
|
170
|
+
`componentType`; multi-value fields should be normalized into a scalar key or
|
|
171
|
+
a separate authorization row.
|
|
172
|
+
- App Function form read helpers enforce view permission for real callers. Do
|
|
173
|
+
not grant broad form read permission and then filter in function/page code.
|
|
174
|
+
- `DataManagementList` can hide buttons from the backend action summary, but it
|
|
175
|
+
is not the authority. Sensitive actions must be represented by form actions or
|
|
176
|
+
App Function server checks.
|
|
177
|
+
|
|
178
|
+
Do not model a dynamic business object set by generating dozens or hundreds of
|
|
179
|
+
roles or permission groups. Use a business authorization form and `scope_policy`
|
|
180
|
+
when the object set is maintained by users, imported from another system, or
|
|
181
|
+
expected to grow.
|
|
182
|
+
|
|
183
|
+
Recommended action pattern:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"code": "campus_profile_manage",
|
|
188
|
+
"formCode": "student_profile",
|
|
189
|
+
"name": "校区管理员档案权限",
|
|
190
|
+
"type": "view",
|
|
191
|
+
"roles": ["campus_admin"],
|
|
192
|
+
"actions": ["view", "edit", "export"],
|
|
193
|
+
"dataPermission": {
|
|
194
|
+
"type": "scope_policy",
|
|
195
|
+
"policyCode": "campus_row_scope"
|
|
196
|
+
},
|
|
197
|
+
"fieldAccessPolicy": {
|
|
198
|
+
"defaultAccess": "readonly",
|
|
199
|
+
"fields": [{ "fieldId": "remark", "access": "edit" }]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
122
204
|
## Role Setting Delegation
|
|
123
205
|
|
|
124
206
|
Setting application roles is itself permission controlled. A user can be a
|
|
@@ -179,6 +261,7 @@ Before implementation, write a matrix with at least these columns:
|
|
|
179
261
|
| Role source | role assignment form / platform role admin / roles manifest / public external role |
|
|
180
262
|
| Role setting delegation | which roles need `apiPermissionCodes`, who grants them first, and whether delegation can continue |
|
|
181
263
|
| Business scope fields | visible fields and hidden scalar keys |
|
|
264
|
+
| Business scope policy | dimensions, grant sources, policy code, sync trigger, empty-grant behavior |
|
|
182
265
|
| Page access | menu codes, route codes, path patterns, role codes |
|
|
183
266
|
| Form access | submit/view/manage operations, data scope, field access policy |
|
|
184
267
|
| Backend checks | App Functions that validate role/scope and deny bad input |
|
|
@@ -197,6 +280,10 @@ Before implementation, write a matrix with at least these columns:
|
|
|
197
280
|
- Mock role context, fake account IDs, fake form instance IDs, or empty-array
|
|
198
281
|
fallback displays.
|
|
199
282
|
- Giving users all form data and filtering in the browser.
|
|
283
|
+
- One platform role or one permission group per customer, project, region,
|
|
284
|
+
store, college, class, or other maintained object.
|
|
285
|
+
- Syncing application-maintained business scope into platform departments just
|
|
286
|
+
to reuse department data scope.
|
|
200
287
|
- Direct platform account API calls that bypass OpenXiangda organization SDK.
|
|
201
288
|
- Reusing public visitor roles as internal admin roles.
|
|
202
289
|
|
|
@@ -208,6 +295,9 @@ Before implementation, write a matrix with at least these columns:
|
|
|
208
295
|
- Admin roles that can manage roles, members, permission groups, or accounts
|
|
209
296
|
declare `apiPermissionCodes`.
|
|
210
297
|
- `openxiangda permission audit --json` has no high-risk gaps.
|
|
298
|
+
- `openxiangda scope explain <policyCode> --json` explains user, current role,
|
|
299
|
+
matched grants, cache state, and final policy for at least one allow and one
|
|
300
|
+
deny case.
|
|
211
301
|
- Allowed role paths and denied role paths are both tested.
|
|
212
302
|
- Query parameter tampering does not expand sensitive access.
|
|
213
303
|
- App Functions that mutate sensitive data verify `ctx.operator.roleCodes`,
|
|
@@ -209,6 +209,171 @@ Condition-style data permission:
|
|
|
209
209
|
}
|
|
210
210
|
```
|
|
211
211
|
|
|
212
|
+
## Business Scope Policy
|
|
213
|
+
|
|
214
|
+
Use `scope_policy` when data access follows application-maintained business
|
|
215
|
+
relationships such as user/role -> customer, project, region, store, campus,
|
|
216
|
+
college, class, merchant, or any other maintained object set.
|
|
217
|
+
|
|
218
|
+
Recommended boundary:
|
|
219
|
+
|
|
220
|
+
- Small fixed app: static roles + a few page/form permission groups.
|
|
221
|
+
- Platform organization fits the business: department data scope.
|
|
222
|
+
- Common complex business scope: authorization form + `scope_policy`.
|
|
223
|
+
- Public access: public-access grant + external role + permission groups.
|
|
224
|
+
- Sensitive writes: App Function service-side role/scope checks.
|
|
225
|
+
|
|
226
|
+
Do not create one platform role or one permission group per business object.
|
|
227
|
+
Do not force form-maintained business scope into platform departments. Frontend
|
|
228
|
+
hiding, query parameters, and page role checks are not sensitive authorization.
|
|
229
|
+
|
|
230
|
+
Declare scope resources:
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
// src/resources/permissions/scope-dimensions/college.json
|
|
234
|
+
{
|
|
235
|
+
"code": "college",
|
|
236
|
+
"name": "学院",
|
|
237
|
+
"sourceFormCode": "college",
|
|
238
|
+
"sourceValueField": "collegeCode",
|
|
239
|
+
"sourceLabelField": "collegeName",
|
|
240
|
+
"hierarchyMode": "flat"
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
// src/resources/permissions/scope-grant-sources/college_grants.json
|
|
246
|
+
{
|
|
247
|
+
"code": "college_grants",
|
|
248
|
+
"name": "学院授权",
|
|
249
|
+
"sourceFormCode": "college_grant",
|
|
250
|
+
"subjectMappings": [
|
|
251
|
+
{ "subjectType": "user", "field": "userId" },
|
|
252
|
+
{ "subjectType": "role", "field": "roleCode" }
|
|
253
|
+
],
|
|
254
|
+
"dimensionMappings": [
|
|
255
|
+
{ "dimensionCode": "college", "field": "collegeCode" }
|
|
256
|
+
],
|
|
257
|
+
"filterJson": {
|
|
258
|
+
"logic": "AND",
|
|
259
|
+
"rules": [
|
|
260
|
+
{ "field": "status", "op": "eq", "value": "enabled" }
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
"sync": true
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
```json
|
|
268
|
+
// src/resources/permissions/data-scope-policies/college_data.json
|
|
269
|
+
{
|
|
270
|
+
"code": "college_data",
|
|
271
|
+
"name": "学院数据",
|
|
272
|
+
"matchMode": "AND",
|
|
273
|
+
"rules": [
|
|
274
|
+
{
|
|
275
|
+
"dimensionCode": "college",
|
|
276
|
+
"field": "collegeCode",
|
|
277
|
+
"operation": "view"
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Reference the policy from a form permission group:
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"code": "college_view",
|
|
288
|
+
"formCode": "student",
|
|
289
|
+
"name": "学院查看学生",
|
|
290
|
+
"type": "view",
|
|
291
|
+
"roles": ["college_admin"],
|
|
292
|
+
"actions": ["view", "edit", "export"],
|
|
293
|
+
"dataPermission": {
|
|
294
|
+
"type": "scope_policy",
|
|
295
|
+
"policyCode": "college_data"
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Unified permission model:
|
|
301
|
+
|
|
302
|
+
- `scope_policy` means data range: which rows this user/current app role can
|
|
303
|
+
access.
|
|
304
|
+
- `actions` means operation ability: what the role can do to rows in that
|
|
305
|
+
range. Form actions are `view`, `create`, `edit`, `delete`, `export`,
|
|
306
|
+
`import`, `change_records`, and `workflow`.
|
|
307
|
+
- The platform still stores actions in the legacy `operations` field. Manifests
|
|
308
|
+
may use either `actions` or `operations`; prefer `actions` in new resources.
|
|
309
|
+
- `type: "submit"` is the legacy create group and maps to `actions:["create"]`.
|
|
310
|
+
- `export` is independent from `view` for new apps. Old apps may temporarily
|
|
311
|
+
rely on view-to-export compatibility, but `permission audit` should be used to
|
|
312
|
+
migrate to explicit `export`.
|
|
313
|
+
- `create` with `scope_policy` validates submitted scope fields; a campus admin
|
|
314
|
+
cannot create a row for another campus just because the create button is
|
|
315
|
+
visible.
|
|
316
|
+
- Frontend button hiding is UX only. The backend action check and action-specific
|
|
317
|
+
data range are authoritative.
|
|
318
|
+
|
|
319
|
+
Create permission with business scope:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"code": "college_student_create",
|
|
324
|
+
"formCode": "student",
|
|
325
|
+
"name": "学院管理员新增本学院学生",
|
|
326
|
+
"type": "submit",
|
|
327
|
+
"roles": ["college_admin"],
|
|
328
|
+
"actions": ["create"],
|
|
329
|
+
"dataPermission": {
|
|
330
|
+
"type": "scope_policy",
|
|
331
|
+
"policyCode": "college_data"
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
For Data View permission groups, use the same `dataPermission` shape inside the
|
|
337
|
+
view's `permissionGroups`. Data View actions are `query`, `stats`, `export`,
|
|
338
|
+
and `refresh`.
|
|
339
|
+
|
|
340
|
+
Default runtime semantics:
|
|
341
|
+
|
|
342
|
+
- Effective scope = personal grants + current app role grants.
|
|
343
|
+
- Multiple policy rules are explicit `AND` unless `matchMode: "OR"` is set.
|
|
344
|
+
- Empty grant sets deny data.
|
|
345
|
+
- App administrators still bypass data filtering, but audit/explain should make
|
|
346
|
+
the bypass visible.
|
|
347
|
+
- Redis caches per user/current-role/policy summaries with a scope version key;
|
|
348
|
+
large grant sets fall back to DB `EXISTS` checks instead of huge SQL `IN`
|
|
349
|
+
lists.
|
|
350
|
+
- Prefer hidden scalar target fields such as `collegeCode` or `projectId`.
|
|
351
|
+
If a policy directly targets a JSONB option/person/department field, declare
|
|
352
|
+
`valuePath: "value"` or a supported `componentType` so the runtime compares
|
|
353
|
+
the stored option value instead of the whole JSON object.
|
|
354
|
+
- `filterJson` filters authorization source rows before materializing grants.
|
|
355
|
+
Use it for enabled/approved/effective authorization rows; unsupported filter
|
|
356
|
+
operations do not match.
|
|
357
|
+
- Source authorization forms must be synced after changes. For formal apps,
|
|
358
|
+
create an automation/App Function that runs `openxiangda scope sync` or the
|
|
359
|
+
equivalent platform API after authorization rows are created, updated, or
|
|
360
|
+
deleted. Publishing with `"sync": true` only syncs during resource publish.
|
|
361
|
+
- App Function `ctx.form.queryOne/queryMany/getById` enforces the caller's view
|
|
362
|
+
permission when there is a real current user. No matching view permission
|
|
363
|
+
group means deny; backend system automations can still use trusted internal
|
|
364
|
+
paths and should add explicit role/scope checks for sensitive logic.
|
|
365
|
+
|
|
366
|
+
Useful commands:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
openxiangda scope dimension-upsert college --json-file src/resources/permissions/scope-dimensions/college.json --write-manifest
|
|
370
|
+
openxiangda scope grant-source-upsert college_grants --json-file src/resources/permissions/scope-grant-sources/college_grants.json --write-manifest
|
|
371
|
+
openxiangda scope policy-upsert college_data --json-file src/resources/permissions/data-scope-policies/college_data.json --write-manifest
|
|
372
|
+
openxiangda scope sync college_grants --json
|
|
373
|
+
openxiangda scope explain college_data --json
|
|
374
|
+
openxiangda permission audit --json
|
|
375
|
+
```
|
|
376
|
+
|
|
212
377
|
## Settings
|
|
213
378
|
|
|
214
379
|
Form settings are deep-merged:
|
|
@@ -52,6 +52,11 @@ openxiangda notification preview public_register_notice --body-json '{"payload":
|
|
|
52
52
|
|
|
53
53
|
openxiangda resource plan storage --profile <name>
|
|
54
54
|
openxiangda data-view upsert --json-file src/resources/data-views/public_lookup.json
|
|
55
|
+
openxiangda scope dimension-upsert college --json-file src/resources/permissions/scope-dimensions/college.json --write-manifest
|
|
56
|
+
openxiangda scope grant-source-upsert college_grants --json-file src/resources/permissions/scope-grant-sources/college_grants.json --write-manifest
|
|
57
|
+
openxiangda scope policy-upsert college_data --json-file src/resources/permissions/data-scope-policies/college_data.json --write-manifest
|
|
58
|
+
openxiangda scope sync college_grants --json
|
|
59
|
+
openxiangda scope explain college_data --json
|
|
55
60
|
openxiangda menu update public_register --json-file src/resources/menus/public_register.json --write-manifest
|
|
56
61
|
openxiangda permission audit --json
|
|
57
62
|
openxiangda permission role-update external_visitor --json-file src/resources/roles/external_visitor.json --write-manifest
|
|
@@ -290,6 +295,88 @@ Ticket 默认单次使用,首次换取 public session 后立即失效;只有
|
|
|
290
295
|
|
|
291
296
|
`grants.forms` 可以写本地 `formCode`,CLI 发布时会解析为真实 `formUuid`。
|
|
292
297
|
|
|
298
|
+
## Business Scope Permission — `src/resources/permissions/scope-*`
|
|
299
|
+
|
|
300
|
+
Use this when a form-maintained business authorization relationship controls
|
|
301
|
+
row visibility. The pattern replaces "one role / one permission group per
|
|
302
|
+
college, class, project, customer, region, store, etc."
|
|
303
|
+
|
|
304
|
+
Dimension:
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"code": "college",
|
|
309
|
+
"name": "学院",
|
|
310
|
+
"sourceFormCode": "college",
|
|
311
|
+
"sourceValueField": "collegeCode",
|
|
312
|
+
"sourceLabelField": "collegeName",
|
|
313
|
+
"hierarchyMode": "flat"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Grant source:
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"code": "college_grants",
|
|
322
|
+
"name": "学院授权",
|
|
323
|
+
"sourceFormCode": "college_grant",
|
|
324
|
+
"subjectMappings": [
|
|
325
|
+
{ "subjectType": "user", "field": "userId" },
|
|
326
|
+
{ "subjectType": "role", "field": "roleCode" }
|
|
327
|
+
],
|
|
328
|
+
"dimensionMappings": [
|
|
329
|
+
{ "dimensionCode": "college", "field": "collegeCode" }
|
|
330
|
+
],
|
|
331
|
+
"filterJson": {
|
|
332
|
+
"logic": "AND",
|
|
333
|
+
"rules": [
|
|
334
|
+
{ "field": "status", "op": "eq", "value": "enabled" }
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
"sync": true
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Policy:
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"code": "college_data",
|
|
346
|
+
"name": "学院数据",
|
|
347
|
+
"matchMode": "AND",
|
|
348
|
+
"rules": [
|
|
349
|
+
{ "dimensionCode": "college", "field": "collegeCode", "operation": "view" }
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Form permission group reference:
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"code": "college_view",
|
|
359
|
+
"formCode": "student",
|
|
360
|
+
"name": "学院查看学生",
|
|
361
|
+
"type": "view",
|
|
362
|
+
"roles": ["college_admin"],
|
|
363
|
+
"operations": ["view"],
|
|
364
|
+
"dataPermission": {
|
|
365
|
+
"type": "scope_policy",
|
|
366
|
+
"policyCode": "college_data"
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Data View permission groups use the same `dataPermission` object inside
|
|
372
|
+
`permissionGroups`. Empty grant sets deny; multiple policy rules are AND by
|
|
373
|
+
default; large grant sets use DB `EXISTS` instead of huge `IN` SQL. Prefer
|
|
374
|
+
hidden scalar target fields such as `collegeCode`; if the target is a JSONB
|
|
375
|
+
option/person/department field, set `valuePath: "value"` or `componentType`.
|
|
376
|
+
Runtime changes to the authorization source form need an automation/App
|
|
377
|
+
Function or explicit platform API call to resync the grant source; manifest
|
|
378
|
+
`sync: true` only runs during resource publish.
|
|
379
|
+
|
|
293
380
|
## 3. Connector — `src/resources/connectors/<code>.json`
|
|
294
381
|
|
|
295
382
|
```json
|
package/package.json
CHANGED
|
@@ -1021,6 +1021,12 @@ interface ProcessDefinition {
|
|
|
1021
1021
|
interface ViewPermissionSummary {
|
|
1022
1022
|
fieldPermissions: Record<string, 'FORM_FILED_HIDDEN' | 'FORM_FILED_VIEW' | 'FORM_FILED_EDIT'>;
|
|
1023
1023
|
operations: string[];
|
|
1024
|
+
actions?: string[];
|
|
1025
|
+
can?: Record<string, boolean>;
|
|
1026
|
+
fieldAccessPolicy?: any;
|
|
1027
|
+
hasFullAccess?: boolean;
|
|
1028
|
+
resourceType?: string;
|
|
1029
|
+
matchedGroupCodes?: string[];
|
|
1024
1030
|
}
|
|
1025
1031
|
/** 表单实例数据 */
|
|
1026
1032
|
interface FormInstanceData {
|
|
@@ -1021,6 +1021,12 @@ interface ProcessDefinition {
|
|
|
1021
1021
|
interface ViewPermissionSummary {
|
|
1022
1022
|
fieldPermissions: Record<string, 'FORM_FILED_HIDDEN' | 'FORM_FILED_VIEW' | 'FORM_FILED_EDIT'>;
|
|
1023
1023
|
operations: string[];
|
|
1024
|
+
actions?: string[];
|
|
1025
|
+
can?: Record<string, boolean>;
|
|
1026
|
+
fieldAccessPolicy?: any;
|
|
1027
|
+
hasFullAccess?: boolean;
|
|
1028
|
+
resourceType?: string;
|
|
1029
|
+
matchedGroupCodes?: string[];
|
|
1024
1030
|
}
|
|
1025
1031
|
/** 表单实例数据 */
|
|
1026
1032
|
interface FormInstanceData {
|