@nodefony/security 10.0.0-alpha.1
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/LICENSE +544 -0
- package/README.md +182 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
- package/dist/index.js +151 -0
- package/dist/nodefony/command/security-secrets.js +158 -0
- package/dist/nodefony/command/security-token.js +335 -0
- package/dist/nodefony/command/security-user-add.js +131 -0
- package/dist/nodefony/command/security-user-delete.js +102 -0
- package/dist/nodefony/command/security-user-list.js +77 -0
- package/dist/nodefony/config/config.js +366 -0
- package/dist/nodefony/config/defineModuleConfig.js +35 -0
- package/dist/nodefony/contracts/IAccessVoter.js +13 -0
- package/dist/nodefony/contracts/IApiKey.js +1 -0
- package/dist/nodefony/contracts/IAuditEvent.js +1 -0
- package/dist/nodefony/contracts/IAuditStore.js +1 -0
- package/dist/nodefony/contracts/IAuthenticator.js +1 -0
- package/dist/nodefony/contracts/IAuthorizationService.js +1 -0
- package/dist/nodefony/contracts/IFirewall.js +1 -0
- package/dist/nodefony/contracts/IFirewallDescription.js +1 -0
- package/dist/nodefony/contracts/IJwtKeystore.js +1 -0
- package/dist/nodefony/contracts/IOAuthProvider.js +1 -0
- package/dist/nodefony/contracts/ISecuredArea.js +1 -0
- package/dist/nodefony/contracts/IToken.js +1 -0
- package/dist/nodefony/contracts/ITokenStore.js +1 -0
- package/dist/nodefony/contracts/ITotpSecret.js +1 -0
- package/dist/nodefony/contracts/ITotpSecretStore.js +1 -0
- package/dist/nodefony/contracts/IWebAuthnCredential.js +1 -0
- package/dist/nodefony/contracts/IWebAuthnCredentialStore.js +1 -0
- package/dist/nodefony/contracts/IWebhookEndpoint.js +1 -0
- package/dist/nodefony/contracts/IWebhookStore.js +1 -0
- package/dist/nodefony/contracts/index.js +2 -0
- package/dist/nodefony/errors/AccessDeniedError.js +14 -0
- package/dist/nodefony/errors/ApiKeyError.js +21 -0
- package/dist/nodefony/errors/AuthenticationError.js +14 -0
- package/dist/nodefony/errors/CsrfError.js +23 -0
- package/dist/nodefony/errors/InvalidTargetError.js +39 -0
- package/dist/nodefony/errors/SsrfError.js +17 -0
- package/dist/nodefony/errors/ThrottledError.js +21 -0
- package/dist/nodefony/errors/UnverifiableTokenError.js +42 -0
- package/dist/nodefony/errors/WebAuthnError.js +21 -0
- package/dist/nodefony/errors/index.js +9 -0
- package/dist/nodefony/service/accessTokenVerifier.js +77 -0
- package/dist/nodefony/service/apiKeys.js +310 -0
- package/dist/nodefony/service/auditService.js +145 -0
- package/dist/nodefony/service/authFlow.js +332 -0
- package/dist/nodefony/service/authorization.js +95 -0
- package/dist/nodefony/service/cors.js +81 -0
- package/dist/nodefony/service/csrf.js +97 -0
- package/dist/nodefony/service/firewall.js +699 -0
- package/dist/nodefony/service/oauth2.js +153 -0
- package/dist/nodefony/service/securityHeaders.js +80 -0
- package/dist/nodefony/service/tokenService.js +486 -0
- package/dist/nodefony/service/totp.js +209 -0
- package/dist/nodefony/service/webAuthn.js +343 -0
- package/dist/nodefony/service/webhooks.js +539 -0
- package/dist/nodefony/src/RoleHierarchyWalker.js +77 -0
- package/dist/nodefony/src/SecuredArea.js +51 -0
- package/dist/nodefony/src/admin/SecurityAdminApi.js +495 -0
- package/dist/nodefony/src/admin/WebhookAdminApi.js +378 -0
- package/dist/nodefony/src/admin/adminAudit.js +37 -0
- package/dist/nodefony/src/admin/userRevocationCascade.js +40 -0
- package/dist/nodefony/src/apikey/apiKeyFormat.js +107 -0
- package/dist/nodefony/src/audit/MemoryAuditStore.js +121 -0
- package/dist/nodefony/src/audit/auditBridge.js +82 -0
- package/dist/nodefony/src/audit/auditFilters.js +60 -0
- package/dist/nodefony/src/audit/auditStoreRegistry.js +25 -0
- package/dist/nodefony/src/audit/readAuditContext.js +24 -0
- package/dist/nodefony/src/audit/recordAudit.js +16 -0
- package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +36 -0
- package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +164 -0
- package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +224 -0
- package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +174 -0
- package/dist/nodefony/src/authenticator/JwtAuthenticator.js +176 -0
- package/dist/nodefony/src/authenticator/SessionAuthenticator.js +92 -0
- package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +95 -0
- package/dist/nodefony/src/authenticator/authenticatorRegistry.js +63 -0
- package/dist/nodefony/src/authenticator/bearer.js +2 -0
- package/dist/nodefony/src/authenticator/externalSubject.js +36 -0
- package/dist/nodefony/src/authenticator/peekIssuer.js +56 -0
- package/dist/nodefony/src/crypto/secretCipher.js +79 -0
- package/dist/nodefony/src/csp.js +54 -0
- package/dist/nodefony/src/csrfToken.js +65 -0
- package/dist/nodefony/src/net/ssrfGuard.js +130 -0
- package/dist/nodefony/src/oauth/oauthProviderRegistry.js +37 -0
- package/dist/nodefony/src/oauth/providers/github.js +65 -0
- package/dist/nodefony/src/oauth/providers/oidc.js +48 -0
- package/dist/nodefony/src/realtime/UserRealtimeToken.js +94 -0
- package/dist/nodefony/src/realtime/frameAuthorizer.js +279 -0
- package/dist/nodefony/src/realtime/realtimeContracts.js +1 -0
- package/dist/nodefony/src/sessionIdentity.js +35 -0
- package/dist/nodefony/src/throttle/LoginThrottler.js +97 -0
- package/dist/nodefony/src/token/AnonymousToken.js +40 -0
- package/dist/nodefony/src/token/JwtKeystore.js +160 -0
- package/dist/nodefony/src/token/MemoryTokenStore.js +236 -0
- package/dist/nodefony/src/token/RemoteJwtVerifier.js +231 -0
- package/dist/nodefony/src/token/UserToken.js +67 -0
- package/dist/nodefony/src/token/jwtRuntime.js +19 -0
- package/dist/nodefony/src/token/secretFile.js +134 -0
- package/dist/nodefony/src/token/tokenCriteria.js +35 -0
- package/dist/nodefony/src/token/tokenFilters.js +72 -0
- package/dist/nodefony/src/token/tokenSort.js +40 -0
- package/dist/nodefony/src/token/tokenStatus.js +35 -0
- package/dist/nodefony/src/token/tokenStoreRegistry.js +25 -0
- package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +97 -0
- package/dist/nodefony/src/totp/totpCipher.js +30 -0
- package/dist/nodefony/src/totp/totpCrypto.js +226 -0
- package/dist/nodefony/src/totp/totpOperations.js +129 -0
- package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +18 -0
- package/dist/nodefony/src/voter/RoleVoter.js +32 -0
- package/dist/nodefony/src/voter/ScopeVoter.js +52 -0
- package/dist/nodefony/src/voter/voterRegistry.js +20 -0
- package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +121 -0
- package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +18 -0
- package/dist/nodefony/src/webhook/MemoryWebhookStore.js +87 -0
- package/dist/nodefony/src/webhook/WebhookDispatcher.js +208 -0
- package/dist/nodefony/src/webhook/webhookCipher.js +27 -0
- package/dist/nodefony/src/webhook/webhookDelivery.js +102 -0
- package/dist/nodefony/src/webhook/webhookFilters.js +56 -0
- package/dist/nodefony/src/webhook/webhookSignature.js +51 -0
- package/dist/nodefony/src/webhook/webhookSort.js +48 -0
- package/dist/nodefony/src/webhook/webhookStoreRegistry.js +18 -0
- package/dist/types/index.d.ts +157 -0
- package/dist/types/nodefony/command/security-secrets.d.ts +24 -0
- package/dist/types/nodefony/command/security-token.d.ts +44 -0
- package/dist/types/nodefony/command/security-user-add.d.ts +28 -0
- package/dist/types/nodefony/command/security-user-delete.d.ts +25 -0
- package/dist/types/nodefony/command/security-user-list.d.ts +28 -0
- package/dist/types/nodefony/config/config.d.ts +295 -0
- package/dist/types/nodefony/config/defineModuleConfig.d.ts +27 -0
- package/dist/types/nodefony/contracts/IAccessVoter.d.ts +23 -0
- package/dist/types/nodefony/contracts/IApiKey.d.ts +75 -0
- package/dist/types/nodefony/contracts/IAuditEvent.d.ts +94 -0
- package/dist/types/nodefony/contracts/IAuditStore.d.ts +80 -0
- package/dist/types/nodefony/contracts/IAuthenticator.d.ts +66 -0
- package/dist/types/nodefony/contracts/IAuthorizationService.d.ts +28 -0
- package/dist/types/nodefony/contracts/IFirewall.d.ts +64 -0
- package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +120 -0
- package/dist/types/nodefony/contracts/IJwtKeystore.d.ts +40 -0
- package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +51 -0
- package/dist/types/nodefony/contracts/ISecuredArea.d.ts +57 -0
- package/dist/types/nodefony/contracts/IToken.d.ts +41 -0
- package/dist/types/nodefony/contracts/ITokenStore.d.ts +240 -0
- package/dist/types/nodefony/contracts/ITotpSecret.d.ts +41 -0
- package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +88 -0
- package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +56 -0
- package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +118 -0
- package/dist/types/nodefony/contracts/IWebhookEndpoint.d.ts +82 -0
- package/dist/types/nodefony/contracts/IWebhookStore.d.ts +85 -0
- package/dist/types/nodefony/contracts/index.d.ts +9 -0
- package/dist/types/nodefony/errors/AccessDeniedError.d.ts +10 -0
- package/dist/types/nodefony/errors/ApiKeyError.d.ts +17 -0
- package/dist/types/nodefony/errors/AuthenticationError.d.ts +10 -0
- package/dist/types/nodefony/errors/CsrfError.d.ts +19 -0
- package/dist/types/nodefony/errors/InvalidTargetError.d.ts +34 -0
- package/dist/types/nodefony/errors/SsrfError.d.ts +13 -0
- package/dist/types/nodefony/errors/ThrottledError.d.ts +16 -0
- package/dist/types/nodefony/errors/UnverifiableTokenError.d.ts +37 -0
- package/dist/types/nodefony/errors/WebAuthnError.d.ts +17 -0
- package/dist/types/nodefony/errors/index.d.ts +8 -0
- package/dist/types/nodefony/service/accessTokenVerifier.d.ts +29 -0
- package/dist/types/nodefony/service/apiKeys.d.ts +103 -0
- package/dist/types/nodefony/service/auditService.d.ts +30 -0
- package/dist/types/nodefony/service/authFlow.d.ts +123 -0
- package/dist/types/nodefony/service/authorization.d.ts +33 -0
- package/dist/types/nodefony/service/cors.d.ts +48 -0
- package/dist/types/nodefony/service/csrf.d.ts +57 -0
- package/dist/types/nodefony/service/firewall.d.ts +148 -0
- package/dist/types/nodefony/service/oauth2.d.ts +66 -0
- package/dist/types/nodefony/service/securityHeaders.d.ts +66 -0
- package/dist/types/nodefony/service/tokenService.d.ts +103 -0
- package/dist/types/nodefony/service/totp.d.ts +58 -0
- package/dist/types/nodefony/service/webAuthn.d.ts +123 -0
- package/dist/types/nodefony/service/webhooks.d.ts +160 -0
- package/dist/types/nodefony/src/RoleHierarchyWalker.d.ts +21 -0
- package/dist/types/nodefony/src/SecuredArea.d.ts +31 -0
- package/dist/types/nodefony/src/admin/SecurityAdminApi.d.ts +82 -0
- package/dist/types/nodefony/src/admin/WebhookAdminApi.d.ts +30 -0
- package/dist/types/nodefony/src/admin/adminAudit.d.ts +27 -0
- package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +31 -0
- package/dist/types/nodefony/src/apikey/apiKeyFormat.d.ts +43 -0
- package/dist/types/nodefony/src/audit/MemoryAuditStore.d.ts +33 -0
- package/dist/types/nodefony/src/audit/auditBridge.d.ts +49 -0
- package/dist/types/nodefony/src/audit/auditFilters.d.ts +56 -0
- package/dist/types/nodefony/src/audit/auditStoreRegistry.d.ts +37 -0
- package/dist/types/nodefony/src/audit/readAuditContext.d.ts +17 -0
- package/dist/types/nodefony/src/audit/recordAudit.d.ts +13 -0
- package/dist/types/nodefony/src/authenticator/AnonymousAuthenticator.d.ts +26 -0
- package/dist/types/nodefony/src/authenticator/ApiKeyAuthenticator.d.ts +74 -0
- package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +132 -0
- package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +78 -0
- package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +69 -0
- package/dist/types/nodefony/src/authenticator/SessionAuthenticator.d.ts +70 -0
- package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +53 -0
- package/dist/types/nodefony/src/authenticator/authenticatorRegistry.d.ts +39 -0
- package/dist/types/nodefony/src/authenticator/bearer.d.ts +22 -0
- package/dist/types/nodefony/src/authenticator/externalSubject.d.ts +27 -0
- package/dist/types/nodefony/src/authenticator/peekIssuer.d.ts +31 -0
- package/dist/types/nodefony/src/crypto/secretCipher.d.ts +31 -0
- package/dist/types/nodefony/src/csp.d.ts +39 -0
- package/dist/types/nodefony/src/csrfToken.d.ts +36 -0
- package/dist/types/nodefony/src/net/ssrfGuard.d.ts +43 -0
- package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +45 -0
- package/dist/types/nodefony/src/oauth/providers/github.d.ts +9 -0
- package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +35 -0
- package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +62 -0
- package/dist/types/nodefony/src/realtime/frameAuthorizer.d.ts +171 -0
- package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +139 -0
- package/dist/types/nodefony/src/sessionIdentity.d.ts +20 -0
- package/dist/types/nodefony/src/throttle/LoginThrottler.d.ts +68 -0
- package/dist/types/nodefony/src/token/AnonymousToken.d.ts +23 -0
- package/dist/types/nodefony/src/token/JwtKeystore.d.ts +43 -0
- package/dist/types/nodefony/src/token/MemoryTokenStore.d.ts +66 -0
- package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +149 -0
- package/dist/types/nodefony/src/token/UserToken.d.ts +41 -0
- package/dist/types/nodefony/src/token/jwtRuntime.d.ts +28 -0
- package/dist/types/nodefony/src/token/secretFile.d.ts +70 -0
- package/dist/types/nodefony/src/token/tokenCriteria.d.ts +20 -0
- package/dist/types/nodefony/src/token/tokenFilters.d.ts +76 -0
- package/dist/types/nodefony/src/token/tokenSort.d.ts +33 -0
- package/dist/types/nodefony/src/token/tokenStatus.d.ts +38 -0
- package/dist/types/nodefony/src/token/tokenStoreRegistry.d.ts +38 -0
- package/dist/types/nodefony/src/totp/MemoryTotpSecretStore.d.ts +43 -0
- package/dist/types/nodefony/src/totp/totpCipher.d.ts +9 -0
- package/dist/types/nodefony/src/totp/totpCrypto.d.ts +164 -0
- package/dist/types/nodefony/src/totp/totpOperations.d.ts +73 -0
- package/dist/types/nodefony/src/totp/totpSecretStoreRegistry.d.ts +27 -0
- package/dist/types/nodefony/src/voter/RoleVoter.d.ts +25 -0
- package/dist/types/nodefony/src/voter/ScopeVoter.d.ts +30 -0
- package/dist/types/nodefony/src/voter/voterRegistry.d.ts +33 -0
- package/dist/types/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.d.ts +39 -0
- package/dist/types/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.d.ts +26 -0
- package/dist/types/nodefony/src/webhook/MemoryWebhookStore.d.ts +37 -0
- package/dist/types/nodefony/src/webhook/WebhookDispatcher.d.ts +69 -0
- package/dist/types/nodefony/src/webhook/webhookCipher.d.ts +8 -0
- package/dist/types/nodefony/src/webhook/webhookDelivery.d.ts +28 -0
- package/dist/types/nodefony/src/webhook/webhookFilters.d.ts +64 -0
- package/dist/types/nodefony/src/webhook/webhookSignature.d.ts +20 -0
- package/dist/types/nodefony/src/webhook/webhookSort.d.ts +39 -0
- package/dist/types/nodefony/src/webhook/webhookStoreRegistry.d.ts +31 -0
- package/docs/api-keys.md +691 -0
- package/docs/audit.md +751 -0
- package/docs/authenticators.md +487 -0
- package/docs/authorization.md +497 -0
- package/docs/cors.md +497 -0
- package/docs/csrf.md +392 -0
- package/docs/external-jwt.md +181 -0
- package/docs/firewall.md +546 -0
- package/docs/headers.md +616 -0
- package/docs/index.md +207 -0
- package/docs/lexique.md +190 -0
- package/docs/oauth2.md +575 -0
- package/docs/obtenir-un-jeton.md +225 -0
- package/docs/tokens.md +520 -0
- package/docs/totp.md +804 -0
- package/docs/webauthn.md +733 -0
- package/docs/webhooks.md +1016 -0
- package/package.json +83 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const areaSchema: z.ZodObject<{
|
|
3
|
+
pattern: z.ZodString;
|
|
4
|
+
security: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
stateless: z.ZodDefault<z.ZodBoolean>;
|
|
6
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
7
|
+
all: "all";
|
|
8
|
+
first: "first";
|
|
9
|
+
}>>;
|
|
10
|
+
authenticators: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
11
|
+
entryPoint: z.ZodOptional<z.ZodString>;
|
|
12
|
+
host: z.ZodOptional<z.ZodString>;
|
|
13
|
+
resource: z.ZodOptional<z.ZodString>;
|
|
14
|
+
realtime: z.ZodDefault<z.ZodBoolean>;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
export declare const securityConfigSchema: z.ZodObject<{
|
|
17
|
+
encoders: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
18
|
+
type: z.ZodDefault<z.ZodEnum<{
|
|
19
|
+
argon2id: "argon2id";
|
|
20
|
+
bcrypt: "bcrypt";
|
|
21
|
+
}>>;
|
|
22
|
+
memoryKiB: z.ZodDefault<z.ZodNumber>;
|
|
23
|
+
timeCost: z.ZodDefault<z.ZodNumber>;
|
|
24
|
+
parallelism: z.ZodDefault<z.ZodNumber>;
|
|
25
|
+
rounds: z.ZodDefault<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strict>>>;
|
|
27
|
+
roleHierarchy: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
28
|
+
areas: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
29
|
+
pattern: z.ZodString;
|
|
30
|
+
security: z.ZodDefault<z.ZodBoolean>;
|
|
31
|
+
stateless: z.ZodDefault<z.ZodBoolean>;
|
|
32
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
33
|
+
all: "all";
|
|
34
|
+
first: "first";
|
|
35
|
+
}>>;
|
|
36
|
+
authenticators: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
37
|
+
entryPoint: z.ZodOptional<z.ZodString>;
|
|
38
|
+
host: z.ZodOptional<z.ZodString>;
|
|
39
|
+
resource: z.ZodOptional<z.ZodString>;
|
|
40
|
+
realtime: z.ZodDefault<z.ZodBoolean>;
|
|
41
|
+
}, z.core.$strict>>>;
|
|
42
|
+
realtimeChannels: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
43
|
+
pattern: z.ZodString;
|
|
44
|
+
authenticated: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
46
|
+
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
47
|
+
}, z.core.$strict>>>;
|
|
48
|
+
cors: z.ZodDefault<z.ZodObject<{
|
|
49
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
50
|
+
origins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
51
|
+
credentials: z.ZodDefault<z.ZodBoolean>;
|
|
52
|
+
methods: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
53
|
+
allowedHeaders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
54
|
+
exposedHeaders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
55
|
+
maxAgeS: z.ZodDefault<z.ZodNumber>;
|
|
56
|
+
}, z.core.$strict>>;
|
|
57
|
+
csrf: z.ZodDefault<z.ZodObject<{
|
|
58
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
59
|
+
fetchMetadata: z.ZodDefault<z.ZodBoolean>;
|
|
60
|
+
sameSite: z.ZodDefault<z.ZodEnum<{
|
|
61
|
+
Lax: "Lax";
|
|
62
|
+
None: "None";
|
|
63
|
+
Strict: "Strict";
|
|
64
|
+
}>>;
|
|
65
|
+
checkOrigin: z.ZodDefault<z.ZodBoolean>;
|
|
66
|
+
strictSameSite: z.ZodDefault<z.ZodBoolean>;
|
|
67
|
+
trustedOrigins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
68
|
+
secret: z.ZodOptional<z.ZodString>;
|
|
69
|
+
}, z.core.$strict>>;
|
|
70
|
+
headers: z.ZodDefault<z.ZodObject<{
|
|
71
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
hsts: z.ZodDefault<z.ZodBoolean>;
|
|
73
|
+
hstsMaxAgeS: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
csp: z.ZodDefault<z.ZodString>;
|
|
75
|
+
cspNonces: z.ZodDefault<z.ZodBoolean>;
|
|
76
|
+
frameguard: z.ZodDefault<z.ZodEnum<{
|
|
77
|
+
deny: "deny";
|
|
78
|
+
sameorigin: "sameorigin";
|
|
79
|
+
}>>;
|
|
80
|
+
noSniff: z.ZodDefault<z.ZodBoolean>;
|
|
81
|
+
referrerPolicy: z.ZodDefault<z.ZodEnum<{
|
|
82
|
+
"no-referrer": "no-referrer";
|
|
83
|
+
"no-referrer-when-downgrade": "no-referrer-when-downgrade";
|
|
84
|
+
origin: "origin";
|
|
85
|
+
"origin-when-cross-origin": "origin-when-cross-origin";
|
|
86
|
+
"same-origin": "same-origin";
|
|
87
|
+
"strict-origin": "strict-origin";
|
|
88
|
+
"strict-origin-when-cross-origin": "strict-origin-when-cross-origin";
|
|
89
|
+
"unsafe-url": "unsafe-url";
|
|
90
|
+
}>>;
|
|
91
|
+
hidePoweredBy: z.ZodDefault<z.ZodBoolean>;
|
|
92
|
+
coop: z.ZodOptional<z.ZodEnum<{
|
|
93
|
+
"same-origin": "same-origin";
|
|
94
|
+
"same-origin-allow-popups": "same-origin-allow-popups";
|
|
95
|
+
"unsafe-none": "unsafe-none";
|
|
96
|
+
}>>;
|
|
97
|
+
coep: z.ZodOptional<z.ZodEnum<{
|
|
98
|
+
credentialless: "credentialless";
|
|
99
|
+
"require-corp": "require-corp";
|
|
100
|
+
"unsafe-none": "unsafe-none";
|
|
101
|
+
}>>;
|
|
102
|
+
corp: z.ZodOptional<z.ZodEnum<{
|
|
103
|
+
"cross-origin": "cross-origin";
|
|
104
|
+
"same-origin": "same-origin";
|
|
105
|
+
"same-site": "same-site";
|
|
106
|
+
}>>;
|
|
107
|
+
originAgentCluster: z.ZodOptional<z.ZodBoolean>;
|
|
108
|
+
permissionsPolicy: z.ZodOptional<z.ZodString>;
|
|
109
|
+
}, z.core.$strict>>;
|
|
110
|
+
rateLimit: z.ZodDefault<z.ZodObject<{
|
|
111
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
112
|
+
freeAttempts: z.ZodDefault<z.ZodNumber>;
|
|
113
|
+
baseDelayS: z.ZodDefault<z.ZodNumber>;
|
|
114
|
+
capDelayS: z.ZodDefault<z.ZodNumber>;
|
|
115
|
+
maxTracked: z.ZodDefault<z.ZodNumber>;
|
|
116
|
+
}, z.core.$strict>>;
|
|
117
|
+
jwt: z.ZodDefault<z.ZodObject<{
|
|
118
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
119
|
+
alg: z.ZodDefault<z.ZodEnum<{
|
|
120
|
+
EdDSA: "EdDSA";
|
|
121
|
+
RS256: "RS256";
|
|
122
|
+
}>>;
|
|
123
|
+
accessTtlS: z.ZodDefault<z.ZodNumber>;
|
|
124
|
+
refreshTtlS: z.ZodDefault<z.ZodNumber>;
|
|
125
|
+
rotateRefresh: z.ZodDefault<z.ZodBoolean>;
|
|
126
|
+
jwks: z.ZodDefault<z.ZodBoolean>;
|
|
127
|
+
audiences: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
128
|
+
issuer: z.ZodOptional<z.ZodString>;
|
|
129
|
+
keystore: z.ZodDefault<z.ZodObject<{
|
|
130
|
+
keySetJson: z.ZodOptional<z.ZodString>;
|
|
131
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
132
|
+
}, z.core.$strict>>;
|
|
133
|
+
}, z.core.$strict>>;
|
|
134
|
+
tokenStore: z.ZodDefault<z.ZodObject<{
|
|
135
|
+
store: z.ZodDefault<z.ZodString>;
|
|
136
|
+
gcIntervalS: z.ZodDefault<z.ZodNumber>;
|
|
137
|
+
gcJitter: z.ZodDefault<z.ZodBoolean>;
|
|
138
|
+
retentionRevokedDays: z.ZodDefault<z.ZodNumber>;
|
|
139
|
+
}, z.core.$strict>>;
|
|
140
|
+
passkeys: z.ZodDefault<z.ZodObject<{
|
|
141
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
142
|
+
rpId: z.ZodOptional<z.ZodString>;
|
|
143
|
+
rpName: z.ZodOptional<z.ZodString>;
|
|
144
|
+
origins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
145
|
+
userVerification: z.ZodDefault<z.ZodEnum<{
|
|
146
|
+
discouraged: "discouraged";
|
|
147
|
+
preferred: "preferred";
|
|
148
|
+
required: "required";
|
|
149
|
+
}>>;
|
|
150
|
+
residentKey: z.ZodDefault<z.ZodEnum<{
|
|
151
|
+
discouraged: "discouraged";
|
|
152
|
+
preferred: "preferred";
|
|
153
|
+
required: "required";
|
|
154
|
+
}>>;
|
|
155
|
+
authenticatorAttachment: z.ZodDefault<z.ZodEnum<{
|
|
156
|
+
any: "any";
|
|
157
|
+
"cross-platform": "cross-platform";
|
|
158
|
+
platform: "platform";
|
|
159
|
+
}>>;
|
|
160
|
+
attestation: z.ZodDefault<z.ZodEnum<{
|
|
161
|
+
direct: "direct";
|
|
162
|
+
enterprise: "enterprise";
|
|
163
|
+
none: "none";
|
|
164
|
+
}>>;
|
|
165
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
166
|
+
maxPerUser: z.ZodDefault<z.ZodNumber>;
|
|
167
|
+
challengeTtlS: z.ZodDefault<z.ZodNumber>;
|
|
168
|
+
store: z.ZodDefault<z.ZodString>;
|
|
169
|
+
}, z.core.$strict>>;
|
|
170
|
+
totp: z.ZodDefault<z.ZodObject<{
|
|
171
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
172
|
+
issuer: z.ZodOptional<z.ZodString>;
|
|
173
|
+
algorithm: z.ZodDefault<z.ZodEnum<{
|
|
174
|
+
SHA1: "SHA1";
|
|
175
|
+
SHA256: "SHA256";
|
|
176
|
+
SHA512: "SHA512";
|
|
177
|
+
}>>;
|
|
178
|
+
digits: z.ZodDefault<z.ZodNumber>;
|
|
179
|
+
period: z.ZodDefault<z.ZodNumber>;
|
|
180
|
+
window: z.ZodDefault<z.ZodNumber>;
|
|
181
|
+
recoveryCodes: z.ZodDefault<z.ZodNumber>;
|
|
182
|
+
encryptionKey: z.ZodOptional<z.ZodString>;
|
|
183
|
+
store: z.ZodDefault<z.ZodString>;
|
|
184
|
+
}, z.core.$strict>>;
|
|
185
|
+
resourceServer: z.ZodDefault<z.ZodObject<{
|
|
186
|
+
issuers: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
187
|
+
issuer: z.ZodString;
|
|
188
|
+
jwksUri: z.ZodOptional<z.ZodString>;
|
|
189
|
+
algorithms: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
190
|
+
ES256: "ES256";
|
|
191
|
+
ES384: "ES384";
|
|
192
|
+
ES512: "ES512";
|
|
193
|
+
Ed25519: "Ed25519";
|
|
194
|
+
EdDSA: "EdDSA";
|
|
195
|
+
PS256: "PS256";
|
|
196
|
+
PS384: "PS384";
|
|
197
|
+
PS512: "PS512";
|
|
198
|
+
RS256: "RS256";
|
|
199
|
+
RS384: "RS384";
|
|
200
|
+
RS512: "RS512";
|
|
201
|
+
}>>>;
|
|
202
|
+
typ: z.ZodOptional<z.ZodString>;
|
|
203
|
+
requiredClaims: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
204
|
+
subjectMapping: z.ZodDefault<z.ZodEnum<{
|
|
205
|
+
prefixed: "prefixed";
|
|
206
|
+
subject: "subject";
|
|
207
|
+
}>>;
|
|
208
|
+
}, z.core.$strict>>>;
|
|
209
|
+
subjectPolicy: z.ZodDefault<z.ZodEnum<{
|
|
210
|
+
ephemeral: "ephemeral";
|
|
211
|
+
require: "require";
|
|
212
|
+
}>>;
|
|
213
|
+
ephemeralRoles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
214
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
215
|
+
cooldownMs: z.ZodDefault<z.ZodNumber>;
|
|
216
|
+
cacheMaxAgeMs: z.ZodDefault<z.ZodNumber>;
|
|
217
|
+
clockToleranceS: z.ZodDefault<z.ZodNumber>;
|
|
218
|
+
}, z.core.$strict>>;
|
|
219
|
+
tokenExchange: z.ZodDefault<z.ZodObject<{
|
|
220
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
221
|
+
}, z.core.$strict>>;
|
|
222
|
+
oauth2: z.ZodDefault<z.ZodObject<{
|
|
223
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
224
|
+
defaultRoles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
225
|
+
allowSignup: z.ZodDefault<z.ZodBoolean>;
|
|
226
|
+
successRedirect: z.ZodDefault<z.ZodString>;
|
|
227
|
+
failureRedirect: z.ZodDefault<z.ZodString>;
|
|
228
|
+
providers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
229
|
+
clientId: z.ZodString;
|
|
230
|
+
clientSecret: z.ZodString;
|
|
231
|
+
redirectUri: z.ZodString;
|
|
232
|
+
issuer: z.ZodOptional<z.ZodString>;
|
|
233
|
+
scopes: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
234
|
+
successRedirect: z.ZodOptional<z.ZodString>;
|
|
235
|
+
failureRedirect: z.ZodOptional<z.ZodString>;
|
|
236
|
+
defaultRoles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
237
|
+
}, z.core.$strict>>>;
|
|
238
|
+
}, z.core.$strict>>;
|
|
239
|
+
apiKeys: z.ZodDefault<z.ZodObject<{
|
|
240
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
241
|
+
prefix: z.ZodDefault<z.ZodString>;
|
|
242
|
+
defaultExpiryDays: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
|
|
243
|
+
lastUsedThrottleS: z.ZodDefault<z.ZodNumber>;
|
|
244
|
+
maxPerSubject: z.ZodDefault<z.ZodNumber>;
|
|
245
|
+
allowedScopes: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
246
|
+
}, z.core.$strict>>;
|
|
247
|
+
webhooks: z.ZodDefault<z.ZodObject<{
|
|
248
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
249
|
+
signAlg: z.ZodDefault<z.ZodEnum<{
|
|
250
|
+
sha256: "sha256";
|
|
251
|
+
}>>;
|
|
252
|
+
timestampToleranceS: z.ZodDefault<z.ZodNumber>;
|
|
253
|
+
denyPrivateIps: z.ZodDefault<z.ZodBoolean>;
|
|
254
|
+
snapshotTtlS: z.ZodDefault<z.ZodNumber>;
|
|
255
|
+
maxRetries: z.ZodDefault<z.ZodNumber>;
|
|
256
|
+
autoDisableThreshold: z.ZodDefault<z.ZodNumber>;
|
|
257
|
+
deliveryTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
258
|
+
maxConcurrent: z.ZodDefault<z.ZodNumber>;
|
|
259
|
+
maxQueue: z.ZodDefault<z.ZodNumber>;
|
|
260
|
+
allowHttp: z.ZodDefault<z.ZodBoolean>;
|
|
261
|
+
store: z.ZodDefault<z.ZodString>;
|
|
262
|
+
encryptionKey: z.ZodOptional<z.ZodString>;
|
|
263
|
+
}, z.core.$strict>>;
|
|
264
|
+
audit: z.ZodDefault<z.ZodObject<{
|
|
265
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
266
|
+
store: z.ZodDefault<z.ZodString>;
|
|
267
|
+
immutable: z.ZodDefault<z.ZodBoolean>;
|
|
268
|
+
stream: z.ZodDefault<z.ZodBoolean>;
|
|
269
|
+
retentionDays: z.ZodDefault<z.ZodNumber>;
|
|
270
|
+
}, z.core.$strict>>;
|
|
271
|
+
studio: z.ZodDefault<z.ZodObject<{
|
|
272
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
273
|
+
exposure: z.ZodDefault<z.ZodEnum<{
|
|
274
|
+
localhost: "localhost";
|
|
275
|
+
private: "private";
|
|
276
|
+
public: "public";
|
|
277
|
+
}>>;
|
|
278
|
+
allowedIps: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
279
|
+
requireMfa: z.ZodDefault<z.ZodBoolean>;
|
|
280
|
+
auditAllActions: z.ZodDefault<z.ZodBoolean>;
|
|
281
|
+
}, z.core.$strict>>;
|
|
282
|
+
}, z.core.$strict>;
|
|
283
|
+
/** Entrée du builder (champs avec défaut optionnels). */
|
|
284
|
+
export type ISecurityConfigInput = z.input<typeof securityConfigSchema>;
|
|
285
|
+
/** Config normalisée et gelée (sortie du builder, lue par le firewall). */
|
|
286
|
+
export type ISecurityConfig = z.output<typeof securityConfigSchema>;
|
|
287
|
+
/** Config d'une zone normalisée. */
|
|
288
|
+
export type ISecurityAreaConfig = z.output<typeof areaSchema>;
|
|
289
|
+
/**
|
|
290
|
+
* Défauts du module, matérialisés depuis le schéma (source unique — tous les
|
|
291
|
+
* `.default()` : Zero Trust, CORS strict, Studio OFF). Toujours valides par
|
|
292
|
+
* construction ; passés au `super(..., config)` du Module class.
|
|
293
|
+
*/
|
|
294
|
+
declare const config: ISecurityConfig;
|
|
295
|
+
export default config;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ISecurityConfig, ISecurityConfigInput } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Builder type-safe de la configuration de sécurité Nodefony (PUR — ne retape
|
|
4
|
+
* JAMAIS un défaut : source unique = `./config.ts`, le schéma Zod commenté).
|
|
5
|
+
*
|
|
6
|
+
* ⭐ TL;DR : MACHINERIE DE BOOT — on n'édite (presque) jamais ce fichier. Même
|
|
7
|
+
* pattern que `nodefony.config.ts` ↔ `defineConfig()` du core : `config.ts` PORTE
|
|
8
|
+
* la config (schéma + défauts), `define<X>Config()` la VALIDE au boot (parse +
|
|
9
|
+
* env + freeze) et publie le JSON Schema Studio.
|
|
10
|
+
*
|
|
11
|
+
* Valide + normalise + gèle ; conflits de patterns de zones détectés au boot.
|
|
12
|
+
*/
|
|
13
|
+
export type { ISecurityConfig, ISecurityConfigInput, ISecurityAreaConfig, } from "./config.js";
|
|
14
|
+
/**
|
|
15
|
+
* Valide + normalise + gèle la configuration de sécurité.
|
|
16
|
+
*
|
|
17
|
+
* @param config - configuration brute de l'app (sections omises = défauts sûrs).
|
|
18
|
+
* @returns config gelée prête pour le firewall.
|
|
19
|
+
* @throws ZodError si invalide ; Error si deux zones partagent un pattern.
|
|
20
|
+
*/
|
|
21
|
+
export declare function defineSecurityConfig(config?: ISecurityConfigInput): ISecurityConfig;
|
|
22
|
+
/**
|
|
23
|
+
* JSON Schema introspectable de la config sécurité — **Studio génère son
|
|
24
|
+
* formulaire d'édition depuis ça** (labels/types/défauts/descriptions),
|
|
25
|
+
* sans UI hardcodée. Surface du data plane `/nodefony/security/api/config/schema`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function securityConfigJsonSchema(): unknown;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IToken } from "./IToken.js";
|
|
2
|
+
/**
|
|
3
|
+
* Vote d'un {@link IAccessVoter}. Stratégie firewall = affirmative + DENY veto :
|
|
4
|
+
* un seul DENY bloque ; sinon un GRANT suffit ; tous ABSTAIN → DENY (Zero Trust).
|
|
5
|
+
*/
|
|
6
|
+
export declare enum VoterVote {
|
|
7
|
+
GRANT = "GRANT",
|
|
8
|
+
DENY = "DENY",
|
|
9
|
+
ABSTAIN = "ABSTAIN"
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Voter contextuel (niveau C de l'autorisation, P6.8) — décide d'un accès en
|
|
13
|
+
* fonction du token ET du sujet (ex. « cet user édite-t-il SON projet ? »).
|
|
14
|
+
*
|
|
15
|
+
* Découverts par DI au boot (`@injectable`), indexés par `supports()`. Killer
|
|
16
|
+
* feature pour le métier complexe (multi-tenant, ownership).
|
|
17
|
+
*/
|
|
18
|
+
export interface IAccessVoter {
|
|
19
|
+
/** Ce voter sait-il décider de cet attribut/sujet ? */
|
|
20
|
+
supports(attribute: string, subject?: unknown): boolean;
|
|
21
|
+
/** Vote pour l'attribut donné sur le sujet donné. */
|
|
22
|
+
vote(token: IToken, attribute: string, subject?: unknown): Promise<VoterVote>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue **publique** d'une clé API (PAT) — projection de `IAccessTokenRecord`
|
|
3
|
+
* **sans le secret ni son hash** (jamais exposés après création). C'est la forme
|
|
4
|
+
* renvoyée par la console (« mes clés ») et le data plane Studio.
|
|
5
|
+
*/
|
|
6
|
+
export interface IApiKeyView {
|
|
7
|
+
/** Identifiant public de la clé (révocation, affichage). */
|
|
8
|
+
id: string;
|
|
9
|
+
/** Préfixe public affichable (`<prefix>_<pubid>`, ex. `nf_a1b2c3d4`). */
|
|
10
|
+
prefix: string | null;
|
|
11
|
+
/** Libellé humain (« CI deploy », « app mobile »). */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Capacités accordées (axe distinct des rôles RBAC). */
|
|
14
|
+
scopes: string[];
|
|
15
|
+
/** Porteur (id utilisateur ou service account). */
|
|
16
|
+
subjectId: string;
|
|
17
|
+
/** Discriminant du porteur. */
|
|
18
|
+
subjectType: "user" | "service";
|
|
19
|
+
/** Organisation/tenant (`null` = global). */
|
|
20
|
+
tenantId: string | null;
|
|
21
|
+
/** Création (epoch ms). */
|
|
22
|
+
createdAt: number;
|
|
23
|
+
/** Expiration (epoch ms) ou `null` (sans expiration). */
|
|
24
|
+
expiresAt: number | null;
|
|
25
|
+
/** Dernier usage (epoch ms) ou `null` (jamais utilisée). */
|
|
26
|
+
lastUsedAt: number | null;
|
|
27
|
+
/**
|
|
28
|
+
* Adresse du dernier usage, ou `null`. C'est ce qui permet à un porteur de
|
|
29
|
+
* reconnaître une clé qui a servi ailleurs que là où il l'a déployée — donc de
|
|
30
|
+
* dater une fuite. Dépouillée de `X-Forwarded-For` selon `trustProxy`.
|
|
31
|
+
*/
|
|
32
|
+
lastUsedIp: string | null;
|
|
33
|
+
/** Agent du dernier usage, ou `null` (même usage : reconnaître l'inattendu). */
|
|
34
|
+
lastUsedUserAgent: string | null;
|
|
35
|
+
/** Révocation (epoch ms) ou `null` (active). */
|
|
36
|
+
revokedAt: number | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Résultat d'une **création** de clé — la vue publique **+ le token en clair**.
|
|
40
|
+
* Le `token` n'est disponible qu'ICI, une seule fois : il n'est jamais stocké
|
|
41
|
+
* (seul son `sha256` l'est) ni re-dérivable. La console l'affiche puis l'oublie.
|
|
42
|
+
*/
|
|
43
|
+
export interface IApiKeyCreated extends IApiKeyView {
|
|
44
|
+
/** Secret EN CLAIR — affiché UNE seule fois (RFC : « shown once »). */
|
|
45
|
+
token: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Capacités/contraintes d'émission des clés API — exposées à la console (« Mes
|
|
49
|
+
* clés ») pour un formulaire de création **honnête** : plafond par porteur,
|
|
50
|
+
* catalogue de scopes, préfixe public, durée par défaut. **Aucune valeur
|
|
51
|
+
* sensible** (config publique : le préfixe figure déjà dans chaque clé).
|
|
52
|
+
*/
|
|
53
|
+
export interface IApiKeyCapabilities {
|
|
54
|
+
/** Émission de clés activée en config (`apiKeys.enabled`). */
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
/** Marqueur public des clés (`<prefix>_…`, ex. `nf`). */
|
|
57
|
+
prefix: string;
|
|
58
|
+
/** Expiration par défaut en jours (`null` = sans expiration). */
|
|
59
|
+
defaultExpiryDays: number | null;
|
|
60
|
+
/** Plafond de clés ACTIVES par porteur (création au-delà → 409). */
|
|
61
|
+
maxPerSubject: number;
|
|
62
|
+
/** Catalogue de scopes autorisés (`null` = libre : tout scope non vide accepté). */
|
|
63
|
+
allowedScopes: string[] | null;
|
|
64
|
+
}
|
|
65
|
+
/** Options de création d'une clé API. */
|
|
66
|
+
export interface ICreateApiKeyOptions {
|
|
67
|
+
/** Libellé humain (obligatoire, non vide). */
|
|
68
|
+
name: string;
|
|
69
|
+
/** Capacités demandées (⊆ `apiKeys.allowedScopes` si un catalogue est défini). */
|
|
70
|
+
scopes?: string[];
|
|
71
|
+
/** Durée de vie en jours ; `null` = sans expiration ; omis = défaut config. */
|
|
72
|
+
expiresInDays?: number | null;
|
|
73
|
+
/** Tenant/organisation (multi-tenant) ; `null` = global. */
|
|
74
|
+
tenantId?: string | null;
|
|
75
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modèle d'un **événement de sécurité** auditable (P6.14) — login, refus d'accès,
|
|
3
|
+
* émission/révocation de jeton, défense CSRF/CORS, verrou de frame WS. Vocabulaire
|
|
4
|
+
* MÉTIER de sécurité, distinct du log technique du trafic (`AuditLogEntry`,
|
|
5
|
+
* `@nodefony/http` P3.1, 1 PDU/requête) : ici on trace une **transition d'état**
|
|
6
|
+
* de sécurité, pas chaque requête. Sérialisable JSON (stream WS + persistance).
|
|
7
|
+
*
|
|
8
|
+
* Règle d'or : un **secret n'entre JAMAIS** dans un événement (ni mot de passe, ni
|
|
9
|
+
* jeton, ni cookie) — seule sa *présence* est tracée via {@link IAuditEvent.flags}.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Sous-système de sécurité concerné — axe de filtrage principal de la console
|
|
13
|
+
* d'audit (Studio P6.15). Fermé : la liste des sous-systèmes est stable.
|
|
14
|
+
*
|
|
15
|
+
* ⚠️ Quatre catégories sont RÉSERVÉES — déclarées au contrat mais **sans émetteur à
|
|
16
|
+
* ce jour** (aucun `record({ category: … })` ne les produit) : `oauth`, `csrf`,
|
|
17
|
+
* `cors`, `config`. La console Studio ne propose donc PAS de filtre pour elles (un
|
|
18
|
+
* filtre qui ne rend jamais rien induit en erreur — cf `studio/.../auditModel.ts`).
|
|
19
|
+
* En câbler une = ajouter son émetteur ICI (l'émetteur), puis la métadonnée
|
|
20
|
+
* d'affichage dans `AUDIT_CATEGORIES`. Émettent réellement : auth, authz, token,
|
|
21
|
+
* session, webauthn, ws, webhook.
|
|
22
|
+
*/
|
|
23
|
+
export type AuditCategory = "auth" | "authz" | "token" | "session" | "oauth" | "webauthn" | "csrf" | "cors" | "ws" | "webhook" | "config";
|
|
24
|
+
/**
|
|
25
|
+
* Issue d'une action de sécurité. La distinction `failure`/`denied` est utile à
|
|
26
|
+
* l'auditeur : `failure` = l'acteur a échoué une preuve (brute-force, signature
|
|
27
|
+
* invalide) ; `denied` = une **politique** a refusé un acteur même bien formé
|
|
28
|
+
* (rôle manquant, origine interdite, plancher WS) — signal d'accès non autorisé.
|
|
29
|
+
*/
|
|
30
|
+
export type AuditOutcome = "success" | "failure" | "denied";
|
|
31
|
+
/** Drapeaux de **présence** de matériel sensible (jamais la valeur). */
|
|
32
|
+
export interface IAuditEventFlags {
|
|
33
|
+
/** Un en-tête `Authorization` était présent. */
|
|
34
|
+
hasAuthorization?: boolean;
|
|
35
|
+
/** Un cookie était présent. */
|
|
36
|
+
hasCookie?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Un événement de sécurité journalisé. `id`/`ts` sont posés par l'`AuditService`
|
|
40
|
+
* (l'émetteur ne fournit qu'un {@link IAuditEventDraft}) → un seul appel système
|
|
41
|
+
* d'horodatage, centralisé hors des points d'émission.
|
|
42
|
+
*/
|
|
43
|
+
export interface IAuditEvent {
|
|
44
|
+
/** Identifiant unique dans le process (corrélation + curseur de pagination). */
|
|
45
|
+
id: string;
|
|
46
|
+
/** Horodatage (epoch ms, `Date.now()`), posé par le service. */
|
|
47
|
+
ts: number;
|
|
48
|
+
/** Sous-système concerné — axe de filtrage principal. */
|
|
49
|
+
category: AuditCategory;
|
|
50
|
+
/**
|
|
51
|
+
* Action conventionnée `<sujet>.<verbe>` (ex. `"login.success"`,
|
|
52
|
+
* `"access.denied"`, `"token.revoked"`, `"session.opened"`). String **ouverte**
|
|
53
|
+
* (la liste grandit lot par lot) — PAS un enum fermé, qui coupleraient
|
|
54
|
+
* `IAuditEvent` à chaque point d'émission. Table de référence : README §Audit.
|
|
55
|
+
*/
|
|
56
|
+
action: string;
|
|
57
|
+
/** Issue de l'action. */
|
|
58
|
+
outcome: AuditOutcome;
|
|
59
|
+
/**
|
|
60
|
+
* Acteur : identité (`token.getUserIdentifier()`) ou `null` si anonyme/non
|
|
61
|
+
* identifié (tentative pré-authentification). Un **libellé d'identité**, jamais
|
|
62
|
+
* un secret.
|
|
63
|
+
*/
|
|
64
|
+
actor: string | null;
|
|
65
|
+
/**
|
|
66
|
+
* Ressource visée (zone firewall, route, canal WS, attribut d'autorisation) —
|
|
67
|
+
* descripteur léger ; jamais le corps ni les en-têtes de la requête.
|
|
68
|
+
*/
|
|
69
|
+
resource?: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Raison **machine** stable et filtrable (ex. `"invalid_credentials"`,
|
|
72
|
+
* `"throttled"`, `"veto"`, `"origin_mismatch"`) — PAS un message libre traduit.
|
|
73
|
+
*/
|
|
74
|
+
reason?: string | null;
|
|
75
|
+
/** IP source si disponible (audit « from where »). */
|
|
76
|
+
ip?: string | null;
|
|
77
|
+
/** User-Agent source si disponible. */
|
|
78
|
+
userAgent?: string | null;
|
|
79
|
+
/** Id de requête (corrélation log ↔ audit via l'ALS, P1.4) ; `null` hors requête. */
|
|
80
|
+
requestId?: string | null;
|
|
81
|
+
/**
|
|
82
|
+
* **Présence** (jamais la valeur) de matériel sensible au moment de l'événement
|
|
83
|
+
* — calque `AuditLogEntry.hasAuthorization`. Trace « un cookie / un Authorization
|
|
84
|
+
* était là » sans jamais journaliser le secret lui-même.
|
|
85
|
+
*/
|
|
86
|
+
flags?: IAuditEventFlags;
|
|
87
|
+
/** Extras applicatifs libres (anti-migration) ; absents par défaut. */
|
|
88
|
+
metadata?: Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Champs fournis par l'**émetteur** d'un événement — l'`AuditService` y ajoute
|
|
92
|
+
* `id` + `ts`. Garde les points d'émission concis et sans appel système d'horloge.
|
|
93
|
+
*/
|
|
94
|
+
export type IAuditEventDraft = Omit<IAuditEvent, "id" | "ts">;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { IPage, IPageQuery } from "nodefony";
|
|
2
|
+
import type { AuditCategory, AuditOutcome, IAuditEvent, IAuditEventDraft } from "./IAuditEvent.js";
|
|
3
|
+
/**
|
|
4
|
+
* Filtre de lecture du journal d'audit (console Studio P6.15, data plane P6.14
|
|
5
|
+
* Lot 3) — {@link IPageQuery} + les critères propres au journal. Tous les
|
|
6
|
+
* critères sont **AND** ; absents = non filtrant.
|
|
7
|
+
*
|
|
8
|
+
* Pagination par **curseur** (jamais `offset`) : un journal reçoit des écritures
|
|
9
|
+
* pendant qu'on le parcourt, un décalage numérique glisserait d'une page à
|
|
10
|
+
* l'autre. Le curseur est **opaque** — sa forme appartient au store, l'appelant
|
|
11
|
+
* ne fait que repasser le {@link IPage.nextCursor} reçu.
|
|
12
|
+
*
|
|
13
|
+
* `q` (hérité) n'est **pas** appliqué : filtrer un journal se fait par ses axes
|
|
14
|
+
* (catégorie / issue / acteur / action / requête / fenêtre), pas en plein texte.
|
|
15
|
+
*/
|
|
16
|
+
export interface IAuditListQuery extends IPageQuery {
|
|
17
|
+
/** Restreint à un sous-système. */
|
|
18
|
+
category?: AuditCategory;
|
|
19
|
+
/** Restreint à une issue (`success`/`failure`/`denied`). */
|
|
20
|
+
outcome?: AuditOutcome;
|
|
21
|
+
/** Restreint à un acteur (égalité exacte de l'identifiant). */
|
|
22
|
+
actor?: string;
|
|
23
|
+
/** Restreint à une action conventionnée (`"login.success"`…). */
|
|
24
|
+
action?: string;
|
|
25
|
+
/** Restreint aux événements d'une **requête** (corrélation trace ↔ audit, P6.15). */
|
|
26
|
+
requestId?: string;
|
|
27
|
+
/** Borne basse d'horodatage (epoch ms, inclus). */
|
|
28
|
+
since?: number;
|
|
29
|
+
/** Borne haute d'horodatage (epoch ms, inclus). */
|
|
30
|
+
until?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Journal d'audit **append-only** (tamper-evident) — état serveur des événements
|
|
34
|
+
* de sécurité. **Pluggable** par backend (mémoire/fichier/ORM/Loki) comme
|
|
35
|
+
* `ITokenStore`. La référence mémoire borne le volume (anti-fuite) ; un backend
|
|
36
|
+
* partagé (ORM/Redis) sert le multi-pod et la rétention longue.
|
|
37
|
+
*
|
|
38
|
+
* `append` est la SEULE écriture : pas d'`update` ni de `delete` ciblé — seul le
|
|
39
|
+
* `gc` (rétention) retire des entrées. L'immuabilité est la garantie d'audit.
|
|
40
|
+
*/
|
|
41
|
+
export interface IAuditStore {
|
|
42
|
+
/** Ajoute un événement (immuable, jamais modifié ensuite). */
|
|
43
|
+
append(event: IAuditEvent): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Lit **une page** d'événements filtrés, du plus récent au plus ancien —
|
|
46
|
+
* l'implémentation ne matérialise jamais plus que `limit` événements.
|
|
47
|
+
*
|
|
48
|
+
* `total` n'est renseigné que si `withTotal` n'est pas `false` (un `COUNT`
|
|
49
|
+
* filtré sur un journal de rétention longue se paie) ; {@link IPage.hasNext}
|
|
50
|
+
* et {@link IPage.nextCursor} sont fiables dans les deux cas.
|
|
51
|
+
*/
|
|
52
|
+
listPage(query: IAuditListQuery): Promise<IPage<IAuditEvent>>;
|
|
53
|
+
/**
|
|
54
|
+
* Purge les événements au-delà de la fenêtre de rétention. À planifier
|
|
55
|
+
* périodiquement par le propriétaire du store (timer `unref`). Sans appel, un
|
|
56
|
+
* backend sans TTL natif s'accumule jusqu'à sa borne de volume.
|
|
57
|
+
*
|
|
58
|
+
* @returns nombre d'événements purgés.
|
|
59
|
+
*/
|
|
60
|
+
gc(now?: number): Promise<number>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Face **émission** du journal, vue par les points sensibles (firewall,
|
|
64
|
+
* authenticators, controllers OAuth/WebAuthn). `record` est **synchrone et
|
|
65
|
+
* fire-and-forget** : l'audit ne doit JAMAIS bloquer ni faire échouer le flux
|
|
66
|
+
* métier (un store en panne se solde par un log d'erreur, pas par un login KO).
|
|
67
|
+
* No-op à coût nul si l'audit est désactivé.
|
|
68
|
+
*/
|
|
69
|
+
export interface IAuditSink {
|
|
70
|
+
/**
|
|
71
|
+
* Journalise un événement : pose `id`+`ts`, persiste (best-effort) et notifie
|
|
72
|
+
* les abonnés live. Aucun effet (aucune allocation côté service) si désactivé.
|
|
73
|
+
*/
|
|
74
|
+
record(event: IAuditEventDraft): void;
|
|
75
|
+
/**
|
|
76
|
+
* S'abonne au flux live des événements (bridge WS, P6.14 Lot 4). Renvoie une
|
|
77
|
+
* fonction de désabonnement (cleanup obligatoire — pas de listener orphelin).
|
|
78
|
+
*/
|
|
79
|
+
subscribe(listener: (event: IAuditEvent) => void): () => void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ContextType } from "@nodefony/http";
|
|
2
|
+
import type { IToken } from "./IToken.js";
|
|
3
|
+
import type { ISecuredArea } from "./ISecuredArea.js";
|
|
4
|
+
/**
|
|
5
|
+
* Stratégie d'authentification — pattern « authenticator » : une classe = une
|
|
6
|
+
* façon de prouver qui on est. Pas de Bridge/Factory (héritage Passport/YAML
|
|
7
|
+
* abandonné).
|
|
8
|
+
*
|
|
9
|
+
* Une zone sécurisée ({@link ISecuredArea}) liste les noms d'authenticators à
|
|
10
|
+
* exécuter selon son `mode` : `"first"` (le premier dont `supports()` est vrai
|
|
11
|
+
* authentifie) ou `"all"` (tous doivent passer — MFA, le dernier porte
|
|
12
|
+
* l'identité). Cycle : `createToken()` extrait le credential (token non
|
|
13
|
+
* authentifié), `authenticate()` valide et promeut ou `throw` (→ 401).
|
|
14
|
+
*
|
|
15
|
+
* Implémentations : `AnonymousAuthenticator`, `UserPasswordAuthenticator`,
|
|
16
|
+
* `SessionAuthenticator`, `JwtAuthenticator`, `ApiKeyAuthenticator`.
|
|
17
|
+
*
|
|
18
|
+
* Le login social ne passe **pas** par un authenticator : `OAuth2Service`
|
|
19
|
+
* échange le code, provisionne l'utilisateur et ouvre une session BFF — c'est
|
|
20
|
+
* ensuite `SessionAuthenticator` qui ré-authentifie les requêtes suivantes.
|
|
21
|
+
*/
|
|
22
|
+
export interface IAuthenticator {
|
|
23
|
+
/** Nom logique (référencé dans `area.authenticators`). */
|
|
24
|
+
readonly name: string;
|
|
25
|
+
/** Peut-on extraire un credential de cette requête ? (sinon authenticator suivant). */
|
|
26
|
+
supports(context: ContextType): boolean;
|
|
27
|
+
/** Construit un token NON authentifié depuis la requête (extraction credential). */
|
|
28
|
+
createToken(context: ContextType): Promise<IToken>;
|
|
29
|
+
/** Valide le token (vérifie le credential) ou `throw` une AuthenticationError. */
|
|
30
|
+
authenticate(token: IToken): Promise<IToken>;
|
|
31
|
+
/** Hook succès — pose le token (cookie JWT, log audit S1…). */
|
|
32
|
+
onSuccess(context: ContextType, token: IToken): Promise<void>;
|
|
33
|
+
/** Hook échec — log audit, throttling (J2)… Le 401 + challenge sont posés par le firewall. */
|
|
34
|
+
onFailure(context: ContextType, error: Error): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Challenge `WWW-Authenticate` (RFC 7235 : tout 401 DOIT en porter un).
|
|
37
|
+
* Le firewall pose celui du premier authenticator de la zone qui en déclare.
|
|
38
|
+
* Ex. `'Basic realm="nodefony", charset="UTF-8"'`, `'Bearer'`.
|
|
39
|
+
*
|
|
40
|
+
* La ZONE est passée parce qu'un défi peut dépendre d'elle — le pointeur
|
|
41
|
+
* `resource_metadata` de la RFC 9728 nomme la ressource, et une instance
|
|
42
|
+
* d'authenticator est partagée entre toutes les zones qui la listent. Sans
|
|
43
|
+
* cet argument, un authenticator ne pouvait rendre qu'un défi constant, donc
|
|
44
|
+
* muet sur la ressource réellement visée.
|
|
45
|
+
*
|
|
46
|
+
* @param area - zone qui a refusé ; absente si l'appelant n'en a pas
|
|
47
|
+
*/
|
|
48
|
+
challenge?(area?: ISecuredArea): string;
|
|
49
|
+
/**
|
|
50
|
+
* Vérifie AU BOOT que la zone qui référence cet authenticator lui donne ce
|
|
51
|
+
* dont il a besoin — sinon `throw`, et la configuration n'existe pas.
|
|
52
|
+
*
|
|
53
|
+
* Appelée une fois par zone qui le liste, avant la première requête. C'est
|
|
54
|
+
* l'authenticator qui dit ce qu'il exige, jamais le firewall : celui-ci ne
|
|
55
|
+
* connaît aucun nom en dur, et un plugin tiers doit pouvoir poser ses propres
|
|
56
|
+
* exigences sans qu'on touche au cœur.
|
|
57
|
+
*
|
|
58
|
+
* Le contrôle vaut d'être fait ici plutôt qu'à la première requête, où il se
|
|
59
|
+
* manifesterait par un 401 sans cause visible, chez un seul appelant, un jour
|
|
60
|
+
* quelconque.
|
|
61
|
+
*
|
|
62
|
+
* @param area - la zone qui référence cet authenticator
|
|
63
|
+
* @throws Error si la zone ne remplit pas les conditions d'emploi
|
|
64
|
+
*/
|
|
65
|
+
validateArea?(area: ISecuredArea): void;
|
|
66
|
+
}
|