Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
c708e619e9 | |||
2cceb3c929 | |||
aee8704691 | |||
43af60237b | |||
e615479b41 | |||
973fb4d2d5 | |||
964feae846 | |||
ea3d8a5634 | |||
48aab2d92a | |||
00eab73954 | |||
5f6f8b12bd | |||
c2d4d6fd49 | |||
04b660ff9b | |||
c292d926be | |||
23b83ceef7 | |||
1324648db6 | |||
735bff3348 | |||
05a6aee782 | |||
c7349c2556 | |||
51f3d06752 | |||
31759d86ab | |||
7c6eed99d2 | |||
bc4b0ec17d | |||
f766348b87 | |||
82281303d0 | |||
1caa17beb0 | |||
1c4d346f9f | |||
4320efb049 | |||
a756423768 | |||
8525fc74c0 | |||
30c0cc5aa8 | |||
b3bbd7c07d | |||
09d4ba2bb0 | |||
30315027c1 | |||
05acefe70e | |||
6c14758e33 | |||
bef8545161 | |||
f15c0ecbb0 |
51
CHANGELOG.md
51
CHANGELOG.md
@ -1,3 +1,54 @@
|
||||
### **5.7.2** (2022-07-13)
|
||||
|
||||
- #135
|
||||
|
||||
### **5.7.1** (2022-07-11)
|
||||
|
||||
- #134
|
||||
|
||||
## **5.7.0** (2022-07-07)
|
||||
|
||||
- Merge pull request #120 from revolunet/logout
|
||||
|
||||
feat: add logout-confirm
|
||||
- fix: use kcMessages
|
||||
- fix: add translations FR
|
||||
- feat: add logout-confirm
|
||||
|
||||
### **5.6.5** (2022-07-06)
|
||||
|
||||
- Merge pull request #133 from bardius/fix/Issue-131-include-all-nested-folders-in-artifact-unzip
|
||||
|
||||
fix: Issue-131: include all nested folders in artifact unzip
|
||||
- Merge pull request #132 from bardius/fix/Issue-130-fix-equality-detection-of-nested-ftl-object-properties
|
||||
|
||||
fix: Issue-130: fix equality detection of nested ftl object property …
|
||||
- fix: Issue-131: include all nested folders in artifact unzip
|
||||
- fix: Issue-130: fix equality detection of nested ftl object property paths
|
||||
|
||||
### **5.6.4** (2022-07-06)
|
||||
|
||||
- Fix login-register-email.ftl
|
||||
- Update to Keycloak 18.0.2 for the test container
|
||||
|
||||
### **5.6.3** (2022-07-03)
|
||||
|
||||
- update powerhooks
|
||||
|
||||
### **5.6.2** (2022-07-03)
|
||||
|
||||
- Update powerhooks and EVT
|
||||
|
||||
### **5.6.1** (2022-07-03)
|
||||
|
||||
- Merge pull request #128 from Ann2827/pull
|
||||
|
||||
Fix bugs on error.ftl template
|
||||
- fix: bugs on error.ftl template
|
||||
- Merge pull request #52 from InseeFrLab/main
|
||||
|
||||
Update fork
|
||||
|
||||
## **5.6.0** (2022-06-28)
|
||||
|
||||
- Merge pull request #127 from aidangilmore/add-totp-support
|
||||
|
@ -38,6 +38,14 @@
|
||||
|
||||
# Changelog highlights
|
||||
|
||||
## 5.7.0
|
||||
|
||||
- Feat `logout-confirm.ftl`. [PR](https://github.com/InseeFrLab/keycloakify/pull/120)
|
||||
|
||||
## 5.6.4
|
||||
|
||||
Fix `login-verify-email.ftl` page. [Before](https://user-images.githubusercontent.com/6702424/177436014-0bad22c4-5bfb-45bb-8fc9-dad65143cd0c.png) - [After](https://user-images.githubusercontent.com/6702424/177435797-ec5d7db3-84cf-49cb-8efc-3427a81f744e.png)
|
||||
|
||||
## v5.6.0
|
||||
|
||||
Add support for `login-config-totp.ftl` page [#127](https://github.com/InseeFrLab/keycloakify/pull/127).
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "keycloakify",
|
||||
"version": "5.6.0",
|
||||
"version": "5.7.2",
|
||||
"description": "Keycloak theme generator for Reacts app",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -77,11 +77,11 @@
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"cheerio": "^1.0.0-rc.5",
|
||||
"cli-select": "^1.1.2",
|
||||
"evt": "2.0.0-beta.44",
|
||||
"evt": "2.0.0-beta.45",
|
||||
"memoizee": "^0.4.15",
|
||||
"minimal-polyfills": "^2.2.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"powerhooks": "^0.20.1",
|
||||
"powerhooks": "^0.20.3",
|
||||
"react-markdown": "^5.0.3",
|
||||
"scripting-tools": "^0.19.13",
|
||||
"tsafe": "^0.10.0",
|
||||
|
@ -91,8 +91,8 @@ export function main() {
|
||||
"cwd": keycloakThemeBuildingDirPath,
|
||||
});
|
||||
|
||||
//We want, however to test in a container running the latest Keycloak version
|
||||
const containerKeycloakVersion = "18.0.0";
|
||||
//We want, however, to test in a container running the latest Keycloak version
|
||||
const containerKeycloakVersion = "18.0.2";
|
||||
|
||||
generateStartKeycloakTestingContainer({
|
||||
keycloakThemeBuildingDirPath,
|
||||
|
@ -30,6 +30,9 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
</#attempt>
|
||||
|
||||
"printIfExists": function (fieldName, x) {
|
||||
<#if !messagesPerField?? >
|
||||
return undefined;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
@ -41,6 +44,9 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"existsError": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return false;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
@ -52,6 +58,9 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"get": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return '';
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
@ -65,6 +74,9 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
throw new Error("There is no " + fieldName + " field");
|
||||
},
|
||||
"exists": function (fieldName) {
|
||||
<#if !messagesPerField?? >
|
||||
return false;
|
||||
</#if>
|
||||
<#list fieldNames as fieldName>
|
||||
if(fieldName === "${fieldName}" ){
|
||||
<#attempt>
|
||||
@ -137,6 +149,9 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
key == "identityProviderBrokerCtx" &&
|
||||
are_same_path(path, []) &&
|
||||
["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(pageId)
|
||||
) || (
|
||||
["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) &&
|
||||
are_same_path(path, ["realm"])
|
||||
)
|
||||
>
|
||||
<#local out_seq += ["/*If you need '" + key + "' on " + pageId + ", please submit an issue to the Keycloakify repo*/"]>
|
||||
@ -290,7 +305,7 @@ ${ftl_object_to_js_code_declaring_an_object(.data_model, [])?no_esc};
|
||||
</#function>
|
||||
<#function are_same_path path searchedPath>
|
||||
|
||||
<#if path?size != path?size>
|
||||
<#if path?size != searchedPath?size>
|
||||
<#return false>
|
||||
</#if>
|
||||
|
||||
|
@ -5,6 +5,7 @@ import { join as pathJoin } from "path";
|
||||
import { objectKeys } from "tsafe/objectKeys";
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
|
||||
// https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java
|
||||
export const pageIds = [
|
||||
"login.ftl",
|
||||
"register.ftl",
|
||||
@ -21,6 +22,7 @@ export const pageIds = [
|
||||
"login-idp-link-email.ftl",
|
||||
"login-page-expired.ftl",
|
||||
"login-config-totp.ftl",
|
||||
"logout-confirm.ftl",
|
||||
] as const;
|
||||
|
||||
export type PageId = typeof pageIds[number];
|
||||
|
@ -17,7 +17,7 @@ export function downloadAndUnzip(params: { url: string; destDirPath: string; pat
|
||||
|
||||
execSync(`curl -L ${url} -o ${zipFilePath}`, { "cwd": tmpDirPath });
|
||||
|
||||
execSync(`unzip ${zipFilePath}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/*"`}`, {
|
||||
execSync(`unzip -o ${zipFilePath}${pathOfDirToExtractInArchive === undefined ? "" : ` "${pathOfDirToExtractInArchive}/**/*"`}`, {
|
||||
"cwd": tmpDirPath,
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,7 @@ import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { getMsg } from "../i18n";
|
||||
|
||||
export const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Info } & KcProps) => {
|
||||
const { msg } = getMsg(kcContext);
|
||||
const { msg, msgStr } = getMsg(kcContext);
|
||||
|
||||
assert(kcContext.message !== undefined);
|
||||
|
||||
@ -24,7 +24,7 @@ export const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.In
|
||||
{message.summary}
|
||||
|
||||
{requiredActions !== undefined && (
|
||||
<b>{requiredActions.map(requiredAction => msg(`requiredAction.${requiredAction}` as const)).join(",")}</b>
|
||||
<b>{requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",")}</b>
|
||||
)}
|
||||
</p>
|
||||
{!skipLink && pageRedirectUri !== undefined ? (
|
||||
|
@ -16,6 +16,7 @@ import { LoginIdpLinkConfirm } from "./LoginIdpLinkConfirm";
|
||||
import { LoginPageExpired } from "./LoginPageExpired";
|
||||
import { LoginIdpLinkEmail } from "./LoginIdpLinkEmail";
|
||||
import { LoginConfigTotp } from "./LoginConfigTotp";
|
||||
import { LogoutConfirm } from "./LogoutConfirm";
|
||||
|
||||
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
|
||||
switch (kcContext.pageId) {
|
||||
@ -49,5 +50,7 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase }
|
||||
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
||||
case "login-config-totp.ftl":
|
||||
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
||||
case "logout-confirm.ftl":
|
||||
return <LogoutConfirm {...{ kcContext, ...props }} />;
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,7 @@ import { getMsg } from "../i18n";
|
||||
export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginVerifyEmail } & KcProps) => {
|
||||
const { msg } = getMsg(kcContext);
|
||||
|
||||
const { url } = kcContext;
|
||||
const { url, user } = kcContext;
|
||||
|
||||
return (
|
||||
<Template
|
||||
@ -17,10 +17,12 @@ export const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcCo
|
||||
headerNode={msg("emailVerifyTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<p className="instruction">{msg("emailVerifyInstruction1")}</p>
|
||||
<p className="instruction">{msg("emailVerifyInstruction1", user?.email)}</p>
|
||||
<p className="instruction">
|
||||
{msg("emailVerifyInstruction2")}
|
||||
<br />
|
||||
<a href={url.loginAction}>{msg("doClickHere")}</a>
|
||||
|
||||
{msg("emailVerifyInstruction3")}
|
||||
</p>
|
||||
</>
|
||||
|
61
src/lib/components/LogoutConfirm.tsx
Normal file
61
src/lib/components/LogoutConfirm.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { memo } from "react";
|
||||
import { useCssAndCx } from "tss-react";
|
||||
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||
import { getMsg } from "../i18n";
|
||||
|
||||
export const LogoutConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LogoutConfirm } & KcProps) => {
|
||||
const { url, client, logoutConfirm } = kcContext;
|
||||
|
||||
const { cx } = useCssAndCx();
|
||||
|
||||
const { msg, msgStr } = getMsg(kcContext);
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, ...props }}
|
||||
doFetchDefaultThemeResources={true}
|
||||
displayMessage={false}
|
||||
headerNode={msg("logoutConfirmTitle")}
|
||||
formNode={
|
||||
<>
|
||||
<div id="kc-logout-confirm" className="content-area">
|
||||
<p className="instruction">{msg("logoutConfirmHeader")}</p>
|
||||
<form className="form-actions" action={url.logoutConfirmAction} method="POST">
|
||||
<input type="hidden" name="session_code" value={logoutConfirm.code} />
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options">
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)}></div>
|
||||
</div>
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormGroupClass)}>
|
||||
<input
|
||||
tabIndex={4}
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass,
|
||||
)}
|
||||
name="confirmLogout"
|
||||
id="kc-logout"
|
||||
type="submit"
|
||||
value={msgStr("doLogout")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div id="kc-info-message">
|
||||
{!logoutConfirm.skipLink && client.baseUrl && (
|
||||
<p>
|
||||
<a href={client.baseUrl} dangerouslySetInnerHTML={{ __html: msgStr("backToApplication") }} />
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
@ -25,7 +25,8 @@ export type KcContextBase =
|
||||
| KcContextBase.LoginIdpLinkConfirm
|
||||
| KcContextBase.LoginIdpLinkEmail
|
||||
| KcContextBase.LoginPageExpired
|
||||
| KcContextBase.LoginConfigTotp;
|
||||
| KcContextBase.LoginConfigTotp
|
||||
| KcContextBase.LogoutConfirm;
|
||||
|
||||
export declare namespace KcContextBase {
|
||||
export type Common = {
|
||||
@ -179,6 +180,10 @@ export declare namespace KcContextBase {
|
||||
|
||||
export type LoginVerifyEmail = Common & {
|
||||
pageId: "login-verify-email.ftl";
|
||||
//NOTE: Optional because maybe it wasn't defined in older keycloak versions.
|
||||
user?: {
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Terms = Common & {
|
||||
@ -252,6 +257,20 @@ export declare namespace KcContextBase {
|
||||
otpCredentials: { id: string; userLabel: string }[];
|
||||
};
|
||||
};
|
||||
|
||||
export type LogoutConfirm = Common & {
|
||||
pageId: "logout-confirm.ftl";
|
||||
url: {
|
||||
logoutConfirmAction: string;
|
||||
};
|
||||
client: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
logoutConfirm: {
|
||||
code: string;
|
||||
skipLink?: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type Attribute = {
|
||||
|
@ -337,6 +337,9 @@ export const kcContextMocks: KcContextBase[] = [
|
||||
id<KcContextBase.LoginVerifyEmail>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "login-verify-email.ftl",
|
||||
"user": {
|
||||
"email": "john.doe@gmail.com",
|
||||
},
|
||||
}),
|
||||
id<KcContextBase.Terms>({
|
||||
...kcContextCommonMock,
|
||||
@ -408,4 +411,17 @@ export const kcContextMocks: KcContextBase[] = [
|
||||
},
|
||||
},
|
||||
}),
|
||||
id<KcContextBase.LogoutConfirm>({
|
||||
...kcContextCommonMock,
|
||||
"pageId": "logout-confirm.ftl",
|
||||
"url": {
|
||||
...kcContextCommonMock.url,
|
||||
"logoutConfirmAction": "Continuer?",
|
||||
},
|
||||
"client": {
|
||||
"clientId": "myApp",
|
||||
"baseUrl": "#",
|
||||
},
|
||||
"logoutConfirm": { "code": "123", skipLink: false },
|
||||
}),
|
||||
];
|
||||
|
@ -20,11 +20,14 @@ export const kcMessages = {
|
||||
"fr": {
|
||||
...kcMessagesBase["fr"],
|
||||
/* spell-checker: disable */
|
||||
"shouldBeEqual": "{0} doit être egale à {1}",
|
||||
"shouldBeEqual": "{0} doit être égal à {1}",
|
||||
"shouldBeDifferent": "{0} doit être différent de {1}",
|
||||
"shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
|
||||
"mustBeAnInteger": "Doit être un nombre entiers",
|
||||
"mustBeAnInteger": "Doit être un nombre entier",
|
||||
"notAValidOption": "N'est pas une option valide",
|
||||
"logoutConfirmTitle": "Déconnexion",
|
||||
"logoutConfirmHeader": "Êtes-vous sûr(e) de vouloir vous déconnecter ?",
|
||||
"doLogout": "Se déconnecter",
|
||||
/* spell-checker: enable */
|
||||
},
|
||||
};
|
||||
|
18
yarn.lock
18
yarn.lock
@ -777,10 +777,10 @@ event-emitter@^0.3.5:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
evt@2.0.0-beta.44:
|
||||
version "2.0.0-beta.44"
|
||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.44.tgz#7b936e65b3a8c2536e21f1c2af9e85fb95cc32ec"
|
||||
integrity sha512-2XKKP3UhwLY1jOe8Ta0iL44ZZCy8iG1fGRHeyuU8W/aRcpcKy0PFi/FkVhaaI3tkWjqqfNJQ0wRgDovNP6N3KQ==
|
||||
evt@2.0.0-beta.45:
|
||||
version "2.0.0-beta.45"
|
||||
resolved "https://registry.yarnpkg.com/evt/-/evt-2.0.0-beta.45.tgz#8b1a81e1376f6eba254f4ec9865531b2ed118a3e"
|
||||
integrity sha512-Me2/YMCjxLgz4y0aWKIxykyJth2LHKqxc91XnsIN39IRksknObBR9Pjmxwpnhmd1lNCR6mPFv/vBt46iQWtI9g==
|
||||
dependencies:
|
||||
minimal-polyfills "^2.2.1"
|
||||
run-exclusive "^2.2.14"
|
||||
@ -1420,12 +1420,12 @@ please-upgrade-node@^3.2.0:
|
||||
dependencies:
|
||||
semver-compare "^1.0.0"
|
||||
|
||||
powerhooks@^0.20.1:
|
||||
version "0.20.1"
|
||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.1.tgz#54e3b391c6037bc39baa9bbf6e94dd8ebbe54126"
|
||||
integrity sha512-f8TmfKEUqpmcS4xsWPGeKuF3ybXR4eHjiOki1Ww/JFiCn1nLr3YkpGKvdF7+WkStv796WqSOi4kqeRN71mLt5g==
|
||||
powerhooks@^0.20.3:
|
||||
version "0.20.3"
|
||||
resolved "https://registry.yarnpkg.com/powerhooks/-/powerhooks-0.20.3.tgz#0a2bb6ac3d6bae527df5889e107f76c7ccca8104"
|
||||
integrity sha512-SrcDdt+Lo+TGtJHRLyrWS6glN3bfjBZETU+LIWWD5ohvwR8ofBcMiPML/sqecPhF+9sRzAdOrimHcQSQxetvSg==
|
||||
dependencies:
|
||||
evt "2.0.0-beta.44"
|
||||
evt "2.0.0-beta.45"
|
||||
memoizee "^0.4.15"
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
tsafe "^0.10.0"
|
||||
|
Reference in New Issue
Block a user