@astram/identity 0.18.0 → 0.18.2
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/features/authz/authz-pool-lifecycle.d.ts +20 -0
- package/dist/features/authz/authz-pool-lifecycle.d.ts.map +1 -0
- package/dist/features/authz/authz-pool-lifecycle.js +52 -0
- package/dist/features/authz/catalog-bootstrap.provider.d.ts +14 -2
- package/dist/features/authz/catalog-bootstrap.provider.d.ts.map +1 -1
- package/dist/features/authz/catalog-bootstrap.provider.js +35 -8
- package/dist/features/authz/record-authorization.d.ts +2 -0
- package/dist/features/authz/record-authorization.d.ts.map +1 -1
- package/dist/features/authz/record-authorization.js +5 -1
- package/dist/features/identity-feature.module.d.ts.map +1 -1
- package/dist/features/identity-feature.module.js +8 -0
- package/dist/features/org-bootstrap/run-org-bootstrap.d.ts.map +1 -1
- package/dist/features/org-bootstrap/run-org-bootstrap.js +2 -4
- package/dist/features/scope/scope-closure.service.d.ts.map +1 -1
- package/dist/features/scope/scope-closure.service.js +2 -1
- package/dist/user.object.d.ts +1 -0
- package/dist/user.object.d.ts.map +1 -1
- package/dist/user.object.js +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type OnApplicationShutdown } from '@nestjs/common';
|
|
2
|
+
import type { Pool } from 'mariadb';
|
|
3
|
+
/**
|
|
4
|
+
* Ends the ONE shared identity authz pool on shutdown (G-026 spec 5.J). `forRoot()` provides the
|
|
5
|
+
* pool as a value (`IDENTITY_AUTHZ_POOL`), and value providers get no lifecycle callback, so this
|
|
6
|
+
* class holds it, like guard's `ConnectionLifecycle`.
|
|
7
|
+
*
|
|
8
|
+
* The close runs in `onApplicationShutdown` (Nest `close()` step 4): after every `onModuleDestroy`
|
|
9
|
+
* (the catalog reconcile has been stopped and drained by `CatalogBootstrapProvider`) and after the
|
|
10
|
+
* HTTP server closed and its in-flight requests finished. Nothing happens in `onModuleDestroy`.
|
|
11
|
+
* Best-effort and idempotent: an `end()` error is swallowed, a second call does nothing, and it
|
|
12
|
+
* never throws.
|
|
13
|
+
*/
|
|
14
|
+
export declare class IdentityAuthzPoolLifecycle implements OnApplicationShutdown {
|
|
15
|
+
private readonly pool;
|
|
16
|
+
private ended;
|
|
17
|
+
constructor(pool: Pool);
|
|
18
|
+
onApplicationShutdown(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=authz-pool-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authz-pool-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/features/authz/authz-pool-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAGpC;;;;;;;;;;GAUG;AACH,qBACa,0BAA2B,YAAW,qBAAqB;IAG7B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAF9D,OAAO,CAAC,KAAK,CAAS;gBAEoC,IAAI,EAAE,IAAI;IAE9D,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;CAS7C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.IdentityAuthzPoolLifecycle = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const org_bootstrap_tokens_js_1 = require("../org-bootstrap/org-bootstrap.tokens.js");
|
|
18
|
+
/**
|
|
19
|
+
* Ends the ONE shared identity authz pool on shutdown (G-026 spec 5.J). `forRoot()` provides the
|
|
20
|
+
* pool as a value (`IDENTITY_AUTHZ_POOL`), and value providers get no lifecycle callback, so this
|
|
21
|
+
* class holds it, like guard's `ConnectionLifecycle`.
|
|
22
|
+
*
|
|
23
|
+
* The close runs in `onApplicationShutdown` (Nest `close()` step 4): after every `onModuleDestroy`
|
|
24
|
+
* (the catalog reconcile has been stopped and drained by `CatalogBootstrapProvider`) and after the
|
|
25
|
+
* HTTP server closed and its in-flight requests finished. Nothing happens in `onModuleDestroy`.
|
|
26
|
+
* Best-effort and idempotent: an `end()` error is swallowed, a second call does nothing, and it
|
|
27
|
+
* never throws.
|
|
28
|
+
*/
|
|
29
|
+
let IdentityAuthzPoolLifecycle = class IdentityAuthzPoolLifecycle {
|
|
30
|
+
pool;
|
|
31
|
+
ended = false;
|
|
32
|
+
constructor(pool) {
|
|
33
|
+
this.pool = pool;
|
|
34
|
+
}
|
|
35
|
+
async onApplicationShutdown() {
|
|
36
|
+
if (this.ended)
|
|
37
|
+
return;
|
|
38
|
+
this.ended = true;
|
|
39
|
+
try {
|
|
40
|
+
await this.pool.end();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// best-effort: a pool already ending or a mid-query close is non-fatal at shutdown.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.IdentityAuthzPoolLifecycle = IdentityAuthzPoolLifecycle;
|
|
48
|
+
exports.IdentityAuthzPoolLifecycle = IdentityAuthzPoolLifecycle = __decorate([
|
|
49
|
+
(0, common_1.Injectable)(),
|
|
50
|
+
__param(0, (0, common_1.Inject)(org_bootstrap_tokens_js_1.IDENTITY_AUTHZ_POOL)),
|
|
51
|
+
__metadata("design:paramtypes", [Object])
|
|
52
|
+
], IdentityAuthzPoolLifecycle);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type OnApplicationBootstrap } from '@nestjs/common';
|
|
1
|
+
import { type OnApplicationBootstrap, type OnModuleDestroy } from '@nestjs/common';
|
|
2
2
|
import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core';
|
|
3
3
|
import { type RecordAuthorizationInputs } from '@astram/guard';
|
|
4
4
|
import type { ObjectType } from '@astram/engine';
|
|
@@ -16,7 +16,7 @@ import { ScopeClosureService } from '../scope/scope-closure.service.js';
|
|
|
16
16
|
* health controller injects — so verify, the post-boot pass, and the endpoint all share
|
|
17
17
|
* one job. This provider owns only the boot verify + the schedule.
|
|
18
18
|
*/
|
|
19
|
-
export declare class CatalogBootstrapProvider implements OnApplicationBootstrap {
|
|
19
|
+
export declare class CatalogBootstrapProvider implements OnApplicationBootstrap, OnModuleDestroy {
|
|
20
20
|
private readonly discovery;
|
|
21
21
|
private readonly scanner;
|
|
22
22
|
private readonly reflector;
|
|
@@ -27,8 +27,20 @@ export declare class CatalogBootstrapProvider implements OnApplicationBootstrap
|
|
|
27
27
|
private readonly recordAuthorization;
|
|
28
28
|
private readonly logger;
|
|
29
29
|
private timer;
|
|
30
|
+
/** The last scheduled reconcile pass (settled or not); drained by `onModuleDestroy`. */
|
|
31
|
+
private inFlightReconcile;
|
|
32
|
+
private destroyed;
|
|
30
33
|
constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, job: CatalogReconcileJob, accessBundleJob: AccessBundleReconcileJob, scopeClosure: ScopeClosureService, registrations: readonly ObjectType[], recordAuthorization: RecordAuthorizationInputs);
|
|
31
34
|
onApplicationBootstrap(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* G-026 (spec 5.K): stop the hourly reconcile before the authz pool ends. Clears the interval,
|
|
37
|
+
* then waits for a reconcile already running (its outcome is not this hook's concern), so
|
|
38
|
+
* `IdentityAuthzPoolLifecycle` (in `onApplicationShutdown`) never ends the pool under it. A tick
|
|
39
|
+
* that fires after this is a no-op. Never throws.
|
|
40
|
+
*/
|
|
41
|
+
onModuleDestroy(): Promise<void>;
|
|
42
|
+
/** One scheduled reconcile pass (catalog, then bundles, then the scope closure). */
|
|
43
|
+
private tick;
|
|
32
44
|
/** Rebuild the scope closure; a failure is logged loudly (never silently swallowed) but does
|
|
33
45
|
* not abort boot or a cron tick — the reconcile is idempotent and retried next interval. */
|
|
34
46
|
private reconcileScopeClosure;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-bootstrap.provider.d.ts","sourceRoot":"","sources":["../../../src/features/authz/catalog-bootstrap.provider.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"catalog-bootstrap.provider.d.ts","sourceRoot":"","sources":["../../../src/features/authz/catalog-bootstrap.provider.ts"],"names":[],"mappings":"AAIA,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC5E,OAAO,EAKL,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAKxE;;;;;;;;;;GAUG;AACH,qBACa,wBAAyB,YAAW,sBAAsB,EAAE,eAAe;IAQpF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY;IAE7B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAE9B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAhBtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6C;IACpE,OAAO,CAAC,KAAK,CAA+B;IAC5C,wFAAwF;IACxF,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,SAAS,CAAS;gBAGP,SAAS,EAAE,gBAAgB,EAC3B,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,SAAS,EACpB,GAAG,EAAE,mBAAmB,EACxB,eAAe,EAAE,wBAAwB,EACzC,YAAY,EAAE,mBAAmB,EAEjC,aAAa,EAAE,SAAS,UAAU,EAAE,EAEpC,mBAAmB,EAAE,yBAAyB;IAG3D,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAmE7C;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAUtC,oFAAoF;IACpF,OAAO,CAAC,IAAI;IAaZ;iGAC6F;YAC/E,qBAAqB;CAOpC"}
|
|
@@ -49,6 +49,9 @@ let CatalogBootstrapProvider = CatalogBootstrapProvider_1 = class CatalogBootstr
|
|
|
49
49
|
recordAuthorization;
|
|
50
50
|
logger = new common_1.Logger(CatalogBootstrapProvider_1.name);
|
|
51
51
|
timer = null;
|
|
52
|
+
/** The last scheduled reconcile pass (settled or not); drained by `onModuleDestroy`. */
|
|
53
|
+
inFlightReconcile = null;
|
|
54
|
+
destroyed = false;
|
|
52
55
|
constructor(discovery, scanner, reflector, job, accessBundleJob, scopeClosure, registrations, recordAuthorization) {
|
|
53
56
|
this.discovery = discovery;
|
|
54
57
|
this.scanner = scanner;
|
|
@@ -104,17 +107,41 @@ let CatalogBootstrapProvider = CatalogBootstrapProvider_1 = class CatalogBootstr
|
|
|
104
107
|
// logged error rather than aborting boot — the reconcile is idempotent and retried on the
|
|
105
108
|
// interval below once the data is fixed.
|
|
106
109
|
await this.reconcileScopeClosure();
|
|
107
|
-
if (process.env.DISABLE_ALL_CRONS !== 'true') {
|
|
108
|
-
this.timer = setInterval(() =>
|
|
109
|
-
const tickIso = new Date().toISOString();
|
|
110
|
-
void this.job
|
|
111
|
-
.runOnce(tickIso)
|
|
112
|
-
.then(() => this.accessBundleJob.runOnce(tickIso))
|
|
113
|
-
.then(() => this.reconcileScopeClosure());
|
|
114
|
-
}, RECONCILE_INTERVAL_MS);
|
|
110
|
+
if (process.env.DISABLE_ALL_CRONS !== 'true' && !this.destroyed) {
|
|
111
|
+
this.timer = setInterval(() => this.tick(), RECONCILE_INTERVAL_MS);
|
|
115
112
|
this.timer.unref?.();
|
|
116
113
|
}
|
|
117
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* G-026 (spec 5.K): stop the hourly reconcile before the authz pool ends. Clears the interval,
|
|
117
|
+
* then waits for a reconcile already running (its outcome is not this hook's concern), so
|
|
118
|
+
* `IdentityAuthzPoolLifecycle` (in `onApplicationShutdown`) never ends the pool under it. A tick
|
|
119
|
+
* that fires after this is a no-op. Never throws.
|
|
120
|
+
*/
|
|
121
|
+
async onModuleDestroy() {
|
|
122
|
+
this.destroyed = true;
|
|
123
|
+
if (this.timer !== null) {
|
|
124
|
+
clearInterval(this.timer);
|
|
125
|
+
this.timer = null;
|
|
126
|
+
}
|
|
127
|
+
const running = this.inFlightReconcile;
|
|
128
|
+
if (running !== null)
|
|
129
|
+
await running.catch(() => undefined);
|
|
130
|
+
}
|
|
131
|
+
/** One scheduled reconcile pass (catalog, then bundles, then the scope closure). */
|
|
132
|
+
tick() {
|
|
133
|
+
if (this.destroyed)
|
|
134
|
+
return;
|
|
135
|
+
const tickIso = new Date().toISOString();
|
|
136
|
+
const run = this.job
|
|
137
|
+
.runOnce(tickIso)
|
|
138
|
+
.then(() => this.accessBundleJob.runOnce(tickIso))
|
|
139
|
+
.then(() => this.reconcileScopeClosure());
|
|
140
|
+
// Kept so onModuleDestroy can drain it. Storing the reference attaches no handler, so a
|
|
141
|
+
// failing tick surfaces exactly as before this change.
|
|
142
|
+
this.inFlightReconcile = run;
|
|
143
|
+
void run;
|
|
144
|
+
}
|
|
118
145
|
/** Rebuild the scope closure; a failure is logged loudly (never silently swallowed) but does
|
|
119
146
|
* not abort boot or a cron tick — the reconcile is idempotent and retried next interval. */
|
|
120
147
|
async reconcileScopeClosure() {
|
|
@@ -30,6 +30,8 @@ export declare function authzPoolLimit(env?: NodeJS.ProcessEnv): number;
|
|
|
30
30
|
* Opens the pool at CALL time (never at import) — the caller owns closing it.
|
|
31
31
|
* `schemas` (the names `forRoot` resolved or was given) set the default database and user; absent
|
|
32
32
|
* ⇒ from the env namespace. An explicit `APP_DB_USER` / `APP_DB_NAME` wins (QA P2-5).
|
|
33
|
+
* G-026 (spec 5.I): idle and timeout settings from guard's `loadPoolTuning` (`minimumIdle`
|
|
34
|
+
* capped at the cap); `IdentityAuthzPoolLifecycle` ends the pool in `onApplicationShutdown`.
|
|
33
35
|
*/
|
|
34
36
|
export declare function createIdentityAuthzPool(schemas?: FrameworkSchemas): Pool;
|
|
35
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-authorization.d.ts","sourceRoot":"","sources":["../../../src/features/authz/record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAgB,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,
|
|
1
|
+
{"version":3,"file":"record-authorization.d.ts","sourceRoot":"","sources":["../../../src/features/authz/record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAgB,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMxF;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAE3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAexE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,IAAI,EACV,YAAY,GAAE,SAAS,eAAe,EAAO;AAC7C,0FAA0F;AAC1F,OAAO,CAAC,EAAE,mBAAmB,GAC5B,yBAAyB,CAS3B"}
|
|
@@ -50,19 +50,23 @@ function authzPoolLimit(env = process.env) {
|
|
|
50
50
|
* Opens the pool at CALL time (never at import) — the caller owns closing it.
|
|
51
51
|
* `schemas` (the names `forRoot` resolved or was given) set the default database and user; absent
|
|
52
52
|
* ⇒ from the env namespace. An explicit `APP_DB_USER` / `APP_DB_NAME` wins (QA P2-5).
|
|
53
|
+
* G-026 (spec 5.I): idle and timeout settings from guard's `loadPoolTuning` (`minimumIdle`
|
|
54
|
+
* capped at the cap); `IdentityAuthzPoolLifecycle` ends the pool in `onApplicationShutdown`.
|
|
53
55
|
*/
|
|
54
56
|
function createIdentityAuthzPool(schemas) {
|
|
55
57
|
const db = schemas === undefined ? (0, guard_1.loadDbConfig)() : (0, guard_1.loadDbConfig)(process.env, schemas);
|
|
56
58
|
const ssl = (0, guard_1.mariadbSslOption)(db.tls);
|
|
59
|
+
const connectionLimit = authzPoolLimit(process.env);
|
|
57
60
|
return mariadb_1.default.createPool({
|
|
58
61
|
host: db.host,
|
|
59
62
|
port: db.port,
|
|
60
63
|
user: db.user,
|
|
61
64
|
password: db.password,
|
|
62
65
|
database: db.database,
|
|
63
|
-
connectionLimit
|
|
66
|
+
connectionLimit,
|
|
64
67
|
// Phase 16: APP_DB_TLS / APP_DB_TLS_CA (production accounts are REQUIRE SSL).
|
|
65
68
|
...(ssl ? { ssl } : {}),
|
|
69
|
+
...(0, guard_1.mariadbPoolTuningOptions)((0, guard_1.loadPoolTuning)(process.env), { connectionLimit }),
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-feature.module.d.ts","sourceRoot":"","sources":["../../src/features/identity-feature.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAaL,KAAK,cAAc,EAGnB,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"identity-feature.module.d.ts","sourceRoot":"","sources":["../../src/features/identity-feature.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAGL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AACtB,OAAO,EAaL,KAAK,cAAc,EAGnB,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAyDxB,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AA2G/E;;;;;;GAMG;AACH,wBAAgB,0CAA0C,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAUnF;AAKD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,GACzC,IAAI,CAwBN;AAKD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC/E,IAAI,CAQN;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,EACzB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,cAAc,CAyBhB;AA2BD,qBACa,qBAAqB;IAChC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,OAAO,CAAC,EACb,OAAO,EACP,YAAiB,EACjB,OAAO,EAAE,YAAY,EACrB,MAAM,GACP,GAAE;QACD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAC5B,YAAY,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;QAC1C,OAAO,CAAC,EAAE,gBAAgB,CAAC;QAC3B,MAAM,CAAC,EAAE,aAAa,CAAC;KACnB,GAAG,aAAa;CAmqBvB"}
|
|
@@ -66,6 +66,7 @@ const service_account_controller_js_1 = require("./service-account/service-accou
|
|
|
66
66
|
const catalog_reconcile_job_js_1 = require("./authz/catalog-reconcile.job.js");
|
|
67
67
|
const access_bundle_reconcile_job_js_1 = require("./authz/access-bundle-reconcile.job.js");
|
|
68
68
|
const catalog_bootstrap_provider_js_1 = require("./authz/catalog-bootstrap.provider.js");
|
|
69
|
+
const authz_pool_lifecycle_js_1 = require("./authz/authz-pool-lifecycle.js");
|
|
69
70
|
const scope_closure_service_js_1 = require("./scope/scope-closure.service.js");
|
|
70
71
|
const mariadb_audit_reader_js_1 = require("./audit/mariadb-audit-reader.js");
|
|
71
72
|
const directory_label_resolver_js_1 = require("./audit/directory-label-resolver.js");
|
|
@@ -203,6 +204,10 @@ function engineDbFromEnv(schemas, env = process.env) {
|
|
|
203
204
|
connectionLimit,
|
|
204
205
|
// Phase 16: APP_DB_TLS / APP_DB_TLS_CA (production accounts are REQUIRE SSL).
|
|
205
206
|
...(ssl ? { ssl } : {}),
|
|
207
|
+
// G-026 (spec 5.H): POOL_MIN_IDLE, POOL_IDLE_TIMEOUT_S and the optional
|
|
208
|
+
// APP_DB_QUERY_TIMEOUT_MS / POOL_LEAK_DETECTION_MS, from guard's one loader (a set but invalid
|
|
209
|
+
// value throws naming the variable). The engine caps minimumIdle at connectionLimit.
|
|
210
|
+
...(0, guard_1.loadPoolTuning)(env),
|
|
206
211
|
};
|
|
207
212
|
}
|
|
208
213
|
/**
|
|
@@ -780,6 +785,9 @@ let IdentityFeatureModule = IdentityFeatureModule_1 = class IdentityFeatureModul
|
|
|
780
785
|
// above), the jobs, the ScopeClosureService, ENGINE_ALL_REGISTRATIONS and the record
|
|
781
786
|
// authorization (whose registry is the verify key universe, KI #70).
|
|
782
787
|
catalog_bootstrap_provider_js_1.CatalogBootstrapProvider,
|
|
788
|
+
// G-026 (spec 5.J): ends the shared authz pool in onApplicationShutdown, after the
|
|
789
|
+
// reconcile above has drained (its onModuleDestroy) and the HTTP server has closed.
|
|
790
|
+
authz_pool_lifecycle_js_1.IdentityAuthzPoolLifecycle,
|
|
783
791
|
// Stored files (file-storage Phase 5): the request-principal RecordsService + the guard's
|
|
784
792
|
// object store (`null`/absent ⇒ every method answers 501) + the file_access audit bridge.
|
|
785
793
|
// G-010 T9 (P-5): the invite, its settings, its audit writer and its two Redis limiters
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-org-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/run-org-bootstrap.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,uBAAuB,EAAQ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"run-org-bootstrap.d.ts","sourceRoot":"","sources":["../../../src/features/org-bootstrap/run-org-bootstrap.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,uBAAuB,EAAQ,MAAM,gBAAgB,CAAC;AA+BpE,OAAO,EAAqB,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAIL,KAAK,OAAO,EAIb,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE3F,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5D;AAED,oDAAoD;AACpD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,kDAAkD;IAClD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAwCD,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,+FAC6D,CAAC;AA8D/F,wBAAsB,eAAe,CACnC,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CAoqB7B"}
|
|
@@ -191,10 +191,8 @@ async function runOrgBootstrap(app, opts) {
|
|
|
191
191
|
const lockName = `astram_org_bootstrap:${schemas.app}`;
|
|
192
192
|
const lockConn = await pool.getConnection();
|
|
193
193
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
opts.lockTimeoutSeconds ?? 30,
|
|
197
|
-
]));
|
|
194
|
+
// G-026 (spec 5.L): APP_DB_QUERY_TIMEOUT_MS never cuts the lock wait; its own timeout applies.
|
|
195
|
+
const got = (await lockConn.query((0, guard_1.withoutStatementTimeout)('SELECT GET_LOCK(?, ?) AS acquired'), [lockName, opts.lockTimeoutSeconds ?? 30]));
|
|
198
196
|
if (Number(got[0]?.acquired) !== 1) {
|
|
199
197
|
throw new org_bootstrap_error_js_1.OrgBootstrapError('lock_not_acquired', 'Another org bootstrap run holds the lock; nothing was written. Retry when it finishes.');
|
|
200
198
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-closure.service.d.ts","sourceRoot":"","sources":["../../../src/features/scope/scope-closure.service.ts"],"names":[],"mappings":"AAqCA,OAAO,
|
|
1
|
+
{"version":3,"file":"scope-closure.service.d.ts","sourceRoot":"","sources":["../../../src/features/scope/scope-closure.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -116,7 +116,8 @@ class ScopeClosureService {
|
|
|
116
116
|
const conn = await this.pool.getConnection();
|
|
117
117
|
let locked = false;
|
|
118
118
|
try {
|
|
119
|
-
|
|
119
|
+
// G-026 (spec 5.L): APP_DB_QUERY_TIMEOUT_MS never cuts the lock wait; its own timeout applies.
|
|
120
|
+
const got = (await conn.query((0, guard_1.withoutStatementTimeout)('SELECT GET_LOCK(?, ?) AS got'), [
|
|
120
121
|
TREE_LOCK,
|
|
121
122
|
TREE_LOCK_TIMEOUT_S,
|
|
122
123
|
]));
|
package/dist/user.object.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class User {
|
|
|
11
11
|
* The sign-in handle (auth capabilities R1.2, migration 103): lower-case `[a-z0-9._-]{3,64}`,
|
|
12
12
|
* globally unique, NULL = none (sign in by email only). Set only by an admin (dominance,
|
|
13
13
|
* `UserService`) or `bootstrap:org`; never by `/account/*`. Not the display name (`username`).
|
|
14
|
+
* `nullable` so an admin's `null` clears it (G-031: the engine refuses NULL otherwise).
|
|
14
15
|
*/
|
|
15
16
|
loginName?: string | null;
|
|
16
17
|
status: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.object.d.ts","sourceRoot":"","sources":["../src/user.object.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBA4Ba,IAAI;IAEf,IAAI,EAAG,MAAM,CAAC;IAGd,QAAQ,EAAG,MAAM,CAAC;IAGlB,KAAK,EAAG,MAAM,CAAC;IAEf
|
|
1
|
+
{"version":3,"file":"user.object.d.ts","sourceRoot":"","sources":["../src/user.object.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBA4Ba,IAAI;IAEf,IAAI,EAAG,MAAM,CAAC;IAGd,QAAQ,EAAG,MAAM,CAAC;IAGlB,KAAK,EAAG,MAAM,CAAC;IAEf;;;;;OAKG;IAEH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;CACpB"}
|
package/dist/user.object.js
CHANGED
|
@@ -25,6 +25,7 @@ let User = class User {
|
|
|
25
25
|
* The sign-in handle (auth capabilities R1.2, migration 103): lower-case `[a-z0-9._-]{3,64}`,
|
|
26
26
|
* globally unique, NULL = none (sign in by email only). Set only by an admin (dominance,
|
|
27
27
|
* `UserService`) or `bootstrap:org`; never by `/account/*`. Not the display name (`username`).
|
|
28
|
+
* `nullable` so an admin's `null` clears it (G-031: the engine refuses NULL otherwise).
|
|
28
29
|
*/
|
|
29
30
|
loginName;
|
|
30
31
|
status;
|
|
@@ -44,7 +45,7 @@ __decorate([
|
|
|
44
45
|
__metadata("design:type", String)
|
|
45
46
|
], User.prototype, "email", void 0);
|
|
46
47
|
__decorate([
|
|
47
|
-
(0, engine_1.Field)({ column: 'u_login_name', type: 'string' }),
|
|
48
|
+
(0, engine_1.Field)({ column: 'u_login_name', type: 'string', nullable: true }),
|
|
48
49
|
__metadata("design:type", Object)
|
|
49
50
|
], User.prototype, "loginName", void 0);
|
|
50
51
|
__decorate([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astram/identity",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Astram framework — the framework-owned identity + tenancy model. Hosts the identity @ObjectType classes (user, company, membership, posting), framework-authored identity migrations, and the identity resolver. Top of the dependency graph: depends on @astram/engine (for @ObjectType/@Field decorators) + @astram/guard / @astram/core (for the auth ports).",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"mariadb": "^3.5.4",
|
|
39
39
|
"@astram/core": "1.12.0",
|
|
40
|
-
"@astram/guard": "0.
|
|
41
|
-
"@astram/engine": "0.
|
|
40
|
+
"@astram/guard": "0.22.0",
|
|
41
|
+
"@astram/engine": "0.15.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@nestjs/common": "^11",
|