@astram/guard 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/audit-chain-sealer.d.ts +90 -0
- package/dist/audit/audit-chain-sealer.d.ts.map +1 -0
- package/dist/audit/audit-chain-sealer.js +520 -0
- package/dist/audit/audit-seal-config.d.ts +22 -0
- package/dist/audit/audit-seal-config.d.ts.map +1 -0
- package/dist/audit/audit-seal-config.js +35 -0
- package/dist/audit/audit-sealer.service.d.ts +62 -0
- package/dist/audit/audit-sealer.service.d.ts.map +1 -0
- package/dist/audit/audit-sealer.service.js +197 -0
- package/dist/audit/node-sha256-hash.d.ts +6 -0
- package/dist/audit/node-sha256-hash.d.ts.map +1 -0
- package/dist/audit/node-sha256-hash.js +16 -0
- 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/persistence.module.d.ts +2 -2
- package/dist/auth/persistence.module.d.ts.map +1 -1
- package/dist/auth/persistence.module.js +19 -3
- 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 +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -3
- 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 +15 -5
package/dist/auth/auth.module.js
CHANGED
|
@@ -104,7 +104,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
104
104
|
})();
|
|
105
105
|
var AuthModule_1;
|
|
106
106
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
107
|
-
exports.AuthModule = exports.singleLayoutProbeProvider = exports.SHIELD_SINGLE_LAYOUT_PROBE = exports.hmacSha1 = void 0;
|
|
107
|
+
exports.AuthModule = exports.attemptLimiterFactoryProvider = exports.singleLayoutProbeProvider = exports.SHIELD_SINGLE_LAYOUT_PROBE = exports.hmacSha1 = void 0;
|
|
108
108
|
exports.resolveTotpVerifier = resolveTotpVerifier;
|
|
109
109
|
exports.makeDefaultTotpVerifier = makeDefaultTotpVerifier;
|
|
110
110
|
exports.resolveDefaultEmailSender = resolveDefaultEmailSender;
|
|
@@ -145,6 +145,8 @@ const permission_version_cache_js_1 = require("../authz/permission-version-cache
|
|
|
145
145
|
const session_restriction_store_js_1 = require("../session/session-restriction-store.js");
|
|
146
146
|
const attempt_limiter_js_1 = require("./rate-limit/attempt-limiter.js");
|
|
147
147
|
const trust_proxy_check_js_1 = require("./rate-limit/trust-proxy-check.js");
|
|
148
|
+
const email_delivery_js_1 = require("../messaging/email-delivery.js");
|
|
149
|
+
const message_dispatcher_js_1 = require("../messaging/message-dispatcher.js");
|
|
148
150
|
const tokens_js_2 = require("../request-auth/tokens.js");
|
|
149
151
|
const mariadb_api_key_store_js_1 = require("../authentication/mariadb-api-key-store.js");
|
|
150
152
|
const core_1 = require("@astram/core");
|
|
@@ -741,10 +743,11 @@ const emailAddressChangerProvider = {
|
|
|
741
743
|
useFactory: (pool, config) => new mariadb_email_address_changer_js_1.MariaDbEmailAddressChanger(pool, undefined, (0, app_config_js_1.frameworkSchemasOf)(config)),
|
|
742
744
|
inject: [tokens_js_1.SHIELD_DB_POOL, config_module_js_1.APP_CONFIG],
|
|
743
745
|
};
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
746
|
+
exports.attemptLimiterFactoryProvider = {
|
|
747
|
+
provide: tokens_js_1.SHIELD_ATTEMPT_LIMITER_FACTORY,
|
|
748
|
+
useFactory: (redis) => (cfg) => new attempt_limiter_js_1.AttemptLimiter(redis, cfg),
|
|
749
|
+
inject: [tokens_js_1.SHIELD_REDIS],
|
|
750
|
+
};
|
|
748
751
|
const emailChangeLimiterProvider = {
|
|
749
752
|
provide: tokens_js_1.SHIELD_EMAIL_CHANGE_LIMITER,
|
|
750
753
|
useFactory: (redis) => new attempt_limiter_js_1.AttemptLimiter(redis, { maxAttempts: 3, windowMs: 60 * 60 * 1000 }),
|
|
@@ -818,6 +821,44 @@ function resolveDefaultEmailSender(rawNodeEnv) {
|
|
|
818
821
|
'to stdout, reset links included. Never run like this in production — supply an emailSender.');
|
|
819
822
|
return new log_email_sender_js_1.LogEmailSender();
|
|
820
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* The email delivery chain (G-010): `SHIELD_EMAIL_DELIVERY` (the bound provider and its per-send
|
|
826
|
+
* bound), `SHIELD_EMAIL_SENDER` (the port existing injectors know) and `SHIELD_MESSAGE_DISPATCHER`
|
|
827
|
+
* (bounded, audited, never throws). For `'env'` the sender is derived from the adapter; otherwise
|
|
828
|
+
* the sender binds exactly as before (AC-11) and the provider wraps it.
|
|
829
|
+
*/
|
|
830
|
+
function makeEmailDeliveryProviders(option) {
|
|
831
|
+
const dispatcher = {
|
|
832
|
+
provide: tokens_js_1.SHIELD_MESSAGE_DISPATCHER,
|
|
833
|
+
useFactory: (delivery, pool, config) => new message_dispatcher_js_1.GuardMessageDispatcher(delivery.provider, (0, email_delivery_js_1.messageAuditWriter)(pool, (0, app_config_js_1.frameworkSchemasOf)(config)), { timeoutMs: delivery.timeoutMs }),
|
|
834
|
+
inject: [tokens_js_1.SHIELD_EMAIL_DELIVERY, tokens_js_1.SHIELD_DB_POOL, config_module_js_1.APP_CONFIG],
|
|
835
|
+
};
|
|
836
|
+
if (option === 'env') {
|
|
837
|
+
return [
|
|
838
|
+
{
|
|
839
|
+
provide: tokens_js_1.SHIELD_EMAIL_DELIVERY,
|
|
840
|
+
useFactory: () => (0, email_delivery_js_1.buildEmailProvider)('env', null, {}),
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
provide: tokens_js_1.SHIELD_EMAIL_SENDER,
|
|
844
|
+
useFactory: (delivery) => delivery.provider
|
|
845
|
+
? (0, email_delivery_js_1.senderFromProvider)(delivery.provider, delivery.timeoutMs)
|
|
846
|
+
: new noop_email_sender_js_1.NoopEmailSender(),
|
|
847
|
+
inject: [tokens_js_1.SHIELD_EMAIL_DELIVERY],
|
|
848
|
+
},
|
|
849
|
+
dispatcher,
|
|
850
|
+
];
|
|
851
|
+
}
|
|
852
|
+
return [
|
|
853
|
+
makeEmailSenderProvider(option),
|
|
854
|
+
{
|
|
855
|
+
provide: tokens_js_1.SHIELD_EMAIL_DELIVERY,
|
|
856
|
+
useFactory: (sender) => (0, email_delivery_js_1.buildEmailProvider)(option, sender, {}),
|
|
857
|
+
inject: [tokens_js_1.SHIELD_EMAIL_SENDER],
|
|
858
|
+
},
|
|
859
|
+
dispatcher,
|
|
860
|
+
];
|
|
861
|
+
}
|
|
821
862
|
function makeRecoveryCodeStoreProvider(override) {
|
|
822
863
|
return override
|
|
823
864
|
? { provide: tokens_js_1.SHIELD_RECOVERY_CODE_STORE, useValue: override }
|
|
@@ -930,7 +971,8 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
930
971
|
// The link-base boot rule (Phase 13 #1) applies only with a real sender: `'disabled'` delivers
|
|
931
972
|
// nothing, and an omitted sender outside development/test already fails the boot through
|
|
932
973
|
// `resolveDefaultEmailSender` (that clearer error is kept; in development/test the rule is off).
|
|
933
|
-
|
|
974
|
+
// G-010: `'env'` is a real sender unless `EMAIL_PROVIDER=disabled` (read after resolveBootSecrets).
|
|
975
|
+
const mailDisabled = (0, email_delivery_js_1.isMailDisabled)(options.emailSender, process.env);
|
|
934
976
|
return {
|
|
935
977
|
module: AuthModule_1,
|
|
936
978
|
imports: [persistence_module_js_1.PersistenceModule.forRoot()],
|
|
@@ -954,8 +996,9 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
954
996
|
emailAddressChangerProvider,
|
|
955
997
|
emailChangeLimiterProvider,
|
|
956
998
|
confirmEmailLimiterProvider,
|
|
999
|
+
exports.attemptLimiterFactoryProvider,
|
|
957
1000
|
makeEmailChangeLinkBaseUrlProvider(options.emailChangeLinkBaseUrl, mailDisabled),
|
|
958
|
-
|
|
1001
|
+
...makeEmailDeliveryProviders(options.emailSender),
|
|
959
1002
|
makeRecoveryCodeStoreProvider(options.recoveryCodeStore),
|
|
960
1003
|
makeProfileWriterProvider(options.profileWriter),
|
|
961
1004
|
makeSessionSweeperProvider(options.sessionSweeper),
|
|
@@ -1065,6 +1108,10 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
1065
1108
|
// Phase 12: identity's admin email change invalidates pending self-service tokens.
|
|
1066
1109
|
tokens_js_1.SHIELD_EMAIL_CHANGE_TOKEN_STORE,
|
|
1067
1110
|
tokens_js_1.SHIELD_EMAIL_SENDER,
|
|
1111
|
+
// G-010: the one exit for framework messages (identity's invite injects it).
|
|
1112
|
+
tokens_js_1.SHIELD_MESSAGE_DISPATCHER,
|
|
1113
|
+
// G-010: identity's invite rate limits (a limiter factory, never the raw Redis client).
|
|
1114
|
+
tokens_js_1.SHIELD_ATTEMPT_LIMITER_FACTORY,
|
|
1068
1115
|
tokens_js_1.SHIELD_RECOVERY_CODE_STORE,
|
|
1069
1116
|
tokens_js_1.SHIELD_PROFILE_WRITER,
|
|
1070
1117
|
tokens_js_1.SHIELD_SESSION_SWEEPER,
|
|
@@ -2,6 +2,7 @@ import type { AttemptLimiter } from './rate-limit/attempt-limiter.js';
|
|
|
2
2
|
import { TrustProxyBootCheck } from './rate-limit/trust-proxy-check.js';
|
|
3
3
|
import type { AuditSink } from './ports/audit-sink.js';
|
|
4
4
|
import type { EmailSender } from './ports/email-sender.js';
|
|
5
|
+
import type { MessageDispatcher } from '../messaging/message-dispatcher.js';
|
|
5
6
|
import type { EmailChangeTokenStore } from './ports/email-change-token-store.js';
|
|
6
7
|
import type { EmailAddressChanger } from './ports/email-address-changer.js';
|
|
7
8
|
import type { ResetTokenStore } from './ports/reset-token-store.js';
|
|
@@ -15,7 +16,8 @@ export declare class ConfirmEmailController {
|
|
|
15
16
|
private readonly auditSink?;
|
|
16
17
|
private readonly limiter?;
|
|
17
18
|
private readonly trustProxyCheck?;
|
|
18
|
-
|
|
19
|
+
private readonly messageDispatcher?;
|
|
20
|
+
constructor(tokens?: (EmailChangeTokenStore | null) | undefined, addresses?: (EmailAddressChanger | null) | undefined, sweeper?: (SessionSweeper | null) | undefined, resetTokens?: (ResetTokenStore | null) | undefined, emailSender?: (EmailSender | null) | undefined, auditSink?: (AuditSink | null) | undefined, limiter?: (AttemptLimiter | null) | undefined, trustProxyCheck?: (Pick<TrustProxyBootCheck, "perIpLimitingEnabled"> | null) | undefined, messageDispatcher?: (MessageDispatcher | null) | undefined);
|
|
19
21
|
confirmEmail(body: {
|
|
20
22
|
token?: unknown;
|
|
21
23
|
}, ip: string): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm-email.controller.d.ts","sourceRoot":"","sources":["../../src/auth/confirm-email.controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"confirm-email.controller.d.ts","sourceRoot":"","sources":["../../src/auth/confirm-email.controller.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAC5E,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAsB,MAAM,kCAAkC,CAAC;AAChG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAcjE,qBACa,sBAAsB;IAI/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAGxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAG3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAGzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAG7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAG3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAGzB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IAKjC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBA1BlB,MAAM,CAAC,GAAE,qBAAqB,GAAG,IAAI,aAAA,EAGrC,SAAS,CAAC,GAAE,mBAAmB,GAAG,IAAI,aAAA,EAGtC,OAAO,CAAC,GAAE,cAAc,GAAG,IAAI,aAAA,EAG/B,WAAW,CAAC,GAAE,eAAe,GAAG,IAAI,aAAA,EAGpC,WAAW,CAAC,GAAE,WAAW,GAAG,IAAI,aAAA,EAGhC,SAAS,CAAC,GAAE,SAAS,GAAG,IAAI,aAAA,EAG5B,OAAO,CAAC,GAAE,cAAc,GAAG,IAAI,aAAA,EAG/B,eAAe,CAAC,GAAE,IAAI,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,GAAG,IAAI,aAAA,EAK1E,iBAAiB,CAAC,GAAE,iBAAiB,GAAG,IAAI,aAAA;IAMzD,YAAY,CACR,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAE3B,EAAE,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC;IAkFxB;;;;;;OAMG;IACH,OAAO,CAAC,KAAK;CAkBd"}
|
|
@@ -39,6 +39,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
39
39
|
const classification_decorator_js_1 = require("../enforcement/classification.decorator.js");
|
|
40
40
|
const tokens_js_1 = require("./tokens.js");
|
|
41
41
|
const trust_proxy_check_js_1 = require("./rate-limit/trust-proxy-check.js");
|
|
42
|
+
const deliver_email_js_1 = require("../messaging/deliver-email.js");
|
|
42
43
|
const email_address_js_1 = require("./email-address.js");
|
|
43
44
|
const email_change_messages_js_1 = require("./email-change-messages.js");
|
|
44
45
|
function fail(status, error) {
|
|
@@ -59,7 +60,8 @@ let ConfirmEmailController = class ConfirmEmailController {
|
|
|
59
60
|
auditSink;
|
|
60
61
|
limiter;
|
|
61
62
|
trustProxyCheck;
|
|
62
|
-
|
|
63
|
+
messageDispatcher;
|
|
64
|
+
constructor(tokens, addresses, sweeper, resetTokens, emailSender, auditSink, limiter, trustProxyCheck, messageDispatcher) {
|
|
63
65
|
this.tokens = tokens;
|
|
64
66
|
this.addresses = addresses;
|
|
65
67
|
this.sweeper = sweeper;
|
|
@@ -68,6 +70,7 @@ let ConfirmEmailController = class ConfirmEmailController {
|
|
|
68
70
|
this.auditSink = auditSink;
|
|
69
71
|
this.limiter = limiter;
|
|
70
72
|
this.trustProxyCheck = trustProxyCheck;
|
|
73
|
+
this.messageDispatcher = messageDispatcher;
|
|
71
74
|
}
|
|
72
75
|
async confirmEmail(body, ip) {
|
|
73
76
|
const tokens = this.tokens;
|
|
@@ -138,9 +141,8 @@ let ConfirmEmailController = class ConfirmEmailController {
|
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
143
|
if (outcome.oldEmail !== null && outcome.oldEmail !== '' && this.emailSender != null) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
.catch(() => undefined); // best-effort: the change is committed
|
|
144
|
+
// Best-effort (the change is committed); G-010: bounded and audited (`security_notice`).
|
|
145
|
+
await (0, deliver_email_js_1.deliverEmail)(this.messageDispatcher, this.emailSender, 'security_notice', (0, email_change_messages_js_1.emailChangedNoticeMessage)(outcome.oldEmail, maskedNew, Date.now()), { actorId: binding.principalId, tenantId: binding.tenantId });
|
|
144
146
|
}
|
|
145
147
|
this.audit(binding, 'account.email_changed', {
|
|
146
148
|
newEmail: maskedNew,
|
|
@@ -201,5 +203,7 @@ exports.ConfirmEmailController = ConfirmEmailController = __decorate([
|
|
|
201
203
|
__param(6, (0, common_1.Inject)(tokens_js_1.SHIELD_CONFIRM_EMAIL_LIMITER)),
|
|
202
204
|
__param(7, (0, common_1.Optional)()),
|
|
203
205
|
__param(7, (0, common_1.Inject)(trust_proxy_check_js_1.TrustProxyBootCheck)),
|
|
204
|
-
|
|
206
|
+
__param(8, (0, common_1.Optional)()),
|
|
207
|
+
__param(8, (0, common_1.Inject)(tokens_js_1.SHIELD_MESSAGE_DISPATCHER)),
|
|
208
|
+
__metadata("design:paramtypes", [Object, Object, Object, Object, Object, Object, Object, Object, Object])
|
|
205
209
|
], ConfirmEmailController);
|
|
@@ -19,6 +19,8 @@ export declare const DEFAULT_EMAIL_CHANGE_LINK_BASE_URL = "http://localhost:5173
|
|
|
19
19
|
* first, and the reset advice is qualified rather than given as the remedy.
|
|
20
20
|
*/
|
|
21
21
|
export declare const NOT_YOU_SENTENCE: string;
|
|
22
|
+
/** Escape a value for HTML text or a DOUBLE-quoted attribute (shared by framework messages). */
|
|
23
|
+
export declare function escapeHtml(value: string): string;
|
|
22
24
|
/** The confirm link: the token rides in the fragment, never the query (A4). */
|
|
23
25
|
export declare function emailChangeLink(base: string, rawToken: string): string;
|
|
24
26
|
export declare function confirmEmailChangeMessage(to: string, link: string): EmailMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-change-messages.d.ts","sourceRoot":"","sources":["../../src/auth/email-change-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD,sFAAsF;AACtF,eAAO,MAAM,kCAAkC,wCAAwC,CAAC;AAExF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAE8C,CAAC;
|
|
1
|
+
{"version":3,"file":"email-change-messages.d.ts","sourceRoot":"","sources":["../../src/auth/email-change-messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAElD,sFAAsF;AACtF,eAAO,MAAM,kCAAkC,wCAAwC,CAAC;AAExF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAE8C,CAAC;AAE5E,gGAAgG;AAChG,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAehF;AAED,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,GACX,YAAY,CAYd"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.NOT_YOU_SENTENCE = exports.DEFAULT_EMAIL_CHANGE_LINK_BASE_URL = exports.EMAIL_CHANGE_TTL_MS = void 0;
|
|
4
|
+
exports.escapeHtml = escapeHtml;
|
|
4
5
|
exports.emailChangeLink = emailChangeLink;
|
|
5
6
|
exports.confirmEmailChangeMessage = confirmEmailChangeMessage;
|
|
6
7
|
exports.emailChangedNoticeMessage = emailChangedNoticeMessage;
|
|
@@ -16,6 +17,7 @@ exports.DEFAULT_EMAIL_CHANGE_LINK_BASE_URL = 'http://localhost:5173/confirm-emai
|
|
|
16
17
|
*/
|
|
17
18
|
exports.NOT_YOU_SENTENCE = "If this wasn't you, contact your administrator immediately. Your sign-in email may have " +
|
|
18
19
|
'changed too, so a password reset from the login page may not reach you.';
|
|
20
|
+
/** Escape a value for HTML text or a DOUBLE-quoted attribute (shared by framework messages). */
|
|
19
21
|
function escapeHtml(value) {
|
|
20
22
|
return value
|
|
21
23
|
.replace(/&/g, '&')
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EmailMessage } from './ports/email-sender.js';
|
|
2
|
+
/**
|
|
3
|
+
* The forgot-password email (G-010 R9: the link is HTML-escaped; before G-010 it was interpolated
|
|
4
|
+
* raw). The text part carries the plain link.
|
|
5
|
+
*/
|
|
6
|
+
export declare function passwordResetMessage(to: string, link: string): EmailMessage;
|
|
7
|
+
//# sourceMappingURL=password-reset-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-message.d.ts","sourceRoot":"","sources":["../../src/auth/password-reset-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG5D;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAQ3E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.passwordResetMessage = passwordResetMessage;
|
|
4
|
+
const email_change_messages_js_1 = require("./email-change-messages.js");
|
|
5
|
+
/**
|
|
6
|
+
* The forgot-password email (G-010 R9: the link is HTML-escaped; before G-010 it was interpolated
|
|
7
|
+
* raw). The text part carries the plain link.
|
|
8
|
+
*/
|
|
9
|
+
function passwordResetMessage(to, link) {
|
|
10
|
+
const safe = (0, email_change_messages_js_1.escapeHtml)(link);
|
|
11
|
+
return {
|
|
12
|
+
to,
|
|
13
|
+
subject: 'Reset your password',
|
|
14
|
+
text: `Reset your password: ${link}`,
|
|
15
|
+
html: `<p>Reset your password: <a href="${safe}">${safe}</a></p>`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
import { type DynamicModule } from '@nestjs/common';
|
|
34
34
|
/**
|
|
35
35
|
* The persistence module. `forRoot()` returns the two connection providers (both
|
|
36
|
-
* exported so AuthModule can inject them)
|
|
37
|
-
* imported when auth is wired.
|
|
36
|
+
* exported so AuthModule can inject them), the shutdown holder and the audit sealer.
|
|
37
|
+
* Opt-in: only imported when auth is wired.
|
|
38
38
|
*/
|
|
39
39
|
export declare class PersistenceModule {
|
|
40
40
|
static forRoot(): DynamicModule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.module.d.ts","sourceRoot":"","sources":["../../src/auth/persistence.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAU,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"persistence.module.d.ts","sourceRoot":"","sources":["../../src/auth/persistence.module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAU,KAAK,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AA2D3E;;;;GAIG;AACH,qBACa,iBAAiB;IAC5B,MAAM,CAAC,OAAO,IAAI,aAAa;CAOhC"}
|
|
@@ -41,7 +41,10 @@ var PersistenceModule_1;
|
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
exports.PersistenceModule = void 0;
|
|
43
43
|
const common_1 = require("@nestjs/common");
|
|
44
|
+
const app_config_js_1 = require("../config/app-config.js");
|
|
44
45
|
const config_module_js_1 = require("../config/config.module.js");
|
|
46
|
+
const audit_sealer_service_js_1 = require("../audit/audit-sealer.service.js");
|
|
47
|
+
const audit_seal_config_js_1 = require("../audit/audit-seal-config.js");
|
|
45
48
|
const tokens_js_1 = require("./tokens.js");
|
|
46
49
|
const connections_js_1 = require("./connections.js");
|
|
47
50
|
/**
|
|
@@ -74,16 +77,29 @@ const lifecycleProvider = {
|
|
|
74
77
|
useFactory: (pool, redis) => new connections_js_1.ConnectionLifecycle(pool, redis),
|
|
75
78
|
inject: [tokens_js_1.SHIELD_DB_POOL, tokens_js_1.SHIELD_REDIS],
|
|
76
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* The deferred audit sealer (audit-chain-store SPEC §5.H): registered here so it exists
|
|
82
|
+
* exactly when {@link SHIELD_DB_POOL} does. `AUDIT_SEAL_*` is read now, so an invalid value
|
|
83
|
+
* fails the boot (SPEC §13). It ticks only once its table probe finds `audit_log` and
|
|
84
|
+
* `audit_chain`; a stub or unreachable pool leaves it idle (never a boot failure).
|
|
85
|
+
*/
|
|
86
|
+
const auditSealerProvider = {
|
|
87
|
+
provide: audit_sealer_service_js_1.AuditSealerService,
|
|
88
|
+
useFactory: (pool, config) => new audit_sealer_service_js_1.AuditSealerService(pool, (0, app_config_js_1.frameworkSchemasOf)(config), {
|
|
89
|
+
config: (0, audit_seal_config_js_1.loadAuditSealConfig)(process.env),
|
|
90
|
+
}),
|
|
91
|
+
inject: [tokens_js_1.SHIELD_DB_POOL, config_module_js_1.APP_CONFIG],
|
|
92
|
+
};
|
|
77
93
|
/**
|
|
78
94
|
* The persistence module. `forRoot()` returns the two connection providers (both
|
|
79
|
-
* exported so AuthModule can inject them)
|
|
80
|
-
* imported when auth is wired.
|
|
95
|
+
* exported so AuthModule can inject them), the shutdown holder and the audit sealer.
|
|
96
|
+
* Opt-in: only imported when auth is wired.
|
|
81
97
|
*/
|
|
82
98
|
let PersistenceModule = PersistenceModule_1 = class PersistenceModule {
|
|
83
99
|
static forRoot() {
|
|
84
100
|
return {
|
|
85
101
|
module: PersistenceModule_1,
|
|
86
|
-
providers: [dbPoolProvider, redisProvider, lifecycleProvider],
|
|
102
|
+
providers: [dbPoolProvider, redisProvider, lifecycleProvider, auditSealerProvider],
|
|
87
103
|
exports: [tokens_js_1.SHIELD_DB_POOL, tokens_js_1.SHIELD_REDIS],
|
|
88
104
|
};
|
|
89
105
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* EmailSender — the delivery seam. The framework builds the message; the consumer
|
|
3
|
-
* delivers it (SendGrid, SMTP, or a test capturer). Consumer-plugged
|
|
3
|
+
* delivers it (SendGrid, SMTP, or a test capturer). Consumer-plugged, or chosen by
|
|
4
|
+
* configuration with `ShieldModule.forRoot({ emailSender: 'env' })` (G-010).
|
|
5
|
+
*
|
|
6
|
+
* `EmailMessage` is core's channel type (G-010 R1); the name and shape are unchanged, so an app's
|
|
7
|
+
* existing sender keeps working.
|
|
4
8
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readonly subject: string;
|
|
8
|
-
readonly html: string;
|
|
9
|
-
readonly text: string;
|
|
10
|
-
}
|
|
9
|
+
import type { EmailMessage } from '@astram/core';
|
|
10
|
+
export type { EmailMessage };
|
|
11
11
|
export interface EmailSender {
|
|
12
12
|
send(message: EmailMessage): Promise<void>;
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-sender.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/email-sender.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"email-sender.d.ts","sourceRoot":"","sources":["../../../src/auth/ports/email-sender.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,YAAY,EAAE,YAAY,EAAE,CAAC;AAE7B,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C"}
|
package/dist/auth/tokens.d.ts
CHANGED
|
@@ -131,6 +131,20 @@ export declare const SHIELD_CONFIRM_EMAIL_LIMITER: unique symbol;
|
|
|
131
131
|
* consumer delivers it. Consumed by forgot-password.
|
|
132
132
|
*/
|
|
133
133
|
export declare const SHIELD_EMAIL_SENDER: unique symbol;
|
|
134
|
+
/**
|
|
135
|
+
* The {@link MessageDispatcher} (G-010, `../messaging/message-dispatcher.ts`): the one exit for
|
|
136
|
+
* framework messages. Bounds, audits (`message.sent` / `message.failed`) and never throws. Every
|
|
137
|
+
* framework send uses it; `@astram/identity`'s invite injects it too.
|
|
138
|
+
*/
|
|
139
|
+
export declare const SHIELD_MESSAGE_DISPATCHER: unique symbol;
|
|
140
|
+
/**
|
|
141
|
+
* A factory for guard's Redis-backed `AttemptLimiter` (G-010 P-5): `(cfg) => AttemptLimiter`. For
|
|
142
|
+
* `@astram/identity`'s invite rate limits; exported from `AuthModule` and `@astram/guard/internal`
|
|
143
|
+
* so identity builds limiters without ever holding the raw Redis client.
|
|
144
|
+
*/
|
|
145
|
+
export declare const SHIELD_ATTEMPT_LIMITER_FACTORY: unique symbol;
|
|
146
|
+
/** Internal: the bound email provider and its per-send bound (`EmailDelivery`). Not exported. */
|
|
147
|
+
export declare const SHIELD_EMAIL_DELIVERY: unique symbol;
|
|
134
148
|
/**
|
|
135
149
|
* The `RecoveryCodeStore` (guard port, `../auth/ports/recovery-code-store.ts`) —
|
|
136
150
|
* persists Argon2-hashed one-time MFA recovery codes. Consumed by MFA enrollment.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/auth/tokens.ts"],"names":[],"mappings":"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"}
|
package/dist/auth/tokens.js
CHANGED
|
@@ -23,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.
|
|
@@ -22,6 +22,11 @@ export interface CredentialProvisioner {
|
|
|
22
22
|
/** Insert an Argon2id credential for `password` (≥ 8 chars). */
|
|
23
23
|
provisionPassword(input: PasswordCredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
24
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>>;
|
|
25
30
|
}
|
|
26
31
|
/** The principal or login hint already has a credential. Fixed message: no hint, no driver text. */
|
|
27
32
|
export declare class CredentialExistsError extends Error {
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -19,6 +19,7 @@ export declare class MariaDbCredentialProvisioner implements CredentialProvision
|
|
|
19
19
|
provisionLocked(input: CredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
20
20
|
provisionPassword(input: PasswordCredentialProvisionInput, query?: CredentialQuery): Promise<void>;
|
|
21
21
|
stateOf(principalId: string, query?: CredentialQuery): Promise<CredentialState>;
|
|
22
|
+
statesOf(principalIds: readonly string[], query?: CredentialQuery): Promise<Map<string, CredentialState>>;
|
|
22
23
|
private validate;
|
|
23
24
|
private run;
|
|
24
25
|
private insert;
|
|
@@ -1 +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;
|
|
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"}
|
|
@@ -6,11 +6,17 @@ const argon2id_password_hasher_js_1 = require("./argon2id-password-hasher.js");
|
|
|
6
6
|
const framework_schema_names_js_1 = require("../config/framework-schema-names.js");
|
|
7
7
|
const MIN_PASSWORD_LENGTH = 8;
|
|
8
8
|
const ER_DUP_ENTRY = 1062;
|
|
9
|
+
/** Max ids bound into one `statesOf` `IN (...)`. */
|
|
10
|
+
const STATES_CHUNK = 500;
|
|
9
11
|
function requireText(value, name) {
|
|
10
12
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
11
13
|
throw new TypeError(`${name} must be a non-empty string`);
|
|
12
14
|
}
|
|
13
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
|
+
}
|
|
14
20
|
class MariaDbCredentialProvisioner {
|
|
15
21
|
pool;
|
|
16
22
|
now;
|
|
@@ -40,7 +46,24 @@ class MariaDbCredentialProvisioner {
|
|
|
40
46
|
const row = rows[0];
|
|
41
47
|
if (!row)
|
|
42
48
|
return 'none';
|
|
43
|
-
return (
|
|
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;
|
|
44
67
|
}
|
|
45
68
|
validate(input) {
|
|
46
69
|
requireText(input.principalId, 'principalId');
|
|
@@ -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"}
|