@astram/guard 0.20.2 → 0.22.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/audit/audit-chain-sealer.d.ts +1 -1
- package/dist/audit/audit-chain-sealer.d.ts.map +1 -1
- package/dist/audit/audit-sealer.service.d.ts +9 -2
- package/dist/audit/audit-sealer.service.d.ts.map +1 -1
- package/dist/audit/audit-sealer.service.js +20 -3
- package/dist/auth/connections.d.ts +66 -8
- package/dist/auth/connections.d.ts.map +1 -1
- package/dist/auth/connections.js +100 -18
- package/dist/auth/persistence.module.d.ts +11 -6
- package/dist/auth/persistence.module.d.ts.map +1 -1
- package/dist/auth/persistence.module.js +34 -11
- package/dist/auth/tokens.d.ts +6 -0
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +8 -2
- package/dist/authentication/mariadb-email-address-changer.d.ts +2 -1
- package/dist/authentication/mariadb-email-address-changer.d.ts.map +1 -1
- package/dist/authentication/mariadb-email-address-changer.js +3 -3
- package/dist/authz/operation-marker-check.d.ts +6 -0
- package/dist/authz/operation-marker-check.d.ts.map +1 -1
- package/dist/authz/operation-marker-check.js +18 -10
- package/dist/boot/boot-sequence.d.ts +7 -2
- package/dist/boot/boot-sequence.d.ts.map +1 -1
- package/dist/boot/boot-sequence.js +21 -4
- package/dist/config/app-config.d.ts +45 -3
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +52 -3
- package/dist/identity/module-registry.bridge.d.ts.map +1 -1
- package/dist/identity/module-registry.bridge.js +10 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/internal.d.ts +6 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +22 -15
- package/package.json +5 -5
|
@@ -16,7 +16,7 @@ export declare const AUDIT_SEAL_STUCK = 200;
|
|
|
16
16
|
/** The logger surface the sealer uses (Nest `Logger` shape). */
|
|
17
17
|
export type AuditSealerLogger = Pick<LoggerService, 'log' | 'warn' | 'error'>;
|
|
18
18
|
export interface AuditChainSealerOptions {
|
|
19
|
-
/** `
|
|
19
|
+
/** `SHIELD_AUDIT_SEALER_POOL` (G-026). Only `getConnection()` is used: every statement runs on the lock's session. */
|
|
20
20
|
readonly pool: Pool;
|
|
21
21
|
/** Resolved framework database names (`FRAMEWORK_SCHEMAS`). */
|
|
22
22
|
readonly schemas: FrameworkSchemas;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-chain-sealer.d.ts","sourceRoot":"","sources":["../../src/audit/audit-chain-sealer.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,SAAS,CAAC;AACpD,OAAO,EASL,KAAK,KAAK,EACV,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,QAAc,CAAC;AAClD,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAC/C,iDAAiD;AACjD,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAOpC,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACtC,
|
|
1
|
+
{"version":3,"file":"audit-chain-sealer.d.ts","sourceRoot":"","sources":["../../src/audit/audit-chain-sealer.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,SAAS,CAAC;AACpD,OAAO,EASL,KAAK,KAAK,EACV,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,oDAAoD;AACpD,eAAO,MAAM,sBAAsB,QAAc,CAAC;AAClD,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,QAAc,CAAC;AAC/C,iDAAiD;AACjD,eAAO,MAAM,eAAe,OAAO,CAAC;AACpC,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,sDAAsD;AACtD,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAOpC,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACtC,sHAAsH;IACtH,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC;AAyDD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CASxD;AAUD,kGAAkG;AAClG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAAG,MAAM,CAOxF;AAOD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IAEzC,8FAA8F;IAC9F,OAAO,CAAC,aAAa,CAAqB;IAC1C,2DAA2D;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqB;IAC3C,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,oDAAoD;IACpD,OAAO,CAAC,cAAc,CAAS;IAC/B;;;OAGG;IACH,OAAO,CAAC,MAAM,CAA6B;gBAE/B,OAAO,EAAE,uBAAuB;IAa5C;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD3B,OAAO,CAAC,gBAAgB;YAQV,QAAQ;IAwCtB,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IAWxB,2FAA2F;YAC7E,eAAe;IAa7B;;;;;OAKG;YACW,cAAc;IA0H5B,wFAAwF;YAC1E,SAAS;IAyDvB,2FAA2F;YAC7E,UAAU;IAmExB,2FAA2F;IAC3F,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,OAAO;CAMhB"}
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
* probe repeats every sweep period (1 h) and starts or stops the ticks. Never throws.
|
|
14
14
|
* - Ticks never overlap: the next one is scheduled only after the previous settled. A
|
|
15
15
|
* rejected tick is logged by error class only and the loop continues.
|
|
16
|
-
* -
|
|
17
|
-
* `
|
|
16
|
+
* - Pool (G-026 spec 5.D, 5.E): the service owns its pool, the one-connection
|
|
17
|
+
* `SHIELD_AUDIT_SEALER_POOL` (never `SHIELD_DB_POOL`), and ends it in `stop()`.
|
|
18
|
+
* - Shutdown (`onModuleDestroy`, Nest `close()` step 1, while the HTTP server still serves, and
|
|
19
|
+
* `onApplicationShutdown`): stops the timers, waits for the in-flight probe and tick, then ends
|
|
20
|
+
* its own pool exactly once (best-effort, never throws). Idempotent: both hooks share one stop.
|
|
21
|
+
* `SHIELD_DB_POOL` is closed later by `ConnectionLifecycle` (`onApplicationShutdown`).
|
|
18
22
|
* - Every timer is `unref()`'d: the sealer never keeps a process alive. Nothing here touches
|
|
19
23
|
* the request path (Shield #12).
|
|
20
24
|
*/
|
|
@@ -46,11 +50,14 @@ export declare class AuditSealerService implements OnApplicationBootstrap, OnMod
|
|
|
46
50
|
private probeTimer;
|
|
47
51
|
private inFlightTick;
|
|
48
52
|
private inFlightProbe;
|
|
53
|
+
private stopping;
|
|
49
54
|
constructor(pool: Pool, schemas: FrameworkSchemas, options?: AuditSealerServiceOptions);
|
|
50
55
|
onApplicationBootstrap(): Promise<void>;
|
|
51
56
|
onModuleDestroy(): Promise<void>;
|
|
52
57
|
onApplicationShutdown(): Promise<void>;
|
|
58
|
+
/** One stop for both hooks: the pool is ended once, after the last probe and tick settled. */
|
|
53
59
|
private stop;
|
|
60
|
+
private drainAndEnd;
|
|
54
61
|
/** Probe once (never overlapping, never throwing) and start or stop the ticks. */
|
|
55
62
|
private runProbe;
|
|
56
63
|
private probe;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-sealer.service.d.ts","sourceRoot":"","sources":["../../src/audit/audit-sealer.service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"audit-sealer.service.d.ts","sourceRoot":"","sources":["../../src/audit/audit-sealer.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,WAAW,yBAAyB;IACxC,kDAAkD;IAClD,QAAQ,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC;IAClC,sCAAsC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IACpC,gGAAgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;CAChD;AAID,qBACa,kBACX,YAAW,sBAAsB,EAAE,eAAe,EAAE,qBAAqB;IAgBvE,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAf1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IAEtD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAA4B;gBAGzB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,gBAAgB,EAC1C,OAAO,GAAE,yBAA8B;IAenC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQvC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAIhC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5C,8FAA8F;IAC9F,OAAO,CAAC,IAAI;YAKE,WAAW;IAiBzB,kFAAkF;IAClF,OAAO,CAAC,QAAQ;YAwBF,KAAK;IA4BnB,4FAA4F;IAC5F,OAAO,CAAC,MAAM;IAsBd,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,OAAO;CAehB"}
|
|
@@ -25,8 +25,12 @@ exports.AuditSealerService = void 0;
|
|
|
25
25
|
* probe repeats every sweep period (1 h) and starts or stops the ticks. Never throws.
|
|
26
26
|
* - Ticks never overlap: the next one is scheduled only after the previous settled. A
|
|
27
27
|
* rejected tick is logged by error class only and the loop continues.
|
|
28
|
-
* -
|
|
29
|
-
* `
|
|
28
|
+
* - Pool (G-026 spec 5.D, 5.E): the service owns its pool, the one-connection
|
|
29
|
+
* `SHIELD_AUDIT_SEALER_POOL` (never `SHIELD_DB_POOL`), and ends it in `stop()`.
|
|
30
|
+
* - Shutdown (`onModuleDestroy`, Nest `close()` step 1, while the HTTP server still serves, and
|
|
31
|
+
* `onApplicationShutdown`): stops the timers, waits for the in-flight probe and tick, then ends
|
|
32
|
+
* its own pool exactly once (best-effort, never throws). Idempotent: both hooks share one stop.
|
|
33
|
+
* `SHIELD_DB_POOL` is closed later by `ConnectionLifecycle` (`onApplicationShutdown`).
|
|
30
34
|
* - Every timer is `unref()`'d: the sealer never keeps a process alive. Nothing here touches
|
|
31
35
|
* the request path (Shield #12).
|
|
32
36
|
*/
|
|
@@ -49,6 +53,7 @@ let AuditSealerService = class AuditSealerService {
|
|
|
49
53
|
probeTimer;
|
|
50
54
|
inFlightTick;
|
|
51
55
|
inFlightProbe;
|
|
56
|
+
stopping;
|
|
52
57
|
constructor(pool, schemas, options = {}) {
|
|
53
58
|
this.pool = pool;
|
|
54
59
|
this.schemas = schemas;
|
|
@@ -79,7 +84,12 @@ let AuditSealerService = class AuditSealerService {
|
|
|
79
84
|
async onApplicationShutdown() {
|
|
80
85
|
await this.stop();
|
|
81
86
|
}
|
|
82
|
-
|
|
87
|
+
/** One stop for both hooks: the pool is ended once, after the last probe and tick settled. */
|
|
88
|
+
stop() {
|
|
89
|
+
this.stopping ??= this.drainAndEnd();
|
|
90
|
+
return this.stopping;
|
|
91
|
+
}
|
|
92
|
+
async drainAndEnd() {
|
|
83
93
|
this.stopped = true;
|
|
84
94
|
this.active = false;
|
|
85
95
|
if (this.tickTimer !== undefined)
|
|
@@ -88,8 +98,15 @@ let AuditSealerService = class AuditSealerService {
|
|
|
88
98
|
clearInterval(this.probeTimer);
|
|
89
99
|
this.tickTimer = undefined;
|
|
90
100
|
this.probeTimer = undefined;
|
|
101
|
+
// Neither rejects (both catch internally); no new probe or tick starts once `stopped` is set.
|
|
91
102
|
await this.inFlightProbe;
|
|
92
103
|
await this.inFlightTick;
|
|
104
|
+
try {
|
|
105
|
+
await this.pool.end();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// best-effort — a pool already ending (or a stub without end()) never fails shutdown.
|
|
109
|
+
}
|
|
93
110
|
}
|
|
94
111
|
/** Probe once (never overlapping, never throwing) and start or stop the ticks. */
|
|
95
112
|
runProbe() {
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* credentials, hot state) flows through the two connections created here.
|
|
6
6
|
*
|
|
7
7
|
* WHY a separate helper (not inline in the provider factory): a Nest `useFactory`
|
|
8
|
-
* value provider is NOT itself lifecycle-aware —
|
|
8
|
+
* value provider is NOT itself lifecycle-aware — lifecycle hooks only fire on
|
|
9
9
|
* PROVIDER CLASSES with that method. So the pool/redis are created by the pure
|
|
10
10
|
* factories below, and a tiny {@link ConnectionLifecycle} provider (a class Nest
|
|
11
|
-
* WILL call `
|
|
11
|
+
* WILL call `onApplicationShutdown` on) holds their handles and closes them on
|
|
12
|
+
* shutdown, after the HTTP server has closed (G-026 spec 5.O). The audit sealer's
|
|
13
|
+
* own pool ({@link createAuditSealerPool}) is ended by the sealer itself.
|
|
12
14
|
* This keeps `SHIELD_DB_POOL` / `SHIELD_REDIS` as the raw driver objects the
|
|
13
15
|
* adapters expect (`new MariaDbSessionRecordStore(pool)` etc.) while still getting
|
|
14
16
|
* graceful teardown.
|
|
@@ -21,11 +23,46 @@
|
|
|
21
23
|
* ADR-0018: the pool cap is taken from the connection-pool budget
|
|
22
24
|
* (`config.pool.perAccountMax.shield_core`), NOT a duplicate knob.
|
|
23
25
|
*/
|
|
24
|
-
import { type
|
|
25
|
-
import { type Pool } from 'mariadb';
|
|
26
|
+
import { type OnApplicationShutdown } from '@nestjs/common';
|
|
27
|
+
import { type Pool, type PoolConfig } from 'mariadb';
|
|
26
28
|
import Redis from 'ioredis';
|
|
27
|
-
import { type AppConfig, type DbTlsConfig } from '../config/app-config.js';
|
|
29
|
+
import { type AppConfig, type DbTlsConfig, type PoolTuning } from '../config/app-config.js';
|
|
28
30
|
import { isLoopbackHost } from '../config/loopback-host.js';
|
|
31
|
+
/** The `mariadb` driver pool options {@link mariadbPoolTuningOptions} returns (G-026 spec 5.B). */
|
|
32
|
+
export interface MariadbPoolTuningOptions {
|
|
33
|
+
/** Idle connections kept (capped at the pool's `connectionLimit` when one is given). */
|
|
34
|
+
readonly minimumIdle: number;
|
|
35
|
+
/** Seconds (the driver's unit). */
|
|
36
|
+
readonly idleTimeout: number;
|
|
37
|
+
/** Ms; session `max_statement_time`, set once per connection. Omitted when off. */
|
|
38
|
+
readonly queryTimeout?: number;
|
|
39
|
+
/** Ms; the driver logs a connection held longer. Omitted when off. */
|
|
40
|
+
readonly leakDetectionTimeout?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface MariadbPoolTuningOptionsOpts {
|
|
43
|
+
/** The pool's cap: `minimumIdle` is capped at it (the driver applies the same `Math.min`). */
|
|
44
|
+
readonly connectionLimit?: number;
|
|
45
|
+
/** Default true. `false` omits `queryTimeout` (the audit sealer pool, spec 5.D). */
|
|
46
|
+
readonly queryTimeout?: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Map {@link PoolTuning} to `mariadb` pool options (G-026 spec 5.B). Every framework pool spreads
|
|
50
|
+
* it (guard core, the audit sealer, identity's engine and authz pools), the same way they spread
|
|
51
|
+
* {@link mariadbSslOption}. Options that are off are omitted, so the driver default stays.
|
|
52
|
+
*
|
|
53
|
+
* Never sets `resetAfterUse`: a reset on release would drop the session `max_statement_time`
|
|
54
|
+
* the query timeout relies on (the driver default is off, and the framework sets it nowhere).
|
|
55
|
+
*/
|
|
56
|
+
export declare function mariadbPoolTuningOptions(tuning: PoolTuning, opts?: MariadbPoolTuningOptionsOpts): MariadbPoolTuningOptions;
|
|
57
|
+
/**
|
|
58
|
+
* Run one fixed framework statement with no statement time limit (G-026 spec 5.L): returns
|
|
59
|
+
* `SET STATEMENT max_statement_time=0 FOR <sql>`, so `APP_DB_QUERY_TIMEOUT_MS` never cuts a
|
|
60
|
+
* framework `GET_LOCK` wait (each keeps its own lock timeout).
|
|
61
|
+
*
|
|
62
|
+
* ⛔ For fixed framework SQL literals only, never for SQL built from input. Values still travel
|
|
63
|
+
* as `?` parameters of the wrapped statement.
|
|
64
|
+
*/
|
|
65
|
+
export declare function withoutStatementTimeout(sql: string): string;
|
|
29
66
|
/**
|
|
30
67
|
* Create the ONE live `mariadb` {@link Pool} for the `shield_core` account from
|
|
31
68
|
* {@link AppConfig}. The `connectionLimit` comes from the ADR-0018 budget
|
|
@@ -36,8 +73,24 @@ import { isLoopbackHost } from '../config/loopback-host.js';
|
|
|
36
73
|
* a wrong/blank password fails LOUD at connect time, which is the correct place
|
|
37
74
|
* for a runtime credential error. The host/port/user/database always have
|
|
38
75
|
* documented defaults, so the descriptor is always well-formed.
|
|
76
|
+
*
|
|
77
|
+
* G-026 (spec 5.C): idle and timeout settings from `config.poolTuning`
|
|
78
|
+
* ({@link mariadbPoolTuningOptions}).
|
|
39
79
|
*/
|
|
40
80
|
export declare function createDbPool(config: AppConfig): Pool;
|
|
81
|
+
/** The options {@link createDbPool} passes to `mariadb.createPool` (exported for tests). */
|
|
82
|
+
export declare function dbPoolOptions(config: AppConfig): PoolConfig;
|
|
83
|
+
/**
|
|
84
|
+
* The audit sealer's own pool (G-026 spec 5.D): same account, database, TLS and idle settings as
|
|
85
|
+
* {@link createDbPool}, capped at one connection (the sealer holds one per tick). A sealer that
|
|
86
|
+
* borrowed from the core pool every interval would keep the core pool's FIFO idle queue from ever
|
|
87
|
+
* reaching the idle timeout. No `queryTimeout`: `APP_DB_QUERY_TIMEOUT_MS` never cuts a sealer
|
|
88
|
+
* statement (Shield #6). Leak detection applies (it only logs). Internal: provided under
|
|
89
|
+
* `SHIELD_AUDIT_SEALER_POOL` and ended by `AuditSealerService.stop()`.
|
|
90
|
+
*/
|
|
91
|
+
export declare function createAuditSealerPool(config: AppConfig): Pool;
|
|
92
|
+
/** The options {@link createAuditSealerPool} passes to `mariadb.createPool` (exported for tests). */
|
|
93
|
+
export declare function auditSealerPoolOptions(config: AppConfig): PoolConfig;
|
|
41
94
|
export { isLoopbackHost };
|
|
42
95
|
/**
|
|
43
96
|
* The boot warning for a production pool to a remote MariaDB without TLS (Phase 16), or
|
|
@@ -66,14 +119,19 @@ export declare function createRedisClient(config: AppConfig): Redis;
|
|
|
66
119
|
/**
|
|
67
120
|
* Lifecycle-aware holder that owns the pool + redis handles and closes them on
|
|
68
121
|
* app shutdown. Provided ALONGSIDE the two raw-connection providers so Nest calls
|
|
69
|
-
* `
|
|
122
|
+
* `onApplicationShutdown` here (value providers get no lifecycle callback). The teardown
|
|
70
123
|
* is best-effort + idempotent: a close error is swallowed so shutdown never hangs
|
|
71
124
|
* or throws, and both connections are always attempted.
|
|
125
|
+
*
|
|
126
|
+
* G-026 spec 5.O: the close runs in `onApplicationShutdown` (Nest `close()` step 4), after every
|
|
127
|
+
* `onModuleDestroy` and after the HTTP server closed and its in-flight requests finished, so a
|
|
128
|
+
* request still being served while the jobs drain (step 1) never meets a closed pool or Redis.
|
|
129
|
+
* Nothing happens in `onModuleDestroy`.
|
|
72
130
|
*/
|
|
73
|
-
export declare class ConnectionLifecycle implements
|
|
131
|
+
export declare class ConnectionLifecycle implements OnApplicationShutdown {
|
|
74
132
|
private readonly pool;
|
|
75
133
|
private readonly redis;
|
|
76
134
|
constructor(pool: Pool, redis: Redis);
|
|
77
|
-
|
|
135
|
+
onApplicationShutdown(): Promise<void>;
|
|
78
136
|
}
|
|
79
137
|
//# sourceMappingURL=connections.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/auth/connections.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connections.d.ts","sourceRoot":"","sources":["../../src/auth/connections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAgB,EAAE,KAAK,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,mGAAmG;AACnG,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,4BAA4B;IAC3C,8FAA8F;IAC9F,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,oFAAoF;IACpF,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE,4BAAiC,GACtC,wBAAwB,CAgB1B;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D;AAmBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAIpD;AAED,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAO3D;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAE7D;AAED,qGAAqG;AACrG,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,UAAU,CAOpE;AAMD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAC7B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CASpB;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,WAAW,GAAG,SAAS,GAC3B;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAA;CAAE,GAAG,SAAS,CAIzE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,KAAK,CAgB1D;AAED;;;;;;;;;;;GAWG;AACH,qBACa,mBAAoB,YAAW,qBAAqB;IAE7D,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBADL,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK;IAGzB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7C"}
|
package/dist/auth/connections.js
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* credentials, hot state) flows through the two connections created here.
|
|
7
7
|
*
|
|
8
8
|
* WHY a separate helper (not inline in the provider factory): a Nest `useFactory`
|
|
9
|
-
* value provider is NOT itself lifecycle-aware —
|
|
9
|
+
* value provider is NOT itself lifecycle-aware — lifecycle hooks only fire on
|
|
10
10
|
* PROVIDER CLASSES with that method. So the pool/redis are created by the pure
|
|
11
11
|
* factories below, and a tiny {@link ConnectionLifecycle} provider (a class Nest
|
|
12
|
-
* WILL call `
|
|
12
|
+
* WILL call `onApplicationShutdown` on) holds their handles and closes them on
|
|
13
|
+
* shutdown, after the HTTP server has closed (G-026 spec 5.O). The audit sealer's
|
|
14
|
+
* own pool ({@link createAuditSealerPool}) is ended by the sealer itself.
|
|
13
15
|
* This keeps `SHIELD_DB_POOL` / `SHIELD_REDIS` as the raw driver objects the
|
|
14
16
|
* adapters expect (`new MariaDbSessionRecordStore(pool)` etc.) while still getting
|
|
15
17
|
* graceful teardown.
|
|
@@ -36,7 +38,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
38
|
};
|
|
37
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
40
|
exports.ConnectionLifecycle = exports.isLoopbackHost = void 0;
|
|
41
|
+
exports.mariadbPoolTuningOptions = mariadbPoolTuningOptions;
|
|
42
|
+
exports.withoutStatementTimeout = withoutStatementTimeout;
|
|
39
43
|
exports.createDbPool = createDbPool;
|
|
44
|
+
exports.dbPoolOptions = dbPoolOptions;
|
|
45
|
+
exports.createAuditSealerPool = createAuditSealerPool;
|
|
46
|
+
exports.auditSealerPoolOptions = auditSealerPoolOptions;
|
|
40
47
|
exports.dbTlsBootWarning = dbTlsBootWarning;
|
|
41
48
|
exports.mariadbSslOption = mariadbSslOption;
|
|
42
49
|
exports.createRedisClient = createRedisClient;
|
|
@@ -47,6 +54,57 @@ const ioredis_1 = __importDefault(require("ioredis"));
|
|
|
47
54
|
const app_config_js_1 = require("../config/app-config.js");
|
|
48
55
|
const loopback_host_js_1 = require("../config/loopback-host.js");
|
|
49
56
|
Object.defineProperty(exports, "isLoopbackHost", { enumerable: true, get: function () { return loopback_host_js_1.isLoopbackHost; } });
|
|
57
|
+
/**
|
|
58
|
+
* Map {@link PoolTuning} to `mariadb` pool options (G-026 spec 5.B). Every framework pool spreads
|
|
59
|
+
* it (guard core, the audit sealer, identity's engine and authz pools), the same way they spread
|
|
60
|
+
* {@link mariadbSslOption}. Options that are off are omitted, so the driver default stays.
|
|
61
|
+
*
|
|
62
|
+
* Never sets `resetAfterUse`: a reset on release would drop the session `max_statement_time`
|
|
63
|
+
* the query timeout relies on (the driver default is off, and the framework sets it nowhere).
|
|
64
|
+
*/
|
|
65
|
+
function mariadbPoolTuningOptions(tuning, opts = {}) {
|
|
66
|
+
const minimumIdle = opts.connectionLimit === undefined
|
|
67
|
+
? tuning.minimumIdle
|
|
68
|
+
: Math.min(tuning.minimumIdle, opts.connectionLimit);
|
|
69
|
+
const withQueryTimeout = opts.queryTimeout !== false;
|
|
70
|
+
return {
|
|
71
|
+
minimumIdle,
|
|
72
|
+
idleTimeout: tuning.idleTimeoutS,
|
|
73
|
+
...(withQueryTimeout && tuning.queryTimeoutMs !== undefined
|
|
74
|
+
? { queryTimeout: tuning.queryTimeoutMs }
|
|
75
|
+
: {}),
|
|
76
|
+
...(tuning.leakDetectionMs !== undefined
|
|
77
|
+
? { leakDetectionTimeout: tuning.leakDetectionMs }
|
|
78
|
+
: {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Run one fixed framework statement with no statement time limit (G-026 spec 5.L): returns
|
|
83
|
+
* `SET STATEMENT max_statement_time=0 FOR <sql>`, so `APP_DB_QUERY_TIMEOUT_MS` never cuts a
|
|
84
|
+
* framework `GET_LOCK` wait (each keeps its own lock timeout).
|
|
85
|
+
*
|
|
86
|
+
* ⛔ For fixed framework SQL literals only, never for SQL built from input. Values still travel
|
|
87
|
+
* as `?` parameters of the wrapped statement.
|
|
88
|
+
*/
|
|
89
|
+
function withoutStatementTimeout(sql) {
|
|
90
|
+
return `SET STATEMENT max_statement_time=0 FOR ${sql}`;
|
|
91
|
+
}
|
|
92
|
+
/** The account, database, connect and TLS options every guard pool shares. */
|
|
93
|
+
function basePoolOptions(config) {
|
|
94
|
+
const { db } = config;
|
|
95
|
+
return {
|
|
96
|
+
host: db.host,
|
|
97
|
+
port: db.port,
|
|
98
|
+
user: db.user,
|
|
99
|
+
password: db.password,
|
|
100
|
+
database: db.database,
|
|
101
|
+
// Fail fast on an unreachable DB rather than hanging the boot indefinitely.
|
|
102
|
+
connectTimeout: 10_000,
|
|
103
|
+
// All platform timestamps are UTC (parity with the session adapters).
|
|
104
|
+
timezone: 'Z',
|
|
105
|
+
...withSsl(mariadbSslOption(db.tls)),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
50
108
|
/**
|
|
51
109
|
* Create the ONE live `mariadb` {@link Pool} for the `shield_core` account from
|
|
52
110
|
* {@link AppConfig}. The `connectionLimit` comes from the ADR-0018 budget
|
|
@@ -57,25 +115,44 @@ Object.defineProperty(exports, "isLoopbackHost", { enumerable: true, get: functi
|
|
|
57
115
|
* a wrong/blank password fails LOUD at connect time, which is the correct place
|
|
58
116
|
* for a runtime credential error. The host/port/user/database always have
|
|
59
117
|
* documented defaults, so the descriptor is always well-formed.
|
|
118
|
+
*
|
|
119
|
+
* G-026 (spec 5.C): idle and timeout settings from `config.poolTuning`
|
|
120
|
+
* ({@link mariadbPoolTuningOptions}).
|
|
60
121
|
*/
|
|
61
122
|
function createDbPool(config) {
|
|
62
|
-
const { db, pool } = config;
|
|
63
123
|
const tlsWarning = dbTlsBootWarning(config);
|
|
64
124
|
if (tlsWarning !== undefined)
|
|
65
125
|
dbBootLogger.warn(tlsWarning);
|
|
66
|
-
return mariadb_1.default.createPool(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
126
|
+
return mariadb_1.default.createPool(dbPoolOptions(config));
|
|
127
|
+
}
|
|
128
|
+
/** The options {@link createDbPool} passes to `mariadb.createPool` (exported for tests). */
|
|
129
|
+
function dbPoolOptions(config) {
|
|
130
|
+
const connectionLimit = config.pool.perAccountMax.shield_core;
|
|
131
|
+
return {
|
|
132
|
+
...basePoolOptions(config),
|
|
133
|
+
connectionLimit,
|
|
134
|
+
...mariadbPoolTuningOptions((0, app_config_js_1.poolTuningOf)(config), { connectionLimit }),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The audit sealer's own pool (G-026 spec 5.D): same account, database, TLS and idle settings as
|
|
139
|
+
* {@link createDbPool}, capped at one connection (the sealer holds one per tick). A sealer that
|
|
140
|
+
* borrowed from the core pool every interval would keep the core pool's FIFO idle queue from ever
|
|
141
|
+
* reaching the idle timeout. No `queryTimeout`: `APP_DB_QUERY_TIMEOUT_MS` never cuts a sealer
|
|
142
|
+
* statement (Shield #6). Leak detection applies (it only logs). Internal: provided under
|
|
143
|
+
* `SHIELD_AUDIT_SEALER_POOL` and ended by `AuditSealerService.stop()`.
|
|
144
|
+
*/
|
|
145
|
+
function createAuditSealerPool(config) {
|
|
146
|
+
return mariadb_1.default.createPool(auditSealerPoolOptions(config));
|
|
147
|
+
}
|
|
148
|
+
/** The options {@link createAuditSealerPool} passes to `mariadb.createPool` (exported for tests). */
|
|
149
|
+
function auditSealerPoolOptions(config) {
|
|
150
|
+
const connectionLimit = app_config_js_1.AUDIT_SEALER_POOL_CONNECTIONS;
|
|
151
|
+
return {
|
|
152
|
+
...basePoolOptions(config),
|
|
153
|
+
connectionLimit,
|
|
154
|
+
...mariadbPoolTuningOptions((0, app_config_js_1.poolTuningOf)(config), { connectionLimit, queryTimeout: false }),
|
|
155
|
+
};
|
|
79
156
|
}
|
|
80
157
|
const dbBootLogger = new common_1.Logger('ShieldAuth');
|
|
81
158
|
/**
|
|
@@ -138,9 +215,14 @@ function createRedisClient(config) {
|
|
|
138
215
|
/**
|
|
139
216
|
* Lifecycle-aware holder that owns the pool + redis handles and closes them on
|
|
140
217
|
* app shutdown. Provided ALONGSIDE the two raw-connection providers so Nest calls
|
|
141
|
-
* `
|
|
218
|
+
* `onApplicationShutdown` here (value providers get no lifecycle callback). The teardown
|
|
142
219
|
* is best-effort + idempotent: a close error is swallowed so shutdown never hangs
|
|
143
220
|
* or throws, and both connections are always attempted.
|
|
221
|
+
*
|
|
222
|
+
* G-026 spec 5.O: the close runs in `onApplicationShutdown` (Nest `close()` step 4), after every
|
|
223
|
+
* `onModuleDestroy` and after the HTTP server closed and its in-flight requests finished, so a
|
|
224
|
+
* request still being served while the jobs drain (step 1) never meets a closed pool or Redis.
|
|
225
|
+
* Nothing happens in `onModuleDestroy`.
|
|
144
226
|
*/
|
|
145
227
|
let ConnectionLifecycle = class ConnectionLifecycle {
|
|
146
228
|
pool;
|
|
@@ -149,7 +231,7 @@ let ConnectionLifecycle = class ConnectionLifecycle {
|
|
|
149
231
|
this.pool = pool;
|
|
150
232
|
this.redis = redis;
|
|
151
233
|
}
|
|
152
|
-
async
|
|
234
|
+
async onApplicationShutdown() {
|
|
153
235
|
// Close both; never let one failure prevent the other, never throw.
|
|
154
236
|
try {
|
|
155
237
|
await this.pool.end();
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
9
9
|
* Team B (connection + composition) OWNS this file. The two factories create the
|
|
10
10
|
* real driver objects; the {@link ConnectionLifecycle} provider (a class Nest
|
|
11
|
-
* invokes `
|
|
12
|
-
* itself lifecycle-aware, so the
|
|
13
|
-
* `connections.ts`). Both connections are exported
|
|
14
|
-
* pool (credential resolver + session record store) and
|
|
11
|
+
* invokes `onApplicationShutdown` on, after the HTTP server has closed; G-026 spec 5.O)
|
|
12
|
+
* closes them on shutdown — a value provider is not itself lifecycle-aware, so the
|
|
13
|
+
* teardown lives in that holder (see `connections.ts`). Both connections are exported
|
|
14
|
+
* so AuthModule can inject the pool (credential resolver + session record store) and
|
|
15
|
+
* the redis (hot store).
|
|
16
|
+
*
|
|
17
|
+
* SHIELD_AUDIT_SEALER_POOL — {@link createAuditSealerPool}: the audit sealer's own
|
|
18
|
+
* one-connection pool (G-026 spec 5.D), NOT exported; only the sealer injects it and
|
|
19
|
+
* its `stop()` ends it.
|
|
15
20
|
*
|
|
16
21
|
* SHIELD_DB_POOL — {@link createDbPool}: `mariadb.createPool` for the
|
|
17
22
|
* `shield_core` account from `config.db`, `connectionLimit` from the ADR-0018
|
|
@@ -33,8 +38,8 @@
|
|
|
33
38
|
import { type DynamicModule } from '@nestjs/common';
|
|
34
39
|
/**
|
|
35
40
|
* The persistence module. `forRoot()` returns the two connection providers (both
|
|
36
|
-
* exported so AuthModule can inject them), the shutdown holder
|
|
37
|
-
* Opt-in: only imported when auth is wired.
|
|
41
|
+
* exported so AuthModule can inject them), the shutdown holder, the audit sealer and its
|
|
42
|
+
* own pool (not exported). Opt-in: only imported when auth is wired.
|
|
38
43
|
*/
|
|
39
44
|
export declare class PersistenceModule {
|
|
40
45
|
static forRoot(): DynamicModule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.module.d.ts","sourceRoot":"","sources":["../../src/auth/persistence.module.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"persistence.module.d.ts","sourceRoot":"","sources":["../../src/auth/persistence.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,EAAU,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AA6E3E;;;;GAIG;AACH,qBACa,iBAAiB;IAC5B,MAAM,CAAC,OAAO,IAAI,aAAa;CAahC"}
|
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
10
10
|
* Team B (connection + composition) OWNS this file. The two factories create the
|
|
11
11
|
* real driver objects; the {@link ConnectionLifecycle} provider (a class Nest
|
|
12
|
-
* invokes `
|
|
13
|
-
* itself lifecycle-aware, so the
|
|
14
|
-
* `connections.ts`). Both connections are exported
|
|
15
|
-
* pool (credential resolver + session record store) and
|
|
12
|
+
* invokes `onApplicationShutdown` on, after the HTTP server has closed; G-026 spec 5.O)
|
|
13
|
+
* closes them on shutdown — a value provider is not itself lifecycle-aware, so the
|
|
14
|
+
* teardown lives in that holder (see `connections.ts`). Both connections are exported
|
|
15
|
+
* so AuthModule can inject the pool (credential resolver + session record store) and
|
|
16
|
+
* the redis (hot store).
|
|
17
|
+
*
|
|
18
|
+
* SHIELD_AUDIT_SEALER_POOL — {@link createAuditSealerPool}: the audit sealer's own
|
|
19
|
+
* one-connection pool (G-026 spec 5.D), NOT exported; only the sealer injects it and
|
|
20
|
+
* its `stop()` ends it.
|
|
16
21
|
*
|
|
17
22
|
* SHIELD_DB_POOL — {@link createDbPool}: `mariadb.createPool` for the
|
|
18
23
|
* `shield_core` account from `config.db`, `connectionLimit` from the ADR-0018
|
|
@@ -69,37 +74,55 @@ const redisProvider = {
|
|
|
69
74
|
};
|
|
70
75
|
/**
|
|
71
76
|
* The lifecycle holder — injects the two connections and closes them on shutdown
|
|
72
|
-
* (`
|
|
73
|
-
* this class is where
|
|
77
|
+
* (`onApplicationShutdown`, after the HTTP server has closed; G-026 spec 5.O). A
|
|
78
|
+
* value-provider connection gets no lifecycle callback, so this class is where
|
|
79
|
+
* graceful teardown happens.
|
|
74
80
|
*/
|
|
75
81
|
const lifecycleProvider = {
|
|
76
82
|
provide: connections_js_1.ConnectionLifecycle,
|
|
77
83
|
useFactory: (pool, redis) => new connections_js_1.ConnectionLifecycle(pool, redis),
|
|
78
84
|
inject: [tokens_js_1.SHIELD_DB_POOL, tokens_js_1.SHIELD_REDIS],
|
|
79
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* The audit sealer's own pool (G-026 spec 5.D): one connection, no query timeout. Registered
|
|
88
|
+
* beside the sealer, so it exists exactly when the sealer does (this module, `auth: true`).
|
|
89
|
+
* Not exported. Ended by the sealer's `stop()` after its last tick settled.
|
|
90
|
+
*/
|
|
91
|
+
const auditSealerPoolProvider = {
|
|
92
|
+
provide: tokens_js_1.SHIELD_AUDIT_SEALER_POOL,
|
|
93
|
+
useFactory: (config) => (0, connections_js_1.createAuditSealerPool)(config),
|
|
94
|
+
inject: [config_module_js_1.APP_CONFIG],
|
|
95
|
+
};
|
|
80
96
|
/**
|
|
81
97
|
* The deferred audit sealer (audit-chain-store SPEC §5.H): registered here so it exists
|
|
82
98
|
* exactly when {@link SHIELD_DB_POOL} does. `AUDIT_SEAL_*` is read now, so an invalid value
|
|
83
99
|
* fails the boot (SPEC §13). It ticks only once its table probe finds `audit_log` and
|
|
84
|
-
* `audit_chain`; a stub or unreachable pool leaves it idle (never a boot failure).
|
|
100
|
+
* `audit_chain`; a stub or unreachable pool leaves it idle (never a boot failure). It runs on
|
|
101
|
+
* its own pool ({@link SHIELD_AUDIT_SEALER_POOL}), never on {@link SHIELD_DB_POOL} (G-026).
|
|
85
102
|
*/
|
|
86
103
|
const auditSealerProvider = {
|
|
87
104
|
provide: audit_sealer_service_js_1.AuditSealerService,
|
|
88
105
|
useFactory: (pool, config) => new audit_sealer_service_js_1.AuditSealerService(pool, (0, app_config_js_1.frameworkSchemasOf)(config), {
|
|
89
106
|
config: (0, audit_seal_config_js_1.loadAuditSealConfig)(process.env),
|
|
90
107
|
}),
|
|
91
|
-
inject: [tokens_js_1.
|
|
108
|
+
inject: [tokens_js_1.SHIELD_AUDIT_SEALER_POOL, config_module_js_1.APP_CONFIG],
|
|
92
109
|
};
|
|
93
110
|
/**
|
|
94
111
|
* The persistence module. `forRoot()` returns the two connection providers (both
|
|
95
|
-
* exported so AuthModule can inject them), the shutdown holder
|
|
96
|
-
* Opt-in: only imported when auth is wired.
|
|
112
|
+
* exported so AuthModule can inject them), the shutdown holder, the audit sealer and its
|
|
113
|
+
* own pool (not exported). Opt-in: only imported when auth is wired.
|
|
97
114
|
*/
|
|
98
115
|
let PersistenceModule = PersistenceModule_1 = class PersistenceModule {
|
|
99
116
|
static forRoot() {
|
|
100
117
|
return {
|
|
101
118
|
module: PersistenceModule_1,
|
|
102
|
-
providers: [
|
|
119
|
+
providers: [
|
|
120
|
+
dbPoolProvider,
|
|
121
|
+
redisProvider,
|
|
122
|
+
lifecycleProvider,
|
|
123
|
+
auditSealerPoolProvider,
|
|
124
|
+
auditSealerProvider,
|
|
125
|
+
],
|
|
103
126
|
exports: [tokens_js_1.SHIELD_DB_POOL, tokens_js_1.SHIELD_REDIS],
|
|
104
127
|
};
|
|
105
128
|
}
|
package/dist/auth/tokens.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ export { SHIELD_PASSWORD_HASHER } from '../authentication/argon2id-password-hash
|
|
|
30
30
|
* {@link SHIELD_SESSION_RECORD_STORE} factory. Disposed on app shutdown.
|
|
31
31
|
*/
|
|
32
32
|
export declare const SHIELD_DB_POOL: unique symbol;
|
|
33
|
+
/**
|
|
34
|
+
* Internal (G-026 spec 5.D): the audit sealer's own one-connection `mariadb` `Pool`, same account
|
|
35
|
+
* and database as {@link SHIELD_DB_POOL}, no query timeout. Provided by `PersistenceModule` (not
|
|
36
|
+
* exported from it); only `AuditSealerService` injects it, and its `stop()` ends it.
|
|
37
|
+
*/
|
|
38
|
+
export declare const SHIELD_AUDIT_SEALER_POOL: unique symbol;
|
|
33
39
|
/**
|
|
34
40
|
* The ONE live `ioredis` client (the hot session tier, ADR-0013). **Provided by
|
|
35
41
|
* Team B** (the persistence factory). **Consumed by** the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/tokens.js
CHANGED
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
* ADR-0003: no framework/DB/crypto imports here — just Symbols (+ one re-export).
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
27
|
-
exports.SHIELD_CAPTCHA_GATE = exports.SHIELD_CAPTCHA_VERIFIER = exports.SHIELD_CAPTCHA_CONFIG = void 0;
|
|
26
|
+
exports.SHIELD_ACCOUNT_UNLOCKER = exports.SHIELD_ADMIN_STEP_UP = exports.SHIELD_MFA_REQUIREMENT = exports.SHIELD_MFA_REQUIREMENT_SOURCE = exports.SHIELD_SIGN_IN_WITH = exports.SHIELD_SESSION_RESTRICTIONS = exports.SHIELD_PERMISSION_VERSION_CACHE = exports.SHIELD_PERMISSION_FACTS = exports.SHIELD_RESET_LIMITER = exports.SHIELD_FORGOT_HINT_LIMITER = exports.SHIELD_FORGOT_LIMITER = exports.SHIELD_LOGIN_TOTP_VERIFIER = exports.SHIELD_MFA_STEP_UP = exports.SHIELD_MFA_LIMITER = exports.SHIELD_RECOVERY_LIMITER = exports.SHIELD_LOGIN_IP_LIMITER = exports.SHIELD_LOGIN_LIMITER = exports.SHIELD_MEMBERSHIP_CHECKER = exports.SHIELD_SCOPE_SUBTREE_RESOLVER = exports.SHIELD_SESSION_FACTS_PROJECTOR = exports.SHIELD_ROOT_IDS = exports.SHIELD_TOTP_VERIFIER_OVERRIDE = exports.SHIELD_TOTP_VERIFIER = exports.SHIELD_RESET_LINK_BASE_URL = exports.SHIELD_SESSION_SWEEPER = exports.SHIELD_MFA_DEVICE_STORE = exports.SHIELD_DEFAULT_AUTH_AUDIT_SINK = exports.SHIELD_AUDIT_SINK = exports.SHIELD_PROFILE_WRITER = exports.SHIELD_RECOVERY_CODE_STORE = exports.SHIELD_EMAIL_DELIVERY = exports.SHIELD_ATTEMPT_LIMITER_FACTORY = exports.SHIELD_MESSAGE_DISPATCHER = exports.SHIELD_EMAIL_SENDER = exports.SHIELD_CONFIRM_EMAIL_LIMITER = exports.SHIELD_EMAIL_CHANGE_LIMITER = exports.SHIELD_EMAIL_CHANGE_LINK_BASE_URL = exports.SHIELD_EMAIL_ADDRESS_CHANGER = exports.SHIELD_EMAIL_CHANGE_TOKEN_STORE = exports.SHIELD_RESET_TOKEN_STORE = exports.SHIELD_CREDENTIAL_WRITER = exports.SHIELD_RECENT_ROTATION_STORE = exports.SHIELD_SESSION_AUTHORITY = exports.SHIELD_AUTHENTICATOR = exports.SHIELD_PROFILE_RESOLVER = exports.SHIELD_CREDENTIAL_RESOLVER = exports.SHIELD_REDIS = exports.SHIELD_AUDIT_SEALER_POOL = exports.SHIELD_DB_POOL = exports.SHIELD_PASSWORD_HASHER = void 0;
|
|
27
|
+
exports.SHIELD_CAPTCHA_GATE = exports.SHIELD_CAPTCHA_VERIFIER = exports.SHIELD_CAPTCHA_CONFIG = exports.SHIELD_MFA_POLICY_FEATURE = void 0;
|
|
28
28
|
var argon2id_password_hasher_js_1 = require("../authentication/argon2id-password-hasher.js");
|
|
29
29
|
Object.defineProperty(exports, "SHIELD_PASSWORD_HASHER", { enumerable: true, get: function () { return argon2id_password_hasher_js_1.SHIELD_PASSWORD_HASHER; } });
|
|
30
30
|
/**
|
|
@@ -35,6 +35,12 @@ Object.defineProperty(exports, "SHIELD_PASSWORD_HASHER", { enumerable: true, get
|
|
|
35
35
|
* {@link SHIELD_SESSION_RECORD_STORE} factory. Disposed on app shutdown.
|
|
36
36
|
*/
|
|
37
37
|
exports.SHIELD_DB_POOL = Symbol('SHIELD_DB_POOL');
|
|
38
|
+
/**
|
|
39
|
+
* Internal (G-026 spec 5.D): the audit sealer's own one-connection `mariadb` `Pool`, same account
|
|
40
|
+
* and database as {@link SHIELD_DB_POOL}, no query timeout. Provided by `PersistenceModule` (not
|
|
41
|
+
* exported from it); only `AuditSealerService` injects it, and its `stop()` ends it.
|
|
42
|
+
*/
|
|
43
|
+
exports.SHIELD_AUDIT_SEALER_POOL = Symbol('SHIELD_AUDIT_SEALER_POOL');
|
|
38
44
|
/**
|
|
39
45
|
* The ONE live `ioredis` client (the hot session tier, ADR-0013). **Provided by
|
|
40
46
|
* Team B** (the persistence factory). **Consumed by** the
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* paths share one collision rule and one login-hint rule.
|
|
7
7
|
*
|
|
8
8
|
* `change` (one MariaDB transaction, lock-then-recheck):
|
|
9
|
-
* 0. `GET_LOCK('shield_email_change')` — serialises email changes
|
|
9
|
+
* 0. `GET_LOCK('shield_email_change')` — serialises email changes (run via
|
|
10
|
+
* `withoutStatementTimeout`, so only its own 10 s timeout ends the wait). The locked collision reads
|
|
10
11
|
* below look up a value that is normally ABSENT; under REPEATABLE READ that takes a gap lock
|
|
11
12
|
* on `uq_users_email` (migration 021, KI #87) and on the `ic_identity_hint` PK. Gap locks do
|
|
12
13
|
* not conflict with each other but block the other side's insert into the gap, so two
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-email-address-changer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-email-address-changer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"mariadb-email-address-changer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-email-address-changer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,IAAI,EAAkB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,wCAAwC,CAAC;AAEhD,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAqB7C,qBAAa,0BAA2B,YAAW,mBAAmB;IASlE,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IATtB,uBAAuB;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGjB,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,MAAM,MAAiB;IAC7C,yFAAyF;IACzF,OAAO,CAAC,EAAE,oBAAoB;IAQ1B,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAejF,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA+BxD,YAAY;CAqE3B"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MariaDbEmailAddressChanger = void 0;
|
|
4
|
+
const connections_js_1 = require("../auth/connections.js");
|
|
4
5
|
const framework_schema_names_js_1 = require("../config/framework-schema-names.js");
|
|
5
6
|
/** Seconds to wait for the email-change named lock before giving up (caller → 503). */
|
|
6
7
|
const LOCK_TIMEOUT_S = 10;
|
|
@@ -48,9 +49,8 @@ class MariaDbEmailAddressChanger {
|
|
|
48
49
|
const conn = await this.pool.getConnection();
|
|
49
50
|
let locked = false;
|
|
50
51
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
]));
|
|
52
|
+
// G-026 spec 5.L: the wait ends only by its own LOCK_TIMEOUT_S, never by APP_DB_QUERY_TIMEOUT_MS.
|
|
53
|
+
const got = (await conn.query((0, connections_js_1.withoutStatementTimeout)("SELECT GET_LOCK('shield_email_change', ?) AS got"), [LOCK_TIMEOUT_S]));
|
|
54
54
|
if (Number(got[0]?.got ?? 0) !== 1)
|
|
55
55
|
throw new Error('email change lock unavailable');
|
|
56
56
|
locked = true;
|