@astram/guard 0.16.0 → 0.18.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 +10 -6
- package/dist/auth/auth.controller.d.ts +3 -1
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +13 -11
- package/dist/auth/auth.module.d.ts +14 -2
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +54 -7
- package/dist/auth/confirm-email.controller.d.ts +3 -1
- package/dist/auth/confirm-email.controller.d.ts.map +1 -1
- package/dist/auth/confirm-email.controller.js +9 -5
- package/dist/auth/email-change-messages.d.ts +2 -0
- package/dist/auth/email-change-messages.d.ts.map +1 -1
- package/dist/auth/email-change-messages.js +2 -0
- package/dist/auth/password-reset-message.d.ts +7 -0
- package/dist/auth/password-reset-message.d.ts.map +1 -0
- package/dist/auth/password-reset-message.js +17 -0
- package/dist/auth/ports/email-sender.d.ts +7 -7
- package/dist/auth/ports/email-sender.d.ts.map +1 -1
- package/dist/auth/tokens.d.ts +14 -0
- package/dist/auth/tokens.d.ts.map +1 -1
- package/dist/auth/tokens.js +15 -1
- package/dist/authentication/argon2id-password-hasher.d.ts +3 -0
- package/dist/authentication/argon2id-password-hasher.d.ts.map +1 -1
- package/dist/authentication/argon2id-password-hasher.js +21 -0
- package/dist/authentication/credential-provisioner.d.ts +35 -0
- package/dist/authentication/credential-provisioner.d.ts.map +1 -0
- package/dist/authentication/credential-provisioner.js +16 -0
- package/dist/authentication/mariadb-credential-provisioner.d.ts +27 -0
- package/dist/authentication/mariadb-credential-provisioner.d.ts.map +1 -0
- package/dist/authentication/mariadb-credential-provisioner.js +95 -0
- package/dist/authz/operation-marker-check.d.ts +14 -0
- package/dist/authz/operation-marker-check.d.ts.map +1 -0
- package/dist/authz/operation-marker-check.js +25 -0
- package/dist/authz/wire-record-authorization.d.ts.map +1 -1
- package/dist/authz/wire-record-authorization.js +3 -2
- package/dist/config/message-config.d.ts +70 -0
- package/dist/config/message-config.d.ts.map +1 -0
- package/dist/config/message-config.js +249 -0
- package/dist/config/reserved-principal-ids.d.ts +14 -0
- package/dist/config/reserved-principal-ids.d.ts.map +1 -0
- package/dist/config/reserved-principal-ids.js +24 -0
- package/dist/config/root-ids-from-env.d.ts +2 -0
- package/dist/config/root-ids-from-env.d.ts.map +1 -0
- package/dist/config/root-ids-from-env.js +41 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/internal.d.ts +11 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +15 -1
- package/dist/messaging/deliver-email.d.ts +10 -0
- package/dist/messaging/deliver-email.d.ts.map +1 -0
- package/dist/messaging/deliver-email.js +19 -0
- package/dist/messaging/email-delivery.d.ts +42 -0
- package/dist/messaging/email-delivery.d.ts.map +1 -0
- package/dist/messaging/email-delivery.js +117 -0
- package/dist/messaging/message-dispatcher.d.ts +66 -0
- package/dist/messaging/message-dispatcher.d.ts.map +1 -0
- package/dist/messaging/message-dispatcher.js +133 -0
- package/dist/messaging/recipient-mask.d.ts +7 -0
- package/dist/messaging/recipient-mask.d.ts.map +1 -0
- package/dist/messaging/recipient-mask.js +15 -0
- package/dist/module/shield.module.d.ts +5 -3
- package/dist/module/shield.module.d.ts.map +1 -1
- package/dist/module/shield.module.js +4 -0
- package/package.json +10 -5
package/dist/auth/tokens.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
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.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_AUDIT_SINK = exports.SHIELD_PROFILE_WRITER = exports.SHIELD_RECOVERY_CODE_STORE = 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_DB_POOL = exports.SHIELD_PASSWORD_HASHER = void 0;
|
|
26
|
+
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_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_DB_POOL = exports.SHIELD_PASSWORD_HASHER = void 0;
|
|
27
27
|
var argon2id_password_hasher_js_1 = require("../authentication/argon2id-password-hasher.js");
|
|
28
28
|
Object.defineProperty(exports, "SHIELD_PASSWORD_HASHER", { enumerable: true, get: function () { return argon2id_password_hasher_js_1.SHIELD_PASSWORD_HASHER; } });
|
|
29
29
|
/**
|
|
@@ -143,6 +143,20 @@ exports.SHIELD_CONFIRM_EMAIL_LIMITER = Symbol('SHIELD_CONFIRM_EMAIL_LIMITER');
|
|
|
143
143
|
* consumer delivers it. Consumed by forgot-password.
|
|
144
144
|
*/
|
|
145
145
|
exports.SHIELD_EMAIL_SENDER = Symbol('SHIELD_EMAIL_SENDER');
|
|
146
|
+
/**
|
|
147
|
+
* The {@link MessageDispatcher} (G-010, `../messaging/message-dispatcher.ts`): the one exit for
|
|
148
|
+
* framework messages. Bounds, audits (`message.sent` / `message.failed`) and never throws. Every
|
|
149
|
+
* framework send uses it; `@astram/identity`'s invite injects it too.
|
|
150
|
+
*/
|
|
151
|
+
exports.SHIELD_MESSAGE_DISPATCHER = Symbol('SHIELD_MESSAGE_DISPATCHER');
|
|
152
|
+
/**
|
|
153
|
+
* A factory for guard's Redis-backed `AttemptLimiter` (G-010 P-5): `(cfg) => AttemptLimiter`. For
|
|
154
|
+
* `@astram/identity`'s invite rate limits; exported from `AuthModule` and `@astram/guard/internal`
|
|
155
|
+
* so identity builds limiters without ever holding the raw Redis client.
|
|
156
|
+
*/
|
|
157
|
+
exports.SHIELD_ATTEMPT_LIMITER_FACTORY = Symbol('SHIELD_ATTEMPT_LIMITER_FACTORY');
|
|
158
|
+
/** Internal: the bound email provider and its per-send bound (`EmailDelivery`). Not exported. */
|
|
159
|
+
exports.SHIELD_EMAIL_DELIVERY = Symbol('SHIELD_EMAIL_DELIVERY');
|
|
146
160
|
/**
|
|
147
161
|
* The `RecoveryCodeStore` (guard port, `../auth/ports/recovery-code-store.ts`) —
|
|
148
162
|
* persists Argon2-hashed one-time MFA recovery codes. Consumed by MFA enrollment.
|
|
@@ -29,6 +29,9 @@ export interface Argon2idParams {
|
|
|
29
29
|
readonly timeCost?: number;
|
|
30
30
|
readonly parallelism?: number;
|
|
31
31
|
}
|
|
32
|
+
/** `!locked:` + a real argon2id hash of 32 random bytes that are never kept. */
|
|
33
|
+
export declare function lockedPasswordHash(): Promise<string>;
|
|
34
|
+
export declare function isLockedHash(hash: string): boolean;
|
|
32
35
|
/** The DI token consumers/ShieldModule bind the concrete hasher under. */
|
|
33
36
|
export declare const SHIELD_PASSWORD_HASHER: unique symbol;
|
|
34
37
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"argon2id-password-hasher.d.ts","sourceRoot":"","sources":["../../src/authentication/argon2id-password-hasher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"argon2id-password-hasher.d.ts","sourceRoot":"","sources":["../../src/authentication/argon2id-password-hasher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAWD,gFAAgF;AAChF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAM1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,eAAmC,CAAC;AAEvE;;;;GAIG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;gBAEnD,MAAM,GAAE,cAAmB;IASvC,8EAA8E;IACxE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C;;;;OAIG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAahE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,GAAE,cAAmB,GAAG;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,sBAAsB,CAAC;CAClC,CAKA"}
|
|
@@ -49,13 +49,29 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
49
49
|
})();
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
51
|
exports.Argon2idPasswordHasher = exports.SHIELD_PASSWORD_HASHER = void 0;
|
|
52
|
+
exports.lockedPasswordHash = lockedPasswordHash;
|
|
53
|
+
exports.isLockedHash = isLockedHash;
|
|
52
54
|
exports.argon2idHasherProvider = argon2idHasherProvider;
|
|
55
|
+
const node_crypto_1 = require("node:crypto");
|
|
53
56
|
const argon2 = __importStar(require("argon2"));
|
|
54
57
|
const DEFAULT_PARAMS = {
|
|
55
58
|
memoryCost: 19_456,
|
|
56
59
|
timeCost: 2,
|
|
57
60
|
parallelism: 1,
|
|
58
61
|
};
|
|
62
|
+
/** Prefix marking a credential nobody can authenticate against (set later via reset). */
|
|
63
|
+
const LOCKED_PREFIX = '!locked:';
|
|
64
|
+
/** `!locked:` + a real argon2id hash of 32 random bytes that are never kept. */
|
|
65
|
+
async function lockedPasswordHash() {
|
|
66
|
+
const hash = await argon2.hash((0, node_crypto_1.randomBytes)(32), {
|
|
67
|
+
type: argon2.argon2id,
|
|
68
|
+
...DEFAULT_PARAMS,
|
|
69
|
+
});
|
|
70
|
+
return LOCKED_PREFIX + hash;
|
|
71
|
+
}
|
|
72
|
+
function isLockedHash(hash) {
|
|
73
|
+
return hash.startsWith(LOCKED_PREFIX);
|
|
74
|
+
}
|
|
59
75
|
/** The DI token consumers/ShieldModule bind the concrete hasher under. */
|
|
60
76
|
exports.SHIELD_PASSWORD_HASHER = Symbol('SHIELD_PASSWORD_HASHER');
|
|
61
77
|
/**
|
|
@@ -85,6 +101,11 @@ class Argon2idPasswordHasher {
|
|
|
85
101
|
*/
|
|
86
102
|
async verify(hash, plaintext) {
|
|
87
103
|
try {
|
|
104
|
+
if (isLockedHash(hash)) {
|
|
105
|
+
// Real verify (no timing shortcut) on the remainder, then false regardless of the outcome.
|
|
106
|
+
await argon2.verify(hash.slice(LOCKED_PREFIX.length), plaintext);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
88
109
|
return await argon2.verify(hash, plaintext);
|
|
89
110
|
}
|
|
90
111
|
catch {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CredentialProvisioner — the INSERT-ONLY port that creates a login credential for a new principal
|
|
3
|
+
* (org bootstrap). It never updates or replaces an existing credential: a principal that already has
|
|
4
|
+
* one is refused with {@link CredentialExistsError}, so provisioning can never reset someone's password.
|
|
5
|
+
*/
|
|
6
|
+
/** Caller-supplied SQL runner (a transaction connection's `query`); absent ⇒ the pool. */
|
|
7
|
+
export type CredentialQuery = (sql: string, params?: unknown[]) => Promise<unknown>;
|
|
8
|
+
export interface CredentialProvisionInput {
|
|
9
|
+
readonly principalId: string;
|
|
10
|
+
/** Login key (`ic_identity_hint`, the canonical email). */
|
|
11
|
+
readonly hint: string;
|
|
12
|
+
readonly tenantId: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PasswordCredentialProvisionInput extends CredentialProvisionInput {
|
|
15
|
+
readonly password: string;
|
|
16
|
+
}
|
|
17
|
+
/** `none` = no credential row; `locked` = unusable placeholder hash; `usable` = a real password hash. */
|
|
18
|
+
export type CredentialState = 'none' | 'locked' | 'usable';
|
|
19
|
+
export interface CredentialProvisioner {
|
|
20
|
+
/** Insert a credential no password verifies against (set later via reset). */
|
|
21
|
+
provisionLocked(input: CredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
22
|
+
/** Insert an Argon2id credential for `password` (≥ 8 chars). */
|
|
23
|
+
provisionPassword(input: PasswordCredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
24
|
+
stateOf(principalId: string, query?: CredentialQuery): Promise<CredentialState>;
|
|
25
|
+
/**
|
|
26
|
+
* Batch {@link stateOf} (G-010 P-6): one query per 500 ids, an entry for EVERY requested id
|
|
27
|
+
* (`none` when no row). Empty input ⇒ empty map, no query. Duplicate ids are fine.
|
|
28
|
+
*/
|
|
29
|
+
statesOf(principalIds: readonly string[], query?: CredentialQuery): Promise<Map<string, CredentialState>>;
|
|
30
|
+
}
|
|
31
|
+
/** The principal or login hint already has a credential. Fixed message: no hint, no driver text. */
|
|
32
|
+
export declare class CredentialExistsError extends Error {
|
|
33
|
+
constructor();
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=credential-provisioner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-provisioner.d.ts","sourceRoot":"","sources":["../../src/authentication/credential-provisioner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,0FAA0F;AAC1F,MAAM,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gCAAiC,SAAQ,wBAAwB;IAChF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,yGAAyG;AACzG,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,eAAe,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,gEAAgE;IAChE,iBAAiB,CACf,KAAK,EAAE,gCAAgC,EACvC,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAChF;;;OAGG;IACH,QAAQ,CACN,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;CAC1C;AAED,oGAAoG;AACpG,qBAAa,qBAAsB,SAAQ,KAAK;;CAK/C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CredentialProvisioner — the INSERT-ONLY port that creates a login credential for a new principal
|
|
4
|
+
* (org bootstrap). It never updates or replaces an existing credential: a principal that already has
|
|
5
|
+
* one is refused with {@link CredentialExistsError}, so provisioning can never reset someone's password.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.CredentialExistsError = void 0;
|
|
9
|
+
/** The principal or login hint already has a credential. Fixed message: no hint, no driver text. */
|
|
10
|
+
class CredentialExistsError extends Error {
|
|
11
|
+
constructor() {
|
|
12
|
+
super('A credential already exists for this principal or login');
|
|
13
|
+
this.name = 'CredentialExistsError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.CredentialExistsError = CredentialExistsError;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MariaDbCredentialProvisioner — the framework CredentialProvisioner over `identity_credential`.
|
|
3
|
+
* INSERT only (never UPDATE/upsert): an existing principal, or a duplicate login hint (PK), is a
|
|
4
|
+
* {@link CredentialExistsError}. Stores only an Argon2id hash (or the locked placeholder); never logs it.
|
|
5
|
+
* Pass `query` to run inside the caller's transaction connection.
|
|
6
|
+
*/
|
|
7
|
+
import type { Pool } from 'mariadb';
|
|
8
|
+
import { type CredentialProvisioner, type CredentialProvisionInput, type CredentialQuery, type CredentialState, type PasswordCredentialProvisionInput } from './credential-provisioner.js';
|
|
9
|
+
import { type FrameworkSchemaNames } from '../config/framework-schema-names.js';
|
|
10
|
+
export declare class MariaDbCredentialProvisioner implements CredentialProvisioner {
|
|
11
|
+
private readonly pool;
|
|
12
|
+
private readonly now;
|
|
13
|
+
/** `<identity>`.`identity_credential`, resolved at construction. */
|
|
14
|
+
private readonly table;
|
|
15
|
+
private readonly hasher;
|
|
16
|
+
constructor(pool: Pool, now?: () => number,
|
|
17
|
+
/** Framework database names; absent ⇒ resolved from `SHIELD_DB_NAMESPACE` here, once. */
|
|
18
|
+
schemas?: FrameworkSchemaNames);
|
|
19
|
+
provisionLocked(input: CredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
20
|
+
provisionPassword(input: PasswordCredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
21
|
+
stateOf(principalId: string, query?: CredentialQuery): Promise<CredentialState>;
|
|
22
|
+
statesOf(principalIds: readonly string[], query?: CredentialQuery): Promise<Map<string, CredentialState>>;
|
|
23
|
+
private validate;
|
|
24
|
+
private run;
|
|
25
|
+
private insert;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=mariadb-credential-provisioner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mariadb-credential-provisioner.d.ts","sourceRoot":"","sources":["../../src/authentication/mariadb-credential-provisioner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gCAAgC,EACtC,MAAM,6BAA6B,CAAC;AAMrC,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAkB7C,qBAAa,4BAA6B,YAAW,qBAAqB;IAMtE,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAGpC,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,MAAM,MAAiB;IAC7C,yFAAyF;IACzF,OAAO,CAAC,EAAE,oBAAoB;IAQ1B,eAAe,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxF,iBAAiB,CACrB,KAAK,EAAE,gCAAgC,EACvC,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,IAAI,CAAC;IAQV,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAU/E,QAAQ,CACZ,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAqBxC,OAAO,CAAC,QAAQ;IAMhB,OAAO,CAAC,GAAG;YAIG,MAAM;CA0BrB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MariaDbCredentialProvisioner = void 0;
|
|
4
|
+
const credential_provisioner_js_1 = require("./credential-provisioner.js");
|
|
5
|
+
const argon2id_password_hasher_js_1 = require("./argon2id-password-hasher.js");
|
|
6
|
+
const framework_schema_names_js_1 = require("../config/framework-schema-names.js");
|
|
7
|
+
const MIN_PASSWORD_LENGTH = 8;
|
|
8
|
+
const ER_DUP_ENTRY = 1062;
|
|
9
|
+
/** Max ids bound into one `statesOf` `IN (...)`. */
|
|
10
|
+
const STATES_CHUNK = 500;
|
|
11
|
+
function requireText(value, name) {
|
|
12
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
13
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Locked placeholder ⇒ `locked`; any other stored hash ⇒ `usable`. The hash never leaves here. */
|
|
17
|
+
function classify(hash) {
|
|
18
|
+
return (0, argon2id_password_hasher_js_1.isLockedHash)(hash) ? 'locked' : 'usable';
|
|
19
|
+
}
|
|
20
|
+
class MariaDbCredentialProvisioner {
|
|
21
|
+
pool;
|
|
22
|
+
now;
|
|
23
|
+
/** `<identity>`.`identity_credential`, resolved at construction. */
|
|
24
|
+
table;
|
|
25
|
+
hasher = new argon2id_password_hasher_js_1.Argon2idPasswordHasher();
|
|
26
|
+
constructor(pool, now = Date.now,
|
|
27
|
+
/** Framework database names; absent ⇒ resolved from `SHIELD_DB_NAMESPACE` here, once. */
|
|
28
|
+
schemas) {
|
|
29
|
+
this.pool = pool;
|
|
30
|
+
this.now = now;
|
|
31
|
+
this.table = (0, framework_schema_names_js_1.qualifiedTableName)((0, framework_schema_names_js_1.schemaNamesAtConstruction)(schemas).identity, 'identity_credential');
|
|
32
|
+
}
|
|
33
|
+
async provisionLocked(input, query) {
|
|
34
|
+
this.validate(input);
|
|
35
|
+
await this.insert(input, await (0, argon2id_password_hasher_js_1.lockedPasswordHash)(), query);
|
|
36
|
+
}
|
|
37
|
+
async provisionPassword(input, query) {
|
|
38
|
+
this.validate(input);
|
|
39
|
+
if (typeof input.password !== 'string' || input.password.length < MIN_PASSWORD_LENGTH) {
|
|
40
|
+
throw new Error(`password must be at least ${MIN_PASSWORD_LENGTH} characters`);
|
|
41
|
+
}
|
|
42
|
+
await this.insert(input, await this.hasher.hash(input.password), query);
|
|
43
|
+
}
|
|
44
|
+
async stateOf(principalId, query) {
|
|
45
|
+
const rows = (await this.run(query)(`SELECT ic_password_hash FROM ${this.table} WHERE ic_principal_id = ? LIMIT 1`, [principalId]));
|
|
46
|
+
const row = rows[0];
|
|
47
|
+
if (!row)
|
|
48
|
+
return 'none';
|
|
49
|
+
return classify(row.ic_password_hash);
|
|
50
|
+
}
|
|
51
|
+
async statesOf(principalIds, query) {
|
|
52
|
+
const ids = [...new Set(principalIds)];
|
|
53
|
+
const out = new Map(ids.map((id) => [id, 'none']));
|
|
54
|
+
const run = this.run(query);
|
|
55
|
+
for (let i = 0; i < ids.length; i += STATES_CHUNK) {
|
|
56
|
+
const chunk = ids.slice(i, i + STATES_CHUNK);
|
|
57
|
+
const rows = (await run(`SELECT ic_principal_id, ic_password_hash FROM ${this.table} WHERE ic_principal_id IN (${chunk
|
|
58
|
+
.map(() => '?')
|
|
59
|
+
.join(', ')})`, chunk));
|
|
60
|
+
for (const row of rows) {
|
|
61
|
+
if (out.has(row.ic_principal_id)) {
|
|
62
|
+
out.set(row.ic_principal_id, classify(row.ic_password_hash));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
validate(input) {
|
|
69
|
+
requireText(input.principalId, 'principalId');
|
|
70
|
+
requireText(input.hint, 'hint');
|
|
71
|
+
requireText(input.tenantId, 'tenantId');
|
|
72
|
+
}
|
|
73
|
+
run(query) {
|
|
74
|
+
return query ?? ((sql, params) => this.pool.query(sql, params));
|
|
75
|
+
}
|
|
76
|
+
async insert(input, hash, query) {
|
|
77
|
+
const run = this.run(query);
|
|
78
|
+
const existing = (await run(`SELECT ic_identity_hint FROM ${this.table} WHERE ic_principal_id = ? LIMIT 1 FOR UPDATE`, [input.principalId]));
|
|
79
|
+
if (existing.length > 0)
|
|
80
|
+
throw new credential_provisioner_js_1.CredentialExistsError();
|
|
81
|
+
const nowMs = this.now();
|
|
82
|
+
try {
|
|
83
|
+
await run(`INSERT INTO ${this.table}
|
|
84
|
+
(ic_identity_hint, ic_password_hash, ic_principal_id, ic_tenant_id, ic_roles, ic_created_at_ms, ic_updated_at_ms)
|
|
85
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [input.hint, hash, input.principalId, input.tenantId, '[]', nowMs, nowMs]);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err?.errno === ER_DUP_ENTRY) {
|
|
89
|
+
throw new credential_provisioner_js_1.CredentialExistsError();
|
|
90
|
+
}
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.MariaDbCredentialProvisioner = MariaDbCredentialProvisioner;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `withOperationMarkerCheck` — the guard-side seal on core gate 7's delegation marker (G-009 R1-2).
|
|
3
|
+
*
|
|
4
|
+
* Core's `decide()` reads `context.attributes['shield.delegation']` and delegates whatever the
|
|
5
|
+
* principal is. The marker therefore must come only from a principal the engine bound to that
|
|
6
|
+
* exact operation step (`operationOf`, an unforgeable WeakMap binding; the engine's
|
|
7
|
+
* `withDelegation` sets the matching marker). Here, every decision entry the authority exposes
|
|
8
|
+
* (`decide`) passes the attribute through ONLY when `operationOf(principal)` exists and its
|
|
9
|
+
* `operationKey` and `stepKey` equal the marker's; in every other case it is removed before the
|
|
10
|
+
* inner `decide()`, so the baseline decision stands (Shield #1, #9).
|
|
11
|
+
*/
|
|
12
|
+
import type { DecisionAuthority } from '@astram/core';
|
|
13
|
+
export declare function withOperationMarkerCheck(inner: DecisionAuthority): DecisionAuthority;
|
|
14
|
+
//# sourceMappingURL=operation-marker-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-marker-check.d.ts","sourceRoot":"","sources":["../../src/authz/operation-marker-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKtD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAoBpF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withOperationMarkerCheck = withOperationMarkerCheck;
|
|
4
|
+
const engine_1 = require("@astram/engine");
|
|
5
|
+
const MARKER = 'shield.delegation';
|
|
6
|
+
function withOperationMarkerCheck(inner) {
|
|
7
|
+
return {
|
|
8
|
+
decide: (principal, action, resource, context) => {
|
|
9
|
+
const attributes = context.attributes;
|
|
10
|
+
if (attributes === undefined || !(MARKER in attributes)) {
|
|
11
|
+
return inner.decide(principal, action, resource, context);
|
|
12
|
+
}
|
|
13
|
+
const raw = attributes[MARKER];
|
|
14
|
+
const binding = (0, engine_1.operationOf)(principal);
|
|
15
|
+
const { operationKey, stepKey } = typeof raw === 'object' && raw !== null ? raw : {};
|
|
16
|
+
const genuine = binding !== undefined &&
|
|
17
|
+
binding.operationKey === operationKey &&
|
|
18
|
+
binding.stepKey === stepKey;
|
|
19
|
+
if (genuine)
|
|
20
|
+
return inner.decide(principal, action, resource, context);
|
|
21
|
+
const { [MARKER]: _forged, ...rest } = attributes;
|
|
22
|
+
return inner.decide(principal, action, resource, { ...context, attributes: rest });
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -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;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;
|
|
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;AAO1C,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,qCAAqC,CAAC;AAC7C,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;IAC/D;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;CACzC;AAOD;;;;;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,CA8BnB;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,CAyE/E"}
|
|
@@ -8,6 +8,7 @@ const mariadb_record_rule_adapter_js_1 = require("./mariadb-record-rule-adapter.
|
|
|
8
8
|
const mariadb_record_share_adapter_js_1 = require("./mariadb-record-share-adapter.js");
|
|
9
9
|
const mariadb_cross_tenant_grant_adapter_js_1 = require("./mariadb-cross-tenant-grant-adapter.js");
|
|
10
10
|
const mariadb_list_scope_adapter_js_1 = require("./mariadb-list-scope-adapter.js");
|
|
11
|
+
const operation_marker_check_js_1 = require("./operation-marker-check.js");
|
|
11
12
|
const list_scope_sql_js_1 = require("./list-scope-sql.js");
|
|
12
13
|
const session_facts_als_js_1 = require("../request-auth/session-facts-als.js");
|
|
13
14
|
const framework_schema_names_js_1 = require("../config/framework-schema-names.js");
|
|
@@ -71,7 +72,7 @@ function buildRecordAuthorization(inputs, options = {}) {
|
|
|
71
72
|
? withRedisPermissionVersion(inputs.permissionFacts, options.versionCache)
|
|
72
73
|
: inputs.permissionFacts;
|
|
73
74
|
const schema = authzSchemaOf(inputs);
|
|
74
|
-
return (0, core_1.createWiredDecisionAuthority)({
|
|
75
|
+
return (0, operation_marker_check_js_1.withOperationMarkerCheck)((0, core_1.createWiredDecisionAuthority)({
|
|
75
76
|
registry: inputs.registry,
|
|
76
77
|
// The app-owned port, wrapped so a gate-5 reload inside an enforcement request notifies the
|
|
77
78
|
// guard (security hardening Phase 1C: a permission-version move re-derives the session's
|
|
@@ -92,7 +93,7 @@ function buildRecordAuthorization(inputs, options = {}) {
|
|
|
92
93
|
// Delegation / trusted-operation authority over the DECLARED manifest (no DB).
|
|
93
94
|
// Empty ⇒ pass-through. The app supplies the entries; the framework owns the adapter.
|
|
94
95
|
operationAuthority: new manifest_operation_authority_adapter_js_1.ManifestOperationAuthorityAdapter(inputs.operationManifest ?? []),
|
|
95
|
-
});
|
|
96
|
+
}));
|
|
96
97
|
}
|
|
97
98
|
/**
|
|
98
99
|
* Construct the engine-facing {@link ListScopeFn} — the LIST-form sibling to
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** The email providers `EMAIL_PROVIDER` accepts. */
|
|
2
|
+
export declare const EMAIL_PROVIDER_VALUES: readonly ["smtp", "brevo", "sendgrid", "mailgun", "disabled"];
|
|
3
|
+
/** Default and ceiling for `MESSAGE_SEND_TIMEOUT_MS` (R6). */
|
|
4
|
+
export declare const DEFAULT_MESSAGE_SEND_TIMEOUT_MS = 10000;
|
|
5
|
+
export declare const MAX_MESSAGE_SEND_TIMEOUT_MS = 30000;
|
|
6
|
+
export interface EmailFromConfig {
|
|
7
|
+
readonly address: string;
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
}
|
|
10
|
+
/** A validated email provider config, structurally `@astram/messaging`'s `EmailProviderConfig`. */
|
|
11
|
+
export type EmailDeliveryConfig = {
|
|
12
|
+
readonly provider: 'smtp';
|
|
13
|
+
readonly from: EmailFromConfig;
|
|
14
|
+
readonly timeoutMs: number;
|
|
15
|
+
readonly host: string;
|
|
16
|
+
readonly port: number;
|
|
17
|
+
readonly tls: 'starttls' | 'implicit' | 'none';
|
|
18
|
+
readonly user?: string;
|
|
19
|
+
readonly password?: string;
|
|
20
|
+
readonly tlsCaPem?: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly provider: 'brevo' | 'sendgrid';
|
|
23
|
+
readonly from: EmailFromConfig;
|
|
24
|
+
readonly timeoutMs: number;
|
|
25
|
+
readonly apiKey: string;
|
|
26
|
+
readonly baseUrl?: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly provider: 'mailgun';
|
|
29
|
+
readonly from: EmailFromConfig;
|
|
30
|
+
readonly timeoutMs: number;
|
|
31
|
+
readonly apiKey: string;
|
|
32
|
+
readonly domain: string;
|
|
33
|
+
readonly region: 'us' | 'eu';
|
|
34
|
+
readonly baseUrl?: string;
|
|
35
|
+
};
|
|
36
|
+
export interface MessageConfig {
|
|
37
|
+
readonly email: EmailDeliveryConfig | {
|
|
38
|
+
readonly provider: 'disabled';
|
|
39
|
+
};
|
|
40
|
+
/** SMS and WhatsApp have ports only this round (OD-1): always `disabled`. */
|
|
41
|
+
readonly sms: 'disabled';
|
|
42
|
+
readonly whatsapp: 'disabled';
|
|
43
|
+
/** `MESSAGE_BOOT_PROBE=1`: check reachability and credentials at boot, sending nothing (R8). */
|
|
44
|
+
readonly probe: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface LoadMessageConfigOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Provenance check for credential variables outside development/test (`wasResolvedFromSecretRef`).
|
|
49
|
+
* Omitted there: fail closed, every credential counts as a literal and is refused.
|
|
50
|
+
*/
|
|
51
|
+
readonly resolvedFromSecretRef?: (envKey: string, env: Readonly<Record<string, string | undefined>>) => boolean;
|
|
52
|
+
/** Reads `SMTP_TLS_CA` (UTF-8 PEM). Injected so the loader stays testable. */
|
|
53
|
+
readonly readFile: (path: string) => string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Read the message config for `emailSender: 'env'`. Refuses (naming variables only):
|
|
57
|
+
* - `EMAIL_PROVIDER` unset or not one of {@link EMAIL_PROVIDER_VALUES};
|
|
58
|
+
* - `SMS_PROVIDER` / `WHATSAPP_PROVIDER` other than `disabled` (no adapter yet);
|
|
59
|
+
* - a missing or malformed `EMAIL_FROM_ADDRESS`, a control character in `EMAIL_FROM_NAME`;
|
|
60
|
+
* - a missing provider credential, an unresolved `secret-ref:` (naming the `SECRETS_PROVIDER`
|
|
61
|
+
* prerequisite when none is set), or outside development/test a credential not resolved from
|
|
62
|
+
* a `secret-ref:`;
|
|
63
|
+
* - `EMAIL_API_BASE_URL` that is not `https:`, carries credentials, or is set for `smtp`;
|
|
64
|
+
* - Mailgun without a domain, or with a region other than `us` / `eu`;
|
|
65
|
+
* - SMTP without a host, with a bad port, half a login, an unknown `SMTP_TLS`, `none` to a
|
|
66
|
+
* non-loopback host outside development/test, or an unreadable `SMTP_TLS_CA`;
|
|
67
|
+
* - `MESSAGE_SEND_TIMEOUT_MS` outside 1–30000, `MESSAGE_BOOT_PROBE` other than `1` / `0`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function loadMessageConfig(env: NodeJS.ProcessEnv, options: LoadMessageConfigOptions): MessageConfig;
|
|
70
|
+
//# sourceMappingURL=message-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-config.d.ts","sourceRoot":"","sources":["../../src/config/message-config.ts"],"names":[],"mappings":"AAkBA,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,+DAAgE,CAAC;AAEnG,8DAA8D;AAC9D,eAAO,MAAM,+BAA+B,QAAS,CAAC;AACtD,eAAO,MAAM,2BAA2B,QAAS,CAAC;AAElD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,mGAAmG;AACnG,MAAM,MAAM,mBAAmB,GAC3B;IACE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,GACD;IACE,QAAQ,CAAC,QAAQ,EAAE,OAAO,GAAG,UAAU,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,GACD;IACE,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEN,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG;QAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAA;KAAE,CAAC;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,gGAAgG;IAChG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,QAAQ,CAAC,qBAAqB,CAAC,EAAE,CAC/B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,KAC9C,OAAO,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AASD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,OAAO,EAAE,wBAAwB,GAChC,aAAa,CAuDf"}
|