@nodefony/security 10.0.0-beta.2 → 10.0.0-beta.4
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/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/index.js +48 -16
- package/dist/nodefony/command/{security-token.js → security-mcp-token.js} +62 -24
- package/dist/nodefony/command/security-oauth-doctor.js +68 -0
- package/dist/nodefony/command/security-secrets.js +52 -29
- package/dist/nodefony/command/security-user-add.js +9 -3
- package/dist/nodefony/command/security-user-delete.js +10 -7
- package/dist/nodefony/command/security-user-list.js +5 -3
- package/dist/nodefony/command/security-user-password.js +9 -14
- package/dist/nodefony/config/config.js +99 -8
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/contracts/IAccessVoter.js +1 -2
- package/dist/nodefony/contracts/index.js +1 -2
- package/dist/nodefony/errors/AccessDeniedError.js +1 -2
- package/dist/nodefony/errors/ApiKeyError.js +3 -2
- package/dist/nodefony/errors/AuthenticationError.js +1 -2
- package/dist/nodefony/errors/CsrfError.js +1 -2
- package/dist/nodefony/errors/InsecureTransportError.js +23 -0
- package/dist/nodefony/errors/InvalidTargetError.js +1 -2
- package/dist/nodefony/errors/SsrfError.js +1 -2
- package/dist/nodefony/errors/ThrottledError.js +1 -2
- package/dist/nodefony/errors/UnverifiableTokenError.js +1 -2
- package/dist/nodefony/errors/WebAuthnError.js +1 -2
- package/dist/nodefony/errors/index.js +2 -1
- package/dist/nodefony/keycloak/keycloakAdmin.js +254 -0
- package/dist/nodefony/keycloak/keycloakRealm.js +259 -0
- package/dist/nodefony/keycloak/keycloakRealmInput.js +123 -0
- package/dist/nodefony/keycloak/scaffold.js +231 -0
- package/dist/nodefony/keycloak/security-keycloak-realm.js +130 -0
- package/dist/nodefony/scaffold/contribute.js +19 -0
- package/dist/nodefony/service/accessTokenVerifier.js +101 -34
- package/dist/nodefony/service/apiKeys.js +45 -5
- package/dist/nodefony/service/auditService.js +4 -5
- package/dist/nodefony/service/authFlow.js +118 -16
- package/dist/nodefony/service/authorization.js +1 -2
- package/dist/nodefony/service/cors.js +1 -2
- package/dist/nodefony/service/csrf.js +1 -2
- package/dist/nodefony/service/firewall.js +116 -25
- package/dist/nodefony/service/keycloak.js +208 -0
- package/dist/nodefony/service/oauth2.js +403 -28
- package/dist/nodefony/service/securityHeaders.js +1 -2
- package/dist/nodefony/service/tokenService.js +94 -27
- package/dist/nodefony/service/totp.js +55 -11
- package/dist/nodefony/service/webAuthn.js +24 -7
- package/dist/nodefony/service/webhooks.js +4 -5
- package/dist/nodefony/src/RoleHierarchyWalker.js +1 -2
- package/dist/nodefony/src/SecuredArea.js +15 -5
- package/dist/nodefony/src/accountBinding.js +96 -0
- package/dist/nodefony/src/admin/SecurityAdminApi.js +71 -42
- package/dist/nodefony/src/admin/WebhookAdminApi.js +23 -7
- package/dist/nodefony/src/admin/adminAudit.js +1 -3
- package/dist/nodefony/src/admin/userRevocationCascade.js +148 -20
- package/dist/nodefony/src/apikey/apiKeyFormat.js +1 -2
- package/dist/nodefony/src/audit/MemoryAuditStore.js +1 -2
- package/dist/nodefony/src/audit/auditBridge.js +1 -2
- package/dist/nodefony/src/audit/auditFilters.js +1 -2
- package/dist/nodefony/src/audit/auditStoreRegistry.js +1 -2
- package/dist/nodefony/src/audit/readAuditContext.js +1 -2
- package/dist/nodefony/src/audit/recordAudit.js +1 -2
- package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +1 -2
- package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +3 -2
- package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +14 -59
- package/dist/nodefony/src/authenticator/ExternalSubjectResolver.js +180 -0
- package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +80 -19
- package/dist/nodefony/src/authenticator/JwtAuthenticator.js +92 -16
- package/dist/nodefony/src/authenticator/SessionAuthenticator.js +9 -5
- package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +14 -5
- package/dist/nodefony/src/authenticator/authenticatorRegistry.js +5 -12
- package/dist/nodefony/src/authenticator/bearer.js +1 -2
- package/dist/nodefony/src/authenticator/externalSubject.js +1 -2
- package/dist/nodefony/src/authenticator/peekIssuer.js +1 -2
- package/dist/nodefony/src/boot/publicRoutesNotice.js +26 -0
- package/dist/nodefony/src/crypto/secretCipher.js +1 -2
- package/dist/nodefony/src/csp.js +1 -2
- package/dist/nodefony/src/csrfToken.js +1 -2
- package/dist/nodefony/src/entryPoint.js +38 -0
- package/dist/nodefony/src/net/ssrfGuard.js +1 -2
- package/dist/nodefony/src/net/transportFailure.js +57 -0
- package/dist/nodefony/src/oauth/clientCredentials.js +39 -0
- package/dist/nodefony/src/oauth/httpJson.js +3 -3
- package/dist/nodefony/src/oauth/metadata.js +28 -12
- package/dist/nodefony/src/oauth/oauth2Client.js +61 -35
- package/dist/nodefony/src/oauth/oauthProviderRegistry.js +40 -8
- package/dist/nodefony/src/oauth/providerDiagnosis.js +282 -0
- package/dist/nodefony/src/oauth/providerRoles.js +78 -0
- package/dist/nodefony/src/oauth/providers/github.js +48 -12
- package/dist/nodefony/src/oauth/providers/oidc.js +90 -8
- package/dist/nodefony/src/openToSignup.js +46 -0
- package/dist/nodefony/src/realtime/UserRealtimeToken.js +17 -3
- package/dist/nodefony/src/realtime/frameAuthorizer.js +1 -2
- package/dist/nodefony/src/rolesWithin.js +57 -0
- package/dist/nodefony/src/sessionAuthentication.js +25 -0
- package/dist/nodefony/src/sessionIdentity.js +29 -3
- package/dist/nodefony/src/throttle/LoginThrottler.js +1 -2
- package/dist/nodefony/src/token/AnonymousToken.js +1 -2
- package/dist/nodefony/src/token/JwtKeystore.js +89 -48
- package/dist/nodefony/src/token/MemoryTokenStore.js +1 -2
- package/dist/nodefony/src/token/RemoteJwtVerifier.js +46 -16
- package/dist/nodefony/src/token/UserToken.js +1 -2
- package/dist/nodefony/src/token/jwtRuntime.js +1 -2
- package/dist/nodefony/src/token/scopeGrants.js +109 -0
- package/dist/nodefony/src/token/secretFile.js +60 -4
- package/dist/nodefony/src/token/tokenCriteria.js +1 -2
- package/dist/nodefony/src/token/tokenFilters.js +1 -2
- package/dist/nodefony/src/token/tokenSort.js +1 -2
- package/dist/nodefony/src/token/tokenStatus.js +1 -2
- package/dist/nodefony/src/token/tokenStoreRegistry.js +1 -2
- package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +1 -2
- package/dist/nodefony/src/totp/totpCipher.js +1 -2
- package/dist/nodefony/src/totp/totpCrypto.js +1 -2
- package/dist/nodefony/src/totp/totpOperations.js +1 -2
- package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +1 -2
- package/dist/nodefony/src/transport/CredentialTransportPolicy.js +50 -0
- package/dist/nodefony/src/voter/RoleVoter.js +1 -2
- package/dist/nodefony/src/voter/ScopeVoter.js +1 -2
- package/dist/nodefony/src/voter/voterRegistry.js +1 -2
- package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +1 -2
- package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +1 -2
- package/dist/nodefony/src/webhook/MemoryWebhookStore.js +1 -2
- package/dist/nodefony/src/webhook/WebhookDispatcher.js +1 -2
- package/dist/nodefony/src/webhook/webhookCipher.js +1 -2
- package/dist/nodefony/src/webhook/webhookDelivery.js +1 -2
- package/dist/nodefony/src/webhook/webhookFilters.js +1 -2
- package/dist/nodefony/src/webhook/webhookSignature.js +1 -2
- package/dist/nodefony/src/webhook/webhookSort.js +1 -2
- package/dist/nodefony/src/webhook/webhookStoreRegistry.js +1 -2
- package/dist/types/index.d.ts +26 -6
- package/dist/types/nodefony/command/{security-token.d.ts → security-mcp-token.d.ts} +16 -6
- package/dist/types/nodefony/command/security-oauth-doctor.d.ts +23 -0
- package/dist/types/nodefony/command/security-secrets.d.ts +2 -0
- package/dist/types/nodefony/command/security-user-delete.d.ts +4 -3
- package/dist/types/nodefony/command/security-user-password.d.ts +4 -4
- package/dist/types/nodefony/config/config.d.ts +64 -1
- package/dist/types/nodefony/contracts/IAuthenticator.d.ts +1 -1
- package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +42 -1
- package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +48 -0
- package/dist/types/nodefony/contracts/ISecuredArea.d.ts +22 -0
- package/dist/types/nodefony/contracts/ITotpSecret.d.ts +4 -1
- package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +7 -1
- package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +4 -1
- package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +6 -0
- package/dist/types/nodefony/errors/ApiKeyError.d.ts +3 -1
- package/dist/types/nodefony/errors/InsecureTransportError.d.ts +20 -0
- package/dist/types/nodefony/errors/index.d.ts +1 -0
- package/dist/types/nodefony/keycloak/keycloakAdmin.d.ts +154 -0
- package/dist/types/nodefony/keycloak/keycloakRealm.d.ts +138 -0
- package/dist/types/nodefony/keycloak/keycloakRealmInput.d.ts +92 -0
- package/dist/types/nodefony/keycloak/scaffold.d.ts +98 -0
- package/dist/types/nodefony/keycloak/security-keycloak-realm.d.ts +43 -0
- package/dist/types/nodefony/scaffold/contribute.d.ts +15 -0
- package/dist/types/nodefony/service/accessTokenVerifier.d.ts +35 -14
- package/dist/types/nodefony/service/apiKeys.d.ts +13 -2
- package/dist/types/nodefony/service/authFlow.d.ts +35 -3
- package/dist/types/nodefony/service/firewall.d.ts +17 -3
- package/dist/types/nodefony/service/keycloak.d.ts +115 -0
- package/dist/types/nodefony/service/oauth2.d.ts +145 -9
- package/dist/types/nodefony/service/tokenService.d.ts +7 -1
- package/dist/types/nodefony/service/totp.d.ts +28 -4
- package/dist/types/nodefony/service/webAuthn.d.ts +23 -1
- package/dist/types/nodefony/src/SecuredArea.d.ts +4 -0
- package/dist/types/nodefony/src/accountBinding.d.ts +82 -0
- package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +72 -12
- package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +2 -26
- package/dist/types/nodefony/src/authenticator/ExternalSubjectResolver.d.ts +99 -0
- package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +17 -1
- package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +37 -1
- package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +11 -3
- package/dist/types/nodefony/src/boot/publicRoutesNotice.d.ts +16 -0
- package/dist/types/nodefony/src/entryPoint.d.ts +37 -0
- package/dist/types/nodefony/src/net/transportFailure.d.ts +22 -0
- package/dist/types/nodefony/src/oauth/clientCredentials.d.ts +54 -0
- package/dist/types/nodefony/src/oauth/httpJson.d.ts +0 -8
- package/dist/types/nodefony/src/oauth/metadata.d.ts +24 -1
- package/dist/types/nodefony/src/oauth/oauth2Client.d.ts +50 -1
- package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +46 -2
- package/dist/types/nodefony/src/oauth/providerDiagnosis.d.ts +117 -0
- package/dist/types/nodefony/src/oauth/providerRoles.d.ts +58 -0
- package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +34 -1
- package/dist/types/nodefony/src/openToSignup.d.ts +36 -0
- package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +7 -1
- package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +6 -0
- package/dist/types/nodefony/src/rolesWithin.d.ts +27 -0
- package/dist/types/nodefony/src/sessionAuthentication.d.ts +14 -0
- package/dist/types/nodefony/src/sessionIdentity.d.ts +22 -0
- package/dist/types/nodefony/src/token/JwtKeystore.d.ts +47 -5
- package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +29 -2
- package/dist/types/nodefony/src/token/scopeGrants.d.ts +77 -0
- package/dist/types/nodefony/src/token/secretFile.d.ts +22 -0
- package/dist/types/nodefony/src/transport/CredentialTransportPolicy.d.ts +47 -0
- package/docs/api-keys.md +182 -175
- package/docs/audit.md +141 -141
- package/docs/authenticators.md +129 -96
- package/docs/authorization.md +116 -69
- package/docs/cors.md +64 -64
- package/docs/csrf.md +77 -72
- package/docs/external-jwt.md +83 -11
- package/docs/firewall.md +119 -83
- package/docs/headers.md +82 -82
- package/docs/index.md +16 -8
- package/docs/keycloak.md +835 -0
- package/docs/login-page.md +361 -0
- package/docs/oauth2.md +291 -166
- package/docs/obtenir-un-jeton.md +131 -34
- package/docs/tokens.md +197 -140
- package/docs/totp.md +177 -136
- package/docs/webauthn.md +157 -154
- package/docs/webhooks.md +128 -125
- package/keycloak/themes/nodefony/account/resources/css/nodefony.css +66 -0
- package/keycloak/themes/nodefony/account/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/account/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/account/theme.properties +14 -0
- package/keycloak/themes/nodefony/admin/resources/css/nodefony.css +66 -0
- package/keycloak/themes/nodefony/admin/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/admin/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/admin/theme.properties +12 -0
- package/keycloak/themes/nodefony/email/html/template.ftl +73 -0
- package/keycloak/themes/nodefony/email/messages/messages_en.properties +2 -0
- package/keycloak/themes/nodefony/email/messages/messages_fr.properties +3 -0
- package/keycloak/themes/nodefony/email/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/email/theme.properties +8 -0
- package/keycloak/themes/nodefony/login/messages/messages_en.properties +22 -0
- package/keycloak/themes/nodefony/login/messages/messages_fr.properties +25 -0
- package/keycloak/themes/nodefony/login/resources/css/login.css +1141 -0
- package/keycloak/themes/nodefony/login/resources/css/nodefony.css +688 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/blueprint.css +70 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/console.css +57 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/dots.css +67 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/enterprise.css +84 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/horizon.css +158 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/ledger.css +224 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/minimal.css +63 -0
- package/keycloak/themes/nodefony/login/resources/css/skins/photo-card.css +72 -0
- package/keycloak/themes/nodefony/login/resources/img/favicon.ico +0 -0
- package/keycloak/themes/nodefony/login/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/login/template.ftl +423 -0
- package/keycloak/themes/nodefony/login/theme.properties +17 -0
- package/keycloak/themes/nodefony/welcome/resources/css/nodefony.css +32 -0
- package/keycloak/themes/nodefony/welcome/resources/img/logo.png +0 -0
- package/keycloak/themes/nodefony/welcome/theme.properties +11 -0
- package/keycloak/themes/nodefony-blueprint/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-console/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-dots/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-enterprise/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-horizon/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-ledger/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-minimal/login/theme.properties +12 -0
- package/keycloak/themes/nodefony-photo-card/login/theme.properties +12 -0
- package/package.json +19 -17
- package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { readIfPresentSync } from "../src/token/secretFile.js";
|
|
2
|
+
import { defineSecurityConfig } from "../config/defineModuleConfig.js";
|
|
3
|
+
import { buildKeycloakRealm, mergeKeycloakRealm, renderKeycloakRealm } from "../keycloak/keycloakRealm.js";
|
|
4
|
+
import { KeycloakRealmError, deriveKeycloakRealmInput, keycloakLoginTheme } from "../keycloak/keycloakRealmInput.js";
|
|
5
|
+
import { KeycloakAdminClient, keycloakAdminTarget } from "../keycloak/keycloakAdmin.js";
|
|
6
|
+
import { Service } from "nodefony";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
// nodefony/service/keycloak.ts
|
|
10
|
+
const serviceName = "keycloak";
|
|
11
|
+
/** Où le compose d'une application importe ses realms (chemin natif). */
|
|
12
|
+
const IMPORT_DIR = path.join("docker", "keycloak", "import");
|
|
13
|
+
/**
|
|
14
|
+
* Le fichier de realm par défaut : l'UNIQUE `.json` du dossier d'import, sinon
|
|
15
|
+
* `realm.json` (le nom que `create app` écrit).
|
|
16
|
+
*/
|
|
17
|
+
function defaultRealmFile(root) {
|
|
18
|
+
const dir = path.join(root, IMPORT_DIR);
|
|
19
|
+
let json = [];
|
|
20
|
+
try {
|
|
21
|
+
json = readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
22
|
+
} catch {}
|
|
23
|
+
const only = json.length === 1 ? json[0] : void 0;
|
|
24
|
+
return path.join(dir, only ?? "realm.json");
|
|
25
|
+
}
|
|
26
|
+
/** Un objet indexable, sans rien affirmer de son contenu. */
|
|
27
|
+
function isRecord(value) {
|
|
28
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Un objet JSON de realm. Le contenu vient de `JSON.parse` : seules des
|
|
32
|
+
* valeurs JSON peuvent s'y trouver, la garde ne vérifie donc que la forme.
|
|
33
|
+
*/
|
|
34
|
+
function isKeycloakObject(value) {
|
|
35
|
+
return isRecord(value);
|
|
36
|
+
}
|
|
37
|
+
/** Un port lu dans `servers.<nom>` de la config ; `null` si le serveur est coupé. */
|
|
38
|
+
function portOf(servers, name, fallback) {
|
|
39
|
+
if (!isRecord(servers)) return fallback;
|
|
40
|
+
const server = servers[name];
|
|
41
|
+
if (server === false) return null;
|
|
42
|
+
if (!isRecord(server)) return fallback;
|
|
43
|
+
const port = Number(server.port);
|
|
44
|
+
return Number.isInteger(port) && port > 0 ? port : fallback;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Le client machine à tenir à jour : celui qu'on nomme, sinon le compte de
|
|
48
|
+
* service que le realm existant déclare déjà (flux `client_credentials` seul).
|
|
49
|
+
*/
|
|
50
|
+
function machineClient(existing, named) {
|
|
51
|
+
if (named !== void 0) return {
|
|
52
|
+
clientId: named,
|
|
53
|
+
secret: null
|
|
54
|
+
};
|
|
55
|
+
const clients = Array.isArray(existing?.clients) ? existing.clients : [];
|
|
56
|
+
for (const client of clients) {
|
|
57
|
+
if (typeof client !== "object" || client === null || Array.isArray(client)) continue;
|
|
58
|
+
if (client.serviceAccountsEnabled === true && client.standardFlowEnabled !== true) {
|
|
59
|
+
if (typeof client.clientId === "string") return {
|
|
60
|
+
clientId: client.clientId,
|
|
61
|
+
secret: null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Service `keycloak` — toutes les actions de Nodefony sur un serveur Keycloak,
|
|
69
|
+
* en un point du conteneur.
|
|
70
|
+
*
|
|
71
|
+
* - **le realm** que la configuration implique : dérivé, fusionné avec le
|
|
72
|
+
* fichier d'import existant, comparé, écrit ({@link realmPlan},
|
|
73
|
+
* {@link writeRealm}) ;
|
|
74
|
+
* - **l'API d'administration** d'un realm vivant ({@link admin}), et ce qui s'y
|
|
75
|
+
* fait : le thème de connexion qui suit `loginPage.skin`
|
|
76
|
+
* ({@link applyLoginTheme}).
|
|
77
|
+
*
|
|
78
|
+
* Les commandes `security:keycloak:*` et les routes de développement
|
|
79
|
+
* l'appellent ; elles ne gardent que l'affichage. Seule la contribution de
|
|
80
|
+
* scaffold (`create app`, `scaffold:sync`) appelle les fonctions pures en
|
|
81
|
+
* direct : elle tourne SANS démarrer l'application, donc sans conteneur.
|
|
82
|
+
*
|
|
83
|
+
* Rien n'est construit au démarrage : la configuration est lue à l'appel, et
|
|
84
|
+
* les clients d'administration (un par fournisseur, jeton gardé) au premier
|
|
85
|
+
* usage.
|
|
86
|
+
*/
|
|
87
|
+
var KeycloakService = class extends Service {
|
|
88
|
+
module;
|
|
89
|
+
#clients = null;
|
|
90
|
+
constructor(module) {
|
|
91
|
+
super(serviceName, module.container ?? void 0, module.notificationsCenter, module.options);
|
|
92
|
+
this.module = module;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* La configuration de sécurité VALIDÉE, relue à chaque appel (chemin froid).
|
|
96
|
+
*
|
|
97
|
+
* @returns la configuration
|
|
98
|
+
*/
|
|
99
|
+
config() {
|
|
100
|
+
return defineSecurityConfig(this.options);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Le realm que la configuration implique, fusionné avec le fichier d'import.
|
|
104
|
+
*
|
|
105
|
+
* Le realm d'import répète ce que la config dit déjà (client, URL de retour,
|
|
106
|
+
* audiences, rôles de `roleMapping`) : écrit à la main, il diverge. Ce plan
|
|
107
|
+
* le DÉRIVE, puis le fusionne pour que ce qu'un humain a ajouté survive
|
|
108
|
+
* (comptes, titres, secret machine, mappers posés dans la console).
|
|
109
|
+
*
|
|
110
|
+
* @param request - fournisseur, fichier, client machine, canal arrière
|
|
111
|
+
* @returns le fichier, son contenu, le realm à écrire et les constats
|
|
112
|
+
* @throws {KeycloakRealmError} Fournisseur absent ou mal décrit, fichier qui
|
|
113
|
+
* n'est pas un realm
|
|
114
|
+
*/
|
|
115
|
+
realmPlan(request = {}) {
|
|
116
|
+
const root = this.kernel?.path ?? process.cwd();
|
|
117
|
+
const file = request.file === void 0 ? defaultRealmFile(root) : path.resolve(root, request.file);
|
|
118
|
+
const current = readIfPresentSync(file);
|
|
119
|
+
let existing = null;
|
|
120
|
+
if (current !== null) {
|
|
121
|
+
const parsed = JSON.parse(current);
|
|
122
|
+
if (!isKeycloakObject(parsed)) throw new KeycloakRealmError(`${file} n'est pas un realm (objet JSON attendu)`);
|
|
123
|
+
existing = parsed;
|
|
124
|
+
}
|
|
125
|
+
const kernelOptions = this.kernel?.options;
|
|
126
|
+
const servers = isRecord(kernelOptions) ? kernelOptions.servers : void 0;
|
|
127
|
+
const appServers = {
|
|
128
|
+
httpPort: portOf(servers, "http", 5151),
|
|
129
|
+
httpsPort: portOf(servers, "https", 5152)
|
|
130
|
+
};
|
|
131
|
+
const derivation = deriveKeycloakRealmInput({
|
|
132
|
+
providerName: request.provider ?? "keycloak",
|
|
133
|
+
config: this.config(),
|
|
134
|
+
servers: appServers,
|
|
135
|
+
production: this.kernel?.environment === "production",
|
|
136
|
+
machine: machineClient(existing, request.machine),
|
|
137
|
+
currentLoginTheme: typeof existing?.loginTheme === "string" ? existing.loginTheme : null,
|
|
138
|
+
...request.backchannelOrigin === void 0 ? {} : { backchannelOrigin: request.backchannelOrigin }
|
|
139
|
+
});
|
|
140
|
+
const derived = buildKeycloakRealm(derivation.input);
|
|
141
|
+
const realm = existing === null ? derived : mergeKeycloakRealm(existing, derived);
|
|
142
|
+
const text = renderKeycloakRealm(realm);
|
|
143
|
+
return {
|
|
144
|
+
file,
|
|
145
|
+
current,
|
|
146
|
+
text,
|
|
147
|
+
warnings: derivation.warnings,
|
|
148
|
+
upToDate: current === text
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Écrit le realm d'un plan — rien si le fichier est déjà à jour.
|
|
153
|
+
*
|
|
154
|
+
* ⚠️ Keycloak n'importe un realm qu'à sa CRÉATION : un realm déjà présent ne
|
|
155
|
+
* relit pas ce fichier ({@link applyLoginTheme} pour le thème, import
|
|
156
|
+
* partiel dans la console pour le reste).
|
|
157
|
+
*
|
|
158
|
+
* @param plan - le rendu de {@link realmPlan}
|
|
159
|
+
* @returns `true` si le fichier a été écrit
|
|
160
|
+
*/
|
|
161
|
+
writeRealm(plan) {
|
|
162
|
+
if (plan.upToDate) return false;
|
|
163
|
+
mkdirSync(path.dirname(plan.file), { recursive: true });
|
|
164
|
+
writeFileSync(plan.file, plan.text);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Le client de l'API d'administration d'un fournisseur Keycloak.
|
|
169
|
+
*
|
|
170
|
+
* @param providerName - nom dans `oauth2.providers` (défaut `keycloak`)
|
|
171
|
+
* @returns le client, construit au premier appel puis gardé (jeton réutilisé)
|
|
172
|
+
* @throws {KeycloakAdminError} Hors développement, fournisseur absent, sans
|
|
173
|
+
* émetteur ou sans compte d'administration
|
|
174
|
+
*/
|
|
175
|
+
admin(providerName = "keycloak") {
|
|
176
|
+
const cached = this.#clients?.get(providerName);
|
|
177
|
+
if (cached !== void 0) return cached;
|
|
178
|
+
const client = new KeycloakAdminClient({
|
|
179
|
+
...keycloakAdminTarget(this.config(), providerName),
|
|
180
|
+
environment: this.kernel?.environment ?? "production"
|
|
181
|
+
});
|
|
182
|
+
this.#clients ??= /* @__PURE__ */ new Map();
|
|
183
|
+
this.#clients.set(providerName, client);
|
|
184
|
+
return client;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Le thème de connexion qu'un habillage demande à Keycloak.
|
|
188
|
+
*
|
|
189
|
+
* @param skin - habillage ; défaut : `loginPage.skin` de la configuration
|
|
190
|
+
* @returns `nodefony` ou `nodefony-<habillage>`
|
|
191
|
+
*/
|
|
192
|
+
expectedLoginTheme(skin) {
|
|
193
|
+
return keycloakLoginTheme(skin ?? this.config().loginPage.skin);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Pose sur le realm VIVANT le thème de connexion d'un habillage.
|
|
197
|
+
*
|
|
198
|
+
* @param options - fournisseur (défaut `keycloak`) et habillage (défaut :
|
|
199
|
+
* `loginPage.skin` ; un aperçu de `/login` passe le sien)
|
|
200
|
+
* @returns le thème d'avant, celui demandé, et ce qui a été fait
|
|
201
|
+
* @throws {KeycloakAdminError} Hors développement, compte absent ou refusé,
|
|
202
|
+
* Keycloak injoignable
|
|
203
|
+
*/
|
|
204
|
+
applyLoginTheme(options = {}) {
|
|
205
|
+
return this.admin(options.provider).applyLoginTheme(options.skin ?? this.config().loginPage.skin);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
export { KeycloakService, KeycloakService as default };
|