@astram/guard 0.6.0 → 0.8.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/account.controller.d.ts +3 -1
- package/dist/auth/account.controller.d.ts.map +1 -1
- package/dist/auth/account.controller.js +12 -5
- package/dist/auth/auth.controller.d.ts +130 -21
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +537 -143
- package/dist/auth/auth.module.d.ts +15 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +241 -30
- package/dist/auth/connections.d.ts +1 -1
- package/dist/auth/connections.d.ts.map +1 -1
- package/dist/auth/connections.js +14 -3
- package/dist/auth/default-session-facts-projector.d.ts +26 -0
- package/dist/auth/default-session-facts-projector.d.ts.map +1 -0
- package/dist/auth/default-session-facts-projector.js +21 -0
- package/dist/auth/mfa-step-up.d.ts +32 -0
- package/dist/auth/mfa-step-up.d.ts.map +1 -0
- package/dist/auth/mfa-step-up.js +109 -0
- package/dist/auth/pending-mfa-store.d.ts +50 -52
- package/dist/auth/pending-mfa-store.d.ts.map +1 -1
- package/dist/auth/pending-mfa-store.js +103 -61
- package/dist/auth/ports/session-facts-projector.d.ts +5 -0
- package/dist/auth/ports/session-facts-projector.d.ts.map +1 -1
- package/dist/auth/rate-limit/canonical-hint.d.ts +24 -0
- package/dist/auth/rate-limit/canonical-hint.d.ts.map +1 -0
- package/dist/auth/rate-limit/canonical-hint.js +63 -0
- package/dist/auth/rate-limit/trust-proxy-check.d.ts +46 -0
- package/dist/auth/rate-limit/trust-proxy-check.d.ts.map +1 -0
- package/dist/auth/rate-limit/trust-proxy-check.js +94 -0
- package/dist/auth/tokens.d.ts +70 -11
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +71 -12
- package/dist/authentication/log-email-sender.d.ts +9 -4
- package/dist/authentication/log-email-sender.d.ts.map +1 -1
- package/dist/authentication/mariadb-api-key-store.d.ts +46 -0
- package/dist/authentication/mariadb-api-key-store.d.ts.map +1 -0
- package/dist/authentication/mariadb-api-key-store.js +111 -0
- package/dist/authentication/mariadb-credential-resolver.d.ts +79 -65
- package/dist/authentication/mariadb-credential-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-resolver.js +116 -73
- package/dist/authentication/mariadb-credential-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-or-totp-verifier.d.ts +21 -3
- package/dist/authentication/mariadb-or-totp-verifier.d.ts.map +1 -1
- package/dist/authentication/mariadb-or-totp-verifier.js +37 -6
- package/dist/authentication/mariadb-profile-resolver.d.ts.map +1 -1
- package/dist/authentication/mariadb-profile-writer.d.ts.map +1 -1
- package/dist/authentication/mariadb-session-sweeper.d.ts +14 -1
- package/dist/authentication/mariadb-session-sweeper.d.ts.map +1 -1
- package/dist/authentication/mariadb-session-sweeper.js +29 -11
- package/dist/authentication/noop-email-sender.d.ts +12 -0
- package/dist/authentication/noop-email-sender.d.ts.map +1 -0
- package/dist/authentication/noop-email-sender.js +9 -0
- package/dist/authz/cross-tenant-grant-writer.d.ts.map +1 -1
- package/dist/authz/manifest-operation-authority-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-cross-tenant-grant-adapter.js +3 -1
- package/dist/authz/mariadb-policy-store.js +1 -1
- package/dist/authz/mariadb-record-rule-adapter.d.ts.map +1 -1
- package/dist/authz/mariadb-record-rule-adapter.js +1 -4
- package/dist/authz/permission-version-cache.d.ts +38 -0
- package/dist/authz/permission-version-cache.d.ts.map +1 -0
- package/dist/authz/permission-version-cache.js +81 -0
- package/dist/authz/wire-record-authorization.d.ts +25 -1
- package/dist/authz/wire-record-authorization.d.ts.map +1 -1
- package/dist/authz/wire-record-authorization.js +55 -2
- package/dist/completeness/catalog-reconciler.d.ts.map +1 -1
- package/dist/config/app-config.d.ts +52 -0
- package/dist/config/app-config.d.ts.map +1 -1
- package/dist/config/app-config.js +66 -4
- package/dist/encryption/local-kek-crypto.d.ts +15 -0
- package/dist/encryption/local-kek-crypto.d.ts.map +1 -0
- package/dist/encryption/local-kek-crypto.js +79 -0
- package/dist/enforcement/enforcement.module.d.ts.map +1 -1
- package/dist/enforcement/enforcement.module.js +9 -0
- package/dist/enforcement/engine-error.filter.d.ts +57 -0
- package/dist/enforcement/engine-error.filter.d.ts.map +1 -0
- package/dist/enforcement/engine-error.filter.js +118 -0
- package/dist/enforcement/global-enforcement.guard.d.ts +31 -2
- package/dist/enforcement/global-enforcement.guard.d.ts.map +1 -1
- package/dist/enforcement/global-enforcement.guard.js +148 -6
- package/dist/identity/auth-facts.controller.d.ts.map +1 -1
- package/dist/identity/identity-actions.d.ts +9 -0
- package/dist/identity/identity-actions.d.ts.map +1 -1
- package/dist/identity/identity-actions.js +16 -1
- package/dist/identity/permission-facts.adapter.d.ts.map +1 -1
- package/dist/identity/permission-facts.adapter.js +2 -3
- package/dist/index.d.ts +17 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -25
- package/dist/module/shield.module.d.ts +9 -2
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +47 -38
- package/dist/request-auth/request-principal.d.ts +11 -3
- package/dist/request-auth/request-principal.d.ts.map +1 -1
- package/dist/request-auth/request-principal.js +58 -2
- package/dist/request-auth/session-facts-als.d.ts +16 -1
- package/dist/request-auth/session-facts-als.d.ts.map +1 -1
- package/dist/request-auth/session-facts-als.js +23 -0
- package/dist/request-auth/session-token-binder.d.ts +166 -10
- package/dist/request-auth/session-token-binder.d.ts.map +1 -1
- package/dist/request-auth/session-token-binder.js +408 -28
- package/dist/request-auth/tokens.d.ts +10 -0
- package/dist/request-auth/tokens.d.ts.map +1 -1
- package/dist/request-auth/tokens.js +11 -1
- package/dist/secrets/encrypted-file-secrets.d.ts +16 -0
- package/dist/secrets/encrypted-file-secrets.d.ts.map +1 -0
- package/dist/secrets/encrypted-file-secrets.js +98 -0
- package/dist/session/hot-revoking-session-record-store.d.ts +46 -0
- package/dist/session/hot-revoking-session-record-store.d.ts.map +1 -0
- package/dist/session/hot-revoking-session-record-store.js +58 -0
- package/dist/session/index.d.ts +3 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +8 -1
- package/dist/session/mariadb-session-record-store.d.ts +6 -0
- package/dist/session/mariadb-session-record-store.d.ts.map +1 -1
- package/dist/session/mariadb-session-record-store.js +16 -1
- package/dist/session/session-lifetimes.d.ts +30 -0
- package/dist/session/session-lifetimes.d.ts.map +1 -0
- package/dist/session/session-lifetimes.js +21 -0
- package/dist/session/session-restriction-store.d.ts +43 -0
- package/dist/session/session-restriction-store.d.ts.map +1 -0
- package/dist/session/session-restriction-store.js +35 -0
- package/migrations/1710000000017-CreateServiceAccountKey.cjs +41 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-profile-writer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-profile-writer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAOtE,qBAAa,oBAAqB,YAAW,aAAa;IAE5C,OAAO,CAAC,QAAQ,CAAC,IAAI;IADjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBACrB,IAAI,EAAE,IAAI;IAIjC,MAAM,
|
|
1
|
+
{"version":3,"file":"mariadb-profile-writer.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-profile-writer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAOtE,qBAAa,oBAAqB,YAAW,aAAa;IAE5C,OAAO,CAAC,QAAQ,CAAC,IAAI;IADjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAyB;gBACrB,IAAI,EAAE,IAAI;IAIjC,MAAM,CACV,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACzC,OAAO,CAAC,kBAAkB,CAAC;CAsC/B"}
|
|
@@ -23,11 +23,24 @@
|
|
|
23
23
|
import type { Pool } from 'mariadb';
|
|
24
24
|
import type { SessionSweeper } from '../auth/ports/session-sweeper.js';
|
|
25
25
|
import type { SessionTokenBinder } from '../request-auth/session-token-binder.js';
|
|
26
|
+
/** The binder surface the sweeper needs; `unbindSessionStrict` (throws on a Redis error) is preferred. */
|
|
27
|
+
type SweepBinder = Pick<SessionTokenBinder, 'unbindSession'> & Partial<Pick<SessionTokenBinder, 'unbindSessionStrict'>>;
|
|
26
28
|
export declare class MariaDbSessionSweeper implements SessionSweeper {
|
|
27
29
|
private readonly pool;
|
|
28
30
|
private readonly binder;
|
|
29
|
-
|
|
31
|
+
/** Clock for the "not yet expired" unbind window (epoch-ms). */
|
|
32
|
+
private readonly now;
|
|
33
|
+
constructor(pool: Pool, binder: SweepBinder,
|
|
34
|
+
/** Clock for the "not yet expired" unbind window (epoch-ms). */
|
|
35
|
+
now?: () => number);
|
|
36
|
+
/**
|
|
37
|
+
* Unbind the given sessions in Redis. Every session is attempted; if any unbind failed, the
|
|
38
|
+
* FIRST error is thrown afterwards (Phase 3 review P1), so an administrative revoke reports it
|
|
39
|
+
* and can be retried rather than leaving live access tokens valid until their TTL.
|
|
40
|
+
*/
|
|
41
|
+
private unbindAll;
|
|
30
42
|
revokeAllForPrincipal(principalId: string): Promise<void>;
|
|
31
43
|
revokeOthersForPrincipal(principalId: string, keepSessionId: string): Promise<void>;
|
|
32
44
|
}
|
|
45
|
+
export {};
|
|
33
46
|
//# sourceMappingURL=mariadb-session-sweeper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-session-sweeper.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-session-sweeper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAMlF,qBAAa,qBAAsB,YAAW,cAAc;IAExD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;
|
|
1
|
+
{"version":3,"file":"mariadb-session-sweeper.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-session-sweeper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAMlF,0GAA0G;AAC1G,KAAK,WAAW,GAAG,IAAI,CAAC,kBAAkB,EAAE,eAAe,CAAC,GAC1D,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAE3D,qBAAa,qBAAsB,YAAW,cAAc;IAExD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAHH,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,WAAW;IACpC,gEAAgE;IAC/C,GAAG,GAAE,MAAM,MAAiB;IAG/C;;;;OAIG;YACW,SAAS;IAUjB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBzD,wBAAwB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAkB1F"}
|
|
@@ -26,35 +26,53 @@ exports.MariaDbSessionSweeper = void 0;
|
|
|
26
26
|
class MariaDbSessionSweeper {
|
|
27
27
|
pool;
|
|
28
28
|
binder;
|
|
29
|
-
|
|
29
|
+
now;
|
|
30
|
+
constructor(pool, binder,
|
|
31
|
+
/** Clock for the "not yet expired" unbind window (epoch-ms). */
|
|
32
|
+
now = Date.now) {
|
|
30
33
|
this.pool = pool;
|
|
31
34
|
this.binder = binder;
|
|
35
|
+
this.now = now;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Unbind the given sessions in Redis. Every session is attempted; if any unbind failed, the
|
|
39
|
+
* FIRST error is thrown afterwards (Phase 3 review P1), so an administrative revoke reports it
|
|
40
|
+
* and can be retried rather than leaving live access tokens valid until their TTL.
|
|
41
|
+
*/
|
|
42
|
+
async unbindAll(sessionIds) {
|
|
43
|
+
const unbind = (id) => this.binder.unbindSessionStrict !== undefined
|
|
44
|
+
? this.binder.unbindSessionStrict(id)
|
|
45
|
+
: this.binder.unbindSession(id);
|
|
46
|
+
const results = await Promise.allSettled(sessionIds.map((id) => unbind(id)));
|
|
47
|
+
const failed = results.find((r) => r.status === 'rejected');
|
|
48
|
+
if (failed !== undefined)
|
|
49
|
+
throw failed.reason;
|
|
32
50
|
}
|
|
33
51
|
async revokeAllForPrincipal(principalId) {
|
|
52
|
+
// The UNBIND set (Phase 3 review P1): every session of the principal that has not reached its
|
|
53
|
+
// absolute expiry, WHATEVER its status — so a retry after a failed unbind (the durable row is
|
|
54
|
+
// already `revoked`) still drops the Redis keys. Read BEFORE the durable revoke.
|
|
34
55
|
const rows = (await this.pool.query(`SELECT s_session_id
|
|
35
56
|
FROM \`shield_identity\`.\`session\`
|
|
36
|
-
WHERE s_principal_id = ? AND
|
|
57
|
+
WHERE s_principal_id = ? AND s_absolute_expires_at_ms > ?`, [principalId, this.now()]));
|
|
37
58
|
await this.pool.query(`UPDATE \`shield_identity\`.\`session\`
|
|
38
59
|
SET s_status = 'revoked'
|
|
39
60
|
WHERE s_principal_id = ? AND s_status = 'active'`, [principalId]);
|
|
40
|
-
// Drop each
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
await this.binder.unbindSession(s_session_id);
|
|
44
|
-
}
|
|
61
|
+
// Drop each session's Redis liveness key so outstanding access tokens die immediately, not
|
|
62
|
+
// at their TTL. A failure is surfaced after every session was attempted.
|
|
63
|
+
await this.unbindAll(rows.map((r) => r.s_session_id));
|
|
45
64
|
}
|
|
46
65
|
async revokeOthersForPrincipal(principalId, keepSessionId) {
|
|
66
|
+
// Same unbind set as revokeAll (not-yet-expired, any status) minus the kept session.
|
|
47
67
|
const rows = (await this.pool.query(`SELECT s_session_id
|
|
48
68
|
FROM \`shield_identity\`.\`session\`
|
|
49
|
-
WHERE s_principal_id = ? AND
|
|
69
|
+
WHERE s_principal_id = ? AND s_absolute_expires_at_ms > ? AND s_session_id <> ?`, [principalId, this.now(), keepSessionId]));
|
|
50
70
|
await this.pool.query(`UPDATE \`shield_identity\`.\`session\`
|
|
51
71
|
SET s_status = 'revoked'
|
|
52
72
|
WHERE s_principal_id = ? AND s_status = 'active' AND s_session_id <> ?`, [principalId, keepSessionId]);
|
|
53
73
|
// Only the OTHER sessions are unbound — the kept (current) session's liveness key
|
|
54
74
|
// is never touched, so the actor stays signed in after a change-password.
|
|
55
|
-
|
|
56
|
-
await this.binder.unbindSession(s_session_id);
|
|
57
|
-
}
|
|
75
|
+
await this.unbindAll(rows.map((r) => r.s_session_id));
|
|
58
76
|
}
|
|
59
77
|
}
|
|
60
78
|
exports.MariaDbSessionSweeper = MariaDbSessionSweeper;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NoopEmailSender — bound when the app passes `emailSender: 'disabled'` (security
|
|
3
|
+
* hardening #6). Delivers nothing and writes nothing (no stdout, no log line), so a
|
|
4
|
+
* deployment without a mail provider can run `auth: true` without leaking reset links.
|
|
5
|
+
* `POST /auth/forgot-password` keeps its always-204 contract; users simply receive no
|
|
6
|
+
* email. AuthModule logs ONE startup warning that email delivery is disabled.
|
|
7
|
+
*/
|
|
8
|
+
import type { EmailSender, EmailMessage } from '../auth/ports/email-sender.js';
|
|
9
|
+
export declare class NoopEmailSender implements EmailSender {
|
|
10
|
+
send(_message: EmailMessage): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=noop-email-sender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noop-email-sender.d.ts","sourceRoot":"","sources":["../../src/authentication/noop-email-sender.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE/E,qBAAa,eAAgB,YAAW,WAAW;IAC3C,IAAI,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAGlD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NoopEmailSender = void 0;
|
|
4
|
+
class NoopEmailSender {
|
|
5
|
+
async send(_message) {
|
|
6
|
+
// Intentionally empty: delivery is disabled by explicit configuration.
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.NoopEmailSender = NoopEmailSender;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cross-tenant-grant-writer.d.ts","sourceRoot":"","sources":["../../src/authz/cross-tenant-grant-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAK9E,8CAA8C;AAC9C,eAAO,MAAM,gCAAgC,eAA6C,CAAC;AAE3F,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/C,gFAAgF;AAChF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,QAAQ,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,KAClC,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,+FAA+F;AAC/F,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,oCAAoC;IACpC,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,GAAG,eAAe,CAAC;IACtE,mCAAmC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,2FAA2F;IAC3F,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,yFAAyF;IACzF,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;QAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;QACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;KAC5C,CAAC;CACH;AAED,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,mDAAmD;IACnD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,iFAAiF;IACjF,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,sDAAsD;AACtD,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wFAAwF;AACxF,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,6FAA6F;AAC7F,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,OAAO,EAAE,MAAM;CAI5B;AAqCD,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,IAAI,EAAE;QAChB,EAAE,EAAE,cAAc,CAAC;QACnB;;;;WAIG;QACH,SAAS,EAAE,gBAAgB,CAAC;QAC5B,oFAAoF;QACpF,KAAK,CAAC,EAAE,cAAc,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,+DAA+D;QAC/D,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;KACtB;IAQD;;;;;;;;;OASG;IACG,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC;IAuEpD;;;;;;;;;OASG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDpD;;;;;;;;;OASG;IACG,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqDpD;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"cross-tenant-grant-writer.d.ts","sourceRoot":"","sources":["../../src/authz/cross-tenant-grant-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-operation-authority-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/manifest-operation-authority-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE/E,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;CACxC;AA6BD,qBAAa,iCAAkC,YAAW,sBAAsB;IAC9E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D;;;;;OAKG;gBACS,OAAO,GAAE,SAAS,sBAAsB,EAAO;IAuB3D,yBAAyB,
|
|
1
|
+
{"version":3,"file":"manifest-operation-authority-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/manifest-operation-authority-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE/E,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,eAA4C,CAAC;AAEzF;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAC;CACxC;AA6BD,qBAAa,iCAAkC,YAAW,sBAAsB;IAC9E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D;;;;;OAKG;gBACS,OAAO,GAAE,SAAS,sBAAsB,EAAO;IAuB3D,yBAAyB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;CAajG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-cross-tenant-grant-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-cross-tenant-grant-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,cAAc,CAAC;AAGtB,wFAAwF;AACxF,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAKvF,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAsDD;;;;GAIG;AACH,qBAAa,8BAA+B,YAAW,oBAAoB;IACzE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAQ9E,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAwFrF;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,uBAAuB,CAAC,KAAK,EAAE;QACnC,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"mariadb-cross-tenant-grant-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-cross-tenant-grant-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,cAAc,CAAC;AAGtB,wFAAwF;AACxF,eAAO,MAAM,8BAA8B,eAA2C,CAAC;AAKvF,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAsDD;;;;GAIG;AACH,qBAAa,8BAA+B,YAAW,oBAAoB;IACzE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAEvB,EAAE,EAAE,cAAc,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAA;KAAO;IAQ9E,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAwFrF;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,uBAAuB,CAAC,KAAK,EAAE;QACnC,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAkCtB"}
|
|
@@ -254,7 +254,9 @@ class MariaDbCrossTenantGrantAdapter {
|
|
|
254
254
|
throw new core_1.AuthorizationUnavailableError(`cross_tenant_grant store unavailable resolving owning tenants for ` +
|
|
255
255
|
`${query.typeKey}:${query.action}: ${err.message}`);
|
|
256
256
|
}
|
|
257
|
-
return rows
|
|
257
|
+
return rows
|
|
258
|
+
.map((r) => r.ctg_owning_tenant_uuid)
|
|
259
|
+
.filter((t) => typeof t === 'string');
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
exports.MariaDbCrossTenantGrantAdapter = MariaDbCrossTenantGrantAdapter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mariadb-record-rule-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-record-rule-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"mariadb-record-rule-adapter.d.ts","sourceRoot":"","sources":["../../src/authz/mariadb-record-rule-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1F,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,eAAoC,CAAC;AAKzE,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE;AAmLD;;;GAGG;AACH,qBAAa,kCAAmC,YAAW,wBAAwB;IACjF,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAgB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,EAAE,EAAE,aAAa,EAAE,IAAI,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKvD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC;CAuE5D"}
|
|
@@ -90,10 +90,7 @@ function orderedCompare(actual, expected) {
|
|
|
90
90
|
return null;
|
|
91
91
|
const actualNum = Number(actual);
|
|
92
92
|
const expectedNum = Number(expected);
|
|
93
|
-
const bothNumeric = actual !== '' &&
|
|
94
|
-
expected !== '' &&
|
|
95
|
-
Number.isFinite(actualNum) &&
|
|
96
|
-
Number.isFinite(expectedNum);
|
|
93
|
+
const bothNumeric = actual !== '' && expected !== '' && Number.isFinite(actualNum) && Number.isFinite(expectedNum);
|
|
97
94
|
if (bothNumeric) {
|
|
98
95
|
return actualNum < expectedNum ? -1 : actualNum > expectedNum ? 1 : 0;
|
|
99
96
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type Redis from 'ioredis';
|
|
2
|
+
/** Redis key prefix for a tenant's permission version. */
|
|
3
|
+
export declare const PERMISSION_VERSION_KEY_PREFIX = "shield:permver:";
|
|
4
|
+
/** Default TTL of a cached version (the bound on a lost publish). */
|
|
5
|
+
export declare const DEFAULT_PERMISSION_VERSION_TTL_MS = 60000;
|
|
6
|
+
/** The read side the gate-5 decorator needs. */
|
|
7
|
+
export interface PermissionVersionReader {
|
|
8
|
+
/**
|
|
9
|
+
* The cached version, or `null` on a MISS (no key / unreadable value). A Redis ERROR throws —
|
|
10
|
+
* the caller fails closed, it never falls back to the DB on an outage.
|
|
11
|
+
*/
|
|
12
|
+
get(tenantId: string): Promise<number | null>;
|
|
13
|
+
/** Raise the cached version to at least `version` (monotonic). A Redis error throws. */
|
|
14
|
+
raise(tenantId: string, version: number): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** The write side the permission-version bumper needs (after its commit). Never throws. */
|
|
17
|
+
export interface PermissionVersionPublisher {
|
|
18
|
+
publish(tenantId: string, version: number): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export interface RedisPermissionVersionCacheOptions {
|
|
21
|
+
/** TTL of a cached version in ms (default {@link DEFAULT_PERMISSION_VERSION_TTL_MS}). */
|
|
22
|
+
readonly ttlMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class RedisPermissionVersionCache implements PermissionVersionReader, PermissionVersionPublisher {
|
|
25
|
+
private readonly redis;
|
|
26
|
+
private readonly logger;
|
|
27
|
+
private readonly ttlMs;
|
|
28
|
+
constructor(redis: Redis, options?: RedisPermissionVersionCacheOptions);
|
|
29
|
+
get(tenantId: string): Promise<number | null>;
|
|
30
|
+
raise(tenantId: string, version: number): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Best-effort publish after a committed bump. A failure is logged (no tenant id, no value) and
|
|
33
|
+
* swallowed: the bump itself is durable, and the TTL bounds how long the old cached version can
|
|
34
|
+
* be served.
|
|
35
|
+
*/
|
|
36
|
+
publish(tenantId: string, version: number): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=permission-version-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-version-cache.d.ts","sourceRoot":"","sources":["../../src/authz/permission-version-cache.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC;AAEjC,0DAA0D;AAC1D,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAE/D,qEAAqE;AACrE,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAcxD,gDAAgD;AAChD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,wFAAwF;IACxF,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,2FAA2F;AAC3F,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D;AAOD,MAAM,WAAW,kCAAkC;IACjD,yFAAyF;IACzF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,2BACX,YAAW,uBAAuB,EAAE,0BAA0B;IAM5D,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAChE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAGZ,KAAK,EAAE,KAAK,EAC7B,OAAO,GAAE,kCAAuC;IAS5C,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAO7C,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7D;;;;OAIG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAShE"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisPermissionVersionCache = exports.DEFAULT_PERMISSION_VERSION_TTL_MS = exports.PERMISSION_VERSION_KEY_PREFIX = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The Redis-held permission version (ADR-0035, GAPS #6). Gate 5 compares a session's cached facts
|
|
6
|
+
* against the tenant's CURRENT permission version on every classified request; reading it from
|
|
7
|
+
* MariaDB put a DB query on the request path (invariant #12). This cache holds the version under
|
|
8
|
+
* `shield:permver:<tenantId>`:
|
|
9
|
+
*
|
|
10
|
+
* - MariaDB `shield_authz.authz_permission_version` stays the source of truth.
|
|
11
|
+
* - WRITES ARE MONOTONIC: one Lua script sets the value only when the key is absent or holds a
|
|
12
|
+
* LOWER version, so an out-of-order write (a reader refilling an older value, two bumps
|
|
13
|
+
* publishing in reverse) can never move the cached version backwards and hide a bump.
|
|
14
|
+
* - Every write sets a TTL (default 60 s). A lost publish therefore delays a bump by at most the
|
|
15
|
+
* TTL; the next miss refills from the DB.
|
|
16
|
+
*
|
|
17
|
+
* The bumper (identity) publishes after its commit; the gate-5 decorator (`wire-record-
|
|
18
|
+
* authorization.ts`) reads, refills on a miss, and fails closed on an error. This file is
|
|
19
|
+
* deliberately free of runtime `@astram/core` imports (it is constructed in `AuthModule`).
|
|
20
|
+
*
|
|
21
|
+
* ⛔ No secret here: tenant ids and integers only. Logs carry no tenant id.
|
|
22
|
+
*/
|
|
23
|
+
const common_1 = require("@nestjs/common");
|
|
24
|
+
/** Redis key prefix for a tenant's permission version. */
|
|
25
|
+
exports.PERMISSION_VERSION_KEY_PREFIX = 'shield:permver:';
|
|
26
|
+
/** Default TTL of a cached version (the bound on a lost publish). */
|
|
27
|
+
exports.DEFAULT_PERMISSION_VERSION_TTL_MS = 60_000;
|
|
28
|
+
/** `SET` the version only when the key is absent, unreadable, or LOWER. Returns 1 when written. */
|
|
29
|
+
const RAISE_SCRIPT = `
|
|
30
|
+
local cur = redis.call('GET', KEYS[1])
|
|
31
|
+
local n = tonumber(ARGV[1])
|
|
32
|
+
local c = cur and tonumber(cur)
|
|
33
|
+
if (not c) or c < n then
|
|
34
|
+
redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
|
|
35
|
+
return 1
|
|
36
|
+
end
|
|
37
|
+
return 0
|
|
38
|
+
`;
|
|
39
|
+
/** A valid version: a non-negative safe integer. */
|
|
40
|
+
function isVersion(v) {
|
|
41
|
+
return typeof v === 'number' && Number.isSafeInteger(v) && v >= 0;
|
|
42
|
+
}
|
|
43
|
+
class RedisPermissionVersionCache {
|
|
44
|
+
redis;
|
|
45
|
+
logger = new common_1.Logger('ShieldPermissionVersion');
|
|
46
|
+
ttlMs;
|
|
47
|
+
constructor(redis, options = {}) {
|
|
48
|
+
this.redis = redis;
|
|
49
|
+
const ttl = options.ttlMs;
|
|
50
|
+
this.ttlMs =
|
|
51
|
+
ttl !== undefined && Number.isSafeInteger(ttl) && ttl > 0
|
|
52
|
+
? ttl
|
|
53
|
+
: exports.DEFAULT_PERMISSION_VERSION_TTL_MS;
|
|
54
|
+
}
|
|
55
|
+
async get(tenantId) {
|
|
56
|
+
const raw = await this.redis.get(`${exports.PERMISSION_VERSION_KEY_PREFIX}${tenantId}`);
|
|
57
|
+
if (raw === null)
|
|
58
|
+
return null;
|
|
59
|
+
const v = Number(raw);
|
|
60
|
+
return isVersion(v) ? v : null;
|
|
61
|
+
}
|
|
62
|
+
async raise(tenantId, version) {
|
|
63
|
+
if (!isVersion(version))
|
|
64
|
+
return;
|
|
65
|
+
await this.redis.eval(RAISE_SCRIPT, 1, `${exports.PERMISSION_VERSION_KEY_PREFIX}${tenantId}`, String(version), String(this.ttlMs));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Best-effort publish after a committed bump. A failure is logged (no tenant id, no value) and
|
|
69
|
+
* swallowed: the bump itself is durable, and the TTL bounds how long the old cached version can
|
|
70
|
+
* be served.
|
|
71
|
+
*/
|
|
72
|
+
async publish(tenantId, version) {
|
|
73
|
+
try {
|
|
74
|
+
await this.raise(tenantId, version);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
this.logger.warn('permission version publish to Redis failed; cached sessions refresh within the version TTL');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.RedisPermissionVersionCache = RedisPermissionVersionCache;
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* consumer opts into `recordAuthorization`. This file (and thus core) loads only then.
|
|
25
25
|
*/
|
|
26
26
|
import type { DecisionAuthority, ModuleRegistry, PermissionFactsPort, Principal, SessionFactsReader } from '@astram/core';
|
|
27
|
+
import type { PermissionVersionReader } from './permission-version-cache.js';
|
|
27
28
|
import type { ObjectType } from '@astram/engine';
|
|
28
29
|
import { type RuleQueryable } from './mariadb-record-rule-adapter.js';
|
|
29
30
|
import { type OperationManifestEntry } from './manifest-operation-authority-adapter.js';
|
|
@@ -63,7 +64,30 @@ export interface RecordAuthorizationInputs {
|
|
|
63
64
|
* to what the consumer's `buildDecisionAuthority` did before Phase 5 (same adapters, same
|
|
64
65
|
* pool, same registry, same ports) — this is a wiring MOVE, not a logic change.
|
|
65
66
|
*/
|
|
66
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Wrap the app's {@link PermissionFactsPort} so gate 5 reads the tenant's CURRENT permission version
|
|
69
|
+
* from Redis (ADR-0035, GAPS #6 — invariant #12: the request path is Redis-only):
|
|
70
|
+
*
|
|
71
|
+
* - HIT → that version; the app port (a DB read) is not called.
|
|
72
|
+
* - MISS (no key: first use, TTL expiry, a Redis restart) → the app port answers from the source of
|
|
73
|
+
* truth and the value is written back (monotonic). A miss is "Redis answered: no key", so this is
|
|
74
|
+
* a cache fill, not an outage fallback.
|
|
75
|
+
* - Redis ERROR → {@link AuthorizationUnavailableError} (fail closed; never served from the DB).
|
|
76
|
+
*
|
|
77
|
+
* `loadCoarseGrants` is delegated unchanged, and its version is written back too (it was read from
|
|
78
|
+
* the DB at the same moment), so a reload also warms the cache. Write-backs are best-effort.
|
|
79
|
+
*/
|
|
80
|
+
export declare function withRedisPermissionVersion(port: PermissionFactsPort, cache: PermissionVersionReader): PermissionFactsPort;
|
|
81
|
+
/** Options for {@link buildRecordAuthorization} (all optional, additive). */
|
|
82
|
+
export interface BuildRecordAuthorizationOptions {
|
|
83
|
+
/**
|
|
84
|
+
* The Redis permission-version cache (ADR-0035). Given → gate 5 reads the version from Redis
|
|
85
|
+
* (see {@link withRedisPermissionVersion}). Absent (no Redis: `auth: false`) → the app port's
|
|
86
|
+
* own version read, unchanged.
|
|
87
|
+
*/
|
|
88
|
+
readonly versionCache?: PermissionVersionReader;
|
|
89
|
+
}
|
|
90
|
+
export declare function buildRecordAuthorization(inputs: RecordAuthorizationInputs, options?: BuildRecordAuthorizationOptions): DecisionAuthority;
|
|
67
91
|
/**
|
|
68
92
|
* The engine-facing list-scope fn (Task 2.2). Given the engine's registered
|
|
69
93
|
* {@link ObjectType}, the requesting {@link Principal}, and the list action key, it returns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wire-record-authorization.d.ts","sourceRoot":"","sources":["../../src/authz/wire-record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EAEd,mBAAmB,EACnB,SAAS,EAET,kBAAkB,EACnB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"wire-record-authorization.d.ts","sourceRoot":"","sources":["../../src/authz/wire-record-authorization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EAEd,mBAAmB,EACnB,SAAS,EAET,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAMtB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,kCAAkC,CAAC;AAM1C,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,2CAA2C,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,uFAAuF;IACvF,QAAQ,CAAC,eAAe,EAAE,mBAAmB,CAAC;IAC9C,6FAA6F;IAC7F,QAAQ,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAC1C,6GAA6G;IAC7G,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAC;CAChE;AAED;;;;;GAKG;AACH;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,mBAAmB,EACzB,KAAK,EAAE,uBAAuB,GAC7B,mBAAmB,CA2BrB;AAED,6EAA6E;AAC7E,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACjD;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,yBAAyB,EACjC,OAAO,GAAE,+BAAoC,GAC5C,iBAAiB,CA2BnB;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,yBAAyB,GAAG,WAAW,CAuE/E"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withRedisPermissionVersion = withRedisPermissionVersion;
|
|
3
4
|
exports.buildRecordAuthorization = buildRecordAuthorization;
|
|
4
5
|
exports.buildListScopeFn = buildListScopeFn;
|
|
5
6
|
const core_1 = require("@astram/core");
|
|
@@ -8,6 +9,7 @@ const mariadb_record_share_adapter_js_1 = require("./mariadb-record-share-adapte
|
|
|
8
9
|
const mariadb_cross_tenant_grant_adapter_js_1 = require("./mariadb-cross-tenant-grant-adapter.js");
|
|
9
10
|
const mariadb_list_scope_adapter_js_1 = require("./mariadb-list-scope-adapter.js");
|
|
10
11
|
const list_scope_sql_js_1 = require("./list-scope-sql.js");
|
|
12
|
+
const session_facts_als_js_1 = require("../request-auth/session-facts-als.js");
|
|
11
13
|
const manifest_operation_authority_adapter_js_1 = require("./manifest-operation-authority-adapter.js");
|
|
12
14
|
/**
|
|
13
15
|
* Construct the four framework-owned record-tier adapters over the supplied pool +
|
|
@@ -15,10 +17,61 @@ const manifest_operation_authority_adapter_js_1 = require("./manifest-operation-
|
|
|
15
17
|
* to what the consumer's `buildDecisionAuthority` did before Phase 5 (same adapters, same
|
|
16
18
|
* pool, same registry, same ports) — this is a wiring MOVE, not a logic change.
|
|
17
19
|
*/
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Wrap the app's {@link PermissionFactsPort} so gate 5 reads the tenant's CURRENT permission version
|
|
22
|
+
* from Redis (ADR-0035, GAPS #6 — invariant #12: the request path is Redis-only):
|
|
23
|
+
*
|
|
24
|
+
* - HIT → that version; the app port (a DB read) is not called.
|
|
25
|
+
* - MISS (no key: first use, TTL expiry, a Redis restart) → the app port answers from the source of
|
|
26
|
+
* truth and the value is written back (monotonic). A miss is "Redis answered: no key", so this is
|
|
27
|
+
* a cache fill, not an outage fallback.
|
|
28
|
+
* - Redis ERROR → {@link AuthorizationUnavailableError} (fail closed; never served from the DB).
|
|
29
|
+
*
|
|
30
|
+
* `loadCoarseGrants` is delegated unchanged, and its version is written back too (it was read from
|
|
31
|
+
* the DB at the same moment), so a reload also warms the cache. Write-backs are best-effort.
|
|
32
|
+
*/
|
|
33
|
+
function withRedisPermissionVersion(port, cache) {
|
|
34
|
+
const warm = async (tenantId, version) => {
|
|
35
|
+
try {
|
|
36
|
+
await cache.raise(tenantId, version);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Best-effort: the answer already came from the source of truth.
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
currentPermissionsVersion: async (principalId, tenantId) => {
|
|
44
|
+
let cached;
|
|
45
|
+
try {
|
|
46
|
+
cached = await cache.get(tenantId);
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
throw new core_1.AuthorizationUnavailableError('permission version cache unavailable', cause);
|
|
50
|
+
}
|
|
51
|
+
if (cached !== null)
|
|
52
|
+
return cached;
|
|
53
|
+
const version = await port.currentPermissionsVersion(principalId, tenantId);
|
|
54
|
+
await warm(tenantId, version);
|
|
55
|
+
return version;
|
|
56
|
+
},
|
|
57
|
+
loadCoarseGrants: async (principalId, tenantId) => {
|
|
58
|
+
const grants = await port.loadCoarseGrants(principalId, tenantId);
|
|
59
|
+
await warm(tenantId, grants.permissionsVersion);
|
|
60
|
+
return grants;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function buildRecordAuthorization(inputs, options = {}) {
|
|
65
|
+
const facts = options.versionCache !== undefined
|
|
66
|
+
? withRedisPermissionVersion(inputs.permissionFacts, options.versionCache)
|
|
67
|
+
: inputs.permissionFacts;
|
|
19
68
|
return (0, core_1.createWiredDecisionAuthority)({
|
|
20
69
|
registry: inputs.registry,
|
|
21
|
-
|
|
70
|
+
// The app-owned port, wrapped so a gate-5 reload inside an enforcement request notifies the
|
|
71
|
+
// guard (security hardening Phase 1C: a permission-version move re-derives the session's
|
|
72
|
+
// scope subtree; ADR-0035: the reloaded grants and live roles are rebound). Results, errors
|
|
73
|
+
// and decisions are unchanged.
|
|
74
|
+
permissionFacts: (0, session_facts_als_js_1.signalGateFiveReloads)(facts),
|
|
22
75
|
sessionFacts: inputs.sessionFacts,
|
|
23
76
|
// ── The FOUR framework-owned record-tier adapters — constructed HERE, and ONLY here.
|
|
24
77
|
// Record-permit ADMIN rules over `shield_authz.record_permission_rule` (widen the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog-reconciler.d.ts","sourceRoot":"","sources":["../../src/completeness/catalog-reconciler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE9E;iFACiF;AACjF,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,iBAAiB;IAC5B;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAgB5E;;;;OAIG;IACG,SAAS,
|
|
1
|
+
{"version":3,"file":"catalog-reconciler.d.ts","sourceRoot":"","sources":["../../src/completeness/catalog-reconciler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE9E;iFACiF;AACjF,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,iBAAiB;IAC5B;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,kBAAkB,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAgB5E;;;;OAIG;IACG,SAAS,CACb,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC;CAoC5B"}
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* 8 × (12 + 8 + 1) + 40 = 208 ≤ 250 ✓
|
|
24
24
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
25
25
|
*/
|
|
26
|
+
import type { SecretsProvider } from '@astram/core';
|
|
26
27
|
export interface AppConfig {
|
|
27
28
|
/** HTTP port the Nest app listens on. */
|
|
28
29
|
readonly port: number;
|
|
@@ -60,7 +61,20 @@ export interface AppConfig {
|
|
|
60
61
|
* `createSessionAuthority({ lifetimes })`.
|
|
61
62
|
*/
|
|
62
63
|
readonly lifetimes: SessionLifetimeOverrides;
|
|
64
|
+
/**
|
|
65
|
+
* Per-client-IP password-login FAILURE limit (security hardening #5, plan D3). Optional
|
|
66
|
+
* so hand-built configs keep compiling; absent ⇒ {@link DEFAULT_LOGIN_IP_LIMIT}
|
|
67
|
+
* (50 failures / 15 min). Read from `LOGIN_IP_MAX_FAILURES` / `LOGIN_IP_WINDOW_MS`.
|
|
68
|
+
*/
|
|
69
|
+
readonly loginIpLimit?: LoginIpLimitConfig;
|
|
63
70
|
}
|
|
71
|
+
/** The per-client-IP login failure limit (failures within `windowMs` that lock the IP). */
|
|
72
|
+
export interface LoginIpLimitConfig {
|
|
73
|
+
readonly maxAttempts: number;
|
|
74
|
+
readonly windowMs: number;
|
|
75
|
+
}
|
|
76
|
+
/** Plan D3 default: 50 failed logins per client IP per 15 minutes. */
|
|
77
|
+
export declare const DEFAULT_LOGIN_IP_LIMIT: LoginIpLimitConfig;
|
|
64
78
|
/**
|
|
65
79
|
* The runtime MariaDB connection descriptor. Non-secret host/port/user/database
|
|
66
80
|
* plus the account password (a secret — supplied via env/secrets-manager, NEVER
|
|
@@ -85,7 +99,15 @@ export interface DbConfig {
|
|
|
85
99
|
export interface RedisConfig {
|
|
86
100
|
/** Full connection url (`redis://[:password@]host:port[/db]`). Preferred. */
|
|
87
101
|
readonly url: string;
|
|
102
|
+
/**
|
|
103
|
+
* Per-command timeout in ms (GAPS #7, `REDIS_COMMAND_TIMEOUT_MS`, default
|
|
104
|
+
* {@link DEFAULT_REDIS_COMMAND_TIMEOUT_MS}). Bounds how long the request path waits on an
|
|
105
|
+
* unreachable Redis before it fails closed. Optional (absent → the default).
|
|
106
|
+
*/
|
|
107
|
+
readonly commandTimeoutMs?: number;
|
|
88
108
|
}
|
|
109
|
+
/** Default Redis per-command timeout (GAPS #7): the request path fails closed within ~2 s. */
|
|
110
|
+
export declare const DEFAULT_REDIS_COMMAND_TIMEOUT_MS = 2000;
|
|
89
111
|
/**
|
|
90
112
|
* Optional per-deployment overrides for the three ADR-0015 session lifetimes.
|
|
91
113
|
* Shape mirrors `@astram/core`'s `Partial<SessionLifetimeConfig>` so it is passed
|
|
@@ -143,6 +165,13 @@ export interface RuntimeAccountPools {
|
|
|
143
165
|
export declare const DEFAULT_POOL_BUDGET: PoolBudgetConfig;
|
|
144
166
|
/** Upper bound the schema-service pool must never exceed (ADR-0018 §2: 1–2). */
|
|
145
167
|
export declare const SCHEMA_SVC_POOL_HARD_CAP = 2;
|
|
168
|
+
/**
|
|
169
|
+
* `true` ONLY when the RAW `NODE_ENV` is explicitly `'development'` or `'test'`.
|
|
170
|
+
* Unset, blank, `'production'`, `'staging'` or anything else ⇒ `false`. Used to gate
|
|
171
|
+
* dev-only defaults that would leak secrets in a real deployment (fail closed: an
|
|
172
|
+
* unset NODE_ENV is treated as "not dev").
|
|
173
|
+
*/
|
|
174
|
+
export declare function isDevOrTestNodeEnv(raw: string | undefined): boolean;
|
|
146
175
|
/**
|
|
147
176
|
* Build the {@link PoolBudgetConfig} from env with documented defaults.
|
|
148
177
|
* Field ↔ env name:
|
|
@@ -187,6 +216,14 @@ export declare function loadRedisConfig(env?: NodeJS.ProcessEnv): RedisConfig;
|
|
|
187
216
|
* refreshAbsoluteCapMs ← REFRESH_ABSOLUTE_CAP_MS
|
|
188
217
|
*/
|
|
189
218
|
export declare function loadSessionLifetimes(env?: NodeJS.ProcessEnv): SessionLifetimeOverrides;
|
|
219
|
+
/**
|
|
220
|
+
* Build the per-client-IP login failure limit from env (plan D3). Each field falls back
|
|
221
|
+
* to {@link DEFAULT_LOGIN_IP_LIMIT} when absent/blank/invalid.
|
|
222
|
+
* Field ↔ env name:
|
|
223
|
+
* maxAttempts ← LOGIN_IP_MAX_FAILURES (default 50)
|
|
224
|
+
* windowMs ← LOGIN_IP_WINDOW_MS (default 900000 = 15 min)
|
|
225
|
+
*/
|
|
226
|
+
export declare function loadLoginIpLimit(env?: NodeJS.ProcessEnv): LoginIpLimitConfig;
|
|
190
227
|
/**
|
|
191
228
|
* Read the refresh-token HMAC key from env (raw; `''` when absent/blank). Total —
|
|
192
229
|
* does NOT throw, so {@link loadAppConfig} stays boot-safe for a non-auth scaffold.
|
|
@@ -222,6 +259,21 @@ export declare function assertRefreshHmacKey(key: string): string;
|
|
|
222
259
|
* auth-composition time (see its doc).
|
|
223
260
|
*/
|
|
224
261
|
export declare function loadAppConfig(env?: NodeJS.ProcessEnv): AppConfig;
|
|
262
|
+
/**
|
|
263
|
+
* ADR-0032 secrets integration — build the {@link AppConfig} AFTER resolving any `secret-ref:<path>`
|
|
264
|
+
* env references via the injected `SecretsProvider`, so an app can hold only REFERENCES in its env
|
|
265
|
+
* (safe to commit/log) and the real credentials are fetched at boot.
|
|
266
|
+
*
|
|
267
|
+
* - With a `provider`: every `secret-ref:` value in `env` is resolved (fail-closed — a missing
|
|
268
|
+
* reference aborts boot; a value is never logged), producing a resolved env the pure
|
|
269
|
+
* {@link loadAppConfig} consumes unchanged.
|
|
270
|
+
* - Without a `provider`: identical to `loadAppConfig(env)` — literal env, unchanged behaviour
|
|
271
|
+
* (backward compatible; references would pass through as literals, which fail loudly at connect
|
|
272
|
+
* time rather than silently — the operator is expected to wire a provider when using references).
|
|
273
|
+
*
|
|
274
|
+
* The core resolver (`resolveSecretRefs`) does the work; this is the guard-side boot seam.
|
|
275
|
+
*/
|
|
276
|
+
export declare function loadAppConfigAsync(provider?: SecretsProvider, env?: NodeJS.ProcessEnv): Promise<AppConfig>;
|
|
225
277
|
/** Result of evaluating a {@link PoolBudgetConfig} against the ADR-0018 budget. */
|
|
226
278
|
export interface PoolBudgetEvaluation {
|
|
227
279
|
/** Σ(per-account runtime pool max) for a single worker. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,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;
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/config/app-config.ts"],"names":[],"mappings":"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"}
|