openxiangda-devkit-core 2.0.0-alpha.67 → 2.0.0-alpha.69
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/dist/application-services.d.ts +9 -0
- package/dist/application-services.d.ts.map +1 -1
- package/dist/application-services.js +21 -0
- package/dist/application-services.js.map +1 -1
- package/dist/compiler/bundle.d.ts.map +1 -1
- package/dist/compiler/bundle.js +42 -0
- package/dist/compiler/bundle.js.map +1 -1
- package/dist/compiler/config.d.ts +146 -1
- package/dist/compiler/config.d.ts.map +1 -1
- package/dist/compiler/config.js +248 -3
- package/dist/compiler/config.js.map +1 -1
- package/dist/control-plane-client.d.ts +10 -1
- package/dist/control-plane-client.d.ts.map +1 -1
- package/dist/control-plane-client.js +9 -0
- package/dist/control-plane-client.js.map +1 -1
- package/package.json +2 -2
package/dist/compiler/config.js
CHANGED
|
@@ -18,6 +18,112 @@ const DATE_TRIGGER_RESERVED_DATA_FIELDS = new Set([
|
|
|
18
18
|
'dueAt',
|
|
19
19
|
]);
|
|
20
20
|
const APP_DATA_FIELD_TYPES = new Set(DATA_FIELD_TYPES);
|
|
21
|
+
const authorizationProjectionFieldPathSchema = {
|
|
22
|
+
type: 'string',
|
|
23
|
+
pattern: '^[A-Za-z_][A-Za-z0-9_]{0,127}(?:\\.(?:value|snapshot\\.[A-Za-z_][A-Za-z0-9_]{0,127}))?$',
|
|
24
|
+
};
|
|
25
|
+
const authorizationProjectionControlProperties = {
|
|
26
|
+
enabledField: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
pattern: '^[A-Za-z_][A-Za-z0-9_]{0,127}$',
|
|
29
|
+
},
|
|
30
|
+
effectiveFromField: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
pattern: '^[A-Za-z_][A-Za-z0-9_]{0,127}$',
|
|
33
|
+
},
|
|
34
|
+
effectiveToField: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
pattern: '^[A-Za-z_][A-Za-z0-9_]{0,127}$',
|
|
37
|
+
},
|
|
38
|
+
failureMode: { const: 'strict' },
|
|
39
|
+
};
|
|
40
|
+
const roleMembershipSourceInputSchema = {
|
|
41
|
+
type: 'object',
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
required: [
|
|
44
|
+
'code',
|
|
45
|
+
'name',
|
|
46
|
+
'resourceCode',
|
|
47
|
+
'userIdField',
|
|
48
|
+
'roleCode',
|
|
49
|
+
'failureMode',
|
|
50
|
+
],
|
|
51
|
+
properties: {
|
|
52
|
+
code: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
pattern: '^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$',
|
|
55
|
+
maxLength: 100,
|
|
56
|
+
},
|
|
57
|
+
name: { type: 'string', minLength: 1, maxLength: 255 },
|
|
58
|
+
resourceCode: { type: 'string' },
|
|
59
|
+
userIdField: authorizationProjectionFieldPathSchema,
|
|
60
|
+
roleCode: { type: 'string' },
|
|
61
|
+
...authorizationProjectionControlProperties,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const relationshipGrantSourceInputSchema = {
|
|
65
|
+
type: 'object',
|
|
66
|
+
additionalProperties: false,
|
|
67
|
+
required: [
|
|
68
|
+
'code',
|
|
69
|
+
'name',
|
|
70
|
+
'resourceCode',
|
|
71
|
+
'subject',
|
|
72
|
+
'relationCode',
|
|
73
|
+
'targetResourceCode',
|
|
74
|
+
'resourceIdField',
|
|
75
|
+
'operations',
|
|
76
|
+
'failureMode',
|
|
77
|
+
],
|
|
78
|
+
properties: {
|
|
79
|
+
code: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
pattern: '^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$',
|
|
82
|
+
maxLength: 100,
|
|
83
|
+
},
|
|
84
|
+
name: { type: 'string', minLength: 1, maxLength: 255 },
|
|
85
|
+
resourceCode: { type: 'string' },
|
|
86
|
+
subject: {
|
|
87
|
+
oneOf: [
|
|
88
|
+
{
|
|
89
|
+
type: 'object',
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
required: ['type', 'userIdField'],
|
|
92
|
+
properties: {
|
|
93
|
+
type: { const: 'user' },
|
|
94
|
+
userIdField: authorizationProjectionFieldPathSchema,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'object',
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
required: ['type', 'userIdField', 'roleCode'],
|
|
101
|
+
properties: {
|
|
102
|
+
type: { const: 'role_membership' },
|
|
103
|
+
userIdField: authorizationProjectionFieldPathSchema,
|
|
104
|
+
roleCode: { type: 'string' },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
relationCode: { type: 'string', minLength: 1, maxLength: 128 },
|
|
110
|
+
targetResourceCode: { type: 'string' },
|
|
111
|
+
resourceIdField: authorizationProjectionFieldPathSchema,
|
|
112
|
+
operations: {
|
|
113
|
+
type: 'array',
|
|
114
|
+
minItems: 1,
|
|
115
|
+
maxItems: 20,
|
|
116
|
+
uniqueItems: true,
|
|
117
|
+
items: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
minLength: 1,
|
|
120
|
+
maxLength: 64,
|
|
121
|
+
pattern: '^[A-Za-z0-9][A-Za-z0-9:._*-]{0,63}$',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
...authorizationProjectionControlProperties,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
21
127
|
export const openXiangdaAppConfigSchema = {
|
|
22
128
|
$id: 'openxiangda.app-config/v3',
|
|
23
129
|
type: 'object',
|
|
@@ -172,6 +278,16 @@ export const openXiangdaAppConfigSchema = {
|
|
|
172
278
|
maxItems: 100,
|
|
173
279
|
items: { type: 'object' },
|
|
174
280
|
},
|
|
281
|
+
roleMembershipSources: {
|
|
282
|
+
type: 'array',
|
|
283
|
+
maxItems: 100,
|
|
284
|
+
items: roleMembershipSourceInputSchema,
|
|
285
|
+
},
|
|
286
|
+
relationshipGrantSources: {
|
|
287
|
+
type: 'array',
|
|
288
|
+
maxItems: 100,
|
|
289
|
+
items: relationshipGrantSourceInputSchema,
|
|
290
|
+
},
|
|
175
291
|
dataPolicies: {
|
|
176
292
|
type: 'array',
|
|
177
293
|
maxItems: 100,
|
|
@@ -637,6 +753,8 @@ export function validateAppConfig(value) {
|
|
|
637
753
|
for (const [name, values] of [
|
|
638
754
|
['scopeDimensions', authz.scopeDimensions],
|
|
639
755
|
['scopeSources', authz.scopeSources],
|
|
756
|
+
['roleMembershipSources', authz.roleMembershipSources],
|
|
757
|
+
['relationshipGrantSources', authz.relationshipGrantSources],
|
|
640
758
|
['dataPolicies', authz.dataPolicies],
|
|
641
759
|
]) {
|
|
642
760
|
if (values !== undefined && !Array.isArray(values)) {
|
|
@@ -660,12 +778,25 @@ export function validateAppConfig(value) {
|
|
|
660
778
|
resourceCode,
|
|
661
779
|
new Set(fields.keys()),
|
|
662
780
|
]));
|
|
781
|
+
const resourceFieldDeclarations = new Map(dataResources.map(rawResource => {
|
|
782
|
+
const resource = object(rawResource);
|
|
783
|
+
const fields = Array.isArray(object(resource.schema).fields)
|
|
784
|
+
? object(resource.schema).fields
|
|
785
|
+
: [];
|
|
786
|
+
return [
|
|
787
|
+
string(resource.code),
|
|
788
|
+
new Map(fields.map(rawField => {
|
|
789
|
+
const field = object(rawField);
|
|
790
|
+
return [string(field.code), field];
|
|
791
|
+
})),
|
|
792
|
+
];
|
|
793
|
+
}));
|
|
663
794
|
const dimensionCodes = new Set();
|
|
664
795
|
dimensions.forEach((raw, index) => {
|
|
665
796
|
const dimension = object(raw);
|
|
666
797
|
const code = string(dimension.code);
|
|
667
798
|
const dimensionPath = `authz.scopeDimensions[${index}]`;
|
|
668
|
-
if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(code) ||
|
|
799
|
+
if (!/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$/.test(code) ||
|
|
669
800
|
dimensionCodes.has(code) ||
|
|
670
801
|
!string(dimension.name) ||
|
|
671
802
|
(dimension.valueType !== undefined &&
|
|
@@ -724,10 +855,10 @@ export function validateAppConfig(value) {
|
|
|
724
855
|
const roleCode = string(subject.roleCode);
|
|
725
856
|
const resourceCode = string(source.resourceCode);
|
|
726
857
|
const declaredFields = resourceFields.get(resourceCode);
|
|
727
|
-
if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(code) ||
|
|
858
|
+
if (!/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$/.test(code) ||
|
|
728
859
|
sourceCodes.has(code) ||
|
|
729
860
|
!string(source.name) ||
|
|
730
|
-
!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(resourceCode) ||
|
|
861
|
+
!/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$/.test(resourceCode) ||
|
|
731
862
|
!['user', 'role_membership'].includes(subjectType) ||
|
|
732
863
|
!SEMANTIC_FIELD_PATH_PATTERN.test(string(subject.userIdField)) ||
|
|
733
864
|
(subjectType === 'role_membership' && !roleCodes.has(roleCode)) ||
|
|
@@ -786,6 +917,120 @@ export function validateAppConfig(value) {
|
|
|
786
917
|
}
|
|
787
918
|
sourceCodes.add(code);
|
|
788
919
|
});
|
|
920
|
+
const projectionSourceCodes = new Set();
|
|
921
|
+
const roleMembershipSources = Array.isArray(authz.roleMembershipSources)
|
|
922
|
+
? authz.roleMembershipSources
|
|
923
|
+
: [];
|
|
924
|
+
const relationshipGrantSources = Array.isArray(authz.relationshipGrantSources)
|
|
925
|
+
? authz.relationshipGrantSources
|
|
926
|
+
: [];
|
|
927
|
+
const projectionUserPathValid = (path, fields) => {
|
|
928
|
+
const root = semanticFieldPathRoot(path);
|
|
929
|
+
return (SEMANTIC_FIELD_PATH_PATTERN.test(path) &&
|
|
930
|
+
path === `${root}.value` &&
|
|
931
|
+
string(fields?.get(root)?.type) === 'user.single');
|
|
932
|
+
};
|
|
933
|
+
const projectionControlFieldsValid = (source, fields) => source.failureMode === 'strict' &&
|
|
934
|
+
(source.enabledField === undefined ||
|
|
935
|
+
string(fields?.get(string(source.enabledField))?.type) === 'boolean') &&
|
|
936
|
+
(source.effectiveFromField === undefined ||
|
|
937
|
+
['date', 'datetime'].includes(string(fields?.get(string(source.effectiveFromField))?.type))) &&
|
|
938
|
+
(source.effectiveToField === undefined ||
|
|
939
|
+
['date', 'datetime'].includes(string(fields?.get(string(source.effectiveToField))?.type)));
|
|
940
|
+
roleMembershipSources.forEach((raw, index) => {
|
|
941
|
+
const source = object(raw);
|
|
942
|
+
const sourcePath = `authz.roleMembershipSources[${index}]`;
|
|
943
|
+
const unknownKeys = Object.keys(source).filter(key => ![
|
|
944
|
+
'code',
|
|
945
|
+
'name',
|
|
946
|
+
'resourceCode',
|
|
947
|
+
'userIdField',
|
|
948
|
+
'roleCode',
|
|
949
|
+
'enabledField',
|
|
950
|
+
'effectiveFromField',
|
|
951
|
+
'effectiveToField',
|
|
952
|
+
'failureMode',
|
|
953
|
+
].includes(key));
|
|
954
|
+
if (unknownKeys.length > 0) {
|
|
955
|
+
diagnostics.push(diagnostic('APP_CONFIG_SCHEMA_INVALID', `role membership source 包含未知属性 ${unknownKeys.join(', ')}`, sourcePath));
|
|
956
|
+
}
|
|
957
|
+
const code = string(source.code);
|
|
958
|
+
const resourceCode = string(source.resourceCode);
|
|
959
|
+
const fields = resourceFieldDeclarations.get(resourceCode);
|
|
960
|
+
if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(code) ||
|
|
961
|
+
code.length > 100 ||
|
|
962
|
+
projectionSourceCodes.has(code) ||
|
|
963
|
+
!string(source.name) ||
|
|
964
|
+
!fields ||
|
|
965
|
+
!projectionUserPathValid(string(source.userIdField), fields) ||
|
|
966
|
+
!roleCodes.has(string(source.roleCode)) ||
|
|
967
|
+
!projectionControlFieldsValid(source, fields)) {
|
|
968
|
+
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_ROLE_MEMBERSHIP_SOURCE_INVALID', 'role membership source 必须声明唯一来源、user.single.value、已声明 package role 与严格控制字段', sourcePath));
|
|
969
|
+
}
|
|
970
|
+
projectionSourceCodes.add(code);
|
|
971
|
+
});
|
|
972
|
+
relationshipGrantSources.forEach((raw, index) => {
|
|
973
|
+
const source = object(raw);
|
|
974
|
+
const sourcePath = `authz.relationshipGrantSources[${index}]`;
|
|
975
|
+
const unknownKeys = Object.keys(source).filter(key => ![
|
|
976
|
+
'code',
|
|
977
|
+
'name',
|
|
978
|
+
'resourceCode',
|
|
979
|
+
'subject',
|
|
980
|
+
'relationCode',
|
|
981
|
+
'targetResourceCode',
|
|
982
|
+
'resourceIdField',
|
|
983
|
+
'operations',
|
|
984
|
+
'enabledField',
|
|
985
|
+
'effectiveFromField',
|
|
986
|
+
'effectiveToField',
|
|
987
|
+
'failureMode',
|
|
988
|
+
].includes(key));
|
|
989
|
+
if (unknownKeys.length > 0) {
|
|
990
|
+
diagnostics.push(diagnostic('APP_CONFIG_SCHEMA_INVALID', `relationship grant source 包含未知属性 ${unknownKeys.join(', ')}`, sourcePath));
|
|
991
|
+
}
|
|
992
|
+
const code = string(source.code);
|
|
993
|
+
const resourceCode = string(source.resourceCode);
|
|
994
|
+
const targetResourceCode = string(source.targetResourceCode);
|
|
995
|
+
const fields = resourceFieldDeclarations.get(resourceCode);
|
|
996
|
+
const subject = object(source.subject);
|
|
997
|
+
const subjectType = string(subject.type);
|
|
998
|
+
const subjectUnknownKeys = Object.keys(subject).filter(key => !['type', 'userIdField', 'roleCode'].includes(key));
|
|
999
|
+
if (subjectUnknownKeys.length > 0) {
|
|
1000
|
+
diagnostics.push(diagnostic('APP_CONFIG_SCHEMA_INVALID', `relationship grant subject 包含未知属性 ${subjectUnknownKeys.join(', ')}`, `${sourcePath}.subject`));
|
|
1001
|
+
}
|
|
1002
|
+
const resourceIdPath = string(source.resourceIdField);
|
|
1003
|
+
const resourceIdRoot = semanticFieldPathRoot(resourceIdPath);
|
|
1004
|
+
const resourceIdField = fields?.get(resourceIdRoot);
|
|
1005
|
+
const resourceReference = object(resourceIdField?.source);
|
|
1006
|
+
const resourceIdValid = (resourceIdPath === 'id' && targetResourceCode === resourceCode) ||
|
|
1007
|
+
(resourceIdPath === `${resourceIdRoot}.value` &&
|
|
1008
|
+
string(resourceIdField?.type) === 'resource-ref.single' &&
|
|
1009
|
+
string(resourceReference.resourceCode) === targetResourceCode);
|
|
1010
|
+
const operations = Array.isArray(source.operations)
|
|
1011
|
+
? source.operations.map(string)
|
|
1012
|
+
: [];
|
|
1013
|
+
if (!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(code) ||
|
|
1014
|
+
code.length > 100 ||
|
|
1015
|
+
projectionSourceCodes.has(code) ||
|
|
1016
|
+
!string(source.name) ||
|
|
1017
|
+
!fields ||
|
|
1018
|
+
!resourceFieldDeclarations.has(targetResourceCode) ||
|
|
1019
|
+
!['user', 'role_membership'].includes(subjectType) ||
|
|
1020
|
+
!projectionUserPathValid(string(subject.userIdField), fields) ||
|
|
1021
|
+
(subjectType === 'role_membership' &&
|
|
1022
|
+
!roleCodes.has(string(subject.roleCode))) ||
|
|
1023
|
+
!/^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/.test(string(source.relationCode)) ||
|
|
1024
|
+
!resourceIdValid ||
|
|
1025
|
+
operations.length === 0 ||
|
|
1026
|
+
operations.length > 20 ||
|
|
1027
|
+
new Set(operations).size !== operations.length ||
|
|
1028
|
+
operations.some(operation => !/^[A-Za-z0-9][A-Za-z0-9:._*-]{0,63}$/.test(operation)) ||
|
|
1029
|
+
!projectionControlFieldsValid(source, fields)) {
|
|
1030
|
+
diagnostics.push(diagnostic('APP_CONFIG_AUTHZ_RELATIONSHIP_GRANT_SOURCE_INVALID', 'relationship grant source 必须声明唯一来源、有效主体/目标关系、常量操作与严格控制字段', sourcePath));
|
|
1031
|
+
}
|
|
1032
|
+
projectionSourceCodes.add(code);
|
|
1033
|
+
});
|
|
789
1034
|
const policyCodes = new Set();
|
|
790
1035
|
policies.forEach((raw, index) => {
|
|
791
1036
|
const policy = object(raw);
|