@astram/guard 0.15.0 → 0.17.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/dist/auth/auth.module.d.ts +8 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +19 -1
- package/dist/authentication/argon2id-password-hasher.d.ts +3 -0
- package/dist/authentication/argon2id-password-hasher.d.ts.map +1 -1
- package/dist/authentication/argon2id-password-hasher.js +21 -0
- package/dist/authentication/credential-provisioner.d.ts +30 -0
- package/dist/authentication/credential-provisioner.d.ts.map +1 -0
- package/dist/authentication/credential-provisioner.js +16 -0
- package/dist/authentication/mariadb-credential-provisioner.d.ts +26 -0
- package/dist/authentication/mariadb-credential-provisioner.d.ts.map +1 -0
- package/dist/authentication/mariadb-credential-provisioner.js +72 -0
- package/dist/authz/operation-marker-check.d.ts +14 -0
- package/dist/authz/operation-marker-check.d.ts.map +1 -0
- package/dist/authz/operation-marker-check.js +25 -0
- package/dist/authz/wire-record-authorization.d.ts.map +1 -1
- package/dist/authz/wire-record-authorization.js +3 -2
- package/dist/boot/boot-sequence.d.ts +6 -0
- package/dist/boot/boot-sequence.d.ts.map +1 -1
- package/dist/boot/boot-sequence.js +34 -2
- package/dist/boot/single-layout-probe.d.ts +66 -0
- package/dist/boot/single-layout-probe.d.ts.map +1 -0
- package/dist/boot/single-layout-probe.js +334 -0
- package/dist/config/app-config.d.ts +17 -0
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +54 -6
- package/dist/config/reserved-principal-ids.d.ts +14 -0
- package/dist/config/reserved-principal-ids.d.ts.map +1 -0
- package/dist/config/reserved-principal-ids.js +24 -0
- package/dist/config/root-ids-from-env.d.ts +2 -0
- package/dist/config/root-ids-from-env.d.ts.map +1 -0
- package/dist/config/root-ids-from-env.js +41 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/internal.d.ts +1 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -1
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +4 -0
- package/package.json +3 -3
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SingleLayoutProbeBoot = void 0;
|
|
4
|
+
exports.probeSingleLayoutPrivileges = probeSingleLayoutPrivileges;
|
|
5
|
+
exports.singleLayoutWarningLines = singleLayoutWarningLines;
|
|
6
|
+
/**
|
|
7
|
+
* Single-database-layout boot privilege probe (single-database-layout spec §5.F, Req 7, AC-8,
|
|
8
|
+
* ADR-0041).
|
|
9
|
+
*
|
|
10
|
+
* With `SHIELD_DB_LAYOUT=single` the four framework modules share one database, so the isolation
|
|
11
|
+
* the four-database grant matrix gave is partly gone. Which protections are really lost depends on
|
|
12
|
+
* what the runtime account may do, so this reads (never writes) `information_schema`
|
|
13
|
+
* USER_PRIVILEGES / SCHEMA_PRIVILEGES / TABLE_PRIVILEGES / COLUMN_PRIVILEGES / APPLICABLE_ROLES for `CURRENT_USER()`, once at boot, and
|
|
14
|
+
* derives the five protections lost (spec §5.F). It does not look for `prune_audit`: MariaDB hides
|
|
15
|
+
* a routine from an account without EXECUTE on it, which the runtime account must not have (spec
|
|
16
|
+
* v3); `audit:prune` reports a missing routine itself.
|
|
17
|
+
*
|
|
18
|
+
* `GRANT ... TO PUBLIC` (MariaDB 10.11+) applies to every account but information_schema shows
|
|
19
|
+
* those rows only to privileged accounts (verified on 10.11 and 11.4: a plain account sees none).
|
|
20
|
+
* `SHOW GRANTS` for the current user needs no privilege and does list them, so its `TO PUBLIC`
|
|
21
|
+
* lines are parsed and merged with the information_schema rows. Its routine-level lines
|
|
22
|
+
* (`ON PROCEDURE|FUNCTION`) are the only view of routine privileges the account has: any such grant
|
|
23
|
+
* on the database loses the retention floor. An unparsable line of the account's own is ignored, an
|
|
24
|
+
* unparsable PUBLIC or routine line fails closed; a failing `SHOW GRANTS`
|
|
25
|
+
* fails closed like any other failed read.
|
|
26
|
+
*
|
|
27
|
+
* The probe never blocks boot (the operator's ack already accepted the risk) and is never on the
|
|
28
|
+
* request path (Shield #12). Warning lines carry no credential and no host (Shield #10): a failed
|
|
29
|
+
* query's driver message is dropped.
|
|
30
|
+
*/
|
|
31
|
+
const common_1 = require("@nestjs/common");
|
|
32
|
+
const PROTECTIONS = {
|
|
33
|
+
'append-only-audit': {
|
|
34
|
+
id: 'append-only-audit',
|
|
35
|
+
invariant: 'Shield #6',
|
|
36
|
+
text: 'Append-only audit: the runtime account can UPDATE or DELETE audit_log.',
|
|
37
|
+
},
|
|
38
|
+
'no-runtime-ddl': {
|
|
39
|
+
id: 'no-runtime-ddl',
|
|
40
|
+
invariant: 'Shield #11',
|
|
41
|
+
text: 'No runtime DDL: the runtime account holds DDL privileges on the database.',
|
|
42
|
+
},
|
|
43
|
+
'schema-separation': {
|
|
44
|
+
id: 'schema-separation',
|
|
45
|
+
invariant: 'Shield #11 (ADR-0014)',
|
|
46
|
+
text: 'Schema separation: identity, authz and audit tables share one database with app tables ' +
|
|
47
|
+
'(always lost in the single layout).',
|
|
48
|
+
},
|
|
49
|
+
'retention-floor': {
|
|
50
|
+
id: 'retention-floor',
|
|
51
|
+
invariant: 'Shield #6',
|
|
52
|
+
text: 'DB-enforced retention floor: audit rows can be deleted directly, around prune_audit, or ' +
|
|
53
|
+
'the runtime account holds a grant on prune_audit.',
|
|
54
|
+
},
|
|
55
|
+
'separate-ddl-account': {
|
|
56
|
+
id: 'separate-ddl-account',
|
|
57
|
+
invariant: 'Shield #11',
|
|
58
|
+
text: 'Separate deploy-time DDL account: the runtime account can run migrations, so locking the ' +
|
|
59
|
+
'migrate account between deploys is not possible.',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const ALL_IDS = Object.keys(PROTECTIONS);
|
|
63
|
+
/** Aligned with `scripts/db-grants/lib.mjs` DDL_PRIVS (GRANT OPTION is judged via IS_GRANTABLE too). */
|
|
64
|
+
const DDL_PRIVILEGES = new Set([
|
|
65
|
+
'CREATE',
|
|
66
|
+
'ALTER',
|
|
67
|
+
'DROP',
|
|
68
|
+
'INDEX',
|
|
69
|
+
'CREATE VIEW',
|
|
70
|
+
'SHOW VIEW',
|
|
71
|
+
'TRIGGER',
|
|
72
|
+
'CREATE ROUTINE',
|
|
73
|
+
'ALTER ROUTINE',
|
|
74
|
+
'EVENT',
|
|
75
|
+
'REFERENCES',
|
|
76
|
+
'CREATE TEMPORARY TABLES',
|
|
77
|
+
'LOCK TABLES',
|
|
78
|
+
'DELETE HISTORY',
|
|
79
|
+
'GRANT OPTION',
|
|
80
|
+
]);
|
|
81
|
+
const AUDIT_TABLE = 'audit_log';
|
|
82
|
+
/** `'user'@'host'` for CURRENT_USER(), built in SQL (never from env). */
|
|
83
|
+
const GRANTEE_SQL = `CONCAT("'", SUBSTRING_INDEX(CURRENT_USER(),'@',1), "'@'", ` +
|
|
84
|
+
`SUBSTRING_INDEX(CURRENT_USER(),'@',-1), "'")`;
|
|
85
|
+
/** Does a GRANT schema pattern (`\_` escaped, `_`/`%` wildcards) cover `database`? */
|
|
86
|
+
function schemaCovers(pattern, database) {
|
|
87
|
+
let re = '';
|
|
88
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
89
|
+
const c = pattern[i];
|
|
90
|
+
if (c === '\\' && i + 1 < pattern.length) {
|
|
91
|
+
re += pattern[++i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
92
|
+
}
|
|
93
|
+
else if (c === '%')
|
|
94
|
+
re += '.*';
|
|
95
|
+
else if (c === '_')
|
|
96
|
+
re += '.';
|
|
97
|
+
else
|
|
98
|
+
re += c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
99
|
+
}
|
|
100
|
+
return new RegExp(`^${re}$`, 'i').test(database);
|
|
101
|
+
}
|
|
102
|
+
/** Splits `a, b (c, d), e` at top-level commas, respecting backticks and parentheses. */
|
|
103
|
+
function splitTopLevel(text) {
|
|
104
|
+
const out = [];
|
|
105
|
+
let cur = '';
|
|
106
|
+
let depth = 0;
|
|
107
|
+
let tick = false;
|
|
108
|
+
for (const c of text) {
|
|
109
|
+
if (c === '`')
|
|
110
|
+
tick = !tick;
|
|
111
|
+
else if (!tick && c === '(')
|
|
112
|
+
depth++;
|
|
113
|
+
else if (!tick && c === ')')
|
|
114
|
+
depth = Math.max(0, depth - 1);
|
|
115
|
+
if (c === ',' && depth === 0 && !tick) {
|
|
116
|
+
out.push(cur);
|
|
117
|
+
cur = '';
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
cur += c;
|
|
121
|
+
}
|
|
122
|
+
out.push(cur);
|
|
123
|
+
return out.map((x) => x.trim()).filter((x) => x.length > 0);
|
|
124
|
+
}
|
|
125
|
+
// `*`, backtick-quoted, ANSI_QUOTES double-quoted, or bare (sql_quote_show_create=0).
|
|
126
|
+
const IDENT = '(\\*|`(?:[^`]|``)*`|"(?:[^"]|"")*"|[^\\s.`"]+)';
|
|
127
|
+
const GRANT_LINE = /^GRANT\s+(.+?)\s+ON\s+(.+?)\s+TO\s+(.+?)(\s+WITH\s+GRANT\s+OPTION)?\s*$/is;
|
|
128
|
+
const TARGET = new RegExp(`^(?:(PROCEDURE|FUNCTION|PACKAGE|PACKAGE BODY)\\s+)?${IDENT}\\.${IDENT}$`, 'i');
|
|
129
|
+
const unquote = (id) => id.startsWith('`')
|
|
130
|
+
? id.slice(1, -1).replace(/``/g, '`')
|
|
131
|
+
: id.startsWith('"')
|
|
132
|
+
? id.slice(1, -1).replace(/""/g, '"')
|
|
133
|
+
: id;
|
|
134
|
+
const ROUTINE_TARGET = /^(PROCEDURE|FUNCTION|PACKAGE)\b/i;
|
|
135
|
+
/**
|
|
136
|
+
* Reads `TO PUBLIC` privileges and routine-level grants out of `SHOW GRANTS` lines. An
|
|
137
|
+
* unparsable `TO PUBLIC` or routine line sets `unreadable` (the probe then fails closed); any
|
|
138
|
+
* other unparsable line is the account's own and is skipped. Never throws.
|
|
139
|
+
*/
|
|
140
|
+
function parseShowGrants(lines) {
|
|
141
|
+
const out = {
|
|
142
|
+
user: [],
|
|
143
|
+
schema: [],
|
|
144
|
+
table: [],
|
|
145
|
+
column: [],
|
|
146
|
+
routineSchemas: [],
|
|
147
|
+
unreadable: false,
|
|
148
|
+
};
|
|
149
|
+
for (const line of lines) {
|
|
150
|
+
const m = GRANT_LINE.exec(line);
|
|
151
|
+
if (!m)
|
|
152
|
+
continue;
|
|
153
|
+
const rawTarget = m[2].trim();
|
|
154
|
+
const target = TARGET.exec(rawTarget);
|
|
155
|
+
if (!target) {
|
|
156
|
+
if (/^PUBLIC$/i.test(m[3].trim()) || ROUTINE_TARGET.test(rawTarget)) {
|
|
157
|
+
out.unreadable = true;
|
|
158
|
+
}
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const kind = target[1];
|
|
162
|
+
const schema = unquote(target[2]);
|
|
163
|
+
const name = unquote(target[3]);
|
|
164
|
+
if (kind) {
|
|
165
|
+
out.routineSchemas.push(schema);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!/^PUBLIC$/i.test(m[3].trim()))
|
|
169
|
+
continue;
|
|
170
|
+
const is = m[4] ? 'YES' : 'NO';
|
|
171
|
+
for (const item of splitTopLevel(m[1])) {
|
|
172
|
+
const paren = item.indexOf('(');
|
|
173
|
+
const type = (paren < 0 ? item : item.slice(0, paren)).trim().toUpperCase();
|
|
174
|
+
if (!type)
|
|
175
|
+
continue;
|
|
176
|
+
if (paren >= 0) {
|
|
177
|
+
// Column-level: one row per column (the name only matters for the table).
|
|
178
|
+
out.column.push({
|
|
179
|
+
TABLE_SCHEMA: schema,
|
|
180
|
+
TABLE_NAME: name,
|
|
181
|
+
PRIVILEGE_TYPE: type,
|
|
182
|
+
IS_GRANTABLE: is,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
else if (schema === '*') {
|
|
186
|
+
out.user.push({ PRIVILEGE_TYPE: type, IS_GRANTABLE: is });
|
|
187
|
+
}
|
|
188
|
+
else if (name === '*') {
|
|
189
|
+
out.schema.push({ TABLE_SCHEMA: schema, PRIVILEGE_TYPE: type, IS_GRANTABLE: is });
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
out.table.push({
|
|
193
|
+
TABLE_SCHEMA: schema,
|
|
194
|
+
TABLE_NAME: name,
|
|
195
|
+
PRIVILEGE_TYPE: type,
|
|
196
|
+
IS_GRANTABLE: is,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
const priv = (r) => String(r.PRIVILEGE_TYPE ?? '').toUpperCase();
|
|
204
|
+
const grantable = (r) => String(r.IS_GRANTABLE ?? '').toUpperCase() === 'YES';
|
|
205
|
+
const failed = () => ({
|
|
206
|
+
lost: ALL_IDS.map((id) => PROTECTIONS[id]),
|
|
207
|
+
readFailed: true,
|
|
208
|
+
});
|
|
209
|
+
/**
|
|
210
|
+
* Derive the protections the runtime account has lost. Never throws, and never reports the audit
|
|
211
|
+
* wall intact when it cannot see the grants: a failed query, a runtime account whose own
|
|
212
|
+
* SELECT+INSERT on the database is invisible, or an account with roles (not inspected) reports all
|
|
213
|
+
* five with `readFailed`.
|
|
214
|
+
*/
|
|
215
|
+
async function probeSingleLayoutPrivileges(query, database) {
|
|
216
|
+
try {
|
|
217
|
+
const userInfo = (await query(`SELECT PRIVILEGE_TYPE, IS_GRANTABLE FROM information_schema.USER_PRIVILEGES ` +
|
|
218
|
+
`WHERE GRANTEE = ${GRANTEE_SQL}`));
|
|
219
|
+
const schemaInfo = (await query(`SELECT TABLE_SCHEMA, PRIVILEGE_TYPE, IS_GRANTABLE FROM information_schema.SCHEMA_PRIVILEGES ` +
|
|
220
|
+
`WHERE GRANTEE = ${GRANTEE_SQL}`));
|
|
221
|
+
const tableInfo = (await query(`SELECT TABLE_SCHEMA, TABLE_NAME, PRIVILEGE_TYPE, IS_GRANTABLE ` +
|
|
222
|
+
`FROM information_schema.TABLE_PRIVILEGES WHERE GRANTEE = ${GRANTEE_SQL}`));
|
|
223
|
+
const columnInfo = (await query(`SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, PRIVILEGE_TYPE, IS_GRANTABLE ` +
|
|
224
|
+
`FROM information_schema.COLUMN_PRIVILEGES WHERE GRANTEE = ${GRANTEE_SQL}`));
|
|
225
|
+
const roles = (await query(`SELECT ROLE_NAME FROM information_schema.APPLICABLE_ROLES WHERE GRANTEE = ${GRANTEE_SQL}`));
|
|
226
|
+
// Privileges granted through roles are not inspected: do not claim a wall we cannot see.
|
|
227
|
+
if (roles.length > 0)
|
|
228
|
+
return failed();
|
|
229
|
+
// PUBLIC grants and routine grants: invisible in information_schema to this account.
|
|
230
|
+
const shown = (await query('SHOW GRANTS'));
|
|
231
|
+
const pub = parseShowGrants(shown.map((row) => String(Object.values(row ?? {})[0] ?? '')).filter((l) => l.length > 0));
|
|
232
|
+
if (pub.unreadable)
|
|
233
|
+
return failed();
|
|
234
|
+
const user = [...userInfo, ...pub.user];
|
|
235
|
+
const schema = [...schemaInfo, ...pub.schema];
|
|
236
|
+
const table = [...tableInfo, ...pub.table];
|
|
237
|
+
const column = [...columnInfo, ...pub.column];
|
|
238
|
+
const inDb = (r) => schemaCovers(String(r.TABLE_SCHEMA ?? ''), database);
|
|
239
|
+
const schemaRows = schema.filter(inDb);
|
|
240
|
+
const tables = table.filter(inDb);
|
|
241
|
+
const columns = column.filter(inDb);
|
|
242
|
+
// Any routine-level grant on the database (EXECUTE, ALTER ROUTINE, ...): prune_audit is reachable.
|
|
243
|
+
const routineGrant = pub.routineSchemas.some((sch) => schemaCovers(sch, database));
|
|
244
|
+
// Privileges that reach the whole database: global plus matching schema grants.
|
|
245
|
+
const db = new Set([...user.map(priv), ...schemaRows.map(priv)]);
|
|
246
|
+
const all = db.has('ALL PRIVILEGES') || db.has('ALL');
|
|
247
|
+
// The account booted, so it holds SELECT and INSERT on the database. Not seeing them means
|
|
248
|
+
// the grants are invisible (restricted view, odd grantee form): fail closed.
|
|
249
|
+
if (!all && !(db.has('SELECT') && db.has('INSERT')))
|
|
250
|
+
return failed();
|
|
251
|
+
// WITH GRANT OPTION anywhere on the database: the account can give itself anything.
|
|
252
|
+
const selfGrant = [...user, ...schemaRows, ...tables, ...columns].some(grantable);
|
|
253
|
+
const auditWrite = selfGrant ||
|
|
254
|
+
all ||
|
|
255
|
+
db.has('UPDATE') ||
|
|
256
|
+
db.has('DELETE') ||
|
|
257
|
+
// DROP allows TRUNCATE and dropping audit_log; ALTER rewrites it.
|
|
258
|
+
db.has('DROP') ||
|
|
259
|
+
db.has('ALTER') ||
|
|
260
|
+
tables.some((r) => String(r.TABLE_NAME ?? '').toLowerCase() === AUDIT_TABLE &&
|
|
261
|
+
['UPDATE', 'DELETE', 'DROP', 'ALTER', 'ALL PRIVILEGES'].includes(priv(r))) ||
|
|
262
|
+
// Column-level UPDATE rewrites audit rows (there is no column-level DELETE).
|
|
263
|
+
columns.some((r) => String(r.TABLE_NAME ?? '').toLowerCase() === AUDIT_TABLE && priv(r) === 'UPDATE');
|
|
264
|
+
const ddl = selfGrant ||
|
|
265
|
+
all ||
|
|
266
|
+
[...db].some((p) => DDL_PRIVILEGES.has(p)) ||
|
|
267
|
+
tables.some((r) => DDL_PRIVILEGES.has(priv(r)));
|
|
268
|
+
// Database-level or global EXECUTE reaches prune_audit.
|
|
269
|
+
const execute = db.has('EXECUTE');
|
|
270
|
+
const lost = new Set(['schema-separation']);
|
|
271
|
+
if (auditWrite || routineGrant || execute) {
|
|
272
|
+
// The floor lives in prune_audit; a direct write on audit_log, or any grant on a routine
|
|
273
|
+
// of the database (EXECUTE / ALTER ROUTINE on prune_audit), gets around or weakens it.
|
|
274
|
+
lost.add('retention-floor');
|
|
275
|
+
}
|
|
276
|
+
if (auditWrite)
|
|
277
|
+
lost.add('append-only-audit');
|
|
278
|
+
if (ddl) {
|
|
279
|
+
lost.add('no-runtime-ddl');
|
|
280
|
+
lost.add('separate-ddl-account');
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
lost: ALL_IDS.filter((id) => lost.has(id)).map((id) => PROTECTIONS[id]),
|
|
284
|
+
readFailed: false,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
return failed();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* The boot warning: a header naming the database and ADR-0041, then one line per lost protection
|
|
293
|
+
* citing its invariant. No credential, host or driver message.
|
|
294
|
+
*/
|
|
295
|
+
function singleLayoutWarningLines(probe, database) {
|
|
296
|
+
const lines = [
|
|
297
|
+
`Shield single database layout on "${database}" (ADR-0041): reduced isolation accepted; ` +
|
|
298
|
+
`protections lost:`,
|
|
299
|
+
];
|
|
300
|
+
if (probe.readFailed) {
|
|
301
|
+
lines.push('Runtime privileges could not be read; assuming all protections are lost.');
|
|
302
|
+
}
|
|
303
|
+
for (const p of probe.lost)
|
|
304
|
+
lines.push(`- ${p.text} (${p.invariant})`);
|
|
305
|
+
const ids = new Set(probe.lost.map((p) => p.id));
|
|
306
|
+
if (!probe.readFailed &&
|
|
307
|
+
!ids.has('append-only-audit') &&
|
|
308
|
+
!ids.has('no-runtime-ddl') &&
|
|
309
|
+
!ids.has('retention-floor')) {
|
|
310
|
+
lines.push('Two-account setup: audit wall intact (no direct UPDATE/DELETE/DDL/grant rights seen for ' +
|
|
311
|
+
'this account).');
|
|
312
|
+
}
|
|
313
|
+
return lines;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Runs the probe once at application bootstrap on the runtime pool and logs the warning. Never
|
|
317
|
+
* throws, so boot is never blocked (spec §5.F).
|
|
318
|
+
*/
|
|
319
|
+
class SingleLayoutProbeBoot {
|
|
320
|
+
query;
|
|
321
|
+
database;
|
|
322
|
+
logger;
|
|
323
|
+
constructor(query, database, logger = new common_1.Logger('Shield')) {
|
|
324
|
+
this.query = query;
|
|
325
|
+
this.database = database;
|
|
326
|
+
this.logger = logger;
|
|
327
|
+
}
|
|
328
|
+
async onApplicationBootstrap() {
|
|
329
|
+
const probe = await probeSingleLayoutPrivileges(this.query, this.database);
|
|
330
|
+
for (const line of singleLayoutWarningLines(probe, this.database))
|
|
331
|
+
this.logger.warn(line);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
exports.SingleLayoutProbeBoot = SingleLayoutProbeBoot;
|
|
@@ -182,6 +182,18 @@ export interface PoolBudgetConfig {
|
|
|
182
182
|
readonly headroom: number;
|
|
183
183
|
/** Per-account RUNTIME pool caps (multiplied by `workers` in the budget). */
|
|
184
184
|
readonly perAccountMax: RuntimeAccountPools;
|
|
185
|
+
/**
|
|
186
|
+
* `'single'` when `SHIELD_DB_LAYOUT=single` (single-database-layout spec §5.C, AC-12, ADR-0041):
|
|
187
|
+
* one account opens the core, engine and authz pools, so the budget sums those instead.
|
|
188
|
+
* Absent in the four layout (today's math, unchanged).
|
|
189
|
+
*/
|
|
190
|
+
readonly layout?: 'single';
|
|
191
|
+
/** Single layout: the engine pool cap (`POOL_MAX_ENGINE ?? APP_DB_MAX_CONNECTIONS ?? 10`). */
|
|
192
|
+
readonly engineMax?: number;
|
|
193
|
+
/** Single layout: the identity authz pool cap (`APP_AUTHZ_POOL_MAX ?? 4`). */
|
|
194
|
+
readonly authzMax?: number;
|
|
195
|
+
/** Single layout: the account's `MAX_USER_CONNECTIONS` (`APP_DB_MAX_USER_CONNECTIONS`), if set. */
|
|
196
|
+
readonly maxUserConnections?: number;
|
|
185
197
|
}
|
|
186
198
|
/**
|
|
187
199
|
* Per-account maximum pool sizes for the RUNTIME accounts (the ones each worker
|
|
@@ -393,6 +405,11 @@ export interface PoolBudgetEvaluation {
|
|
|
393
405
|
readonly withinBudget: boolean;
|
|
394
406
|
/** True iff shield_schema_svc pool ≤ {@link SCHEMA_SVC_POOL_HARD_CAP}. */
|
|
395
407
|
readonly schemaSvcWithinHardCap: boolean;
|
|
408
|
+
/**
|
|
409
|
+
* True iff requiredConnections ≤ `maxUserConnections` (always true when unset or in the four
|
|
410
|
+
* layout). Single layout only (spec AC-12).
|
|
411
|
+
*/
|
|
412
|
+
readonly withinUserBudget: boolean;
|
|
396
413
|
}
|
|
397
414
|
/**
|
|
398
415
|
* Evaluate a pool budget. Pure: no throwing, no I/O — the caller decides how to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAWtE,MAAM,WAAW,SAAS;IACxB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC;IAC5D,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAGhC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,wBAAwB,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,gBAAgB,CAEvF;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,EAAE,kBAGpC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IAC5B,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,8FAA8F;AAC9F,eAAO,MAAM,gCAAgC,OAAO,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,yDAAyD;IACzD,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,6 +84,21 @@ function parsePositiveInt(raw, fallback) {
|
|
|
84
84
|
const n = Number.parseInt(raw, 10);
|
|
85
85
|
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* A pool-size variable: unset or blank gives `undefined`; otherwise a positive integer in plain
|
|
89
|
+
* digits (surrounding whitespace allowed) or it throws naming the variable (never the value; fail
|
|
90
|
+
* closed). Identity's engine and authz pools apply the same rule, so the budget counts exactly the
|
|
91
|
+
* pools that open.
|
|
92
|
+
*/
|
|
93
|
+
function strictOptionalPoolInt(raw, name) {
|
|
94
|
+
if (raw === undefined || raw.trim() === '')
|
|
95
|
+
return undefined;
|
|
96
|
+
const t = raw.trim();
|
|
97
|
+
const n = /^[0-9]+$/.test(t) ? Number(t) : Number.NaN;
|
|
98
|
+
if (!Number.isSafeInteger(n) || n < 1)
|
|
99
|
+
throw new Error(`${name} must be a positive integer`);
|
|
100
|
+
return n;
|
|
101
|
+
}
|
|
87
102
|
/**
|
|
88
103
|
* Build the {@link PoolBudgetConfig} from env with documented defaults.
|
|
89
104
|
* Field ↔ env name:
|
|
@@ -95,7 +110,27 @@ function parsePositiveInt(raw, fallback) {
|
|
|
95
110
|
* perAccountMax.shield_schema_svc ← POOL_MAX_SCHEMA_SVC
|
|
96
111
|
*/
|
|
97
112
|
function loadPoolBudget(env = process.env) {
|
|
113
|
+
const single = env.SHIELD_DB_LAYOUT === 'single';
|
|
114
|
+
const maxUser = single
|
|
115
|
+
? strictOptionalPoolInt(env.APP_DB_MAX_USER_CONNECTIONS, 'APP_DB_MAX_USER_CONNECTIONS')
|
|
116
|
+
: undefined;
|
|
98
117
|
return {
|
|
118
|
+
...(single
|
|
119
|
+
? {
|
|
120
|
+
layout: 'single',
|
|
121
|
+
// Same rule as the engine pool identity opens (`engineDbFromEnv`): POOL_MAX_ENGINE, else
|
|
122
|
+
// today's APP_DB_MAX_CONNECTIONS fallback, else 10. The budget must count the pool that
|
|
123
|
+
// really opens, so an unset POOL_MAX_ENGINE with a large APP_DB_MAX_CONNECTIONS fails the
|
|
124
|
+
// budget instead of under-counting it (spec AC-12).
|
|
125
|
+
// The numbers are parsed exactly as identity's pools parse them (strict: a set but
|
|
126
|
+
// invalid value throws naming the variable, never a lenient fallback).
|
|
127
|
+
engineMax: strictOptionalPoolInt(env.POOL_MAX_ENGINE, 'POOL_MAX_ENGINE') ??
|
|
128
|
+
strictOptionalPoolInt(env.APP_DB_MAX_CONNECTIONS, 'APP_DB_MAX_CONNECTIONS') ??
|
|
129
|
+
10,
|
|
130
|
+
authzMax: strictOptionalPoolInt(env.APP_AUTHZ_POOL_MAX, 'APP_AUTHZ_POOL_MAX') ?? 4,
|
|
131
|
+
...(maxUser !== undefined ? { maxUserConnections: maxUser } : {}),
|
|
132
|
+
}
|
|
133
|
+
: {}),
|
|
99
134
|
workers: parsePositiveInt(env.POOL_WORKERS, exports.DEFAULT_POOL_BUDGET.workers),
|
|
100
135
|
maxConnections: parsePositiveInt(env.APP_DB_MAX_CONNECTIONS, exports.DEFAULT_POOL_BUDGET.maxConnections),
|
|
101
136
|
headroom: parsePositiveInt(env.POOL_HEADROOM, exports.DEFAULT_POOL_BUDGET.headroom),
|
|
@@ -166,6 +201,10 @@ function parseOptionalPositiveInt(raw) {
|
|
|
166
201
|
*/
|
|
167
202
|
function loadDbConfig(env = process.env, schemas = (0, core_1.resolveFrameworkSchemas)(env)) {
|
|
168
203
|
const database = parseString(env.APP_DB_NAME, schemas.identity);
|
|
204
|
+
// Single layout (spec §5.C): no `<ns>_core` account exists to default to; fail closed.
|
|
205
|
+
if (schemas.layout === 'single' && parseString(env.APP_DB_USER, '') === '') {
|
|
206
|
+
throw new core_1.FrameworkSchemaConfigError('APP_DB_USER is required when SHIELD_DB_LAYOUT=single (there is no default runtime account).');
|
|
207
|
+
}
|
|
169
208
|
const allowed = [schemas.app, schemas.identity, schemas.authz, schemas.audit];
|
|
170
209
|
if (!allowed.includes(database)) {
|
|
171
210
|
throw new core_1.FrameworkSchemaConfigError(`APP_DB_NAME must be one of the framework databases for namespace ` +
|
|
@@ -174,7 +213,9 @@ function loadDbConfig(env = process.env, schemas = (0, core_1.resolveFrameworkSc
|
|
|
174
213
|
return {
|
|
175
214
|
host: parseString(env.APP_DB_HOST, exports.DEFAULT_DB_CONFIG.host),
|
|
176
215
|
port: parsePort(env.APP_DB_PORT, exports.DEFAULT_DB_CONFIG.port),
|
|
177
|
-
user:
|
|
216
|
+
user: schemas.layout === 'single'
|
|
217
|
+
? parseString(env.APP_DB_USER, '')
|
|
218
|
+
: parseString(env.APP_DB_USER, (0, core_1.frameworkAccountName)(schemas.namespace, 'core')),
|
|
178
219
|
password: parseString(env.APP_DB_PASSWORD, ''),
|
|
179
220
|
database,
|
|
180
221
|
tls: loadDbTlsConfig(env),
|
|
@@ -437,18 +478,25 @@ function parseStorageMaxBytes(raw) {
|
|
|
437
478
|
* react (the boot assertion throws; tests inspect the fields).
|
|
438
479
|
*/
|
|
439
480
|
function evaluatePoolBudget(cfg) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
cfg.perAccountMax.
|
|
481
|
+
// Single layout (AC-12): one account opens core + engine + authz; app_crud/schema_svc are never opened.
|
|
482
|
+
const perWorkerConnections = cfg.layout === 'single'
|
|
483
|
+
? cfg.perAccountMax.shield_core + (cfg.engineMax ?? 10) + (cfg.authzMax ?? 4)
|
|
484
|
+
: cfg.perAccountMax.shield_core +
|
|
485
|
+
cfg.perAccountMax.shield_app_crud +
|
|
486
|
+
cfg.perAccountMax.shield_schema_svc;
|
|
443
487
|
const runtimeConnections = cfg.workers * perWorkerConnections;
|
|
444
488
|
const requiredConnections = runtimeConnections + cfg.headroom;
|
|
445
489
|
const slack = cfg.maxConnections - requiredConnections;
|
|
490
|
+
const withinUserBudget = cfg.layout !== 'single' ||
|
|
491
|
+
cfg.maxUserConnections === undefined ||
|
|
492
|
+
requiredConnections <= cfg.maxUserConnections;
|
|
446
493
|
return {
|
|
447
494
|
perWorkerConnections,
|
|
448
495
|
runtimeConnections,
|
|
449
496
|
requiredConnections,
|
|
450
497
|
slack,
|
|
451
|
-
withinBudget: requiredConnections <= cfg.maxConnections,
|
|
452
|
-
schemaSvcWithinHardCap: cfg.perAccountMax.shield_schema_svc <= exports.SCHEMA_SVC_POOL_HARD_CAP,
|
|
498
|
+
withinBudget: requiredConnections <= cfg.maxConnections && withinUserBudget,
|
|
499
|
+
schemaSvcWithinHardCap: cfg.layout === 'single' || cfg.perAccountMax.shield_schema_svc <= exports.SCHEMA_SVC_POOL_HARD_CAP,
|
|
500
|
+
withinUserBudget,
|
|
453
501
|
};
|
|
454
502
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Principal ids the framework reserves for its own trusted operations (Shield #9).
|
|
3
|
+
*
|
|
4
|
+
* The org-bootstrap operation principal carries NO credential; its authority exists only through
|
|
5
|
+
* the runner's operation binding. It must never be a platform root, so `rootIdsFromEnv` refuses it.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ORG_BOOTSTRAP_PRINCIPAL_ID = "5b0d7e1a-0c4f-4a6e-9d3b-b007572a9001";
|
|
8
|
+
/**
|
|
9
|
+
* Refuse a root allowlist that names a reserved principal, however the app built it (`rootIdsFromEnv`,
|
|
10
|
+
* its own variable, or a literal). Called by `ShieldModule.forRoot` and `IdentityFeatureModule.forRoot`.
|
|
11
|
+
* Compared case-insensitively; the message never names the value.
|
|
12
|
+
*/
|
|
13
|
+
export declare function assertNoReservedRootId(rootIds: readonly string[] | null | undefined): void;
|
|
14
|
+
//# sourceMappingURL=reserved-principal-ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reserved-principal-ids.d.ts","sourceRoot":"","sources":["../../src/config/reserved-principal-ids.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,yCAAyC,CAAC;AAEjF;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAM1F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ORG_BOOTSTRAP_PRINCIPAL_ID = void 0;
|
|
4
|
+
exports.assertNoReservedRootId = assertNoReservedRootId;
|
|
5
|
+
/**
|
|
6
|
+
* Principal ids the framework reserves for its own trusted operations (Shield #9).
|
|
7
|
+
*
|
|
8
|
+
* The org-bootstrap operation principal carries NO credential; its authority exists only through
|
|
9
|
+
* the runner's operation binding. It must never be a platform root, so `rootIdsFromEnv` refuses it.
|
|
10
|
+
*/
|
|
11
|
+
exports.ORG_BOOTSTRAP_PRINCIPAL_ID = '5b0d7e1a-0c4f-4a6e-9d3b-b007572a9001';
|
|
12
|
+
/**
|
|
13
|
+
* Refuse a root allowlist that names a reserved principal, however the app built it (`rootIdsFromEnv`,
|
|
14
|
+
* its own variable, or a literal). Called by `ShieldModule.forRoot` and `IdentityFeatureModule.forRoot`.
|
|
15
|
+
* Compared case-insensitively; the message never names the value.
|
|
16
|
+
*/
|
|
17
|
+
function assertNoReservedRootId(rootIds) {
|
|
18
|
+
if (!rootIds)
|
|
19
|
+
return;
|
|
20
|
+
const reserved = exports.ORG_BOOTSTRAP_PRINCIPAL_ID.toLowerCase();
|
|
21
|
+
if (rootIds.some((id) => typeof id === 'string' && id.trim().toLowerCase() === reserved)) {
|
|
22
|
+
throw new Error('rootIds contains a reserved principal id; a reserved id is never a root.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-ids-from-env.d.ts","sourceRoot":"","sources":["../../src/config/root-ids-from-env.ts"],"names":[],"mappings":"AAcA,wBAAgB,cAAc,CAC5B,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAe,GAC9D,MAAM,EAAE,CAyBV"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rootIdsFromEnv = rootIdsFromEnv;
|
|
4
|
+
/**
|
|
5
|
+
* G-009 R9: the platform-root ids from `SHIELD_ROOT_IDS` (comma-separated uuids), for both
|
|
6
|
+
* `ShieldModule.forRoot({ rootIds })` and `IdentityFeatureModule.forRoot({ rootIds })`.
|
|
7
|
+
*
|
|
8
|
+
* Fail closed: every entry must already be the lower-case canonical uuid (`canonicalUuid(v) === v`),
|
|
9
|
+
* so a root id is spelled exactly as the database stores it; an empty, malformed or duplicated entry
|
|
10
|
+
* throws instead of being skipped. Errors name the variable and the entry position, never a value.
|
|
11
|
+
* Unset or blank ⇒ no roots. Root is never inferred from `u_is_platform_superuser`.
|
|
12
|
+
*/
|
|
13
|
+
const engine_1 = require("@astram/engine");
|
|
14
|
+
const reserved_principal_ids_js_1 = require("./reserved-principal-ids.js");
|
|
15
|
+
const VARIABLE = 'SHIELD_ROOT_IDS';
|
|
16
|
+
function rootIdsFromEnv(env = process.env) {
|
|
17
|
+
const raw = env[VARIABLE];
|
|
18
|
+
if (raw === undefined || raw.trim() === '')
|
|
19
|
+
return [];
|
|
20
|
+
const ids = [];
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
raw.split(',').forEach((part, index) => {
|
|
23
|
+
const value = part.trim();
|
|
24
|
+
const position = index + 1;
|
|
25
|
+
if (value === '') {
|
|
26
|
+
throw new Error(`${VARIABLE}: entry ${position} is empty.`);
|
|
27
|
+
}
|
|
28
|
+
if ((0, engine_1.canonicalUuid)(value) !== value) {
|
|
29
|
+
throw new Error(`${VARIABLE}: entry ${position} is not a lower-case canonical uuid.`);
|
|
30
|
+
}
|
|
31
|
+
if (value === reserved_principal_ids_js_1.ORG_BOOTSTRAP_PRINCIPAL_ID) {
|
|
32
|
+
throw new Error(`${VARIABLE}: entry ${position} is a reserved principal id.`);
|
|
33
|
+
}
|
|
34
|
+
if (seen.has(value)) {
|
|
35
|
+
throw new Error(`${VARIABLE}: entry ${position} repeats an earlier entry.`);
|
|
36
|
+
}
|
|
37
|
+
seen.add(value);
|
|
38
|
+
ids.push(value);
|
|
39
|
+
});
|
|
40
|
+
return ids;
|
|
41
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,9 @@ export { MariaDbApiKeyStore } from './authentication/mariadb-api-key-store.js';
|
|
|
32
32
|
export { SHIELD_SESSION_FACTS_ALS, NO_SESSION_FACTS, type SessionFactsStore, } from './request-auth/session-facts-als.js';
|
|
33
33
|
export { GlobalEnforcementGuard } from './enforcement/global-enforcement.guard.js';
|
|
34
34
|
export { stubDecisionAuthority, type Effect, type CoreDecisionAuthority, type CoreDecision, type Principal, } from './enforcement/core-seam.js';
|
|
35
|
-
export { Argon2idPasswordHasher, argon2idHasherProvider, SHIELD_PASSWORD_HASHER, type Argon2idParams, } from './authentication/argon2id-password-hasher.js';
|
|
35
|
+
export { Argon2idPasswordHasher, argon2idHasherProvider, SHIELD_PASSWORD_HASHER, lockedPasswordHash, isLockedHash, type Argon2idParams, } from './authentication/argon2id-password-hasher.js';
|
|
36
|
+
export { CredentialExistsError, type CredentialProvisioner, type CredentialProvisionInput, type PasswordCredentialProvisionInput, type CredentialState, type CredentialQuery, } from './authentication/credential-provisioner.js';
|
|
37
|
+
export { resolveAuthLinkBaseUrls } from './auth/link-base-url.js';
|
|
36
38
|
export { LocalKekCrypto } from './encryption/local-kek-crypto.js';
|
|
37
39
|
export { EncryptedFileSecretsProvider } from './secrets/encrypted-file-secrets.js';
|
|
38
40
|
export { resolveBootSecrets, createSecretsProviderFromEnv, assertNoUnresolvedSecretRefs, wasResolvedFromSecretRef, type ResolveBootSecretsOptions, } from './secrets/boot-secrets.js';
|
|
@@ -67,6 +69,8 @@ export { APP_CONFIG } from './config/config.module.js';
|
|
|
67
69
|
export { FRAMEWORK_SCHEMAS } from './config/config.module.js';
|
|
68
70
|
export type { FrameworkSchemaNames } from './config/framework-schema-names.js';
|
|
69
71
|
export { frameworkSchemasOf, loadAppConfig, loadPoolBudget, evaluatePoolBudget, DEFAULT_POOL_BUDGET, SCHEMA_SVC_POOL_HARD_CAP, type AppConfig, type PoolBudgetConfig, type PoolBudgetEvaluation, type RuntimeAccountPools, loadDbConfig, loadRedisConfig, loadSessionLifetimes, loadRefreshHmacKey, assertRefreshHmacKey, DEFAULT_DB_CONFIG, DEFAULT_REDIS_URL, type DbConfig, type RedisConfig, type SessionLifetimeOverrides, loadLoginIpLimit, loadRefreshReplayWindowMs, DEFAULT_LOGIN_IP_LIMIT, isDevOrTestNodeEnv, type LoginIpLimitConfig, loadDbTlsConfig, type DbTlsConfig, } from './config/app-config.js';
|
|
72
|
+
export { rootIdsFromEnv } from './config/root-ids-from-env.js';
|
|
73
|
+
export { ORG_BOOTSTRAP_PRINCIPAL_ID, assertNoReservedRootId, } from './config/reserved-principal-ids.js';
|
|
70
74
|
export { mariadbSslOption } from './auth/connections.js';
|
|
71
75
|
export { SHIELD_REDIS, SHIELD_CREDENTIAL_RESOLVER, SHIELD_PROFILE_RESOLVER, SHIELD_AUTHENTICATOR, SHIELD_SESSION_AUTHORITY, SHIELD_CREDENTIAL_WRITER, SHIELD_RESET_TOKEN_STORE, SHIELD_EMAIL_CHANGE_TOKEN_STORE, SHIELD_EMAIL_SENDER, SHIELD_RECOVERY_CODE_STORE, SHIELD_PROFILE_WRITER, SHIELD_SESSION_SWEEPER, SHIELD_AUDIT_SINK, SHIELD_MFA_DEVICE_STORE, SHIELD_TOTP_VERIFIER_OVERRIDE, } from './auth/tokens.js';
|
|
72
76
|
export { MariaDbCredentialResolver } from './authentication/mariadb-credential-resolver.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AASzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,0BAA0B,EAC1B,oBAAoB,EACpB,uBAAuB,EACvB,UAAU,EACV,SAAS,GACV,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,YAAY,EACZ,yBAAyB,EAGzB,2BAA2B,EAC3B,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,EAI7B,KAAK,yBAAyB,GAC/B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,MAAM,EACN,UAAU,EACV,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,2CAA2C,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAKhF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAKpG,OAAO,EAAE,yBAAyB,EAAE,MAAM,+CAA+C,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AACjG,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,eAAe,GAChB,MAAM,uCAAuC,CAAC;AAM/C,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,YAAY,EACV,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,QAAQ,GACT,MAAM,qCAAqC,CAAC;AAG7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAS/E,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,iBAAiB,GACvB,MAAM,qCAAqC,CAAC;AAU7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AAKnF,OAAO,EACL,qBAAqB,EACrB,KAAK,MAAM,EACX,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,4BAA4B,CAAC;AAMpC,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,cAAc,GACpB,MAAM,8CAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|