keycloakify 10.0.0-rc.23 → 10.0.0-rc.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLIC_URL.d.ts +1 -1
- package/PUBLIC_URL.js +1 -1
- package/PUBLIC_URL.js.map +1 -1
- package/account/Fallback.js.map +1 -1
- package/account/Template.js +3 -3
- package/account/Template.js.map +1 -1
- package/account/i18n/baseMessages/index.js.map +1 -1
- package/account/i18n/i18n.js.map +1 -1
- package/account/index.d.ts +2 -2
- package/account/index.js +1 -2
- package/account/index.js.map +1 -1
- package/account/kcContext/KcContext.d.ts +12 -0
- package/account/kcContext/KcContext.js.map +1 -1
- package/account/kcContext/getKcContextMock.d.ts +24 -0
- package/account/kcContext/getKcContextMock.js +28 -0
- package/account/kcContext/getKcContextMock.js.map +1 -0
- package/account/kcContext/index.d.ts +2 -1
- package/account/kcContext/index.js +1 -1
- package/account/kcContext/index.js.map +1 -1
- package/account/kcContext/kcContextMocks.js +23 -91
- package/account/kcContext/kcContextMocks.js.map +1 -1
- package/account/pages/Account.js +1 -1
- package/account/pages/Account.js.map +1 -1
- package/account/pages/Applications.js +2 -2
- package/account/pages/Applications.js.map +1 -1
- package/account/pages/FederatedIdentity.js +1 -1
- package/account/pages/FederatedIdentity.js.map +1 -1
- package/account/pages/Log.js +1 -1
- package/account/pages/Log.js.map +1 -1
- package/account/pages/Password.js +6 -4
- package/account/pages/Password.js.map +1 -1
- package/account/pages/Sessions.js +1 -1
- package/account/pages/Sessions.js.map +1 -1
- package/account/pages/Totp.js +1 -1
- package/account/pages/Totp.js.map +1 -1
- package/bin/main.js +152 -162
- package/lib/BASE_URL.js.map +1 -1
- package/login/Fallback.js.map +1 -1
- package/login/Template.js +7 -7
- package/login/Template.js.map +1 -1
- package/login/UserProfileFormFields.js +24 -68
- package/login/UserProfileFormFields.js.map +1 -1
- package/login/i18n/baseMessages/index.js.map +1 -1
- package/login/i18n/i18n.js.map +1 -1
- package/login/index.d.ts +2 -3
- package/login/index.js +1 -2
- package/login/index.js.map +1 -1
- package/login/kcContext/KcContext.d.ts +23 -12
- package/login/kcContext/KcContext.js.map +1 -1
- package/login/kcContext/getKcContextMock.d.ts +24 -0
- package/login/kcContext/getKcContextMock.js +28 -0
- package/login/kcContext/getKcContextMock.js.map +1 -0
- package/login/kcContext/index.d.ts +2 -1
- package/login/kcContext/index.js +1 -1
- package/login/kcContext/index.js.map +1 -1
- package/login/kcContext/kcContextMocks.js +32 -99
- package/login/kcContext/kcContextMocks.js.map +1 -1
- package/login/lib/useDownloadTerms.js +8 -14
- package/login/lib/useDownloadTerms.js.map +1 -1
- package/login/lib/useGetClassName.js +1 -1
- package/login/lib/useGetClassName.js.map +1 -1
- package/login/lib/useUserProfileForm.d.ts +9 -1
- package/login/lib/useUserProfileForm.js +94 -15
- package/login/lib/useUserProfileForm.js.map +1 -1
- package/login/pages/Code.js +1 -1
- package/login/pages/Code.js.map +1 -1
- package/login/pages/DeleteAccountConfirm.js +2 -2
- package/login/pages/DeleteAccountConfirm.js.map +1 -1
- package/login/pages/DeleteCredential.js +1 -1
- package/login/pages/DeleteCredential.js.map +1 -1
- package/login/pages/Error.js +1 -1
- package/login/pages/Error.js.map +1 -1
- package/login/pages/FrontchannelLogout.js +1 -1
- package/login/pages/FrontchannelLogout.js.map +1 -1
- package/login/pages/IdpReviewUserProfile.js +1 -1
- package/login/pages/IdpReviewUserProfile.js.map +1 -1
- package/login/pages/Info.js +5 -5
- package/login/pages/Info.js.map +1 -1
- package/login/pages/Login.js +4 -4
- package/login/pages/Login.js.map +1 -1
- package/login/pages/LoginConfigTotp.js +2 -2
- package/login/pages/LoginConfigTotp.js.map +1 -1
- package/login/pages/LoginIdpLinkConfirm.js +1 -1
- package/login/pages/LoginIdpLinkConfirm.js.map +1 -1
- package/login/pages/LoginIdpLinkEmail.js +1 -1
- package/login/pages/LoginIdpLinkEmail.js.map +1 -1
- package/login/pages/LoginOauth2DeviceVerifyUserCode.js +1 -1
- package/login/pages/LoginOauth2DeviceVerifyUserCode.js.map +1 -1
- package/login/pages/LoginOauthGrant.js +2 -2
- package/login/pages/LoginOauthGrant.js.map +1 -1
- package/login/pages/LoginOtp.js +1 -1
- package/login/pages/LoginOtp.js.map +1 -1
- package/login/pages/LoginPageExpired.js +1 -1
- package/login/pages/LoginPageExpired.js.map +1 -1
- package/login/pages/LoginPassword.js +3 -3
- package/login/pages/LoginPassword.js.map +1 -1
- package/login/pages/LoginRecoveryAuthnCodeConfig.js +10 -10
- package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
- package/login/pages/LoginRecoveryAuthnCodeInput.js +1 -1
- package/login/pages/LoginRecoveryAuthnCodeInput.js.map +1 -1
- package/login/pages/LoginResetOtp.js +1 -1
- package/login/pages/LoginResetOtp.js.map +1 -1
- package/login/pages/LoginResetPassword.js +2 -2
- package/login/pages/LoginResetPassword.js.map +1 -1
- package/login/pages/LoginUpdatePassword.js +3 -3
- package/login/pages/LoginUpdatePassword.js.map +1 -1
- package/login/pages/LoginUpdateProfile.js +4 -2
- package/login/pages/LoginUpdateProfile.js.map +1 -1
- package/login/pages/LoginUsername.js +3 -3
- package/login/pages/LoginUsername.js.map +1 -1
- package/login/pages/LoginVerifyEmail.js +1 -1
- package/login/pages/LoginVerifyEmail.js.map +1 -1
- package/login/pages/LoginX509Info.js +1 -1
- package/login/pages/LoginX509Info.js.map +1 -1
- package/login/pages/LogoutConfirm.js +1 -1
- package/login/pages/LogoutConfirm.js.map +1 -1
- package/login/pages/Register.js +8 -4
- package/login/pages/Register.js.map +1 -1
- package/login/pages/SamlPostForm.js +1 -1
- package/login/pages/SamlPostForm.js.map +1 -1
- package/login/pages/SelectAuthenticator.js +2 -2
- package/login/pages/SelectAuthenticator.js.map +1 -1
- package/login/pages/Terms.js +1 -1
- package/login/pages/Terms.js.map +1 -1
- package/login/pages/UpdateEmail.js +5 -3
- package/login/pages/UpdateEmail.js.map +1 -1
- package/login/pages/WebauthnAuthenticate.js +8 -8
- package/login/pages/WebauthnAuthenticate.js.map +1 -1
- package/login/pages/WebauthnError.js +2 -2
- package/login/pages/WebauthnError.js.map +1 -1
- package/login/pages/WebauthnRegister.js +5 -5
- package/login/pages/WebauthnRegister.js.map +1 -1
- package/package.json +26 -38
- package/src/PUBLIC_URL.ts +1 -1
- package/src/account/Template.tsx +2 -3
- package/src/account/index.ts +2 -2
- package/src/account/kcContext/KcContext.ts +19 -1
- package/src/account/kcContext/getKcContextMock.ts +80 -0
- package/src/account/kcContext/index.ts +2 -1
- package/src/account/kcContext/kcContextMocks.ts +26 -91
- package/src/bin/copy-keycloak-resources-to-public.ts +1 -4
- package/src/bin/keycloakify/generateFtl/ftl_object_to_js_code_declaring_an_object.ftl +4 -6
- package/src/bin/tools/getNpmWorkspaceRootDirPath.ts +25 -25
- package/src/login/Template.tsx +4 -5
- package/src/login/UserProfileFormFields.tsx +28 -80
- package/src/login/index.ts +6 -3
- package/src/login/kcContext/KcContext.ts +41 -27
- package/src/login/kcContext/getKcContextMock.ts +80 -0
- package/src/login/kcContext/index.ts +7 -1
- package/src/login/kcContext/kcContextMocks.ts +95 -156
- package/src/login/lib/useDownloadTerms.ts +10 -24
- package/src/login/lib/useGetClassName.ts +1 -1
- package/src/login/lib/useUserProfileForm.tsx +117 -13
- package/src/login/pages/LoginConfigTotp.tsx +1 -1
- package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +7 -8
- package/src/login/pages/WebauthnAuthenticate.tsx +2 -3
- package/src/login/pages/WebauthnRegister.tsx +2 -3
- package/src/tools/ExtractAfterStartingWith.ts +4 -0
- package/src/tools/StatefulObservable/hooks/useRerenderOnChange.ts +4 -4
- package/src/tools/ValueOf.ts +2 -0
- package/src/tools/deepAssign.ts +51 -20
- package/src/tools/structuredCloneButFunctions.ts +24 -0
- package/src/tools/useInsertLinkTags.ts +78 -87
- package/src/tools/useInsertScriptTags.ts +69 -78
- package/src/tools/useOnFirstMount.ts +18 -0
- package/tools/Array.prototype.every.js.map +1 -1
- package/tools/ExtractAfterStartingWith.d.ts +1 -0
- package/tools/ExtractAfterStartingWith.js +2 -0
- package/tools/ExtractAfterStartingWith.js.map +1 -0
- package/tools/HTMLElement.prototype.prepend.js.map +1 -1
- package/tools/StatefulObservable/StatefulObservable.js.map +1 -1
- package/tools/StatefulObservable/hooks/useRerenderOnChange.d.ts +1 -1
- package/tools/StatefulObservable/hooks/useRerenderOnChange.js +4 -4
- package/tools/StatefulObservable/hooks/useRerenderOnChange.js.map +1 -1
- package/tools/ValueOf.d.ts +2 -0
- package/tools/ValueOf.js +2 -0
- package/tools/ValueOf.js.map +1 -0
- package/tools/clsx.js.map +1 -1
- package/tools/deepAssign.d.ts +1 -0
- package/tools/deepAssign.js +39 -16
- package/tools/deepAssign.js.map +1 -1
- package/tools/formatNumber.js.map +1 -1
- package/tools/structuredCloneButFunctions.d.ts +7 -0
- package/tools/structuredCloneButFunctions.js +19 -0
- package/tools/structuredCloneButFunctions.js.map +1 -0
- package/tools/useInsertLinkTags.d.ts +11 -6
- package/tools/useInsertLinkTags.js +53 -53
- package/tools/useInsertLinkTags.js.map +1 -1
- package/tools/useInsertScriptTags.d.ts +15 -6
- package/tools/useInsertScriptTags.js +56 -64
- package/tools/useInsertScriptTags.js.map +1 -1
- package/tools/useOnFirstMount.d.ts +2 -0
- package/tools/useOnFirstMount.js +15 -0
- package/tools/useOnFirstMount.js.map +1 -0
- package/tools/useSetClassName.js.map +1 -1
- package/vite-plugin/index.js +66 -64
- package/account/kcContext/createGetKcContext.d.ts +0 -19
- package/account/kcContext/createGetKcContext.js +0 -78
- package/account/kcContext/createGetKcContext.js.map +0 -1
- package/account/kcContext/getKcContext.d.ts +0 -13
- package/account/kcContext/getKcContext.js +0 -13
- package/account/kcContext/getKcContext.js.map +0 -1
- package/account/kcContext/getKcContextFromWindow.d.ts +0 -10
- package/account/kcContext/getKcContextFromWindow.js +0 -5
- package/account/kcContext/getKcContextFromWindow.js.map +0 -1
- package/login/kcContext/createGetKcContext.d.ts +0 -19
- package/login/kcContext/createGetKcContext.js +0 -114
- package/login/kcContext/createGetKcContext.js.map +0 -1
- package/login/kcContext/getKcContext.d.ts +0 -13
- package/login/kcContext/getKcContext.js +0 -13
- package/login/kcContext/getKcContext.js.map +0 -1
- package/login/kcContext/getKcContextFromWindow.d.ts +0 -10
- package/login/kcContext/getKcContextFromWindow.js +0 -5
- package/login/kcContext/getKcContextFromWindow.js.map +0 -1
- package/src/account/kcContext/createGetKcContext.ts +0 -134
- package/src/account/kcContext/getKcContext.ts +0 -23
- package/src/account/kcContext/getKcContextFromWindow.ts +0 -15
- package/src/login/kcContext/createGetKcContext.ts +0 -199
- package/src/login/kcContext/getKcContext.ts +0 -23
- package/src/login/kcContext/getKcContextFromWindow.ts +0 -15
- package/src/tools/AndByDiscriminatingKey.ts +0 -31
- package/src/tools/deepClone.ts +0 -19
- package/src/tools/memoize.ts +0 -55
- package/tools/AndByDiscriminatingKey.d.ts +0 -5
- package/tools/AndByDiscriminatingKey.js +0 -2
- package/tools/AndByDiscriminatingKey.js.map +0 -1
- package/tools/deepClone.d.ts +0 -2
- package/tools/deepClone.js +0 -14
- package/tools/deepClone.js.map +0 -1
- package/tools/memoize.d.ts +0 -7
- package/tools/memoize.js +0 -38
- package/tools/memoize.js.map +0 -1
@@ -1,14 +1,11 @@
|
|
1
|
-
import { useEffect } from "react";
|
2
|
-
import { memoize } from "keycloakify/tools/memoize";
|
3
1
|
import { fallbackLanguageTag } from "keycloakify/login/i18n/i18n";
|
4
|
-
import { useConst } from "keycloakify/tools/useConst";
|
5
|
-
import { useConstCallback } from "keycloakify/tools/useConstCallback";
|
6
2
|
import { assert } from "tsafe/assert";
|
7
3
|
import {
|
8
4
|
createStatefulObservable,
|
9
5
|
useRerenderOnChange
|
10
6
|
} from "keycloakify/tools/StatefulObservable";
|
11
7
|
import { KcContext } from "../kcContext";
|
8
|
+
import { useOnFistMount } from "keycloakify/tools/useOnFirstMount";
|
12
9
|
|
13
10
|
const obsTermsMarkdown = createStatefulObservable<string | undefined>(() => undefined);
|
14
11
|
|
@@ -27,29 +24,18 @@ export function useDownloadTerms(params: {
|
|
27
24
|
kcContext: KcContextLike;
|
28
25
|
downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string>;
|
29
26
|
}) {
|
30
|
-
const { kcContext } = params;
|
27
|
+
const { kcContext, downloadTermMarkdown } = params;
|
31
28
|
|
32
|
-
|
33
|
-
const { downloadTermMarkdown } = params;
|
34
|
-
|
35
|
-
const downloadTermMarkdownConst = useConstCallback(downloadTermMarkdown);
|
36
|
-
|
37
|
-
const downloadTermMarkdownMemoized = useConst(() =>
|
38
|
-
memoize((currentLanguageTag: string) =>
|
39
|
-
downloadTermMarkdownConst({ currentLanguageTag })
|
40
|
-
)
|
41
|
-
);
|
42
|
-
|
43
|
-
return { downloadTermMarkdownMemoized };
|
44
|
-
})();
|
45
|
-
|
46
|
-
useEffect(() => {
|
29
|
+
useOnFistMount(async () => {
|
47
30
|
if (kcContext.pageId === "terms.ftl" || kcContext.termsAcceptanceRequired) {
|
48
|
-
|
49
|
-
|
50
|
-
|
31
|
+
const termsMarkdown = await downloadTermMarkdown({
|
32
|
+
currentLanguageTag:
|
33
|
+
kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag
|
34
|
+
});
|
35
|
+
|
36
|
+
obsTermsMarkdown.current = termsMarkdown;
|
51
37
|
}
|
52
|
-
}
|
38
|
+
});
|
53
39
|
}
|
54
40
|
|
55
41
|
export function useTermsMarkdown() {
|
@@ -3,6 +3,7 @@ import type { ClassKey } from "keycloakify/login/TemplateProps";
|
|
3
3
|
|
4
4
|
export const { useGetClassName } = createUseClassName<ClassKey>({
|
5
5
|
defaultClasses: {
|
6
|
+
kcHtmlClass: "login-pf",
|
6
7
|
kcBodyClass: undefined,
|
7
8
|
kcHeaderWrapperClass: undefined,
|
8
9
|
kcLocaleWrapperClass: undefined,
|
@@ -54,7 +55,6 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
|
|
54
55
|
kcLogoLink: "http://www.keycloak.org",
|
55
56
|
kcContainerClass: "container-fluid",
|
56
57
|
kcSelectAuthListItemTitle: "select-auth-box-paragraph",
|
57
|
-
kcHtmlClass: "login-pf",
|
58
58
|
kcLoginOTPListItemTitleClass: "pf-c-tile__title",
|
59
59
|
"kcLogoIdP-openshift-v4": "pf-icon pf-icon-openshift",
|
60
60
|
kcWebAuthnUnknownIcon: "pficon pficon-key unknown-transport-class",
|
@@ -8,7 +8,8 @@ import { emailRegexp } from "keycloakify/tools/emailRegExp";
|
|
8
8
|
import type { KcContext, PasswordPolicies } from "keycloakify/login/kcContext/KcContext";
|
9
9
|
import { assert, type Equals } from "tsafe/assert";
|
10
10
|
import { formatNumber } from "keycloakify/tools/formatNumber";
|
11
|
-
import {
|
11
|
+
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
|
12
|
+
import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
|
12
13
|
import type { I18n } from "../i18n";
|
13
14
|
|
14
15
|
export type FormFieldError = {
|
@@ -67,7 +68,7 @@ export type FormAction =
|
|
67
68
|
export type KcContextLike = {
|
68
69
|
messagesPerField: Pick<KcContext.Common["messagesPerField"], "existsError" | "get">;
|
69
70
|
profile: {
|
70
|
-
|
71
|
+
attributesByName: Record<string, Attribute>;
|
71
72
|
html5DataAnnotations?: Record<string, string>;
|
72
73
|
};
|
73
74
|
passwordRequired?: boolean;
|
@@ -102,12 +103,11 @@ namespace internal {
|
|
102
103
|
};
|
103
104
|
}
|
104
105
|
|
105
|
-
const { useInsertScriptTags } = createUseInsertScriptTags();
|
106
|
-
|
107
106
|
export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTypeOfUseUserProfileForm {
|
108
107
|
const { kcContext, i18n, doMakeUserConfirmPassword } = params;
|
109
108
|
|
110
109
|
const { insertScriptTags } = useInsertScriptTags({
|
110
|
+
componentOrHookName: "useUserProfileForm",
|
111
111
|
scriptTags: Object.keys(kcContext.profile?.html5DataAnnotations ?? {})
|
112
112
|
.filter(key => key !== "kcMultivalued" && key !== "kcNumberFormat") // NOTE: Keycloakify handles it.
|
113
113
|
.map(key => ({
|
@@ -136,7 +136,11 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
136
136
|
|
137
137
|
const attributes = (() => {
|
138
138
|
retrocompat_patch: {
|
139
|
-
if (
|
139
|
+
if (
|
140
|
+
"profile" in kcContext &&
|
141
|
+
"attributesByName" in kcContext.profile &&
|
142
|
+
Object.keys(kcContext.profile.attributesByName).length !== 0
|
143
|
+
) {
|
140
144
|
break retrocompat_patch;
|
141
145
|
}
|
142
146
|
|
@@ -216,7 +220,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
216
220
|
assert(false, "Unable to mock user profile from the current kcContext");
|
217
221
|
}
|
218
222
|
|
219
|
-
return kcContext.profile.
|
223
|
+
return Object.values(kcContext.profile.attributesByName).map(attribute_pre_group_patch => {
|
220
224
|
if (typeof attribute_pre_group_patch.group === "string" && attribute_pre_group_patch.group !== "") {
|
221
225
|
const { group, groupDisplayHeader, groupDisplayDescription, groupAnnotations, ...rest } =
|
222
226
|
attribute_pre_group_patch as Attribute & {
|
@@ -242,7 +246,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
242
246
|
})();
|
243
247
|
|
244
248
|
for (const attribute of attributes) {
|
245
|
-
syntheticAttributes.push(attribute);
|
249
|
+
syntheticAttributes.push(structuredCloneButFunctions(attribute));
|
246
250
|
|
247
251
|
add_password_and_password_confirm: {
|
248
252
|
if (!kcContext.passwordRequired) {
|
@@ -284,6 +288,21 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
284
288
|
}
|
285
289
|
}
|
286
290
|
|
291
|
+
// NOTE: Consistency patch
|
292
|
+
syntheticAttributes.forEach(attribute => {
|
293
|
+
if (getIsMultivaluedSingleField({ attribute })) {
|
294
|
+
attribute.multivalued = true;
|
295
|
+
}
|
296
|
+
|
297
|
+
if (attribute.multivalued) {
|
298
|
+
attribute.values ??= attribute.value !== undefined ? [attribute.value] : [];
|
299
|
+
delete attribute.value;
|
300
|
+
} else {
|
301
|
+
attribute.value ??= attribute.values?.[0];
|
302
|
+
delete attribute.values;
|
303
|
+
}
|
304
|
+
});
|
305
|
+
|
287
306
|
return syntheticAttributes;
|
288
307
|
})();
|
289
308
|
|
@@ -299,10 +318,10 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
299
318
|
break handle_multi_valued_attribute;
|
300
319
|
}
|
301
320
|
|
302
|
-
const values = attribute.values
|
321
|
+
const values = attribute.values?.length ? attribute.values : [""];
|
303
322
|
|
304
323
|
apply_validator_min_range: {
|
305
|
-
if (attribute
|
324
|
+
if (getIsMultivaluedSingleField({ attribute })) {
|
306
325
|
break apply_validator_min_range;
|
307
326
|
}
|
308
327
|
|
@@ -349,7 +368,8 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
349
368
|
attributeName: attribute.name,
|
350
369
|
formFieldStates: initialFormFieldState
|
351
370
|
}),
|
352
|
-
hasLostFocusAtLeastOnce:
|
371
|
+
hasLostFocusAtLeastOnce:
|
372
|
+
valueOrValues instanceof Array && !getIsMultivaluedSingleField({ attribute }) ? valueOrValues.map(() => false) : false,
|
353
373
|
valueOrValues: valueOrValues
|
354
374
|
}))
|
355
375
|
};
|
@@ -543,7 +563,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
543
563
|
|
544
564
|
server_side_error: {
|
545
565
|
if (attribute.multivalued) {
|
546
|
-
const defaultValues = attribute.values
|
566
|
+
const defaultValues = attribute.values?.length ? attribute.values : [""];
|
547
567
|
|
548
568
|
assert(valueOrValues instanceof Array);
|
549
569
|
|
@@ -595,7 +615,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
595
615
|
break handle_multi_valued_multi_fields;
|
596
616
|
}
|
597
617
|
|
598
|
-
if (attribute
|
618
|
+
if (getIsMultivaluedSingleField({ attribute })) {
|
599
619
|
break handle_multi_valued_multi_fields;
|
600
620
|
}
|
601
621
|
|
@@ -674,7 +694,7 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
674
694
|
break handle_multi_valued_single_field;
|
675
695
|
}
|
676
696
|
|
677
|
-
if (!attribute
|
697
|
+
if (!getIsMultivaluedSingleField({ attribute })) {
|
678
698
|
break handle_multi_valued_single_field;
|
679
699
|
}
|
680
700
|
|
@@ -913,6 +933,10 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
913
933
|
return valueOrValues;
|
914
934
|
})();
|
915
935
|
|
936
|
+
if (usernameValue === "") {
|
937
|
+
break check_password_policy_x;
|
938
|
+
}
|
939
|
+
|
916
940
|
if (value !== usernameValue) {
|
917
941
|
break check_password_policy_x;
|
918
942
|
}
|
@@ -950,6 +974,10 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
950
974
|
{
|
951
975
|
const emailValue = emailFormFieldState.valueOrValues;
|
952
976
|
|
977
|
+
if (emailValue === "") {
|
978
|
+
break check_password_policy_x;
|
979
|
+
}
|
980
|
+
|
953
981
|
if (value !== emailValue) {
|
954
982
|
break check_password_policy_x;
|
955
983
|
}
|
@@ -1239,3 +1267,79 @@ function useGetErrors(params: { kcContext: Pick<KcContextLike, "messagesPerField
|
|
1239
1267
|
|
1240
1268
|
return { getErrors };
|
1241
1269
|
}
|
1270
|
+
|
1271
|
+
function getIsMultivaluedSingleField(params: { attribute: Attribute }) {
|
1272
|
+
const { attribute } = params;
|
1273
|
+
|
1274
|
+
return attribute.annotations.inputType?.startsWith("multiselect") ?? false;
|
1275
|
+
}
|
1276
|
+
|
1277
|
+
export function getButtonToDisplayForMultivaluedAttributeField(params: { attribute: Attribute; values: string[]; fieldIndex: number }) {
|
1278
|
+
const { attribute, values, fieldIndex } = params;
|
1279
|
+
|
1280
|
+
const hasRemove = (() => {
|
1281
|
+
if (values.length === 1) {
|
1282
|
+
return false;
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
const minCount = (() => {
|
1286
|
+
const { multivalued } = attribute.validators;
|
1287
|
+
|
1288
|
+
if (multivalued === undefined) {
|
1289
|
+
return undefined;
|
1290
|
+
}
|
1291
|
+
|
1292
|
+
const minStr = multivalued.min;
|
1293
|
+
|
1294
|
+
if (minStr === undefined) {
|
1295
|
+
return undefined;
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
return parseInt(`${minStr}`);
|
1299
|
+
})();
|
1300
|
+
|
1301
|
+
if (minCount === undefined) {
|
1302
|
+
return true;
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
if (values.length === minCount) {
|
1306
|
+
return false;
|
1307
|
+
}
|
1308
|
+
|
1309
|
+
return true;
|
1310
|
+
})();
|
1311
|
+
|
1312
|
+
const hasAdd = (() => {
|
1313
|
+
if (fieldIndex + 1 !== values.length) {
|
1314
|
+
return false;
|
1315
|
+
}
|
1316
|
+
|
1317
|
+
const maxCount = (() => {
|
1318
|
+
const { multivalued } = attribute.validators;
|
1319
|
+
|
1320
|
+
if (multivalued === undefined) {
|
1321
|
+
return undefined;
|
1322
|
+
}
|
1323
|
+
|
1324
|
+
const maxStr = multivalued.max;
|
1325
|
+
|
1326
|
+
if (maxStr === undefined) {
|
1327
|
+
return undefined;
|
1328
|
+
}
|
1329
|
+
|
1330
|
+
return parseInt(`${maxStr}`);
|
1331
|
+
})();
|
1332
|
+
|
1333
|
+
if (maxCount === undefined) {
|
1334
|
+
return false;
|
1335
|
+
}
|
1336
|
+
|
1337
|
+
if (values.length === maxCount) {
|
1338
|
+
return false;
|
1339
|
+
}
|
1340
|
+
|
1341
|
+
return true;
|
1342
|
+
})();
|
1343
|
+
|
1344
|
+
return { hasRemove, hasAdd };
|
1345
|
+
}
|
@@ -25,7 +25,7 @@ export default function LoginConfigTotp(props: PageProps<Extract<KcContext, { pa
|
|
25
25
|
|
26
26
|
<ul id="kc-totp-supported-apps">
|
27
27
|
{totp.supportedApplications.map(app => (
|
28
|
-
<li>{advancedMsg(app)}</li>
|
28
|
+
<li key={app}>{advancedMsg(app)}</li>
|
29
29
|
))}
|
30
30
|
</ul>
|
31
31
|
</li>
|
@@ -2,12 +2,10 @@ import { useEffect } from "react";
|
|
2
2
|
import { clsx } from "keycloakify/tools/clsx";
|
3
3
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
4
4
|
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
5
|
-
import {
|
5
|
+
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
|
6
6
|
import type { KcContext } from "../kcContext";
|
7
7
|
import type { I18n } from "../i18n";
|
8
8
|
|
9
|
-
const { useInsertScriptTags } = createUseInsertScriptTags();
|
10
|
-
|
11
9
|
export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<KcContext, { pageId: "login-recovery-authn-code-config.ftl" }>, I18n>) {
|
12
10
|
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
13
11
|
|
@@ -21,6 +19,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|
21
19
|
const { msg, msgStr } = i18n;
|
22
20
|
|
23
21
|
const { insertScriptTags } = useInsertScriptTags({
|
22
|
+
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
|
24
23
|
scriptTags: [
|
25
24
|
{
|
26
25
|
type: "text/javascript",
|
@@ -31,7 +30,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|
31
30
|
var tmpTextarea = document.createElement("textarea");
|
32
31
|
var codes = document.getElementById("kc-recovery-codes-list").getElementsByTagName("li");
|
33
32
|
for (i = 0; i < codes.length; i++) {
|
34
|
-
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "
|
33
|
+
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
|
35
34
|
}
|
36
35
|
document.body.appendChild(tmpTextarea);
|
37
36
|
tmpTextarea.select();
|
@@ -65,7 +64,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|
65
64
|
|
66
65
|
for (var i = 0; i < recoveryCodes.length; i++) {
|
67
66
|
var recoveryCodeLiElement = recoveryCodes[i].innerText;
|
68
|
-
recoveryCodeList += recoveryCodeLiElement + "
|
67
|
+
recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
|
69
68
|
}
|
70
69
|
|
71
70
|
return recoveryCodeList;
|
@@ -84,9 +83,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|
84
83
|
};
|
85
84
|
|
86
85
|
return fileBodyContent =
|
87
|
-
"${msgStr("recovery-codes-download-file-header")}
|
88
|
-
recoveryCodeList + "
|
89
|
-
"${msgStr("recovery-codes-download-file-description")}
|
86
|
+
"${msgStr("recovery-codes-download-file-header")}\\n\\n" +
|
87
|
+
recoveryCodeList + "\\n" +
|
88
|
+
"${msgStr("recovery-codes-download-file-description")}\\n\\n" +
|
90
89
|
"${msgStr("recovery-codes-download-file-date")} " + formatCurrentDateTime();
|
91
90
|
}
|
92
91
|
|
@@ -3,12 +3,10 @@ import { clsx } from "keycloakify/tools/clsx";
|
|
3
3
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
4
4
|
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
5
5
|
import { assert } from "tsafe/assert";
|
6
|
-
import {
|
6
|
+
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
|
7
7
|
import type { KcContext } from "../kcContext";
|
8
8
|
import type { I18n } from "../i18n";
|
9
9
|
|
10
|
-
const { useInsertScriptTags } = createUseInsertScriptTags();
|
11
|
-
|
12
10
|
export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext, { pageId: "webauthn-authenticate.ftl" }>, I18n>) {
|
13
11
|
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
14
12
|
|
@@ -31,6 +29,7 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
|
31
29
|
const { msg, msgStr, advancedMsg } = i18n;
|
32
30
|
|
33
31
|
const { insertScriptTags } = useInsertScriptTags({
|
32
|
+
componentOrHookName: "WebauthnAuthenticate",
|
34
33
|
scriptTags: [
|
35
34
|
{
|
36
35
|
type: "text/javascript",
|
@@ -3,12 +3,10 @@ import { clsx } from "keycloakify/tools/clsx";
|
|
3
3
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
4
4
|
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
5
5
|
import { assert } from "tsafe/assert";
|
6
|
-
import {
|
6
|
+
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
|
7
7
|
import type { KcContext } from "../kcContext";
|
8
8
|
import type { I18n } from "../i18n";
|
9
9
|
|
10
|
-
const { useInsertScriptTags } = createUseInsertScriptTags();
|
11
|
-
|
12
10
|
export default function WebauthnRegister(props: PageProps<Extract<KcContext, { pageId: "webauthn-register.ftl" }>, I18n>) {
|
13
11
|
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
14
12
|
|
@@ -35,6 +33,7 @@ export default function WebauthnRegister(props: PageProps<Extract<KcContext, { p
|
|
35
33
|
const { msg, msgStr } = i18n;
|
36
34
|
|
37
35
|
const { insertScriptTags } = useInsertScriptTags({
|
36
|
+
componentOrHookName: "WebauthnRegister",
|
38
37
|
scriptTags: [
|
39
38
|
{
|
40
39
|
type: "text/javascript",
|
@@ -5,15 +5,15 @@ import type { StatefulObservable } from "../StatefulObservable";
|
|
5
5
|
/**
|
6
6
|
* Equivalent of https://docs.evt.land/api/react-hooks
|
7
7
|
* */
|
8
|
-
export function useRerenderOnChange(
|
8
|
+
export function useRerenderOnChange(obs: StatefulObservable<unknown>): void {
|
9
9
|
//NOTE: We use function in case the state is a function
|
10
|
-
const [, setCurrent] = useState(() =>
|
10
|
+
const [, setCurrent] = useState(() => obs.current);
|
11
11
|
|
12
12
|
useObservable(
|
13
13
|
({ registerSubscription }) => {
|
14
|
-
const subscription =
|
14
|
+
const subscription = obs.subscribe(current => setCurrent(() => current));
|
15
15
|
registerSubscription(subscription);
|
16
16
|
},
|
17
|
-
[
|
17
|
+
[obs]
|
18
18
|
);
|
19
19
|
}
|
package/src/tools/deepAssign.ts
CHANGED
@@ -1,45 +1,61 @@
|
|
1
1
|
import { assert } from "tsafe/assert";
|
2
2
|
import { is } from "tsafe/is";
|
3
|
-
import {
|
3
|
+
import { structuredCloneButFunctions } from "./structuredCloneButFunctions";
|
4
4
|
|
5
|
-
|
5
|
+
/** NOTE: Array a copied over, not merged. */
|
6
6
|
export function deepAssign(params: {
|
7
7
|
target: Record<string, unknown>;
|
8
8
|
source: Record<string, unknown>;
|
9
|
-
}) {
|
10
|
-
const { target } = params;
|
11
|
-
|
12
|
-
const source = deepClone(params.source);
|
9
|
+
}): void {
|
10
|
+
const { target, source } = params;
|
13
11
|
|
14
12
|
Object.keys(source).forEach(key => {
|
15
13
|
var dereferencedSource = source[key];
|
16
14
|
|
15
|
+
if (dereferencedSource === undefined) {
|
16
|
+
delete target[key];
|
17
|
+
return;
|
18
|
+
}
|
19
|
+
|
20
|
+
if (dereferencedSource instanceof Date) {
|
21
|
+
assign({
|
22
|
+
target,
|
23
|
+
key,
|
24
|
+
value: new Date(dereferencedSource.getTime())
|
25
|
+
});
|
26
|
+
|
27
|
+
return;
|
28
|
+
}
|
29
|
+
|
30
|
+
if (dereferencedSource instanceof Array) {
|
31
|
+
assign({
|
32
|
+
target,
|
33
|
+
key,
|
34
|
+
value: structuredCloneButFunctions(dereferencedSource)
|
35
|
+
});
|
36
|
+
|
37
|
+
return;
|
38
|
+
}
|
39
|
+
|
17
40
|
if (
|
18
|
-
target[key] === undefined ||
|
19
41
|
dereferencedSource instanceof Function ||
|
20
42
|
!(dereferencedSource instanceof Object)
|
21
43
|
) {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
configurable: true,
|
44
|
+
assign({
|
45
|
+
target,
|
46
|
+
key,
|
26
47
|
value: dereferencedSource
|
27
48
|
});
|
28
49
|
|
29
50
|
return;
|
30
51
|
}
|
31
52
|
|
32
|
-
|
33
|
-
|
34
|
-
if (dereferencedSource instanceof Array) {
|
35
|
-
assert(is<unknown[]>(dereferencedTarget));
|
36
|
-
assert(is<unknown[]>(dereferencedSource));
|
37
|
-
|
38
|
-
dereferencedSource.forEach(entry => dereferencedTarget.push(entry));
|
39
|
-
|
40
|
-
return;
|
53
|
+
if (!(target[key] instanceof Object)) {
|
54
|
+
target[key] = {};
|
41
55
|
}
|
42
56
|
|
57
|
+
const dereferencedTarget = target[key];
|
58
|
+
|
43
59
|
assert(is<Record<string, unknown>>(dereferencedTarget));
|
44
60
|
assert(is<Record<string, unknown>>(dereferencedSource));
|
45
61
|
|
@@ -49,3 +65,18 @@ export function deepAssign(params: {
|
|
49
65
|
});
|
50
66
|
});
|
51
67
|
}
|
68
|
+
|
69
|
+
function assign(params: {
|
70
|
+
target: Record<string, unknown>;
|
71
|
+
key: string;
|
72
|
+
value: unknown;
|
73
|
+
}): void {
|
74
|
+
const { target, key, value } = params;
|
75
|
+
|
76
|
+
Object.defineProperty(target, key, {
|
77
|
+
enumerable: true,
|
78
|
+
writable: true,
|
79
|
+
configurable: true,
|
80
|
+
value
|
81
|
+
});
|
82
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import "minimal-polyfills/Object.fromEntries";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Functionally equivalent to structuredClone but
|
5
|
+
* functions are not cloned but kept as is.
|
6
|
+
* (as opposed to structuredClone that chokes if it encounters a function)
|
7
|
+
*/
|
8
|
+
export function structuredCloneButFunctions<T>(o: T): T {
|
9
|
+
if (!(o instanceof Object)) {
|
10
|
+
return o;
|
11
|
+
}
|
12
|
+
|
13
|
+
if (typeof o === "function") {
|
14
|
+
return o;
|
15
|
+
}
|
16
|
+
|
17
|
+
if (o instanceof Array) {
|
18
|
+
return o.map(structuredCloneButFunctions) as any;
|
19
|
+
}
|
20
|
+
|
21
|
+
return Object.fromEntries(
|
22
|
+
Object.entries(o).map(([key, value]) => [key, structuredCloneButFunctions(value)])
|
23
|
+
) as any;
|
24
|
+
}
|