/* * 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.cluster; import java.io.IOException; import java.util.List; import javax.mail.MessagingException; import org.jboss.arquillian.graphene.page.Page; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.AuthenticationSessionManager; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; import org.keycloak.testsuite.pages.LoginUpdateProfilePage; import org.keycloak.testsuite.util.UserBuilder; import org.openqa.selenium.Cookie; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; import static org.keycloak.testsuite.util.WaitUtils.pause; /** * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class AuthenticationSessionFailoverClusterTest extends AbstractFailoverClusterTest { private String userId; @Page protected LoginPage loginPage; @Page protected LoginPasswordUpdatePage updatePasswordPage; @Page protected LoginUpdateProfilePage updateProfilePage; @Page protected AppPage appPage; @Before public void setup() { try { adminClient.realm("test").remove(); } catch (Exception ignore) { } RealmRepresentation testRealm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class); adminClient.realms().create(testRealm); UserRepresentation user = UserBuilder.create() .username("login-test") .email("login@test.com") .enabled(true) .requiredAction(UserModel.RequiredAction.UPDATE_PASSWORD.toString()) .requiredAction(UserModel.RequiredAction.UPDATE_PROFILE.toString()) .build(); userId = ApiUtil.createUserAndResetPasswordWithAdminClient(adminClient.realm("test"), user, "password"); getCleanup().addUserId(userId); oauth.clientId("test-app"); } @After public void after() { adminClient.realm("test").remove(); } @Test public void failoverDuringAuthentication() throws Exception { boolean expectSuccessfulFailover = SESSION_CACHE_OWNERS >= 2; log.info("AUTHENTICATION FAILOVER TEST: cluster size = " + getClusterSize() + ", session-cache owners = " + SESSION_CACHE_OWNERS + " --> Testsing for " + (expectSuccessfulFailover ? "" : "UN") + "SUCCESSFUL session failover."); assertEquals(2, getClusterSize()); failoverTest(expectSuccessfulFailover); } protected void failoverTest(boolean expectSuccessfulFailover) throws IOException, MessagingException { loginPage.open(); String cookieValue1 = getAuthSessionCookieValue(); // Login and assert on "updatePassword" page loginPage.login("login-test", "password"); updatePasswordPage.assertCurrent(); // Route didn't change Assert.assertEquals(cookieValue1, getAuthSessionCookieValue()); log.info("Authentication session cookie: " + cookieValue1); setCurrentFailNodeForRoute(cookieValue1); failure(); pause(REBALANCE_WAIT); logFailoverSetup(); // Trigger the action now updatePasswordPage.changePassword("password", "password"); if (expectSuccessfulFailover) { //Action was successful updateProfilePage.assertCurrent(); String cookieValue2 = getAuthSessionCookieValue(); log.info("Authentication session cookie after failover: " + cookieValue2); // Cookie was moved to the second node Assert.assertEquals(cookieValue1.substring(0, 36), cookieValue2.substring(0, 36)); Assert.assertNotEquals(cookieValue1, cookieValue2); } else { loginPage.assertCurrent(); String error = loginPage.getError(); log.info("Failover not successful as expected. Error on login page: " + error); Assert.assertNotNull(error); loginPage.login("login-test", "password"); updatePasswordPage.changePassword("password", "password"); } updateProfilePage.assertCurrent(); // Successfully update profile and assert user logged updateProfilePage.update("John", "Doe3", "john@doe3.com"); appPage.assertCurrent(); } private String getAuthSessionCookieValue() { Cookie authSessionCookie = driver.manage().getCookieNamed(AuthenticationSessionManager.AUTH_SESSION_ID); Assert.assertNotNull(authSessionCookie); return authSessionCookie.getValue(); } }