@objectstack/plugin-security 4.0.4 → 4.1.0
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/README.md +97 -27
- package/dist/index.d.mts +5407 -566
- package/dist/index.d.ts +5407 -566
- package/dist/index.js +923 -183
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +921 -181
- package/dist/index.mjs.map +1 -1
- package/package.json +33 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -264
- package/src/field-masker.ts +0 -75
- package/src/index.ts +0 -16
- package/src/objects/index.ts +0 -10
- package/src/objects/sys-permission-set.object.ts +0 -94
- package/src/objects/sys-role.object.ts +0 -93
- package/src/permission-evaluator.ts +0 -112
- package/src/rls-compiler.ts +0 -143
- package/src/security-plugin.test.ts +0 -302
- package/src/security-plugin.ts +0 -181
- package/tsconfig.json +0 -18
package/CHANGELOG.md
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
# @objectstack/plugin-security
|
|
2
|
-
|
|
3
|
-
## 4.0.4
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies [326b66b]
|
|
8
|
-
- @objectstack/spec@4.0.4
|
|
9
|
-
- @objectstack/core@4.0.4
|
|
10
|
-
|
|
11
|
-
## 4.0.3
|
|
12
|
-
|
|
13
|
-
### Patch Changes
|
|
14
|
-
|
|
15
|
-
- @objectstack/spec@4.0.3
|
|
16
|
-
- @objectstack/core@4.0.3
|
|
17
|
-
|
|
18
|
-
## 4.0.2
|
|
19
|
-
|
|
20
|
-
### Patch Changes
|
|
21
|
-
|
|
22
|
-
- Updated dependencies [5f659e9]
|
|
23
|
-
- @objectstack/spec@4.0.2
|
|
24
|
-
- @objectstack/core@4.0.2
|
|
25
|
-
|
|
26
|
-
## 4.0.0
|
|
27
|
-
|
|
28
|
-
### Patch Changes
|
|
29
|
-
|
|
30
|
-
- Updated dependencies [f08ffc3]
|
|
31
|
-
- Updated dependencies [e0b0a78]
|
|
32
|
-
- @objectstack/spec@4.0.0
|
|
33
|
-
- @objectstack/core@4.0.0
|
|
34
|
-
|
|
35
|
-
## 3.3.1
|
|
36
|
-
|
|
37
|
-
### Patch Changes
|
|
38
|
-
|
|
39
|
-
- @objectstack/spec@3.3.1
|
|
40
|
-
- @objectstack/core@3.3.1
|
|
41
|
-
|
|
42
|
-
## 3.3.0
|
|
43
|
-
|
|
44
|
-
### Patch Changes
|
|
45
|
-
|
|
46
|
-
- @objectstack/spec@3.3.0
|
|
47
|
-
- @objectstack/core@3.3.0
|
|
48
|
-
|
|
49
|
-
## 3.2.9
|
|
50
|
-
|
|
51
|
-
### Patch Changes
|
|
52
|
-
|
|
53
|
-
- @objectstack/spec@3.2.9
|
|
54
|
-
- @objectstack/core@3.2.9
|
|
55
|
-
|
|
56
|
-
## 3.2.8
|
|
57
|
-
|
|
58
|
-
### Patch Changes
|
|
59
|
-
|
|
60
|
-
- @objectstack/spec@3.2.8
|
|
61
|
-
- @objectstack/core@3.2.8
|
|
62
|
-
|
|
63
|
-
## 3.2.7
|
|
64
|
-
|
|
65
|
-
### Patch Changes
|
|
66
|
-
|
|
67
|
-
- @objectstack/spec@3.2.7
|
|
68
|
-
- @objectstack/core@3.2.7
|
|
69
|
-
|
|
70
|
-
## 3.2.6
|
|
71
|
-
|
|
72
|
-
### Patch Changes
|
|
73
|
-
|
|
74
|
-
- @objectstack/spec@3.2.6
|
|
75
|
-
- @objectstack/core@3.2.6
|
|
76
|
-
|
|
77
|
-
## 3.2.5
|
|
78
|
-
|
|
79
|
-
### Patch Changes
|
|
80
|
-
|
|
81
|
-
- @objectstack/spec@3.2.5
|
|
82
|
-
- @objectstack/core@3.2.5
|
|
83
|
-
|
|
84
|
-
## 3.2.4
|
|
85
|
-
|
|
86
|
-
### Patch Changes
|
|
87
|
-
|
|
88
|
-
- @objectstack/spec@3.2.4
|
|
89
|
-
- @objectstack/core@3.2.4
|
|
90
|
-
|
|
91
|
-
## 3.2.3
|
|
92
|
-
|
|
93
|
-
### Patch Changes
|
|
94
|
-
|
|
95
|
-
- @objectstack/spec@3.2.3
|
|
96
|
-
- @objectstack/core@3.2.3
|
|
97
|
-
|
|
98
|
-
## 3.2.2
|
|
99
|
-
|
|
100
|
-
### Patch Changes
|
|
101
|
-
|
|
102
|
-
- Updated dependencies [46defbb]
|
|
103
|
-
- @objectstack/spec@3.2.2
|
|
104
|
-
- @objectstack/core@3.2.2
|
|
105
|
-
|
|
106
|
-
## 3.2.1
|
|
107
|
-
|
|
108
|
-
### Patch Changes
|
|
109
|
-
|
|
110
|
-
- Updated dependencies [850b546]
|
|
111
|
-
- @objectstack/spec@3.2.1
|
|
112
|
-
- @objectstack/core@3.2.1
|
|
113
|
-
|
|
114
|
-
## 3.2.0
|
|
115
|
-
|
|
116
|
-
### Patch Changes
|
|
117
|
-
|
|
118
|
-
- Updated dependencies [5901c29]
|
|
119
|
-
- @objectstack/spec@3.2.0
|
|
120
|
-
- @objectstack/core@3.2.0
|
|
121
|
-
|
|
122
|
-
## 3.1.1
|
|
123
|
-
|
|
124
|
-
### Patch Changes
|
|
125
|
-
|
|
126
|
-
- Updated dependencies [953d667]
|
|
127
|
-
- @objectstack/spec@3.1.1
|
|
128
|
-
- @objectstack/core@3.1.1
|
|
129
|
-
|
|
130
|
-
## 3.1.0
|
|
131
|
-
|
|
132
|
-
### Patch Changes
|
|
133
|
-
|
|
134
|
-
- Updated dependencies [0088830]
|
|
135
|
-
- @objectstack/spec@3.1.0
|
|
136
|
-
- @objectstack/core@3.1.0
|
|
137
|
-
|
|
138
|
-
## 3.0.11
|
|
139
|
-
|
|
140
|
-
### Patch Changes
|
|
141
|
-
|
|
142
|
-
- Updated dependencies [92d9d99]
|
|
143
|
-
- @objectstack/spec@3.0.11
|
|
144
|
-
- @objectstack/core@3.0.11
|
|
145
|
-
|
|
146
|
-
## 3.0.10
|
|
147
|
-
|
|
148
|
-
### Patch Changes
|
|
149
|
-
|
|
150
|
-
- Updated dependencies [d1e5d31]
|
|
151
|
-
- @objectstack/spec@3.0.10
|
|
152
|
-
- @objectstack/core@3.0.10
|
|
153
|
-
|
|
154
|
-
## 3.0.9
|
|
155
|
-
|
|
156
|
-
### Patch Changes
|
|
157
|
-
|
|
158
|
-
- Updated dependencies [15e0df6]
|
|
159
|
-
- @objectstack/spec@3.0.9
|
|
160
|
-
- @objectstack/core@3.0.9
|
|
161
|
-
|
|
162
|
-
## 3.0.8
|
|
163
|
-
|
|
164
|
-
### Patch Changes
|
|
165
|
-
|
|
166
|
-
- Updated dependencies [5a968a2]
|
|
167
|
-
- @objectstack/spec@3.0.8
|
|
168
|
-
- @objectstack/core@3.0.8
|
|
169
|
-
|
|
170
|
-
## 3.0.7
|
|
171
|
-
|
|
172
|
-
### Patch Changes
|
|
173
|
-
|
|
174
|
-
- Updated dependencies [0119bd7]
|
|
175
|
-
- Updated dependencies [5426bdf]
|
|
176
|
-
- @objectstack/spec@3.0.7
|
|
177
|
-
- @objectstack/core@3.0.7
|
|
178
|
-
|
|
179
|
-
## 3.0.6
|
|
180
|
-
|
|
181
|
-
### Patch Changes
|
|
182
|
-
|
|
183
|
-
- Updated dependencies [5df254c]
|
|
184
|
-
- @objectstack/spec@3.0.6
|
|
185
|
-
- @objectstack/core@3.0.6
|
|
186
|
-
|
|
187
|
-
## 3.0.5
|
|
188
|
-
|
|
189
|
-
### Patch Changes
|
|
190
|
-
|
|
191
|
-
- Updated dependencies [23a4a68]
|
|
192
|
-
- @objectstack/spec@3.0.5
|
|
193
|
-
- @objectstack/core@3.0.5
|
|
194
|
-
|
|
195
|
-
## 3.0.4
|
|
196
|
-
|
|
197
|
-
### Patch Changes
|
|
198
|
-
|
|
199
|
-
- Updated dependencies [d738987]
|
|
200
|
-
- @objectstack/spec@3.0.4
|
|
201
|
-
- @objectstack/core@3.0.4
|
|
202
|
-
|
|
203
|
-
## 3.0.3
|
|
204
|
-
|
|
205
|
-
### Patch Changes
|
|
206
|
-
|
|
207
|
-
- c7267f6: Patch release for maintenance updates and improvements.
|
|
208
|
-
- Updated dependencies [c7267f6]
|
|
209
|
-
- @objectstack/spec@3.0.3
|
|
210
|
-
- @objectstack/core@3.0.3
|
|
211
|
-
|
|
212
|
-
## 3.0.2
|
|
213
|
-
|
|
214
|
-
### Patch Changes
|
|
215
|
-
|
|
216
|
-
- Updated dependencies [28985f5]
|
|
217
|
-
- @objectstack/spec@3.0.2
|
|
218
|
-
- @objectstack/core@3.0.2
|
|
219
|
-
|
|
220
|
-
## 3.0.1
|
|
221
|
-
|
|
222
|
-
### Patch Changes
|
|
223
|
-
|
|
224
|
-
- Updated dependencies [389725a]
|
|
225
|
-
- @objectstack/spec@3.0.1
|
|
226
|
-
- @objectstack/core@3.0.1
|
|
227
|
-
|
|
228
|
-
## 3.0.0
|
|
229
|
-
|
|
230
|
-
### Major Changes
|
|
231
|
-
|
|
232
|
-
- Release v3.0.0 — unified version bump for all ObjectStack packages.
|
|
233
|
-
|
|
234
|
-
### Patch Changes
|
|
235
|
-
|
|
236
|
-
- Updated dependencies
|
|
237
|
-
- @objectstack/spec@3.0.0
|
|
238
|
-
- @objectstack/core@3.0.0
|
|
239
|
-
|
|
240
|
-
## 2.0.7
|
|
241
|
-
|
|
242
|
-
### Patch Changes
|
|
243
|
-
|
|
244
|
-
- Updated dependencies
|
|
245
|
-
- @objectstack/spec@2.0.7
|
|
246
|
-
- @objectstack/core@2.0.7
|
|
247
|
-
|
|
248
|
-
## 2.0.6
|
|
249
|
-
|
|
250
|
-
### Patch Changes
|
|
251
|
-
|
|
252
|
-
- Patch release for maintenance and stability improvements
|
|
253
|
-
- Updated dependencies
|
|
254
|
-
- @objectstack/spec@2.0.6
|
|
255
|
-
- @objectstack/core@2.0.6
|
|
256
|
-
|
|
257
|
-
## 2.0.5
|
|
258
|
-
|
|
259
|
-
### Patch Changes
|
|
260
|
-
|
|
261
|
-
- Unify all package versions with a patch release
|
|
262
|
-
- Updated dependencies
|
|
263
|
-
- @objectstack/spec@2.0.5
|
|
264
|
-
- @objectstack/core@2.0.5
|
package/src/field-masker.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { FieldPermission } from '@objectstack/spec/security';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* FieldMasker
|
|
7
|
-
*
|
|
8
|
-
* Applies field-level security by stripping restricted fields from query results.
|
|
9
|
-
*/
|
|
10
|
-
export class FieldMasker {
|
|
11
|
-
/**
|
|
12
|
-
* Mask fields in query results based on field permissions.
|
|
13
|
-
* Removes fields that the user does not have read access to.
|
|
14
|
-
*/
|
|
15
|
-
maskResults(
|
|
16
|
-
results: any | any[],
|
|
17
|
-
fieldPermissions: Record<string, FieldPermission>,
|
|
18
|
-
_objectName: string
|
|
19
|
-
): any | any[] {
|
|
20
|
-
// If no field permissions defined, return results as-is
|
|
21
|
-
if (Object.keys(fieldPermissions).length === 0) return results;
|
|
22
|
-
|
|
23
|
-
// Get list of non-readable fields
|
|
24
|
-
const hiddenFields = Object.entries(fieldPermissions)
|
|
25
|
-
.filter(([, perm]) => !perm.readable)
|
|
26
|
-
.map(([field]) => field);
|
|
27
|
-
|
|
28
|
-
if (hiddenFields.length === 0) return results;
|
|
29
|
-
|
|
30
|
-
if (Array.isArray(results)) {
|
|
31
|
-
return results.map(record => this.maskRecord(record, hiddenFields));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return this.maskRecord(results, hiddenFields);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Get non-editable fields for use in write operations.
|
|
39
|
-
* Returns a list of field names that should be stripped from incoming data.
|
|
40
|
-
*/
|
|
41
|
-
getNonEditableFields(
|
|
42
|
-
fieldPermissions: Record<string, FieldPermission>
|
|
43
|
-
): string[] {
|
|
44
|
-
return Object.entries(fieldPermissions)
|
|
45
|
-
.filter(([, perm]) => !perm.editable)
|
|
46
|
-
.map(([field]) => field);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Strip non-editable fields from write data.
|
|
51
|
-
*/
|
|
52
|
-
stripNonEditableFields(
|
|
53
|
-
data: Record<string, any>,
|
|
54
|
-
fieldPermissions: Record<string, FieldPermission>
|
|
55
|
-
): Record<string, any> {
|
|
56
|
-
const nonEditable = this.getNonEditableFields(fieldPermissions);
|
|
57
|
-
if (nonEditable.length === 0) return data;
|
|
58
|
-
|
|
59
|
-
const result = { ...data };
|
|
60
|
-
for (const field of nonEditable) {
|
|
61
|
-
delete result[field];
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private maskRecord(record: any, hiddenFields: string[]): any {
|
|
67
|
-
if (!record || typeof record !== 'object') return record;
|
|
68
|
-
|
|
69
|
-
const result = { ...record };
|
|
70
|
-
for (const field of hiddenFields) {
|
|
71
|
-
delete result[field];
|
|
72
|
-
}
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @objectstack/plugin-security
|
|
5
|
-
*
|
|
6
|
-
* Security Plugin for ObjectStack
|
|
7
|
-
* Provides RBAC, Row-Level Security (RLS), and Field-Level Security runtime.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export { SecurityPlugin } from './security-plugin.js';
|
|
11
|
-
export { PermissionEvaluator } from './permission-evaluator.js';
|
|
12
|
-
export { RLSCompiler } from './rls-compiler.js';
|
|
13
|
-
export { FieldMasker } from './field-masker.js';
|
|
14
|
-
|
|
15
|
-
// System Object Definitions (sys namespace)
|
|
16
|
-
export { SysRole, SysPermissionSet } from './objects/index.js';
|
package/src/objects/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Security Plugin — System Object Definitions (sys namespace)
|
|
5
|
-
*
|
|
6
|
-
* Canonical ObjectSchema definitions for security-related system objects.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { SysRole } from './sys-role.object.js';
|
|
10
|
-
export { SysPermissionSet } from './sys-permission-set.object.js';
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* sys_permission_set — System Permission Set Object
|
|
7
|
-
*
|
|
8
|
-
* Named groupings of fine-grained permissions.
|
|
9
|
-
* Permission sets can be assigned to roles or directly to users
|
|
10
|
-
* for granular access control.
|
|
11
|
-
*
|
|
12
|
-
* @namespace sys
|
|
13
|
-
*/
|
|
14
|
-
export const SysPermissionSet = ObjectSchema.create({
|
|
15
|
-
namespace: 'sys',
|
|
16
|
-
name: 'permission_set',
|
|
17
|
-
label: 'Permission Set',
|
|
18
|
-
pluralLabel: 'Permission Sets',
|
|
19
|
-
icon: 'lock',
|
|
20
|
-
isSystem: true,
|
|
21
|
-
description: 'Named permission groupings for fine-grained access control',
|
|
22
|
-
titleFormat: '{name}',
|
|
23
|
-
compactLayout: ['name', 'label', 'active'],
|
|
24
|
-
|
|
25
|
-
fields: {
|
|
26
|
-
id: Field.text({
|
|
27
|
-
label: 'Permission Set ID',
|
|
28
|
-
required: true,
|
|
29
|
-
readonly: true,
|
|
30
|
-
}),
|
|
31
|
-
|
|
32
|
-
created_at: Field.datetime({
|
|
33
|
-
label: 'Created At',
|
|
34
|
-
defaultValue: 'NOW()',
|
|
35
|
-
readonly: true,
|
|
36
|
-
}),
|
|
37
|
-
|
|
38
|
-
updated_at: Field.datetime({
|
|
39
|
-
label: 'Updated At',
|
|
40
|
-
defaultValue: 'NOW()',
|
|
41
|
-
readonly: true,
|
|
42
|
-
}),
|
|
43
|
-
|
|
44
|
-
name: Field.text({
|
|
45
|
-
label: 'API Name',
|
|
46
|
-
required: true,
|
|
47
|
-
searchable: true,
|
|
48
|
-
maxLength: 100,
|
|
49
|
-
description: 'Unique machine name for the permission set',
|
|
50
|
-
}),
|
|
51
|
-
|
|
52
|
-
label: Field.text({
|
|
53
|
-
label: 'Display Name',
|
|
54
|
-
required: true,
|
|
55
|
-
maxLength: 255,
|
|
56
|
-
}),
|
|
57
|
-
|
|
58
|
-
description: Field.textarea({
|
|
59
|
-
label: 'Description',
|
|
60
|
-
required: false,
|
|
61
|
-
}),
|
|
62
|
-
|
|
63
|
-
object_permissions: Field.textarea({
|
|
64
|
-
label: 'Object Permissions',
|
|
65
|
-
required: false,
|
|
66
|
-
description: 'JSON-serialized object-level CRUD permissions',
|
|
67
|
-
}),
|
|
68
|
-
|
|
69
|
-
field_permissions: Field.textarea({
|
|
70
|
-
label: 'Field Permissions',
|
|
71
|
-
required: false,
|
|
72
|
-
description: 'JSON-serialized field-level read/write permissions',
|
|
73
|
-
}),
|
|
74
|
-
|
|
75
|
-
active: Field.boolean({
|
|
76
|
-
label: 'Active',
|
|
77
|
-
defaultValue: true,
|
|
78
|
-
}),
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
indexes: [
|
|
82
|
-
{ fields: ['name'], unique: true },
|
|
83
|
-
{ fields: ['active'] },
|
|
84
|
-
],
|
|
85
|
-
|
|
86
|
-
enable: {
|
|
87
|
-
trackHistory: true,
|
|
88
|
-
searchable: true,
|
|
89
|
-
apiEnabled: true,
|
|
90
|
-
apiMethods: ['get', 'list', 'create', 'update', 'delete'],
|
|
91
|
-
trash: true,
|
|
92
|
-
mru: true,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* sys_role — System Role Object
|
|
7
|
-
*
|
|
8
|
-
* RBAC role definition for the ObjectStack platform.
|
|
9
|
-
* Roles group permissions and are assigned to users or members.
|
|
10
|
-
*
|
|
11
|
-
* @namespace sys
|
|
12
|
-
*/
|
|
13
|
-
export const SysRole = ObjectSchema.create({
|
|
14
|
-
namespace: 'sys',
|
|
15
|
-
name: 'role',
|
|
16
|
-
label: 'Role',
|
|
17
|
-
pluralLabel: 'Roles',
|
|
18
|
-
icon: 'shield',
|
|
19
|
-
isSystem: true,
|
|
20
|
-
description: 'Role definitions for RBAC access control',
|
|
21
|
-
titleFormat: '{name}',
|
|
22
|
-
compactLayout: ['name', 'label', 'active'],
|
|
23
|
-
|
|
24
|
-
fields: {
|
|
25
|
-
id: Field.text({
|
|
26
|
-
label: 'Role ID',
|
|
27
|
-
required: true,
|
|
28
|
-
readonly: true,
|
|
29
|
-
}),
|
|
30
|
-
|
|
31
|
-
created_at: Field.datetime({
|
|
32
|
-
label: 'Created At',
|
|
33
|
-
defaultValue: 'NOW()',
|
|
34
|
-
readonly: true,
|
|
35
|
-
}),
|
|
36
|
-
|
|
37
|
-
updated_at: Field.datetime({
|
|
38
|
-
label: 'Updated At',
|
|
39
|
-
defaultValue: 'NOW()',
|
|
40
|
-
readonly: true,
|
|
41
|
-
}),
|
|
42
|
-
|
|
43
|
-
name: Field.text({
|
|
44
|
-
label: 'API Name',
|
|
45
|
-
required: true,
|
|
46
|
-
searchable: true,
|
|
47
|
-
maxLength: 100,
|
|
48
|
-
description: 'Unique machine name for the role (e.g. admin, editor, viewer)',
|
|
49
|
-
}),
|
|
50
|
-
|
|
51
|
-
label: Field.text({
|
|
52
|
-
label: 'Display Name',
|
|
53
|
-
required: true,
|
|
54
|
-
maxLength: 255,
|
|
55
|
-
}),
|
|
56
|
-
|
|
57
|
-
description: Field.textarea({
|
|
58
|
-
label: 'Description',
|
|
59
|
-
required: false,
|
|
60
|
-
}),
|
|
61
|
-
|
|
62
|
-
permissions: Field.textarea({
|
|
63
|
-
label: 'Permissions',
|
|
64
|
-
required: false,
|
|
65
|
-
description: 'JSON-serialized array of permission strings',
|
|
66
|
-
}),
|
|
67
|
-
|
|
68
|
-
active: Field.boolean({
|
|
69
|
-
label: 'Active',
|
|
70
|
-
defaultValue: true,
|
|
71
|
-
}),
|
|
72
|
-
|
|
73
|
-
is_default: Field.boolean({
|
|
74
|
-
label: 'Default Role',
|
|
75
|
-
defaultValue: false,
|
|
76
|
-
description: 'Automatically assigned to new users',
|
|
77
|
-
}),
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
indexes: [
|
|
81
|
-
{ fields: ['name'], unique: true },
|
|
82
|
-
{ fields: ['active'] },
|
|
83
|
-
],
|
|
84
|
-
|
|
85
|
-
enable: {
|
|
86
|
-
trackHistory: true,
|
|
87
|
-
searchable: true,
|
|
88
|
-
apiEnabled: true,
|
|
89
|
-
apiMethods: ['get', 'list', 'create', 'update', 'delete'],
|
|
90
|
-
trash: true,
|
|
91
|
-
mru: true,
|
|
92
|
-
},
|
|
93
|
-
});
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { PermissionSet, ObjectPermission, FieldPermission } from '@objectstack/spec/security';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Operation type mapping to permission checks
|
|
7
|
-
*/
|
|
8
|
-
const OPERATION_TO_PERMISSION: Record<string, keyof ObjectPermission> = {
|
|
9
|
-
find: 'allowRead',
|
|
10
|
-
findOne: 'allowRead',
|
|
11
|
-
count: 'allowRead',
|
|
12
|
-
aggregate: 'allowRead',
|
|
13
|
-
insert: 'allowCreate',
|
|
14
|
-
update: 'allowEdit',
|
|
15
|
-
delete: 'allowDelete',
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* PermissionEvaluator
|
|
20
|
-
*
|
|
21
|
-
* Runtime evaluator for PermissionSet definitions.
|
|
22
|
-
* Resolves aggregated permissions from roles to concrete allow/deny decisions.
|
|
23
|
-
*/
|
|
24
|
-
export class PermissionEvaluator {
|
|
25
|
-
/**
|
|
26
|
-
* Check if an operation is allowed on an object for the given permission sets.
|
|
27
|
-
* Uses "most permissive" merging: if ANY permission set allows, it's allowed.
|
|
28
|
-
*/
|
|
29
|
-
checkObjectPermission(
|
|
30
|
-
operation: string,
|
|
31
|
-
objectName: string,
|
|
32
|
-
permissionSets: PermissionSet[]
|
|
33
|
-
): boolean {
|
|
34
|
-
const permKey = OPERATION_TO_PERMISSION[operation];
|
|
35
|
-
if (!permKey) return true; // Unknown operations are allowed by default
|
|
36
|
-
|
|
37
|
-
for (const ps of permissionSets) {
|
|
38
|
-
const objPerm = ps.objects?.[objectName];
|
|
39
|
-
if (objPerm) {
|
|
40
|
-
// Check if modifyAllRecords is set (super-user bypass for write ops)
|
|
41
|
-
if (['allowEdit', 'allowDelete'].includes(permKey) && objPerm.modifyAllRecords) {
|
|
42
|
-
return true;
|
|
43
|
-
}
|
|
44
|
-
// Check if viewAllRecords is set (super-user bypass for read ops)
|
|
45
|
-
if (permKey === 'allowRead' && (objPerm.viewAllRecords || objPerm.modifyAllRecords)) {
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
// Check the specific permission
|
|
49
|
-
if (objPerm[permKey]) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Get the merged field permissions for an object.
|
|
60
|
-
* Returns a map of field names to their effective permissions.
|
|
61
|
-
* Uses "most permissive" merging.
|
|
62
|
-
*/
|
|
63
|
-
getFieldPermissions(
|
|
64
|
-
objectName: string,
|
|
65
|
-
permissionSets: PermissionSet[]
|
|
66
|
-
): Record<string, FieldPermission> {
|
|
67
|
-
const result: Record<string, FieldPermission> = {};
|
|
68
|
-
|
|
69
|
-
for (const ps of permissionSets) {
|
|
70
|
-
if (!ps.fields) continue;
|
|
71
|
-
|
|
72
|
-
for (const [key, perm] of Object.entries(ps.fields)) {
|
|
73
|
-
// Field keys are in format: "object_name.field_name"
|
|
74
|
-
if (!key.startsWith(`${objectName}.`)) continue;
|
|
75
|
-
const fieldName = key.substring(objectName.length + 1);
|
|
76
|
-
|
|
77
|
-
if (!result[fieldName]) {
|
|
78
|
-
result[fieldName] = { readable: false, editable: false };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Most permissive merge
|
|
82
|
-
if (perm.readable) result[fieldName].readable = true;
|
|
83
|
-
if (perm.editable) result[fieldName].editable = true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Resolve permission sets for a list of role names from metadata.
|
|
92
|
-
*/
|
|
93
|
-
resolvePermissionSets(
|
|
94
|
-
roles: string[],
|
|
95
|
-
metadataService: any
|
|
96
|
-
): PermissionSet[] {
|
|
97
|
-
const result: PermissionSet[] = [];
|
|
98
|
-
|
|
99
|
-
// Get all permission sets from metadata
|
|
100
|
-
const allPermSets = metadataService.list?.('permissions') || [];
|
|
101
|
-
|
|
102
|
-
for (const ps of allPermSets) {
|
|
103
|
-
// A permission set is relevant if it's a profile assigned to any of the user's roles,
|
|
104
|
-
// or if the role name matches the permission set name
|
|
105
|
-
if (roles.includes(ps.name)) {
|
|
106
|
-
result.push(ps);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return result;
|
|
111
|
-
}
|
|
112
|
-
}
|