@endora-commerce/mod-admin-roles 0.100.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/LICENSE +21 -0
- package/README.md +52 -0
- package/dist/admin/index.d.ts +47 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +27 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +20 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +13 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +64 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +74 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +20 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +29 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/entities/admin-role.entity.d.ts +20 -0
- package/dist/backend/entities/admin-role.entity.d.ts.map +1 -0
- package/dist/backend/entities/admin-role.entity.js +65 -0
- package/dist/backend/entities/admin-role.entity.js.map +1 -0
- package/dist/backend/index.d.ts +96 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +114 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/permission-inventory.d.ts +117 -0
- package/dist/backend/permission-inventory.d.ts.map +1 -0
- package/dist/backend/permission-inventory.js +585 -0
- package/dist/backend/permission-inventory.js.map +1 -0
- package/dist/backend/services/admin-role-ports.d.ts +39 -0
- package/dist/backend/services/admin-role-ports.d.ts.map +1 -0
- package/dist/backend/services/admin-role-ports.js +72 -0
- package/dist/backend/services/admin-role-ports.js.map +1 -0
- package/dist/backend/services/admin-role-service.d.ts +77 -0
- package/dist/backend/services/admin-role-service.d.ts.map +1 -0
- package/dist/backend/services/admin-role-service.js +219 -0
- package/dist/backend/services/admin-role-service.js.map +1 -0
- package/dist/backend/services/permission-catalogue.service.d.ts +130 -0
- package/dist/backend/services/permission-catalogue.service.d.ts.map +1 -0
- package/dist/backend/services/permission-catalogue.service.js +175 -0
- package/dist/backend/services/permission-catalogue.service.js.map +1 -0
- package/dist/backend/services/permission-service.d.ts +24 -0
- package/dist/backend/services/permission-service.d.ts.map +1 -0
- package/dist/backend/services/permission-service.js +40 -0
- package/dist/backend/services/permission-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +192 -0
- package/dist/manifest.js.map +1 -0
- package/docs/admin_roles.md +38 -0
- package/i18n/en.json +10 -0
- package/i18n/pl.json +10 -0
- package/package.json +73 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
4
|
+
/**
|
|
5
|
+
* Static inventory of the permission codes the backend actually enforces.
|
|
6
|
+
*
|
|
7
|
+
* It exists so `/admin-roles` can never advertise less than the server checks:
|
|
8
|
+
* a code enforced at a gate but absent from the assignable catalogue is a screen
|
|
9
|
+
* only a `'*'` role can open, and nothing else in the tree notices.
|
|
10
|
+
*
|
|
11
|
+
* The scanner therefore has to read the gate call in **every shape the tree
|
|
12
|
+
* writes it**, not the one shape it was first written against. It previously
|
|
13
|
+
* required an optional-call dot before the argument list, so a plain
|
|
14
|
+
* `requireAdmin('code')` — nearly the whole tree — was invisible to it, and it
|
|
15
|
+
* read clean while modules gated screens on codes no role could hold. The shapes are
|
|
16
|
+
* enumerated on `collectFromSource` and each is covered by a fixture in
|
|
17
|
+
* `test/unit/admin_roles/permission-inventory-scanner.test.ts`; an argument the
|
|
18
|
+
* scanner cannot resolve is reported as `unresolved` rather than dropped,
|
|
19
|
+
* because a silently dropped gate is exactly the failure this file guards.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* `packages/contracts/src` — permission maps shared across modules live here.
|
|
23
|
+
*
|
|
24
|
+
* Resolved through the package's own `package.json`, never by climbing out of
|
|
25
|
+
* this file's directory (D-100). The climb worked while this module lived at
|
|
26
|
+
* `backend/src/modules/admin_roles/` and answered a *different* directory the
|
|
27
|
+
* day it became a package — silently, because every path it produced still
|
|
28
|
+
* existed. `createRequire` asks the resolver the question the manifest already
|
|
29
|
+
* answers, and this module declares `@endora-commerce/contracts` as a peer.
|
|
30
|
+
*/
|
|
31
|
+
const CONTRACTS_SRC = (() => {
|
|
32
|
+
// Its entry point, then up to the directory that owns the manifest. Asking
|
|
33
|
+
// for `./package.json` directly is not available: that package's `exports`
|
|
34
|
+
// map answers a wildcard and resolves the request to `dist/package.json.js`.
|
|
35
|
+
let dir = dirname(createRequire(import.meta.url).resolve('@endora-commerce/contracts'));
|
|
36
|
+
for (;;) {
|
|
37
|
+
if (existsSync(join(dir, 'package.json')))
|
|
38
|
+
return join(dir, 'src');
|
|
39
|
+
const parent = dirname(dir);
|
|
40
|
+
if (parent === dir)
|
|
41
|
+
return '';
|
|
42
|
+
dir = parent;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
/**
|
|
46
|
+
* The roots a bare-core build enforces gates in, plus the active deployment's
|
|
47
|
+
* overlay modules (feature 057 — an overlay module is an ordinary lifecycle
|
|
48
|
+
* participant, so its gates count for that deployment and for no other).
|
|
49
|
+
*
|
|
50
|
+
* `src/apps` is skipped during the walk rather than excluded by name: a
|
|
51
|
+
* deployment that is not the selected one is not part of this build, and its
|
|
52
|
+
* permissions are not assignable here.
|
|
53
|
+
*
|
|
54
|
+
* **A module that is a workspace package is not here, and is the caller's to
|
|
55
|
+
* supply.** This scanner has no runtime caller — its two callers are
|
|
56
|
+
* `test/contract/admin_users/permission-inventory.test.ts` and the scanner's own
|
|
57
|
+
* unit test — and the module layout is derived by `scripts/lib/module-roots.ts`,
|
|
58
|
+
* which a module may not import (`check:module-boundary`, and it is right to
|
|
59
|
+
* refuse: this file belongs to `admin_roles`). So the root list is an input, and
|
|
60
|
+
* the caller that already resolves the layout passes it. Leaving a packaged
|
|
61
|
+
* module out silently reports its codes as *"grantable but enforced by no
|
|
62
|
+
* gate"* — a checkbox `/admin-roles` says grants nothing while the gate is
|
|
63
|
+
* there — which is what the contract test caught the day `blog` moved.
|
|
64
|
+
*
|
|
65
|
+
* **The overlay root is an input for the same reason** (feature 080, T051). It
|
|
66
|
+
* used to be computed here, from `overlay/overlay-roots.js` — a file
|
|
67
|
+
* `contracts/host-package.md` §1.4l classifies **A**, because `overlay/*` is
|
|
68
|
+
* how the platform *discovers* overlays and a module reading it as an installed
|
|
69
|
+
* package would be an artefact enumerating its own siblings (O3's cycle). The
|
|
70
|
+
* answer is the same one this paragraph already gives for a packaged module:
|
|
71
|
+
* the caller resolves the layout, and there is no runtime caller to inconvenience.
|
|
72
|
+
* `null` means a bare-core build, which is what a deployment-less environment
|
|
73
|
+
* has always produced.
|
|
74
|
+
*/
|
|
75
|
+
export function defaultScanRoots(applicationSourceRoot, overlayModulesRoot) {
|
|
76
|
+
return overlayModulesRoot === null
|
|
77
|
+
? [applicationSourceRoot]
|
|
78
|
+
: [applicationSourceRoot, overlayModulesRoot];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Distinct permission codes referenced by an enforcement site under the scan
|
|
82
|
+
* roots. Kept for callers that only need the set (SC-001).
|
|
83
|
+
*/
|
|
84
|
+
export function scanEnforcedPermissionCodes(roots) {
|
|
85
|
+
return scanEnforcedPermissionGates(roots).codes;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Every enforcement site under `roots`, classified.
|
|
89
|
+
*
|
|
90
|
+
* `roots` is required. It used to default to `defaultScanRoots()`, which
|
|
91
|
+
* resolved the active deployment for itself; that resolution is the caller's
|
|
92
|
+
* now (see {@link defaultScanRoots}), and a default of "bare core" would be a
|
|
93
|
+
* scan that silently covered less than it was asked for.
|
|
94
|
+
*
|
|
95
|
+
* Each site's `file` is reported relative to the **first bare-string root**,
|
|
96
|
+
* which is the application's own source root by construction of
|
|
97
|
+
* {@link defaultScanRoots} — a module package is passed as an object carrying
|
|
98
|
+
* its `moduleId`. It used to be relative to a `backend/src` this file computed
|
|
99
|
+
* by climbing out of its own directory, which answered a different directory
|
|
100
|
+
* the day `admin_roles` became a package (feature 080, T040b).
|
|
101
|
+
*/
|
|
102
|
+
export function scanEnforcedPermissionGates(roots) {
|
|
103
|
+
const sites = [];
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
const resolver = new ConstantResolver();
|
|
106
|
+
const base = roots.find((entry) => typeof entry === 'string') ?? '';
|
|
107
|
+
for (const entry of roots) {
|
|
108
|
+
const root = typeof entry === 'string' ? { dir: entry } : entry;
|
|
109
|
+
for (const file of walkSources(root.dir, seen)) {
|
|
110
|
+
collectFromFile(file, root, base, resolver, sites);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const codes = new Set();
|
|
114
|
+
for (const site of sites)
|
|
115
|
+
for (const code of site.codes)
|
|
116
|
+
codes.add(code);
|
|
117
|
+
return {
|
|
118
|
+
codes,
|
|
119
|
+
sites,
|
|
120
|
+
unresolved: sites.filter((s) => s.resolution === 'unresolved'),
|
|
121
|
+
runtimeValue: sites.filter((s) => s.resolution === 'runtime-value'),
|
|
122
|
+
authenticatedAdminOnly: sites.filter((s) => s.resolution === 'authenticated-admin'),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function* walkSources(dir, seen) {
|
|
126
|
+
if (!existsSync(dir))
|
|
127
|
+
return;
|
|
128
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
129
|
+
const path = join(dir, entry.name);
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
// `apps` is reached through `defaultScanRoots`, and only for the selected
|
|
132
|
+
// deployment; `node_modules` is never ours. `dist` is a module package's
|
|
133
|
+
// build output — the same gates a second time, compiled, which would
|
|
134
|
+
// double every site and attribute half of them to a path nobody edits.
|
|
135
|
+
if (entry.name === 'node_modules' || entry.name === 'apps' || entry.name === 'dist') {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
yield* walkSources(path, seen);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!entry.isFile() || !/\.(ts|js)$/.test(entry.name))
|
|
142
|
+
continue;
|
|
143
|
+
// **A test is not an enforcement site, and this exclusion had no subject
|
|
144
|
+
// until feature 106.** Every test used to live under `backend/test`, which
|
|
145
|
+
// no scan root reaches, so the walk could not meet one; 106 moved the
|
|
146
|
+
// co-located tests *into* `packages/modules/<id>/src/`, and this scanner's
|
|
147
|
+
// own unit test — whose fixtures are deliberately fake codes and
|
|
148
|
+
// deliberately unreadable gate arguments — moved with them. Its fixtures
|
|
149
|
+
// were then read as real gates: 18 codes reported *"enforced but not
|
|
150
|
+
// grantable"* and 2 as unresolvable, red on `master`. A gate written in a
|
|
151
|
+
// test guards nothing an operator can reach, so the population is source
|
|
152
|
+
// and never a test, whichever directory the test now sits in. The predicate
|
|
153
|
+
// is the suffix and not the substring: a module legitimately named for test
|
|
154
|
+
// infrastructure keeps its gates.
|
|
155
|
+
if (/\.(test|spec)\.(ts|js)$/.test(entry.name))
|
|
156
|
+
continue;
|
|
157
|
+
if (seen.has(path))
|
|
158
|
+
continue;
|
|
159
|
+
seen.add(path);
|
|
160
|
+
yield path;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/** The module directory owning `file`, for attribution in failure messages. */
|
|
164
|
+
function moduleIdFor(file, root) {
|
|
165
|
+
// A root that names its module **is** that module's directory, whatever it
|
|
166
|
+
// keeps inside it.
|
|
167
|
+
if (root.moduleId !== undefined)
|
|
168
|
+
return root.moduleId;
|
|
169
|
+
const rel = relative(root.dir, file);
|
|
170
|
+
const segments = rel.split(/[\\/]/);
|
|
171
|
+
if (segments[0] === 'modules')
|
|
172
|
+
return segments[1] ?? null;
|
|
173
|
+
// An overlay root IS the modules root, so the first segment is the module id.
|
|
174
|
+
return segments.length > 1 ? (segments[0] ?? null) : null;
|
|
175
|
+
}
|
|
176
|
+
function collectFromFile(file, root, base, resolver, out) {
|
|
177
|
+
const source = resolver.sourceOf(file);
|
|
178
|
+
if (source === null)
|
|
179
|
+
return;
|
|
180
|
+
if (!/requireAdmin|hasPermission/.test(source))
|
|
181
|
+
return;
|
|
182
|
+
const moduleId = moduleIdFor(file, root);
|
|
183
|
+
const relFile = relative(base, file);
|
|
184
|
+
for (const site of collectFromSource(source, file, resolver)) {
|
|
185
|
+
out.push({ ...site, file: relFile, moduleId });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The enforcement shapes this recognises, all of them present in the tree:
|
|
190
|
+
*
|
|
191
|
+
* - `requireAdmin('code')`, and the same call reached through a dependency bag
|
|
192
|
+
* or a container cradle (`deps.requireAdmin(…)`, `cradle().requireAdmin(…)`);
|
|
193
|
+
* - the optional-call form `requireAdmin?.('code')`;
|
|
194
|
+
* - `requireAdminAny(['a', 'b'])` in either of the above positions;
|
|
195
|
+
* - a constant argument, resolved module-locally, through a relative import, or
|
|
196
|
+
* through `@endora-commerce/contracts`, including a member read of a permission map;
|
|
197
|
+
* - `permissionService.hasPermission(actor, 'code')` — the capability check a
|
|
198
|
+
* service uses where there is no route to hang a `preHandler` on.
|
|
199
|
+
*
|
|
200
|
+
* Comments and regular-expression literals are blanked first: a doc comment
|
|
201
|
+
* quoting a gate is not a gate, several files quote one, and this file's own
|
|
202
|
+
* matcher would otherwise read itself as a call site.
|
|
203
|
+
*/
|
|
204
|
+
function* collectFromSource(source, file, resolver) {
|
|
205
|
+
const bound = collectRuntimeBindings(source);
|
|
206
|
+
const callRe = /(?:[\w$]+\s*\??\.\s*)*(requireAdmin(?:Any)?|hasPermission)\s*(?:\?\.)?\s*\(/g;
|
|
207
|
+
let m;
|
|
208
|
+
while ((m = callRe.exec(source)) !== null) {
|
|
209
|
+
const callee = m[1] ?? '';
|
|
210
|
+
const open = source.indexOf('(', m.index + m[0].length - 1);
|
|
211
|
+
if (open === -1)
|
|
212
|
+
continue;
|
|
213
|
+
const args = readBalanced(source, open);
|
|
214
|
+
// A parameter list, not an argument list: the guard's own signature in an
|
|
215
|
+
// interface or an implementation. Nothing is enforced by a declaration.
|
|
216
|
+
if (PARAMETER_LIST_RE.test(args))
|
|
217
|
+
continue;
|
|
218
|
+
// `hasPermission(actor, 'code')` puts the code second; the guards put it first.
|
|
219
|
+
const argument = (callee === 'hasPermission' ? lastArgument(args) : firstArgument(args)).trim();
|
|
220
|
+
const expression = argument.replace(/\s+/g, ' ');
|
|
221
|
+
if (argument === '') {
|
|
222
|
+
if (callee === 'hasPermission')
|
|
223
|
+
continue;
|
|
224
|
+
yield { expression: '', resolution: 'authenticated-admin', codes: [] };
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// `permission ?? ''` is the same runtime value with a default.
|
|
228
|
+
const normalised = argument.replace(/\s*\?\?\s*(['"]).*?\1\s*$/, '').trim();
|
|
229
|
+
if (/^['"[]/.test(normalised)) {
|
|
230
|
+
const codes = [...normalised.matchAll(/(['"])([^'"]*)\1/g)]
|
|
231
|
+
.map((lit) => lit[2] ?? '')
|
|
232
|
+
.filter((code) => code.length > 0);
|
|
233
|
+
yield codes.length > 0
|
|
234
|
+
? { expression, resolution: 'code', codes }
|
|
235
|
+
: { expression, resolution: 'unresolved', codes: [] };
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
// Resolve before classifying: a constant that happens to share a name with
|
|
239
|
+
// a parameter somewhere in the file is still a constant.
|
|
240
|
+
const identifier = /^[A-Za-z_$][\w$]*$/.test(normalised) ? normalised : null;
|
|
241
|
+
const member = /^([A-Za-z_$][\w$]*)\.([A-Za-z_$][\w$]*)$/.exec(normalised);
|
|
242
|
+
const value = identifier
|
|
243
|
+
? resolver.lookup(file, identifier, null)
|
|
244
|
+
: member?.[1] && member[2]
|
|
245
|
+
? resolver.lookup(file, member[1], member[2])
|
|
246
|
+
: null;
|
|
247
|
+
if (value !== null) {
|
|
248
|
+
yield { expression, resolution: 'code', codes: [value] };
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const rootName = identifier ?? member?.[1] ?? null;
|
|
252
|
+
yield rootName !== null && bound.has(rootName)
|
|
253
|
+
? { expression, resolution: 'runtime-value', codes: [] }
|
|
254
|
+
: { expression, resolution: 'unresolved', codes: [] };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** A leading `name:` or `name?:` marks a parameter list rather than arguments. */
|
|
258
|
+
const PARAMETER_LIST_RE = /^\s*(?:readonly\s+)?[A-Za-z_$][\w$]*\s*\??\s*:/;
|
|
259
|
+
/**
|
|
260
|
+
* Identifiers the file binds to a value only known at runtime: function and
|
|
261
|
+
* arrow parameters, `for (const x of …)` loop variables, and a `const` whose
|
|
262
|
+
* initialiser is not a string literal (a registry lookup, say). An argument
|
|
263
|
+
* rooted in one of these carries no literal to check; the literal — if there is
|
|
264
|
+
* one — is written at the caller or the contributor, a site this scan reads
|
|
265
|
+
* separately. Resolution is attempted first, so a genuine constant that shares
|
|
266
|
+
* a name with a binding is still read as a constant.
|
|
267
|
+
*/
|
|
268
|
+
function collectRuntimeBindings(source) {
|
|
269
|
+
const bound = new Set();
|
|
270
|
+
let m;
|
|
271
|
+
const computedBindingRe = /(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^=;]+)?=\s*(?!['"])/g;
|
|
272
|
+
while ((m = computedBindingRe.exec(source)) !== null) {
|
|
273
|
+
if (m[1])
|
|
274
|
+
bound.add(m[1]);
|
|
275
|
+
}
|
|
276
|
+
const typedParamRe = /[(,]\s*(?:readonly\s+)?([A-Za-z_$][\w$]*)\s*\??\s*:/g;
|
|
277
|
+
while ((m = typedParamRe.exec(source)) !== null) {
|
|
278
|
+
if (m[1])
|
|
279
|
+
bound.add(m[1]);
|
|
280
|
+
}
|
|
281
|
+
const arrowHeadRe = /\(([^()]*)\)\s*=>/g;
|
|
282
|
+
while ((m = arrowHeadRe.exec(source)) !== null) {
|
|
283
|
+
for (const part of (m[1] ?? '').split(',')) {
|
|
284
|
+
const name = /^\s*([A-Za-z_$][\w$]*)\s*$/.exec(part);
|
|
285
|
+
if (name?.[1])
|
|
286
|
+
bound.add(name[1]);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const loopBindingRe = /for\s*\(\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s+(?:of|in)\b/g;
|
|
290
|
+
while ((m = loopBindingRe.exec(source)) !== null) {
|
|
291
|
+
if (m[1])
|
|
292
|
+
bound.add(m[1]);
|
|
293
|
+
}
|
|
294
|
+
return bound;
|
|
295
|
+
}
|
|
296
|
+
/** Text between `openIndex`'s bracket and its match, quotes respected. */
|
|
297
|
+
function readBalanced(source, openIndex) {
|
|
298
|
+
let depth = 0;
|
|
299
|
+
let i = openIndex;
|
|
300
|
+
for (; i < source.length; i += 1) {
|
|
301
|
+
const c = source[i];
|
|
302
|
+
if (c === '(' || c === '[' || c === '{')
|
|
303
|
+
depth += 1;
|
|
304
|
+
else if (c === ')' || c === ']' || c === '}') {
|
|
305
|
+
depth -= 1;
|
|
306
|
+
if (depth === 0)
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
else if (c === "'" || c === '"' || c === '`') {
|
|
310
|
+
i = skipString(source, i);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return source.slice(openIndex + 1, i);
|
|
314
|
+
}
|
|
315
|
+
/** Split on top-level commas only — an array or object argument keeps its own. */
|
|
316
|
+
function splitArguments(args) {
|
|
317
|
+
const parts = [];
|
|
318
|
+
let depth = 0;
|
|
319
|
+
let start = 0;
|
|
320
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
321
|
+
const c = args[i];
|
|
322
|
+
if (c === '(' || c === '[' || c === '{')
|
|
323
|
+
depth += 1;
|
|
324
|
+
else if (c === ')' || c === ']' || c === '}')
|
|
325
|
+
depth -= 1;
|
|
326
|
+
else if (c === "'" || c === '"' || c === '`')
|
|
327
|
+
i = skipString(args, i);
|
|
328
|
+
else if (c === ',' && depth === 0) {
|
|
329
|
+
parts.push(args.slice(start, i));
|
|
330
|
+
start = i + 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
parts.push(args.slice(start));
|
|
334
|
+
return parts;
|
|
335
|
+
}
|
|
336
|
+
function firstArgument(args) {
|
|
337
|
+
return splitArguments(args)[0] ?? '';
|
|
338
|
+
}
|
|
339
|
+
function lastArgument(args) {
|
|
340
|
+
const parts = splitArguments(args);
|
|
341
|
+
return parts[parts.length - 1] ?? '';
|
|
342
|
+
}
|
|
343
|
+
/** Index of the closing quote of the string starting at `i`. */
|
|
344
|
+
function skipString(source, i) {
|
|
345
|
+
const quote = source[i];
|
|
346
|
+
let j = i + 1;
|
|
347
|
+
while (j < source.length) {
|
|
348
|
+
if (source[j] === '\\') {
|
|
349
|
+
j += 2;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (source[j] === quote)
|
|
353
|
+
return j;
|
|
354
|
+
j += 1;
|
|
355
|
+
}
|
|
356
|
+
return j;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Resolves a constant argument to its literal, following at most a few hops:
|
|
360
|
+
* module-local, a relative import, or `@endora-commerce/contracts`. Deeper indirection has
|
|
361
|
+
* never appeared, and pretending to resolve it would hide the same gate the
|
|
362
|
+
* `unresolved` bucket is there to surface.
|
|
363
|
+
*
|
|
364
|
+
* Exported for `scripts/check-action-route-permissions.ts` (issue #232), which
|
|
365
|
+
* asks the third direction — is an action's `requiredPermission` the code
|
|
366
|
+
* enforced on **its own** target route — and needs the same argument shapes read
|
|
367
|
+
* the same way. A second resolver would answer the same question differently the
|
|
368
|
+
* first time either of them learned a shape.
|
|
369
|
+
*/
|
|
370
|
+
export class ConstantResolver {
|
|
371
|
+
#facts = new Map();
|
|
372
|
+
#contractsFiles = null;
|
|
373
|
+
sourceOf(file) {
|
|
374
|
+
return this.#factsFor(file)?.source ?? null;
|
|
375
|
+
}
|
|
376
|
+
lookup(file, name, property, depth = 0) {
|
|
377
|
+
if (depth > 4)
|
|
378
|
+
return null;
|
|
379
|
+
const facts = this.#factsFor(file);
|
|
380
|
+
if (!facts)
|
|
381
|
+
return null;
|
|
382
|
+
if (property === null) {
|
|
383
|
+
const scalar = facts.scalars.get(name);
|
|
384
|
+
if (scalar !== undefined)
|
|
385
|
+
return scalar;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
const value = facts.objects.get(name)?.get(property);
|
|
389
|
+
if (value !== undefined)
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
const imported = facts.imports.get(name);
|
|
393
|
+
if (!imported)
|
|
394
|
+
return null;
|
|
395
|
+
for (const target of this.#resolveSpecifier(file, imported.specifier)) {
|
|
396
|
+
const value = this.lookup(target, imported.exportedAs, property, depth + 1);
|
|
397
|
+
if (value !== null)
|
|
398
|
+
return value;
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
#factsFor(file) {
|
|
403
|
+
if (this.#facts.has(file))
|
|
404
|
+
return this.#facts.get(file) ?? null;
|
|
405
|
+
let raw;
|
|
406
|
+
try {
|
|
407
|
+
raw = readFileSync(file, 'utf8');
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
this.#facts.set(file, null);
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
const source = blankComments(raw);
|
|
414
|
+
const facts = {
|
|
415
|
+
source,
|
|
416
|
+
scalars: collectScalars(source),
|
|
417
|
+
objects: collectObjects(source),
|
|
418
|
+
imports: collectImports(source),
|
|
419
|
+
};
|
|
420
|
+
this.#facts.set(file, facts);
|
|
421
|
+
return facts;
|
|
422
|
+
}
|
|
423
|
+
/** Candidate files a specifier may resolve to, in preference order. */
|
|
424
|
+
#resolveSpecifier(from, specifier) {
|
|
425
|
+
if (specifier === '@endora-commerce/contracts')
|
|
426
|
+
return this.#contractsSources();
|
|
427
|
+
if (!specifier.startsWith('.'))
|
|
428
|
+
return [];
|
|
429
|
+
const base = resolve(dirname(from), specifier);
|
|
430
|
+
const candidates = [base.replace(/\.js$/, '.ts'), `${base}.ts`, join(base, 'index.ts'), base];
|
|
431
|
+
return candidates.filter((c) => existsSync(c) && statSync(c).isFile());
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* `@endora-commerce/contracts` re-exports everything from its barrel, so following the
|
|
435
|
+
* barrel would mean parsing every `export *`. Scanning the package's own
|
|
436
|
+
* sources for the constant reaches the same answer in one hop.
|
|
437
|
+
*/
|
|
438
|
+
#contractsSources() {
|
|
439
|
+
if (this.#contractsFiles !== null)
|
|
440
|
+
return this.#contractsFiles;
|
|
441
|
+
const files = [];
|
|
442
|
+
if (existsSync(CONTRACTS_SRC)) {
|
|
443
|
+
for (const entry of readdirSync(CONTRACTS_SRC, { withFileTypes: true })) {
|
|
444
|
+
if (entry.isFile() && entry.name.endsWith('.ts'))
|
|
445
|
+
files.push(join(CONTRACTS_SRC, entry.name));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
this.#contractsFiles = files;
|
|
449
|
+
return files;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Replace comment and regular-expression bodies with spaces, preserving offsets
|
|
454
|
+
* and string literals.
|
|
455
|
+
*
|
|
456
|
+
* A regex literal is a `/` in a position where an expression may start (after an
|
|
457
|
+
* operator, a bracket or a keyword), which is what distinguishes it from
|
|
458
|
+
* division — division follows a value. Blanking them keeps a matcher written as
|
|
459
|
+
* a pattern, this file's included, from reading as the thing it matches.
|
|
460
|
+
*/
|
|
461
|
+
function blankComments(source) {
|
|
462
|
+
let out = '';
|
|
463
|
+
let i = 0;
|
|
464
|
+
while (i < source.length) {
|
|
465
|
+
const c = source[i];
|
|
466
|
+
const next = source[i + 1];
|
|
467
|
+
if (c === '/' && next !== '/' && next !== '*' && startsExpression(out)) {
|
|
468
|
+
const end = skipRegex(source, i);
|
|
469
|
+
out += '/';
|
|
470
|
+
for (let k = i + 1; k < end; k += 1)
|
|
471
|
+
out += source[k] === '\n' ? '\n' : ' ';
|
|
472
|
+
out += end < source.length ? '/' : '';
|
|
473
|
+
i = end + 1;
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (c === '/' && next === '/') {
|
|
477
|
+
while (i < source.length && source[i] !== '\n') {
|
|
478
|
+
out += ' ';
|
|
479
|
+
i += 1;
|
|
480
|
+
}
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (c === '/' && next === '*') {
|
|
484
|
+
while (i < source.length && !(source[i] === '*' && source[i + 1] === '/')) {
|
|
485
|
+
out += source[i] === '\n' ? '\n' : ' ';
|
|
486
|
+
i += 1;
|
|
487
|
+
}
|
|
488
|
+
out += ' ';
|
|
489
|
+
i += 2;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
if (c === "'" || c === '"' || c === '`') {
|
|
493
|
+
const end = skipString(source, i);
|
|
494
|
+
out += source.slice(i, end + 1);
|
|
495
|
+
i = end + 1;
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
out += c;
|
|
499
|
+
i += 1;
|
|
500
|
+
}
|
|
501
|
+
return out;
|
|
502
|
+
}
|
|
503
|
+
/** True when the last significant character allows an expression to start. */
|
|
504
|
+
function startsExpression(emitted) {
|
|
505
|
+
const trimmed = emitted.replace(/\s+$/, '');
|
|
506
|
+
if (trimmed.length === 0)
|
|
507
|
+
return true;
|
|
508
|
+
const last = trimmed[trimmed.length - 1] ?? '';
|
|
509
|
+
if ('([{,;:=!&|?+-*%~^<>'.includes(last))
|
|
510
|
+
return true;
|
|
511
|
+
return /\b(?:return|typeof|case|in|of|new|delete|void|do|else)$/.test(trimmed);
|
|
512
|
+
}
|
|
513
|
+
/** Index of the closing `/` of the regex literal starting at `i`. */
|
|
514
|
+
function skipRegex(source, i) {
|
|
515
|
+
let j = i + 1;
|
|
516
|
+
let inClass = false;
|
|
517
|
+
while (j < source.length) {
|
|
518
|
+
const c = source[j];
|
|
519
|
+
if (c === '\\') {
|
|
520
|
+
j += 2;
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
if (c === '\n')
|
|
524
|
+
return j - 1;
|
|
525
|
+
if (c === '[')
|
|
526
|
+
inClass = true;
|
|
527
|
+
else if (c === ']')
|
|
528
|
+
inClass = false;
|
|
529
|
+
else if (c === '/' && !inClass)
|
|
530
|
+
return j;
|
|
531
|
+
j += 1;
|
|
532
|
+
}
|
|
533
|
+
return j;
|
|
534
|
+
}
|
|
535
|
+
function collectScalars(source) {
|
|
536
|
+
const scalars = new Map();
|
|
537
|
+
const re = /(?:export\s+)?(?:const|let)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^=;]+)?=\s*(['"])([^'"]*)\2/g;
|
|
538
|
+
let m;
|
|
539
|
+
while ((m = re.exec(source)) !== null) {
|
|
540
|
+
if (m[1] && m[3] !== undefined)
|
|
541
|
+
scalars.set(m[1], m[3]);
|
|
542
|
+
}
|
|
543
|
+
return scalars;
|
|
544
|
+
}
|
|
545
|
+
/** Flat `{ KEY: 'code' }` permission maps; nothing in the tree nests one. */
|
|
546
|
+
function collectObjects(source) {
|
|
547
|
+
const objects = new Map();
|
|
548
|
+
const re = /(?:export\s+)?const\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^={;]+)?=\s*\{([^{}]*)\}/g;
|
|
549
|
+
let m;
|
|
550
|
+
while ((m = re.exec(source)) !== null) {
|
|
551
|
+
const name = m[1];
|
|
552
|
+
const body = m[2];
|
|
553
|
+
if (!name || body === undefined)
|
|
554
|
+
continue;
|
|
555
|
+
const entries = new Map();
|
|
556
|
+
const propRe = /([A-Za-z_$][\w$]*)\s*:\s*(['"])([^'"]*)\2/g;
|
|
557
|
+
let p;
|
|
558
|
+
while ((p = propRe.exec(body)) !== null) {
|
|
559
|
+
if (p[1] && p[3] !== undefined)
|
|
560
|
+
entries.set(p[1], p[3]);
|
|
561
|
+
}
|
|
562
|
+
if (entries.size > 0)
|
|
563
|
+
objects.set(name, entries);
|
|
564
|
+
}
|
|
565
|
+
return objects;
|
|
566
|
+
}
|
|
567
|
+
function collectImports(source) {
|
|
568
|
+
const imports = new Map();
|
|
569
|
+
const re = /import\s*(?:type\s+)?\{([^}]*)\}\s*from\s*['"]([^'"]+)['"]/g;
|
|
570
|
+
let m;
|
|
571
|
+
while ((m = re.exec(source)) !== null) {
|
|
572
|
+
const clause = m[1];
|
|
573
|
+
const specifier = m[2];
|
|
574
|
+
if (!clause || !specifier)
|
|
575
|
+
continue;
|
|
576
|
+
for (const part of clause.split(',')) {
|
|
577
|
+
const named = /^(?:type\s+)?([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/.exec(part.trim());
|
|
578
|
+
if (!named?.[1])
|
|
579
|
+
continue;
|
|
580
|
+
imports.set(named[2] ?? named[1], { exportedAs: named[1], specifier });
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return imports;
|
|
584
|
+
}
|
|
585
|
+
//# sourceMappingURL=permission-inventory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-inventory.js","sourceRoot":"","sources":["../../src/backend/permission-inventory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { EntityManager } from '@mikro-orm/postgresql';
|
|
2
|
+
import type { AdminRolePort, AdminRoleRecord, SystemRoleCodePort } from '@endora-commerce/contracts';
|
|
3
|
+
import { AdminRole } from '../entities/admin-role.entity.js';
|
|
4
|
+
import { type AdminRoleService } from './admin-role-service.js';
|
|
5
|
+
/**
|
|
6
|
+
* The published face of `admin_roles` (feature 075, Phase P).
|
|
7
|
+
*
|
|
8
|
+
* Fourteen inbound sites: `admin_users` reads a role beside its user and
|
|
9
|
+
* enforces `requiresTwoFactor` at login, `admin_actions` filters the command
|
|
10
|
+
* palette by a user's permissions, `blog` protects the role it seeds, and the
|
|
11
|
+
* dev seed creates one.
|
|
12
|
+
*
|
|
13
|
+
* `findByCode` is the method with no direct predecessor. `admin_users`'
|
|
14
|
+
* service and the dev seed both look a role up by code with
|
|
15
|
+
* `em.findOne(AdminRole, { code })`, which is the read it replaces.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AdminRoleReadService {
|
|
18
|
+
private readonly emFactory;
|
|
19
|
+
constructor(emFactory: () => EntityManager);
|
|
20
|
+
findByCode(code: string): Promise<AdminRoleRecord | null>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The full role port, adapting `AdminRoleService` and adding the by-code
|
|
24
|
+
* lookup. The service arrives as a getter so the adapter resolves it per call
|
|
25
|
+
* rather than capturing it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createAdminRolePort(emFactory: () => EntityManager, getService: () => AdminRoleService): AdminRolePort;
|
|
28
|
+
/**
|
|
29
|
+
* The contribution seam for deletion-protected role codes.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately a plain object over the module-level set rather than anything
|
|
32
|
+
* gated: `blog` registers its seeded code from a boot hook, and a gate would
|
|
33
|
+
* both throw during composition and — worse — let an operator delete a
|
|
34
|
+
* protected role by switching its owner off for a moment. See the port's
|
|
35
|
+
* contract for the full reasoning.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createSystemRoleCodePort(): SystemRoleCodePort;
|
|
38
|
+
export declare function toAdminRoleRecord(role: AdminRole): AdminRoleRecord;
|
|
39
|
+
//# sourceMappingURL=admin-role-ports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-role-ports.d.ts","sourceRoot":"","sources":["../../../src/backend/services/admin-role-ports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrG,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EACL,KAAK,gBAAgB,EAGtB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;;;;;;GAWG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,aAAa;IAErD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;CAIhE;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,aAAa,EAC9B,UAAU,EAAE,MAAM,gBAAgB,GACjC,aAAa,CAef;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,kBAAkB,CAK7D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,eAAe,CAUlE"}
|