/*
* 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.testsuite.account;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.resources.AccountService;
import org.keycloak.services.resources.RealmsResource;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.drone.Different;
import org.keycloak.testsuite.pages.AccountApplicationsPage;
import org.keycloak.testsuite.pages.AccountFederatedIdentityPage;
import org.keycloak.testsuite.pages.AccountLogPage;
import org.keycloak.testsuite.pages.AccountPasswordPage;
import org.keycloak.testsuite.pages.AccountSessionsPage;
import org.keycloak.testsuite.pages.AccountTotpPage;
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
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.RegisterPage;
import org.keycloak.testsuite.util.IdentityProviderBuilder;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import javax.ws.rs.core.UriBuilder;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.*;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
*/
public class AccountTest extends AbstractTestRealmKeycloakTest {
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
//UserRepresentation user = findUserInRealmRep(testRealm, "test-user@localhost");
//ClientRepresentation accountApp = findClientInRealmRep(testRealm, ACCOUNT_MANAGEMENT_CLIENT_ID);
UserRepresentation user2 = UserBuilder.create()
.enabled(true)
.username("test-user-no-access@localhost")
.email("test-user-no-access@localhost")
.password("password")
.build();
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
.providerId("github")
.alias("github")
.build());
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
.providerId("saml")
.alias("mysaml")
.build());
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
.providerId("oidc")
.alias("myoidc")
.displayName("MyOIDC")
.build());
testRealm.addIdentityProvider(IdentityProviderBuilder.create()
.providerId("oidc")
.alias("myhiddenoidc")
.displayName("MyHiddenOIDC")
.hideOnLoginPage()
.build());
RealmBuilder.edit(testRealm)
.user(user2);
}
private static final UriBuilder BASE = UriBuilder.fromUri("http://localhost:8180/auth");
private static final String ACCOUNT_URL = RealmsResource.accountUrl(BASE.clone()).build("test").toString();
public static String ACCOUNT_REDIRECT = AccountService.loginRedirectUrl(BASE.clone()).build("test").toString();
// Create second session
@Drone
@Different
WebDriver driver2;
@Rule
public AssertEvents events = new AssertEvents(this);
@Page
protected AppPage appPage;
@Page
protected LoginPage loginPage;
@Page
protected RegisterPage registerPage;
@Page
protected AccountPasswordPage changePasswordPage;
@Page
protected AccountUpdateProfilePage profilePage;
@Page
protected AccountTotpPage totpPage;
@Page
protected AccountLogPage logPage;
@Page
protected AccountSessionsPage sessionsPage;
@Page
protected AccountApplicationsPage applicationsPage;
@Page
protected AccountFederatedIdentityPage federatedIdentityPage;
@Page
protected ErrorPage errorPage;
private TimeBasedOTP totp = new TimeBasedOTP();
private String userId;
@Before
public void before() {
userId = findUser("test-user@localhost").getId();
// Revert any password policy and user password changes
setPasswordPolicy("");
ApiUtil.resetUserPassword(testRealm().users().get(userId), "password", false);
}
@Test
public void returnToAppFromQueryParam() {
driver.navigate().to(profilePage.getPath() + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
driver.navigate().to(profilePage.getPath() + "?referrer=test-app&referrer_uri=http://localhost:8180/auth/realms/master/app/auth?test");
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
Assert.assertEquals(appPage.baseUrl + "?test", driver.getCurrentUrl());
driver.navigate().to(profilePage.getPath() + "?referrer=test-app");
Assert.assertTrue(profilePage.isCurrent());
driver.findElement(By.linkText("Authenticator")).click();
Assert.assertTrue(totpPage.isCurrent());
driver.findElement(By.linkText("Account")).click();
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
events.clear();
}
@Test
public void changePassword() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
EventRepresentation event = events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
String sessionId = event.getSessionId();
String userId = event.getUserId();
changePasswordPage.changePassword("", "new-password", "new-password");
Assert.assertEquals("Please specify password.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_MISSING).assertEvent();
changePasswordPage.changePassword("password", "new-password", "new-password2");
Assert.assertEquals("Password confirmation doesn't match.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_CONFIRM_ERROR).assertEvent();
changePasswordPage.changePassword("password", "new-password", "new-password");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
changePasswordPage.logout();
events.expectLogout(sessionId).detail(Details.REDIRECT_URI, changePasswordPage.getPath()).assertEvent();
loginPage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertEquals("Invalid username or password.", loginPage.getError());
events.expectLogin().session((String) null).error(Errors.INVALID_USER_CREDENTIALS)
.removeDetail(Details.CONSENT)
.assertEvent();
loginPage.open();
loginPage.login("test-user@localhost", "new-password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().assertEvent();
}
private void setPasswordPolicy(String policy) {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setPasswordPolicy(policy);
testRealm().update(testRealm);
}
@Test
public void changePasswordWithBlankCurrentPassword() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("", "new", "new");
Assert.assertEquals("Please specify password.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_MISSING).assertEvent();
changePasswordPage.changePassword("password", "new", "new");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithLengthPasswordPolicy() {
setPasswordPolicy("length(8)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "1234", "1234");
Assert.assertEquals("Invalid password: minimum length 8.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "12345678", "12345678");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithDigitsPolicy() {
setPasswordPolicy("digits(2)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "invalidPassword1", "invalidPassword1");
Assert.assertEquals("Invalid password: must contain at least 2 numerical digits.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "validPassword12", "validPassword12");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithLowerCasePolicy() {
setPasswordPolicy("lowerCase(2)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "iNVALIDPASSWORD", "iNVALIDPASSWORD");
Assert.assertEquals("Invalid password: must contain at least 2 lower case characters.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "vaLIDPASSWORD", "vaLIDPASSWORD");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithUpperCasePolicy() {
setPasswordPolicy("upperCase(2)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "Invalidpassword", "Invalidpassword");
Assert.assertEquals("Invalid password: must contain at least 2 upper case characters.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "VAlidpassword", "VAlidpassword");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithSpecialCharsPolicy() {
setPasswordPolicy("specialChars(2)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "invalidPassword*", "invalidPassword*");
Assert.assertEquals("Invalid password: must contain at least 2 special characters.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "validPassword*#", "validPassword*#");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithNotUsernamePolicy() {
setPasswordPolicy("notUsername(1)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "test-user@localhost", "test-user@localhost");
Assert.assertEquals("Invalid password: must not be equal to the username.", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "newPassword", "newPassword");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
@Test
public void changePasswordWithRegexPatternsPolicy() {
setPasswordPolicy("regexPattern(^[A-Z]+#[a-z]{8}$)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
changePasswordPage.changePassword("password", "invalidPassword", "invalidPassword");
Assert.assertEquals("Invalid password: fails to match regex pattern(s).", profilePage.getError());
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
changePasswordPage.changePassword("password", "VALID#password", "VALID#password");
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
private void assertChangePasswordSucceeds(String currentPassword, String newPassword) {
changePasswordPage.changePassword(currentPassword, newPassword, newPassword);
Assert.assertEquals("Your password has been updated.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
}
private void assertChangePasswordFails(String currentPassword, String newPassword) {
changePasswordPage.changePassword(currentPassword, newPassword, newPassword);
Assert.assertThat(profilePage.getError(), containsString("Invalid password: must not be equal to any of last"));
events.expectAccount(EventType.UPDATE_PASSWORD_ERROR).error(Errors.PASSWORD_REJECTED).assertEvent();
}
@Test
public void changePasswordWithPasswordHistoryPolicyThreePasswords() {
setPasswordPolicy(PasswordPolicy.PASSWORD_HISTORY_ID + "(3)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
assertChangePasswordFails ("password", "password"); // current: password
assertChangePasswordSucceeds("password", "password3"); // current: password
assertChangePasswordFails ("password3", "password"); // current: password1, history: password
assertChangePasswordFails ("password3", "password3"); // current: password1, history: password
assertChangePasswordSucceeds("password3", "password4"); // current: password1, history: password
assertChangePasswordFails ("password4", "password"); // current: password2, history: password, password1
assertChangePasswordFails ("password4", "password3"); // current: password2, history: password, password1
assertChangePasswordFails ("password4", "password4"); // current: password2, history: password, password1
assertChangePasswordSucceeds("password4", "password5"); // current: password2, history: password, password1
assertChangePasswordSucceeds("password5", "password"); // current: password3, history: password1, password2
}
@Test
public void changePasswordWithPasswordHistoryPolicyTwoPasswords() {
setPasswordPolicy(PasswordPolicy.PASSWORD_HISTORY_ID + "(2)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
assertChangePasswordFails ("password", "password"); // current: password
assertChangePasswordSucceeds("password", "password1"); // current: password
assertChangePasswordFails ("password1", "password"); // current: password1, history: password
assertChangePasswordFails ("password1", "password1"); // current: password1, history: password
assertChangePasswordSucceeds("password1", "password2"); // current: password1, history: password
assertChangePasswordFails ("password2", "password1"); // current: password2, history: password1
assertChangePasswordFails ("password2", "password2"); // current: password2, history: password1
assertChangePasswordSucceeds("password2", "password"); // current: password2, history: password1
}
@Test
public void changePasswordWithPasswordHistoryPolicyOnePwds() {
// One password means only the active password is checked
setPasswordPolicy(PasswordPolicy.PASSWORD_HISTORY_ID + "(1)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
assertChangePasswordFails ("password", "password"); // current: password
assertChangePasswordSucceeds("password", "password6"); // current: password
assertChangePasswordFails ("password6", "password6"); // current: password1
assertChangePasswordSucceeds("password6", "password"); // current: password1
}
@Test
public void changePasswordWithPasswordHistoryPolicyZeroPwdsInHistory() {
setPasswordPolicy(PasswordPolicy.PASSWORD_HISTORY_ID + "(0)");
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=password").assertEvent();
assertChangePasswordFails ("password", "password"); // current: password
assertChangePasswordSucceeds("password", "password1"); // current: password
assertChangePasswordFails ("password1", "password1"); // current: password1
assertChangePasswordSucceeds("password1", "password"); // current: password1
}
@Test
public void changeProfile() throws Exception {
setEditUsernameAllowed(false);
setRegistrationEmailAsUsername(false);
profilePage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("Tom", profilePage.getFirstName());
Assert.assertEquals("Brady", profilePage.getLastName());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
// All fields are required, so there should be an error when something is missing.
profilePage.updateProfile("", "New last", "new@email.com");
Assert.assertEquals("Please specify first name.", profilePage.getError());
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
events.assertEmpty();
profilePage.updateProfile("New first", "", "new@email.com");
Assert.assertEquals("Please specify last name.", profilePage.getError());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
events.assertEmpty();
profilePage.updateProfile("New first", "New last", "");
Assert.assertEquals("Please specify email.", profilePage.getError());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("", profilePage.getEmail());
events.assertEmpty();
profilePage.clickCancel();
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("Tom", profilePage.getFirstName());
Assert.assertEquals("Brady", profilePage.getLastName());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
events.assertEmpty();
profilePage.updateProfile("New first", "New last", "new@email.com");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
events.expectAccount(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent();
events.expectAccount(EventType.UPDATE_PROFILE).assertEvent();
// reset user for other tests
profilePage.updateProfile("Tom", "Brady", "test-user@localhost");
events.clear();
// Revert
setEditUsernameAllowed(true);
}
@Test
public void changeProfileEmailAsUsernameEnabled() throws Exception {
setRegistrationEmailAsUsername(true);
profilePage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertFalse(driver.findElements(By.id("username")).size() > 0);
// Revert
setRegistrationEmailAsUsername(false);
}
private void setEditUsernameAllowed(boolean allowed) {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setEditUsernameAllowed(allowed);
testRealm().update(testRealm);
}
private void setRegistrationEmailAsUsername(boolean allowed) {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setRegistrationEmailAsUsername(allowed);
testRealm().update(testRealm);
}
private void setDuplicateEmailsAllowed(boolean allowed) {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setDuplicateEmailsAllowed(allowed);
testRealm().update(testRealm);
}
@Test
public void changeUsername() {
// allow to edit the username in realm
setEditUsernameAllowed(true);
profilePage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("Tom", profilePage.getFirstName());
Assert.assertEquals("Brady", profilePage.getLastName());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
// All fields are required, so there should be an error when something is missing.
profilePage.updateProfile("", "New first", "New last", "new@email.com");
Assert.assertEquals("Please specify username.", profilePage.getError());
Assert.assertEquals("", profilePage.getUsername());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
events.assertEmpty();
// Change to the username already occupied by other user
profilePage.updateProfile("test-user-no-access@localhost", "New first", "New last", "new@email.com");
Assert.assertEquals("Username already exists.", profilePage.getError());
Assert.assertEquals("test-user-no-access@localhost", profilePage.getUsername());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
events.assertEmpty();
profilePage.updateProfile("test-user-new@localhost", "New first", "New last", "new@email.com");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
Assert.assertEquals("test-user-new@localhost", profilePage.getUsername());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("new@email.com", profilePage.getEmail());
// Revert
profilePage.updateProfile("test-user@localhost", "Tom", "Brady", "test-user@localhost");
}
private void addUser(String username, String email) {
UserRepresentation user = UserBuilder.create()
.username(username)
.enabled(true)
.email(email)
.firstName("first")
.lastName("last")
.build();
ApiUtil.createUserAndResetPasswordWithAdminClient(testRealm(), user, "password");
}
@Test
public void changeUsernameLoginWithOldUsername() {
addUser("change-username", "change-username@localhost");
setEditUsernameAllowed(true);
profilePage.open();
loginPage.login("change-username", "password");
profilePage.updateUsername("change-username-updated");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
profilePage.logout();
profilePage.open();
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("change-username", "password");
Assert.assertTrue(loginPage.isCurrent());
Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.login("change-username-updated", "password");
}
@Test
public void changeEmailLoginWithOldEmail() {
addUser("change-email", "change-username@localhost");
setEditUsernameAllowed(true);
profilePage.open();
loginPage.login("change-username@localhost", "password");
profilePage.updateEmail("change-username-updated@localhost");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
profilePage.logout();
profilePage.open();
Assert.assertTrue(loginPage.isCurrent());
loginPage.login("change-username@localhost", "password");
Assert.assertTrue(loginPage.isCurrent());
Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.login("change-username-updated@localhost", "password");
}
// KEYCLOAK-1534
@Test
public void changeEmailToExistingForbidden() {
profilePage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
// Change to the email, which some other user has
profilePage.updateProfile("New first", "New last", "test-user-no-access@localhost");
profilePage.assertCurrent();
Assert.assertEquals("Email already exists.", profilePage.getError());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("test-user-no-access@localhost", profilePage.getEmail());
events.assertEmpty();
// Change some other things, but not email
profilePage.updateProfile("New first", "New last", "test-user@localhost");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
Assert.assertEquals("New first", profilePage.getFirstName());
Assert.assertEquals("New last", profilePage.getLastName());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
events.expectAccount(EventType.UPDATE_PROFILE).assertEvent();
// Change email and other things to original values
profilePage.updateProfile("Tom", "Brady", "test-user@localhost");
events.expectAccount(EventType.UPDATE_PROFILE).assertEvent();
}
@Test
public void changeEmailToExistingAllowed() {
setDuplicateEmailsAllowed(true);
profilePage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
Assert.assertEquals("test-user@localhost", profilePage.getUsername());
Assert.assertEquals("test-user@localhost", profilePage.getEmail());
// Change to the email, which some other user has
profilePage.updateProfile("New first", "New last", "test-user-no-access@localhost");
Assert.assertEquals("Your account has been updated.", profilePage.getSuccess());
}
@Test
public void setupTotp() {
totpPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=totp").assertEvent();
Assert.assertTrue(totpPage.isCurrent());
Assert.assertFalse(driver.getPageSource().contains("Remove Google"));
// Error with false code
totpPage.configure(totp.generateTOTP(totpPage.getTotpSecret() + "123"));
Assert.assertEquals("Invalid authenticator code.", profilePage.getError());
totpPage.configure(totp.generateTOTP(totpPage.getTotpSecret()));
Assert.assertEquals("Mobile authenticator configured.", profilePage.getSuccess());
events.expectAccount(EventType.UPDATE_TOTP).assertEvent();
Assert.assertTrue(driver.getPageSource().contains("pficon-delete"));
totpPage.removeTotp();
events.expectAccount(EventType.REMOVE_TOTP).assertEvent();
}
@Test
public void changeProfileNoAccess() throws Exception {
profilePage.open();
loginPage.login("test-user-no-access@localhost", "password");
UserRepresentation noAccessUser = this.findUser("test-user-no-access@localhost");
events.expectLogin().client("account").user(noAccessUser.getId())
.detail(Details.USERNAME, "test-user-no-access@localhost")
.detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT).assertEvent();
Assert.assertTrue("Expected errorPage but was " + driver.getTitle() + " (" + driver.getCurrentUrl() + "). Page source: " + driver.getPageSource(), errorPage.isCurrent());
Assert.assertEquals("No access", errorPage.getError());
}
private void setEventsEnabled() {
RealmRepresentation testRealm = testRealm().toRepresentation();
testRealm.setEventsEnabled(true);
testRealm().update(testRealm);
}
@Test
public void viewLog() {
setEventsEnabled();
List<EventRepresentation> expectedEvents = new LinkedList<>();
loginPage.open();
loginPage.clickRegister();
registerPage.register("view", "log", "view-log@localhost", "view-log", "password", "password");
expectedEvents.add(events.poll());
expectedEvents.add(events.poll());
profilePage.open();
profilePage.updateProfile("view", "log2", "view-log@localhost");
expectedEvents.add(events.poll());
logPage.open();
Assert.assertTrue(logPage.isCurrent());
List<List<String>> actualEvents = logPage.getEvents();
Assert.assertEquals(expectedEvents.size(), actualEvents.size());
for (EventRepresentation e : expectedEvents) {
boolean match = false;
for (List<String> a : logPage.getEvents()) {
if (e.getType().toString().replace('_', ' ').toLowerCase().equals(a.get(1)) &&
e.getIpAddress().equals(a.get(2)) &&
e.getClientId().equals(a.get(3))) {
match = true;
break;
}
}
if (!match) {
Assert.fail("Event not found " + e.getType());
}
}
}
@Test
public void sessions() {
loginPage.open();
loginPage.clickRegister();
registerPage.register("view", "sessions", "view-sessions@localhost", "view-sessions", "password", "password");
EventRepresentation registerEvent = events.expectRegister("view-sessions", "view-sessions@localhost").assertEvent();
String userId = registerEvent.getUserId();
events.expectLogin().user(userId).detail(Details.USERNAME, "view-sessions").assertEvent();
sessionsPage.open();
Assert.assertTrue(sessionsPage.isCurrent());
List<List<String>> sessions = sessionsPage.getSessions();
Assert.assertEquals(1, sessions.size());
Assert.assertEquals("127.0.0.1", sessions.get(0).get(0));
// Create second session
try {
OAuthClient oauth2 = new OAuthClient();
oauth2.init(adminClient, driver2);
oauth2.doLogin("view-sessions", "password");
EventRepresentation login2Event = events.expectLogin().user(userId).detail(Details.USERNAME, "view-sessions").assertEvent();
sessionsPage.open();
sessions = sessionsPage.getSessions();
Assert.assertEquals(2, sessions.size());
sessionsPage.logoutAll();
events.expectLogout(registerEvent.getSessionId());
events.expectLogout(login2Event.getSessionId());
} finally {
driver2.close();
}
}
// More tests (including revoke) are in OAuthGrantTest and OfflineTokenTest
@Test
public void applications() {
applicationsPage.open();
loginPage.login("test-user@localhost", "password");
events.expectLogin().client("account").detail(Details.REDIRECT_URI, ACCOUNT_REDIRECT + "?path=applications").assertEvent();
Assert.assertTrue(applicationsPage.isCurrent());
Map<String, AccountApplicationsPage.AppEntry> apps = applicationsPage.getApplications();
Assert.assertThat(apps.keySet(), containsInAnyOrder("Account", "test-app", "test-app-scope", "third-party", "test-app-authz", "My Named Test App", "Test App Named - ${client_account}"));
AccountApplicationsPage.AppEntry accountEntry = apps.get("Account");
Assert.assertEquals(3, accountEntry.getRolesAvailable().size());
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account links in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("Manage account in Account"));
Assert.assertTrue(accountEntry.getRolesAvailable().contains("View profile in Account"));
Assert.assertEquals(1, accountEntry.getRolesGranted().size());
Assert.assertTrue(accountEntry.getRolesGranted().contains("Full Access"));
Assert.assertEquals(1, accountEntry.getProtocolMappersGranted().size());
Assert.assertTrue(accountEntry.getProtocolMappersGranted().contains("Full Access"));
AccountApplicationsPage.AppEntry testAppEntry = apps.get("test-app");
Assert.assertEquals(5, testAppEntry.getRolesAvailable().size());
Assert.assertTrue(testAppEntry.getRolesAvailable().contains("Offline access"));
Assert.assertTrue(testAppEntry.getRolesGranted().contains("Full Access"));
Assert.assertTrue(testAppEntry.getProtocolMappersGranted().contains("Full Access"));
AccountApplicationsPage.AppEntry thirdPartyEntry = apps.get("third-party");
Assert.assertEquals(2, thirdPartyEntry.getRolesAvailable().size());
Assert.assertTrue(thirdPartyEntry.getRolesAvailable().contains("Have User privileges"));
Assert.assertTrue(thirdPartyEntry.getRolesAvailable().contains("Have Customer User privileges in test-app"));
Assert.assertEquals(0, thirdPartyEntry.getRolesGranted().size());
Assert.assertEquals(0, thirdPartyEntry.getProtocolMappersGranted().size());
}
@Test
public void loginToSpecificPage() {
changePasswordPage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(changePasswordPage.isCurrent());
events.clear();
}
@Test
public void loginToSpecificPageWithReferrer() {
driver.navigate().to(changePasswordPage.getPath() + "?referrer=account");
System.out.println(driver.getCurrentUrl());
loginPage.login("test-user@localhost", "password");
System.out.println(driver.getCurrentUrl());
Assert.assertTrue(changePasswordPage.isCurrent());
events.clear();
}
@Test
public void testIdentityProviderCapitalization(){
loginPage.open();
Assert.assertEquals("GitHub", loginPage.findSocialButton("github").getText());
Assert.assertEquals("mysaml", loginPage.findSocialButton("mysaml").getText());
Assert.assertEquals("MyOIDC", loginPage.findSocialButton("myoidc").getText());
}
@Test
public void testIdentityProviderHiddenOnLoginPageIsVisbleInAccount(){
federatedIdentityPage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertNotNull(federatedIdentityPage.findAddProviderButton("myhiddenoidc"));
}
@Test
public void testInvalidReferrer() {
driver.navigate().to(profilePage.getPath() + "?referrer=test-app");
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(profilePage.isCurrent());
profilePage.backToApplication();
Assert.assertTrue(appPage.isCurrent());
driver.navigate().to(profilePage.getPath() + "?referrer=test-invalid&referrer_uri=http://localhost:8180/auth/realms/master/app/auth?test");
Assert.assertTrue(profilePage.isCurrent());
events.clear();
}
@Test
public void testReferrerLinkContents() {
RealmResource testRealm = testRealm();
List<ClientRepresentation> foundClients = testRealm.clients().findByClientId("named-test-app");
if (foundClients.isEmpty()) {
Assert.fail("Unable to find named-test-app");
}
ClientRepresentation namedClient = foundClients.get(0);
driver.navigate().to(profilePage.getPath() + "?referrer=" + namedClient.getClientId());
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(profilePage.isCurrent());
// When a client has a name provided, the name should be available to the back link
Assert.assertEquals("Back to " + namedClient.getName(), profilePage.getBackToApplicationLinkText());
Assert.assertEquals(namedClient.getBaseUrl(), profilePage.getBackToApplicationLinkHref());
foundClients = testRealm.clients().findByClientId("var-named-test-app");
if (foundClients.isEmpty()) {
Assert.fail("Unable to find var-named-test-app");
}
namedClient = foundClients.get(0);
driver.navigate().to(profilePage.getPath() + "?referrer=" + namedClient.getClientId());
Assert.assertTrue(profilePage.isCurrent());
// When a client has a name provided as a variable, the name should be resolved using a localized bundle and available to the back link
Assert.assertEquals("Back to Test App Named - Account", profilePage.getBackToApplicationLinkText());
Assert.assertEquals(namedClient.getBaseUrl(), profilePage.getBackToApplicationLinkHref());
foundClients = testRealm.clients().findByClientId("test-app");
if (foundClients.isEmpty()) {
Assert.fail("Unable to find test-app");
}
ClientRepresentation namelessClient = foundClients.get(0);
driver.navigate().to(profilePage.getPath() + "?referrer=" + namelessClient.getClientId());
Assert.assertTrue(profilePage.isCurrent());
// When a client has no name provided, the client-id should be available to the back link
Assert.assertEquals("Back to " + namelessClient.getClientId(), profilePage.getBackToApplicationLinkText());
Assert.assertEquals(namelessClient.getBaseUrl(), profilePage.getBackToApplicationLinkHref());
driver.navigate().to(profilePage.getPath() + "?referrer=test-invalid");
Assert.assertTrue(profilePage.isCurrent());
// When a client is invalid, the back link should not exist
Assert.assertNull(profilePage.getBackToApplicationLinkText());
events.clear();
}
}