endpoint-permissions-kit 0.1.0 → 0.2.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 +27 -20
- package/bin/pkit.mjs +2 -2
- package/dist/cjs/index.cjs +716 -473
- package/dist/cjs/index.cjs.map +16 -14
- package/dist/esm/cli/child.js +131 -376
- package/dist/esm/cli/child.js.map +10 -11
- package/dist/esm/cli/generate.js +78 -420
- package/dist/esm/cli/generate.js.map +5 -9
- package/dist/esm/index.js +716 -473
- package/dist/esm/index.js.map +16 -14
- package/dist/types/cli/generate.d.ts +6 -4
- package/dist/types/cli/protocol.d.ts +10 -7
- package/dist/types/constants.d.ts +9 -6
- package/dist/types/context.d.ts +5 -6
- package/dist/types/definitions.d.ts +15 -0
- package/dist/types/errors.d.ts +12 -11
- package/dist/types/identifiers.d.ts +21 -0
- package/dist/types/index.d.ts +16 -17
- package/dist/types/permissions.d.ts +10 -5
- package/dist/types/properties.d.ts +17 -0
- package/dist/types/registry.d.ts +4 -1
- package/dist/types/resolve.d.ts +50 -6
- package/dist/types/seal.d.ts +8 -1
- package/dist/types/state.d.ts +12 -9
- package/dist/types/types.d.ts +25 -34
- package/dist/types/validate.d.ts +10 -3
- package/package.json +1 -1
- package/dist/types/validators.d.ts +0 -53
package/dist/esm/cli/child.js
CHANGED
|
@@ -2,407 +2,162 @@
|
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
|
|
5
|
-
// src/constants.ts
|
|
6
|
-
var METHODS = ["find", "update", "create", "remove"];
|
|
7
|
-
var GENERAL_ROLE = "general";
|
|
8
|
-
var GLOBAL_HOOK_OWNER = "*";
|
|
9
|
-
var ALL_FIELDS = "*";
|
|
10
|
-
var MODULE_SEPARATOR = ".";
|
|
11
|
-
var PERMISSION_ID_SEPARATOR = "::";
|
|
12
|
-
|
|
13
|
-
// src/state.ts
|
|
14
|
-
function getOrCreateState() {
|
|
15
|
-
const stateKey = Symbol.for("endpoint-permissions-kit");
|
|
16
|
-
const stateHost = globalThis;
|
|
17
|
-
if (stateHost[stateKey] === undefined) {
|
|
18
|
-
stateHost[stateKey] = { roles: new Set([GENERAL_ROLE]), modules: new Map, snapshot: null };
|
|
19
|
-
}
|
|
20
|
-
return stateHost[stateKey];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
5
|
// src/errors.ts
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
function resolveAccess(nameEntry, permissionId, role, method, assigned) {
|
|
36
|
-
if (assigned.has(permissionId))
|
|
37
|
-
return resolveDirectAccess(nameEntry, role, method);
|
|
38
|
-
return resolveGrantedAccess(nameEntry, method, assigned);
|
|
39
|
-
}
|
|
40
|
-
function resolveDirectAccess(nameEntry, role, method) {
|
|
41
|
-
const definition = nameEntry.actions.get(role)?.[method];
|
|
42
|
-
if (!definition?.enabled)
|
|
43
|
-
return DISABLED_ACCESS;
|
|
44
|
-
return { status: "granted", properties: definition.properties, authorization: DIRECT_AUTHORIZATION };
|
|
45
|
-
}
|
|
46
|
-
function resolveGrantedAccess(nameEntry, method, assigned) {
|
|
47
|
-
const grantedBy = [];
|
|
48
|
-
const properties = new Set;
|
|
49
|
-
let reachable = false;
|
|
50
|
-
for (const [enablingId, grant] of nameEntry.grants) {
|
|
51
|
-
if (!assigned.has(enablingId))
|
|
52
|
-
continue;
|
|
53
|
-
reachable = true;
|
|
54
|
-
const definition = grant.actions[method];
|
|
55
|
-
if (!definition)
|
|
56
|
-
continue;
|
|
57
|
-
grantedBy.push(enablingId);
|
|
58
|
-
for (const field of definition.properties)
|
|
59
|
-
properties.add(field);
|
|
6
|
+
var errors = {
|
|
7
|
+
create(code, message) {
|
|
8
|
+
return Object.assign(new Error(message), { name: "PkitError", code });
|
|
9
|
+
},
|
|
10
|
+
describe(value) {
|
|
11
|
+
try {
|
|
12
|
+
return String(value);
|
|
13
|
+
} catch {
|
|
14
|
+
return typeof value;
|
|
15
|
+
}
|
|
60
16
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const authorization = Object.freeze({ direct: false, grantedBy: Object.freeze(grantedBy.sort()) });
|
|
64
|
-
return { status: "granted", properties: Object.freeze([...properties]), authorization };
|
|
65
|
-
}
|
|
17
|
+
};
|
|
18
|
+
var errors_default = errors;
|
|
66
19
|
|
|
67
|
-
// src/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
20
|
+
// src/constants.ts
|
|
21
|
+
var constants = {
|
|
22
|
+
METHODS: Object.freeze(["find", "update", "create", "remove"]),
|
|
23
|
+
GENERAL_ROLE: "general",
|
|
24
|
+
GLOBAL_HOOK_OWNER: "*",
|
|
25
|
+
ALL_FIELDS: "*",
|
|
26
|
+
MODULE_SEPARATOR: ".",
|
|
27
|
+
PERMISSION_ID_SEPARATOR: "::"
|
|
28
|
+
};
|
|
29
|
+
var constants_default = constants;
|
|
30
|
+
|
|
31
|
+
// src/identifiers.ts
|
|
32
|
+
var identifiers = {
|
|
33
|
+
build(role, action, name) {
|
|
34
|
+
return `${role}${constants_default.PERMISSION_ID_SEPARATOR}${action}${constants_default.PERMISSION_ID_SEPARATOR}${name}`;
|
|
35
|
+
},
|
|
36
|
+
parse(value, code, subject) {
|
|
37
|
+
if (typeof value !== "string")
|
|
38
|
+
throw invalidIdentifier(value, code, subject);
|
|
39
|
+
const parts = value.split(constants_default.PERMISSION_ID_SEPARATOR);
|
|
40
|
+
const [role, action, name] = parts;
|
|
41
|
+
if (parts.length !== 3 || role === undefined || action === undefined || name === undefined) {
|
|
42
|
+
throw invalidIdentifier(value, code, subject);
|
|
79
43
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
function validateRoleCatalog(roles, state) {
|
|
105
|
-
validateOpenRegistry(state);
|
|
106
|
-
if (state.modules.size) {
|
|
107
|
-
throw pkitError("INVALID_DEFINITION", "roles must be declared before registering permissions: import pkit.config.js first");
|
|
108
|
-
}
|
|
109
|
-
validateStrings(roles, {
|
|
110
|
-
code: "INVALID_DEFINITION",
|
|
111
|
-
message: "roles must be an array of non-empty strings",
|
|
112
|
-
minimumLength: 1
|
|
113
|
-
});
|
|
114
|
-
if (roles.length === 0)
|
|
115
|
-
throw pkitError("INVALID_DEFINITION", "roles must declare at least one role");
|
|
116
|
-
for (const role of roles)
|
|
117
|
-
validateRoleSegment(role);
|
|
118
|
-
}
|
|
44
|
+
const isValidRole = isCleanSegment(role) && role !== constants_default.GLOBAL_HOOK_OWNER;
|
|
45
|
+
const isValidName = isCleanSegment(name) && name !== constants_default.GLOBAL_HOOK_OWNER;
|
|
46
|
+
const isValidAction = action.split(constants_default.MODULE_SEPARATOR).every(isCleanSegment);
|
|
47
|
+
if (!isValidRole || !isValidName || !isValidAction)
|
|
48
|
+
throw invalidIdentifier(value, code, subject);
|
|
49
|
+
return { role, action, name };
|
|
50
|
+
},
|
|
51
|
+
checkRole(role) {
|
|
52
|
+
if (typeof role === "string" && isCleanSegment(role) && role !== constants_default.GLOBAL_HOOK_OWNER)
|
|
53
|
+
return;
|
|
54
|
+
throw errors_default.create("INVALID_DEFINITION", `invalid role: "${errors_default.describe(role)}" (non-empty string, no ":" or surrounding whitespace; "${constants_default.GLOBAL_HOOK_OWNER}" is reserved for global hooks)`);
|
|
55
|
+
},
|
|
56
|
+
checkModuleSegment(segment) {
|
|
57
|
+
if (typeof segment === "string" && isCleanSegment(segment) && !segment.includes(constants_default.MODULE_SEPARATOR))
|
|
58
|
+
return;
|
|
59
|
+
throw errors_default.create("INVALID_DEFINITION", `invalid module name: "${errors_default.describe(segment)}" (non-empty string, no dots, no ":" or surrounding whitespace)`);
|
|
60
|
+
},
|
|
61
|
+
checkName(name) {
|
|
62
|
+
if (typeof name === "string" && isCleanSegment(name) && name !== constants_default.GLOBAL_HOOK_OWNER)
|
|
63
|
+
return;
|
|
64
|
+
throw errors_default.create("INVALID_DEFINITION", `invalid permission name: "${errors_default.describe(name)}" (non-empty string, no ":" or surrounding whitespace; "*" is reserved)`);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
119
67
|
function isCleanSegment(value) {
|
|
120
68
|
return value.length > 0 && value.trim() === value && !value.includes(":");
|
|
121
69
|
}
|
|
122
|
-
function
|
|
123
|
-
|
|
124
|
-
return;
|
|
125
|
-
throw pkitError("INVALID_DEFINITION", `invalid role: "${String(role)}" (non-empty string, no ":" or surrounding whitespace; "${GLOBAL_HOOK_OWNER}" is reserved for global hooks)`);
|
|
126
|
-
}
|
|
127
|
-
function validateModuleName(moduleName) {
|
|
128
|
-
if (typeof moduleName === "string" && isCleanSegment(moduleName) && !moduleName.includes(MODULE_SEPARATOR))
|
|
129
|
-
return;
|
|
130
|
-
throw pkitError("INVALID_DEFINITION", `invalid module name: "${String(moduleName)}" (non-empty string, no dots, no ":" or surrounding whitespace)`);
|
|
131
|
-
}
|
|
132
|
-
function validatePermissionName(name) {
|
|
133
|
-
if (typeof name === "string" && isCleanSegment(name) && name !== GLOBAL_HOOK_OWNER)
|
|
134
|
-
return;
|
|
135
|
-
throw pkitError("INVALID_DEFINITION", `invalid permission name: "${String(name)}" (non-empty string, no ":" or surrounding whitespace; "*" is reserved)`);
|
|
136
|
-
}
|
|
137
|
-
function parsePermissionId(value, failure) {
|
|
138
|
-
if (typeof value !== "string")
|
|
139
|
-
throw pkitError(failure.code, failure.message);
|
|
140
|
-
const parts = value.split(PERMISSION_ID_SEPARATOR);
|
|
141
|
-
const [role, action, name] = parts;
|
|
142
|
-
if (parts.length !== 3 || role === undefined || action === undefined || name === undefined) {
|
|
143
|
-
throw pkitError(failure.code, failure.message);
|
|
144
|
-
}
|
|
145
|
-
const isValidRole = isCleanSegment(role) && role !== GLOBAL_HOOK_OWNER;
|
|
146
|
-
const isValidName = isCleanSegment(name) && name !== GLOBAL_HOOK_OWNER;
|
|
147
|
-
const isValidAction = action.split(MODULE_SEPARATOR).every(isCleanSegment);
|
|
148
|
-
if (!isValidRole || !isValidName || !isValidAction)
|
|
149
|
-
throw pkitError(failure.code, failure.message);
|
|
150
|
-
return { role, action, name };
|
|
151
|
-
}
|
|
152
|
-
function validateMethod(method, failure) {
|
|
153
|
-
if (METHODS.includes(method))
|
|
154
|
-
return;
|
|
155
|
-
throw pkitError(failure.code, `${failure.message}: method "${String(method)}" does not exist. Methods: ${METHODS.join(", ")}`);
|
|
156
|
-
}
|
|
157
|
-
function readActionDefinition(definition, permissionPath) {
|
|
158
|
-
validateObject(definition, { code: "INVALID_DEFINITION", message: `${permissionPath}: must be an object` });
|
|
159
|
-
if (typeof definition.enabled !== "boolean") {
|
|
160
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}: enabled must be a boolean`);
|
|
161
|
-
}
|
|
162
|
-
const { properties } = definition;
|
|
163
|
-
if (properties === ALL_FIELDS)
|
|
164
|
-
return Object.freeze({ enabled: definition.enabled, properties });
|
|
165
|
-
validateStrings(properties, {
|
|
166
|
-
code: "INVALID_DEFINITION",
|
|
167
|
-
message: `${permissionPath}: properties must be string[] or '*'`,
|
|
168
|
-
minimumLength: 1
|
|
169
|
-
});
|
|
170
|
-
if (properties.includes(ALL_FIELDS)) {
|
|
171
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}: '${ALL_FIELDS}' is only allowed as the whole properties value`);
|
|
172
|
-
}
|
|
173
|
-
if (new Set(properties).size !== properties.length) {
|
|
174
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}: properties contains duplicate fields`);
|
|
175
|
-
}
|
|
176
|
-
return Object.freeze({ enabled: definition.enabled, properties: Object.freeze([...properties]) });
|
|
177
|
-
}
|
|
178
|
-
function readActionDefinitions(actions, permissionPath) {
|
|
179
|
-
validateObject(actions, { code: "INVALID_DEFINITION", message: `${permissionPath}: registerActions expects an object` });
|
|
180
|
-
const registeredActions = Object.create(null);
|
|
181
|
-
for (const [method, definition] of Object.entries(actions)) {
|
|
182
|
-
validateMethod(method, { code: "INVALID_DEFINITION", message: permissionPath });
|
|
183
|
-
registeredActions[method] = readActionDefinition(definition, `${permissionPath}.${method}`);
|
|
184
|
-
}
|
|
185
|
-
return Object.freeze(registeredActions);
|
|
70
|
+
function invalidIdentifier(value, code, subject) {
|
|
71
|
+
return errors_default.create(code, `${subject}: "${errors_default.describe(value)}" (format role::module::name)`);
|
|
186
72
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
function validateGrantActions(actions, registration, state) {
|
|
197
|
-
const { action, name, permissionId } = registration;
|
|
198
|
-
validateOpenRegistry(state);
|
|
199
|
-
const source = parsePermissionId(permissionId, {
|
|
200
|
-
code: "INVALID_DEFINITION",
|
|
201
|
-
message: `"${action}::${name}": invalid grantTo identifier: "${String(permissionId)}" (format role::module::name)`
|
|
202
|
-
});
|
|
203
|
-
validateRole(source.role, state.roles, "ROLE_NOT_DECLARED");
|
|
204
|
-
if (source.action === action && source.name === name) {
|
|
205
|
-
throw pkitError("INVALID_DEFINITION", `"${permissionId}" cannot grant to itself`);
|
|
206
|
-
}
|
|
207
|
-
if (state.modules.get(action)?.names.get(name)?.grants.has(permissionId)) {
|
|
208
|
-
throw pkitError("DUPLICATE_REGISTRATION", `"${action}::${name}" already has a grant for "${permissionId}"`);
|
|
209
|
-
}
|
|
210
|
-
const permissionPath = `${action}::${name} [grantTo ${permissionId}]`;
|
|
211
|
-
const registeredActions = readActionDefinitions(actions, permissionPath);
|
|
212
|
-
for (const [method, definition] of Object.entries(registeredActions)) {
|
|
213
|
-
if (definition.enabled !== true) {
|
|
214
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}.${method}: a grant does not allow enabled: false`);
|
|
215
|
-
}
|
|
216
|
-
if (definition.properties === ALL_FIELDS) {
|
|
217
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}.${method}: a grant requires an explicit properties list`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return { source, actions: registeredActions };
|
|
221
|
-
}
|
|
222
|
-
function validateHook(hook, registration, state) {
|
|
223
|
-
const { action, name, role, method } = registration;
|
|
224
|
-
validateOpenRegistry(state);
|
|
225
|
-
if (role !== undefined && role !== GLOBAL_HOOK_OWNER)
|
|
226
|
-
validateRole(role, state.roles, "ROLE_NOT_DECLARED");
|
|
227
|
-
const permissionPath = name === undefined ? action : `${action}::${name}`;
|
|
228
|
-
validateMethod(method, { code: "INVALID_DEFINITION", message: permissionPath });
|
|
229
|
-
if (typeof hook !== "function")
|
|
230
|
-
throw pkitError("INVALID_DEFINITION", `${permissionPath}: hook("${method}") expects a function`);
|
|
231
|
-
}
|
|
232
|
-
function declaresMethod(nameEntry, method) {
|
|
233
|
-
for (const actions of nameEntry.actions.values()) {
|
|
234
|
-
if (Object.hasOwn(actions, method))
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
function hasAccessPath(nameEntry, role, method) {
|
|
240
|
-
if (nameEntry.actions.get(role)?.[method])
|
|
241
|
-
return true;
|
|
242
|
-
for (const grant of nameEntry.grants.values()) {
|
|
243
|
-
if (grant.source.role === role && grant.actions[method])
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
function validateGrantReferences(permissionPath, nameEntry, modules) {
|
|
249
|
-
for (const [permissionId, grant] of nameEntry.grants) {
|
|
250
|
-
const sourceEntry = modules.get(grant.source.action)?.names.get(grant.source.name);
|
|
251
|
-
if (!sourceEntry?.actions.has(grant.source.role)) {
|
|
252
|
-
throw pkitError("INVALID_DEFINITION", `"${permissionPath}": grantTo "${permissionId}" references a permission with no registered actions`);
|
|
253
|
-
}
|
|
254
|
-
for (const method of Object.keys(grant.actions)) {
|
|
255
|
-
if (declaresMethod(nameEntry, method))
|
|
256
|
-
continue;
|
|
257
|
-
throw pkitError("INVALID_DEFINITION", `"${permissionPath}": grantTo "${permissionId}" grants "${method}", which no role declares on that permission`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
function validateRoleHooks(permissionPath, nameEntry) {
|
|
262
|
-
for (const [hookRole, methodHooks] of nameEntry.hooks) {
|
|
263
|
-
if (hookRole === GLOBAL_HOOK_OWNER)
|
|
264
|
-
continue;
|
|
265
|
-
for (const method of methodHooks.keys()) {
|
|
266
|
-
if (hasAccessPath(nameEntry, hookRole, method))
|
|
267
|
-
continue;
|
|
268
|
-
throw pkitError("INVALID_DEFINITION", `"${permissionPath}": hook for "${hookRole}" on "${method}" has no registered actions or grant for that role`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
function validateSealedRegistry(modules) {
|
|
273
|
-
for (const [action, registeredModule] of modules) {
|
|
274
|
-
if (registeredModule.names.size === 0) {
|
|
275
|
-
throw pkitError("INVALID_DEFINITION", `"${action}" has hooks but no name with registered actions`);
|
|
73
|
+
var identifiers_default = identifiers;
|
|
74
|
+
|
|
75
|
+
// src/state.ts
|
|
76
|
+
var STATE_KEY = Symbol.for("endpoint-permissions-kit");
|
|
77
|
+
var state = {
|
|
78
|
+
getOrCreate() {
|
|
79
|
+
const stateHost = globalThis;
|
|
80
|
+
if (stateHost[STATE_KEY] === undefined) {
|
|
81
|
+
stateHost[STATE_KEY] = { roles: new Set([constants_default.GENERAL_ROLE]), cropper: false, modules: new Map, snapshot: null };
|
|
276
82
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
83
|
+
return stateHost[STATE_KEY];
|
|
84
|
+
},
|
|
85
|
+
requireOpen(currentState) {
|
|
86
|
+
if (currentState.snapshot)
|
|
87
|
+
throw errors_default.create("SEALED", "pkit.seal() was already called: no more registrations allowed");
|
|
88
|
+
},
|
|
89
|
+
requireSnapshot(currentState) {
|
|
90
|
+
if (currentState.snapshot === null) {
|
|
91
|
+
throw errors_default.create("NOT_SEALED", "pkit.seal() has not been called: call it after importing every permission file");
|
|
284
92
|
}
|
|
93
|
+
return currentState.snapshot;
|
|
285
94
|
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
95
|
+
};
|
|
96
|
+
var state_default = state;
|
|
97
|
+
|
|
98
|
+
// src/context.ts
|
|
99
|
+
var context = Object.freeze({
|
|
100
|
+
set(key, value) {
|
|
101
|
+
const contextKey = checkKey(key);
|
|
102
|
+
const currentState = state_default.getOrCreate();
|
|
103
|
+
state_default.requireOpen(currentState);
|
|
104
|
+
if (contextKey === "cropper") {
|
|
105
|
+
if (typeof value !== "boolean")
|
|
106
|
+
throw errors_default.create("INVALID_DEFINITION", "cropper must be a boolean");
|
|
107
|
+
currentState.cropper = value;
|
|
108
|
+
return;
|
|
300
109
|
}
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (properties.includes(field))
|
|
318
|
-
selectedFields.push(field);
|
|
110
|
+
currentState.roles = new Set(readRoleCatalog(value, currentState.modules.size));
|
|
111
|
+
},
|
|
112
|
+
get(key) {
|
|
113
|
+
const contextKey = checkKey(key);
|
|
114
|
+
const currentState = state_default.getOrCreate();
|
|
115
|
+
return contextKey === "cropper" ? currentState.cropper : [...currentState.roles];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
function checkKey(key) {
|
|
119
|
+
if (key === "roles" || key === "cropper")
|
|
120
|
+
return key;
|
|
121
|
+
throw errors_default.create("INVALID_DEFINITION", `unknown context key: "${errors_default.describe(key)}"`);
|
|
122
|
+
}
|
|
123
|
+
function readRoleCatalog(value, registeredModuleCount) {
|
|
124
|
+
if (registeredModuleCount > 0) {
|
|
125
|
+
throw errors_default.create("INVALID_DEFINITION", "roles must be declared before registering permissions: import pkit.config.js first");
|
|
319
126
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (!properties.includes(field))
|
|
326
|
-
forbiddenFields.push(field);
|
|
327
|
-
}
|
|
328
|
-
if (forbiddenFields.length === 0)
|
|
329
|
-
return;
|
|
330
|
-
throw Object.assign(pkitError("PROPERTIES_NOT_ALLOWED", `fields not allowed in "${permissionPath}": ${forbiddenFields.join(", ")}`), { fields: forbiddenFields });
|
|
331
|
-
}
|
|
332
|
-
function validateRequest(input, state) {
|
|
333
|
-
validateSnapshot(state.snapshot);
|
|
334
|
-
const { action, name, method } = input;
|
|
335
|
-
validateMethod(method, { code: "INVALID_INPUT", message: "method is required" });
|
|
336
|
-
if (method !== "find" && Object.hasOwn(input, "select"))
|
|
337
|
-
throw pkitError("INVALID_INPUT", "select only applies to find");
|
|
338
|
-
if (typeof action !== "string" || typeof name !== "string")
|
|
339
|
-
throw pkitError("INVALID_INPUT", "action and name are required");
|
|
340
|
-
const { role, assigned } = validateIdentity(input, state);
|
|
341
|
-
const registeredModule = state.modules.get(action);
|
|
342
|
-
if (!registeredModule)
|
|
343
|
-
throw pkitError("UNKNOWN_ACTION", `module "${action}" is not registered`);
|
|
344
|
-
const nameEntry = registeredModule.names.get(name);
|
|
345
|
-
if (!nameEntry)
|
|
346
|
-
throw pkitError("UNKNOWN_PERMISSION", `permission "${action}::${name}" is not registered`);
|
|
347
|
-
const permissionId = permissionIdOf(role, action, name);
|
|
348
|
-
const access = resolveAccess(nameEntry, permissionId, role, method, assigned);
|
|
349
|
-
if (access.status === "unassigned") {
|
|
350
|
-
throw pkitError("PERMISSION_NOT_ASSIGNED", `"${permissionId}" is not assigned or granted by the user permissions`);
|
|
351
|
-
}
|
|
352
|
-
if (access.status === "disabled")
|
|
353
|
-
throw pkitError("METHOD_DISABLED", `"${permissionId}.${method}" is not enabled`);
|
|
354
|
-
const { data, context } = input;
|
|
355
|
-
if (context !== undefined)
|
|
356
|
-
validateObject(context, { code: "INVALID_INPUT", message: "context must be an object" });
|
|
357
|
-
const { properties, authorization } = access;
|
|
358
|
-
const permission = Object.freeze({ role, action, name, permissionId, method, enabled: true, properties, authorization });
|
|
359
|
-
const request = { registeredModule, nameEntry, permission, data, context };
|
|
360
|
-
if (method === "find") {
|
|
361
|
-
if (data !== undefined)
|
|
362
|
-
validateObject(data, { code: "INVALID_INPUT", message: "data must be an object" });
|
|
363
|
-
const { select } = input;
|
|
364
|
-
if (select === undefined)
|
|
365
|
-
return { ...request, result: properties };
|
|
366
|
-
if (properties === ALL_FIELDS) {
|
|
367
|
-
validateStrings(select, { code: "INVALID_INPUT", message: "select must be an array of strings", minimumLength: 0 });
|
|
368
|
-
return { ...request, result: select };
|
|
127
|
+
if (!Array.isArray(value))
|
|
128
|
+
throw errors_default.create("INVALID_DEFINITION", "roles must be an array of non-empty strings");
|
|
129
|
+
for (const role of value) {
|
|
130
|
+
if (typeof role !== "string" || role.length === 0) {
|
|
131
|
+
throw errors_default.create("INVALID_DEFINITION", "roles must be an array of non-empty strings");
|
|
369
132
|
}
|
|
370
|
-
return { ...request, result: trimSelection(select, properties) };
|
|
371
133
|
}
|
|
372
|
-
|
|
373
|
-
if (
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// src/context.ts
|
|
379
|
-
function setRoles(key, roles) {
|
|
380
|
-
validateContextKey(key);
|
|
381
|
-
const state = getOrCreateState();
|
|
382
|
-
validateRoleCatalog(roles, state);
|
|
383
|
-
state.roles = new Set(roles);
|
|
384
|
-
}
|
|
385
|
-
function getRoles(key) {
|
|
386
|
-
validateContextKey(key);
|
|
387
|
-
return [...getOrCreateState().roles];
|
|
134
|
+
const catalog = value;
|
|
135
|
+
if (catalog.length === 0)
|
|
136
|
+
throw errors_default.create("INVALID_DEFINITION", "roles must declare at least one role");
|
|
137
|
+
for (const role of catalog)
|
|
138
|
+
identifiers_default.checkRole(role);
|
|
139
|
+
return catalog;
|
|
388
140
|
}
|
|
389
|
-
var
|
|
141
|
+
var context_default = context;
|
|
390
142
|
|
|
391
143
|
// src/cli/protocol.ts
|
|
392
|
-
var
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
144
|
+
var protocol = {
|
|
145
|
+
EXIT_CODE: { success: 0, failure: 1, usage: 2 },
|
|
146
|
+
CATALOG_MARKER: "__PKIT_CATALOG__",
|
|
147
|
+
CHILD_TIMEOUT_MS: 30000,
|
|
148
|
+
CHILD_MAX_BUFFER_BYTES: 1048576
|
|
149
|
+
};
|
|
150
|
+
var protocol_default = protocol;
|
|
396
151
|
|
|
397
152
|
// src/cli/child.ts
|
|
398
153
|
var configPath = process.argv[2];
|
|
399
154
|
if (configPath === undefined) {
|
|
400
155
|
console.error("usage: child <pkit.config.js>");
|
|
401
|
-
process.exit(EXIT_CODE.usage);
|
|
156
|
+
process.exit(protocol_default.EXIT_CODE.usage);
|
|
402
157
|
}
|
|
403
158
|
await import(pathToFileURL(resolve(configPath)).href);
|
|
404
159
|
process.stdout.write(`
|
|
405
|
-
${CATALOG_MARKER}${JSON.stringify({ roles:
|
|
160
|
+
${protocol_default.CATALOG_MARKER}${JSON.stringify({ roles: context_default.get("roles") })}
|
|
406
161
|
`);
|
|
407
162
|
|
|
408
|
-
//# debugId=
|
|
163
|
+
//# debugId=E06FAADB234DDEA064756E2164756E21
|