openxiangda 1.0.138 → 1.0.139
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 +21 -0
- package/lib/design-gates.js +1 -1
- package/openxiangda-skills/references/permission-design-patterns.md +5 -3
- package/openxiangda-skills/references/permissions-settings.md +6 -4
- package/openxiangda-skills/references/resource-manifest-cheatsheet.md +7 -3
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -7079,6 +7079,12 @@ function validateResourceItem(kind, item, errors, warnings) {
|
|
|
7079
7079
|
if (kind === 'scopeGrantSources') {
|
|
7080
7080
|
if (!item.name) warnings.push(`${label}: 未声明 name,将使用 code`);
|
|
7081
7081
|
if (!item.sourceFormUuid && !item.formUuid) errors.push(`${label}: 缺少 sourceFormUuid`);
|
|
7082
|
+
if (
|
|
7083
|
+
item.syncMode !== undefined &&
|
|
7084
|
+
!['on_write', 'manual'].includes(String(item.syncMode))
|
|
7085
|
+
) {
|
|
7086
|
+
errors.push(`${label}: syncMode 只能是 on_write 或 manual`);
|
|
7087
|
+
}
|
|
7082
7088
|
const subjectMappings = item.subjectMappings || item.subjects;
|
|
7083
7089
|
const dimensionMappings = item.dimensionMappings || item.dimensions;
|
|
7084
7090
|
if (!Array.isArray(subjectMappings) || subjectMappings.length === 0) {
|
|
@@ -7159,6 +7165,11 @@ function validateScopePermissionResources(manifest, errors, warnings) {
|
|
|
7159
7165
|
.map(String)
|
|
7160
7166
|
);
|
|
7161
7167
|
const scopePolicyRefs = [];
|
|
7168
|
+
for (const source of manifest.scopeGrantSources || []) {
|
|
7169
|
+
if (String(source.syncMode || '').trim() === 'manual') {
|
|
7170
|
+
warnings.push(`${resourceLabel('scopeGrantSources', source)}: syncMode=manual,运行时来源表单变更不会自动物化;请确认有显式 scope sync 作业`);
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
7162
7173
|
const inspectPermissionGroup = (kind, item) => {
|
|
7163
7174
|
const groups = kind === 'dataViews'
|
|
7164
7175
|
? (Array.isArray(item.permissionGroups) ? item.permissionGroups : [])
|
|
@@ -10610,10 +10621,20 @@ function normalizeScopeGrantSourceManifest(source, bound) {
|
|
|
10610
10621
|
effectiveFromField: source.effectiveFromField,
|
|
10611
10622
|
effectiveToField: source.effectiveToField,
|
|
10612
10623
|
filterJson: source.filterJson || source.filter,
|
|
10624
|
+
syncMode: normalizeScopeGrantSourceSyncMode(source.syncMode, source.sync),
|
|
10613
10625
|
enabled: source.enabled !== false,
|
|
10614
10626
|
});
|
|
10615
10627
|
}
|
|
10616
10628
|
|
|
10629
|
+
function normalizeScopeGrantSourceSyncMode(value, legacySync) {
|
|
10630
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
10631
|
+
if (['manual', 'off', 'none', 'disabled', 'false'].includes(raw)) {
|
|
10632
|
+
return 'manual';
|
|
10633
|
+
}
|
|
10634
|
+
if (legacySync === false) return 'manual';
|
|
10635
|
+
return 'on_write';
|
|
10636
|
+
}
|
|
10637
|
+
|
|
10617
10638
|
function normalizeDataScopePolicyManifest(policy) {
|
|
10618
10639
|
const code = policy.code || policy.resourceCode;
|
|
10619
10640
|
return stripUndefinedValues({
|
package/lib/design-gates.js
CHANGED
|
@@ -135,7 +135,7 @@ const DESIGN_GATE_TOPICS = [
|
|
|
135
135
|
'正式后台优先选择 managed-platform-account;如果平台账号已存在,选择 existing-platform-user-assignment;固定内部门户选择 static-role-permission',
|
|
136
136
|
'query-param-context 仅用于低风险页面上下文、筛选条件或公开 ticket 输入,不作为敏感数据授权依据',
|
|
137
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',
|
|
138
|
+
'复杂业务范围优先写 permissions/scope-dimensions、permissions/scope-grant-sources、permissions/data-scope-policies,并在表单权限组或 data-view 权限组使用 dataPermission.type=scope_policy;授权来源默认 syncMode=on_write,由平台在来源表单写入后自动物化',
|
|
139
139
|
'scope_policy 只表达数据范围,actions 表达操作能力;按钮隐藏只是 UX,后端 action check 才是权威',
|
|
140
140
|
'scope_policy 默认语义是个人授权 + 当前应用角色授权;多维 rules 显式 AND;空授权集合拒绝;管理员绕过但可审计',
|
|
141
141
|
'能管理角色或给别人分配角色的应用角色必须在 roles manifest 声明 apiPermissionCodes;至少包含 app:role:manage,按需加入 app:page-permission-group:manage、app:form-permission-group:manage、app:organization:manage',
|
|
@@ -162,9 +162,11 @@ Default semantics:
|
|
|
162
162
|
- Authorization source rows should include an explicit status/enabled field and
|
|
163
163
|
grant sources should use `filterJson` or `enabledField` so drafts, disabled
|
|
164
164
|
rows, or rejected assignments are not materialized.
|
|
165
|
-
- Source forms
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
- Source authorization forms are synced by the platform after form
|
|
166
|
+
create/update/delete/import when the grant source uses the default
|
|
167
|
+
`syncMode: "on_write"`. Use `syncMode: "manual"` only for externally
|
|
168
|
+
maintained sources or unusual bulk jobs; `sync: true` still performs an
|
|
169
|
+
immediate full sync during resource publish.
|
|
168
170
|
- Policy target fields should normally be hidden scalar keys. If targeting a
|
|
169
171
|
JSONB option/person/department field directly, declare `valuePath: "value"` or
|
|
170
172
|
`componentType`; multi-value fields should be normalized into a scalar key or
|
|
@@ -260,6 +260,7 @@ Declare scope resources:
|
|
|
260
260
|
{ "field": "status", "op": "eq", "value": "enabled" }
|
|
261
261
|
]
|
|
262
262
|
},
|
|
263
|
+
"syncMode": "on_write",
|
|
263
264
|
"sync": true
|
|
264
265
|
}
|
|
265
266
|
```
|
|
@@ -354,10 +355,11 @@ Default runtime semantics:
|
|
|
354
355
|
- `filterJson` filters authorization source rows before materializing grants.
|
|
355
356
|
Use it for enabled/approved/effective authorization rows; unsupported filter
|
|
356
357
|
operations do not match.
|
|
357
|
-
- Source authorization forms
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
- Source authorization forms are synced by the platform after create/update/
|
|
359
|
+
delete/import when the grant source uses the default `syncMode: "on_write"`.
|
|
360
|
+
Use `syncMode: "manual"` only for externally maintained sources or unusual
|
|
361
|
+
bulk jobs that explicitly call `openxiangda scope sync`. Publishing with
|
|
362
|
+
`"sync": true` still performs an immediate full sync during resource publish.
|
|
361
363
|
- App Function `ctx.form.queryOne/queryMany/getById` enforces the caller's view
|
|
362
364
|
permission when there is a real current user. No matching view permission
|
|
363
365
|
group means deny; backend system automations can still use trusted internal
|
|
@@ -334,6 +334,7 @@ Grant source:
|
|
|
334
334
|
{ "field": "status", "op": "eq", "value": "enabled" }
|
|
335
335
|
]
|
|
336
336
|
},
|
|
337
|
+
"syncMode": "on_write",
|
|
337
338
|
"sync": true
|
|
338
339
|
}
|
|
339
340
|
```
|
|
@@ -373,9 +374,12 @@ Data View permission groups use the same `dataPermission` object inside
|
|
|
373
374
|
default; large grant sets use DB `EXISTS` instead of huge `IN` SQL. Prefer
|
|
374
375
|
hidden scalar target fields such as `collegeCode`; if the target is a JSONB
|
|
375
376
|
option/person/department field, set `valuePath: "value"` or `componentType`.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
`
|
|
377
|
+
Authorization source form changes are synced by the platform after
|
|
378
|
+
create/update/delete/import when the grant source uses the default
|
|
379
|
+
`syncMode: "on_write"`. Use `syncMode: "manual"` only for externally maintained
|
|
380
|
+
sources or special bulk jobs that explicitly call `openxiangda scope sync`.
|
|
381
|
+
Manifest `sync: true` still performs an immediate full sync during resource
|
|
382
|
+
publish.
|
|
379
383
|
|
|
380
384
|
## 3. Connector — `src/resources/connectors/<code>.json`
|
|
381
385
|
|