emilsoftware-utilities 2.0.0-dev.80 → 2.0.0-dev.81

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.
@@ -8,5 +8,6 @@
8
8
  export * from "../../accessi-module";
9
9
  export * from "./standalone";
10
10
  export * from "./tenants";
11
+ export * from "./tenantStores";
11
12
  export type { AccessiOptions as AccessiServerOptions } from "../../accessi-module";
12
13
  export { createAccessiOptionsFromEnv, createCatalogScriptsOptionsFromEnv, parseAccessiPreviousPublicKeys, } from "../../accessi-module/database-updates/accessiOptionsFromEnv";
@@ -25,6 +25,7 @@ exports.parseAccessiPreviousPublicKeys = exports.createCatalogScriptsOptionsFrom
25
25
  __exportStar(require("../../accessi-module"), exports);
26
26
  __exportStar(require("./standalone"), exports);
27
27
  __exportStar(require("./tenants"), exports);
28
+ __exportStar(require("./tenantStores"), exports);
28
29
  var accessiOptionsFromEnv_1 = require("../../accessi-module/database-updates/accessiOptionsFromEnv");
29
30
  Object.defineProperty(exports, "createAccessiOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createAccessiOptionsFromEnv; } });
30
31
  Object.defineProperty(exports, "createCatalogScriptsOptionsFromEnv", { enumerable: true, get: function () { return accessiOptionsFromEnv_1.createCatalogScriptsOptionsFromEnv; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,uDAAqC;AACrC,+CAA6B;AAC7B,4CAA0B;AAE1B,qGAIqE;AAHnE,oIAAA,2BAA2B,OAAA;AAC3B,2IAAA,kCAAkC,OAAA;AAClC,uIAAA,8BAA8B,OAAA","sourcesContent":["/**\n * Entry point server Accessi.\n *\n * Questo e l'unico entry point che puo importare database, SMTP, migrazioni,\n * console e servizi Nest completi. I backend fratelli devono usare\n * `accessi/resource-server` e `accessi/client`.\n */\nexport * from \"../../accessi-module\";\nexport * from \"./standalone\";\nexport * from \"./tenants\";\nexport type { AccessiOptions as AccessiServerOptions } from \"../../accessi-module\";\nexport {\n createAccessiOptionsFromEnv,\n createCatalogScriptsOptionsFromEnv,\n parseAccessiPreviousPublicKeys,\n} from \"../../accessi-module/database-updates/accessiOptionsFromEnv\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/accessi/server/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;GAMG;AACH,uDAAqC;AACrC,+CAA6B;AAC7B,4CAA0B;AAC1B,iDAA+B;AAE/B,qGAIqE;AAHnE,oIAAA,2BAA2B,OAAA;AAC3B,2IAAA,kCAAkC,OAAA;AAClC,uIAAA,8BAA8B,OAAA","sourcesContent":["/**\n * Entry point server Accessi.\n *\n * Questo e l'unico entry point che puo importare database, SMTP, migrazioni,\n * console e servizi Nest completi. I backend fratelli devono usare\n * `accessi/resource-server` e `accessi/client`.\n */\nexport * from \"../../accessi-module\";\nexport * from \"./standalone\";\nexport * from \"./tenants\";\nexport * from \"./tenantStores\";\nexport type { AccessiOptions as AccessiServerOptions } from \"../../accessi-module\";\nexport {\n createAccessiOptionsFromEnv,\n createCatalogScriptsOptionsFromEnv,\n parseAccessiPreviousPublicKeys,\n} from \"../../accessi-module/database-updates/accessiOptionsFromEnv\";\n"]}
@@ -0,0 +1,74 @@
1
+ import type { AccessiOptions } from '../../accessi-module/AccessiModule';
2
+ import type { RateLimitStore } from '../../accessi-module/security/rateLimitStore';
3
+ import type { ConsoleEntryStore } from '../../accessi-module/security/consoleEntry';
4
+ import type { SessionRevocationStore } from '../../accessi-module/security/sessionRevocation';
5
+ import type { AccessTokenRevocationStore } from '../../accessi-module/security/accessTokenRevocation';
6
+ import type { RefreshTokenRotationStore } from '../../accessi-module/security/refreshTokenRotation';
7
+ /** Client Redis minimo accettato dagli store Accessi. */
8
+ export interface AccessiTenantRedisClient {
9
+ eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;
10
+ }
11
+ /** Prefissi Redis per singolo store, tutti relativi al tenant. */
12
+ export interface AccessiTenantStorePrefixes {
13
+ base?: string;
14
+ rateLimit?: string;
15
+ serviceTokenRateLimit?: string;
16
+ sessionRevocation?: string;
17
+ accessTokenRevocation?: string;
18
+ refreshTokenRotation?: string;
19
+ refreshTokenFamily?: string;
20
+ consoleEntry?: string;
21
+ }
22
+ export interface ResolvedAccessiTenantStorePrefixes {
23
+ base: string;
24
+ rateLimit: string;
25
+ serviceTokenRateLimit: string;
26
+ sessionRevocation: string;
27
+ accessTokenRevocation: string;
28
+ refreshTokenRotation: string;
29
+ refreshTokenFamily: string;
30
+ consoleEntry: string;
31
+ }
32
+ export interface CreateAccessiTenantStoresOptions {
33
+ tenantId: string;
34
+ client: AccessiTenantRedisClient;
35
+ /** Segreto per cifrare i ticket console a riposo. Usare un segreto del tenant. */
36
+ encryptionKey: string | Buffer;
37
+ /** Base prefix. Default: `accessi:<tenantId>:`. */
38
+ prefix?: string;
39
+ prefixes?: AccessiTenantStorePrefixes;
40
+ /** TTL revoca sessioni. Default dello store (365 giorni). */
41
+ sessionRevocationTtlMs?: number;
42
+ /** TTL revoca jti. Default dello store (24 ore). */
43
+ accessTokenRevocationTtlMs?: number;
44
+ consoleEntryMaxIssueAttempts?: number;
45
+ /** Attiva la rotazione refresh token nell'options risultante. Default: true. */
46
+ enableRefreshTokenRotation?: boolean;
47
+ }
48
+ export interface AccessiTenantStores {
49
+ tenantId: string;
50
+ prefixes: ResolvedAccessiTenantStorePrefixes;
51
+ rateLimit: RateLimitStore;
52
+ serviceTokenRateLimit: RateLimitStore;
53
+ sessionRevocation: SessionRevocationStore;
54
+ accessTokenRevocation: AccessTokenRevocationStore;
55
+ refreshTokenRotation: RefreshTokenRotationStore;
56
+ consoleEntry: ConsoleEntryStore;
57
+ /** Collega tutti gli store alle opzioni Accessi del tenant. */
58
+ applyTo(options: AccessiOptions): AccessiOptions;
59
+ }
60
+ /**
61
+ * Prefissi Redis isolati per tenant.
62
+ *
63
+ * Ogni store ha un namespace distinto: condividere un prefisso tra tenant
64
+ * significherebbe condividere rate limit, revoche o refresh token.
65
+ */
66
+ export declare function getAccessiTenantStorePrefixes(tenantId: string, overrides?: AccessiTenantStorePrefixes): ResolvedAccessiTenantStorePrefixes;
67
+ /**
68
+ * Crea tutti gli store Redis di un tenant con prefissi gia isolati.
69
+ *
70
+ * `applyTo` restituisce una copia delle opzioni con gli store collegati:
71
+ * `publicAuthRateLimit`, `serviceTokens`, `sessionRevocation`,
72
+ * `accessTokenRevocation`, `refreshTokenRotation` e `consoleEntry`.
73
+ */
74
+ export declare function createAccessiTenantStores(options: CreateAccessiTenantStoresOptions): AccessiTenantStores;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAccessiTenantStorePrefixes = getAccessiTenantStorePrefixes;
4
+ exports.createAccessiTenantStores = createAccessiTenantStores;
5
+ const tenants_1 = require("./tenants");
6
+ const rateLimitStore_1 = require("../../accessi-module/security/rateLimitStore");
7
+ const consoleEntry_1 = require("../../accessi-module/security/consoleEntry");
8
+ const sessionRevocation_1 = require("../../accessi-module/security/sessionRevocation");
9
+ const accessTokenRevocation_1 = require("../../accessi-module/security/accessTokenRevocation");
10
+ const refreshTokenRotation_1 = require("../../accessi-module/security/refreshTokenRotation");
11
+ /**
12
+ * Prefissi Redis isolati per tenant.
13
+ *
14
+ * Ogni store ha un namespace distinto: condividere un prefisso tra tenant
15
+ * significherebbe condividere rate limit, revoche o refresh token.
16
+ */
17
+ function getAccessiTenantStorePrefixes(tenantId, overrides = {}) {
18
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19
+ const base = (_a = overrides.base) !== null && _a !== void 0 ? _a : (0, tenants_1.getAccessiTenantRedisPrefix)(tenantId);
20
+ return {
21
+ base,
22
+ rateLimit: (_b = overrides.rateLimit) !== null && _b !== void 0 ? _b : `${base}rate:`,
23
+ serviceTokenRateLimit: (_c = overrides.serviceTokenRateLimit) !== null && _c !== void 0 ? _c : `${base}service-token-rate:`,
24
+ sessionRevocation: (_d = overrides.sessionRevocation) !== null && _d !== void 0 ? _d : `${base}session:`,
25
+ accessTokenRevocation: (_e = overrides.accessTokenRevocation) !== null && _e !== void 0 ? _e : `${base}jti:`,
26
+ refreshTokenRotation: (_f = overrides.refreshTokenRotation) !== null && _f !== void 0 ? _f : `${base}refresh:`,
27
+ refreshTokenFamily: (_g = overrides.refreshTokenFamily) !== null && _g !== void 0 ? _g : `${base}refresh-family:`,
28
+ consoleEntry: (_h = overrides.consoleEntry) !== null && _h !== void 0 ? _h : `${base}console-entry:`,
29
+ };
30
+ }
31
+ /**
32
+ * Crea tutti gli store Redis di un tenant con prefissi gia isolati.
33
+ *
34
+ * `applyTo` restituisce una copia delle opzioni con gli store collegati:
35
+ * `publicAuthRateLimit`, `serviceTokens`, `sessionRevocation`,
36
+ * `accessTokenRevocation`, `refreshTokenRotation` e `consoleEntry`.
37
+ */
38
+ function createAccessiTenantStores(options) {
39
+ var _a;
40
+ if (!(options === null || options === void 0 ? void 0 : options.tenantId) || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(options.tenantId)) {
41
+ throw new Error('createAccessiTenantStores: tenantId non valido.');
42
+ }
43
+ if (!options.client || typeof options.client.eval !== 'function') {
44
+ throw new Error('createAccessiTenantStores: client.eval non configurato.');
45
+ }
46
+ const encryptionKey = options.encryptionKey;
47
+ if (encryptionKey === undefined || encryptionKey === null || encryptionKey.length === 0) {
48
+ throw new Error('createAccessiTenantStores: encryptionKey obbligatoria per i ticket console.');
49
+ }
50
+ const prefixes = getAccessiTenantStorePrefixes(options.tenantId, Object.assign(Object.assign({}, (options.prefix ? { base: options.prefix } : {})), ((_a = options.prefixes) !== null && _a !== void 0 ? _a : {})));
51
+ const { client } = options;
52
+ const enableRefreshTokenRotation = options.enableRefreshTokenRotation !== false;
53
+ const rateLimit = (0, rateLimitStore_1.createRedisRateLimitStore)({ client, keyPrefix: prefixes.rateLimit });
54
+ const serviceTokenRateLimit = (0, rateLimitStore_1.createRedisRateLimitStore)({
55
+ client,
56
+ keyPrefix: prefixes.serviceTokenRateLimit,
57
+ });
58
+ const sessionRevocation = (0, sessionRevocation_1.createRedisSessionRevocationStore)(Object.assign({ client, keyPrefix: prefixes.sessionRevocation }, (options.sessionRevocationTtlMs !== undefined ? { ttlMs: options.sessionRevocationTtlMs } : {})));
59
+ const accessTokenRevocation = (0, accessTokenRevocation_1.createRedisAccessTokenRevocationStore)(Object.assign({ client, keyPrefix: prefixes.accessTokenRevocation }, (options.accessTokenRevocationTtlMs !== undefined ? { ttlMs: options.accessTokenRevocationTtlMs } : {})));
60
+ const refreshTokenRotation = (0, refreshTokenRotation_1.createRedisRefreshTokenRotationStore)({
61
+ client,
62
+ keyPrefix: prefixes.refreshTokenRotation,
63
+ familyKeyPrefix: prefixes.refreshTokenFamily,
64
+ });
65
+ const consoleEntry = (0, consoleEntry_1.createRedisConsoleEntryStore)(Object.assign({ client,
66
+ encryptionKey, keyPrefix: prefixes.consoleEntry }, (options.consoleEntryMaxIssueAttempts !== undefined
67
+ ? { maxIssueAttempts: options.consoleEntryMaxIssueAttempts }
68
+ : {})));
69
+ return {
70
+ tenantId: options.tenantId,
71
+ prefixes,
72
+ rateLimit,
73
+ serviceTokenRateLimit,
74
+ sessionRevocation,
75
+ accessTokenRevocation,
76
+ refreshTokenRotation,
77
+ consoleEntry,
78
+ applyTo(accessiOptions) {
79
+ var _a, _b;
80
+ return Object.assign(Object.assign({}, accessiOptions), { tenantId: options.tenantId, publicAuthRateLimit: Object.assign(Object.assign({}, accessiOptions.publicAuthRateLimit), { store: rateLimit }), serviceTokens: Object.assign(Object.assign({}, accessiOptions.serviceTokens), { rateLimitStore: serviceTokenRateLimit }), sessionRevocation: Object.assign(Object.assign({}, accessiOptions.sessionRevocation), { store: sessionRevocation }), accessTokenRevocation: Object.assign(Object.assign({}, accessiOptions.accessTokenRevocation), { store: accessTokenRevocation }), refreshTokenRotation: Object.assign(Object.assign({}, accessiOptions.refreshTokenRotation), { enabled: (_b = (_a = accessiOptions.refreshTokenRotation) === null || _a === void 0 ? void 0 : _a.enabled) !== null && _b !== void 0 ? _b : enableRefreshTokenRotation, store: refreshTokenRotation }), consoleEntry: Object.assign(Object.assign({}, accessiOptions.consoleEntry), { store: consoleEntry }) });
81
+ },
82
+ };
83
+ }
84
+ //# sourceMappingURL=tenantStores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenantStores.js","sourceRoot":"","sources":["../../../src/accessi/server/tenantStores.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccessiOptions } from '../../accessi-module/AccessiModule';\nimport { getAccessiTenantRedisPrefix } from './tenants';\nimport { createRedisRateLimitStore } from '../../accessi-module/security/rateLimitStore';\nimport type { RateLimitStore } from '../../accessi-module/security/rateLimitStore';\nimport { createRedisConsoleEntryStore } from '../../accessi-module/security/consoleEntry';\nimport type { ConsoleEntryStore } from '../../accessi-module/security/consoleEntry';\nimport { createRedisSessionRevocationStore } from '../../accessi-module/security/sessionRevocation';\nimport type { SessionRevocationStore } from '../../accessi-module/security/sessionRevocation';\nimport { createRedisAccessTokenRevocationStore } from '../../accessi-module/security/accessTokenRevocation';\nimport type { AccessTokenRevocationStore } from '../../accessi-module/security/accessTokenRevocation';\nimport { createRedisRefreshTokenRotationStore } from '../../accessi-module/security/refreshTokenRotation';\nimport type { RefreshTokenRotationStore } from '../../accessi-module/security/refreshTokenRotation';\n\n/** Client Redis minimo accettato dagli store Accessi. */\nexport interface AccessiTenantRedisClient {\n eval(script: string, numKeys: number, ...args: Array<string | number>): Promise<unknown>;\n}\n\n/** Prefissi Redis per singolo store, tutti relativi al tenant. */\nexport interface AccessiTenantStorePrefixes {\n base?: string;\n rateLimit?: string;\n serviceTokenRateLimit?: string;\n sessionRevocation?: string;\n accessTokenRevocation?: string;\n refreshTokenRotation?: string;\n refreshTokenFamily?: string;\n consoleEntry?: string;\n}\n\nexport interface ResolvedAccessiTenantStorePrefixes {\n base: string;\n rateLimit: string;\n serviceTokenRateLimit: string;\n sessionRevocation: string;\n accessTokenRevocation: string;\n refreshTokenRotation: string;\n refreshTokenFamily: string;\n consoleEntry: string;\n}\n\nexport interface CreateAccessiTenantStoresOptions {\n tenantId: string;\n client: AccessiTenantRedisClient;\n /** Segreto per cifrare i ticket console a riposo. Usare un segreto del tenant. */\n encryptionKey: string | Buffer;\n /** Base prefix. Default: `accessi:<tenantId>:`. */\n prefix?: string;\n prefixes?: AccessiTenantStorePrefixes;\n /** TTL revoca sessioni. Default dello store (365 giorni). */\n sessionRevocationTtlMs?: number;\n /** TTL revoca jti. Default dello store (24 ore). */\n accessTokenRevocationTtlMs?: number;\n consoleEntryMaxIssueAttempts?: number;\n /** Attiva la rotazione refresh token nell'options risultante. Default: true. */\n enableRefreshTokenRotation?: boolean;\n}\n\nexport interface AccessiTenantStores {\n tenantId: string;\n prefixes: ResolvedAccessiTenantStorePrefixes;\n rateLimit: RateLimitStore;\n serviceTokenRateLimit: RateLimitStore;\n sessionRevocation: SessionRevocationStore;\n accessTokenRevocation: AccessTokenRevocationStore;\n refreshTokenRotation: RefreshTokenRotationStore;\n consoleEntry: ConsoleEntryStore;\n /** Collega tutti gli store alle opzioni Accessi del tenant. */\n applyTo(options: AccessiOptions): AccessiOptions;\n}\n\n/**\n * Prefissi Redis isolati per tenant.\n *\n * Ogni store ha un namespace distinto: condividere un prefisso tra tenant\n * significherebbe condividere rate limit, revoche o refresh token.\n */\nexport function getAccessiTenantStorePrefixes(\n tenantId: string,\n overrides: AccessiTenantStorePrefixes = {},\n): ResolvedAccessiTenantStorePrefixes {\n const base = overrides.base ?? getAccessiTenantRedisPrefix(tenantId);\n return {\n base,\n rateLimit: overrides.rateLimit ?? `${base}rate:`,\n serviceTokenRateLimit: overrides.serviceTokenRateLimit ?? `${base}service-token-rate:`,\n sessionRevocation: overrides.sessionRevocation ?? `${base}session:`,\n accessTokenRevocation: overrides.accessTokenRevocation ?? `${base}jti:`,\n refreshTokenRotation: overrides.refreshTokenRotation ?? `${base}refresh:`,\n refreshTokenFamily: overrides.refreshTokenFamily ?? `${base}refresh-family:`,\n consoleEntry: overrides.consoleEntry ?? `${base}console-entry:`,\n };\n}\n\n/**\n * Crea tutti gli store Redis di un tenant con prefissi gia isolati.\n *\n * `applyTo` restituisce una copia delle opzioni con gli store collegati:\n * `publicAuthRateLimit`, `serviceTokens`, `sessionRevocation`,\n * `accessTokenRevocation`, `refreshTokenRotation` e `consoleEntry`.\n */\nexport function createAccessiTenantStores(\n options: CreateAccessiTenantStoresOptions,\n): AccessiTenantStores {\n if (!options?.tenantId || !/^[a-z0-9][a-z0-9_-]{0,62}$/.test(options.tenantId)) {\n throw new Error('createAccessiTenantStores: tenantId non valido.');\n }\n if (!options.client || typeof options.client.eval !== 'function') {\n throw new Error('createAccessiTenantStores: client.eval non configurato.');\n }\n const encryptionKey = options.encryptionKey;\n if (encryptionKey === undefined || encryptionKey === null || encryptionKey.length === 0) {\n throw new Error('createAccessiTenantStores: encryptionKey obbligatoria per i ticket console.');\n }\n\n const prefixes = getAccessiTenantStorePrefixes(options.tenantId, {\n ...(options.prefix ? { base: options.prefix } : {}),\n ...(options.prefixes ?? {}),\n });\n const { client } = options;\n const enableRefreshTokenRotation = options.enableRefreshTokenRotation !== false;\n\n const rateLimit = createRedisRateLimitStore({ client, keyPrefix: prefixes.rateLimit });\n const serviceTokenRateLimit = createRedisRateLimitStore({\n client,\n keyPrefix: prefixes.serviceTokenRateLimit,\n });\n const sessionRevocation = createRedisSessionRevocationStore({\n client,\n keyPrefix: prefixes.sessionRevocation,\n ...(options.sessionRevocationTtlMs !== undefined ? { ttlMs: options.sessionRevocationTtlMs } : {}),\n });\n const accessTokenRevocation = createRedisAccessTokenRevocationStore({\n client,\n keyPrefix: prefixes.accessTokenRevocation,\n ...(options.accessTokenRevocationTtlMs !== undefined ? { ttlMs: options.accessTokenRevocationTtlMs } : {}),\n });\n const refreshTokenRotation = createRedisRefreshTokenRotationStore({\n client,\n keyPrefix: prefixes.refreshTokenRotation,\n familyKeyPrefix: prefixes.refreshTokenFamily,\n });\n const consoleEntry = createRedisConsoleEntryStore({\n client,\n encryptionKey,\n keyPrefix: prefixes.consoleEntry,\n ...(options.consoleEntryMaxIssueAttempts !== undefined\n ? { maxIssueAttempts: options.consoleEntryMaxIssueAttempts }\n : {}),\n });\n\n return {\n tenantId: options.tenantId,\n prefixes,\n rateLimit,\n serviceTokenRateLimit,\n sessionRevocation,\n accessTokenRevocation,\n refreshTokenRotation,\n consoleEntry,\n applyTo(accessiOptions) {\n return {\n ...accessiOptions,\n tenantId: options.tenantId,\n publicAuthRateLimit: {\n ...accessiOptions.publicAuthRateLimit,\n store: rateLimit,\n },\n serviceTokens: {\n ...accessiOptions.serviceTokens,\n rateLimitStore: serviceTokenRateLimit,\n },\n sessionRevocation: {\n ...accessiOptions.sessionRevocation,\n store: sessionRevocation,\n },\n accessTokenRevocation: {\n ...accessiOptions.accessTokenRevocation,\n store: accessTokenRevocation,\n },\n refreshTokenRotation: {\n ...accessiOptions.refreshTokenRotation,\n enabled: accessiOptions.refreshTokenRotation?.enabled ?? enableRefreshTokenRotation,\n store: refreshTokenRotation,\n },\n consoleEntry: {\n ...accessiOptions.consoleEntry,\n store: consoleEntry,\n },\n };\n },\n };\n}\n"]}
@@ -1,15 +1,80 @@
1
- import type { AccessiOptions } from '../../accessi-module/AccessiModule';
2
- /** Variabili usate dal manifest del tenant (oltre alle normali `ACCESSI_*`). */
3
- export interface AccessiTenantEnv extends NodeJS.ProcessEnv {
4
- ACCESSI_TENANT_ID?: string;
5
- /** Host separati da virgola; supporta `*.example.com`. */
6
- ACCESSI_TENANT_HOSTS?: string;
7
- /** Prefisso path, es. `/tenant-a`. */
8
- ACCESSI_TENANT_PATH?: string;
9
- ACCESSI_TENANT_ENABLED?: string;
10
- /** Porta del servizio quando si usa `startAccessiTenants`. */
11
- ACCESSI_TENANT_PORT?: string;
12
- ACCESSI_TENANT_DESCRIPTION?: string;
1
+ import type { AccessiOptions, AccessiHttpOptions, AdminBootstrapOptions, CatalogScriptsOptions, ConsoleEntryOptions, EmailOptions, ExtensionFieldsOptions, FederatedAuthenticationOptions, JwtOptions, PublicAuthRateLimitOptions, PublicRegistrationOptions, ServiceTokenOptions, SystemSeedsOptions } from '../../accessi-module/AccessiModule';
2
+ import type { AccessiEdgeRateLimitOptions } from '../../accessi-module/security/edgeRateLimit';
3
+ import type { AccessiAuthCookieOptions } from '../../accessi-module/security/authCookies';
4
+ import type { AccessiMetricsOptions } from '../../accessi-module/observability/metrics';
5
+ import type { AccessiAuditOptions } from '../../accessi-module/security/audit';
6
+ import type { SessionRevocationOptions } from '../../accessi-module/security/sessionRevocation';
7
+ import type { AccessTokenRevocationOptions } from '../../accessi-module/security/accessTokenRevocation';
8
+ import type { RefreshTokenRotationOptions } from '../../accessi-module/security/refreshTokenRotation';
9
+ import type { OpenApiSetupOptions } from '../../openapi/OpenApiConfig';
10
+ /** Identita e routing del tenant. */
11
+ export interface AccessiTenantSettings {
12
+ /** Minuscole, numeri, `-` e `_`; max 63 caratteri. */
13
+ id: string;
14
+ /** Host esatti o wildcard `*.example.com`. */
15
+ hosts?: string[];
16
+ /** Prefisso path, es. `/acme`. */
17
+ path?: string;
18
+ /** Porta del processo quando si usa il supervisore. */
19
+ port?: number;
20
+ /** Default `true`. */
21
+ enabled?: boolean;
22
+ description?: string;
23
+ }
24
+ /** Connessione Firebird del tenant, tipizzata senza callback. */
25
+ export interface AccessiTenantDatabaseOptions {
26
+ host: string;
27
+ port?: number;
28
+ database: string;
29
+ user?: string;
30
+ password?: string;
31
+ /** `disabled`/`optional`; `required` e il default del driver. */
32
+ wireCrypt?: 'required' | 'optional' | 'disabled';
33
+ pluginName?: string;
34
+ }
35
+ /**
36
+ * Configurazione serializzabile di un tenant.
37
+ *
38
+ * I campi che contengono store, callback o logger (`store`, `sink`, `match`,
39
+ * `onDocument`, `logger`, ...) sono esclusi: vanno collegati dal backend
40
+ * ospitante, non da un file di configurazione.
41
+ */
42
+ export interface AccessiTenantEnvironment {
43
+ tenant: AccessiTenantSettings;
44
+ database: AccessiTenantDatabaseOptions;
45
+ jwt: JwtOptions;
46
+ email: EmailOptions;
47
+ encryptionKey: string;
48
+ confirmationEmailUrl: string;
49
+ confirmationEmailReturnUrl: string;
50
+ confirmationEmailPrefix?: string;
51
+ customResetPage?: string;
52
+ publicBaseUrl?: string;
53
+ consoleApiBaseUrl?: string;
54
+ mockDemoUser?: boolean;
55
+ passwordExpiration?: boolean;
56
+ passwordExpirationDays?: number;
57
+ legacyPasswordMigrationOnStartup?: boolean;
58
+ autoUpdateDatabase?: boolean;
59
+ http?: AccessiHttpOptions;
60
+ authCookie?: AccessiAuthCookieOptions;
61
+ publicAuthRateLimit?: Omit<PublicAuthRateLimitOptions, 'store'>;
62
+ publicRegistration?: PublicRegistrationOptions;
63
+ federatedAuthentication?: FederatedAuthenticationOptions;
64
+ extensionFieldsOptions?: ExtensionFieldsOptions[];
65
+ serviceTokens?: Omit<ServiceTokenOptions, 'rateLimitStore'>;
66
+ catalogScripts?: CatalogScriptsOptions;
67
+ systemSeeds?: SystemSeedsOptions;
68
+ adminBootstrap?: AdminBootstrapOptions;
69
+ openapi?: Omit<OpenApiSetupOptions, 'onDocument'>;
70
+ consoleEntry?: Omit<ConsoleEntryOptions, 'store'>;
71
+ edgeRateLimit?: Omit<AccessiEdgeRateLimitOptions, 'store' | 'match'>;
72
+ sessionRevocation?: Omit<SessionRevocationOptions, 'store'>;
73
+ accessTokenRevocation?: Omit<AccessTokenRevocationOptions, 'store'>;
74
+ refreshTokenRotation?: Omit<RefreshTokenRotationOptions, 'store'>;
75
+ metrics?: Omit<AccessiMetricsOptions, 'sink'>;
76
+ audit?: Omit<AccessiAuditOptions, 'sinks' | 'store' | 'tenantId'>;
77
+ bufferLogs?: boolean;
13
78
  }
14
79
  export interface AccessiTenant {
15
80
  id: string;
@@ -18,10 +83,9 @@ export interface AccessiTenant {
18
83
  enabled: boolean;
19
84
  description?: string;
20
85
  port?: number;
21
- /** Nome del file `.env` di origine (mai il contenuto). */
86
+ /** Nome del file `*.environment.ts` di origine (mai il contenuto). */
22
87
  file: string;
23
- /** Variabili risolte del tenant (base + file); non serializzarle nei log. */
24
- env: NodeJS.ProcessEnv;
88
+ environmentFile: string;
25
89
  options: AccessiOptions;
26
90
  }
27
91
  export interface AccessiTenantSummary {
@@ -63,10 +127,8 @@ export interface AccessiTenantRegistry {
63
127
  summary(): AccessiTenantSummary[];
64
128
  }
65
129
  export interface LoadAccessiTenantsOptions {
66
- /** Cartella con `<tenant>.env`. */
130
+ /** Cartella con `<id>.environment.ts`. */
67
131
  directory: string;
68
- /** Variabili base (default `process.env`); il file tenant vince. */
69
- env?: NodeJS.ProcessEnv;
70
132
  /** Abilita la risoluzione via header `x-accessi-tenant`. Default: false. */
71
133
  allowHeader?: boolean;
72
134
  /** Consente issuer/audience uguali tra tenant (sconsigliato). Default: false. */
@@ -78,20 +140,47 @@ export declare class AccessiTenantConfigurationError extends Error {
78
140
  constructor(issues: string[]);
79
141
  }
80
142
  /**
81
- * Parser minimale di un file `.env`.
143
+ * Helper di tipizzazione per i file tenant.
144
+ *
145
+ * ```ts
146
+ * import type { AccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';
147
+ * import { defineAccessiTenantEnvironment } from 'emilsoftware-utilities/accessi/server';
82
148
  *
83
- * Supporta commenti, `export KEY=...`, valori tra apici singoli o doppi e le
84
- * sequenze `\n`, `\r`, `\t`, `\"` e `\\` nei valori tra doppi apici. Non
85
- * interpreta variabili annidate: la configurazione tenant deve restare esplicita.
149
+ * export default defineAccessiTenantEnvironment({
150
+ * tenant: { id: 'acme', hosts: ['accessi.acme.example.com'] },
151
+ * database: { host: 'db-acme', database: '/data/acme.fdb' },
152
+ * jwt: { secret: process.env.ACME_JWT_SECRET!, expiresIn: '1h' },
153
+ * email: { ... },
154
+ * encryptionKey: process.env.ACME_ENCRYPTION_KEY!,
155
+ * confirmationEmailUrl: 'https://accessi.acme.example.com',
156
+ * confirmationEmailReturnUrl: 'https://accessi.acme.example.com',
157
+ * });
158
+ * ```
86
159
  */
87
- export declare function parseAccessiTenantEnvFile(content: string): Record<string, string>;
160
+ export declare function defineAccessiTenantEnvironment<const T extends AccessiTenantEnvironment>(environment: T): T;
88
161
  /** Prefisso Redis consigliato per isolare gli store del tenant. */
89
162
  export declare function getAccessiTenantRedisPrefix(tenantId: string): string;
90
163
  /**
91
- * Carica i tenant da una cartella di file `<id>.env`.
164
+ * Converte la configurazione tipizzata in `AccessiOptions`.
165
+ *
166
+ * Applica i default di Firebird e collega `tenantId`; non crea store o sink,
167
+ * che restano responsabilita del backend ospitante.
168
+ */
169
+ export declare function accessiOptionsFromTenantEnvironment(environment: AccessiTenantEnvironment): AccessiOptions;
170
+ /**
171
+ * Carica un singolo file `*.environment.ts`.
172
+ *
173
+ * Usa jiti per eseguire TypeScript anche nel pacchetto compilato, senza
174
+ * richiedere un passo di build separato per i file tenant.
175
+ */
176
+ export declare function loadAccessiTenantEnvironmentFile(file: string): {
177
+ tenant: AccessiTenant;
178
+ environment: AccessiTenantEnvironment;
179
+ };
180
+ /**
181
+ * Carica i tenant da una cartella di file `<id>.environment.ts`.
92
182
  *
93
- * Non modifica `process.env`: ogni tenant riceve una copia isolata delle
94
- * variabili. Un errore in un file blocca l'intero caricamento, cosi non si
95
- * avvia un tenant con configurazione parziale.
183
+ * La validazione e aggregata: un errore blocca l'intera cartella, cosi non si
184
+ * avvia un tenant con configurazione parziale. Nessun segreto viene stampato.
96
185
  */
97
186
  export declare function loadAccessiTenants(options: LoadAccessiTenantsOptions): AccessiTenantRegistry;