From 92b2aac9e65657a7bb4c8cbd60b100bcb5957f53 Mon Sep 17 00:00:00 2001 From: rmartinc Date: Fri, 28 Mar 2025 12:39:51 +0100 Subject: [PATCH] Add locale attribute to the registration context Closes #38029 Signed-off-by: rmartinc --- ...DeclarativeUserProfileProviderFactory.java | 3 + .../testsuite/forms/RegisterTest.java | 28 ++++- .../forms/RegisterWithUserProfileTest.java | 2 +- .../theme/base/login/user-profile-commons.ftl | 102 +++++++++--------- .../login/user-profile-commons.ftl | 84 ++++++++------- 5 files changed, 128 insertions(+), 91 deletions(-) diff --git a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java index b9e0feb5e0e7..86323fbc4ce4 100644 --- a/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java +++ b/services/src/main/java/org/keycloak/userprofile/DeclarativeUserProfileProviderFactory.java @@ -389,6 +389,9 @@ private UserProfileMetadata createRegistrationUserCreationProfile(AttributeValid metadata.getAttribute(UserModel.EMAIL).get(0).addValidators(Collections.singletonList( new AttributeValidatorMetadata(RegistrationEmailAsUsernameEmailValueValidator.ID))); + metadata.addAttribute(UserModel.LOCALE, -1, DeclarativeUserProfileProviderFactory::isInternationalizationEnabled, DeclarativeUserProfileProviderFactory::isInternationalizationEnabled) + .setRequired(AttributeMetadata.ALWAYS_FALSE); + return metadata; } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java index 82cfa7f57cd1..a26868a104cf 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterTest.java @@ -61,6 +61,8 @@ import jakarta.ws.rs.core.Response; import java.io.IOException; +import java.util.List; +import java.util.Map; import java.util.UUID; import static org.hamcrest.MatcherAssert.assertThat; @@ -69,7 +71,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; /** @@ -381,7 +382,28 @@ public void registerUserSuccess() { UserRepresentation user = getUser(userId); - assertNull(user.getAttributes()); + assertEquals(Map.of(UserModel.LOCALE, List.of("en")), user.getAttributes()); + } + + @Test + public void registerUserChangedLocaleSuccess() { + oauth.openLoginForm(); + loginPage.assertCurrent(); + loginPage.clickRegister(); + registerPage.assertCurrent(); + errorPage.openLanguage("Deutsch"); + assertEquals("Deutsch", errorPage.getLanguageDropdownText()); + + registerPage.register("firstName", "lastName", "registerGerman@localhost", "registerGerman", "password", "password"); + + appPage.assertCurrent(); + assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); + + String userId = events.expectRegister("registerGerman", "registerGerman@localhost").assertEvent().getUserId(); + assertUserRegistered(userId, "registergerman", "registerGerman@localhost"); + + UserRepresentation user = getUser(userId); + assertEquals(Map.of(UserModel.LOCALE, List.of("de")), user.getAttributes()); } @Test @@ -820,7 +842,7 @@ public void registerUserSuccessTermsAcceptance() { String userId = events.expectRegister("registerUserSuccessTermsAcceptance", "registerUserSuccessTermsAcceptance@email") .assertEvent().getUserId(); UserRepresentation user = assertUserRegistered(userId, "registerUserSuccessTermsAcceptance", "registerUserSuccessTermsAcceptance@email"); - Assert.assertNull(user.getAttributes()); + assertEquals(Map.of(UserModel.LOCALE, List.of("en")), user.getAttributes()); } finally { configureRegistrationFlowWithCustomRegistrationPageForm(UUID.randomUUID().toString()); } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java index 2d5340f0a379..1391e2d026b8 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/RegisterWithUserProfileTest.java @@ -290,7 +290,7 @@ public void testAttributeGuiOrder() { //assert fields location in form List element = driver.findElements(By.cssSelector("form#kc-register-form input")); - String[] labelOrder = new String[]{"lastName", "department", "username", "password", "password-confirm", "firstName", "email"}; + String[] labelOrder = new String[]{"locale", "lastName", "department", "username", "password", "password-confirm", "firstName", "email"}; for (int i = 0; i < labelOrder.length; i++) { WebElement webElement = element.get(i); String id = webElement.getAttribute("id"); diff --git a/themes/src/main/resources/theme/base/login/user-profile-commons.ftl b/themes/src/main/resources/theme/base/login/user-profile-commons.ftl index 9d5dfac23e12..0beda8900416 100644 --- a/themes/src/main/resources/theme/base/login/user-profile-commons.ftl +++ b/themes/src/main/resources/theme/base/login/user-profile-commons.ftl @@ -3,59 +3,65 @@ <#list profile.attributes as attribute> - <#assign group = (attribute.group)!""> - <#if group != currentGroup> - <#assign currentGroup=group> - <#if currentGroup != ""> -
- data-${key}="${value}" - - > - - <#assign groupDisplayHeader=group.displayHeader!""> - <#if groupDisplayHeader != ""> - <#assign groupHeaderText=advancedMsg(groupDisplayHeader)!group> - <#else> - <#assign groupHeaderText=group.name!""> - -
- -
+ <#if attribute.name=='locale' && realm.internationalizationEnabled && locale.currentLanguageTag?has_content> + + <#else> + + <#assign group = (attribute.group)!""> + <#if group != currentGroup> + <#assign currentGroup=group> + <#if currentGroup != ""> +
+ data-${key}="${value}" + + > - <#assign groupDisplayDescription=group.displayDescription!""> - <#if groupDisplayDescription != ""> - <#assign groupDescriptionText=advancedMsg(groupDisplayDescription)!""> -
- + <#assign groupDisplayHeader=group.displayHeader!""> + <#if groupDisplayHeader != ""> + <#assign groupHeaderText=advancedMsg(groupDisplayHeader)!group> + <#else> + <#assign groupHeaderText=group.name!""> + +
+
- -
+ + <#assign groupDisplayDescription=group.displayDescription!""> + <#if groupDisplayDescription != ""> + <#assign groupDescriptionText=advancedMsg(groupDisplayDescription)!""> +
+ +
+ +
+ - - <#nested "beforeField" attribute> -
-
- - <#if attribute.required>* -
-
- <#if attribute.annotations.inputHelperTextBefore??> -
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}
- - <@inputFieldByType attribute=attribute/> - <#if messagesPerField.existsError('${attribute.name}')> - - ${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc} - - - <#if attribute.annotations.inputHelperTextAfter??> -
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}
- + <#nested "beforeField" attribute> +
+
+ + <#if attribute.required>* +
+
+ <#if attribute.annotations.inputHelperTextBefore??> +
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}
+ + <@inputFieldByType attribute=attribute/> + <#if messagesPerField.existsError('${attribute.name}')> + + ${kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc} + + + <#if attribute.annotations.inputHelperTextAfter??> +
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}
+ +
-
- <#nested "afterField" attribute> + <#nested "afterField" attribute> + + <#list profile.html5DataAnnotations?keys as key> diff --git a/themes/src/main/resources/theme/keycloak.v2/login/user-profile-commons.ftl b/themes/src/main/resources/theme/keycloak.v2/login/user-profile-commons.ftl index ef1726cabad1..d9aaa69bef4a 100644 --- a/themes/src/main/resources/theme/keycloak.v2/login/user-profile-commons.ftl +++ b/themes/src/main/resources/theme/keycloak.v2/login/user-profile-commons.ftl @@ -4,51 +4,57 @@ <#list profile.attributes as attribute> - <#assign group = (attribute.group)!""> - <#if group != currentGroup> - <#assign currentGroup=group> - <#if currentGroup != ""> -
- data-${key}="${value}" - - > + <#if attribute.name=='locale' && realm.internationalizationEnabled && locale.currentLanguageTag?has_content> + + <#else> - <#assign groupDisplayHeader=group.displayHeader!""> - <#if groupDisplayHeader != ""> - <#assign groupHeaderText=advancedMsg(groupDisplayHeader)!group> - <#else> - <#assign groupHeaderText=group.name!""> - -
- -
+ <#assign group = (attribute.group)!""> + <#if group != currentGroup> + <#assign currentGroup=group> + <#if currentGroup != ""> +
+ data-${key}="${value}" + + > - <#assign groupDisplayDescription=group.displayDescription!""> - <#if groupDisplayDescription != ""> - <#assign groupDescriptionText=advancedMsg(groupDisplayDescription)!""> -
- + <#assign groupDisplayHeader=group.displayHeader!""> + <#if groupDisplayHeader != ""> + <#assign groupHeaderText=advancedMsg(groupDisplayHeader)!group> + <#else> + <#assign groupHeaderText=group.name!""> + +
+
- -
+ + <#assign groupDisplayDescription=group.displayDescription!""> + <#if groupDisplayDescription != ""> + <#assign groupDescriptionText=advancedMsg(groupDisplayDescription)!""> +
+ +
+ +
+ - - <#nested "beforeField" attribute> + <#nested "beforeField" attribute> - <@field.group name=attribute.name label=advancedMsg(attribute.displayName!'') error=kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc required=attribute.required> -
- <#if attribute.annotations.inputHelperTextBefore??> -
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}
- - <@inputFieldByType attribute=attribute/> - <#if attribute.annotations.inputHelperTextAfter??> -
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}
- -
- - <#nested "afterField" attribute> + <@field.group name=attribute.name label=advancedMsg(attribute.displayName!'') error=kcSanitize(messagesPerField.get('${attribute.name}'))?no_esc required=attribute.required> +
+ <#if attribute.annotations.inputHelperTextBefore??> +
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextBefore))?no_esc}
+ + <@inputFieldByType attribute=attribute/> + <#if attribute.annotations.inputHelperTextAfter??> +
${kcSanitize(advancedMsg(attribute.annotations.inputHelperTextAfter))?no_esc}
+ +
+ + <#nested "afterField" attribute> + + <#list profile.html5DataAnnotations?keys as key>