@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.
Files changed (249) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  2. package/dist/index.js +48 -16
  3. package/dist/nodefony/command/{security-token.js → security-mcp-token.js} +62 -24
  4. package/dist/nodefony/command/security-oauth-doctor.js +68 -0
  5. package/dist/nodefony/command/security-secrets.js +52 -29
  6. package/dist/nodefony/command/security-user-add.js +9 -3
  7. package/dist/nodefony/command/security-user-delete.js +10 -7
  8. package/dist/nodefony/command/security-user-list.js +5 -3
  9. package/dist/nodefony/command/security-user-password.js +9 -14
  10. package/dist/nodefony/config/config.js +99 -8
  11. package/dist/nodefony/config/defineModuleConfig.js +1 -2
  12. package/dist/nodefony/contracts/IAccessVoter.js +1 -2
  13. package/dist/nodefony/contracts/index.js +1 -2
  14. package/dist/nodefony/errors/AccessDeniedError.js +1 -2
  15. package/dist/nodefony/errors/ApiKeyError.js +3 -2
  16. package/dist/nodefony/errors/AuthenticationError.js +1 -2
  17. package/dist/nodefony/errors/CsrfError.js +1 -2
  18. package/dist/nodefony/errors/InsecureTransportError.js +23 -0
  19. package/dist/nodefony/errors/InvalidTargetError.js +1 -2
  20. package/dist/nodefony/errors/SsrfError.js +1 -2
  21. package/dist/nodefony/errors/ThrottledError.js +1 -2
  22. package/dist/nodefony/errors/UnverifiableTokenError.js +1 -2
  23. package/dist/nodefony/errors/WebAuthnError.js +1 -2
  24. package/dist/nodefony/errors/index.js +2 -1
  25. package/dist/nodefony/keycloak/keycloakAdmin.js +254 -0
  26. package/dist/nodefony/keycloak/keycloakRealm.js +259 -0
  27. package/dist/nodefony/keycloak/keycloakRealmInput.js +123 -0
  28. package/dist/nodefony/keycloak/scaffold.js +231 -0
  29. package/dist/nodefony/keycloak/security-keycloak-realm.js +130 -0
  30. package/dist/nodefony/scaffold/contribute.js +19 -0
  31. package/dist/nodefony/service/accessTokenVerifier.js +101 -34
  32. package/dist/nodefony/service/apiKeys.js +45 -5
  33. package/dist/nodefony/service/auditService.js +4 -5
  34. package/dist/nodefony/service/authFlow.js +118 -16
  35. package/dist/nodefony/service/authorization.js +1 -2
  36. package/dist/nodefony/service/cors.js +1 -2
  37. package/dist/nodefony/service/csrf.js +1 -2
  38. package/dist/nodefony/service/firewall.js +116 -25
  39. package/dist/nodefony/service/keycloak.js +208 -0
  40. package/dist/nodefony/service/oauth2.js +403 -28
  41. package/dist/nodefony/service/securityHeaders.js +1 -2
  42. package/dist/nodefony/service/tokenService.js +94 -27
  43. package/dist/nodefony/service/totp.js +55 -11
  44. package/dist/nodefony/service/webAuthn.js +24 -7
  45. package/dist/nodefony/service/webhooks.js +4 -5
  46. package/dist/nodefony/src/RoleHierarchyWalker.js +1 -2
  47. package/dist/nodefony/src/SecuredArea.js +15 -5
  48. package/dist/nodefony/src/accountBinding.js +96 -0
  49. package/dist/nodefony/src/admin/SecurityAdminApi.js +71 -42
  50. package/dist/nodefony/src/admin/WebhookAdminApi.js +23 -7
  51. package/dist/nodefony/src/admin/adminAudit.js +1 -3
  52. package/dist/nodefony/src/admin/userRevocationCascade.js +148 -20
  53. package/dist/nodefony/src/apikey/apiKeyFormat.js +1 -2
  54. package/dist/nodefony/src/audit/MemoryAuditStore.js +1 -2
  55. package/dist/nodefony/src/audit/auditBridge.js +1 -2
  56. package/dist/nodefony/src/audit/auditFilters.js +1 -2
  57. package/dist/nodefony/src/audit/auditStoreRegistry.js +1 -2
  58. package/dist/nodefony/src/audit/readAuditContext.js +1 -2
  59. package/dist/nodefony/src/audit/recordAudit.js +1 -2
  60. package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +1 -2
  61. package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +3 -2
  62. package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +14 -59
  63. package/dist/nodefony/src/authenticator/ExternalSubjectResolver.js +180 -0
  64. package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +80 -19
  65. package/dist/nodefony/src/authenticator/JwtAuthenticator.js +92 -16
  66. package/dist/nodefony/src/authenticator/SessionAuthenticator.js +9 -5
  67. package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +14 -5
  68. package/dist/nodefony/src/authenticator/authenticatorRegistry.js +5 -12
  69. package/dist/nodefony/src/authenticator/bearer.js +1 -2
  70. package/dist/nodefony/src/authenticator/externalSubject.js +1 -2
  71. package/dist/nodefony/src/authenticator/peekIssuer.js +1 -2
  72. package/dist/nodefony/src/boot/publicRoutesNotice.js +26 -0
  73. package/dist/nodefony/src/crypto/secretCipher.js +1 -2
  74. package/dist/nodefony/src/csp.js +1 -2
  75. package/dist/nodefony/src/csrfToken.js +1 -2
  76. package/dist/nodefony/src/entryPoint.js +38 -0
  77. package/dist/nodefony/src/net/ssrfGuard.js +1 -2
  78. package/dist/nodefony/src/net/transportFailure.js +57 -0
  79. package/dist/nodefony/src/oauth/clientCredentials.js +39 -0
  80. package/dist/nodefony/src/oauth/httpJson.js +3 -3
  81. package/dist/nodefony/src/oauth/metadata.js +28 -12
  82. package/dist/nodefony/src/oauth/oauth2Client.js +61 -35
  83. package/dist/nodefony/src/oauth/oauthProviderRegistry.js +40 -8
  84. package/dist/nodefony/src/oauth/providerDiagnosis.js +282 -0
  85. package/dist/nodefony/src/oauth/providerRoles.js +78 -0
  86. package/dist/nodefony/src/oauth/providers/github.js +48 -12
  87. package/dist/nodefony/src/oauth/providers/oidc.js +90 -8
  88. package/dist/nodefony/src/openToSignup.js +46 -0
  89. package/dist/nodefony/src/realtime/UserRealtimeToken.js +17 -3
  90. package/dist/nodefony/src/realtime/frameAuthorizer.js +1 -2
  91. package/dist/nodefony/src/rolesWithin.js +57 -0
  92. package/dist/nodefony/src/sessionAuthentication.js +25 -0
  93. package/dist/nodefony/src/sessionIdentity.js +29 -3
  94. package/dist/nodefony/src/throttle/LoginThrottler.js +1 -2
  95. package/dist/nodefony/src/token/AnonymousToken.js +1 -2
  96. package/dist/nodefony/src/token/JwtKeystore.js +89 -48
  97. package/dist/nodefony/src/token/MemoryTokenStore.js +1 -2
  98. package/dist/nodefony/src/token/RemoteJwtVerifier.js +46 -16
  99. package/dist/nodefony/src/token/UserToken.js +1 -2
  100. package/dist/nodefony/src/token/jwtRuntime.js +1 -2
  101. package/dist/nodefony/src/token/scopeGrants.js +109 -0
  102. package/dist/nodefony/src/token/secretFile.js +60 -4
  103. package/dist/nodefony/src/token/tokenCriteria.js +1 -2
  104. package/dist/nodefony/src/token/tokenFilters.js +1 -2
  105. package/dist/nodefony/src/token/tokenSort.js +1 -2
  106. package/dist/nodefony/src/token/tokenStatus.js +1 -2
  107. package/dist/nodefony/src/token/tokenStoreRegistry.js +1 -2
  108. package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +1 -2
  109. package/dist/nodefony/src/totp/totpCipher.js +1 -2
  110. package/dist/nodefony/src/totp/totpCrypto.js +1 -2
  111. package/dist/nodefony/src/totp/totpOperations.js +1 -2
  112. package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +1 -2
  113. package/dist/nodefony/src/transport/CredentialTransportPolicy.js +50 -0
  114. package/dist/nodefony/src/voter/RoleVoter.js +1 -2
  115. package/dist/nodefony/src/voter/ScopeVoter.js +1 -2
  116. package/dist/nodefony/src/voter/voterRegistry.js +1 -2
  117. package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +1 -2
  118. package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +1 -2
  119. package/dist/nodefony/src/webhook/MemoryWebhookStore.js +1 -2
  120. package/dist/nodefony/src/webhook/WebhookDispatcher.js +1 -2
  121. package/dist/nodefony/src/webhook/webhookCipher.js +1 -2
  122. package/dist/nodefony/src/webhook/webhookDelivery.js +1 -2
  123. package/dist/nodefony/src/webhook/webhookFilters.js +1 -2
  124. package/dist/nodefony/src/webhook/webhookSignature.js +1 -2
  125. package/dist/nodefony/src/webhook/webhookSort.js +1 -2
  126. package/dist/nodefony/src/webhook/webhookStoreRegistry.js +1 -2
  127. package/dist/types/index.d.ts +26 -6
  128. package/dist/types/nodefony/command/{security-token.d.ts → security-mcp-token.d.ts} +16 -6
  129. package/dist/types/nodefony/command/security-oauth-doctor.d.ts +23 -0
  130. package/dist/types/nodefony/command/security-secrets.d.ts +2 -0
  131. package/dist/types/nodefony/command/security-user-delete.d.ts +4 -3
  132. package/dist/types/nodefony/command/security-user-password.d.ts +4 -4
  133. package/dist/types/nodefony/config/config.d.ts +64 -1
  134. package/dist/types/nodefony/contracts/IAuthenticator.d.ts +1 -1
  135. package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +42 -1
  136. package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +48 -0
  137. package/dist/types/nodefony/contracts/ISecuredArea.d.ts +22 -0
  138. package/dist/types/nodefony/contracts/ITotpSecret.d.ts +4 -1
  139. package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +7 -1
  140. package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +4 -1
  141. package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +6 -0
  142. package/dist/types/nodefony/errors/ApiKeyError.d.ts +3 -1
  143. package/dist/types/nodefony/errors/InsecureTransportError.d.ts +20 -0
  144. package/dist/types/nodefony/errors/index.d.ts +1 -0
  145. package/dist/types/nodefony/keycloak/keycloakAdmin.d.ts +154 -0
  146. package/dist/types/nodefony/keycloak/keycloakRealm.d.ts +138 -0
  147. package/dist/types/nodefony/keycloak/keycloakRealmInput.d.ts +92 -0
  148. package/dist/types/nodefony/keycloak/scaffold.d.ts +98 -0
  149. package/dist/types/nodefony/keycloak/security-keycloak-realm.d.ts +43 -0
  150. package/dist/types/nodefony/scaffold/contribute.d.ts +15 -0
  151. package/dist/types/nodefony/service/accessTokenVerifier.d.ts +35 -14
  152. package/dist/types/nodefony/service/apiKeys.d.ts +13 -2
  153. package/dist/types/nodefony/service/authFlow.d.ts +35 -3
  154. package/dist/types/nodefony/service/firewall.d.ts +17 -3
  155. package/dist/types/nodefony/service/keycloak.d.ts +115 -0
  156. package/dist/types/nodefony/service/oauth2.d.ts +145 -9
  157. package/dist/types/nodefony/service/tokenService.d.ts +7 -1
  158. package/dist/types/nodefony/service/totp.d.ts +28 -4
  159. package/dist/types/nodefony/service/webAuthn.d.ts +23 -1
  160. package/dist/types/nodefony/src/SecuredArea.d.ts +4 -0
  161. package/dist/types/nodefony/src/accountBinding.d.ts +82 -0
  162. package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +72 -12
  163. package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +2 -26
  164. package/dist/types/nodefony/src/authenticator/ExternalSubjectResolver.d.ts +99 -0
  165. package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +17 -1
  166. package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +37 -1
  167. package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +11 -3
  168. package/dist/types/nodefony/src/boot/publicRoutesNotice.d.ts +16 -0
  169. package/dist/types/nodefony/src/entryPoint.d.ts +37 -0
  170. package/dist/types/nodefony/src/net/transportFailure.d.ts +22 -0
  171. package/dist/types/nodefony/src/oauth/clientCredentials.d.ts +54 -0
  172. package/dist/types/nodefony/src/oauth/httpJson.d.ts +0 -8
  173. package/dist/types/nodefony/src/oauth/metadata.d.ts +24 -1
  174. package/dist/types/nodefony/src/oauth/oauth2Client.d.ts +50 -1
  175. package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +46 -2
  176. package/dist/types/nodefony/src/oauth/providerDiagnosis.d.ts +117 -0
  177. package/dist/types/nodefony/src/oauth/providerRoles.d.ts +58 -0
  178. package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +34 -1
  179. package/dist/types/nodefony/src/openToSignup.d.ts +36 -0
  180. package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +7 -1
  181. package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +6 -0
  182. package/dist/types/nodefony/src/rolesWithin.d.ts +27 -0
  183. package/dist/types/nodefony/src/sessionAuthentication.d.ts +14 -0
  184. package/dist/types/nodefony/src/sessionIdentity.d.ts +22 -0
  185. package/dist/types/nodefony/src/token/JwtKeystore.d.ts +47 -5
  186. package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +29 -2
  187. package/dist/types/nodefony/src/token/scopeGrants.d.ts +77 -0
  188. package/dist/types/nodefony/src/token/secretFile.d.ts +22 -0
  189. package/dist/types/nodefony/src/transport/CredentialTransportPolicy.d.ts +47 -0
  190. package/docs/api-keys.md +182 -175
  191. package/docs/audit.md +141 -141
  192. package/docs/authenticators.md +129 -96
  193. package/docs/authorization.md +116 -69
  194. package/docs/cors.md +64 -64
  195. package/docs/csrf.md +77 -72
  196. package/docs/external-jwt.md +83 -11
  197. package/docs/firewall.md +119 -83
  198. package/docs/headers.md +82 -82
  199. package/docs/index.md +16 -8
  200. package/docs/keycloak.md +835 -0
  201. package/docs/login-page.md +361 -0
  202. package/docs/oauth2.md +291 -166
  203. package/docs/obtenir-un-jeton.md +131 -34
  204. package/docs/tokens.md +197 -140
  205. package/docs/totp.md +177 -136
  206. package/docs/webauthn.md +157 -154
  207. package/docs/webhooks.md +128 -125
  208. package/keycloak/themes/nodefony/account/resources/css/nodefony.css +66 -0
  209. package/keycloak/themes/nodefony/account/resources/img/favicon.ico +0 -0
  210. package/keycloak/themes/nodefony/account/resources/img/logo.png +0 -0
  211. package/keycloak/themes/nodefony/account/theme.properties +14 -0
  212. package/keycloak/themes/nodefony/admin/resources/css/nodefony.css +66 -0
  213. package/keycloak/themes/nodefony/admin/resources/img/favicon.ico +0 -0
  214. package/keycloak/themes/nodefony/admin/resources/img/logo.png +0 -0
  215. package/keycloak/themes/nodefony/admin/theme.properties +12 -0
  216. package/keycloak/themes/nodefony/email/html/template.ftl +73 -0
  217. package/keycloak/themes/nodefony/email/messages/messages_en.properties +2 -0
  218. package/keycloak/themes/nodefony/email/messages/messages_fr.properties +3 -0
  219. package/keycloak/themes/nodefony/email/resources/img/logo.png +0 -0
  220. package/keycloak/themes/nodefony/email/theme.properties +8 -0
  221. package/keycloak/themes/nodefony/login/messages/messages_en.properties +22 -0
  222. package/keycloak/themes/nodefony/login/messages/messages_fr.properties +25 -0
  223. package/keycloak/themes/nodefony/login/resources/css/login.css +1141 -0
  224. package/keycloak/themes/nodefony/login/resources/css/nodefony.css +688 -0
  225. package/keycloak/themes/nodefony/login/resources/css/skins/blueprint.css +70 -0
  226. package/keycloak/themes/nodefony/login/resources/css/skins/console.css +57 -0
  227. package/keycloak/themes/nodefony/login/resources/css/skins/dots.css +67 -0
  228. package/keycloak/themes/nodefony/login/resources/css/skins/enterprise.css +84 -0
  229. package/keycloak/themes/nodefony/login/resources/css/skins/horizon.css +158 -0
  230. package/keycloak/themes/nodefony/login/resources/css/skins/ledger.css +224 -0
  231. package/keycloak/themes/nodefony/login/resources/css/skins/minimal.css +63 -0
  232. package/keycloak/themes/nodefony/login/resources/css/skins/photo-card.css +72 -0
  233. package/keycloak/themes/nodefony/login/resources/img/favicon.ico +0 -0
  234. package/keycloak/themes/nodefony/login/resources/img/logo.png +0 -0
  235. package/keycloak/themes/nodefony/login/template.ftl +423 -0
  236. package/keycloak/themes/nodefony/login/theme.properties +17 -0
  237. package/keycloak/themes/nodefony/welcome/resources/css/nodefony.css +32 -0
  238. package/keycloak/themes/nodefony/welcome/resources/img/logo.png +0 -0
  239. package/keycloak/themes/nodefony/welcome/theme.properties +11 -0
  240. package/keycloak/themes/nodefony-blueprint/login/theme.properties +12 -0
  241. package/keycloak/themes/nodefony-console/login/theme.properties +12 -0
  242. package/keycloak/themes/nodefony-dots/login/theme.properties +12 -0
  243. package/keycloak/themes/nodefony-enterprise/login/theme.properties +12 -0
  244. package/keycloak/themes/nodefony-horizon/login/theme.properties +12 -0
  245. package/keycloak/themes/nodefony-ledger/login/theme.properties +12 -0
  246. package/keycloak/themes/nodefony-minimal/login/theme.properties +12 -0
  247. package/keycloak/themes/nodefony-photo-card/login/theme.properties +12 -0
  248. package/package.json +19 -17
  249. 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 };