diff --git a/.prettierignore b/.prettierignore index 6cc2f1d3..95725db6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,4 +12,5 @@ node_modules/ /sample_react_project/ /sample_custom_react_project/ /keycloakify_starter_test/ -/.storybook/static/keycloak-resources/ \ No newline at end of file +/.storybook/static/keycloak-resources/ +/src/bin/start-keycloak/*.json \ No newline at end of file diff --git a/scripts/build/downloadKeycloakifyLogging.ts b/scripts/build/downloadKeycloakifyLogging.ts new file mode 100644 index 00000000..bfd77b84 --- /dev/null +++ b/scripts/build/downloadKeycloakifyLogging.ts @@ -0,0 +1,39 @@ +import { downloadAndExtractArchive } from "../../src/bin/tools/downloadAndExtractArchive"; +import { cacheDirPath } from "../shared/cacheDirPath"; +import { getProxyFetchOptions } from "../../src/bin/tools/fetchProxyOptions"; +import { getThisCodebaseRootDirPath } from "../../src/bin/tools/getThisCodebaseRootDirPath"; +import { existsAsync } from "../../src/bin/tools/fs.existsAsync"; +import * as fs from "fs/promises"; +import { + KEYCLOAKIFY_LOGGING_VERSION, + KEYCLOAKIFY_LOGIN_JAR_BASENAME +} from "../../src/bin/shared/constants"; +import { join as pathJoin } from "path"; + +export async function downloadKeycloakifyLogging(params: { distDirPath: string }) { + const { distDirPath } = params; + + const jarFilePath = pathJoin( + distDirPath, + "src", + "bin", + "start-keycloak", + KEYCLOAKIFY_LOGIN_JAR_BASENAME + ); + + if (await existsAsync(jarFilePath)) { + return; + } + + const { archiveFilePath } = await downloadAndExtractArchive({ + cacheDirPath, + fetchOptions: getProxyFetchOptions({ + npmConfigGetCwd: getThisCodebaseRootDirPath() + }), + url: `https://github.com/keycloakify/keycloakify-logging/releases/download/${KEYCLOAKIFY_LOGGING_VERSION}/keycloakify-logging-${KEYCLOAKIFY_LOGGING_VERSION}.jar`, + uniqueIdOfOnArchiveFile: "no extraction", + onArchiveFile: async () => {} + }); + + await fs.cp(archiveFilePath, jarFilePath); +} diff --git a/scripts/build/main.ts b/scripts/build/main.ts index 2fdcdbc6..937c06db 100644 --- a/scripts/build/main.ts +++ b/scripts/build/main.ts @@ -7,6 +7,7 @@ import { createAccountV1Dir } from "./createAccountV1Dir"; import chalk from "chalk"; import { run } from "../shared/run"; import { vendorFrontendDependencies } from "./vendorFrontendDependencies"; +import { downloadKeycloakifyLogging } from "./downloadKeycloakifyLogging"; (async () => { console.log(chalk.cyan("Building Keycloakify...")); @@ -148,9 +149,6 @@ import { vendorFrontendDependencies } from "./vendorFrontendDependencies"; fs.cpSync(dirBasename, destDirPath, { recursive: true }); } - await createPublicKeycloakifyDevResourcesDir(); - await createAccountV1Dir(); - transformCodebase({ srcDirPath: join("stories"), destDirPath: join("dist", "stories"), @@ -163,6 +161,12 @@ import { vendorFrontendDependencies } from "./vendorFrontendDependencies"; } }); + await createPublicKeycloakifyDevResourcesDir(); + await createAccountV1Dir(); + await downloadKeycloakifyLogging({ + distDirPath: join(process.cwd(), "dist") + }); + console.log( chalk.green(`✓ built in ${((Date.now() - startTime) / 1000).toFixed(2)}s`) ); diff --git a/scripts/dump-keycloak-realm.ts b/scripts/dump-keycloak-realm.ts index adf4e20a..95b740b3 100644 --- a/scripts/dump-keycloak-realm.ts +++ b/scripts/dump-keycloak-realm.ts @@ -1,11 +1,13 @@ import { CONTAINER_NAME } from "../src/bin/shared/constants"; import child_process from "child_process"; import { SemVer } from "../src/bin/tools/SemVer"; -import { join as pathJoin, relative as pathRelative } from "path"; +import { dumpContainerConfig } from "../src/bin/start-keycloak/realmConfig/dumpContainerConfig"; +import { cacheDirPath } from "./shared/cacheDirPath"; +import { runPrettier } from "../src/bin/tools/runPrettier"; +import { getThisCodebaseRootDirPath } from "../src/bin/tools/getThisCodebaseRootDirPath"; +import { join as pathJoin } from "path"; +import * as fs from "fs"; import chalk from "chalk"; -import { Deferred } from "evt/tools/Deferred"; -import { assert, is } from "tsafe/assert"; -import { run } from "./shared/run"; (async () => { const keycloakMajorVersionNumber = SemVer.parse( @@ -16,103 +18,32 @@ import { run } from "./shared/run"; .split(":")[1] ).major; - { - // https://github.com/keycloak/keycloak/issues/33800 - const doesUseLockedH2Database = keycloakMajorVersionNumber >= 26; + const parsedRealmJson = await dumpContainerConfig({ + buildContext: { + cacheDirPath + }, + keycloakMajorVersionNumber, + realmName: "myrealm" + }); - if (doesUseLockedH2Database) { - child_process.execSync( - `docker exec ${CONTAINER_NAME} sh -c "cp -rp /opt/keycloak/data/h2 /tmp"` - ); - } + let sourceCode = JSON.stringify(parsedRealmJson, null, 2); - const dCompleted = new Deferred(); - - const child = child_process.spawn( - "docker", - [ - ...["exec", CONTAINER_NAME], - ...["/opt/keycloak/bin/kc.sh", "export"], - ...["--dir", "/tmp"], - ...["--realm", "myrealm"], - ...["--users", "realm_file"], - ...(!doesUseLockedH2Database - ? [] - : [ - ...["--db", "dev-file"], - ...[ - "--db-url", - "'jdbc:h2:file:/tmp/h2/keycloakdb;NON_KEYWORDS=VALUE'" - ] - ]) - ], - { shell: true } - ); - - let output = ""; - - const onExit = (code: number | null) => { - dCompleted.reject(new Error(`Exited with code ${code}`)); - }; - - child.once("exit", onExit); - - child.stdout.on("data", data => { - const outputStr = data.toString("utf8"); - - if (outputStr.includes("Export finished successfully")) { - child.removeListener("exit", onExit); - - // NOTE: On older Keycloak versions the process keeps running after the export is done. - const timer = setTimeout(() => { - child.removeListener("exit", onExit2); - child.kill(); - dCompleted.resolve(); - }, 1500); - - const onExit2 = () => { - clearTimeout(timer); - dCompleted.resolve(); - }; - - child.once("exit", onExit2); - } - - output += outputStr; - }); - - child.stderr.on("data", data => (output += chalk.red(data.toString("utf8")))); - - try { - await dCompleted.pr; - } catch (error) { - assert(is(error)); - - console.log(chalk.red(error.message)); - - console.log(output); - - process.exit(1); - } - - if (doesUseLockedH2Database) { - run(`docker exec ${CONTAINER_NAME} sh -c "rm -rf /tmp/h2"`); - } - } - - const targetFilePath = pathRelative( - process.cwd(), - pathJoin( - __dirname, - "..", - "src", - "bin", - "start-keycloak", - `myrealm-realm-${keycloakMajorVersionNumber}.json` - ) + const filePath = pathJoin( + getThisCodebaseRootDirPath(), + "src", + "bin", + "start-keycloak", + "realmConfig", + "defaultConfig", + `realm-kc-${keycloakMajorVersionNumber}.json` ); - run(`docker cp ${CONTAINER_NAME}:/tmp/myrealm-realm.json ${targetFilePath}`); + sourceCode = await runPrettier({ + sourceCode, + filePath + }); - console.log(`${chalk.green(`✓ Exported realm to`)} ${chalk.bold(targetFilePath)}`); + fs.writeFileSync(filePath, Buffer.from(sourceCode, "utf8")); + + console.log(chalk.green(`Realm config dumped to ${filePath}`)); })(); diff --git a/src/bin/shared/constants.ts b/src/bin/shared/constants.ts index 56433dd4..b31b434e 100644 --- a/src/bin/shared/constants.ts +++ b/src/bin/shared/constants.ts @@ -81,3 +81,9 @@ export const CUSTOM_HANDLER_ENV_NAMES = { export const KEYCLOAK_THEME = "keycloak-theme"; export const KEYCLOAKIFY_SPA_DEV_SERVER_PORT = "KEYCLOAKIFY_SPA_DEV_SERVER_PORT"; + +export const KEYCLOAKIFY_LOGGING_VERSION = "1.0.3"; + +export const KEYCLOAKIFY_LOGIN_JAR_BASENAME = `keycloakify-logging-${KEYCLOAKIFY_LOGGING_VERSION}.jar`; + +export const TEST_APP_URL = "https://my-theme.keycloakify.dev"; diff --git a/src/bin/start-keycloak/getSupportedDockerImageTags.ts b/src/bin/start-keycloak/getSupportedDockerImageTags.ts new file mode 100644 index 00000000..fdc33cb1 --- /dev/null +++ b/src/bin/start-keycloak/getSupportedDockerImageTags.ts @@ -0,0 +1,230 @@ +import fetch from "make-fetch-happen"; +import type { BuildContext } from "../shared/buildContext"; +import { assert, type Equals } from "tsafe/assert"; +import { id } from "tsafe/id"; +import { z } from "zod"; +import { SemVer } from "../tools/SemVer"; +import { exclude } from "tsafe/exclude"; +import { getSupportedKeycloakMajorVersions } from "./realmConfig/defaultConfig"; +import { join as pathJoin, dirname as pathDirname } from "path"; +import * as fs from "fs/promises"; +import { existsAsync } from "../tools/fs.existsAsync"; +import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion"; + +export type BuildContextLike = { + fetchOptions: BuildContext["fetchOptions"]; + cacheDirPath: string; +}; + +assert; + +export async function getSupportedDockerImageTags(params: { + buildContext: BuildContextLike; +}) { + const { buildContext } = params; + + { + const result = await getCachedValue({ cacheDirPath: buildContext.cacheDirPath }); + + if (result !== undefined) { + return result; + } + } + + const tags: string[] = []; + + await (async function callee(url: string) { + const r = await fetch(url, buildContext.fetchOptions); + + await Promise.all([ + (async () => { + tags.push( + ...z + .object({ + tags: z.array(z.string()) + }) + .parse(await r.json()).tags + ); + })(), + (async () => { + const link = r.headers.get("link"); + + if (link === null) { + return; + } + + const split = link.split(";").map(s => s.trim()); + + assert(split.length === 2); + + assert(split[1] === 'rel="next"'); + + const match = split[0].match(/^<(.+)>$/); + + assert(match !== null); + + const nextUrl = new URL(url).origin + match[1]; + + await callee(nextUrl); + })() + ]); + })("https://quay.io/v2/keycloak/keycloak/tags/list"); + + const arr = tags + .map(tag => ({ + tag, + version: (() => { + if (tag.includes("-")) { + return undefined; + } + + let version: SemVer; + + try { + version = SemVer.parse(tag); + } catch { + return undefined; + } + + return version; + })() + })) + .map(({ tag, version }) => (version === undefined ? undefined : { tag, version })) + .filter(exclude(undefined)); + + const versionByMajor: Record = {}; + + for (const { version } of arr) { + const version_current = versionByMajor[version.major]; + + if ( + version_current === undefined || + SemVer.compare(version_current, version) === -1 + ) { + versionByMajor[version.major] = version; + } + } + + const supportedKeycloakMajorVersions = getSupportedKeycloakMajorVersions(); + + const result = Object.entries(versionByMajor) + .sort(([a], [b]) => parseInt(b) - parseInt(a)) + .map(([, version]) => version) + .map(version => { + assert(version !== undefined); + + if (!supportedKeycloakMajorVersions.includes(version.major)) { + return undefined; + } + + return SemVer.stringify(version); + }) + .filter(exclude(undefined)); + + await setCachedValue({ cacheDirPath: buildContext.cacheDirPath, result }); + + return result; +} + +const { getCachedValue, setCachedValue } = (() => { + type Cache = { + keycloakifyVersion: string; + time: number; + result: string[]; + }; + + const zCache = (() => { + type TargetType = Cache; + + const zTargetType = z.object({ + keycloakifyVersion: z.string(), + time: z.number(), + result: z.array(z.string()) + }); + + type InferredType = z.infer; + + assert>; + + return id>(zTargetType); + })(); + + let inMemoryCachedResult: Cache["result"] | undefined = undefined; + + function getCacheFilePath(params: { cacheDirPath: string }) { + const { cacheDirPath } = params; + + return pathJoin(cacheDirPath, "supportedDockerImageTags.json"); + } + + async function getCachedValue(params: { cacheDirPath: string }) { + const { cacheDirPath } = params; + + if (inMemoryCachedResult !== undefined) { + return inMemoryCachedResult; + } + + const cacheFilePath = getCacheFilePath({ cacheDirPath }); + + if (!(await existsAsync(cacheFilePath))) { + return undefined; + } + + let cache: Cache | undefined; + + try { + cache = zCache.parse(JSON.parse(await fs.readFile(cacheFilePath, "utf8"))); + } catch { + return undefined; + } + + if (cache.keycloakifyVersion !== readThisNpmPackageVersion()) { + return undefined; + } + + if (Date.now() - cache.time > 3_600 * 24) { + return undefined; + } + + inMemoryCachedResult = cache.result; + + return cache.result; + } + + async function setCachedValue(params: { + cacheDirPath: string; + result: Cache["result"]; + }) { + const { cacheDirPath, result } = params; + + inMemoryCachedResult = result; + + const cacheFilePath = getCacheFilePath({ cacheDirPath }); + + { + const dirPath = pathDirname(cacheFilePath); + + if (!(await existsAsync(dirPath))) { + await fs.mkdir(dirPath, { recursive: true }); + } + } + + await fs.writeFile( + cacheFilePath, + JSON.stringify( + zCache.parse({ + keycloakifyVersion: readThisNpmPackageVersion(), + time: Date.now(), + result + }), + null, + 2 + ) + ); + } + + return { + getCachedValue, + setCachedValue + }; +})(); diff --git a/src/bin/start-keycloak/realmConfig/ParsedRealmJson.ts b/src/bin/start-keycloak/realmConfig/ParsedRealmJson.ts new file mode 100644 index 00000000..ed67d501 --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/ParsedRealmJson.ts @@ -0,0 +1,118 @@ +import { z } from "zod"; +import { assert, type Equals } from "tsafe/assert"; +import { is } from "tsafe/is"; +import { id } from "tsafe/id"; +import * as fs from "fs"; + +export type ParsedRealmJson = { + realm: string; + loginTheme?: string; + accountTheme?: string; + adminTheme?: string; + emailTheme?: string; + eventsListeners: string[]; + users: { + id: string; + email: string; + username: string; + credentials: { + type: string /* "password" or something else */; + }[]; + clientRoles?: Record; + }[]; + roles: { + client: Record< + string, + { + name: string; + containerId: string; // client id + }[] + >; + }; + clients: { + id: string; + clientId: string; // example: realm-management + baseUrl?: string; + redirectUris?: string[]; + webOrigins?: string[]; + attributes?: { + "post.logout.redirect.uris"?: string; + }; + protocol?: string; + protocolMappers?: unknown[]; + }[]; +}; + +const zParsedRealmJson = (() => { + type TargetType = ParsedRealmJson; + + const zTargetType = z.object({ + realm: z.string(), + loginTheme: z.string().optional(), + accountTheme: z.string().optional(), + adminTheme: z.string().optional(), + emailTheme: z.string().optional(), + eventsListeners: z.array(z.string()), + users: z.array( + z.object({ + id: z.string(), + email: z.string(), + username: z.string(), + credentials: z.array( + z.object({ + type: z.string() + }) + ), + clientRoles: z.record(z.array(z.string())).optional() + }) + ), + roles: z.object({ + client: z.record( + z.array( + z.object({ + name: z.string(), + containerId: z.string() + }) + ) + ) + }), + clients: z.array( + z.object({ + id: z.string(), + clientId: z.string(), + baseUrl: z.string().optional(), + redirectUris: z.array(z.string()).optional(), + webOrigins: z.array(z.string()).optional(), + attributes: z + .object({ + "post.logout.redirect.uris": z.string().optional() + }) + .optional(), + protocol: z.string().optional(), + protocolMappers: z.array(z.unknown()).optional() + }) + ) + }); + + type InferredType = z.infer; + + assert>; + + return id>(zTargetType); +})(); + +export function readRealmJsonFile(params: { + realmJsonFilePath: string; +}): ParsedRealmJson { + const { realmJsonFilePath } = params; + + const parsedRealmJson = JSON.parse( + fs.readFileSync(realmJsonFilePath).toString("utf8") + ) as unknown; + + zParsedRealmJson.parse(parsedRealmJson); + + assert(is(parsedRealmJson)); + + return parsedRealmJson; +} diff --git a/src/bin/start-keycloak/realmConfig/defaultConfig/defaultConfig.ts b/src/bin/start-keycloak/realmConfig/defaultConfig/defaultConfig.ts new file mode 100644 index 00000000..2fd970bf --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/defaultConfig.ts @@ -0,0 +1,75 @@ +import { join as pathJoin, dirname as pathDirname } from "path"; +import { getThisCodebaseRootDirPath } from "../../../tools/getThisCodebaseRootDirPath"; +import * as fs from "fs"; +import { exclude } from "tsafe/exclude"; +import { assert } from "tsafe/assert"; +import { type ParsedRealmJson, readRealmJsonFile } from "../ParsedRealmJson"; + +export function getDefaultRealmJsonFilePath(params: { + keycloakMajorVersionNumber: number; +}) { + const { keycloakMajorVersionNumber } = params; + + return pathJoin( + getThisCodebaseRootDirPath(), + "src", + "bin", + "start-keycloak", + "realmConfig", + "defaultConfig", + `realm-kc-${keycloakMajorVersionNumber}.json` + ); +} + +export const { getSupportedKeycloakMajorVersions } = (() => { + let cache: number[] | undefined = undefined; + + function getSupportedKeycloakMajorVersions(): number[] { + if (cache !== undefined) { + return cache; + } + + cache = fs + .readdirSync( + pathDirname( + getDefaultRealmJsonFilePath({ keycloakMajorVersionNumber: 0 }) + ) + ) + .map(fileBasename => { + const match = fileBasename.match(/^realm-kc-(\d+)\.json$/); + + if (match === null) { + return undefined; + } + + const n = parseInt(match[1]); + + assert(!isNaN(n)); + + return n; + }) + .filter(exclude(undefined)) + .sort((a, b) => b - a); + + return cache; + } + + return { getSupportedKeycloakMajorVersions }; +})(); + +export function getDefaultConfig(params: { + keycloakMajorVersionNumber: number; +}): ParsedRealmJson { + const { keycloakMajorVersionNumber } = params; + + assert( + getSupportedKeycloakMajorVersions().includes(keycloakMajorVersionNumber), + `We do not have a default config for Keycloak ${keycloakMajorVersionNumber}` + ); + + return readRealmJsonFile({ + realmJsonFilePath: getDefaultRealmJsonFilePath({ + keycloakMajorVersionNumber + }) + }); +} diff --git a/src/bin/start-keycloak/realmConfig/defaultConfig/index.ts b/src/bin/start-keycloak/realmConfig/defaultConfig/index.ts new file mode 100644 index 00000000..a067247f --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/index.ts @@ -0,0 +1 @@ +export * from "./defaultConfig"; diff --git a/src/bin/start-keycloak/myrealm-realm-18.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-18.json similarity index 90% rename from src/bin/start-keycloak/myrealm-realm-18.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-18.json index a438eb64..7b46daf1 100644 --- a/src/bin/start-keycloak/myrealm-realm-18.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-18.json @@ -73,7 +73,7 @@ "composites": { "realm": ["offline_access", "uma_authorization"], "client": { - "account": ["delete-account", "view-profile", "manage-account"] + "account": ["view-profile", "manage-account", "delete-account"] } }, "clientRole": false, @@ -398,6 +398,26 @@ "otpPolicyLookAheadWindow": 1, "otpPolicyPeriod": 30, "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], "users": [ { "id": "00a62e75-bcc1-419a-a292-63ee5d161ed3", @@ -422,30 +442,43 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "create-client", + "view-identity-providers", + "manage-realm", + "query-groups", + "manage-clients", + "query-users", + "realm-admin", + "view-authorization", + "view-events", + "view-clients", + "view-realm", + "manage-events", + "query-realms", + "query-clients", + "manage-identity-providers", + "manage-users", + "view-users", + "impersonation", + "manage-authorization" + ], + "broker": ["read-token"], + "account": [ + "view-profile", + "manage-account-links", + "view-applications", + "manage-consent", + "delete-account", + "manage-account", + "view-consent" + ] + }, "notBefore": 0, "groups": [] } ], - "webAuthnPolicyRpEntityName": "keycloak", - "webAuthnPolicySignatureAlgorithms": ["ES256"], - "webAuthnPolicyRpId": "", - "webAuthnPolicyAttestationConveyancePreference": "not specified", - "webAuthnPolicyAuthenticatorAttachment": "not specified", - "webAuthnPolicyRequireResidentKey": "not specified", - "webAuthnPolicyUserVerificationRequirement": "not specified", - "webAuthnPolicyCreateTimeout": 0, - "webAuthnPolicyAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyAcceptableAaguids": [], - "webAuthnPolicyPasswordlessRpEntityName": "keycloak", - "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], - "webAuthnPolicyPasswordlessRpId": "", - "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", - "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", - "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", - "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", - "webAuthnPolicyPasswordlessCreateTimeout": 0, - "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, - "webAuthnPolicyPasswordlessAcceptableAaguids": [], "scopeMappings": [ { "clientScope": "offline_access", @@ -505,8 +538,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -518,6 +555,7 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { + "post.logout.redirect.uris": "+", "pkce.code.challenge.method": "S256" }, "authenticationFlowBindingOverrides": {}, @@ -636,7 +674,7 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", + "post.logout.redirect.uris": "+", "display.on.consent.screen": "false", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false" @@ -694,8 +732,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -707,6 +749,7 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { + "post.logout.redirect.uris": "+", "pkce.code.challenge.method": "S256" }, "authenticationFlowBindingOverrides": {}, @@ -757,7 +800,8 @@ "consentRequired": false, "config": { "id.token.claim": "true", - "access.token.claim": "true" + "access.token.claim": "true", + "userinfo.token.claim": "true" } } ] @@ -1205,6 +1249,7 @@ "consentRequired": false, "config": { "multivalued": "true", + "userinfo.token.claim": "true", "user.attribute": "foo", "id.token.claim": "true", "access.token.claim": "true", @@ -1271,11 +1316,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1291,14 +1336,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", - "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", - "saml-role-list-mapper", - "oidc-usermodel-property-mapper" + "saml-role-list-mapper" ] } }, @@ -1347,14 +1392,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-usermodel-property-mapper", - "oidc-address-mapper", "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", "saml-user-property-mapper", - "saml-user-attribute-mapper", - "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", - "saml-role-list-mapper" + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", + "oidc-address-mapper", + "saml-user-attribute-mapper" ] } }, @@ -1394,6 +1439,12 @@ "providerId": "rsa-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEpAIBAAKCAQEA+VQAcuaRivrzLVI8H/tt8PKbtRznTQKmmxOdLRR37leY/ph7sFnEmZt6K02Rvut7R0dxUFtTdiEHUKxhyM8CADMznGUjDYj/EXQzLfZ3LEwbwmR39zp+fZL/H24UDO03zt23Ov9C8Aly0ufXZ1Ic1c33KW6UtUEK/3M52pU8Y0daWdjx7nBj1eRlzWfVG+BYotTTWEnFJuEoZPFQMiXqeA5ob1zZdXjL5JDuGEiBsYjtiiaKbKL5545+FmEBnoCmWXqGu0qWxI2TzvV2dohxfl5KjNzRoKt40ydraiVk5rtBpoNDpeEApuphbokH5dJVwJ5cvWu1CSTnYPW2jXeG4wIDAQABAoIBAQDHV6AcPbhz8/xlafBkabQXBwHzJi7QZaQrLN1n44uX5jWOqP+LmdoULjjZUmWKzd98t+QjKUFrmzCsEYcE9G1XF5jWHA6Qjc3ReKRKxVm28wrmu0knQ39KizKrQGmLhEYwgRg0dU5heExzz6VrGD2xu8E3QRBocp6GauwAlXz4qcnTPHOl8OBPeDHAc0RUdaL5+jRLgKQzf9nnnKB19imBKP++zwrwFrkOZti2ZPs1I7j/ym27mHUbi8TDI2VepDX4QwjjC5a+v3vTsVAGE+1tUAZtqpxpIP9hiUkLH3ajyvp3typhnmZHklqsSZdwtRcK94WiMzL3TkiY70y8abMhAoGBAP8I4EQRXxcKfBn23eaRw8Cd4PFrOouz4zFbYLrBODsvXfku/jnQOMFD0If4IzT6y0FGgBd+t/yqnFJi98oZOKm3P8w+NZBXTbFLH8rgmsElXyS0+9LVMjVa7+UlqZB1eRZbUeLREp03Fsz1y2rflnoWgUnpDIlyhmJqGhCsJdebAoGBAPpFmJ9P42mUTeDWpCyCxgg0zpp6rlpAP8StqZkcvr7kYjhbWrJfJuxrTXtzTTA1zZ59L9EvEAxuug/gl9BkuZ11Uzg8ZLOr4gSuAJZlAORaxJlcoylmNMYIL1fP/K0dxhdO0eHZOpPVpBmGctgev2HBtWp9ZwzQ3DddKimZfNZZAoGAfNOOWSKbhT6HgXnYIHtl8YgUynUuYaR5ZfYQwTfDWwyTFVzP5+IndUjI71Qff1XlWBy2o0lNqmijPJveJlfz6PWdT01/kBd7GnTnqbgHZtPw3pmKzCW3fm/1DRZDCUbGLpAh4z9rufF1wnnnx3aKQ1VykId1sGySo+bEvTZVC1MCgYAlv6uWk/ksKpdYi2d14z+1aymieVClAj3cD4meM4y9xDrgXz8d2mZHkKO+NBT3aZYbCqzUs3GLPoRH8stTPm4UxuaHe+yAgTN1Gz2xcYih6OLwct2VV/oryH5Dk3Z8Mhp314amtxozxCydQP8/g9vABfS0HDgX4cTlgOLkJWeD+QKBgQDuRtsstQ4Q3yK44himPi1JQMMvbYAqyGgRxWH8G1Kr41DV2sQ4wt9CbYxeh6RwMsE+YYNMkTAw1kksUTugWdcDnYpcSVG7xHLJk8WMti0WTqI/7KlkoRehXXv18WJNEXaCr5mJTtJL9wuQcd8nhkEDrrCZubZiJzX9IDnEqZc4Mg==" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy58etTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE4Mzg0M1oXDTM0MTIxNTE4NDAyM1owEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPlUAHLmkYr68y1SPB/7bfDym7Uc500CppsTnS0Ud+5XmP6Ye7BZxJmbeitNkb7re0dHcVBbU3YhB1CsYcjPAgAzM5xlIw2I/xF0My32dyxMG8Jkd/c6fn2S/x9uFAztN87dtzr/QvAJctLn12dSHNXN9ylulLVBCv9zOdqVPGNHWlnY8e5wY9XkZc1n1RvgWKLU01hJxSbhKGTxUDIl6ngOaG9c2XV4y+SQ7hhIgbGI7Yomimyi+eeOfhZhAZ6Apll6hrtKlsSNk871dnaIcX5eSozc0aCreNMna2olZOa7QaaDQ6XhAKbqYW6JB+XSVcCeXL1rtQkk52D1to13huMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAH/nsEi88hFiNPCWYvTB3lERZpeUCbpDzAXQT/4TONmOw8zi7Cd2OlX8BGBFqjh/fESHv+adlzsY1mUdMvpVaYgHr3gYi8sBSrq5TMUfSYaWp4WCD7utiXXGprG08GCdbye1lpyyNnniWp12Bgjao+rtGamL/M1d6+WZTC+XL+H30u4VHURAiFBsAEoX6tlGV8ynhYOr/b8B43jy0/R0JfrzLjwSKEcA6RfKM7ozbZ0QZuQDALULymPIesrV4mvZ2Qwg4YgpAKaki9Sse45yiIhsIY0p5RnuNZRZnCbukyeBzIyDJobEBGhpui/KT2dqXBlRgRuOhCUf7OGCcPVHKNQ==" + ], "priority": ["100"] } }, @@ -1403,6 +1454,12 @@ "providerId": "rsa-enc-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEogIBAAKCAQEAn82AU+InXwYlE8u9lMwhQghZB7oQ71Hg3PdFqS9ICGzw1u1JcENooCsZse55V6nqptdYF1oZA8QrxnhHzCVCGIqFHtXSoPGHVtozO3Fe1cVIVFm1D9TNS3JHe1C8SBQQT4hGItO5cjDyfGdK3x09RkoAcelrzH5uQ78zd0FKHkzbsTMsP2V8V94c35+ViIUjyGhH2T2BpIyGRLignL+6d0wHbw463L1Ewj/J9z8BtNLCH9PaVLWiGQARjlWyL9vtWBig9XXL0Z9tZUuoLihjh4StkXt2lQ++DKxUklsAjyenRAG5d72T2rY8MO5a1Z2ZSt8+s86D5esrAEIFZc9mqwIDAQABAoIBAAmmCcqGzCPDpjd0xMSYMqXfBSkfReh9RBtzXqRhc3L2yO/hMd7yYv3QvGNu56qwWreqJup6CSqeDJqWJpef5EbBDlqXRHltO+O1lwROyxATMlPNes4y5hZZFxHOBSBA/d8fdkSiDf9kDzANuIqSJGH7E93M3zJgq92xTLU1nvkHR/VYJQv+j+Pjye7MWvjIePfhwFeBqEWlWPTlw/080Mpfp8Hhbl6JeKjx2inkSphp43v4wR1Wmp+E2JIHF4P4sVXPPuPf3JDwg5uGOrROw1ziloD3jTI+LnQ+kRm6R2EbqRqqVsehXT7mZy2puQNqVc4vVqWQdxIErMBazYEpZOECgYEA+8PEcDiIPr2PTYZk+/jErRVYwsxyLgDJexPak7onLxLBJRNRnp1Uk6b1LXM6af5qp+Y510kyAe1k+9xkQLx1gW8rMka9rvVsM+1A2ACvF99V23sRw29CVxeFV/zNn83MinYPX5biUl6MkOX2PvWUhdwRGhKByjiYcAeBOsXkz3ECgYEAon2yYXGzph8Vb8Fetv0wFFbjQOixuL02OjVp/nU1XVE8Aw9BJ7uzA6GQ7akPG0HsaUq7AEHP1uUOsJWQTNQ8WYD9LDuDOl/JFqkG+zrmdUdm0mAIYyH1/GBqgaTLvMq78qqosua8BBJojEyoXDz69UBHpu7cwtUgmzRNQSYqgdsCgYASvD3JEBvrd1XLsh2ftqKEMtt5G5e/nqVfuFmCts6lrSKcbLSdNh4OItWJ/VIygxFSz0osoDDNfeoO6Ba5zox8BlbTlfoVpAPaVWSG7n4ZK7CK9bybq5LnQkPVCWYP51O6VhDMz0CmWozhV4ucoc/cqkTHiOsJrm6Bn71ZL1LYsQKBgFNb8qgk4YnGhoPHiuSLbR/yFzGUbqAciXZBMrg0vwS5iPT03XMZytOBDk2uHi7YmgTGLrsKCCrxZaDXiaiwdKliD/+iJEdNHmc+nXNDGzltQOWKGKNqp7wqZllOBqs6wkLSpCrrTec03mejZ/ex3Pj2WgvcnGpjVg/pO/zBLKtjAoGACzGQNEF93fabHQJTsHmb/g+jO2iumjF6ZIWzdFh2KzQABONcoBvy1MJNASFQj3iVy/8kEo4SfmexvMWLBW9igi2z1pHeHY32EuImzuc4xnVDm6dkmDdsO43Ex6CFBx8lM40H4l27mXu+EZRzGClUY8TnmV/FBGmX+LPtOiiwT7s=" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy58fHjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE4Mzg0M1oXDTM0MTIxNTE4NDAyM1owEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/NgFPiJ18GJRPLvZTMIUIIWQe6EO9R4Nz3RakvSAhs8NbtSXBDaKArGbHueVep6qbXWBdaGQPEK8Z4R8wlQhiKhR7V0qDxh1baMztxXtXFSFRZtQ/UzUtyR3tQvEgUEE+IRiLTuXIw8nxnSt8dPUZKAHHpa8x+bkO/M3dBSh5M27EzLD9lfFfeHN+flYiFI8hoR9k9gaSMhkS4oJy/undMB28OOty9RMI/yfc/AbTSwh/T2lS1ohkAEY5Vsi/b7VgYoPV1y9GfbWVLqC4oY4eErZF7dpUPvgysVJJbAI8np0QBuXe9k9q2PDDuWtWdmUrfPrPOg+XrKwBCBWXPZqsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEATwmKBzLiZiUjyB9BWUR4BCXh46DxsiM0BCublewlUFY6FBTn7ea6q3G+X3QP2WM6xa0oAmQz9dq1KChbIoC2WPbceAbwd5XZZfziWsRCv6+xPswtpHPIrsenz8TR4K4P73aeCC+vTVs/y+2tGPEVbnSkcNnOP71hRQGlt0LvjKlEetJSRyYz5depSdJOjl4F3ehpxQtTK/48xUVAytu9ZotJj6AUA7jWFlP5GHgoB+mPk6QTHNWddnc7BQx2FMvg151vxu722ywLh5Dh7WzgFhJNwkX4xpwzhfo0Q1gSygGTdZaJCGj5jfF+KwdiKpN04UxJ8OrRgJqklQgrSVnsgQ==" + ], "priority": ["100"], "algorithm": ["RSA-OAEP"] } @@ -1413,6 +1470,8 @@ "providerId": "aes-generated", "subComponents": {}, "config": { + "kid": ["132fb843-59e9-4f36-ad55-5ce2d3a13fb3"], + "secret": ["ETyyqapnrkUsNXLQ-tBVKw"], "priority": ["100"] } }, @@ -1422,6 +1481,10 @@ "providerId": "hmac-generated", "subComponents": {}, "config": { + "kid": ["5110d380-c930-49d9-b91b-87f338f6170b"], + "secret": [ + "uCpQrJvP5OBuTxXfDb4JRL0bCKpXUgfGn5vb8UvL-Sfs_sZ9rtvBmd6vuFWARqyezjJQtpoNlMv7sXgxkN-yxQ" + ], "priority": ["100"], "algorithm": ["HS256"] } @@ -1454,7 +1517,7 @@ "defaultLocale": "en", "authenticationFlows": [ { - "id": "f7f2b89b-43cb-491d-8e7c-f1814024a6da", + "id": "223ce532-2038-4f24-a606-2a5c73f7bd65", "alias": "Account verification options", "description": "Method with which to verity the existing account", "providerId": "basic-flow", @@ -1480,7 +1543,7 @@ ] }, { - "id": "17cdac6f-d2a3-4907-8d44-a42827610b63", + "id": "57e47732-79cc-4d60-bee7-4f0b8fd44540", "alias": "Authentication Options", "description": "Authentication options.", "providerId": "basic-flow", @@ -1514,7 +1577,7 @@ ] }, { - "id": "53a3e43f-9468-401f-8051-40f982d12f85", + "id": "c2735d89-60c0-45a4-9b3c-ae5df17df395", "alias": "Browser - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1540,7 +1603,7 @@ ] }, { - "id": "26286808-3b7b-43df-b32e-af55a37af2e9", + "id": "11a5a507-2b9a-443f-961b-dffd66f4318d", "alias": "Direct Grant - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1566,7 +1629,7 @@ ] }, { - "id": "8a6a752a-9a9a-4d38-b1f8-edf0a9433490", + "id": "963bd753-6ea7-4d93-ab56-30f9ab59d597", "alias": "First broker login - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1592,7 +1655,7 @@ ] }, { - "id": "a6f6804c-4160-4a84-8a1f-c2747a2d3f27", + "id": "1db6a489-a3b4-44c4-b480-1d1e8c123d20", "alias": "Handle Existing Account", "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId": "basic-flow", @@ -1618,7 +1681,7 @@ ] }, { - "id": "740baa9e-8328-4035-9e1a-8fc1616d1f0f", + "id": "7a38f32d-4f34-450f-8f03-64802d7cb8f1", "alias": "Reset - Conditional OTP", "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId": "basic-flow", @@ -1644,7 +1707,7 @@ ] }, { - "id": "e60187a8-3e16-4a0c-9daa-f3a4a1fcfdba", + "id": "0df88739-3739-4d70-8893-47c546f19003", "alias": "User creation or linking", "description": "Flow for the existing/non-existing user alternatives", "providerId": "basic-flow", @@ -1671,7 +1734,7 @@ ] }, { - "id": "d959d0c2-4004-4633-b280-f80d6423f574", + "id": "35025424-e291-4c54-8a29-70aadba549ce", "alias": "Verify Existing Account by Re-authentication", "description": "Reauthentication of existing account", "providerId": "basic-flow", @@ -1697,7 +1760,7 @@ ] }, { - "id": "ba02689d-b9e8-4a4b-8fdd-0d1386b198fc", + "id": "1813b7f2-c3c2-4b92-8ffc-9ff2d12186c6", "alias": "browser", "description": "browser based authentication", "providerId": "basic-flow", @@ -1739,7 +1802,7 @@ ] }, { - "id": "f09ac92a-e091-4e84-9cd1-cb905ca57b89", + "id": "954283ac-f1c2-40b6-a39f-bf23ff9f3ce8", "alias": "clients", "description": "Base authentication for clients", "providerId": "client-flow", @@ -1781,7 +1844,7 @@ ] }, { - "id": "aaf72b22-cec4-4714-93d6-f54d5a986ab8", + "id": "52a789ce-2cad-4f0f-93b2-295b7fd519f0", "alias": "direct grant", "description": "OpenID Connect Resource Owner Grant", "providerId": "basic-flow", @@ -1815,7 +1878,7 @@ ] }, { - "id": "c4a54bb3-f009-4231-a82b-376c2515e07e", + "id": "5a6a71e1-9105-45b6-b5f0-52538461357b", "alias": "docker auth", "description": "Used by Docker clients to authenticate against the IDP", "providerId": "basic-flow", @@ -1833,7 +1896,7 @@ ] }, { - "id": "f55ded54-683a-4f5a-a101-9cfbd7b96781", + "id": "8392b6e7-bdbf-4d7f-97b6-885761c200db", "alias": "first broker login", "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId": "basic-flow", @@ -1860,7 +1923,7 @@ ] }, { - "id": "931d5a82-378f-4533-8c69-2239a4acd047", + "id": "52136d70-8d08-42ea-b04b-cf40ea2807aa", "alias": "forms", "description": "Username, password, otp and other auth forms.", "providerId": "basic-flow", @@ -1886,7 +1949,7 @@ ] }, { - "id": "22b05374-f480-4ca8-aca8-9db8b6dd1729", + "id": "26bbc7e6-ef01-4cdb-9dba-520e2f3f8993", "alias": "http challenge", "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId": "basic-flow", @@ -1912,7 +1975,7 @@ ] }, { - "id": "c0371832-e4b7-485e-bf23-6babe4c6ac83", + "id": "f0887979-04eb-4033-8f19-0ffd8c8b7f6a", "alias": "registration", "description": "registration flow", "providerId": "basic-flow", @@ -1931,7 +1994,7 @@ ] }, { - "id": "4d0445da-073e-465e-b25b-af522915c73f", + "id": "a3b7b94b-bfbf-4760-a8c9-7d9cd98d262e", "alias": "registration form", "description": "registration form", "providerId": "form-flow", @@ -1973,7 +2036,7 @@ ] }, { - "id": "740d467f-4203-425b-8203-9bfd3eed25ae", + "id": "dc68a665-2e51-4a22-aaad-bd693ddc77cc", "alias": "reset credentials", "description": "Reset credentials for a user if they forgot their password or something", "providerId": "basic-flow", @@ -2015,7 +2078,7 @@ ] }, { - "id": "cf1a9af9-dadd-4cb9-a26e-fbbba216f8e1", + "id": "ae6b73aa-1318-4ae8-a3d9-d01b5e7d957e", "alias": "saml ecp", "description": "SAML ECP Profile Authentication Flow", "providerId": "basic-flow", @@ -2035,14 +2098,14 @@ ], "authenticatorConfig": [ { - "id": "4e65eb4b-9f0a-4ab8-98b2-6daf50cd1bf8", + "id": "0c18de7f-0714-41f4-9a3f-ed4edd53ae9c", "alias": "create unique user config", "config": { "require.password.update.after.registration": "false" } }, { - "id": "5e8dc1c5-1489-4d39-bb75-9c499583b91b", + "id": "65b3c8bb-34a4-4d19-b578-245dc8ff53ea", "alias": "review profile config", "config": { "update.profile.on.first.login": "missing" @@ -2132,8 +2195,8 @@ "attributes": { "cibaBackchannelTokenDeliveryMode": "poll", "cibaAuthRequestedUserHint": "login_hint", - "oauth2DevicePollingInterval": "5", "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", "clientSessionIdleTimeout": "0", "userProfileEnabled": "true", "clientOfflineSessionIdleTimeout": "0", diff --git a/src/bin/start-keycloak/myrealm-realm-19.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-19.json similarity index 96% rename from src/bin/start-keycloak/myrealm-realm-19.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-19.json index e71ef276..136f1321 100644 --- a/src/bin/start-keycloak/myrealm-realm-19.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-19.json @@ -73,7 +73,7 @@ "composites": { "realm": ["offline_access", "uma_authorization"], "client": { - "account": ["delete-account", "view-profile", "manage-account"] + "account": ["view-profile", "manage-account", "delete-account"] } }, "clientRole": false, @@ -435,13 +435,46 @@ "type": "password", "userLabel": "My password", "createdDate": 1716214710762, - "secretData": "{\"value\":\"OaI4sKqQn+NZtS6N/bcqoZ8Q+ucpBby1n4XmzVmioKw=\",\"salt\":\"temixVCSbpA7Genml2KTAw==\",\"additionalParameters\":{}}", + "secretData": "{\"value\":\"QzJjOdXU0L9Pdxdx1V5xUs7BY9beGlmN8NpR2qiWxbkjrQ434Q1GwSiJKekZQ/zrLDtNZ7sAbVu+SS+XIe9Zaw==\",\"salt\":\"x8cABpa0Hk/nJ2BPKdFXTg==\",\"additionalParameters\":{}}", "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" } ], "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "create-client", + "view-identity-providers", + "manage-realm", + "query-groups", + "manage-clients", + "query-users", + "realm-admin", + "view-authorization", + "view-events", + "view-clients", + "view-realm", + "manage-events", + "query-realms", + "query-clients", + "manage-identity-providers", + "manage-users", + "view-users", + "impersonation", + "manage-authorization" + ], + "broker": ["read-token"], + "account": [ + "view-profile", + "manage-account-links", + "view-applications", + "manage-consent", + "delete-account", + "manage-account", + "view-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -507,8 +540,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -643,7 +680,6 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", "post.logout.redirect.uris": "+", "display.on.consent.screen": "false", "oauth2.device.authorization.grant.enabled": "false", @@ -704,8 +740,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1284,11 +1324,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1304,14 +1344,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-full-name-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper", - "oidc-address-mapper", "saml-user-property-mapper", - "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", - "saml-role-list-mapper" + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" ] } }, @@ -1360,14 +1400,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "saml-user-property-mapper", - "saml-user-attribute-mapper", - "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", - "oidc-address-mapper", + "oidc-usermodel-property-mapper", "saml-role-list-mapper", - "oidc-usermodel-property-mapper" + "oidc-full-name-mapper", + "saml-user-property-mapper", + "oidc-address-mapper", + "saml-user-attribute-mapper" ] } }, @@ -1485,7 +1525,7 @@ "defaultLocale": "en", "authenticationFlows": [ { - "id": "e134634e-f219-4df4-867c-8110688d8e56", + "id": "1f4d4e13-1591-4751-8985-17886a8c98a9", "alias": "Account verification options", "description": "Method with which to verity the existing account", "providerId": "basic-flow", @@ -1511,7 +1551,7 @@ ] }, { - "id": "a611a8eb-9626-4aa4-8b54-ee565ea6e5dc", + "id": "126f07c3-1bcb-4a02-bf16-bb44674bf55d", "alias": "Authentication Options", "description": "Authentication options.", "providerId": "basic-flow", @@ -1545,7 +1585,7 @@ ] }, { - "id": "d87cbb31-5c69-45c8-888d-f9649ebbbf97", + "id": "eb3a08c8-5f99-49b6-b02b-16b62571f273", "alias": "Browser - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1571,7 +1611,7 @@ ] }, { - "id": "752ba282-a369-4592-92e8-b4287192dbbf", + "id": "3dc19838-5025-4bbb-b569-b574bd5a8d90", "alias": "Direct Grant - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1597,7 +1637,7 @@ ] }, { - "id": "2349282e-40ff-431a-984d-53911511e3d3", + "id": "70d6fd40-d740-4dae-b0e6-350f8e9d4a1c", "alias": "First broker login - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1623,7 +1663,7 @@ ] }, { - "id": "4ff5463d-26d9-4219-ba85-41464401098f", + "id": "6e24dcb3-5818-483c-8e44-883858171901", "alias": "Handle Existing Account", "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId": "basic-flow", @@ -1649,7 +1689,7 @@ ] }, { - "id": "87bb6c6d-cca8-4832-b5ab-67ecb9454a42", + "id": "ac6254cd-403b-457b-b308-22a2a0e4f99d", "alias": "Reset - Conditional OTP", "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId": "basic-flow", @@ -1675,7 +1715,7 @@ ] }, { - "id": "1fc3d028-0e0a-43a4-aaf9-ba7f7d60b409", + "id": "485e74e6-9b3e-4b2c-a9b9-927802dc4f06", "alias": "User creation or linking", "description": "Flow for the existing/non-existing user alternatives", "providerId": "basic-flow", @@ -1702,7 +1742,7 @@ ] }, { - "id": "036aae59-641f-4799-9124-c7e5034af6c1", + "id": "ff9bb879-1d6a-4d1c-9836-1e4fab6f8997", "alias": "Verify Existing Account by Re-authentication", "description": "Reauthentication of existing account", "providerId": "basic-flow", @@ -1728,7 +1768,7 @@ ] }, { - "id": "2e8b9f28-93b8-4368-84b0-1a8326daafe0", + "id": "af8b2470-d581-401c-9984-762b966ebcc2", "alias": "browser", "description": "browser based authentication", "providerId": "basic-flow", @@ -1770,7 +1810,7 @@ ] }, { - "id": "0b826105-8493-45ce-87b3-7d917d190b39", + "id": "414dbda4-eb3f-4baa-b23a-d3423af1eae6", "alias": "clients", "description": "Base authentication for clients", "providerId": "client-flow", @@ -1812,7 +1852,7 @@ ] }, { - "id": "bf6d9edd-48d8-4392-bbc8-4b17a6866074", + "id": "1cae0c4b-8dfb-4f5d-a781-e74d0a13c940", "alias": "direct grant", "description": "OpenID Connect Resource Owner Grant", "providerId": "basic-flow", @@ -1846,7 +1886,7 @@ ] }, { - "id": "97e31722-dd11-42be-aa99-88788fa2dde6", + "id": "e798b655-7d85-4b6b-aee7-1448a3e1e0ea", "alias": "docker auth", "description": "Used by Docker clients to authenticate against the IDP", "providerId": "basic-flow", @@ -1864,7 +1904,7 @@ ] }, { - "id": "3f45cf34-231f-4ea1-8e58-d636c451a76b", + "id": "eb94b723-1041-426a-87bf-f7b4bd2f485d", "alias": "first broker login", "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId": "basic-flow", @@ -1891,7 +1931,7 @@ ] }, { - "id": "9bef2f7c-f989-4871-aaa7-18e2cfa73f22", + "id": "452d1d5f-7632-44d7-bc89-77ff2b209b3e", "alias": "forms", "description": "Username, password, otp and other auth forms.", "providerId": "basic-flow", @@ -1917,7 +1957,7 @@ ] }, { - "id": "0bfaa325-acde-4443-8bd8-1dc2ae759c5f", + "id": "7c1b9e8f-6b57-49d1-a9a7-494862f93c0f", "alias": "http challenge", "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId": "basic-flow", @@ -1943,7 +1983,7 @@ ] }, { - "id": "37ddbe8c-abf3-4654-bd6d-ffabbeefbb98", + "id": "2b38f34a-1739-499e-bb24-1dff96f32009", "alias": "registration", "description": "registration flow", "providerId": "basic-flow", @@ -1962,7 +2002,7 @@ ] }, { - "id": "5d7b4bc9-e93b-40da-aeb6-ba0c38392f1a", + "id": "d26ae72b-a933-44dc-9927-1c82757004b2", "alias": "registration form", "description": "registration form", "providerId": "form-flow", @@ -2004,7 +2044,7 @@ ] }, { - "id": "ee7a56e4-c827-4f24-8b8b-8476050b0b64", + "id": "222ee8d6-1892-4768-9ada-720274b6bf9a", "alias": "reset credentials", "description": "Reset credentials for a user if they forgot their password or something", "providerId": "basic-flow", @@ -2046,7 +2086,7 @@ ] }, { - "id": "360f0031-4c3b-4272-84ca-2172d430b4bc", + "id": "e8b4d92c-27c1-4a9b-9b16-7ceb810fa230", "alias": "saml ecp", "description": "SAML ECP Profile Authentication Flow", "providerId": "basic-flow", @@ -2066,14 +2106,14 @@ ], "authenticatorConfig": [ { - "id": "53630acd-a33a-40e3-8786-cf85464c6f9e", + "id": "e5847a0b-855d-4d93-85fd-94714be3ed92", "alias": "create unique user config", "config": { "require.password.update.after.registration": "false" } }, { - "id": "c0d2b6a0-caad-4e90-b040-17cacdaf70bb", + "id": "a2a18aa4-bd4c-4c2a-9286-e9d6c64f4812", "alias": "review profile config", "config": { "update.profile.on.first.login": "missing" diff --git a/src/bin/start-keycloak/myrealm-realm-20.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-20.json similarity index 96% rename from src/bin/start-keycloak/myrealm-realm-20.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-20.json index 56d0c4c7..c30db8f6 100644 --- a/src/bin/start-keycloak/myrealm-realm-20.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-20.json @@ -73,7 +73,7 @@ "composites": { "realm": ["offline_access", "uma_authorization"], "client": { - "account": ["delete-account", "view-profile", "manage-account"] + "account": ["view-profile", "manage-account", "delete-account"] } }, "clientRole": false, @@ -407,7 +407,7 @@ "otpPolicyLookAheadWindow": 1, "otpPolicyPeriod": 30, "otpPolicyCodeReusable": false, - "otpSupportedApplications": ["totpAppGoogleName", "totpAppFreeOTPName"], + "otpSupportedApplications": ["totpAppFreeOTPName", "totpAppGoogleName"], "webAuthnPolicyRpEntityName": "keycloak", "webAuthnPolicySignatureAlgorithms": ["ES256"], "webAuthnPolicyRpId": "", @@ -452,6 +452,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "create-client", + "view-identity-providers", + "manage-realm", + "query-groups", + "manage-clients", + "query-users", + "realm-admin", + "view-authorization", + "view-events", + "view-clients", + "view-realm", + "manage-events", + "query-realms", + "query-clients", + "manage-identity-providers", + "manage-users", + "view-users", + "impersonation", + "manage-authorization" + ], + "broker": ["read-token"], + "account": [ + "view-profile", + "manage-account-links", + "view-applications", + "manage-consent", + "delete-account", + "manage-account", + "view-groups", + "view-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -517,8 +551,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -653,7 +691,6 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", "post.logout.redirect.uris": "+", "display.on.consent.screen": "false", "oauth2.device.authorization.grant.enabled": "false", @@ -714,8 +751,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1294,11 +1335,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1314,14 +1355,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "saml-user-property-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-attribute-mapper", - "saml-user-attribute-mapper", "oidc-address-mapper", - "saml-role-list-mapper", "oidc-full-name-mapper", - "oidc-usermodel-property-mapper" + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "saml-user-attribute-mapper" ] } }, @@ -1370,14 +1411,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "oidc-address-mapper", - "saml-role-list-mapper", "saml-user-attribute-mapper", - "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "oidc-address-mapper", "saml-user-property-mapper", - "oidc-usermodel-property-mapper" + "oidc-usermodel-attribute-mapper" ] } }, @@ -1495,7 +1536,7 @@ "defaultLocale": "en", "authenticationFlows": [ { - "id": "19317acb-fe8e-4c79-82bc-90e159273075", + "id": "c40791b4-4d59-4df2-bebd-2b71e793704f", "alias": "Account verification options", "description": "Method with which to verity the existing account", "providerId": "basic-flow", @@ -1521,7 +1562,7 @@ ] }, { - "id": "122857d2-33da-4086-8acb-cb0e303aaf1b", + "id": "8813b6d1-8b88-4672-b29b-8420ce3f3975", "alias": "Authentication Options", "description": "Authentication options.", "providerId": "basic-flow", @@ -1555,7 +1596,7 @@ ] }, { - "id": "abf5dd35-4791-4268-a10c-5f4b6a06b84a", + "id": "a9937c40-a1ee-4c57-adf7-ede0a9983953", "alias": "Browser - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1581,7 +1622,7 @@ ] }, { - "id": "a18daeec-a33c-4a43-b014-10c84ec69b81", + "id": "2d494b5a-eb73-40d0-94d3-a8d8024a7db4", "alias": "Direct Grant - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1607,7 +1648,7 @@ ] }, { - "id": "e9f032a7-32f7-457c-becf-011a1a35cc6a", + "id": "2e977f5a-8110-412b-b704-3e15164dbb1b", "alias": "First broker login - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1633,7 +1674,7 @@ ] }, { - "id": "9db65b7c-98ca-4003-beea-611038831ffe", + "id": "6f171b4b-8723-4e6d-bb1e-6b4293a7bb3f", "alias": "Handle Existing Account", "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId": "basic-flow", @@ -1659,7 +1700,7 @@ ] }, { - "id": "7bd0854c-d7ae-43d7-a1ae-7b759a34cb1d", + "id": "2dbb7f27-757d-4178-8217-4a24fdb0163c", "alias": "Reset - Conditional OTP", "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId": "basic-flow", @@ -1685,7 +1726,7 @@ ] }, { - "id": "2de1a450-fe98-443a-9c6c-d24d8a7ebcb3", + "id": "7295aaf7-acf4-4b78-8186-d2415ea4ede0", "alias": "User creation or linking", "description": "Flow for the existing/non-existing user alternatives", "providerId": "basic-flow", @@ -1712,7 +1753,7 @@ ] }, { - "id": "7b3efad5-4b7d-4385-a41c-fecc73afdcc4", + "id": "e0d34d7c-7bbb-4847-8864-fbd97a1f3e89", "alias": "Verify Existing Account by Re-authentication", "description": "Reauthentication of existing account", "providerId": "basic-flow", @@ -1738,7 +1779,7 @@ ] }, { - "id": "de93418e-8f28-4099-b15e-ad36ec194796", + "id": "5f3d0fb0-d95e-4841-89d3-a27d0cdbbcb4", "alias": "browser", "description": "browser based authentication", "providerId": "basic-flow", @@ -1780,7 +1821,7 @@ ] }, { - "id": "0dd3345c-6e82-4c3a-a39a-d49ae1f5c409", + "id": "c246380d-af25-4151-ab19-1f1e5b553008", "alias": "clients", "description": "Base authentication for clients", "providerId": "client-flow", @@ -1822,7 +1863,7 @@ ] }, { - "id": "87fb4dd0-5326-47a1-b670-982f4872ff89", + "id": "abacf398-0f1f-4f28-a310-8d306d588048", "alias": "direct grant", "description": "OpenID Connect Resource Owner Grant", "providerId": "basic-flow", @@ -1856,7 +1897,7 @@ ] }, { - "id": "344723b3-4ab1-4999-abdd-32398e82327b", + "id": "a0f87683-619a-44d4-8b4f-4b053bba2346", "alias": "docker auth", "description": "Used by Docker clients to authenticate against the IDP", "providerId": "basic-flow", @@ -1874,7 +1915,7 @@ ] }, { - "id": "f3341938-caf9-4c8a-9cd5-eb34609809ab", + "id": "e8820c7c-22a7-4618-beb7-3e09be72c00c", "alias": "first broker login", "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId": "basic-flow", @@ -1901,7 +1942,7 @@ ] }, { - "id": "ba7b7357-e324-4b71-9bda-f8512a760e02", + "id": "cac00c38-ee44-44c9-b95e-cc755bab36ef", "alias": "forms", "description": "Username, password, otp and other auth forms.", "providerId": "basic-flow", @@ -1927,7 +1968,7 @@ ] }, { - "id": "134971e6-bf63-432c-806e-74ca4fb09963", + "id": "688cde36-507e-4a68-afdf-18ec4ad626a7", "alias": "http challenge", "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId": "basic-flow", @@ -1953,7 +1994,7 @@ ] }, { - "id": "6ea9e2cf-5684-4c65-8c07-930d1cbb0b46", + "id": "e058697c-f450-4f14-ae64-04e9299fa24f", "alias": "registration", "description": "registration flow", "providerId": "basic-flow", @@ -1972,7 +2013,7 @@ ] }, { - "id": "67e3c8c7-1b5e-4119-84a2-e90876293150", + "id": "ad768088-32c9-4979-90dd-61bf111fd72e", "alias": "registration form", "description": "registration form", "providerId": "form-flow", @@ -2014,7 +2055,7 @@ ] }, { - "id": "fc6d48ec-a1f1-41b1-9310-54f58861d5aa", + "id": "47d4b090-f965-4588-b5bc-029ccb59876f", "alias": "reset credentials", "description": "Reset credentials for a user if they forgot their password or something", "providerId": "basic-flow", @@ -2056,7 +2097,7 @@ ] }, { - "id": "80b1d464-c2ec-4eb1-82e8-32cbede779a8", + "id": "1f68feec-7f99-4c49-afe6-45d46684ca21", "alias": "saml ecp", "description": "SAML ECP Profile Authentication Flow", "providerId": "basic-flow", @@ -2076,14 +2117,14 @@ ], "authenticatorConfig": [ { - "id": "86b1d5fa-450c-40d8-899c-725861ac39fc", + "id": "bd7365c7-842b-4bc6-a4ca-498cf025c210", "alias": "create unique user config", "config": { "require.password.update.after.registration": "false" } }, { - "id": "ea724f02-029a-493d-b4d3-08972be21cfb", + "id": "b929192d-f650-4a09-9701-3d3216547552", "alias": "review profile config", "config": { "update.profile.on.first.login": "missing" diff --git a/src/bin/start-keycloak/myrealm-realm-21.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-21.json similarity index 97% rename from src/bin/start-keycloak/myrealm-realm-21.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-21.json index adfc1e78..a4e9f84b 100644 --- a/src/bin/start-keycloak/myrealm-realm-21.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-21.json @@ -73,7 +73,7 @@ "composites": { "realm": ["offline_access", "uma_authorization"], "client": { - "account": ["delete-account", "view-profile", "manage-account"] + "account": ["view-profile", "manage-account", "delete-account"] } }, "clientRole": false, @@ -456,6 +456,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "create-client", + "view-identity-providers", + "manage-realm", + "query-groups", + "manage-clients", + "query-users", + "realm-admin", + "view-authorization", + "view-events", + "view-clients", + "view-realm", + "manage-events", + "query-realms", + "query-clients", + "manage-identity-providers", + "manage-users", + "view-users", + "impersonation", + "manage-authorization" + ], + "broker": ["read-token"], + "account": [ + "view-profile", + "manage-account-links", + "view-applications", + "manage-consent", + "delete-account", + "manage-account", + "view-groups", + "view-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -521,8 +555,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -657,7 +695,6 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", "post.logout.redirect.uris": "+", "display.on.consent.screen": "false", "oauth2.device.authorization.grant.enabled": "false", @@ -718,8 +755,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1298,11 +1339,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1318,13 +1359,13 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", - "saml-user-property-mapper", - "saml-role-list-mapper", - "saml-user-attribute-mapper", - "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", "oidc-address-mapper" ] } @@ -1374,14 +1415,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", - "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", - "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-user-property-mapper", "saml-role-list-mapper", - "saml-user-property-mapper" + "saml-user-attribute-mapper" ] } }, diff --git a/src/bin/start-keycloak/myrealm-realm-23.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-23.json similarity index 97% rename from src/bin/start-keycloak/myrealm-realm-23.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-23.json index 6b498de1..8e720d31 100644 --- a/src/bin/start-keycloak/myrealm-realm-23.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-23.json @@ -55,7 +55,7 @@ "composites": { "realm": ["offline_access", "uma_authorization"], "client": { - "account": ["delete-account", "view-profile", "manage-account"] + "account": ["view-profile", "delete-account", "manage-account"] } }, "clientRole": false, @@ -459,6 +459,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "query-clients", + "manage-identity-providers", + "create-client", + "view-users", + "query-groups", + "view-realm", + "manage-authorization", + "view-authorization", + "query-users", + "impersonation", + "realm-admin", + "manage-users", + "view-identity-providers", + "manage-realm", + "manage-clients", + "query-realms", + "view-events", + "manage-events", + "view-clients" + ], + "broker": ["read-token"], + "account": [ + "manage-account", + "view-consent", + "view-groups", + "delete-account", + "view-applications", + "manage-account-links", + "view-profile", + "manage-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -505,7 +539,6 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", @@ -532,8 +565,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -649,7 +686,11 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["https://my-theme.keycloakify.dev/*", "http://localhost*"], + "redirectUris": [ + "https://my-theme.keycloakify.dev/*", + "http://localhost*", + "http://127.0.0.1*" + ], "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, @@ -664,8 +705,7 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", - "post.logout.redirect.uris": "https://my-theme.keycloakify.dev/*", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", "backchannel.logout.revoke.offline.tokens": "false" @@ -725,8 +765,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1336,12 +1380,12 @@ "strictTransportSecurity": "max-age=31536000; includeSubDomains" }, "smtpServer": {}, - "loginTheme": "", - "accountTheme": "keycloakify-starter", + "loginTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1357,13 +1401,13 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "saml-user-property-mapper", - "oidc-address-mapper", - "oidc-full-name-mapper", "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-address-mapper", + "saml-user-property-mapper", "oidc-usermodel-property-mapper" ] } @@ -1433,13 +1477,13 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "saml-role-list-mapper", - "oidc-full-name-mapper", - "oidc-address-mapper", "saml-user-attribute-mapper", - "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper" ] } diff --git a/src/bin/start-keycloak/myrealm-realm-24.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-24.json similarity index 98% rename from src/bin/start-keycloak/myrealm-realm-24.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-24.json index 1269d783..73793582 100644 --- a/src/bin/start-keycloak/myrealm-realm-24.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-24.json @@ -468,6 +468,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "manage-clients", + "manage-users", + "view-identity-providers", + "view-users", + "impersonation", + "manage-identity-providers", + "query-users", + "query-realms", + "realm-admin", + "view-events", + "view-realm", + "manage-events", + "manage-authorization", + "manage-realm", + "query-clients", + "query-groups", + "view-clients", + "create-client", + "view-authorization" + ], + "broker": ["read-token"], + "account": [ + "manage-consent", + "manage-account-links", + "view-applications", + "view-consent", + "manage-account", + "view-profile", + "view-groups", + "delete-account" + ] + }, "notBefore": 0, "groups": [] } @@ -514,7 +548,6 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", @@ -541,8 +574,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/realms/myrealm/account/*"], - "webOrigins": [], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -658,7 +695,11 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["https://my-theme.keycloakify.dev/*", "http://localhost*"], + "redirectUris": [ + "https://my-theme.keycloakify.dev/*", + "http://localhost*", + "http://127.0.0.1*" + ], "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, @@ -673,8 +714,7 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", - "post.logout.redirect.uris": "https://my-theme.keycloakify.dev/*##http://localhost*", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", "backchannel.logout.revoke.offline.tokens": "false" @@ -840,8 +880,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1451,12 +1495,12 @@ "strictTransportSecurity": "max-age=31536000; includeSubDomains" }, "smtpServer": {}, - "loginTheme": "keycloak", - "accountTheme": "keycloakify-starter", + "loginTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1504,11 +1548,11 @@ "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", - "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", - "oidc-full-name-mapper", - "saml-user-property-mapper" + "oidc-full-name-mapper" ] } }, @@ -1540,14 +1584,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper", "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", - "saml-user-property-mapper", - "saml-role-list-mapper", - "saml-user-attribute-mapper" + "saml-user-property-mapper" ] } }, diff --git a/src/bin/start-keycloak/myrealm-realm-25.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-25.json similarity index 92% rename from src/bin/start-keycloak/myrealm-realm-25.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-25.json index 89153229..969f3bc9 100644 --- a/src/bin/start-keycloak/myrealm-realm-25.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-25.json @@ -538,10 +538,10 @@ "emailVerified": true, "attributes": { "additional_emails": ["test.user@protonmail.com", "testuser@hotmail.com"], - "gender": ["prefer_not_to_say"], "favorite_pet": ["cats"], - "favourite_pet": ["cat"], + "gender": ["prefer_not_to_say"], "bio": ["Hello I'm Test User and I do not exist."], + "favourite_pet": ["cat"], "phone_number": ["1111111111"], "locale": ["en"], "favorite_media": ["movies", "series"] @@ -562,6 +562,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "manage-users", + "create-client", + "view-users", + "view-realm", + "query-realms", + "impersonation", + "view-events", + "realm-admin", + "manage-authorization", + "manage-events", + "view-authorization", + "manage-clients", + "query-users", + "query-groups", + "manage-realm", + "query-clients", + "manage-identity-providers", + "view-clients", + "view-identity-providers" + ], + "broker": ["read-token"], + "account": [ + "delete-account", + "view-applications", + "manage-account", + "view-consent", + "view-groups", + "view-profile", + "manage-account-links", + "manage-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -636,7 +670,7 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["*"], + "redirectUris": ["http://localhost*", "http://127.0.0.1*", "*"], "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, @@ -798,8 +832,7 @@ "attributes": { "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", - "post.logout.redirect.uris": "https://my-theme.keycloakify.dev/*##http://localhost*##http://127.0.0.1*", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", "backchannel.logout.revoke.offline.tokens": "false" @@ -892,8 +925,12 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -1551,11 +1588,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1581,14 +1618,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "oidc-full-name-mapper", - "oidc-usermodel-property-mapper", "saml-role-list-mapper", - "saml-user-attribute-mapper", + "oidc-full-name-mapper", "saml-user-property-mapper", + "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", - "oidc-address-mapper" + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper" ] } }, @@ -1618,14 +1655,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-sha256-pairwise-sub-mapper", - "saml-user-property-mapper", - "oidc-usermodel-attribute-mapper", "oidc-address-mapper", - "saml-role-list-mapper", - "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", - "oidc-full-name-mapper" + "oidc-full-name-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper" ] } }, @@ -1678,6 +1715,12 @@ "providerId": "rsa-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAso89qpvLhf9DIcCb2JAbxItRLSIvP/NCZhMdAExTHyrhM5B27ZQ6MZ7dJQbnMu7QJ7yiClsD1XnDN7Wlj07sY2As3lY3v9kjODBeADYlPuN1m7/fXFHX3qfRT+PwVSaAhMykmqvWp86UTg7t7rNjVBnXPPXItmRLIF+jZUMWQduwNznr6Jh54ZdIwEy4hvX1bpNw0nPl4KXiOi2elvg+rk7BhFywGwQ/HUCGkrcq0XS/aNOy1ChmqDbtq817mYpVeteCDe8xP3MPrZ/s2LiEt4Ip1cNo0dY+a4JwOzwL42h3GaR+80iK3pZNo+Mr0KBOY9GXvdV/MvcPHLQ7VujUGQIDAQABAoIBAAHV0OQwmDxUazqiVGe61Bzmcqs5q03SC1K/FmCi/YVikdskvGLaOmk5UQa4+1uDEq7J30onH9ML8+qeFRQek0rn2ZDfxtBpDqsx7LwTUmQtqc8z6buKQs37db5ctnhlk34UmAotQyDz5wMmCkzWWVUWCT02PdMev5qW/mKuIxaCWLHUFiMJaGrYCCwB/Ra8KLcadKgRbytSUth9qILC4krFfmWtzIx1P6nM1pzQ1nydxNnNPJKjoWtLRJ5b701Y5/h2vAAg6Mr+jKe1DPa9QmAqhQudjGbZ31av+0f1/I+XkflpZfokfU+MrAqNYRTYkevRYgc3wakK5mfVYUiMuOECgYEA7fk55O2OJFsR0Vjy4Dx4eSIwgwobvwEuHxlyWn0RC7nFb00eh6OPuc5sHrOk8bK3P367q67sEhxGyBF16nwxgX/T+c8gTC8QRuwNymosA4Je/zJHbKvyzLGOouCP5gYwq/wUmVWzNApVC7LBfxbsqYyivHABc5xgPmTgecY0VWkCgYEAwBXcUKoyq1KZegyNJcTuwuvBXoYVveFGm6QKKKwzojCCKaR3XXtdSon1qYfuKT0MLxgEDyyBks9DgfCodSsTmajX90Yolhyz3ptcOmRURqTRoJhM4g6qA+Ybd3uy8vAz32RdS+4rCTgnMG/5Xpn5B4ojOnhRcnA2TPCJgWz6QzECgYEAhj1FjD75JMb+mRJNB3L1HpfLt8+28RsQUli/ag4M1Il5txxQsYDxbYXk9biuvezrc/Tglqs43cp3nxpCYwClyIA8KjnN5UvTKb601M7pfx1GyzwokEO61f7/ECAO7FnnkMzFLe3rBdsiOFQg1LkwzT/Y+OVR3E6E+A1dlzPYh6kCgYBIP3CwfnO0cMr9Vv8394x+kEIZFYHT+4mdPOP9TFfXZztuAkhLRv1d7eoSq+fuZuHQTM4qDullmMOhei1CdMNYhmNExIS7gWw+DF1yMQ5py9B1ARPZ6v4TnVczZ7l1GtfH7G4TAy/4tcA3vcYjyPIb3d9GPL8VthMWeVqe7ahr4QKBgEwA7ASbs4NxfBsStEGQYQYAeWOoKnTc50FeYz38O4KrOirtTFPNsJcyCiTE0o4cqu/OebSA5irrauV7SEDl/gfH54g3ZWusQbLt2uMnZYtkd2+Ka3T9XM0QfQW/vYl3eJtdQj89TqzLzyP0AgvAyIgeG3RMH8ojqCh3YKY0FTv/" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy2TGBjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE3MzQ1OVoXDTM0MTIxNTE3MzYzOVowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKPPaqby4X/QyHAm9iQG8SLUS0iLz/zQmYTHQBMUx8q4TOQdu2UOjGe3SUG5zLu0Ce8ogpbA9V5wze1pY9O7GNgLN5WN7/ZIzgwXgA2JT7jdZu/31xR196n0U/j8FUmgITMpJqr1qfOlE4O7e6zY1QZ1zz1yLZkSyBfo2VDFkHbsDc56+iYeeGXSMBMuIb19W6TcNJz5eCl4jotnpb4Pq5OwYRcsBsEPx1AhpK3KtF0v2jTstQoZqg27avNe5mKVXrXgg3vMT9zD62f7Ni4hLeCKdXDaNHWPmuCcDs8C+NodxmkfvNIit6WTaPjK9CgTmPRl73VfzL3Dxy0O1bo1BkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAggzxmYvHqUaCPLxxSidLQMgpu1pTozg3rTq8dcxhcHINI//A/z7qQyDA/QQN5cuSpYvdt2MRWoNop+uRNKqSr3C8aRErbY0j4acl7yG/ghNfQUZ9KxDBxKrd0HLFUibdZobg10+Ih/qXo3Mi2VtkqyZQRl/iy0O3ITgqb7YJUEx5tuEWyGbn+SerFvqZNcmsLziOJefm1n4uqroHgIfmgY6Deh+wZK0DwO3WZ6ThjhMp5GFi1oNeZ9xoExNEXrYp07b2xTQFF57oypc7prf733lqGjPRLfoVJP6qcsjvAlOA7f8TG9sKwGuRsPfadYY9PxmdHxl2k7PHDJeDhA7VdQ==" + ], "priority": ["100"] } }, @@ -1687,6 +1730,12 @@ "providerId": "rsa-enc-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAxoEvnv+YHCqUWANGuku5QYscAZyUE0WHSlcAzZ0bQugPow63piQsuxPz0cpPIuLab6adssXUqKEFheT1H0BqtmT9L/7iOKB6MRuInN4aRzzTH9q02TKPkcpSAzAHTGcsJBMMawlbnIdMu5+mevMPxqeVVxvrnKG27S8H3W5jqIkQw8bo646Hr3l5Dxq/jY7slcSXXXe4ZdefeCvnSqea+fy5c+r/r546nX4FTGiklu6KLQaDc9SfGccrZDmljY7DX1kHrmvIdLShcuukTHc0hi2qbgMcUte/7/svSJLUWOZObKxetd4y1OA49v36xrMqGhwGDdwrWf0VuMBN8eHOCQIDAQABAoIBABz/hUXnFRZURWHKxLvKpnBZPTOiZzfzfxfl4tOmq54CtDoVQyXNq2J+6oOPWC/X+ky3hy+1BQ5x9hJrx+qTU04m2EfOe8da8M7DX28kZlauyjF2loG+MvP7ctn4BluWcip+RTZOYn2DfxBPpRcunR409V+JesoMY7fSwtrfA/Gm0PrXgBK7OuE0nxqFFWnsLOc+HxZECS5r0n1MHEBHe774HkqGcK91j8S+QU+/diTnK+N/ClnKWnabMK8bUO5wAUuKwf2deYkGP91pCEJlVnVZyaXshEM+uxTuMRUlq9h1QAIUatvdQwfOKqZ9XvmTVC8b79qLwmezjoDxNCKbaMMCgYEA71WDpMnA2uS2wCJ/MVwzWGSBDjfeKUPRy33BeUfwLGp4Dro+S1sTrLHgi1HGmvmC8ReZrifUlUHUi3ZHauR6vbNsEoSQ3hplO013kj12EfcBpvKYFg1ODCwevb/JtBTWbDG1P+E9DGiF/2u0aicoJoPolNeNVzgO6YK1OI/S/LMCgYEA1FPTqFPulXxcOK12LgYap8typqJ7zu4fByr42010yrKM+LLNA3bT/i/oRkKc7J1ztKSqlVckADWgK4Y27lI4j1tSgTOxFzwxnTZOeF7ZwGSxq9iy9A84nDiW+m6Hj5RDyBjTSoP2Qqv6d5kTUx+pczZvOVTWRlIEnFETbbxOoFMCgYEA0r1etHx+V4AqtxXpH6KLB5s/1DA3a+hu1BrAgLVqcwGxA27VKW9h7J+YE7UHBzELLpVUWfhyhJa5u6+DhUj4Fw/k6o1WLmvZlZVJ4zhBPeJczw8wAcLnZWp4CybUScBLamt+qGgBZGqpCtZgv1QJU5i09FK0/wa6grz4K3zhEGcCgYAlnGe8xIlZr3rCi2+IvYoROQepHtUhlaqnYWRNrI3IrhIsp7eLKoxo1WGmuHwFqepqEFUrORFmfBlQPGkUlDnyovGdc2OmQwJi39DMn7igzPVwBGXGt7+GZLvRxqx6sX/EPSmIZJHFw6MNdm8m5U/l2bmgBTgjormwWug/IwEmgwKBgEouISIuXsjGxeLmhrOXHKXb6IfKglNJeBM6lTQ6MLaVOso7KdelIntwZNtZwMIi3hlwaUb1X1QmztFbnrvnPhWwJR4ZgMEWanRHthtm0SHzg8EHKT40S91oKabsgHk3wpOvq/iWs+k8qWN4HYp6UO603uLMOfxPYJCFxRtg2TsJ" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy2TG/jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE3MzQ1OVoXDTM0MTIxNTE3MzYzOVowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMaBL57/mBwqlFgDRrpLuUGLHAGclBNFh0pXAM2dG0LoD6MOt6YkLLsT89HKTyLi2m+mnbLF1KihBYXk9R9AarZk/S/+4jigejEbiJzeGkc80x/atNkyj5HKUgMwB0xnLCQTDGsJW5yHTLufpnrzD8anlVcb65yhtu0vB91uY6iJEMPG6OuOh695eQ8av42O7JXEl113uGXXn3gr50qnmvn8uXPq/6+eOp1+BUxopJbuii0Gg3PUnxnHK2Q5pY2Ow19ZB65ryHS0oXLrpEx3NIYtqm4DHFLXv+/7L0iS1FjmTmysXrXeMtTgOPb9+sazKhocBg3cK1n9FbjATfHhzgkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdUIlJ91E0UkFS45AByjFufRnQbAi1smnHkC3WSN39bhcFT7Hgip97qtABODR58zVHSTS0XcMiL4mMObH3Vyz9J3gmwWZnbokAuo9tYeyrhPh/gqXv3LGtGhTpWlUJ7JEJxH7RVI4UZZyG6Y6FR+3zwiZ0j1p3QsZclfcNmacoi/Ano+4TfloOnY4k8yP7G6LWUTJHpcRNWVVozM3RwekYgpJRAtXDoYfm9p2hRQ090e7NvbblSuVQ/FXhUn4g0wz91WdCWlwXZfvNaRjbynPCHejJpszqiyjPkx3aRKTWqer0ZocKNmY8+RO27XIsXmwOYcjdpX2TCFDv6O+VLfNdw==" + ], "priority": ["100"], "algorithm": ["RSA-OAEP"] } @@ -1697,6 +1746,8 @@ "providerId": "aes-generated", "subComponents": {}, "config": { + "kid": ["95db7eb8-b57b-475e-90cd-58841a9388d3"], + "secret": ["dp6bv53YrC2PZuJCxa3aNA"], "priority": ["100"] } }, @@ -1706,6 +1757,10 @@ "providerId": "hmac-generated", "subComponents": {}, "config": { + "kid": ["d0254883-059e-4fdd-bf03-704c76650aab"], + "secret": [ + "bcW7E4rcbgSKZIQysWOSuhezRGYs5Kzmp3ZESthdTUMyFivK8RbBAdBE4PhFPk5B9TuByDO2RWvd8F7F5YhGJitf6cfYB1BfDuAk-2iBAtdZA98g7a2h4jpwzh-GIgtoRbGbH9qnquUn52f5qteo34g5WifKE2bWjOELza9FrTo" + ], "priority": ["100"], "algorithm": ["HS512"] } diff --git a/src/bin/start-keycloak/myrealm-realm-26.json b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-26.json similarity index 92% rename from src/bin/start-keycloak/myrealm-realm-26.json rename to src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-26.json index 25169a75..1ae0dcd7 100644 --- a/src/bin/start-keycloak/myrealm-realm-26.json +++ b/src/bin/start-keycloak/realmConfig/defaultConfig/realm-kc-26.json @@ -563,6 +563,40 @@ "disableableCredentialTypes": [], "requiredActions": [], "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "manage-users", + "create-client", + "view-users", + "view-realm", + "query-realms", + "impersonation", + "view-events", + "realm-admin", + "manage-authorization", + "view-authorization", + "manage-events", + "manage-clients", + "query-users", + "query-groups", + "manage-realm", + "query-clients", + "manage-identity-providers", + "view-identity-providers", + "view-clients" + ], + "broker": ["read-token"], + "account": [ + "delete-account", + "view-applications", + "manage-account", + "view-consent", + "view-groups", + "view-profile", + "manage-account-links", + "manage-consent" + ] + }, "notBefore": 0, "groups": [] } @@ -638,7 +672,11 @@ "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["*"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/realms/myrealm/account/*" + ], "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, @@ -805,8 +843,7 @@ "realm_client": "false", "oidc.ciba.grant.enabled": "false", "backchannel.logout.session.required": "true", - "login_theme": "keycloakify-starter", - "post.logout.redirect.uris": "https://my-theme.keycloakify.dev/*##http://localhost*##http://127.0.0.1*", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "display.on.consent.screen": "false", "backchannel.logout.revoke.offline.tokens": "false" @@ -894,14 +931,20 @@ "id": "fce8a109-6f32-4814-9a20-2ff2435d2da6", "clientId": "security-admin-console", "name": "${client_security-admin-console}", + "description": "", "rootUrl": "${authAdminUrl}", + "adminUrl": "", "baseUrl": "/admin/myrealm/console/", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", - "redirectUris": ["/admin/myrealm/console/*"], - "webOrigins": ["+"], + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], "notBefore": 0, "bearerOnly": false, "consentRequired": false, @@ -914,9 +957,14 @@ "protocol": "openid-connect", "attributes": { "realm_client": "false", + "oidc.ciba.grant.enabled": "false", "client.use.lightweight.access.token.enabled": "true", + "backchannel.logout.session.required": "true", "post.logout.redirect.uris": "+", - "pkce.code.challenge.method": "S256" + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "pkce.code.challenge.method": "S256", + "backchannel.logout.revoke.offline.tokens": "false" }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, @@ -1561,11 +1609,11 @@ }, "smtpServer": {}, "loginTheme": "keycloakify-starter", - "accountTheme": "keycloakify-starter", + "accountTheme": "", "adminTheme": "", "emailTheme": "", "eventsEnabled": false, - "eventsListeners": ["jboss-logging"], + "eventsListeners": ["keycloakify-logging", "jboss-logging"], "enabledEventTypes": [], "adminEventsEnabled": false, "adminEventsDetailsEnabled": false, @@ -1591,14 +1639,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "saml-user-attribute-mapper", - "oidc-sha256-pairwise-sub-mapper", - "oidc-full-name-mapper", - "saml-user-property-mapper", - "oidc-address-mapper", "oidc-usermodel-property-mapper", - "saml-role-list-mapper", - "oidc-usermodel-attribute-mapper" + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "oidc-usermodel-attribute-mapper", + "saml-role-list-mapper" ] } }, @@ -1628,14 +1676,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-usermodel-attribute-mapper", - "oidc-full-name-mapper", - "oidc-usermodel-property-mapper", - "oidc-address-mapper", - "saml-user-property-mapper", - "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper", - "oidc-sha256-pairwise-sub-mapper" + "oidc-usermodel-property-mapper", + "oidc-full-name-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper" ] } }, @@ -1688,6 +1736,12 @@ "providerId": "rsa-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEoQIBAAKCAQEAxTFMvRiNiQjY9zajvLsah6Vy4pn8U7smsnBcHS9SkLJ1j9O8+90B90tIZk4IqEE4gdJA/mbbeUnou1vWuc0k69diQMFelzdIaDqJaFFeOS+J1DoApjThjGIz7FIgmGi6qoN8xnrPVD/6oMYAuxTvQaJH7mENiIG0198dvaufV1mFPg+krTsh7Womo2CJeZmNuAXv7RDQYxwPYDCFZLbppez48D7+2D+1V6Stk6Xwz8IDQZvljxDF6W2P9rhPWV1C5tcJpC/9RPyGDo+ke8UN3fM6X7YOgpbMztVrg8J0aTqPXZ7dt6QFUqVOufo+5wYL2jCafpYNV8cmaGlY+Q3d5QIDAQABAoH/DIPcaZaJTLG4FeUKGOaT40nesEiINRY99aeIkp+hdGj1EgTEn49TyLENGnhrrdbIvOJDeD6Z6dbpJBDvfFevxa589EnVKaGaaW5U91FDyVYH2YPU411dAeOp0z1xwxXzlJqX3h42ZJnvLAp/2l1Xo64vGCoTJtYlppAvpe2MjANxPNObAc65Phdi/sConAlwMeBylWXJ574uryFrJ64W/sUuIUMSunGGz0db4Y1hfkX9U2YnxB3DdXCBH09jQJyKDSj6feNXR87+1KhqcFMd5DUiGSAOqRBzuBMsDf1QDJd8A/DDlK7e/PA1Yk/Dii4hsf+LCeOdmhlifuyROqJBAoGBAOEm4gLvaBWwnUhmr4sW8xywIhGGbU+MX6vm/KkGtScres7pPhmfy6ARUzCxxyBqIE+nhCRNBpOEPhP7dv8naJhZZ4fRvNzuXpUMT2X3bc5yNzdhaOxBJl95YQbrYUHhjcIw2kdXnIkpdbB/RqmY0F5BUTYECrd0tKWbjuL5RIRNAoGBAOA1wTXrYyVorouxV+mGNb62Py+utHJQKSa5cxF9nbbwWJd+FdreiBOJddjATmH8ovKjueQFVqK7koDveOb+pgRY2bpT88/NW8UF6a2wMiI0p6pxrR+hgzas480YiOCWr6XlsprqsSKBbEu4W97GicleZ6P5Iso/gBr9aHj9EWv5AoGAYhRzHj42RESUr4Zz8A5GR3f+z02U7rNCtfrAk80lOvP44ou+jqEKrib961d2XAt/GdPqf3nCZJ6WAFRp6Qq8yKkhrYvTTxbTwvAC4nNftTASF6DqeQiEc9DHUKFW08Ey5KYtYCitOx8BcqpvGNBF7NldTD+Ef5hqXT4fh4Z4r30CgYEAy2OYGMymTRowNKK06C+Kc62plhy6rnRPUESswLIeLwTKqOqE8t4pvOdWk0CoGjVusAOcLuA03jyfwvz5xTo96fWb1W4w31IgLJOXjqsmX2c6reCfNvFyMVgW8keOa4XmYu0C34uFEpMrZWkhVe7usVBFXjczuxptoI4+hnqzoikCgYBICBVR9Z7n2LvmWH19/Nnns8dsMn5peL7H6Mey76Lo9RMEMp4qhiJTqVZzWgxEyVjr0KFCHmdmwkTOm6A1yYmkqqXDdiJ9v4J4fXe0lRAoUoYPTOWynrCyd6uqq+3zlzTKW8jY9luywHq6msn07D636PvveeZ93DNCcO8Whw36rQ==" + ], + "certificate": [ + "MIICnTCCAYUCBgGTulJBzTANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxMjEwMDExM1oXDTM0MTIxMjEwMDI1M1owEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMUxTL0YjYkI2Pc2o7y7GoelcuKZ/FO7JrJwXB0vUpCydY/TvPvdAfdLSGZOCKhBOIHSQP5m23lJ6Ltb1rnNJOvXYkDBXpc3SGg6iWhRXjkvidQ6AKY04YxiM+xSIJhouqqDfMZ6z1Q/+qDGALsU70GiR+5hDYiBtNffHb2rn1dZhT4PpK07Ie1qJqNgiXmZjbgF7+0Q0GMcD2AwhWS26aXs+PA+/tg/tVekrZOl8M/CA0Gb5Y8Qxeltj/a4T1ldQubXCaQv/UT8hg6PpHvFDd3zOl+2DoKWzM7Va4PCdGk6j12e3bekBVKlTrn6PucGC9owmn6WDVfHJmhpWPkN3eUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEATZXyOluloTj6Q/Mv0JjstfdvPQbzGFzWtULB1ttOJqQVL+IJoF8V79HIvfP9U5OYaOdYk9dDurQcd2hXvEtX+zQlLYGniRfJlFI7d+m6MDXa7/g1r+OmcvaiXX7O3ol7eJdymPKS79+PSWFsHk0JjfgRJ11jajOscYPoQ+IvxXgwuy6v7VHigsLnGnmmo+KWiKO6Cna6eilm6/awYXaoym4ky9S4T5+WaJwd/tH/n5VY77zyXaXfANd1hU/+4Ux/eaGVnoMAM4ud2emd4qCN2tQQ3HusIVl+5V+S8Uq1y54mBpXv6CAODDGDJeFa+cGPJUSLdv/ZT2F8yfDlDc4J6g==" + ], "priority": ["100"] } }, @@ -1697,6 +1751,12 @@ "providerId": "rsa-enc-generated", "subComponents": {}, "config": { + "privateKey": [ + "MIIEogIBAAKCAQEAungL4osLyP8bE6MSKj8ZMJTG8WBh3K2/xB5BJYCYc7P1CIORZI9o/vKQx1QnP+CXkIKnnR2kzIzC0rnTqlIOkaZfhmSn50jG5vNBS9qPT+WU7Ue3qKxuWJFwcaFU5SEJawJHqnDPK+pktkkxkudeMHz6iaKPs+wKcbfrRJ6+3a3FqQQdHEQg4IjVU8pBZmag1c7JHayiM56OT5y6jmE5JvY60959iPrZPXSTMU3hNoiVwdyK6QwdK+/0wrO681VhIP+u2pe92nQ+hsgMSSQJegLx1UsEEyU87syblG+p3zAKSS+kt2nviV/a2cYiiME0LdlQ3lnKsQ4t1Y6yZBiS2QIDAQABAoIBABhozI18TC+kjWPVrfQPzHlakGxahJUBvZ+rojWJjutefE4AAxFZ4JG3KRKexoCLIuwM3monzkHkj0BMiRO7qCKS1+Bc3snc8gSbhUmrs6Tu1b7162nOIKfBainFx7oyx+vVIZKDL+t8xHBERpQHa4IHajiIKi2QUZGvVMHn0e5srkPK0eSMjb5Z5j61aFb8InQzs7tczr99ke4VavOPT1gmRWGnbTavUbw/zIQ9sxAuMiD2v0nrGlOLZrMhaqzsT6PjIWVCSZrWex1pin9gA4XwGZ39E7+zFWgg+2OX0dEvehVDluAQR0K4PBUknuL1LFFW8dpvCrUSTmGGQOSVuB0CgYEA+bQjbjTNiMTEfoxx/WvVDgtLRL/x9RVyeYTPia2TGNBwpEcU64lLMOwUt5X/QuGXayPr0EGAxMA8kwq/E8Wj2t9+SuqkGK9SIwvghi2fOh0KWghuQbKYMogG5hsJAI8+/mBIOJJ8pyh0RX58vaTlYctbThO22aVahhZQ2weaW58CgYEAvyu4vIe44/7F19Hjh2BW+9lHsHA2zwHvC5T1kFaEdBYEwGsLMW6leCsiEMfpc2Uq3k9+buZgVpTE5APs9cSJX1aUXEG5QHQmYDxAAMiTyvpj0o2cKbDi1A5QZCRo23lC+uDyR7g2zLDJuHek0uyCtd83hbgyxIVFUnfvI9EmfocCgYBtpcZxHEqspgrKrw1XBMTXl+oDVG4A+tv7tHAVutx+5vivim8LRox3/RLT0s/2JG2DJJDmL/1FaEyxHOTu37il4cHpT8Oi+0mMDikXgm0K7bmf81fHDY97kPPGk1SOpFg7BzhvbxPBqyfzZCmOdRwsp0l+rXV7ePqZKq9ynpIPbQKBgFO/LZC5zE9k/vrK4egeVjzCNNugbQJGkJf8S49Nt3y7YJ2Cx0aCeE6qZqP/T8/Tk/IL1RF0LuP/DDnvVlFcJen0Hc5EpIkN2Pnzqv4s4EHdavmEO9MvwE6xbppQMPdkqekJvlmY47jMAbKkBzq3jZNrFAGqbeMVlwbHr6V7LGflAoGANFbzOnUMJwUfIdoI9uEG2QOTAcBb7vzt9MurO67wiTexOYadOSlcV1lQX3RKR9mCFJwy4kud0TN0gD++Ggl10eNB6f8JOF95e5+tWrtz88xZ5EalBOMfh+ATdKq8Q9MBSWZvO9bizhW1dhZZds/QmHgEItdwsTKDAq1PEiXhD0c=" + ], + "certificate": [ + "MIICnTCCAYUCBgGTulJDCDANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxMjEwMDExM1oXDTM0MTIxMjEwMDI1M1owEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALp4C+KLC8j/GxOjEio/GTCUxvFgYdytv8QeQSWAmHOz9QiDkWSPaP7ykMdUJz/gl5CCp50dpMyMwtK506pSDpGmX4Zkp+dIxubzQUvaj0/llO1Ht6isbliRcHGhVOUhCWsCR6pwzyvqZLZJMZLnXjB8+omij7PsCnG360Sevt2txakEHRxEIOCI1VPKQWZmoNXOyR2sojOejk+cuo5hOSb2OtPefYj62T10kzFN4TaIlcHciukMHSvv9MKzuvNVYSD/rtqXvdp0PobIDEkkCXoC8dVLBBMlPO7Mm5Rvqd8wCkkvpLdp74lf2tnGIojBNC3ZUN5ZyrEOLdWOsmQYktkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPhPdLFcXdQT4k06oXB06ZSJ8AkZNXLvQFWCHXI34OmrS2yTse+dLqrqehnC3kPwxElVmawoUVc1sbsk7fUnspfM+Xw20PaABZu4MO2m5TB98f1hEkezP9fSqgPeuWJgTL8ZW5kkZyiD3IaZoqyxzYXaFxKHhU455g+k2+DO+N6FreVKcYz12Q5EMaxZ6U1neZAo3vicNxM3/TA5V8sPK8+oKvon7v5OyjpOH0goJo9v/klKeUk36h4u2h1S67IhVSU7tfzVFYrpns1JhrwGZ2xavVqEoqX8zFp3GKz3yVXkwHRHlrzYkZoGn21rm5boXIP3wEB7yXZbXWTiUko/IFw==" + ], "priority": ["100"], "algorithm": ["RSA-OAEP"] } @@ -1707,6 +1767,8 @@ "providerId": "aes-generated", "subComponents": {}, "config": { + "kid": ["c36222c6-6a43-4d32-9d44-d5d355e5cabd"], + "secret": ["rzL4qUQ7wTEkZDbgt595VA"], "priority": ["100"] } }, @@ -1716,6 +1778,10 @@ "providerId": "hmac-generated", "subComponents": {}, "config": { + "kid": ["06532a54-c310-41c1-829c-58776ce2ab4a"], + "secret": [ + "9v1ZjFhEFH6UpY6ncFkaCbqJYHMyI4tA0cvx4GuQ5KtMXYbimitSSVDqxIKwa-gBC_8bY2O4FQfpmp1Qn1-L4fFmPFfIF3ZKsO16263BwpADo_FNSBTte8Le4gJLylqFULdsn3ye17FHyq5Jjms_OTt3opzcDLNduCuK22GBBsU" + ], "priority": ["100"], "algorithm": ["HS512"] } @@ -2385,7 +2451,7 @@ "clientSessionMaxLifespan": "0", "organizationsEnabled": "false" }, - "keycloakVersion": "26.0.6", + "keycloakVersion": "26.0.7", "userManagedAccessAllowed": false, "organizationsEnabled": false, "clientProfiles": { diff --git a/src/bin/start-keycloak/realmConfig/dumpContainerConfig.ts b/src/bin/start-keycloak/realmConfig/dumpContainerConfig.ts new file mode 100644 index 00000000..59e68d1b --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/dumpContainerConfig.ts @@ -0,0 +1,147 @@ +import { CONTAINER_NAME } from "../../shared/constants"; +import child_process from "child_process"; +import { join as pathJoin } from "path"; +import chalk from "chalk"; +import { Deferred } from "evt/tools/Deferred"; +import { assert, is } from "tsafe/assert"; +import type { BuildContext } from "../../shared/buildContext"; +import { type ParsedRealmJson, readRealmJsonFile } from "./ParsedRealmJson"; + +export type BuildContextLike = { + cacheDirPath: string; +}; + +assert(); + +export async function dumpContainerConfig(params: { + realmName: string; + keycloakMajorVersionNumber: number; + buildContext: BuildContextLike; +}): Promise { + const { realmName, keycloakMajorVersionNumber, buildContext } = params; + + { + // https://github.com/keycloak/keycloak/issues/33800 + const doesUseLockedH2Database = keycloakMajorVersionNumber >= 25; + + if (doesUseLockedH2Database) { + child_process.execSync( + `docker exec ${CONTAINER_NAME} sh -c "cp -rp /opt/keycloak/data/h2 /tmp"` + ); + } + + const dCompleted = new Deferred(); + + const child = child_process.spawn( + "docker", + [ + ...["exec", CONTAINER_NAME], + ...["/opt/keycloak/bin/kc.sh", "export"], + ...["--dir", "/tmp"], + ...["--realm", realmName], + ...["--users", "realm_file"], + ...(!doesUseLockedH2Database + ? [] + : [ + ...["--db", "dev-file"], + ...[ + "--db-url", + "'jdbc:h2:file:/tmp/h2/keycloakdb;NON_KEYWORDS=VALUE'" + ] + ]) + ], + { shell: true } + ); + + let output = ""; + + const onExit = (code: number | null) => { + dCompleted.reject(new Error(`Exited with code ${code}`)); + }; + + child.once("exit", onExit); + + child.stdout.on("data", data => { + const outputStr = data.toString("utf8"); + + if (outputStr.includes("Export finished successfully")) { + child.removeListener("exit", onExit); + + // NOTE: On older Keycloak versions the process keeps running after the export is done. + const timer = setTimeout(() => { + child.removeListener("exit", onExit2); + child.kill(); + dCompleted.resolve(); + }, 1500); + + const onExit2 = () => { + clearTimeout(timer); + dCompleted.resolve(); + }; + + child.once("exit", onExit2); + } + + output += outputStr; + }); + + child.stderr.on("data", data => (output += chalk.red(data.toString("utf8")))); + + try { + await dCompleted.pr; + } catch (error) { + assert(is(error)); + + console.log(chalk.red(error.message)); + + console.log(output); + + process.exit(1); + } + + if (doesUseLockedH2Database) { + const dCompleted = new Deferred(); + + child_process.exec( + `docker exec ${CONTAINER_NAME} sh -c "rm -rf /tmp/h2"`, + error => { + if (error !== null) { + dCompleted.reject(error); + return; + } + + dCompleted.resolve(); + } + ); + + await dCompleted.pr; + } + } + + const targetRealmConfigJsonFilePath_tmp = pathJoin( + buildContext.cacheDirPath, + "realm.json" + ); + + { + const dCompleted = new Deferred(); + + child_process.exec( + `docker cp ${CONTAINER_NAME}:/tmp/${realmName}-realm.json ${targetRealmConfigJsonFilePath_tmp}`, + error => { + if (error !== null) { + dCompleted.reject(error); + return; + } + + dCompleted.resolve(); + } + ); + + await dCompleted.pr; + } + + return readRealmJsonFile({ + realmJsonFilePath: targetRealmConfigJsonFilePath_tmp + }); +} diff --git a/src/bin/start-keycloak/realmConfig/index.ts b/src/bin/start-keycloak/realmConfig/index.ts new file mode 100644 index 00000000..09a46a68 --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/index.ts @@ -0,0 +1 @@ +export * from "./realmConfig"; diff --git a/src/bin/start-keycloak/realmConfig/prepareRealmConfig.ts b/src/bin/start-keycloak/realmConfig/prepareRealmConfig.ts new file mode 100644 index 00000000..e97bf3ca --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/prepareRealmConfig.ts @@ -0,0 +1,302 @@ +import { assert } from "tsafe/assert"; +import type { ParsedRealmJson } from "./ParsedRealmJson"; +import { getDefaultConfig } from "./defaultConfig"; +import type { BuildContext } from "../../shared/buildContext"; +import { objectKeys } from "tsafe/objectKeys"; +import { TEST_APP_URL } from "../../shared/constants"; +import { sameFactory } from "evt/tools/inDepth/same"; + +export type BuildContextLike = { + themeNames: BuildContext["themeNames"]; + implementedThemeTypes: BuildContext["implementedThemeTypes"]; +}; + +assert; + +export function prepareRealmConfig(params: { + parsedRealmJson: ParsedRealmJson; + keycloakMajorVersionNumber: number; + buildContext: BuildContextLike; +}): { + realmName: string; + clientName: string; + username: string; +} { + const { parsedRealmJson, keycloakMajorVersionNumber, buildContext } = params; + + const { username } = addOrEditTestUser({ + parsedRealmJson, + keycloakMajorVersionNumber + }); + + const { clientId } = addOrEditClient({ + parsedRealmJson, + keycloakMajorVersionNumber + }); + + editAccountConsoleAndSecurityAdminConsole({ parsedRealmJson }); + + enableCustomThemes({ + parsedRealmJson, + themeName: buildContext.themeNames[0], + implementedThemeTypes: buildContext.implementedThemeTypes + }); + + enable_custom_events_listeners: { + const name = "keycloakify-logging"; + + if (parsedRealmJson.eventsListeners.includes(name)) { + break enable_custom_events_listeners; + } + + parsedRealmJson.eventsListeners.push(name); + + parsedRealmJson.eventsListeners.sort(); + } + + return { + realmName: parsedRealmJson.realm, + clientName: clientId, + username + }; +} + +function enableCustomThemes(params: { + parsedRealmJson: ParsedRealmJson; + themeName: string; + implementedThemeTypes: BuildContextLike["implementedThemeTypes"]; +}) { + const { parsedRealmJson, themeName, implementedThemeTypes } = params; + + for (const themeType of objectKeys(implementedThemeTypes)) { + if (!implementedThemeTypes[themeType].isImplemented) { + continue; + } + + parsedRealmJson[`${themeType}Theme` as const] = themeName; + } +} + +function addOrEditTestUser(params: { + parsedRealmJson: ParsedRealmJson; + keycloakMajorVersionNumber: number; +}): { username: string } { + const { parsedRealmJson, keycloakMajorVersionNumber } = params; + + const parsedRealmJson_default = getDefaultConfig({ keycloakMajorVersionNumber }); + + const [defaultUser_default] = parsedRealmJson_default.users; + + assert(defaultUser_default !== undefined); + + const defaultUser_preexisting = parsedRealmJson.users.find( + user => user.username === defaultUser_default.username + ); + + const newUser = structuredClone( + defaultUser_preexisting ?? + (() => { + const firstUser = parsedRealmJson.users[0]; + + if (firstUser === undefined) { + return undefined; + } + + const firstUserCopy = structuredClone(firstUser); + + firstUserCopy.id = defaultUser_default.id; + + return firstUserCopy; + })() ?? + defaultUser_default + ); + + newUser.username = defaultUser_default.username; + newUser.email = defaultUser_default.email; + + delete_existing_password_credential_if_any: { + const i = newUser.credentials.findIndex( + credential => credential.type === "password" + ); + + if (i === -1) { + break delete_existing_password_credential_if_any; + } + + newUser.credentials.splice(i, 1); + } + + { + const credential = defaultUser_default.credentials.find( + credential => credential.type === "password" + ); + + assert(credential !== undefined); + + newUser.credentials.push(credential); + } + + { + const nameByClientId = Object.fromEntries( + parsedRealmJson.clients.map(client => [client.id, client.clientId] as const) + ); + + const newClientRoles: NonNullable< + ParsedRealmJson["users"][number]["clientRoles"] + > = {}; + + for (const clientRole of Object.values(parsedRealmJson.roles.client).flat()) { + const clientName = nameByClientId[clientRole.containerId]; + + assert(clientName !== undefined); + + (newClientRoles[clientName] ??= []).push(clientRole.name); + } + + const { same: sameSet } = sameFactory({ + takeIntoAccountArraysOrdering: false + }); + + for (const [clientName, roles] of Object.entries(newClientRoles)) { + keep_previous_ordering_if_possible: { + const roles_previous = newUser.clientRoles?.[clientName]; + + if (roles_previous === undefined) { + break keep_previous_ordering_if_possible; + } + + if (!sameSet(roles_previous, roles)) { + break keep_previous_ordering_if_possible; + } + + continue; + } + + (newUser.clientRoles ??= {})[clientName] = roles; + } + } + + if (defaultUser_preexisting === undefined) { + parsedRealmJson.users.push(newUser); + } else { + const i = parsedRealmJson.users.indexOf(defaultUser_preexisting); + assert(i !== -1); + parsedRealmJson.users[i] = newUser; + } + + return { username: newUser.username }; +} + +function addOrEditClient(params: { + parsedRealmJson: ParsedRealmJson; + keycloakMajorVersionNumber: number; +}): { clientId: string } { + const { parsedRealmJson, keycloakMajorVersionNumber } = params; + + const parsedRealmJson_default = getDefaultConfig({ keycloakMajorVersionNumber }); + + const testClient_default = (() => { + const clients = parsedRealmJson_default.clients.filter(client => { + return JSON.stringify(client).includes(TEST_APP_URL); + }); + + assert(clients.length === 1); + + return clients[0]; + })(); + + const clientIds_builtIn = parsedRealmJson_default.clients + .map(client => client.clientId) + .filter(clientId => clientId !== testClient_default.clientId); + + const testClient_preexisting = (() => { + const clients = parsedRealmJson.clients + .filter(client => !clientIds_builtIn.includes(client.clientId)) + .filter(client => client.protocol === "openid-connect"); + + { + const client = clients.find( + client => client.clientId === testClient_default.clientId + ); + + if (client !== undefined) { + return client; + } + } + + { + const client = clients.find( + client => + client.redirectUris?.find(redirectUri => + redirectUri.startsWith(TEST_APP_URL) + ) !== undefined + ); + + if (client !== undefined) { + return client; + } + } + + const [client] = clients; + + if (client === undefined) { + return undefined; + } + + return client; + })(); + + let testClient: typeof testClient_default; + + if (testClient_preexisting !== undefined) { + testClient = testClient_preexisting; + } else { + testClient = structuredClone(testClient_default); + delete testClient.protocolMappers; + parsedRealmJson.clients.push(testClient); + } + + testClient.redirectUris = [ + `${TEST_APP_URL}/*`, + "http://localhost*", + "http://127.0.0.1*" + ] + .sort() + .reverse(); + + (testClient.attributes ??= {})["post.logout.redirect.uris"] = "+"; + + testClient.webOrigins = ["*"]; + + return { clientId: testClient.clientId }; +} + +function editAccountConsoleAndSecurityAdminConsole(params: { + parsedRealmJson: ParsedRealmJson; +}) { + const { parsedRealmJson } = params; + + for (const clientId of ["account-console", "security-admin-console"]) { + const client = parsedRealmJson.clients.find( + client => client.clientId === clientId + ); + + assert(client !== undefined); + + { + const arr = (client.redirectUris ??= []); + + for (const value of ["http://localhost*", "http://127.0.0.1*"]) { + if (!arr.includes(value)) { + arr.push(value); + } + } + + client.redirectUris?.sort().reverse(); + } + + (client.attributes ??= {})["post.logout.redirect.uris"] = "+"; + + client.webOrigins = ["*"]; + } +} diff --git a/src/bin/start-keycloak/realmConfig/realmConfig.ts b/src/bin/start-keycloak/realmConfig/realmConfig.ts new file mode 100644 index 00000000..930df9a2 --- /dev/null +++ b/src/bin/start-keycloak/realmConfig/realmConfig.ts @@ -0,0 +1,151 @@ +import type { BuildContext } from "../../shared/buildContext"; +import { assert } from "tsafe/assert"; +import { runPrettier, getIsPrettierAvailable } from "../../tools/runPrettier"; +import { getDefaultConfig } from "./defaultConfig"; +import { + prepareRealmConfig, + type BuildContextLike as BuildContextLike_prepareRealmConfig +} from "./prepareRealmConfig"; +import * as fs from "fs"; +import { + join as pathJoin, + dirname as pathDirname, + relative as pathRelative, + sep as pathSep +} from "path"; +import { existsAsync } from "../../tools/fs.existsAsync"; +import { readRealmJsonFile, type ParsedRealmJson } from "./ParsedRealmJson"; +import { + dumpContainerConfig, + type BuildContextLike as BuildContextLike_dumpContainerConfig +} from "./dumpContainerConfig"; +import * as runExclusive from "run-exclusive"; +import { waitForDebounceFactory } from "powerhooks/tools/waitForDebounce"; +import chalk from "chalk"; + +export type BuildContextLike = BuildContextLike_dumpContainerConfig & + BuildContextLike_prepareRealmConfig & { + projectDirPath: string; + }; + +assert; + +export async function getRealmConfig(params: { + keycloakMajorVersionNumber: number; + realmJsonFilePath_userProvided: string | undefined; + buildContext: BuildContextLike; +}): Promise<{ + realmJsonFilePath: string; + clientName: string; + realmName: string; + username: string; + onRealmConfigChange: () => Promise; +}> { + const { keycloakMajorVersionNumber, realmJsonFilePath_userProvided, buildContext } = + params; + + const realmJsonFilePath = pathJoin( + buildContext.projectDirPath, + ".keycloakify", + `realm-kc-${keycloakMajorVersionNumber}.json` + ); + + const parsedRealmJson = await (async () => { + if (realmJsonFilePath_userProvided !== undefined) { + return readRealmJsonFile({ + realmJsonFilePath: realmJsonFilePath_userProvided + }); + } + + if (await existsAsync(realmJsonFilePath)) { + return readRealmJsonFile({ + realmJsonFilePath + }); + } + + return getDefaultConfig({ keycloakMajorVersionNumber }); + })(); + + const { clientName, realmName, username } = prepareRealmConfig({ + parsedRealmJson, + buildContext, + keycloakMajorVersionNumber + }); + + { + const dirPath = pathDirname(realmJsonFilePath); + + if (!(await existsAsync(dirPath))) { + fs.mkdirSync(dirPath, { recursive: true }); + } + } + + const writeRealmJsonFile = async (params: { parsedRealmJson: ParsedRealmJson }) => { + const { parsedRealmJson } = params; + + let sourceCode = JSON.stringify(parsedRealmJson, null, 2); + + if (await getIsPrettierAvailable()) { + sourceCode = await runPrettier({ + sourceCode, + filePath: realmJsonFilePath + }); + } + + fs.writeFileSync(realmJsonFilePath, sourceCode); + }; + + await writeRealmJsonFile({ parsedRealmJson }); + + const { onRealmConfigChange } = (() => { + const run = runExclusive.build(async () => { + const start = Date.now(); + + console.log( + chalk.grey(`Changes detected to the '${realmName}' config, backing up...`) + ); + + const parsedRealmJson = await dumpContainerConfig({ + buildContext, + realmName, + keycloakMajorVersionNumber + }); + + await writeRealmJsonFile({ parsedRealmJson }); + + console.log( + [ + chalk.grey( + `Save changed to \`.${pathSep}${pathRelative(buildContext.projectDirPath, realmJsonFilePath)}\`` + ), + chalk.grey( + `Next time you'll be running \`keycloakify start-keycloak\`, the realm '${realmName}' will be restored to this state.` + ), + chalk.green( + `✓ '${realmName}' config backed up completed in ${Date.now() - start}ms` + ) + ].join("\n") + ); + }); + + const { waitForDebounce } = waitForDebounceFactory({ + delay: 1_000 + }); + + async function onRealmConfigChange() { + await waitForDebounce(); + + run(); + } + + return { onRealmConfigChange }; + })(); + + return { + realmJsonFilePath, + clientName, + realmName, + username, + onRealmConfigChange + }; +} diff --git a/src/bin/start-keycloak/start-keycloak.ts b/src/bin/start-keycloak/start-keycloak.ts index 42326c93..a4b88a58 100644 --- a/src/bin/start-keycloak/start-keycloak.ts +++ b/src/bin/start-keycloak/start-keycloak.ts @@ -1,7 +1,11 @@ import type { BuildContext } from "../shared/buildContext"; import { exclude } from "tsafe/exclude"; -import { promptKeycloakVersion } from "../shared/promptKeycloakVersion"; -import { CONTAINER_NAME, KEYCLOAKIFY_SPA_DEV_SERVER_PORT } from "../shared/constants"; +import { + CONTAINER_NAME, + KEYCLOAKIFY_SPA_DEV_SERVER_PORT, + KEYCLOAKIFY_LOGIN_JAR_BASENAME, + TEST_APP_URL +} from "../shared/constants"; import { SemVer } from "../tools/SemVer"; import { assert, type Equals } from "tsafe/assert"; import * as fs from "fs"; @@ -9,8 +13,7 @@ import { join as pathJoin, relative as pathRelative, sep as pathSep, - basename as pathBasename, - dirname as pathDirname + basename as pathBasename } from "path"; import * as child_process from "child_process"; import chalk from "chalk"; @@ -28,6 +31,9 @@ import { existsAsync } from "../tools/fs.existsAsync"; import { rm } from "../tools/fs.rm"; import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive"; import { startViteDevServer } from "./startViteDevServer"; +import { getSupportedKeycloakMajorVersions } from "./realmConfig/defaultConfig"; +import { getSupportedDockerImageTags } from "./getSupportedDockerImageTags"; +import { getRealmConfig } from "./realmConfig"; export async function command(params: { buildContext: BuildContext; @@ -91,9 +97,32 @@ export async function command(params: { const { cliCommandOptions, buildContext } = params; + const availableTags = await getSupportedDockerImageTags({ + buildContext + }); + const { dockerImageTag } = await (async () => { if (cliCommandOptions.keycloakVersion !== undefined) { - return { dockerImageTag: cliCommandOptions.keycloakVersion }; + const cliCommandOptions_keycloakVersion = cliCommandOptions.keycloakVersion; + + const tag = availableTags.find(tag => + tag.startsWith(cliCommandOptions_keycloakVersion) + ); + + if (tag === undefined) { + console.log( + chalk.red( + [ + `We could not find a Keycloak Docker image for ${cliCommandOptions_keycloakVersion}`, + `Example of valid values: --keycloak-version 26, --keycloak-version 26.0.7` + ].join("\n") + ) + ); + + process.exit(1); + } + + return { dockerImageTag: tag }; } if (buildContext.startKeycloakOptions.dockerImage !== undefined) { @@ -108,50 +137,84 @@ export async function command(params: { "On which version of Keycloak do you want to test your theme?" ), chalk.gray( - "You can also explicitly provide the version with `npx keycloakify start-keycloak --keycloak-version 25.0.2` (or any other version)" + "You can also explicitly provide the version with `npx keycloakify start-keycloak --keycloak-version 26` (or any other version)" ) ].join("\n") ); - const { keycloakVersion } = await promptKeycloakVersion({ - startingFromMajor: 18, - excludeMajorVersions: [22], - doOmitPatch: true, - buildContext + const { value: tag } = await cliSelect({ + values: availableTags + }).catch(() => { + process.exit(-1); }); - console.log(`→ ${keycloakVersion}`); + console.log(`→ ${tag}`); - return { dockerImageTag: keycloakVersion }; + return { dockerImageTag: tag }; })(); const keycloakMajorVersionNumber = (() => { - if (buildContext.startKeycloakOptions.dockerImage === undefined) { - return SemVer.parse(dockerImageTag).major; - } - - const { tag } = buildContext.startKeycloakOptions.dockerImage; - - const [wrap] = [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28] + const [wrap] = getSupportedKeycloakMajorVersions() .map(majorVersionNumber => ({ majorVersionNumber, - index: tag.indexOf(`${majorVersionNumber}`) + index: dockerImageTag.indexOf(`${majorVersionNumber}`) })) .filter(({ index }) => index !== -1) .sort((a, b) => a.index - b.index); if (wrap === undefined) { - console.warn( - chalk.yellow( - `Could not determine the major Keycloak version number from the docker image tag ${tag}. Assuming 25` - ) - ); - return 25; + try { + const version = SemVer.parse(dockerImageTag); + + console.error( + chalk.yellow( + `Keycloak version ${version.major} is not supported, supported versions are ${getSupportedKeycloakMajorVersions().join(", ")}` + ) + ); + + process.exit(1); + } catch { + // NOTE: Latest version + const [n] = getSupportedKeycloakMajorVersions(); + + console.warn( + chalk.yellow( + `Could not determine the major Keycloak version number from the docker image tag ${dockerImageTag}. Assuming ${n}` + ) + ); + return n; + } } return wrap.majorVersionNumber; })(); + const { clientName, onRealmConfigChange, realmJsonFilePath, realmName, username } = + await getRealmConfig({ + keycloakMajorVersionNumber, + realmJsonFilePath_userProvided: await (async () => { + if (cliCommandOptions.realmJsonFilePath !== undefined) { + return getAbsoluteAndInOsFormatPath({ + pathIsh: cliCommandOptions.realmJsonFilePath, + cwd: process.cwd() + }); + } + + if (buildContext.startKeycloakOptions.realmJsonFilePath !== undefined) { + assert( + await existsAsync( + buildContext.startKeycloakOptions.realmJsonFilePath + ), + `${pathRelative(process.cwd(), buildContext.startKeycloakOptions.realmJsonFilePath)} does not exist` + ); + return buildContext.startKeycloakOptions.realmJsonFilePath; + } + + return undefined; + })(), + buildContext + }); + { const { isAppBuildSuccess } = await appBuild({ buildContext @@ -189,154 +252,48 @@ export async function command(params: { assert(jarFilePath !== undefined); - const extensionJarFilePaths = await Promise.all( - buildContext.startKeycloakOptions.extensionJars.map(async extensionJar => { - switch (extensionJar.type) { - case "path": { - assert( - await existsAsync(extensionJar.path), - `${extensionJar.path} does not exist` - ); - return extensionJar.path; + const extensionJarFilePaths = [ + ...(keycloakMajorVersionNumber <= 20 + ? (console.log( + chalk.yellow( + "WARNING: With older version of keycloak your changes to the realm configuration are not persisted" + ) + ), + []) + : [ + pathJoin( + getThisCodebaseRootDirPath(), + "src", + "bin", + "start-keycloak", + KEYCLOAKIFY_LOGIN_JAR_BASENAME + ) + ]), + ...(await Promise.all( + buildContext.startKeycloakOptions.extensionJars.map(async extensionJar => { + switch (extensionJar.type) { + case "path": { + assert( + await existsAsync(extensionJar.path), + `${extensionJar.path} does not exist` + ); + return extensionJar.path; + } + case "url": { + const { archiveFilePath } = await downloadAndExtractArchive({ + cacheDirPath: buildContext.cacheDirPath, + fetchOptions: buildContext.fetchOptions, + url: extensionJar.url, + uniqueIdOfOnArchiveFile: "no extraction", + onArchiveFile: async () => {} + }); + return archiveFilePath; + } } - case "url": { - const { archiveFilePath } = await downloadAndExtractArchive({ - cacheDirPath: buildContext.cacheDirPath, - fetchOptions: buildContext.fetchOptions, - url: extensionJar.url, - uniqueIdOfOnArchiveFile: "no extraction", - onArchiveFile: async () => {} - }); - return archiveFilePath; - } - } - assert>(false); - }) - ); - - const getRealmJsonFilePath_defaultForKeycloakMajor = ( - keycloakMajorVersionNumber: number - ) => - pathJoin( - getThisCodebaseRootDirPath(), - "src", - "bin", - "start-keycloak", - `myrealm-realm-${keycloakMajorVersionNumber}.json` - ); - - const realmJsonFilePath = await (async () => { - if (cliCommandOptions.realmJsonFilePath !== undefined) { - if (cliCommandOptions.realmJsonFilePath === "none") { - return undefined; - } - return getAbsoluteAndInOsFormatPath({ - pathIsh: cliCommandOptions.realmJsonFilePath, - cwd: process.cwd() - }); - } - - if (buildContext.startKeycloakOptions.realmJsonFilePath !== undefined) { - assert( - await existsAsync(buildContext.startKeycloakOptions.realmJsonFilePath), - `${pathRelative(process.cwd(), buildContext.startKeycloakOptions.realmJsonFilePath)} does not exist` - ); - return buildContext.startKeycloakOptions.realmJsonFilePath; - } - - const internalFilePath = await (async () => { - const defaultFilePath = getRealmJsonFilePath_defaultForKeycloakMajor( - keycloakMajorVersionNumber - ); - - if (fs.existsSync(defaultFilePath)) { - return defaultFilePath; - } - - console.log( - `${chalk.yellow( - `Keycloakify do not have a realm configuration for Keycloak ${keycloakMajorVersionNumber} yet.` - )}` - ); - - console.log(chalk.cyan("Select what configuration to use:")); - - const dirPath = pathDirname(defaultFilePath); - - const { value } = await cliSelect({ - values: [ - ...fs - .readdirSync(dirPath) - .filter(fileBasename => fileBasename.endsWith(".json")), - "none" - ] - }).catch(() => { - process.exit(-1); - }); - - if (value === "none") { - return undefined; - } - - return pathJoin(dirPath, value); - })(); - - if (internalFilePath === undefined) { - return undefined; - } - - const filePath = pathJoin( - buildContext.cacheDirPath, - pathBasename(internalFilePath) - ); - - fs.writeFileSync( - filePath, - Buffer.from( - fs - .readFileSync(internalFilePath) - .toString("utf8") - .replace(/keycloakify\-starter/g, buildContext.themeNames[0]) - ), - "utf8" - ); - - return filePath; - })(); - - add_test_user_if_missing: { - if (realmJsonFilePath === undefined) { - break add_test_user_if_missing; - } - - const realm: Record = JSON.parse( - fs.readFileSync(realmJsonFilePath).toString("utf8") - ); - - if (realm.users !== undefined) { - break add_test_user_if_missing; - } - - const realmJsonFilePath_internal = (() => { - const filePath = getRealmJsonFilePath_defaultForKeycloakMajor( - keycloakMajorVersionNumber - ); - - if (!fs.existsSync(filePath)) { - return getRealmJsonFilePath_defaultForKeycloakMajor(25); - } - - return filePath; - })(); - - const users = JSON.parse( - fs.readFileSync(realmJsonFilePath_internal).toString("utf8") - ).users; - - realm.users = users; - - fs.writeFileSync(realmJsonFilePath, JSON.stringify(realm, null, 2), "utf8"); - } + assert>(false); + }) + )) + ]; async function extractThemeResourcesFromJar() { await extractArchive({ @@ -376,9 +333,7 @@ export async function command(params: { }); } catch {} - const DEFAULT_PORT = 8080; - const port = - cliCommandOptions.port ?? buildContext.startKeycloakOptions.port ?? DEFAULT_PORT; + const port = cliCommandOptions.port ?? buildContext.startKeycloakOptions.port ?? 8080; const doStartDevServer = (() => { const hasSpaUi = @@ -434,8 +389,15 @@ export async function command(params: { const dockerRunArgs: string[] = [ `-p${SPACE_PLACEHOLDER}${port}:8080`, `--name${SPACE_PLACEHOLDER}${CONTAINER_NAME}`, - `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN=admin`, - `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN_PASSWORD=admin`, + ...(keycloakMajorVersionNumber >= 26 + ? [ + `-e${SPACE_PLACEHOLDER}KC_BOOTSTRAP_ADMIN_USERNAME=admin`, + `-e${SPACE_PLACEHOLDER}KC_BOOTSTRAP_ADMIN_PASSWORD=admin` + ] + : [ + `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN=admin`, + `-e${SPACE_PLACEHOLDER}KEYCLOAK_ADMIN_PASSWORD=admin` + ]), ...(devServerPort === undefined ? [] : [ @@ -451,7 +413,7 @@ export async function command(params: { ...(realmJsonFilePath === undefined ? [] : [ - `-v${SPACE_PLACEHOLDER}"${realmJsonFilePath}":/opt/keycloak/data/import/myrealm-realm.json` + `-v${SPACE_PLACEHOLDER}"${realmJsonFilePath}":/opt/keycloak/data/import/${realmName}-realm.json` ]), `-v${SPACE_PLACEHOLDER}"${jarFilePath_cacheDir}":/opt/keycloak/providers/keycloak-theme.jar`, ...extensionJarFilePaths.map( @@ -526,7 +488,14 @@ export async function command(params: { { shell: true } ); - child.stdout.on("data", data => process.stdout.write(data)); + child.stdout.on("data", async data => { + if (data.toString("utf8").includes("keycloakify-logging: REALM_CONFIG_CHANGED")) { + await onRealmConfigChange(); + return; + } + + process.stdout.write(data); + }); child.stderr.on("data", data => process.stderr.write(data)); @@ -573,9 +542,9 @@ export async function command(params: { `${chalk.green("Your theme is accessible at:")}`, `${chalk.green("➜")} ${chalk.cyan.bold( (() => { - const url = new URL("https://my-theme.keycloakify.dev"); + const url = new URL(TEST_APP_URL); - if (port !== DEFAULT_PORT) { + if (port !== 8080) { url.searchParams.set("port", `${port}`); } if (kcHttpRelativePath !== undefined) { @@ -584,13 +553,20 @@ export async function command(params: { kcHttpRelativePath ); } + if (realmName !== "myrealm") { + url.searchParams.set("realm", realmName); + } + + if (clientName !== "myclient") { + url.searchParams.set("client", clientName); + } return url.href; })() )}`, "", "You can login with the following credentials:", - `- username: ${chalk.cyan.bold("testuser")}`, + `- username: ${chalk.cyan.bold(username)}`, `- password: ${chalk.cyan.bold("password123")}`, "", `Watching for changes in ${chalk.bold(