Incomplete registration form when edit email is disabled and email is set as username

Closes #34876

Signed-off-by: Martin Kanis <mkanis@redhat.com>
This commit is contained in:
Martin Kanis
2024-12-06 14:29:58 +01:00
committed by Pedro Igor
parent 18afeb83b6
commit dbd9429256
4 changed files with 153 additions and 37 deletions

View File

@@ -94,6 +94,9 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
@Override
public boolean isReadOnly(String name) {
if (isReadableOrWritableDuringRegistration(name)) {
return false;
}
if (isReadOnlyFromMetadata(name) || isReadOnlyInternalAttribute(name)) {
return true;
}
@@ -105,6 +108,14 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
return getMetadata(name) == null;
}
private boolean isReadableOrWritableDuringRegistration(String name) {
if (context.equals(UserProfileContext.REGISTRATION) && isRequired(name)) {
// in context of registration, username or email (email as username) cannot be readonly otherwise registration is not possible
return UserModel.EMAIL.equals(name) || UserModel.USERNAME.equals(name);
}
return false;
}
private boolean isAllowEditUnmanagedAttribute() {
UnmanagedAttributePolicy unmanagedAttributesPolicy = upConfig.getUnmanagedAttributePolicy();
@@ -284,10 +295,12 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
continue;
}
AttributeContext attributeContext = createAttributeContext(metadata);
if (!isReadableOrWritableDuringRegistration(name)) {
AttributeContext attributeContext = createAttributeContext(metadata);
if (!metadata.canView(attributeContext) || !metadata.isSelected(attributeContext)) {
attributes.remove(name);
if (!metadata.canView(attributeContext) || !metadata.isSelected(attributeContext)) {
attributes.remove(name);
}
}
}

View File

@@ -21,7 +21,6 @@ import org.keycloak.userprofile.AttributeValidatorMetadata;
import org.keycloak.userprofile.Attributes;
import org.keycloak.userprofile.UserProfile;
import org.keycloak.userprofile.UserProfileProvider;
/**
* Abstract base for Freemarker context bean providing information about user profile to render dynamic or crafted forms.
*

View File

@@ -103,9 +103,11 @@ public class RegisterPage extends AbstractPage {
lastNameInput.sendKeys(lastName);
}
emailInput.clear();
if (email != null) {
emailInput.sendKeys(email);
if (isEmailPresent()) {
emailInput.clear();
if (email != null) {
emailInput.sendKeys(email);
}
}
usernameInput.clear();
@@ -244,6 +246,22 @@ public class RegisterPage extends AbstractPage {
}
}
public boolean isEmailPresent() {
try {
return driver.findElement(By.name("email")).isDisplayed();
} catch (NoSuchElementException nse) {
return false;
}
}
public boolean isUsernamePresent() {
try {
return driver.findElement(By.name("username")).isDisplayed();
} catch (NoSuchElementException nse) {
return false;
}
}
public boolean isCurrent() {
return isCurrent("Register");

View File

@@ -28,11 +28,13 @@ import java.io.IOException;
import java.util.Map;
import java.util.Set;
import org.hamcrest.Matchers;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.BearerAuthFilter;
@@ -53,6 +55,9 @@ import org.keycloak.representations.userprofile.config.UPAttributeRequired;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.representations.userprofile.config.UPConfig.UnmanagedAttributePolicy;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.util.AssertAdminEvents;
import org.keycloak.testsuite.util.UserBuilder;
import org.keycloak.userprofile.config.UPConfigUtils;
@@ -77,6 +82,15 @@ public class UIRealmResourceTest extends AbstractTestRealmKeycloakTest {
private static Keycloak keycloakAdminClientViewUsers;
private static Keycloak keycloakAdminClientWithoutAdminRoles;
@Page
protected RegisterPage registerPage;
@Page
protected AppPage appPage;
@Rule
public AssertEvents events = new AssertEvents(this);
@Rule
public AssertAdminEvents assertAdminEvents = new AssertAdminEvents(this);
@@ -147,43 +161,115 @@ public class UIRealmResourceTest extends AbstractTestRealmKeycloakTest {
@Test
public void testUpdateUserProfileModification() throws IOException {
RealmRepresentation rep = testRealm().toRepresentation();
UPConfig upConfigOrig = testRealm().users().userProfile().getConfiguration();
try {
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
upConfig.addOrReplaceAttribute(new UPAttribute("foo",
new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN))));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
AdminEventRepresentation adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
Assert.assertNotNull(adminEvent.getRepresentation());
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setDisplayName("Foo");
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setPermissions(new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER)));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setRequired(new UPAttributeRequired(Set.of(UPConfigUtils.ROLE_ADMIN, UPConfigUtils.ROLE_USER), Set.of()));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
upConfig.getAttribute("foo").setValidations(Map.of("length", Map.of("min", "3", "max", "128")));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
assertAdminEvents.assertEmpty();
} finally {
updateRealmExt(toUIRealmRepresentation(rep, upConfigOrig));
}
}
@Test
public void testRegistrationFormWithNotReadableOrWritableRequiredEmail() throws IOException {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setRegistrationEmailAsUsername(true);
getCleanup().addCleanup(() -> {
testRealm.setRegistrationEmailAsUsername(false);
testRealm().update(testRealm);
});
testRealm().update(testRealm);
// set email as not readable or writable for a user
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
upConfig.addOrReplaceAttribute(new UPAttribute("foo",
new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN))));
upConfig.addOrReplaceAttribute(new UPAttribute("email",
new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN))));
updateRealmExt(toUIRealmRepresentation(testRealm, upConfig));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
AdminEventRepresentation adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
Assert.assertNotNull(adminEvent.getRepresentation());
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
// open the registration form
driver.navigate().to(oauth.getLoginFormUrl());
loginPage.form().register();
registerPage.assertCurrent();
upConfig.getAttribute("foo").setDisplayName("Foo");
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
Assert.assertTrue("Email is missing on the registration page.", registerPage.isEmailPresent());
upConfig.getAttribute("foo").setPermissions(new UPAttributePermissions(Set.of(), Set.of(UPConfigUtils.ROLE_USER)));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
registerPage.registerWithEmailAsUsername("Tom", "Brady", "tbrady@email.com", "password", "password");
upConfig.getAttribute("foo").setRequired(new UPAttributeRequired(Set.of(UPConfigUtils.ROLE_ADMIN, UPConfigUtils.ROLE_USER), Set.of()));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
upConfig.getAttribute("foo").setValidations(Map.of("length", Map.of("min", "3", "max", "128")));
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
adminEvent = assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, "ui-ext", ResourceType.USER_PROFILE);
assertEquals(upConfig, toUpConfig(adminEvent.getRepresentation()));
String userId = events.expectRegister("tbrady@email.com", "tbrady@email.com").assertEvent().getUserId();
UserRepresentation user = testRealm().users().get(userId).toRepresentation();
assertEquals("Tom", user.getFirstName());
assertEquals("Brady", user.getLastName());
}
updateRealmExt(toUIRealmRepresentation(rep, upConfig));
assertAdminEvents.assertEvent(TEST_REALM_NAME, OperationType.UPDATE, Matchers.nullValue(String.class), ResourceType.REALM);
assertAdminEvents.assertEmpty();
@Test
public void testRegistrationFormWithReadonlyUsernameAndEmail() throws IOException {
RealmRepresentation testRealm = testRealm().toRepresentation();
// set username and email as readonly for a user
UPConfig upConfig = testRealm().users().userProfile().getConfiguration();
upConfig.addOrReplaceAttribute(new UPAttribute("username",
new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN))));
upConfig.addOrReplaceAttribute(new UPAttribute("email",
new UPAttributePermissions(Set.of(UPConfigUtils.ROLE_USER, UPConfigUtils.ROLE_ADMIN), Set.of(UPConfigUtils.ROLE_ADMIN))));
updateRealmExt(toUIRealmRepresentation(testRealm, upConfig));
// open the registration form
driver.navigate().to(oauth.getLoginFormUrl());
loginPage.form().register();
registerPage.assertCurrent();
Assert.assertTrue("Username is missing on the registration page.", registerPage.isUsernamePresent());
Assert.assertFalse("Email should not be present on the registration page.", registerPage.isEmailPresent());
registerPage.register("Alice", "Wood", null, "awood", "password", "password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
String userId = events.expectRegister("awood", null).removeDetail("email").assertEvent().getUserId();
UserRepresentation user = testRealm().users().get(userId).toRepresentation();
assertEquals("awood", user.getUsername());
assertEquals("Alice", user.getFirstName());
assertEquals("Wood", user.getLastName());
}
@Test