keycloak-uncached

Details

diff --git a/testsuite/integration-arquillian/servers/eap7/pom.xml b/testsuite/integration-arquillian/servers/eap7/pom.xml
index 47bec1b..1b43939 100644
--- a/testsuite/integration-arquillian/servers/eap7/pom.xml
+++ b/testsuite/integration-arquillian/servers/eap7/pom.xml
@@ -1,20 +1,20 @@
 <?xml version="1.0"?>
 <!--
-  ~ 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.
-  -->
+~ 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.
+-->
 
 <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
@@ -118,6 +118,68 @@
         </profile>
         
         <profile>
+            <id>ssl</id>
+            <activation>
+                <property>
+                    <name>auth.server.ssl.required</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>xml-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>configure-adapter-subsystem-security</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>transform</goal>
+                                </goals>
+                                <configuration>
+                                    <transformationSets>
+                                        <transformationSet>
+                                            <dir>${keycloak.server.home}/standalone/configuration</dir>
+                                            <includes>
+                                                <include>standalone.xml</include>
+                                            </includes>
+                                            <stylesheet>src/main/xslt/security.xsl</stylesheet>
+                                            <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+                                        </transformationSet>
+                                    </transformationSets>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-resources-plugin</artifactId>
+                        <version>2.7</version>
+                        <executions>
+                            <execution>
+                                <id>copy-keystore</id>
+                                <phase>process-resources</phase>
+                                <goals>
+                                    <goal>copy-resources</goal>
+                                </goals>
+                                <configuration>
+                                    <outputDirectory>${keycloak.server.home}/standalone/configuration</outputDirectory>
+                                    <resources>
+                                        <resource>
+                                            <directory>src/main/keystore</directory>
+                                            <includes>
+                                                <include>keycloak.jks</include>
+                                                <include>keycloak.truststore</include>
+                                            </includes>
+                                        </resource>
+                                    </resources>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
             <id>jpa</id>
             <properties>
                 <jdbc.mvn.driver.deployment.dir>${keycloak.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
@@ -255,13 +317,14 @@
                 </plugins>
             </build>
         </profile>
+        
         <profile>
-            <id>ssl</id>
-            <activation>
-                <property>
-                    <name>auth.server.ssl.required</name>
-                </property>
-            </activation>
+            <id>auth-server-eap7-cluster</id>
+            <properties>
+                <session.cache.owners>1</session.cache.owners>
+                <offline.session.cache.owners>1</offline.session.cache.owners>
+                <login.failure.cache.owners>1</login.failure.cache.owners>
+            </properties>
             <build>
                 <plugins>
                     <plugin>
@@ -269,53 +332,62 @@
                         <artifactId>xml-maven-plugin</artifactId>
                         <executions>
                             <execution>
-                                <id>configure-adapter-subsystem-security</id>
+                                <id>configure-wildfly-datasource</id>
                                 <phase>process-resources</phase>
                                 <goals>
                                     <goal>transform</goal>
                                 </goals>
                                 <configuration>
                                     <transformationSets>
+                                        <!-- point KeycloakDS datasource to H2 running on TCP port -->
                                         <transformationSet>
                                             <dir>${keycloak.server.home}/standalone/configuration</dir>
                                             <includes>
-                                                <include>standalone.xml</include>
+                                                <include>standalone-ha.xml</include>
                                             </includes>
-                                            <stylesheet>src/main/xslt/security.xsl</stylesheet>
+                                            <stylesheet>src/main/xslt/datasource-jdbc-url.xsl</stylesheet>
                                             <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+                                            <parameters>
+                                                <parameter>
+                                                    <name>pool.name</name>
+                                                    <value>KeycloakDS</value>
+                                                </parameter>
+                                                <parameter>
+                                                    <name>jdbc.url</name>
+                                                    <value>jdbc:h2:tcp://${jboss.bind.address:localhost}:9092/mem:keycloak;DB_CLOSE_DELAY=-1</value>
+                                                </parameter>
+                                            </parameters>
+                                        </transformationSet>
+                                        <transformationSet>
+                                            <dir>${keycloak.server.home}/standalone/configuration</dir>
+                                            <includes>
+                                                <include>standalone-ha.xml</include>
+                                            </includes>
+                                            <stylesheet>src/main/xslt/ispn-cache-owners.xsl</stylesheet>
+                                            <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+                                            <parameters>
+                                                <parameter>
+                                                    <name>sessionCacheOwners</name>
+                                                    <value>${session.cache.owners}</value>
+                                                </parameter>
+                                                <parameter>
+                                                    <name>offlineSessionCacheOwners</name>
+                                                    <value>${offline.session.cache.owners}</value>
+                                                </parameter>
+                                                <parameter>
+                                                    <name>loginFailureCacheOwners</name>
+                                                    <value>${login.failure.cache.owners}</value>
+                                                </parameter>
+                                            </parameters>
                                         </transformationSet>
                                     </transformationSets>
                                 </configuration>
                             </execution>
                         </executions>
                     </plugin>
-                    <plugin>
-                        <artifactId>maven-resources-plugin</artifactId>
-                        <version>2.7</version>
-                        <executions>
-                            <execution>
-                                <id>copy-keystore</id>
-                                <phase>process-resources</phase>
-                                <goals>
-                                    <goal>copy-resources</goal>
-                                </goals>
-                                <configuration>
-                                     <outputDirectory>${keycloak.server.home}/standalone/configuration</outputDirectory>
-                                     <resources>
-                                         <resource>
-                                              <directory>src/main/keystore</directory>
-                                              <includes>
-                                                  <include>keycloak.jks</include>
-                                                  <include>keycloak.truststore</include>
-                                              </includes>
-                                         </resource>
-                                     </resources>
-                                </configuration>
-                            </execution>
-                        </executions>
-                    </plugin>
                 </plugins>
             </build>
         </profile>
+        
     </profiles>
 </project>
diff --git a/testsuite/integration-arquillian/servers/eap7/src/main/xslt/datasource-jdbc-url.xsl b/testsuite/integration-arquillian/servers/eap7/src/main/xslt/datasource-jdbc-url.xsl
new file mode 100644
index 0000000..589ee4c
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap7/src/main/xslt/datasource-jdbc-url.xsl
@@ -0,0 +1,36 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:j="urn:jboss:domain:4.0"
+                xmlns:ds="urn:jboss:domain:datasources:4.0"
+                xmlns:k="urn:jboss:domain:keycloak:1.1"
+                xmlns:sec="urn:jboss:domain:security:1.2"
+                version="2.0"
+                exclude-result-prefixes="xalan j ds k sec">
+
+    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+    <xsl:strip-space elements="*"/>
+
+
+    <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+    
+    <xsl:param name="pool.name" select="'KeycloakDS'"/>
+    <xsl:param name="jdbc.url" />
+
+    <!-- replace JDBC URL -->
+    <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+		         /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+                         /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name=$pool.name]
+                         /*[local-name()='connection-url' and starts-with(namespace-uri(), $nsDS)]">
+        <connection-url>
+            <xsl:value-of select="$jdbc.url"/>
+        </connection-url>
+    </xsl:template>
+
+    <!-- Copy everything else. -->
+    <xsl:template match="@*|node()">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()" />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/eap7/src/main/xslt/ispn-cache-owners.xsl b/testsuite/integration-arquillian/servers/eap7/src/main/xslt/ispn-cache-owners.xsl
new file mode 100644
index 0000000..7237d89
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap7/src/main/xslt/ispn-cache-owners.xsl
@@ -0,0 +1,40 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:j="urn:jboss:domain:4.0"
+                xmlns:i="urn:jboss:domain:infinispan:4.0"
+                version="2.0"
+                exclude-result-prefixes="xalan i">
+
+    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+    <xsl:strip-space elements="*"/>
+
+    <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+    
+    <xsl:param name="sessionCacheOwners" select="'1'"/>
+    <xsl:param name="offlineSessionCacheOwners" select="'1'"/>
+    <xsl:param name="loginFailureCacheOwners" select="'1'"/>
+
+    <xsl:template match="//i:cache-container/i:distributed-cache[@name='sessions']/@owners">
+        <xsl:attribute name="owners">
+            <xsl:value-of select="$sessionCacheOwners"/>
+        </xsl:attribute>
+    </xsl:template>
+    <xsl:template match="//i:cache-container/i:distributed-cache[@name='offlineSessions']/@owners">
+        <xsl:attribute name="owners">
+            <xsl:value-of select="$offlineSessionCacheOwners"/>
+        </xsl:attribute>
+    </xsl:template>
+    <xsl:template match="//i:cache-container/i:distributed-cache[@name='loginFailures']/@owners">
+        <xsl:attribute name="owners">
+            <xsl:value-of select="$loginFailureCacheOwners"/>
+        </xsl:attribute>
+    </xsl:template>
+
+    <!-- Copy everything else. -->
+    <xsl:template match="@*|node()">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()" />
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml
index 1546e35..68bc07f 100644
--- a/testsuite/integration-arquillian/servers/pom.xml
+++ b/testsuite/integration-arquillian/servers/pom.xml
@@ -54,6 +54,13 @@
                 <module>eap7</module>
             </modules>
         </profile>
+        <profile>
+            <id>auth-server-eap7-cluster</id>
+            <modules>
+                <module>eap7</module>
+                <module>wildfly-balancer</module>
+            </modules>
+        </profile>
     </profiles>    
 
 </project>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountManagement.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountManagement.java
index ba45448..0de5299 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountManagement.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountManagement.java
@@ -18,8 +18,8 @@ package org.keycloak.testsuite.auth.page.account;
 
 import javax.ws.rs.core.UriBuilder;
 import org.jboss.arquillian.graphene.findby.FindByJQuery;
-import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.testsuite.auth.page.AuthRealm;
+import org.keycloak.testsuite.page.PageWithLogOutAction;
 import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
@@ -29,7 +29,7 @@ import org.openqa.selenium.support.FindBy;
  * @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
  * @author tkyjovsk
  */
-public class AccountManagement extends AuthRealm {
+public class AccountManagement extends AuthRealm implements PageWithLogOutAction {
 
     @Override
     public UriBuilder createUriBuilder() {
@@ -76,7 +76,12 @@ public class AccountManagement extends AuthRealm {
     public void signOut() {
         signOutLink.click();
     }
-
+    
+    @Override
+    public void logOut() {
+        signOut();
+    }
+    
     public void account() {
         accountLink.click();
     }
@@ -108,4 +113,5 @@ public class AccountManagement extends AuthRealm {
     public void waitForAccountLinkPresent() {
         waitUntilElement(accountLink, "account link should be present").is().present();
     }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
index 18a5352..c46b59f 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
@@ -25,6 +25,7 @@ import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
 import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
 import org.keycloak.testsuite.console.page.fragment.Menu;
 import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import org.keycloak.testsuite.page.PageWithLogOutAction;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
@@ -32,8 +33,8 @@ import org.openqa.selenium.support.FindBy;
  *
  * @author Petr Mensik
  */
-public class AdminConsole extends AuthServer implements PageWithLoginUrl {
-    
+public class AdminConsole extends AuthServer implements PageWithLoginUrl, PageWithLogOutAction {
+
     public static final String ADMIN_REALM = "adminRealm";
 
     public AdminConsole() {
@@ -56,7 +57,7 @@ public class AdminConsole extends AuthServer implements PageWithLoginUrl {
 
     @Page
     private Menu menu;
-    
+
     @FindBy(xpath = "//div[@class='modal-dialog']")
     protected ModalDialog modalDialog;
 
@@ -79,7 +80,8 @@ public class AdminConsole extends AuthServer implements PageWithLoginUrl {
 
     @FindBy(css = "navbar-brand")
     protected WebElement brandLink;
-    
+
+    @Override
     public void logOut() {
         menu.logOut();
     }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/PageWithLogOutAction.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/PageWithLogOutAction.java
new file mode 100644
index 0000000..33fa4fc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/PageWithLogOutAction.java
@@ -0,0 +1,11 @@
+package org.keycloak.testsuite.page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public interface PageWithLogOutAction {
+    
+    public void logOut();
+    
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/SessionFailoverClusterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/SessionFailoverClusterTest.java
index 3597b75..f15444d 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/SessionFailoverClusterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/cluster/SessionFailoverClusterTest.java
@@ -1,17 +1,19 @@
 package org.keycloak.testsuite.cluster;
 
 import java.util.List;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.keycloak.representations.idm.RealmRepresentation;
 import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
-import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
 import static org.keycloak.testsuite.util.WaitUtils.pause;
 import org.openqa.selenium.Cookie;
+import org.keycloak.testsuite.page.AbstractPage;
+import org.keycloak.testsuite.page.PageWithLogOutAction;
+import org.junit.Before;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
 
 /**
  *
@@ -20,77 +22,131 @@ import org.openqa.selenium.Cookie;
 public class SessionFailoverClusterTest extends AbstractClusterTest {
 
     public static final String KEYCLOAK_SESSION_COOKIE = "KEYCLOAK_SESSION";
-    public static final String KEYCLOAK_IDENTITY_COOKIE = "KEYCLOAK_IDENTITY";
+
+    public static final Integer SESSION_CACHE_OWNERS = Integer.parseInt(System.getProperty("session.cache.owners", "1"));
+    public static final Integer OFFLINE_SESSION_CACHE_OWNERS = Integer.parseInt(System.getProperty("offline.session.cache.owners", "1"));
+    public static final Integer LOGIN_FAILURES_CACHE_OWNERS = Integer.parseInt(System.getProperty("login.failure.cache.owners", "1"));
+
+    public static final Integer REBALANCE_WAIT = Integer.parseInt(System.getProperty("rebalance.wait", "5000"));
 
     @Override
     public void addTestRealms(List<RealmRepresentation> testRealms) {
     }
 
+    @Before
+    public void beforeSessionFailover() {
+        log.info("Initial node failure");
+        failure();
+        pause(REBALANCE_WAIT);
+    }
+
     @Test
-    @Ignore("work in progress") // only works with owners="2" at the moment
     public void sessionFailover() {
+
+        boolean expectSuccessfulFailover = SESSION_CACHE_OWNERS >= getClusterSize();
+
+        log.info("SESSION FAILOVER TEST: cluster size = " + getClusterSize() + ", session-cache owners = " + SESSION_CACHE_OWNERS
+                + " --> Testsing for " + (expectSuccessfulFailover ? "" : "UN") + "SUCCESSFUL session failover.");
+
+        assertEquals(2, getClusterSize());
         
+        sessionFailover(expectSuccessfulFailover);
+    }
+
+    protected void sessionFailover(boolean expectSuccessfulFailover) {
+
         // LOGIN
-        accountPage.navigateTo();
-        driver.navigate().refresh();
-        pause(3000);
-        loginPage.form().login(ADMIN, ADMIN);
-        assertCurrentUrlStartsWith(accountPage);
-        
-        Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNotNull(sessionCookie);
+        Cookie sessionCookie = login(accountPage);
 
-        failure();
+        switchFailedNode();
 
-        // check if session survived backend failure
-        
-        driver.navigate().refresh();
-        pause(3000);
-        
-        assertCurrentUrlStartsWith(accountPage);
-        Cookie sessionCookieAfterFailover = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNotNull(sessionCookieAfterFailover);
-        assertEquals(sessionCookieAfterFailover.getValue(), sessionCookie.getValue());
+        // VERIFY
+        if (expectSuccessfulFailover) {
+            verifyLoggedIn(accountPage, sessionCookie);
+        } else {
+            verifyLoggedOut(accountPage);
+            // FIXME test fails if I put re-login here
+        }
 
-        failback();
-        iterateCurrentFailNode();
+        switchFailedNode();
 
-        // check if session survived backend failback
-        driver.navigate().refresh();
-        pause(3000);
-        assertCurrentUrlStartsWith(accountPage);
-        Cookie sessionCookieAfterFailback = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNotNull(sessionCookieAfterFailback);
-        assertEquals(sessionCookieAfterFailover.getValue(), sessionCookie.getValue());
+        // VERIFY again
+        if (expectSuccessfulFailover) {
+            verifyLoggedIn(accountPage, sessionCookie);
+        } else {
+            verifyLoggedOut(accountPage);
+            login(accountPage);
+        }
 
         // LOGOUT
-        accountPage.navigateTo();
-        accountPage.signOut();
+        logout(accountPage);
+        verifyLoggedOut(accountPage);
 
-        assertCurrentUrlDoesntStartWith(accountPage);
-        masterRealmPage.navigateTo();
-        sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNull(sessionCookie);
+        switchFailedNode();
+
+        // VERIFY
+        verifyLoggedOut(accountPage);
 
+    }
+
+    /**
+     * failure --> failback --> failure of next node
+     */
+    protected void switchFailedNode() {
+        assertFalse(controller.isStarted(getCurrentFailNode().getQualifier()));
+        
+        failback();
+        pause(REBALANCE_WAIT);
+        
+        iterateCurrentFailNode();
+        
         failure();
+        pause(REBALANCE_WAIT);
         
-        // check if session survived backend failure
-        driver.navigate().refresh();
-        pause(3000);
-        assertCurrentUrlDoesntStartWith(accountPage);
+        assertFalse(controller.isStarted(getCurrentFailNode().getQualifier()));
+    }
+
+    protected Cookie login(AbstractPage targetPage) {
+        targetPage.navigateTo();
+        assertCurrentUrlStartsWith(loginPage);
+        loginPage.form().login(ADMIN, ADMIN);
+        assertCurrentUrlStartsWith(targetPage);
+        Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
+        assertNotNull(sessionCookie);
+        return sessionCookie;
+    }
+
+    protected void logout(AbstractPage targetPage) {
+        if (!(targetPage instanceof PageWithLogOutAction)) {
+            throw new IllegalArgumentException(targetPage.getClass().getSimpleName() + " must implement PageWithLogOutAction interface");
+        }
+        targetPage.navigateTo();
+        assertCurrentUrlStartsWith(targetPage);
+        ((PageWithLogOutAction) targetPage).logOut();
+    }
+
+    protected Cookie verifyLoggedIn(AbstractPage targetPage, Cookie sessionCookieForVerification) {
+        // verify on realm path
         masterRealmPage.navigateTo();
-        sessionCookieAfterFailover = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNull(sessionCookieAfterFailover);
-        
-        failback();
-    
-        // check if session survived backend failback
+        Cookie sessionCookieOnRealmPath = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
+        assertNotNull(sessionCookieOnRealmPath);
+        assertEquals(sessionCookieOnRealmPath.getValue(), sessionCookieForVerification.getValue());
+        // verify on target page
+        targetPage.navigateTo();
+        assertCurrentUrlStartsWith(targetPage);
+        Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
+        assertNotNull(sessionCookie);
+        assertEquals(sessionCookie.getValue(), sessionCookieForVerification.getValue());
+        return sessionCookie;
+    }
+
+    protected void verifyLoggedOut(AbstractPage targetPage) {
+        // verify on target page
+        targetPage.navigateTo();
         driver.navigate().refresh();
-        pause(3000);
-        assertCurrentUrlDoesntStartWith(accountPage);
-        masterRealmPage.navigateTo();
-        sessionCookieAfterFailback = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
-        assertNull(sessionCookieAfterFailback);
+        assertCurrentUrlStartsWith(loginPage);
+        Cookie sessionCookie = driver.manage().getCookieNamed(KEYCLOAK_SESSION_COOKIE);
+        assertNull(sessionCookie);
     }
 
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
index f01f781..7c9e96c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
@@ -139,6 +139,66 @@
         </configuration>
     </container>
 
+    <group qualifier="auth-server-eap7-cluster">
+        <container qualifier="auth-server-eap7-balancer" mode="suite" >
+            <configuration>
+                <property name="enabled">${auth.server.eap7.cluster}</property>
+                <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+                <property name="jbossHome">${keycloak.balancer.home}</property>
+                <property name="jbossArguments">
+                    -Djboss.socket.binding.port-offset=${auth.server.port.offset} 
+                </property>
+                <property name="javaVmArguments">
+                    -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
+                    -Djava.net.preferIPv4Stack=true
+                </property>
+                <property name="outputToConsole">${frontend.console.output}</property>
+                <property name="managementPort">${auth.server.management.port}</property>
+                <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+            </configuration>
+        </container>
+        <container qualifier="auth-server-eap7-backend1" mode="manual" >
+            <configuration>
+                <property name="enabled">${auth.server.eap7.cluster}</property>
+                <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+                <property name="jbossHome">${keycloak.backend1.home}</property>
+                <property name="serverConfig">standalone-ha.xml</property>
+                <property name="jbossArguments">
+                    -Djboss.socket.binding.port-offset=${auth.server.backend1.port.offset} 
+                    -Djboss.node.name=node1
+                    ${adapter.test.props}
+                </property>
+                <property name="javaVmArguments">
+                    -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
+                    -Djava.net.preferIPv4Stack=true
+                </property>
+                <property name="outputToConsole">${backends.console.output}</property>
+                <property name="managementPort">${auth.server.backend1.management.port}</property>
+                <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+            </configuration>
+        </container>
+        <container qualifier="auth-server-eap7-backend2" mode="manual" >
+            <configuration>
+                <property name="enabled">${auth.server.eap7.cluster}</property>
+                <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+                <property name="jbossHome">${keycloak.backend2.home}</property>
+                <property name="serverConfig">standalone-ha.xml</property>
+                <property name="jbossArguments">
+                    -Djboss.socket.binding.port-offset=${auth.server.backend2.port.offset} 
+                    -Djboss.node.name=node2
+                    ${adapter.test.props}
+                </property>
+                <property name="javaVmArguments">
+                    -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
+                    -Djava.net.preferIPv4Stack=true
+                </property>
+                <property name="outputToConsole">${backends.console.output}</property>
+                <property name="managementPort">${auth.server.backend2.management.port}</property>
+                <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+            </configuration>
+        </container>
+    </group>
+
     <!-- PREVIOUS VERSIONS OF KEYCLOAK FOR MIGRATION TESTS -->
     
     <container qualifier="auth-server-wildfly-kc16" mode="manual" >
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 4e9bb83..50925af 100644
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -664,6 +664,7 @@
             </build>
         </profile>
 
+
         <profile>
             <id>auth-server-eap7</id>
             <properties>
@@ -743,6 +744,129 @@
             </build>
         </profile>
         
+        <profile>
+            <id>auth-server-eap7-cluster</id>
+            <properties>
+                <!--disable exclusion pattern for cluster test which is enabled by default in base/pom.xml-->
+                <exclude.cluster>-</exclude.cluster>
+                
+                <auth.server.container>auth-server-eap7-cluster</auth.server.container>
+                <startup.timeout.sec>300</startup.timeout.sec>
+                <adapter.test.props/>
+                <h2.version>1.3.173</h2.version>
+
+                <keycloak.balancer.home>${containers.home}/balancer/wildfly-balancer-${project.version}</keycloak.balancer.home>
+                <keycloak.backend1.home>${containers.home}/node1/keycloak-${version.server.dist}</keycloak.backend1.home>
+                <keycloak.backend2.home>${containers.home}/node2/keycloak-${version.server.dist}</keycloak.backend2.home>
+
+                <keycloak.home>${keycloak.backend1.home}</keycloak.home>
+                <jboss.server.config.dir>${keycloak.home}/standalone/configuration</jboss.server.config.dir>
+            </properties>
+            <dependencies>
+                <dependency>
+                    <groupId>org.wildfly</groupId>
+                    <artifactId>wildfly-arquillian-container-managed</artifactId>
+                </dependency>
+            </dependencies>
+            <build>
+                <pluginManagement>
+                    <plugins>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-enforcer-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>enforce-properties</id>
+                                    <goals>
+                                        <goal>enforce</goal>
+                                    </goals>
+                                    <configuration>
+                                        <rules>
+                                            <requireProperty>
+                                                <property>version.server.dist</property>
+                                            </requireProperty>
+                                        </rules>
+                                    </configuration>
+                                </execution>
+                            </executions>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-surefire-plugin</artifactId>
+                            <configuration>
+                                <systemPropertyVariables>
+                                    <run.h2>true</run.h2>
+                                    
+                                    <auth.server.eap7.cluster>true</auth.server.eap7.cluster>
+                                    <auth.server.undertow>false</auth.server.undertow>
+                                    <adapter.test.props>${adapter.test.props}</adapter.test.props>
+                                    
+                                    <keycloak.balancer.home>${keycloak.balancer.home}</keycloak.balancer.home>
+                                    <keycloak.backend1.home>${keycloak.backend1.home}</keycloak.backend1.home>
+                                    <keycloak.backend2.home>${keycloak.backend2.home}</keycloak.backend2.home>
+
+                                    <!--100-->
+                                    <auth.server.backend1.port.offset>101</auth.server.backend1.port.offset>
+                                    <auth.server.backend2.port.offset>102</auth.server.backend2.port.offset>
+                                    <!--8180-->
+                                    <auth.server.backend1.http.port>8181</auth.server.backend1.http.port>
+                                    <auth.server.backend2.http.port>8182</auth.server.backend2.http.port>
+                                    <!--8543-->
+                                    <auth.server.backend1.https.port>8544</auth.server.backend1.https.port>
+                                    <auth.server.backend2.https.port>8545</auth.server.backend2.https.port>
+                                    <!--10090-->
+                                    <auth.server.backend1.management.port>10091</auth.server.backend1.management.port>
+                                    <auth.server.backend2.management.port>10092</auth.server.backend2.management.port>
+                                    <!--10099-->
+                                    <auth.server.backend1.management.port.jmx>10100</auth.server.backend1.management.port.jmx>
+                                    <auth.server.backend2.management.port.jmx>10101</auth.server.backend2.management.port.jmx>
+                                </systemPropertyVariables>
+                            </configuration>
+                        </plugin>
+                        <plugin>
+                            <groupId>org.apache.maven.plugins</groupId>
+                            <artifactId>maven-dependency-plugin</artifactId>
+                            <executions>
+                                <execution>
+                                    <id>unpack-auth-server-wildfly</id>
+                                    <phase>generate-test-resources</phase>
+                                    <goals>
+                                        <goal>unpack</goal>
+                                    </goals>
+                                    <configuration>
+                                        <artifactItems>
+                                            <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-server-wildfly-balancer</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>zip</type>
+                                                <outputDirectory>${containers.home}/balancer</outputDirectory>
+                                            </artifactItem>
+                                            <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-server-eap7</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>zip</type>
+                                                <outputDirectory>${containers.home}/node1</outputDirectory>
+                                            </artifactItem>
+                                            <artifactItem>
+                                                <groupId>org.keycloak.testsuite</groupId>
+                                                <artifactId>integration-arquillian-server-eap7</artifactId>
+                                                <version>${project.version}</version>
+                                                <type>zip</type>
+                                                <outputDirectory>${containers.home}/node2</outputDirectory>
+                                            </artifactItem>
+                                        </artifactItems>
+                                        <overWriteIfNewer>true</overWriteIfNewer>
+                                    </configuration>
+                                </execution>
+                            </executions>
+                        </plugin>
+                    </plugins>
+                </pluginManagement>
+            </build>
+        </profile>
+
         <!-- Profiles for migration tests-->
         
         <profile>