Keycloak config persistance implemented (to test)
This commit is contained in:
@ -3,11 +3,14 @@ import { assert, type Equals } from "tsafe/assert";
|
||||
import { is } from "tsafe/is";
|
||||
import { id } from "tsafe/id";
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { getThisCodebaseRootDirPath } from "../../tools/getThisCodebaseRootDirPath";
|
||||
|
||||
export type ParsedRealmJson = {
|
||||
name: string;
|
||||
loginTheme?: string;
|
||||
accountTheme?: string;
|
||||
adminTheme?: string;
|
||||
emailTheme?: string;
|
||||
eventsListeners: string[];
|
||||
users: {
|
||||
id: string;
|
||||
email: string;
|
||||
@ -52,6 +55,11 @@ export function readRealmJsonFile(params: {
|
||||
|
||||
const zTargetType = z.object({
|
||||
name: 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(),
|
||||
@ -105,19 +113,3 @@ export function readRealmJsonFile(params: {
|
||||
|
||||
return parsedRealmJson;
|
||||
}
|
||||
|
||||
export function getDefaultConfig(params: {
|
||||
keycloakMajorVersionNumber: number;
|
||||
}): ParsedRealmJson {
|
||||
const { keycloakMajorVersionNumber } = params;
|
||||
|
||||
const realmJsonFilePath = pathJoin(
|
||||
getThisCodebaseRootDirPath(),
|
||||
"src",
|
||||
"bin",
|
||||
"start-keycloak",
|
||||
`myrealm-realm-${keycloakMajorVersionNumber}.json`
|
||||
);
|
||||
|
||||
return readRealmJsonFile({ realmJsonFilePath });
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
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));
|
||||
|
||||
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
|
||||
})
|
||||
});
|
||||
}
|
@ -0,0 +1 @@
|
||||
export * from "./defaultConfig";
|
@ -1,4 +1,3 @@
|
||||
import { runPrettier, getIsPrettierAvailable } from "../../tools/runPrettier";
|
||||
import { CONTAINER_NAME } from "../../shared/constants";
|
||||
import child_process from "child_process";
|
||||
import { join as pathJoin } from "path";
|
||||
@ -6,7 +5,7 @@ import chalk from "chalk";
|
||||
import { Deferred } from "evt/tools/Deferred";
|
||||
import { assert, is } from "tsafe/assert";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import * as fs from "fs/promises";
|
||||
import { type ParsedRealmJson, readRealmJsonFile } from "./ParsedRealmJson";
|
||||
|
||||
export type BuildContextLike = {
|
||||
cacheDirPath: string;
|
||||
@ -17,15 +16,9 @@ assert<BuildContext extends BuildContextLike ? true : false>();
|
||||
export async function dumpContainerConfig(params: {
|
||||
realmName: string;
|
||||
keycloakMajorVersionNumber: number;
|
||||
targetRealmConfigJsonFilePath: string;
|
||||
buildContext: BuildContextLike;
|
||||
}) {
|
||||
const {
|
||||
realmName,
|
||||
keycloakMajorVersionNumber,
|
||||
targetRealmConfigJsonFilePath,
|
||||
buildContext
|
||||
} = params;
|
||||
}): Promise<ParsedRealmJson> {
|
||||
const { realmName, keycloakMajorVersionNumber, buildContext } = params;
|
||||
|
||||
{
|
||||
// https://github.com/keycloak/keycloak/issues/33800
|
||||
@ -148,20 +141,7 @@ export async function dumpContainerConfig(params: {
|
||||
await dCompleted.pr;
|
||||
}
|
||||
|
||||
let sourceCode = (await fs.readFile(targetRealmConfigJsonFilePath_tmp)).toString(
|
||||
"utf8"
|
||||
);
|
||||
|
||||
run_prettier: {
|
||||
if (!(await getIsPrettierAvailable())) {
|
||||
break run_prettier;
|
||||
}
|
||||
|
||||
sourceCode = await runPrettier({
|
||||
filePath: targetRealmConfigJsonFilePath,
|
||||
sourceCode: sourceCode
|
||||
});
|
||||
}
|
||||
|
||||
await fs.writeFile(targetRealmConfigJsonFilePath, Buffer.from(sourceCode, "utf8"));
|
||||
return readRealmJsonFile({
|
||||
realmJsonFilePath: targetRealmConfigJsonFilePath_tmp
|
||||
});
|
||||
}
|
||||
|
1
src/bin/start-keycloak/realmConfig/index.ts
Normal file
1
src/bin/start-keycloak/realmConfig/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./realmConfig";
|
@ -1,15 +1,26 @@
|
||||
import { assert } from "tsafe/assert";
|
||||
import { getDefaultConfig, type ParsedRealmJson } from "./ParsedRealmJson";
|
||||
import type { ParsedRealmJson } from "./ParsedRealmJson";
|
||||
import { getDefaultConfig } from "./defaultConfig";
|
||||
import type { BuildContext } from "../../shared/buildContext";
|
||||
import { objectKeys } from "tsafe/objectKeys";
|
||||
|
||||
export type BuildContextLike = {
|
||||
themeNames: BuildContext["themeNames"];
|
||||
implementedThemeTypes: BuildContext["implementedThemeTypes"];
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>;
|
||||
|
||||
export function prepareRealmConfig(params: {
|
||||
parsedRealmJson: ParsedRealmJson;
|
||||
keycloakMajorVersionNumber: number;
|
||||
buildContext: BuildContextLike;
|
||||
}): {
|
||||
realmName: string;
|
||||
clientName: string;
|
||||
username: string;
|
||||
} {
|
||||
const { parsedRealmJson, keycloakMajorVersionNumber } = params;
|
||||
const { parsedRealmJson, keycloakMajorVersionNumber, buildContext } = params;
|
||||
|
||||
const { username } = addOrEditTestUser({
|
||||
parsedRealmJson,
|
||||
@ -23,6 +34,22 @@ export function prepareRealmConfig(params: {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return {
|
||||
realmName: parsedRealmJson.name,
|
||||
clientName: clientId,
|
||||
@ -30,6 +57,21 @@ export function prepareRealmConfig(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function enableCustomThemes(params: {
|
||||
parsedRealmJson: ParsedRealmJson;
|
||||
themeName: string;
|
||||
implementedThemeTypes: BuildContextLike["implementedThemeTypes"];
|
||||
}) {
|
||||
const { parsedRealmJson, themeName, implementedThemeTypes } = params;
|
||||
|
||||
for (const themeType of objectKeys(implementedThemeTypes)) {
|
||||
parsedRealmJson[`${themeType}Theme` as const] = implementedThemeTypes[themeType]
|
||||
.isImplemented
|
||||
? themeName
|
||||
: "";
|
||||
}
|
||||
}
|
||||
|
||||
function addOrEditTestUser(params: {
|
||||
parsedRealmJson: ParsedRealmJson;
|
||||
keycloakMajorVersionNumber: number;
|
||||
|
108
src/bin/start-keycloak/realmConfig/realmConfig.ts
Normal file
108
src/bin/start-keycloak/realmConfig/realmConfig.ts
Normal file
@ -0,0 +1,108 @@
|
||||
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 } from "path";
|
||||
import { existsAsync } from "../../tools/fs.existsAsync";
|
||||
import { readRealmJsonFile, type ParsedRealmJson } from "./ParsedRealmJson";
|
||||
import {
|
||||
dumpContainerConfig,
|
||||
type BuildContextLike as BuildContextLike_dumpContainerConfig
|
||||
} from "./dumpContainerConfig";
|
||||
|
||||
export type BuildContextLike = BuildContextLike_dumpContainerConfig &
|
||||
BuildContextLike_prepareRealmConfig & {
|
||||
projectDirPath: string;
|
||||
};
|
||||
|
||||
assert<BuildContext extends BuildContextLike ? true : false>;
|
||||
|
||||
export async function getRealmConfig(params: {
|
||||
keycloakMajorVersionNumber: number;
|
||||
realmJsonFilePath_userProvided: string | undefined;
|
||||
buildContext: BuildContextLike;
|
||||
}): Promise<{
|
||||
realmJsonFilePath: string;
|
||||
clientName: string;
|
||||
realmName: string;
|
||||
username: string;
|
||||
onRealmConfigChange: () => Promise<void>;
|
||||
}> {
|
||||
const { keycloakMajorVersionNumber, realmJsonFilePath_userProvided, buildContext } =
|
||||
params;
|
||||
|
||||
const realmJsonFilePath = pathJoin(
|
||||
buildContext.projectDirPath,
|
||||
`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 });
|
||||
|
||||
async function onRealmConfigChange() {
|
||||
const parsedRealmJson = await dumpContainerConfig({
|
||||
buildContext,
|
||||
realmName,
|
||||
keycloakMajorVersionNumber
|
||||
});
|
||||
|
||||
await writeRealmJsonFile({ parsedRealmJson });
|
||||
}
|
||||
|
||||
return {
|
||||
realmJsonFilePath,
|
||||
clientName,
|
||||
realmName,
|
||||
username,
|
||||
onRealmConfigChange
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user