From 694b4c802799059d7070f7a76a6d5b0657d3ebc0 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 1 Feb 2024 06:22:33 +0100 Subject: [PATCH 1/7] Reintroduce doBuildRetrocompatAccountTheme (for now) and fix multiple things in the account default theme --- README.md | 11 ----- src/bin/constants.ts | 1 + src/bin/download-builtin-keycloak-theme.ts | 45 +++++++++++++++++++ src/bin/keycloakify/BuildOptions.ts | 4 +- .../bringInAccountV1.ts | 20 +++++++-- .../generateJavaStackFiles.ts | 13 +++++- .../generateTheme/generateTheme.ts | 35 ++++++++++++--- src/bin/promptKeycloakVersion.ts | 2 + 8 files changed, 109 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 0acfb895..54c3a984 100644 --- a/README.md +++ b/README.md @@ -125,17 +125,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d # Changelog highlights -## 9.2 - -Remove the `_retrocompat` option in the account theme dropdown and the `doBuildRetrocompatAccountTheme` build option. -`_retrocompat` was useless because `` works fine even on older Keycloak version. - -What you have to remember is that: - -- If you have no account theme: You can use the `retrocompat_*.jar` it will work on any Keycloak version. -- If you have an Account theme: Use `retrocompat_*.jar` only if your Keycloak is in a version prior to 23. - Keycloak 22 is not supported (Only login themes works in Keycloak 22). - ## 9.0 Bring back support for account themes in Keycloak v23 and up! [See issue](https://github.com/keycloakify/keycloakify/issues/389). diff --git a/src/bin/constants.ts b/src/bin/constants.ts index 6cb76d97..fa8c2a49 100644 --- a/src/bin/constants.ts +++ b/src/bin/constants.ts @@ -3,6 +3,7 @@ export const resources_common = "resources-common"; export const lastKeycloakVersionWithAccountV1 = "21.1.2"; export const themeTypes = ["login", "account"] as const; +export const retrocompatPostfix = "_retrocompat"; export const accountV1 = "account-v1"; export type ThemeType = (typeof themeTypes)[number]; diff --git a/src/bin/download-builtin-keycloak-theme.ts b/src/bin/download-builtin-keycloak-theme.ts index ab257e92..47801229 100644 --- a/src/bin/download-builtin-keycloak-theme.ts +++ b/src/bin/download-builtin-keycloak-theme.ts @@ -27,6 +27,51 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st "preCacheTransform": { "actionCacheId": "npm install and build", "action": async ({ destDirPath }) => { + fix_account_css: { + const accountCssFilePath = pathJoin(destDirPath, "keycloak", "account", "resources", "css", "account.css"); + + if (!fs.existsSync(accountCssFilePath)) { + break fix_account_css; + } + + fs.writeFileSync( + accountCssFilePath, + Buffer.from(fs.readFileSync(accountCssFilePath).toString("utf8").replace("top: -34px;", "top: -34px !important;"), "utf8") + ); + } + + fix_account_topt: { + const totpFtlFilePath = pathJoin(destDirPath, "base", "account", "totp.ftl"); + + if (!fs.existsSync(totpFtlFilePath)) { + break fix_account_topt; + } + + fs.writeFileSync( + totpFtlFilePath, + Buffer.from( + fs + .readFileSync(totpFtlFilePath) + .toString("utf8") + .replace( + [ + " <#list totp.policy.supportedApplications as app>", + "
  • ${app}
  • ", + " " + ].join("\n"), + [ + " <#if totp.policy.supportedApplications?has_content>", + " <#list totp.policy.supportedApplications as app>", + "
  • ${app}
  • ", + " ", + " " + ].join("\n") + ), + "utf8" + ) + ); + } + install_common_node_modules: { const commonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources"); diff --git a/src/bin/keycloakify/BuildOptions.ts b/src/bin/keycloakify/BuildOptions.ts index 67b30f31..119dd557 100644 --- a/src/bin/keycloakify/BuildOptions.ts +++ b/src/bin/keycloakify/BuildOptions.ts @@ -24,6 +24,7 @@ export type BuildOptions = { /** If your app is hosted under a subpath, it's the case in CRA if you have "homepage": "https://example.com/my-app" in your package.json * In this case the urlPathname will be "/my-app/" */ urlPathname: string | undefined; + doBuildRetrocompatAccountTheme: boolean; }; export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions { @@ -150,6 +151,7 @@ export function readBuildOptions(params: { reactAppRootDirPath: string; processA const out = url.pathname.replace(/([^/])$/, "$1/"); return out === "/" ? undefined : out; - })() + })(), + "doBuildRetrocompatAccountTheme": parsedPackageJson.keycloakify?.doBuildRetrocompatAccountTheme ?? true }; } diff --git a/src/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.ts b/src/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.ts index 3ca94b27..c41c618f 100644 --- a/src/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.ts +++ b/src/bin/keycloakify/generateJavaStackFiles/bringInAccountV1.ts @@ -38,7 +38,21 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike const commonResourceFilePaths = [ "node_modules/patternfly/dist/css/patternfly.min.css", - "node_modules/patternfly/dist/css/patternfly-additions.min.css" + "node_modules/patternfly/dist/css/patternfly-additions.min.css", + ...[ + "OpenSans-Light-webfont.woff2", + "OpenSans-Regular-webfont.woff2", + "OpenSans-Bold-webfont.woff2", + "OpenSans-Semibold-webfont.woff2", + "OpenSans-Bold-webfont.woff", + "OpenSans-Light-webfont.woff", + "OpenSans-Regular-webfont.woff", + "OpenSans-Semibold-webfont.woff", + "OpenSans-Regular-webfont.ttf", + "OpenSans-Light-webfont.ttf", + "OpenSans-Semibold-webfont.ttf", + "OpenSans-Bold-webfont.ttf" + ].map(path => `node_modules/patternfly/dist/fonts/${path}`) ]; for (const relativeFilePath of commonResourceFilePaths.map(path => pathJoin(...path.split("/")))) { @@ -49,7 +63,7 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike fs.cpSync(pathJoin(builtinKeycloakThemeTmpDirPath, "keycloak", "common", "resources", relativeFilePath), destFilePath); } - const resourceFilePaths = ["css/account.css"]; + const resourceFilePaths = ["css/account.css", "img/icon-sidebar-active.png", "img/logo.png"]; for (const relativeFilePath of resourceFilePaths.map(path => pathJoin(...path.split("/")))) { const destFilePath = pathJoin(accountV1DirPath, "resources", relativeFilePath); @@ -69,7 +83,7 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike "", "locales=ar,ca,cs,da,de,en,es,fr,fi,hu,it,ja,lt,nl,no,pl,pt-BR,ru,sk,sv,tr,zh-CN", "", - "styles=" + [...resourceFilePaths, ...commonResourceFilePaths.map(path => `resources_common/${path}`)].join(" "), + "styles=" + [...resourceFilePaths, ...commonResourceFilePaths.map(path => `resources-common/${path}`)].join(" "), "", "##### css classes for form buttons", "# main class used for all buttons", diff --git a/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts b/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts index 732c2007..df7b92ac 100644 --- a/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts +++ b/src/bin/keycloakify/generateJavaStackFiles/generateJavaStackFiles.ts @@ -3,7 +3,7 @@ import { join as pathJoin, dirname as pathDirname } from "path"; import { assert } from "tsafe/assert"; import { Reflect } from "tsafe/Reflect"; import type { BuildOptions } from "../BuildOptions"; -import { type ThemeType, accountV1 } from "../../constants"; +import { type ThemeType, retrocompatPostfix, accountV1 } from "../../constants"; import { bringInAccountV1 } from "./bringInAccountV1"; export type BuildOptionsLike = { @@ -13,6 +13,7 @@ export type BuildOptionsLike = { cacheDirPath: string; keycloakifyBuildDirPath: string; themeNames: string[]; + doBuildRetrocompatAccountTheme: boolean; }; { @@ -113,7 +114,15 @@ export async function generateJavaStackFiles(params: { "types": Object.entries(implementedThemeTypes) .filter(([, isImplemented]) => isImplemented) .map(([themeType]) => themeType) - } + }, + ...(!implementedThemeTypes.account || !buildOptions.doBuildRetrocompatAccountTheme + ? [] + : [ + { + "name": `${themeName}${retrocompatPostfix}`, + "types": ["account"] + } + ]) ]) .flat() ] diff --git a/src/bin/keycloakify/generateTheme/generateTheme.ts b/src/bin/keycloakify/generateTheme/generateTheme.ts index 1cffad27..b0e9ef84 100644 --- a/src/bin/keycloakify/generateTheme/generateTheme.ts +++ b/src/bin/keycloakify/generateTheme/generateTheme.ts @@ -1,10 +1,10 @@ import { transformCodebase } from "../../tools/transformCodebase"; import * as fs from "fs"; -import { join as pathJoin, resolve as pathResolve } from "path"; +import { join as pathJoin, basename as pathBasename, resolve as pathResolve } from "path"; import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode"; import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode"; import { generateFtlFilesCodeFactory, loginThemePageIds, accountThemePageIds } from "../generateFtl"; -import { themeTypes, type ThemeType, lastKeycloakVersionWithAccountV1, keycloak_resources, accountV1 } from "../../constants"; +import { themeTypes, type ThemeType, lastKeycloakVersionWithAccountV1, keycloak_resources, retrocompatPostfix, accountV1 } from "../../constants"; import { isInside } from "../../tools/isInside"; import type { BuildOptions } from "../BuildOptions"; import { assert, type Equals } from "tsafe/assert"; @@ -22,6 +22,7 @@ export type BuildOptionsLike = { keycloakifyBuildDirPath: string; reactAppBuildDirPath: string; cacheDirPath: string; + doBuildRetrocompatAccountTheme: boolean; }; assert(); @@ -35,9 +36,17 @@ export async function generateTheme(params: { }): Promise { const { themeName, themeSrcDirPath, keycloakifySrcDirPath, buildOptions, keycloakifyVersion } = params; - const getThemeTypeDirPath = (params: { themeType: ThemeType | "email" }) => { - const { themeType } = params; - return pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", themeName, themeType); + const getThemeTypeDirPath = (params: { themeType: ThemeType | "email"; isRetrocompat?: true }) => { + const { themeType, isRetrocompat = false } = params; + return pathJoin( + buildOptions.keycloakifyBuildDirPath, + "src", + "main", + "resources", + "theme", + `${themeName}${isRetrocompat ? retrocompatPostfix : ""}`, + themeType + ); }; let allCssGlobalsToDefine: Record = {}; @@ -192,6 +201,22 @@ export async function generateTheme(params: { "utf8" ) ); + + if (themeType === "account" && buildOptions.doBuildRetrocompatAccountTheme) { + transformCodebase({ + "srcDirPath": themeTypeDirPath, + "destDirPath": getThemeTypeDirPath({ themeType, "isRetrocompat": true }), + "transformSourceCode": ({ filePath, sourceCode }) => { + if (pathBasename(filePath) === "theme.properties") { + return { + "modifiedSourceCode": Buffer.from(sourceCode.toString("utf8").replace(`parent=${accountV1}`, "parent=keycloak"), "utf8") + }; + } + + return { "modifiedSourceCode": sourceCode }; + } + }); + } } email: { diff --git a/src/bin/promptKeycloakVersion.ts b/src/bin/promptKeycloakVersion.ts index 276dc90f..63e7b25c 100644 --- a/src/bin/promptKeycloakVersion.ts +++ b/src/bin/promptKeycloakVersion.ts @@ -1,6 +1,7 @@ import { getLatestsSemVersionedTagFactory } from "./tools/octokit-addons/getLatestsSemVersionedTag"; import { Octokit } from "@octokit/rest"; import cliSelect from "cli-select"; +import { lastKeycloakVersionWithAccountV1 } from "./constants"; export async function promptKeycloakVersion() { const { getLatestsSemVersionedTag } = (() => { @@ -26,6 +27,7 @@ export async function promptKeycloakVersion() { "owner": "keycloak", "repo": "keycloak" }).then(arr => arr.map(({ tag }) => tag))), + lastKeycloakVersionWithAccountV1, "11.0.3" ]; From 82f34c38f61262346252c7c872e6fc14a62d3c8b Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 1 Feb 2024 06:22:54 +0100 Subject: [PATCH 2/7] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e548ec9..e38189c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "9.2.0", + "version": "9.3.0", "description": "Create Keycloak themes using React", "repository": { "type": "git", From ca255985c08e9d0ee0f10ac0d8227419c40c15a3 Mon Sep 17 00:00:00 2001 From: giorgoslytos Date: Fri, 2 Feb 2024 14:35:14 +0200 Subject: [PATCH 3/7] fix: radio inputs on login-otp page --- src/login/lib/useGetClassName.ts | 2 +- src/login/pages/LoginOtp.tsx | 160 +++++++++++++------------------ 2 files changed, 66 insertions(+), 96 deletions(-) diff --git a/src/login/lib/useGetClassName.ts b/src/login/lib/useGetClassName.ts index 2692cfb5..25bb6572 100644 --- a/src/login/lib/useGetClassName.ts +++ b/src/login/lib/useGetClassName.ts @@ -94,7 +94,7 @@ export const { useGetClassName } = createUseClassName({ "kcAuthenticatorWebAuthnPasswordlessClass": "fa fa-key list-view-pf-icon-lg", //css classes for the OTP Login Form - "kcSelectOTPListClass": "card-pf card-pf-view card-pf-view-select card-pf-view-single-select", + "kcSelectOTPListClass": "card-pf card-pf-view card-pf-view-select card-pf-view-single-select col-xs-12", "kcSelectOTPListItemClass": "card-pf-body card-pf-top-element", "kcAuthenticatorOtpCircleClass": "fa fa-mobile card-pf-icon-circle", "kcSelectOTPItemHeadingClass": "card-pf-title text-center", diff --git a/src/login/pages/LoginOtp.tsx b/src/login/pages/LoginOtp.tsx index f4e99d34..3dc8419b 100644 --- a/src/login/pages/LoginOtp.tsx +++ b/src/login/pages/LoginOtp.tsx @@ -1,5 +1,3 @@ -import { useEffect } from "react"; -import { headInsert } from "keycloakify/tools/headInsert"; import { clsx } from "keycloakify/tools/clsx"; import type { PageProps } from "keycloakify/login/pages/PageProps"; import { useGetClassName } from "keycloakify/login/lib/useGetClassName"; @@ -18,105 +16,77 @@ export default function LoginOtp(props: PageProps { - let isCleanedUp = false; - - const { prLoaded, remove } = headInsert({ - "type": "javascript", - "src": `${kcContext.url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js` - }); - - (async () => { - await prLoaded; - - if (isCleanedUp) { - return; - } - - evaluateInlineScript(); - })(); - - return () => { - isCleanedUp = true; - remove(); - }; - }, []); - return ( - + ); } - -declare const $: any; - -function evaluateInlineScript() { - $(document).ready(function () { - // Card Single Select - $(".card-pf-view-single-select").click(function (this: any) { - if ($(this).hasClass("active")) { - $(this).removeClass("active"); - $(this).children().removeAttr("name"); - } else { - $(".card-pf-view-single-select").removeClass("active"); - $(".card-pf-view-single-select").children().removeAttr("name"); - $(this).addClass("active"); - $(this).children().attr("name", "selectedCredentialId"); - } - }); - - var defaultCred = $(".card-pf-view-single-select")[0]; - if (defaultCred) { - defaultCred.click(); - } - }); -} From 83df27ec99efe8ad5f6a138100eff2955f8c2afd Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 07:11:45 +0000 Subject: [PATCH 4/7] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 54c3a984..fcd5d23b 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Markus Siemens
    Markus Siemens

    💻 Rlok
    Rlok

    💻 Moulyy
    Moulyy

    💻 + giorgoslytos
    giorgoslytos

    💻 From b6ecff2dd3069c2b247144143c48d08b520ddba8 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 07:11:46 +0000 Subject: [PATCH 5/7] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ba2c18dc..2302c06a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -231,6 +231,15 @@ "contributions": [ "code" ] + }, + { + "login": "giorgoslytos", + "name": "giorgoslytos", + "avatar_url": "https://avatars.githubusercontent.com/u/50946162?v=4", + "profile": "https://github.com/giorgoslytos", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, From a9231e2ed841c3331955a8540901529a16e50c04 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 4 Feb 2024 06:31:10 +0100 Subject: [PATCH 6/7] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e38189c1..64714813 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "9.3.0", + "version": "9.3.1", "description": "Create Keycloak themes using React", "repository": { "type": "git", From 7e4eba63762023472f18935058aac661a1afeaf3 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 7 Feb 2024 10:29:46 +0100 Subject: [PATCH 7/7] Update README, add poll --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fcd5d23b..60fd2c18 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ Keycloakify is fully compatible with Keycloak 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, [~~22~~](https://github.com/keycloakify/keycloakify/issues/389#issuecomment-1822509763), **23** [and up](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791)! +> 📣 I've observed that a few people have unstarred the project recently. +> I'm concerned that I may have inadvertently introduced some misinformation in the documentation, leading to frustration. +> If you're having a negative experience, [please let me know so I can resolve the issue](https://github.com/keycloakify/keycloakify/discussions/507). + ## Sponsor 👼 We are exclusively sponsored by [Cloud IAM](https://cloud-iam.com/?mtm_campaign=keycloakify-deal&mtm_source=keycloakify-github), a French company offering Keycloak as a service. @@ -128,7 +132,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d ## 9.0 -Bring back support for account themes in Keycloak v23 and up! [See issue](https://github.com/keycloakify/keycloakify/issues/389). +Bring back support for account themes in Keycloak v23 and up! [See issue](https://github.com/keycloakify/keycloakify/issues/389). +[Read the migration guide](https://docs.keycloakify.dev/migration-guides/v8-greater-than-v9). ### Breaking changes