@astram/guard 0.17.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/credential-provisioner.d.ts +5 -0
- package/dist/authentication/credential-provisioner.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-provisioner.d.ts +1 -0
- package/dist/authentication/mariadb-credential-provisioner.d.ts.map +1 -1
- package/dist/authentication/mariadb-credential-provisioner.js +24 -1
- 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/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/internal.d.ts +10 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +13 -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/package.json +10 -5
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type EmailProvider } from '@astram/core';
|
|
2
|
+
import type { FrameworkSchemas } from '@astram/core';
|
|
3
|
+
import type { Pool } from 'mariadb';
|
|
4
|
+
import type { EmailSender } from '../auth/ports/email-sender.js';
|
|
5
|
+
import { type EmailDeliveryConfig } from '../config/message-config.js';
|
|
6
|
+
import { type MessageAuditWriter } from './message-dispatcher.js';
|
|
7
|
+
/** `ShieldModule.forRoot({ emailSender })`. */
|
|
8
|
+
export type EmailSenderOption = EmailSender | 'disabled' | 'env';
|
|
9
|
+
/** What `@astram/messaging` must export for guard (spec P-2). */
|
|
10
|
+
export interface MessagingModule {
|
|
11
|
+
createEmailProvider(config: EmailDeliveryConfig): Promise<EmailProvider>;
|
|
12
|
+
}
|
|
13
|
+
export interface EmailDelivery {
|
|
14
|
+
/** `null` ⇒ mail disabled. */
|
|
15
|
+
readonly provider: EmailProvider | null;
|
|
16
|
+
/** The per-send bound the dispatcher applies. */
|
|
17
|
+
readonly timeoutMs: number;
|
|
18
|
+
}
|
|
19
|
+
export interface BuildEmailProviderDeps {
|
|
20
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
21
|
+
/** Loader for the optional peer (default `require`). */
|
|
22
|
+
readonly load?: (id: string) => unknown;
|
|
23
|
+
readonly readFile?: (path: string) => string;
|
|
24
|
+
}
|
|
25
|
+
export declare function buildEmailProvider(option: EmailSenderOption | undefined, boundSender: EmailSender | null, deps: BuildEmailProviderDeps): Promise<EmailDelivery>;
|
|
26
|
+
/**
|
|
27
|
+
* The link-base boot rule applies only with a real sender (Phase 13 #1). `'env'` is real unless
|
|
28
|
+
* `EMAIL_PROVIDER=disabled`. Read at `forRoot` time, after `resolveBootSecrets()`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isMailDisabled(option: EmailSenderOption | undefined, env: Readonly<Record<string, string | undefined>>): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The `EmailSender` bound under `SHIELD_EMAIL_SENDER` for `'env'`, so existing injectors keep the
|
|
33
|
+
* port they know. Each send is bounded; a failure rejects (callers already treat it best-effort).
|
|
34
|
+
*/
|
|
35
|
+
export declare function senderFromProvider(provider: EmailProvider, timeoutMs: number): EmailSender;
|
|
36
|
+
/**
|
|
37
|
+
* `message.sent` / `message.failed` rows go to the framework audit log over guard's own pool
|
|
38
|
+
* (the runtime account holds append-only INSERT on the audit schema), independent of the
|
|
39
|
+
* optional app `auditSink` (Shield #6).
|
|
40
|
+
*/
|
|
41
|
+
export declare function messageAuditWriter(pool: Pick<Pool, 'query'>, schemas: Pick<FrameworkSchemas, 'audit'>): MessageAuditWriter;
|
|
42
|
+
//# sourceMappingURL=email-delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-delivery.d.ts","sourceRoot":"","sources":["../../src/messaging/email-delivery.ts"],"names":[],"mappings":"AAcA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAEpC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAEtF,+CAA+C;AAC/C,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;AAEjE,iEAAiE;AACjE,MAAM,WAAW,eAAe;IAC9B,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IACxC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC9C;AAKD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,WAAW,EAAE,WAAW,GAAG,IAAI,EAC/B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,aAAa,CAAC,CA+BxB;AA+BD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAChD,OAAO,CAIT;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,CAM1F;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EACzB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GACvC,kBAAkB,CAKpB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildEmailProvider = buildEmailProvider;
|
|
4
|
+
exports.isMailDisabled = isMailDisabled;
|
|
5
|
+
exports.senderFromProvider = senderFromProvider;
|
|
6
|
+
exports.messageAuditWriter = messageAuditWriter;
|
|
7
|
+
/**
|
|
8
|
+
* Email delivery binding (G-010, docs/features/message-delivery/SPEC.md R2, R8, R14). Turns the
|
|
9
|
+
* `ShieldModule.forRoot({ emailSender })` option into the provider the {@link MessageDispatcher}
|
|
10
|
+
* wraps:
|
|
11
|
+
* - `'env'` → {@link loadMessageConfig}, then `@astram/messaging` loaded as an OPTIONAL peer (only
|
|
12
|
+
* here, so apps that never choose `'env'` never load it) and its `createEmailProvider`; with
|
|
13
|
+
* `MESSAGE_BOOT_PROBE=1` the adapter's probe runs (no message is sent) and a failure refuses
|
|
14
|
+
* boot. `EMAIL_PROVIDER=disabled` binds nothing and loads nothing.
|
|
15
|
+
* - `'disabled'` → no provider (nothing delivered, nothing audited).
|
|
16
|
+
* - an app `EmailSender` → adapted as provider `custom`; omitted (development/test) → `log`.
|
|
17
|
+
*/
|
|
18
|
+
const node_fs_1 = require("node:fs");
|
|
19
|
+
const common_1 = require("@nestjs/common");
|
|
20
|
+
const core_1 = require("@astram/core");
|
|
21
|
+
const engine_1 = require("@astram/engine");
|
|
22
|
+
const message_config_js_1 = require("../config/message-config.js");
|
|
23
|
+
const boot_secrets_js_1 = require("../secrets/boot-secrets.js");
|
|
24
|
+
const optional_peer_js_1 = require("../storage/optional-peer.js");
|
|
25
|
+
const message_dispatcher_js_1 = require("./message-dispatcher.js");
|
|
26
|
+
const MESSAGING_PACKAGE = '@astram/messaging';
|
|
27
|
+
const logger = new common_1.Logger('ShieldMessaging');
|
|
28
|
+
async function buildEmailProvider(option, boundSender, deps) {
|
|
29
|
+
const fallback = { timeoutMs: message_config_js_1.DEFAULT_MESSAGE_SEND_TIMEOUT_MS };
|
|
30
|
+
if (option === 'disabled')
|
|
31
|
+
return { provider: null, ...fallback };
|
|
32
|
+
if (option !== 'env') {
|
|
33
|
+
if (boundSender === null)
|
|
34
|
+
return { provider: null, ...fallback };
|
|
35
|
+
return { provider: (0, message_dispatcher_js_1.providerFromSender)(boundSender, option ? 'custom' : 'log'), ...fallback };
|
|
36
|
+
}
|
|
37
|
+
const env = deps.env ?? process.env;
|
|
38
|
+
const config = (0, message_config_js_1.loadMessageConfig)(env, {
|
|
39
|
+
resolvedFromSecretRef: boot_secrets_js_1.wasResolvedFromSecretRef,
|
|
40
|
+
readFile: deps.readFile ?? ((path) => (0, node_fs_1.readFileSync)(path, 'utf8')),
|
|
41
|
+
});
|
|
42
|
+
if (config.email.provider === 'disabled') {
|
|
43
|
+
logger.warn("emailSender: 'env' with EMAIL_PROVIDER=disabled — framework emails (password reset, " +
|
|
44
|
+
'invites, notices) are NOT delivered.');
|
|
45
|
+
return { provider: null, ...fallback };
|
|
46
|
+
}
|
|
47
|
+
const messaging = (0, optional_peer_js_1.requireOptionalPeer)(MESSAGING_PACKAGE, "ShieldModule.forRoot({ emailSender: 'env' })", deps.load);
|
|
48
|
+
const provider = await messaging.createEmailProvider(config.email);
|
|
49
|
+
const timeoutMs = config.email.timeoutMs;
|
|
50
|
+
if (config.probe)
|
|
51
|
+
await probe(provider, timeoutMs);
|
|
52
|
+
logger.log(`Email delivery: ${provider.name}${config.probe ? ' (boot probe passed)' : ''}`);
|
|
53
|
+
return { provider, timeoutMs };
|
|
54
|
+
}
|
|
55
|
+
/** Reachability + credentials, no message (R8). Bounded; any failure refuses boot. */
|
|
56
|
+
async function probe(provider, timeoutMs) {
|
|
57
|
+
const fail = (detail) => new Error(`MESSAGE_BOOT_PROBE: the ${provider.name} check failed (${detail}); check EMAIL_PROVIDER ` +
|
|
58
|
+
'and the provider host / credential variables');
|
|
59
|
+
if (!provider.probe)
|
|
60
|
+
return;
|
|
61
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
62
|
+
let timer;
|
|
63
|
+
const expiry = new Promise((_, reject) => {
|
|
64
|
+
timer = setTimeout(() => reject(fail('timeout')), timeoutMs + 1_000);
|
|
65
|
+
timer.unref?.();
|
|
66
|
+
});
|
|
67
|
+
try {
|
|
68
|
+
await Promise.race([provider.probe({ signal }), expiry]);
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
if (e instanceof core_1.MessageDeliveryError) {
|
|
72
|
+
const status = e.status !== undefined ? `, HTTP ${e.status}` : '';
|
|
73
|
+
throw fail(`${e.failureClass}${status}`);
|
|
74
|
+
}
|
|
75
|
+
if (signal.aborted)
|
|
76
|
+
throw fail('timeout');
|
|
77
|
+
if (e instanceof Error && e.message.startsWith('MESSAGE_BOOT_PROBE:'))
|
|
78
|
+
throw e;
|
|
79
|
+
throw fail('unreachable');
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The link-base boot rule applies only with a real sender (Phase 13 #1). `'env'` is real unless
|
|
87
|
+
* `EMAIL_PROVIDER=disabled`. Read at `forRoot` time, after `resolveBootSecrets()`.
|
|
88
|
+
*/
|
|
89
|
+
function isMailDisabled(option, env) {
|
|
90
|
+
if (option === undefined || option === 'disabled')
|
|
91
|
+
return true;
|
|
92
|
+
if (option === 'env')
|
|
93
|
+
return env.EMAIL_PROVIDER?.trim() === 'disabled';
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The `EmailSender` bound under `SHIELD_EMAIL_SENDER` for `'env'`, so existing injectors keep the
|
|
98
|
+
* port they know. Each send is bounded; a failure rejects (callers already treat it best-effort).
|
|
99
|
+
*/
|
|
100
|
+
function senderFromProvider(provider, timeoutMs) {
|
|
101
|
+
return {
|
|
102
|
+
send: async (message) => {
|
|
103
|
+
await provider.send(message, { signal: AbortSignal.timeout(timeoutMs) });
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* `message.sent` / `message.failed` rows go to the framework audit log over guard's own pool
|
|
109
|
+
* (the runtime account holds append-only INSERT on the audit schema), independent of the
|
|
110
|
+
* optional app `auditSink` (Shield #6).
|
|
111
|
+
*/
|
|
112
|
+
function messageAuditWriter(pool, schemas) {
|
|
113
|
+
const sink = new engine_1.MariaDbAuditSink({ schemas });
|
|
114
|
+
return {
|
|
115
|
+
record: (event) => sink.record(event, (sql, params) => pool.query(sql, params)),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one exit for framework messages (G-010, docs/features/message-delivery/SPEC.md R6, R9, R10;
|
|
3
|
+
* spec P-1). Every framework email — password reset, invite, email-change confirmation, security
|
|
4
|
+
* notices — goes through {@link MessageDispatcher.sendEmail}, which:
|
|
5
|
+
* - bounds the send (the provider gets an abort signal at `timeoutMs`; the call itself settles by
|
|
6
|
+
* `timeoutMs + graceMs` even if the provider ignores the signal);
|
|
7
|
+
* - records `message.sent` / `message.failed` with channel, provider, purpose, masked recipient
|
|
8
|
+
* and the provider's message id or the failure class — never the link, subject or body;
|
|
9
|
+
* - never throws, so a delivery failure never changes a public response.
|
|
10
|
+
*
|
|
11
|
+
* It wraps whatever is bound: an `@astram/messaging` adapter (`emailSender: 'env'`), an app's own
|
|
12
|
+
* `EmailSender` (provider `custom`) or the dev log sender (`log`). `'disabled'` binds no provider
|
|
13
|
+
* and audits nothing.
|
|
14
|
+
*/
|
|
15
|
+
import { type AuditEvent, type DeliveryFailureClass, type EmailMessage, type EmailProvider, type MessagePurpose } from '@astram/core';
|
|
16
|
+
import type { EmailSender } from '../auth/ports/email-sender.js';
|
|
17
|
+
/** Who the message is about, for the audit row (from the session or the resolved credential). */
|
|
18
|
+
export interface MessageAuditContext {
|
|
19
|
+
readonly actorId: string;
|
|
20
|
+
readonly tenantId: string;
|
|
21
|
+
}
|
|
22
|
+
export type EmailDeliveryOutcome = {
|
|
23
|
+
readonly status: 'sent';
|
|
24
|
+
readonly provider: string;
|
|
25
|
+
readonly messageId?: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly status: 'failed';
|
|
28
|
+
readonly provider: string;
|
|
29
|
+
readonly failureClass: DeliveryFailureClass;
|
|
30
|
+
} | {
|
|
31
|
+
readonly status: 'disabled';
|
|
32
|
+
};
|
|
33
|
+
export interface MessageDispatcher {
|
|
34
|
+
/** `false` when mail is `'disabled'`: nothing can be delivered. */
|
|
35
|
+
readonly emailEnabled: boolean;
|
|
36
|
+
sendEmail(purpose: MessagePurpose, message: EmailMessage, context: MessageAuditContext): Promise<EmailDeliveryOutcome>;
|
|
37
|
+
}
|
|
38
|
+
/** Appends one audit event. Best-effort here: a failure is swallowed. */
|
|
39
|
+
export interface MessageAuditWriter {
|
|
40
|
+
record(event: AuditEvent): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export interface MessageDispatcherOptions {
|
|
43
|
+
/** The per-send bound (`MESSAGE_SEND_TIMEOUT_MS`). */
|
|
44
|
+
readonly timeoutMs: number;
|
|
45
|
+
/** Extra time after the bound for a provider to honour the abort before the call settles. */
|
|
46
|
+
readonly graceMs?: number;
|
|
47
|
+
readonly now?: () => number;
|
|
48
|
+
}
|
|
49
|
+
export declare class GuardMessageDispatcher implements MessageDispatcher {
|
|
50
|
+
private readonly provider;
|
|
51
|
+
private readonly audit;
|
|
52
|
+
private readonly options;
|
|
53
|
+
readonly emailEnabled: boolean;
|
|
54
|
+
private readonly graceMs;
|
|
55
|
+
private readonly now;
|
|
56
|
+
constructor(provider: EmailProvider | null, audit: MessageAuditWriter | null, options: MessageDispatcherOptions);
|
|
57
|
+
sendEmail(purpose: MessagePurpose, message: EmailMessage, context: MessageAuditContext): Promise<EmailDeliveryOutcome>;
|
|
58
|
+
private bounded;
|
|
59
|
+
private record;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Adapt an app's `EmailSender` (or the dev log sender) to the provider port. The message passes
|
|
63
|
+
* through verbatim; the sender cannot be aborted, so the dispatcher's race settles the call.
|
|
64
|
+
*/
|
|
65
|
+
export declare function providerFromSender(sender: EmailSender, name: string): EmailProvider;
|
|
66
|
+
//# sourceMappingURL=message-dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-dispatcher.d.ts","sourceRoot":"","sources":["../../src/messaging/message-dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAGjE,iGAAiG;AACjG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,oBAAoB,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACnF;IACE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;CAC7C,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,SAAS,CACP,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC;CAClC;AAED,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,wBAAwB;IACvC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAMD,qBAAa,sBAAuB,YAAW,iBAAiB;IAM5D,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGhB,QAAQ,EAAE,aAAa,GAAG,IAAI,EAC9B,KAAK,EAAE,kBAAkB,GAAG,IAAI,EAChC,OAAO,EAAE,wBAAwB;IAO9C,SAAS,CACb,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,oBAAoB,CAAC;IA4BhC,OAAO,CAAC,OAAO;YAwBD,MAAM;CAiCrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAQnF"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GuardMessageDispatcher = void 0;
|
|
4
|
+
exports.providerFromSender = providerFromSender;
|
|
5
|
+
/**
|
|
6
|
+
* The one exit for framework messages (G-010, docs/features/message-delivery/SPEC.md R6, R9, R10;
|
|
7
|
+
* spec P-1). Every framework email — password reset, invite, email-change confirmation, security
|
|
8
|
+
* notices — goes through {@link MessageDispatcher.sendEmail}, which:
|
|
9
|
+
* - bounds the send (the provider gets an abort signal at `timeoutMs`; the call itself settles by
|
|
10
|
+
* `timeoutMs + graceMs` even if the provider ignores the signal);
|
|
11
|
+
* - records `message.sent` / `message.failed` with channel, provider, purpose, masked recipient
|
|
12
|
+
* and the provider's message id or the failure class — never the link, subject or body;
|
|
13
|
+
* - never throws, so a delivery failure never changes a public response.
|
|
14
|
+
*
|
|
15
|
+
* It wraps whatever is bound: an `@astram/messaging` adapter (`emailSender: 'env'`), an app's own
|
|
16
|
+
* `EmailSender` (provider `custom`) or the dev log sender (`log`). `'disabled'` binds no provider
|
|
17
|
+
* and audits nothing.
|
|
18
|
+
*/
|
|
19
|
+
const core_1 = require("@astram/core");
|
|
20
|
+
const email_address_js_1 = require("../auth/email-address.js");
|
|
21
|
+
const DEFAULT_GRACE_MS = 1_000;
|
|
22
|
+
class BoundExpired extends Error {
|
|
23
|
+
}
|
|
24
|
+
class GuardMessageDispatcher {
|
|
25
|
+
provider;
|
|
26
|
+
audit;
|
|
27
|
+
options;
|
|
28
|
+
emailEnabled;
|
|
29
|
+
graceMs;
|
|
30
|
+
now;
|
|
31
|
+
constructor(provider, audit, options) {
|
|
32
|
+
this.provider = provider;
|
|
33
|
+
this.audit = audit;
|
|
34
|
+
this.options = options;
|
|
35
|
+
this.emailEnabled = provider !== null;
|
|
36
|
+
this.graceMs = options.graceMs ?? DEFAULT_GRACE_MS;
|
|
37
|
+
this.now = options.now ?? Date.now;
|
|
38
|
+
}
|
|
39
|
+
async sendEmail(purpose, message, context) {
|
|
40
|
+
const provider = this.provider;
|
|
41
|
+
if (provider === null)
|
|
42
|
+
return { status: 'disabled' };
|
|
43
|
+
let outcome;
|
|
44
|
+
let status;
|
|
45
|
+
try {
|
|
46
|
+
const receipt = await this.bounded(provider, message);
|
|
47
|
+
outcome = {
|
|
48
|
+
status: 'sent',
|
|
49
|
+
provider: receipt.provider || provider.name,
|
|
50
|
+
...(receipt.messageId ? { messageId: receipt.messageId } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
const failureClass = e instanceof BoundExpired
|
|
55
|
+
? 'timeout'
|
|
56
|
+
: e instanceof core_1.MessageDeliveryError
|
|
57
|
+
? e.failureClass
|
|
58
|
+
: 'transient';
|
|
59
|
+
if (e instanceof core_1.MessageDeliveryError)
|
|
60
|
+
status = e.status;
|
|
61
|
+
outcome = { status: 'failed', provider: provider.name, failureClass };
|
|
62
|
+
}
|
|
63
|
+
await this.record(purpose, message.to, context, outcome, status);
|
|
64
|
+
return outcome;
|
|
65
|
+
}
|
|
66
|
+
bounded(provider, message) {
|
|
67
|
+
const signal = AbortSignal.timeout(this.options.timeoutMs);
|
|
68
|
+
let timer;
|
|
69
|
+
const expiry = new Promise((_, reject) => {
|
|
70
|
+
timer = setTimeout(() => reject(new BoundExpired()), this.options.timeoutMs + this.graceMs);
|
|
71
|
+
timer.unref?.();
|
|
72
|
+
});
|
|
73
|
+
// Started inside a promise so a provider that throws synchronously still settles the race
|
|
74
|
+
// (never an orphaned expiry rejection, and the timer below is always cleared).
|
|
75
|
+
const send = Promise.resolve()
|
|
76
|
+
.then(() => provider.send(message, { signal }))
|
|
77
|
+
.catch((e) => {
|
|
78
|
+
// A provider that stopped on our abort reports it however it likes: it is the bound.
|
|
79
|
+
if (signal.aborted &&
|
|
80
|
+
!(e instanceof core_1.MessageDeliveryError && e.failureClass !== 'timeout')) {
|
|
81
|
+
throw new BoundExpired();
|
|
82
|
+
}
|
|
83
|
+
throw e;
|
|
84
|
+
});
|
|
85
|
+
return Promise.race([send, expiry]).finally(() => clearTimeout(timer));
|
|
86
|
+
}
|
|
87
|
+
async record(purpose, to, context, outcome, status) {
|
|
88
|
+
if (this.audit === null || outcome.status === 'disabled')
|
|
89
|
+
return;
|
|
90
|
+
const attributes = {
|
|
91
|
+
channel: 'email',
|
|
92
|
+
provider: outcome.provider,
|
|
93
|
+
purpose,
|
|
94
|
+
recipient: (0, email_address_js_1.maskEmail)(to),
|
|
95
|
+
};
|
|
96
|
+
if (outcome.status === 'sent') {
|
|
97
|
+
if (outcome.messageId)
|
|
98
|
+
attributes.messageId = outcome.messageId;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
attributes.failureClass = outcome.failureClass;
|
|
102
|
+
if (status !== undefined)
|
|
103
|
+
attributes.status = status;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
await this.audit.record({
|
|
107
|
+
type: purpose === 'invite' ? 'admin_action' : 'authentication',
|
|
108
|
+
occurredAtMs: this.now(),
|
|
109
|
+
actorId: context.actorId,
|
|
110
|
+
tenantId: context.tenantId,
|
|
111
|
+
action: { key: outcome.status === 'sent' ? 'message.sent' : 'message.failed' },
|
|
112
|
+
attributes,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Best-effort: an audit outage never fails a delivery the user already got (or did not).
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.GuardMessageDispatcher = GuardMessageDispatcher;
|
|
121
|
+
/**
|
|
122
|
+
* Adapt an app's `EmailSender` (or the dev log sender) to the provider port. The message passes
|
|
123
|
+
* through verbatim; the sender cannot be aborted, so the dispatcher's race settles the call.
|
|
124
|
+
*/
|
|
125
|
+
function providerFromSender(sender, name) {
|
|
126
|
+
return {
|
|
127
|
+
name,
|
|
128
|
+
send: async (message) => {
|
|
129
|
+
await sender.send(message);
|
|
130
|
+
return { provider: name };
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The masked form of a phone number for audit rows (G-010 R9): `+`, the first two digits, one `*`
|
|
3
|
+
* per hidden digit, the last two digits (`+919876543210` → `+91********10`). A value that is not
|
|
4
|
+
* E.164 is never echoed (`***`). Emails use `maskEmail`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function maskPhone(value: string): string;
|
|
7
|
+
//# sourceMappingURL=recipient-mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient-mask.d.ts","sourceRoot":"","sources":["../../src/messaging/recipient-mask.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI/C"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maskPhone = maskPhone;
|
|
4
|
+
const core_1 = require("@astram/core");
|
|
5
|
+
/**
|
|
6
|
+
* The masked form of a phone number for audit rows (G-010 R9): `+`, the first two digits, one `*`
|
|
7
|
+
* per hidden digit, the last two digits (`+919876543210` → `+91********10`). A value that is not
|
|
8
|
+
* E.164 is never echoed (`***`). Emails use `maskEmail`.
|
|
9
|
+
*/
|
|
10
|
+
function maskPhone(value) {
|
|
11
|
+
if (!(0, core_1.isE164PhoneNumber)(value))
|
|
12
|
+
return '***';
|
|
13
|
+
const digits = value.slice(1);
|
|
14
|
+
return `+${digits.slice(0, 2)}${'*'.repeat(digits.length - 4)}${digits.slice(-2)}`;
|
|
15
|
+
}
|
|
@@ -115,8 +115,10 @@ export interface ShieldModuleOptions {
|
|
|
115
115
|
* sender, or `'disabled'` (no-op, forgot-password still 204). REQUIRED under
|
|
116
116
|
* `auth: true` unless the raw `NODE_ENV` is `development`/`test` (where the dev
|
|
117
117
|
* `LogEmailSender` is the default); otherwise boot fails (security hardening #6).
|
|
118
|
+
* `'env'` (G-010) reads the provider from `EMAIL_PROVIDER` and the `EMAIL_*` / provider
|
|
119
|
+
* variables (needs the `@astram/messaging` package; runbook §6).
|
|
118
120
|
*/
|
|
119
|
-
readonly emailSender?: EmailSender | 'disabled';
|
|
121
|
+
readonly emailSender?: EmailSender | 'disabled' | 'env';
|
|
120
122
|
readonly recoveryCodeStore?: RecoveryCodeStore;
|
|
121
123
|
readonly profileWriter?: ProfileWriter;
|
|
122
124
|
readonly sessionSweeper?: SessionSweeper;
|
|
@@ -248,8 +250,8 @@ export interface ShieldModuleAsyncOptions {
|
|
|
248
250
|
*/
|
|
249
251
|
readonly credentialWriter?: CredentialWriter;
|
|
250
252
|
readonly resetTokenStore?: ResetTokenStore;
|
|
251
|
-
/** See {@link ShieldModuleOptions.emailSender} (a sender, or `'
|
|
252
|
-
readonly emailSender?: EmailSender | 'disabled';
|
|
253
|
+
/** See {@link ShieldModuleOptions.emailSender} (a sender, `'disabled'` or `'env'`). */
|
|
254
|
+
readonly emailSender?: EmailSender | 'disabled' | 'env';
|
|
253
255
|
readonly recoveryCodeStore?: RecoveryCodeStore;
|
|
254
256
|
readonly profileWriter?: ProfileWriter;
|
|
255
257
|
readonly sessionSweeper?: SessionSweeper;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AAOnF,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAoB,MAAM,cAAc,CAAC;AAQzF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AACvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,0CAA0C,CAAC;AACtF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AASzE,OAAO,EAAqC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAsC5F,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAC5F,OAAO,EAAE,yBAAyB,EAAE,2BAA2B,EAAE,CAAC;AAElE,OAAO,EAGL,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAIzC,YAAY,EAAE,yBAAyB,EAAE,MAAM,uCAAuC,CAAC;AA6JvF;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;IACnD;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;IACzD;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAC7C,QAAQ,CAAC,eAAe,CAAC,EAAE,eAAe,CAAC;IAC3C
|
|
1
|
+
{"version":3,"file":"shield.module.d.ts","sourceRoot":"","sources":["../../src/module/shield.module.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astram/guard",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Astram framework — the enforcement guard + real-world adapters (the 'hands'). One global deny-by-default gate every request passes through (ADR-0002), classification decorators, boot-pool assertion (ADR-0018), the forRoot() registration seam, and the DB/Redis/crypto adapters that connect the sealed core to a real backend. Depends on @astram/core (ADR-0003, ADR-0023).",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -36,17 +36,21 @@
|
|
|
36
36
|
"argon2": "^0.41.1",
|
|
37
37
|
"ioredis": "^5.4.1",
|
|
38
38
|
"mariadb": "^3.5.4",
|
|
39
|
-
"@astram/
|
|
40
|
-
"@astram/
|
|
39
|
+
"@astram/core": "1.9.0",
|
|
40
|
+
"@astram/engine": "0.10.1"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@nestjs/common": "^11",
|
|
44
44
|
"@nestjs/core": "^11",
|
|
45
45
|
"@nestjs/platform-express": "^11",
|
|
46
46
|
"reflect-metadata": "^0.2",
|
|
47
|
-
"rxjs": "^7"
|
|
47
|
+
"rxjs": "^7",
|
|
48
|
+
"@astram/messaging": "^0.1.1"
|
|
48
49
|
},
|
|
49
50
|
"peerDependenciesMeta": {
|
|
51
|
+
"@astram/messaging": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
50
54
|
"@nestjs/platform-express": {
|
|
51
55
|
"optional": true
|
|
52
56
|
}
|
|
@@ -63,7 +67,8 @@
|
|
|
63
67
|
"supertest": "^7.0.0",
|
|
64
68
|
"typescript": "^5.7.0",
|
|
65
69
|
"unplugin-swc": "^1.5.1",
|
|
66
|
-
"vitest": "^2.1.8"
|
|
70
|
+
"vitest": "^2.1.8",
|
|
71
|
+
"@astram/messaging": "0.1.1"
|
|
67
72
|
},
|
|
68
73
|
"scripts": {
|
|
69
74
|
"build": "tsc -p tsconfig.build.json",
|