migrated RequiredActionUpdateProfileTest. Closes #48149 (#48648)

Signed-off-by: Yike Gao <yikegao8@gmail.com>
This commit is contained in:
Yike Gao 2026-05-19 10:12:12 +01:00 committed by GitHub
parent 2a79636bbe
commit 4aff9a43ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 540 additions and 128 deletions

View file

@ -0,0 +1,334 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testframework.ui.page;
import java.util.LinkedHashMap;
import java.util.Map;
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginUpdateProfilePage extends AbstractLoginPage {
@FindBy(id = "username")
private WebElement usernameInput;
@FindBy(name = "firstName")
private WebElement firstNameInput;
@FindBy(name = "lastName")
private WebElement lastNameInput;
@FindBy(name = "email")
private WebElement emailInput;
@FindBy(name = "department")
private WebElement departmentInput;
@FindBy(css = "input[type=\"submit\"]")
private WebElement submitButton;
@FindBy(name = "cancel-aia")
private WebElement cancelAIAButton;
@FindBy(css = "div[class^='pf-v5-c-alert'], div[class^='alert-error']")
private WebElement loginAlertErrorMessage;
private final UpdateProfileErrors errorsPage;
public LoginUpdateProfilePage(ManagedWebDriver driver) {
super(driver);
this.errorsPage = new UpdateProfileErrors(driver);
}
public void update(String firstName, String lastName) {
prepareUpdate().firstName(firstName).lastName(lastName).submit();
}
public void update(String firstName, String lastName, String email) {
prepareUpdate().firstName(firstName).lastName(lastName).email(email).submit();
}
public void update(Map<String, String> attributes) {
prepareUpdate().otherProfileAttribute(attributes).submit();
}
public Update prepareUpdate() {
return new Update(this);
}
public void cancel() {
cancelAIAButton.click();
}
public String getAlertError() {
try {
return loginAlertErrorMessage.getText();
} catch (NoSuchElementException e) {
return null;
}
}
public String getUsername() {
return readValue(By.id("username"));
}
public String getFirstName() {
return readValue(By.name("firstName"));
}
public String getLastName() {
return readValue(By.name("lastName"));
}
public String getEmail() {
return readValue(By.name("email"));
}
public String getDepartment() {
return readValue(By.name("department"));
}
private String readValue(By locator) {
return driver.waiting().until(d -> {
try {
return d.findElement(locator).getAttribute("value");
} catch (StaleElementReferenceException e) {
return null;
}
});
}
public boolean isDepartmentEnabled() {
return departmentInput.isEnabled();
}
public UpdateProfileErrors getInputErrors() {
return errorsPage;
}
public String getLabelForField(String fieldId) {
return driver.findElement(By.cssSelector("label[for=" + fieldId + "]")).getText().replaceAll("\\s\\*$", "");
}
public WebElement getElementById(String fieldId) {
try {
return driver.findElement(By.id(fieldId));
} catch (NoSuchElementException ignore) {
return null;
}
}
public boolean isUsernamePresent() {
try {
return usernameInput.isDisplayed();
} catch (NoSuchElementException nse) {
return false;
}
}
public boolean isEmailInputPresent() {
try {
return emailInput.isDisplayed();
} catch (NoSuchElementException e) {
return false;
}
}
public boolean isDepartmentPresent() {
try {
return departmentInput.isDisplayed();
} catch (NoSuchElementException e) {
return false;
}
}
public boolean isCancelDisplayed() {
try {
return cancelAIAButton.isDisplayed();
} catch (NoSuchElementException e) {
return false;
}
}
public void setAttribute(String elementId, String value) {
WebElement element = getElementById(elementId);
if (element != null) {
element.clear();
element.sendKeys(value);
}
}
public void clickAddAttributeValue(String elementId) {
WebElement element = getElementById("kc-add-" + elementId);
if (element != null) {
element.click();
}
}
public void clickRemoveAttributeValue(String elementId) {
WebElement element = getElementById("kc-remove-" + elementId);
if (element != null) {
element.click();
}
}
public String getAttribute(String elementId) {
WebElement element = getElementById(elementId);
if (element != null) {
return element.getAttribute("value");
}
return null;
}
@Override
public String getExpectedPageId() {
return "login-login-update-profile";
}
public static class Update {
private final LoginUpdateProfilePage page;
private String username;
private String firstName;
private String lastName;
private String department;
private String email;
private final Map<String, String> other = new LinkedHashMap<>();
protected Update(LoginUpdateProfilePage page) {
this.page = page;
}
public Update username(String username) {
this.username = username;
return this;
}
public Update firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Update lastName(String lastName) {
this.lastName = lastName;
return this;
}
public Update department(String department) {
this.department = department;
return this;
}
public Update email(String email) {
this.email = email;
return this;
}
public Update otherProfileAttribute(Map<String, String> attributes) {
other.putAll(attributes);
return this;
}
public void submit() {
if (username != null) {
page.usernameInput.clear();
page.usernameInput.sendKeys(username);
}
if (firstName != null) {
page.firstNameInput.clear();
page.firstNameInput.sendKeys(firstName);
}
if (lastName != null) {
page.lastNameInput.clear();
page.lastNameInput.sendKeys(lastName);
}
if (department != null) {
page.departmentInput.clear();
page.departmentInput.sendKeys(department);
}
if (email != null) {
page.emailInput.clear();
page.emailInput.sendKeys(email);
}
for (Map.Entry<String, String> entry : other.entrySet()) {
WebElement el = page.driver.findElement(By.id(entry.getKey()));
if (el != null) {
el.clear();
el.sendKeys(entry.getValue());
}
}
page.submitButton.submit();
}
}
// For managing input errors
public static class UpdateProfileErrors {
private final ManagedWebDriver driver;
public UpdateProfileErrors(ManagedWebDriver driver) {
this.driver = driver;
}
private String getTextById(String id) {
try {
return driver.findElement(By.id(id)).getText();
} catch (NoSuchElementException e) {
return null;
}
}
public String getFirstNameError() {
String text = getTextById("input-error-firstname");
if (text != null) {
return text;
}
return getTextById("input-error-firstName");
}
public String getLastNameError() {
String text = getTextById("input-error-lastname");
if (text != null) {
return text;
}
return getTextById("input-error-lastName");
}
public String getEmailError() {
return getTextById("input-error-email");
}
public String getUsernameError() {
return getTextById("input-error-username");
}
}
}

View file

@ -14,7 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.testsuite.actions;
package org.keycloak.tests.actions;
import java.util.Arrays;
import java.util.HashMap;
@ -29,116 +30,114 @@ import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.userprofile.config.UPAttribute;
import org.keycloak.representations.userprofile.config.UPAttributePermissions;
import org.keycloak.representations.userprofile.config.UPConfig;
import org.keycloak.testframework.annotations.InjectEvents;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.InjectUser;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.EventAssertion;
import org.keycloak.testframework.events.Events;
import org.keycloak.testframework.injection.LifeCycle;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.realm.ManagedUser;
import org.keycloak.testframework.realm.RealmBuilder;
import org.keycloak.testframework.realm.RealmConfig;
import org.keycloak.testframework.realm.UserBuilder;
import org.keycloak.testsuite.AbstractChangeImportedUserPasswordsTest;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.admin.AdminApiUtil;
import org.keycloak.testsuite.arquillian.annotation.IgnoreBrowserDriver;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.ErrorPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
import org.keycloak.testframework.realm.UserConfig;
import org.keycloak.testframework.ui.annotations.InjectPage;
import org.keycloak.testframework.ui.annotations.InjectWebDriver;
import org.keycloak.testframework.ui.page.ErrorPage;
import org.keycloak.testframework.ui.page.LoginPage;
import org.keycloak.testframework.ui.page.LoginUpdateProfilePage;
import org.keycloak.testframework.ui.webdriver.BrowserType;
import org.keycloak.testframework.ui.webdriver.ManagedWebDriver;
import org.keycloak.tests.suites.DatabaseTest;
import org.keycloak.tests.utils.PasswordGenerateUtil;
import org.keycloak.tests.utils.admin.AdminApiUtil;
import org.keycloak.userprofile.UserProfileContext;
import org.keycloak.utils.StringUtil;
import org.hamcrest.Matchers;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.ElementClickInterceptedException;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserPasswordsTest {
@KeycloakIntegrationTest
@DatabaseTest
public class RequiredActionUpdateProfileTest {
@Rule
public AssertEvents events = new AssertEvents(this);
private static final String PASSWORD = PasswordGenerateUtil.generatePassword();
@Page
protected AppPage appPage;
@InjectRealm(config = RequiredActionUpdateProfileRealmConfig.class)
ManagedRealm realm;
@Page
@InjectWebDriver
ManagedWebDriver driver;
@InjectOAuthClient
OAuthClient oauth;
@InjectEvents
Events events;
@InjectUser(ref = "testUser", config = TestUserConfig.class, lifecycle = LifeCycle.METHOD)
ManagedUser testUser;
@InjectUser(ref = "johnDoh", config = JohnDohUserConfig.class, lifecycle = LifeCycle.METHOD)
ManagedUser johnDohUser;
@InjectPage
protected LoginPage loginPage;
@Page
protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
@InjectPage
protected LoginUpdateProfilePage updateProfilePage;
@Page
@InjectPage
protected ErrorPage errorPage;
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
super.configureTestRealm(testRealm);
ActionUtil.addRequiredActionForUser(testRealm, "test-user@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
ActionUtil.addRequiredActionForUser(testRealm, "john-doh@localhost", UserModel.RequiredAction.UPDATE_PROFILE.name());
}
@Before
public void beforeTest() {
AdminApiUtil.removeUserByUsername(managedRealm.admin(), "test-user@localhost");
UserRepresentation user = UserBuilder.create().enabled(true)
.username("test-user@localhost")
.email("test-user@localhost")
.firstName("Tom")
.lastName("Brady")
.emailVerified(true)
.requiredActions(UserModel.RequiredAction.UPDATE_PROFILE.name()).build();
AdminApiUtil.createUserAndResetPasswordWithAdminClient(managedRealm.admin(), user, generatePassword("test-user@localhost"));
AdminApiUtil.removeUserByUsername(managedRealm.admin(), "john-doh@localhost");
user = UserBuilder.create().enabled(true)
.username("john-doh@localhost")
.email("john-doh@localhost")
.firstName("John")
.lastName("Doh")
.emailVerified(true)
.requiredActions(UserModel.RequiredAction.UPDATE_PROFILE.name()).build();
AdminApiUtil.createUserAndResetPasswordWithAdminClient(managedRealm.admin(), user, generatePassword("john-doh@localhost"));
}
@Test
public void updateProfile() {
oauth.openLoginForm();
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
user.setEmailVerified(true);
adminClient.realm("test").users().get(user.getId()).update(user);
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
assertFalse(updateProfilePage.isCancelDisplayed());
updateProfilePage.prepareUpdate().username("test-user@localhost").firstName("New first").lastName("New last").email("new@email.com").submit();
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.PREVIOUS_FIRST_NAME, "Tom").detail(Details.UPDATED_FIRST_NAME, "New first")
.detail(Details.PREVIOUS_LAST_NAME, "Brady").detail(Details.UPDATED_LAST_NAME, "New last")
.detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com")
.assertEvent();
Assertions.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
EventAssertion.assertSuccess(pollEvent())
.type(EventType.UPDATE_PROFILE)
.details(Details.PREVIOUS_FIRST_NAME, "Tom")
.details(Details.UPDATED_FIRST_NAME, "New first")
.details(Details.PREVIOUS_LAST_NAME, "Brady")
.details(Details.UPDATED_LAST_NAME, "New last")
.details(Details.PREVIOUS_EMAIL, "test-user@localhost")
.details(Details.UPDATED_EMAIL, "new@email.com");
assertThat(oauth.parseLoginResponse().getCode(), notNullValue());
EventAssertion.expectLoginSuccess(events.poll());
EventAssertion.expectLoginSuccess(pollEvent());
// assert user is really updated in persistent store
user = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
UserRepresentation user = testUser.admin().toRepresentation();
Assertions.assertEquals("New first", user.getFirstName());
Assertions.assertEquals("New last", user.getLastName());
Assertions.assertEquals("new@email.com", user.getEmail());
@ -151,15 +150,16 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
public void updateUsername() {
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
String userId = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost").getId();
String userId = johnDohUser.getId();
updateProfilePage.assertCurrent();
updateProfilePage.prepareUpdate().username("new").firstName("New first").lastName("New last").email("john-doh@localhost").submit();
EventAssertion.assertSuccess(events.poll())
EventAssertion.assertSuccess(pollEvent())
.type(EventType.UPDATE_PROFILE)
.isCodeId()
.sessionId(null)
@ -169,26 +169,26 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
.details(Details.UPDATED_LAST_NAME, "New last")
.withoutDetails(Details.CONSENT);
Assertions.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
assertThat(oauth.parseLoginResponse().getCode(), notNullValue());
EventAssertion.expectLoginSuccess(events.poll()).details(Details.USERNAME, "john-doh@localhost").userId(userId);
EventAssertion.expectLoginSuccess(pollEvent()).details(Details.USERNAME, "john-doh@localhost").userId(userId);
// assert user is really updated in persistent store
UserRepresentation user = ActionUtil.findUserWithAdminClient(adminClient, "new");
UserRepresentation user = johnDohUser.admin().toRepresentation();
Assertions.assertEquals("New first", user.getFirstName());
Assertions.assertEquals("New last", user.getLastName());
Assertions.assertEquals("john-doh@localhost", user.getEmail());
Assertions.assertEquals("new", user.getUsername());
// email not changed so verify that emailVerified flag is NOT reset
Assertions.assertEquals(true, user.isEmailVerified());
getCleanup().addUserId(user.getId());
}
@Test
public void updateProfileMissingFirstName() {
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -202,14 +202,15 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("new@email.com", updateProfilePage.getEmail());
Assertions.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getFirstNameError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileMissingLastName() {
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -224,14 +225,15 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Please specify this field.", updateProfilePage.getInputErrors().getLastNameError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileMissingEmail() {
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -250,19 +252,20 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
containsString("Please specify this field")
));
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileInvalidEmail() {
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
updateProfilePage.prepareUpdate().username("invalid").firstName("New first").lastName("New last")
.email("invalidemail").submit();
.email("invalidemail").submit();
updateProfilePage.assertCurrent();
@ -273,14 +276,15 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Invalid email address.", updateProfilePage.getInputErrors().getEmailError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileMissingUsername() {
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -296,14 +300,15 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Please specify username.", updateProfilePage.getInputErrors().getUsernameError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileDuplicateUsername() {
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -319,14 +324,15 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Username already exists.", updateProfilePage.getInputErrors().getUsernameError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileDuplicatedEmail() {
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -342,16 +348,18 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Email already exists.", updateProfilePage.getInputErrors().getEmailError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileDuplicateUsernameWithEmail() {
getCleanup().addUserId(createUser(TEST_REALM_NAME, "user1@local.com", generatePassword("user1@local.com"), "user1", "user1", "user1@local.org"));
String userId = createUser("user1@local.com", "user1", "user1", "user1@local.org");
realm.cleanup().add(r -> r.users().get(userId).remove());
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -367,16 +375,18 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Username already exists.", updateProfilePage.getInputErrors().getUsernameError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileDuplicatedEmailWithUsername() {
getCleanup().addUserId(createUser(TEST_REALM_NAME, "user1@local.com", generatePassword("user1@local.com"), "user1", "user1", "user1@local.org"));
String userId = createUser("user1@local.com", "user1", "user1", "user1@local.org");
realm.cleanup().add(r -> r.users().get(userId).remove());
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
@ -392,38 +402,34 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
Assertions.assertEquals("Email already exists.", updateProfilePage.getInputErrors().getEmailError());
events.assertEmpty();
assertNoMoreEvents();
}
@Test
public void updateProfileExpiredCookies() {
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
// Expire cookies and assert the page with "back to application" link present
driver.manage().deleteAllCookies();
driver.cookies().deleteAll();
updateProfilePage.prepareUpdate().username("test-user@localhost").firstName("New first").lastName("New last").email("keycloak-user@localhost").submit();
errorPage.assertCurrent();
String backToAppLink = errorPage.getBackToApplicationLink();
ClientRepresentation client = AdminApiUtil.findClientByClientId(adminClient.realm("test"), "test-app").toRepresentation();
Assertions.assertEquals(backToAppLink, client.getBaseUrl());
}
@Test
public void updateProfileWithoutRemoveCustomAttributes() {
UserProfileResource upResource = adminClient.realm("test").users().userProfile();
UserProfileResource upResource = realm.admin().users().userProfile();
UPConfig upConfig = upResource.getConfiguration();
upConfig.setUnmanagedAttributePolicy(UPConfig.UnmanagedAttributePolicy.ADMIN_EDIT);
upResource.update(upConfig);
try {
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
UserResource user = adminClient.realm("test").users().get(userRep.getId());
UserResource user = testUser.admin();
UserRepresentation userRep = user.toRepresentation();
userRep.setAttributes(new HashMap<>());
userRep.getAttributes().put("custom", Arrays.asList("custom"));
@ -432,21 +438,26 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
oauth.openLoginForm();
loginPage.login("test-user@localhost", getPassword("test-user@localhost"));
loginPage.fillLogin("test-user@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
assertFalse(updateProfilePage.isCancelDisplayed());
updateProfilePage.prepareUpdate().username("test-user@localhost").firstName("New first").lastName("New last").email("new@email.com").submit();
events.expectRequiredAction(EventType.UPDATE_PROFILE).detail(Details.CONTEXT, UserProfileContext.UPDATE_PROFILE.name()).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
EventAssertion.assertSuccess(pollEvent())
.type(EventType.UPDATE_PROFILE)
.details(Details.CONTEXT, UserProfileContext.UPDATE_PROFILE.name())
.details(Details.PREVIOUS_EMAIL, "test-user@localhost")
.details(Details.UPDATED_EMAIL, "new@email.com");
Assertions.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
assertThat(oauth.parseLoginResponse().getCode(), notNullValue());
EventAssertion.expectLoginSuccess(events.poll());
EventAssertion.expectLoginSuccess(pollEvent());
// assert user is really updated in persistent store
userRep = ActionUtil.findUserWithAdminClient(adminClient, "test-user@localhost");
userRep = testUser.admin().toRepresentation();
Assertions.assertEquals("New first", userRep.getFirstName());
Assertions.assertEquals("New last", userRep.getLastName());
Assertions.assertEquals("new@email.com", userRep.getEmail());
@ -460,9 +471,10 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
}
@Test
@IgnoreBrowserDriver(HtmlUnitDriver.class) // we can't yet run modern JavaScript using HtmlUnit
public void testMultivaluedAttributes() {
UserProfileResource userProfile = managedRealm.admin().users().userProfile();
Assumptions.assumeFalse(driver.getBrowserType().equals(BrowserType.HTML_UNIT)); // we can't yet run modern JavaScript using HtmlUnit
UserProfileResource userProfile = realm.admin().users().userProfile();
UPConfig configuration = userProfile.getConfiguration();
try {
@ -480,7 +492,8 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
userProfile.update(testUpConfig);
oauth.openLoginForm();
loginPage.login("john-doh@localhost", getPassword("john-doh@localhost"));
loginPage.fillLogin("john-doh@localhost", PASSWORD);
loginPage.submit();
updateProfilePage.assertCurrent();
for (String attribute : attributes) {
@ -493,12 +506,13 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
updateProfilePage.clickAddAttributeValue(elementId);
}
updateProfilePage.update("f", "l", "e@keycloak.org");
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
driver.waiting().waitForOAuthCallback();
UserRepresentation userRep = johnDohUser.admin().toRepresentation();
assertThat(userRep.getAttributes().get(attribute), Matchers.containsInAnyOrder(values.toArray()));
// make sure multiple values are properly rendered
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
managedRealm.admin().users().get(userRep.getId()).update(userRep);
johnDohUser.admin().update(userRep);
oauth.openLoginForm();
assertThat(IntStream.range(0, 5).mapToObj(value -> updateProfilePage.getAttribute(attribute + "-" + value)).collect(Collectors.toSet()), Matchers.equalTo(valuesSet));
@ -515,13 +529,14 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
}
}
updateProfilePage.update("f", "l", "e@keycloak.org");
userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
driver.waiting().waitForOAuthCallback();
userRep = johnDohUser.admin().toRepresentation();
assertThat(userRep.getAttributes().get(attribute), Matchers.hasSize(1));
assertThat(userRep.getAttributes().get(attribute).get(0), Matchers.in(values));
// make sure adding/removing within the same context works
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
managedRealm.admin().users().get(userRep.getId()).update(userRep);
johnDohUser.admin().update(userRep);
oauth.openLoginForm();
for (String value : values) {
String elementId = attribute + "-" + value;
@ -545,13 +560,14 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
updateProfilePage.setAttribute(attribute + "-0", lastValue);
}
updateProfilePage.update("f", "l", "e@keycloak.org");
userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
driver.waiting().waitForOAuthCallback();
userRep = johnDohUser.admin().toRepresentation();
assertThat(userRep.getAttributes().get(attribute), Matchers.hasSize(1));
assertThat(userRep.getAttributes().get(attribute).get(0), Matchers.in(values));
// at the end the attribute is set with multiple values
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
managedRealm.admin().users().get(userRep.getId()).update(userRep);
johnDohUser.admin().update(userRep);
oauth.openLoginForm();
for (String value : values) {
String elementId = attribute + "-" + value;
@ -559,15 +575,16 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
updateProfilePage.clickAddAttributeValue(elementId);
}
updateProfilePage.update("f", "l", "e@keycloak.org");
driver.waiting().waitForOAuthCallback();
// restart the update profile flow
userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
userRep = johnDohUser.admin().toRepresentation();
userRep.setRequiredActions(List.of(UserModel.RequiredAction.UPDATE_PROFILE.name()));
managedRealm.admin().users().get(userRep.getId()).update(userRep);
johnDohUser.admin().update(userRep);
oauth.openLoginForm();
}
UserRepresentation userRep = ActionUtil.findUserWithAdminClient(adminClient, "john-doh@localhost");
UserRepresentation userRep = johnDohUser.admin().toRepresentation();
// all attributes should be set with multiple values
for (String attribute : attributes) {
@ -578,4 +595,64 @@ public class RequiredActionUpdateProfileTest extends AbstractChangeImportedUserP
}
}
private String createUser(String username, String firstName, String lastName, String email) {
UserRepresentation user = UserBuilder.create().enabled(true)
.username(username)
.email(email)
.firstName(firstName)
.lastName(lastName)
.emailVerified(true)
.build();
return AdminApiUtil.createUserAndResetPasswordWithAdminClient(realm.admin(), user, PASSWORD);
}
private void assertNoMoreEvents() {
EventRepresentation event = events.poll();
assertNull(event, "Expected no more events but got: " + (event != null ? event.getType() : null));
}
private EventRepresentation pollEvent() {
return driver.waiting().until(d -> events.poll());
}
public static class RequiredActionUpdateProfileRealmConfig implements RealmConfig {
@Override
public RealmBuilder configure(RealmBuilder realm) {
realm.editUsernameAllowed(true);
realm.users(UserBuilder.create("keycloak-user@localhost")
.email("keycloak-user@localhost")
.name("keycloak", "User")
.password(PASSWORD)
.emailVerified(true));
return realm;
}
}
public static class TestUserConfig implements UserConfig {
@Override
public UserBuilder configure(UserBuilder user) {
return user.username("test-user@localhost")
.email("test-user@localhost")
.name("Tom", "Brady")
.password(PASSWORD)
.emailVerified(true)
.requiredActions(UserModel.RequiredAction.UPDATE_PROFILE.name());
}
}
public static class JohnDohUserConfig implements UserConfig {
@Override
public UserBuilder configure(UserBuilder user) {
return user.username("john-doh@localhost")
.email("john-doh@localhost")
.name("John", "Doh")
.password(PASSWORD)
.emailVerified(true)
.requiredActions(UserModel.RequiredAction.UPDATE_PROFILE.name());
}
}
}

View file

@ -6,6 +6,7 @@ import org.junit.platform.suite.api.Suite;
@Suite
@SelectPackages({
"org.keycloak.tests.account",
"org.keycloak.tests.actions",
"org.keycloak.tests.authz",
"org.keycloak.tests.broker",
"org.keycloak.tests.client",

View file

@ -1,5 +1,6 @@
package org.keycloak.tests.suites;
import org.keycloak.tests.actions.RequiredActionUpdateProfileTest;
import org.keycloak.tests.i18n.LoginPageTest;
import org.junit.platform.suite.api.SelectClasses;
@ -8,7 +9,8 @@ import org.junit.platform.suite.api.Suite;
@Suite
// TODO: Select relevant test classes or packages once they have been migrated
@SelectClasses({
LoginPageTest.class
LoginPageTest.class,
RequiredActionUpdateProfileTest.class
})
public class FormsTestSuite {
}

View file

@ -6,7 +6,6 @@ ExportImportTest
KcOidcBrokerTest
LDAPUserLoginTest
LoginTest
RequiredActionUpdateProfileTest
SamlClientTest
UserProfileTest
OidcAdvancedClaimToGroupMapperTest

View file

@ -1,3 +1,2 @@
org.keycloak.testsuite.forms.**
org.keycloak.testsuite.actions.RequiredActionUpdateProfileTest
org.keycloak.testsuite.actions.TermsAndConditionsTest