keycloak-aplcache

Changes

core/src/main/java/org/keycloak/representations/adapters/action/UserStats.java 26(+0 -26)

Details

diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
index fe169ee..edc976e 100644
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
@@ -17,17 +17,29 @@
                 <constraints nullable="false"/>
             </column>
             <column name="ALLOWED_CLAIMS_MASK" type="BIGINT"/>
-            <column name="ENABLED" type="BOOLEAN"/>
-            <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN"/>
+            <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="NAME" type="VARCHAR(255)"/>
             <column name="NOT_BEFORE" type="INT"/>
-            <column name="PUBLIC_CLIENT" type="BOOLEAN"/>
+            <column name="PUBLIC_CLIENT" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="SECRET" type="VARCHAR(255)"/>
             <column name="BASE_URL" type="VARCHAR(255)"/>
-            <column name="BEARER_ONLY" type="BOOLEAN"/>
+            <column name="BEARER_ONLY" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="MANAGEMENT_URL" type="VARCHAR(255)"/>
-            <column name="SURROGATE_AUTH_REQUIRED" type="BOOLEAN"/>
-            <column name="DIRECT_GRANTS_ONLY" type="BOOLEAN"/>
+            <column name="SURROGATE_AUTH_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="DIRECT_GRANTS_ONLY" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="REALM_ID" type="VARCHAR(36)"/>
         </createTable>
         <createTable tableName="CLIENT_SESSION">
@@ -63,7 +75,7 @@
             </column>
             <column name="DEVICE" type="VARCHAR(255)"/>
             <column name="HASH_ITERATIONS" type="INT"/>
-            <column name="SALT" type="VARBINARY(16)"/>
+            <column name="SALT" type="BLOB(16)"/>
             <column name="TYPE" type="VARCHAR(255)"/>
             <column name="VALUE" type="VARCHAR(255)"/>
             <column name="USER_ID" type="VARCHAR(36)"/>
@@ -95,7 +107,9 @@
                 <constraints nullable="false"/>
             </column>
             <column name="APP_REALM_CONSTRAINT" type="VARCHAR(36)"/>
-            <column name="APPLICATION_ROLE" type="BOOLEAN"/>
+            <column name="APPLICATION_ROLE" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="DESCRIPTION" type="VARCHAR(255)"/>
             <column name="NAME" type="VARCHAR(255)"/>
             <column name="REALM_ID" type="VARCHAR(255)"/>
@@ -112,25 +126,43 @@
             <column name="ACCOUNT_THEME" type="VARCHAR(255)"/>
             <column name="ADMIN_THEME" type="VARCHAR(255)"/>
             <column name="EMAIL_THEME" type="VARCHAR(255)"/>
-            <column name="ENABLED" type="BOOLEAN"/>
-            <column name="EVENTS_ENABLED" type="BOOLEAN"/>
+            <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="EVENTS_ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="EVENTS_EXPIRATION" type="BIGINT"/>
             <column name="LOGIN_THEME" type="VARCHAR(255)"/>
             <column name="NAME" type="VARCHAR(255)"/>
             <column name="NOT_BEFORE" type="INT"/>
-            <column name="PASSWORD_CRED_GRANT_ALLOWED" type="BOOLEAN"/>
+            <column name="PASSWORD_CRED_GRANT_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="PASSWORD_POLICY" type="VARCHAR(255)"/>
             <column name="PRIVATE_KEY" type="VARCHAR(2048)"/>
             <column name="PUBLIC_KEY" type="VARCHAR(2048)"/>
-            <column name="REGISTRATION_ALLOWED" type="BOOLEAN"/>
-            <column name="REMEMBER_ME" type="BOOLEAN"/>
-            <column name="RESET_PASSWORD_ALLOWED" type="BOOLEAN"/>
-            <column name="SOCIAL" type="BOOLEAN"/>
+            <column name="REGISTRATION_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REMEMBER_ME" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="RESET_PASSWORD_ALLOWED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="SOCIAL" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="SSL_REQUIRED" type="VARCHAR(255)"/>
             <column name="SSO_IDLE_TIMEOUT" type="INT"/>
             <column name="SSO_MAX_LIFESPAN" type="INT"/>
-            <column name="UPDATE_PROFILE_ON_SOC_LOGIN" type="BOOLEAN"/>
-            <column name="VERIFY_EMAIL" type="BOOLEAN"/>
+            <column name="UPDATE_PROFILE_ON_SOC_LOGIN" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VERIFY_EMAIL" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="MASTER_ADMIN_APP" type="VARCHAR(36)"/>
         </createTable>
         <createTable tableName="REALM_APPLICATION">
@@ -169,8 +201,12 @@
                 <constraints nullable="false"/>
             </column>
             <column name="FORM_LABEL" type="VARCHAR(255)"/>
-            <column name="INPUT" type="BOOLEAN"/>
-            <column name="SECRET" type="BOOLEAN"/>
+            <column name="INPUT" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="SECRET" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="REALM_ID" type="VARCHAR(36)">
                 <constraints nullable="false"/>
             </column>
@@ -234,13 +270,19 @@
             </column>
             <column name="EMAIL" type="VARCHAR(255)"/>
             <column name="EMAIL_CONSTRAINT" type="VARCHAR(255)"/>
-            <column name="EMAIL_VERIFIED" type="BOOLEAN"/>
-            <column name="ENABLED" type="BOOLEAN"/>
+            <column name="EMAIL_VERIFIED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="FEDERATION_LINK" type="VARCHAR(255)"/>
             <column name="FIRST_NAME" type="VARCHAR(255)"/>
             <column name="LAST_NAME" type="VARCHAR(255)"/>
             <column name="REALM_ID" type="VARCHAR(255)"/>
-            <column name="TOTP" type="BOOLEAN"/>
+            <column name="TOTP" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="USERNAME" type="VARCHAR(255)"/>
         </createTable>
         <createTable tableName="USER_FEDERATION_CONFIG">
@@ -289,7 +331,9 @@
             <column name="LAST_SESSION_REFRESH" type="INT"/>
             <column name="LOGIN_USERNAME" type="VARCHAR(255)"/>
             <column name="REALM_ID" type="VARCHAR(255)"/>
-            <column name="REMEMBER_ME" type="BOOLEAN"/>
+            <column name="REMEMBER_ME" type="BOOLEAN" defaultValueBoolean="false">
+                <constraints nullable="false"/>
+            </column>
             <column name="STARTED" type="INT"/>
             <column name="USER_ID" type="VARCHAR(255)"/>
         </createTable>
@@ -331,46 +375,46 @@
         <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_F" tableName="CREDENTIAL"/>
         <addPrimaryKey columnNames="USER_FEDERATION_PROVIDER_ID, NAME" constraintName="CONSTRAINT_F9" tableName="USER_FEDERATION_CONFIG"/>
         <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FB" tableName="USER_ENTITY"/>
-        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" disabled="false" initiallyDeferred="false" tableName="APPLICATION_DEFAULT_ROLES"/>
-        <addUniqueConstraint columnNames="REALM_ID,NAME" constraintName="UK_B71CJLBENV945RB6GCON438AT" deferrable="false" disabled="false" initiallyDeferred="false" tableName="CLIENT"/>
-        <addUniqueConstraint columnNames="USERFEDERATIONPROVIDERS_ID" constraintName="UK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" disabled="false" initiallyDeferred="false" tableName="FED_PROVIDERS"/>
-        <addUniqueConstraint columnNames="REALM_ID,EMAIL_CONSTRAINT" constraintName="UK_DYKN684SL8UP1CRFEI6ECKHD7" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
-        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_DEFAULT_ROLES"/>
-        <addUniqueConstraint columnNames="NAME,APP_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2" deferrable="false" disabled="false" initiallyDeferred="false" tableName="KEYCLOAK_ROLE"/>
-        <addUniqueConstraint columnNames="REALM_ID" constraintName="UK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_APPLICATION"/>
-        <addUniqueConstraint columnNames="NAME" constraintName="UK_ORVSDMLA56612EAEFIQ6WL5OI" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM"/>
-        <addUniqueConstraint columnNames="REALM_ID,USERNAME" constraintName="UK_RU8TT6T700S9V50BU18WS5HA6" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_ROLE" constraintName="FK_11B7SGQW18I532811V7O2DV76" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REDIRECT_URIS" constraintName="FK_1BURS8PB4OUJ97H5WUPPAHV9F" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_PROVIDER" constraintName="FK_1FJ32F6PTOLW2QY60CD8N01E8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="FED_PROVIDERS" constraintName="FK_213LYQ09FKXQ8K8NY8DY3737T" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_REQUIRED_CREDENTIAL" constraintName="FK_5HG65LYBEVAVKQFKI3KPONH9V" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ATTRIBUTE" constraintName="FK_5HRM2VLF9QL5FU043KQEPOVBR" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_SOCIAL_LINK" constraintName="FK_68CJYS5UWM55UY823Y75XG4OM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_REQUIRED_ACTION" constraintName="FK_6QJ3W1JW9CVAFHE19BWSIUVMD" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="REALM" baseTableName="KEYCLOAK_ROLE" constraintName="FK_6VYQFE4CN4WLQ8R6KT5VDSJ5C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SMTP_CONFIG" constraintName="FK_70EJ8XDXGXD0B9HH6180IRR0O" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_71S3P0DIUXAWWQQSA528UBY2Q" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ATTRIBUTE" constraintName="FK_8SHXD6L3E9ATQUKACXGPFFPTW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="COMPOSITE" baseTableName="COMPOSITE_ROLE" constraintName="FK_A63WVEKFTU8JO1PNJ81E7MCE2" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
-        <addForeignKeyConstraint baseColumnNames="SESSION_ID" baseTableName="CLIENT_SESSION" constraintName="FK_B4AO2VCVAT6UKAU74WBWTFQO1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_SESSION"/>
-        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ROLE_MAPPING" constraintName="FK_C4FQV34P1MBYLLOXANG7B1Q3L" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="USERFEDERATIONPROVIDERS_ID" baseTableName="FED_PROVIDERS" constraintName="FK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_EVUDB1PPW84OXFAX2DRS03ICC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="CHILD_ROLE" baseTableName="COMPOSITE_ROLE" constraintName="FK_GR7THLLB9LU8Q4VQA4524JJY8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
-        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_EVENTS_LISTENERS" constraintName="FK_H846O4H0W8EPX5NXEV9F5Y69J" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="WEB_ORIGINS" constraintName="FK_LOJPHO213XCX4WNKOG82SSRFY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_OUSE064PLMLR732LXJCN1Q5F1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT" constraintName="FK_P56CTINXXB9GSK57FO49F9TAC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="CREDENTIAL" constraintName="FK_PFYR0GLASQYL0DEI3KL69R6V0" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
-        <addForeignKeyConstraint baseColumnNames="APPLICATION" baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_APP" baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SOCIAL_CONFIG" constraintName="FK_SV5I3C2TI7G0G922FGE683SOV" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
-        <addForeignKeyConstraint baseColumnNames="USER_FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_CONFIG" constraintName="FK_T13HPU1J94R2EBPEKR39X5EU5" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_8AELWNIBJI49AVXSRTUF6XJOW" tableName="APPLICATION_DEFAULT_ROLES"/>
+        <addUniqueConstraint columnNames="REALM_ID,NAME" constraintName="UK_B71CJLBENV945RB6GCON438AT" tableName="CLIENT"/>
+        <addUniqueConstraint columnNames="USERFEDERATIONPROVIDERS_ID" constraintName="UK_DCCIRJLIPU1478VQC89DID88C" tableName="FED_PROVIDERS"/>
+        <addUniqueConstraint columnNames="REALM_ID,EMAIL_CONSTRAINT" constraintName="UK_DYKN684SL8UP1CRFEI6ECKHD7" tableName="USER_ENTITY"/>
+        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_H4WPD7W4HSOOLNI3H0SW7BTJE" tableName="REALM_DEFAULT_ROLES"/>
+        <addUniqueConstraint columnNames="NAME,APP_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2" tableName="KEYCLOAK_ROLE"/>
+        <addUniqueConstraint columnNames="REALM_ID" constraintName="UK_L5QGA3RFME47335JY8JXYXH3I" tableName="REALM_APPLICATION"/>
+        <addUniqueConstraint columnNames="NAME" constraintName="UK_ORVSDMLA56612EAEFIQ6WL5OI" tableName="REALM"/>
+        <addUniqueConstraint columnNames="REALM_ID,USERNAME" constraintName="UK_RU8TT6T700S9V50BU18WS5HA6" tableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_ROLE" constraintName="FK_11B7SGQW18I532811V7O2DV76" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REDIRECT_URIS" constraintName="FK_1BURS8PB4OUJ97H5WUPPAHV9F" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_PROVIDER" constraintName="FK_1FJ32F6PTOLW2QY60CD8N01E8" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="FED_PROVIDERS" constraintName="FK_213LYQ09FKXQ8K8NY8DY3737T" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_REQUIRED_CREDENTIAL" constraintName="FK_5HG65LYBEVAVKQFKI3KPONH9V" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ATTRIBUTE" constraintName="FK_5HRM2VLF9QL5FU043KQEPOVBR" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_SOCIAL_LINK" constraintName="FK_68CJYS5UWM55UY823Y75XG4OM" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_REQUIRED_ACTION" constraintName="FK_6QJ3W1JW9CVAFHE19BWSIUVMD" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="REALM" baseTableName="KEYCLOAK_ROLE" constraintName="FK_6VYQFE4CN4WLQ8R6KT5VDSJ5C" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SMTP_CONFIG" constraintName="FK_70EJ8XDXGXD0B9HH6180IRR0O" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_71S3P0DIUXAWWQQSA528UBY2Q" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_8AELWNIBJI49AVXSRTUF6XJOW" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ATTRIBUTE" constraintName="FK_8SHXD6L3E9ATQUKACXGPFFPTW" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="COMPOSITE" baseTableName="COMPOSITE_ROLE" constraintName="FK_A63WVEKFTU8JO1PNJ81E7MCE2" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="SESSION_ID" baseTableName="CLIENT_SESSION" constraintName="FK_B4AO2VCVAT6UKAU74WBWTFQO1" referencedColumnNames="ID" referencedTableName="USER_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ROLE_MAPPING" constraintName="FK_C4FQV34P1MBYLLOXANG7B1Q3L" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USERFEDERATIONPROVIDERS_ID" baseTableName="FED_PROVIDERS" constraintName="FK_DCCIRJLIPU1478VQC89DID88C" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_EVUDB1PPW84OXFAX2DRS03ICC" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="CHILD_ROLE" baseTableName="COMPOSITE_ROLE" constraintName="FK_GR7THLLB9LU8Q4VQA4524JJY8" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_H4WPD7W4HSOOLNI3H0SW7BTJE" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_EVENTS_LISTENERS" constraintName="FK_H846O4H0W8EPX5NXEV9F5Y69J" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_L5QGA3RFME47335JY8JXYXH3I" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="WEB_ORIGINS" constraintName="FK_LOJPHO213XCX4WNKOG82SSRFY" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_OUSE064PLMLR732LXJCN1Q5F1" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT" constraintName="FK_P56CTINXXB9GSK57FO49F9TAC" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="CREDENTIAL" constraintName="FK_PFYR0GLASQYL0DEI3KL69R6V0" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION" baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_APP" baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SOCIAL_CONFIG" constraintName="FK_SV5I3C2TI7G0G922FGE683SOV" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_CONFIG" constraintName="FK_T13HPU1J94R2EBPEKR39X5EU5" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
     </changeSet>
 </databaseChangeLog>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
index 3f95c43..8344266 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
@@ -50,8 +50,8 @@
         <addPrimaryKey columnNames="CLIENT_ID, NAME" constraintName="CONSTRAINT_3C" tableName="CLIENT_ATTRIBUTES"/>
         <addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTRAINT_5E" tableName="CLIENT_SESSION_NOTE"/>
         <addPrimaryKey columnNames="APPLICATION_ID, NAME" constraintName="CONSTRAINT_84" tableName="APP_NODE_REGISTRATIONS"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_ATTRIBUTES" constraintName="FK3C47C64BEACCA966" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
-        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_NOTE" constraintName="FK5EDFB00FF51C2736" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
-        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APP_NODE_REGISTRATIONS" constraintName="FK8454723BA992F594" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_ATTRIBUTES" constraintName="FK3C47C64BEACCA966" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_NOTE" constraintName="FK5EDFB00FF51C2736" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APP_NODE_REGISTRATIONS" constraintName="FK8454723BA992F594" referencedColumnNames="ID" referencedTableName="CLIENT"/>
     </changeSet>
 </databaseChangeLog>
\ No newline at end of file
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectToMapMapper.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectToMapMapper.java
index 1de0867..14ada88 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectToMapMapper.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectToMapMapper.java
@@ -23,6 +23,11 @@ public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
             String key = entry.getKey();
             Object value = entry.getValue();
 
+            // Workaround as manually inserted numbers into mongo may be treated as "Double"
+            if (value instanceof Double && context.getGenericTypes().get(1) == Integer.class) {
+                value = ((Double)value).intValue();
+            }
+
             if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
                 key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
             }
diff --git a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
index ddc68aa..6feb655 100755
--- a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
+++ b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java
@@ -10,8 +10,7 @@ public interface AdapterConstants {
     public static final String K_LOGOUT = "k_logout";
     public static final String K_VERSION = "k_version";
     public static final String K_PUSH_NOT_BEFORE = "k_push_not_before";
-    public static final String K_GET_USER_STATS = "k_get_user_stats";
-    public static final String K_GET_SESSION_STATS = "k_get_session_stats";
+    public static final String K_TEST_AVAILABLE = "k_test_available";
     public static final String K_QUERY_BEARER_TOKEN = "k_query_bearer_token";
 
     // This param name is defined again in Keycloak Subsystem class
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/GlobalRequestResult.java b/core/src/main/java/org/keycloak/representations/adapters/action/GlobalRequestResult.java
new file mode 100644
index 0000000..d8ab434
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/GlobalRequestResult.java
@@ -0,0 +1,60 @@
+package org.keycloak.representations.adapters.action;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Result of the "global" request (like push notBefore or logoutAll), which is send to all cluster nodes
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class GlobalRequestResult {
+
+    private List<String> successRequests;
+    private List<String> failedRequests;
+
+    public void addSuccessRequest(String reqUri) {
+        if (successRequests == null) {
+            successRequests = new ArrayList<String>();
+        }
+        successRequests.add(reqUri);
+    }
+
+    public void addFailedRequest(String reqUri) {
+        if (failedRequests == null) {
+            failedRequests = new ArrayList<String>();
+        }
+        failedRequests.add(reqUri);
+    }
+
+    public void addAllSuccessRequests(List<String> reqUris) {
+        if (successRequests == null) {
+            successRequests = new ArrayList<String>();
+        }
+        successRequests.addAll(reqUris);
+    }
+
+    public void addAllFailedRequests(List<String> reqUris) {
+        if (failedRequests == null) {
+            failedRequests = new ArrayList<String>();
+        }
+        failedRequests.addAll(reqUris);
+    }
+
+    public void addAll(GlobalRequestResult merged) {
+        if (merged.getSuccessRequests() != null && merged.getSuccessRequests().size() > 0) {
+            addAllSuccessRequests(merged.getSuccessRequests());
+        }
+        if (merged.getFailedRequests() != null && merged.getFailedRequests().size() > 0) {
+            addAllFailedRequests(merged.getFailedRequests());
+        }
+    }
+
+    public List<String> getSuccessRequests() {
+        return successRequests;
+    }
+
+    public List<String> getFailedRequests() {
+        return failedRequests;
+    }
+}
diff --git a/core/src/main/java/org/keycloak/representations/adapters/action/TestAvailabilityAction.java b/core/src/main/java/org/keycloak/representations/adapters/action/TestAvailabilityAction.java
new file mode 100644
index 0000000..13fc1d8
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/action/TestAvailabilityAction.java
@@ -0,0 +1,22 @@
+package org.keycloak.representations.adapters.action;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TestAvailabilityAction extends AdminAction {
+
+    public static final String TEST_AVAILABILITY = "TEST_AVAILABILITY";
+
+    public TestAvailabilityAction() {
+    }
+
+    public TestAvailabilityAction(String id, int expiration, String resource) {
+        super(id, expiration, resource, TEST_AVAILABILITY);
+    }
+
+    @Override
+    public boolean validate() {
+        return TEST_AVAILABILITY.equals(action);
+    }
+
+}
diff --git a/core/src/main/java/org/keycloak/util/UriUtils.java b/core/src/main/java/org/keycloak/util/UriUtils.java
index 873283f..8532c5b 100644
--- a/core/src/main/java/org/keycloak/util/UriUtils.java
+++ b/core/src/main/java/org/keycloak/util/UriUtils.java
@@ -1,12 +1,15 @@
 package org.keycloak.util;
 
 import java.net.URI;
+import java.util.regex.Pattern;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 public class UriUtils {
 
+    private static final Pattern originPattern = Pattern.compile("(http://|https://)[\\w]+(\\.[\\w]+)*(:[\\d]{2,5})?");
+
     public static String getOrigin(URI uri) {
         return getOrigin(uri.toString());
     }
@@ -16,4 +19,8 @@ public class UriUtils {
         return u.substring(0, u.indexOf('/', 8));
     }
 
+    public static boolean isOrigin(String url) {
+        return originPattern.matcher(url).matches();
+    }
+
 }
diff --git a/core/src/test/java/org/keycloak/util/UriUtilsTest.java b/core/src/test/java/org/keycloak/util/UriUtilsTest.java
new file mode 100644
index 0000000..52d484d
--- /dev/null
+++ b/core/src/test/java/org/keycloak/util/UriUtilsTest.java
@@ -0,0 +1,44 @@
+package org.keycloak.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class UriUtilsTest {
+
+    @Test
+    public void testOrigins() {
+        assertValid("http://test");
+        assertValid("http://test:8080");
+        assertValid("https://test");
+        assertValid("http://test.com");
+        assertValid("https://test.com");
+        assertValid("https://test.com:8080");
+        assertValid("http://sub.test.com");
+        assertValid("https://sub.test.com");
+        assertValid("https://sub.test.com:8080");
+        assertValid("http://192.168.123.123");
+        assertValid("https://192.168.123.123");
+        assertValid("https://192.168.123.123:8080");
+
+        assertInvalid("https://test/");
+        assertInvalid("{");
+        assertInvalid("https://{}");
+        assertInvalid("https://)");
+        assertInvalid("http://test:test");
+        assertInvalid("http://test:8080:8080");
+    }
+
+    public void assertValid(String origin) {
+        assertTrue(UriUtils.isOrigin(origin));
+    }
+
+    public void assertInvalid(String origin) {
+        assertFalse(UriUtils.isOrigin(origin));
+    }
+
+}
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index 3f0e20a..a2a3a96 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -141,6 +141,14 @@
                 "/product-portal/*"
             ],
             "secret": "password"
+        },
+        {
+            "name": "database-service",
+            "enabled": true,
+            "adminUrl": "/database",
+            "baseUrl": "/database",
+            "bearerOnly": true,
+            "secret": "password"
         }
     ],
     "oauthClients": [
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index fe33ab1..b46b13f 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -440,6 +440,42 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'ApplicationCredentialsCtrl'
         })
+        .when('/realms/:realm/applications/:application/clustering', {
+            templateUrl : 'partials/application-clustering.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationClusteringCtrl'
+        })
+        .when('/register-node/realms/:realm/applications/:application/clustering', {
+            templateUrl : 'partials/application-clustering-node.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationClusteringNodeCtrl'
+        })
+        .when('/realms/:realm/applications/:application/clustering/:node', {
+            templateUrl : 'partials/application-clustering-node.html',
+            resolve : {
+                realm : function(RealmLoader) {
+                    return RealmLoader();
+                },
+                application : function(ApplicationLoader) {
+                    return ApplicationLoader();
+                }
+            },
+            controller : 'ApplicationClusteringNodeCtrl'
+        })
         .when('/realms/:realm/applications/:application/certificate', {
             templateUrl : 'partials/application-keys.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
index 6d68bac..dc4d353 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/applications.js
@@ -592,15 +592,134 @@ module.controller('ApplicationRevocationCtrl', function($scope, realm, applicati
     $scope.setNotBeforeNow = function() {
         $scope.application.notBefore = new Date().getTime()/1000;
         Application.update({ realm : realm.realm, application: $scope.application.id}, $scope.application, function () {
-            Notifications.success('Not Before cleared for application.');
+            Notifications.success('Not Before set for application.');
             refresh();
         });
     }
     $scope.pushRevocation = function() {
-        ApplicationPushRevocation.save({realm : realm.realm, application: $scope.application.id}, function () {
-            Notifications.success('Push sent for application.');
+        ApplicationPushRevocation.save({realm : realm.realm, application: $scope.application.id}, function (globalReqResult) {
+            var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
+            var failedCount  = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
+
+            if (successCount==0 && failedCount==0) {
+                Notifications.warn('No push sent. No admin URI configured or no registered cluster nodes available');
+            } else if (failedCount > 0) {
+                var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : '';
+                Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
+            } else {
+                Notifications.success('Successfully push notBefore to: ' + globalReqResult.successRequests);
+            }
         });
     }
 
 });
 
+module.controller('ApplicationClusteringCtrl', function($scope, application, Application, ApplicationTestNodesAvailable, realm, $location, $route, Notifications, TimeUnit) {
+    $scope.application = application;
+    $scope.realm = realm;
+
+    var oldCopy = angular.copy($scope.application);
+    $scope.changed = false;
+
+    $scope.$watch('application', function() {
+        if (!angular.equals($scope.application, oldCopy)) {
+            $scope.changed = true;
+        }
+    }, true);
+
+    $scope.application.nodeReRegistrationTimeoutUnit = TimeUnit.autoUnit(application.nodeReRegistrationTimeout);
+    $scope.application.nodeReRegistrationTimeout = TimeUnit.toUnit(application.nodeReRegistrationTimeout, $scope.application.nodeReRegistrationTimeoutUnit);
+    $scope.$watch('application.nodeReRegistrationTimeoutUnit', function(to, from) {
+        $scope.application.nodeReRegistrationTimeout = TimeUnit.convert($scope.application.nodeReRegistrationTimeout, from, to);
+    });
+
+    $scope.save = function() {
+        var appCopy = angular.copy($scope.application);
+        delete appCopy['nodeReRegistrationTimeoutUnit'];
+        appCopy.nodeReRegistrationTimeout = TimeUnit.toSeconds($scope.application.nodeReRegistrationTimeout, $scope.application.nodeReRegistrationTimeoutUnit)
+        Application.update({ realm : realm.realm, application : application.id }, appCopy, function () {
+            $route.reload();
+            Notifications.success('Your changes have been saved to the application.');
+        });
+    };
+
+    $scope.reset = function() {
+        $route.reload();
+    };
+
+    $scope.testNodesAvailable = function() {
+        console.log('testNodesAvailable');
+        ApplicationTestNodesAvailable.get({ realm : realm.realm, application : application.id }, function(globalReqResult) {
+            $route.reload();
+
+            var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
+            var failedCount  = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
+
+            if (successCount==0 && failedCount==0) {
+                Notifications.warn('No requests sent. No admin URI configured or no registered cluster nodes available');
+            } else if (failedCount > 0) {
+                var msgStart = successCount>0 ? 'Successfully verify availability for ' + globalReqResult.successRequests + ' . ' : '';
+                Notifications.error(msgStart + 'Failed to verify availability for: ' + globalReqResult.failedRequests + '. Fix or unregister failed cluster nodes and try again');
+            } else {
+                Notifications.success('Successfully sent requests to: ' + globalReqResult.successRequests);
+            }
+        });
+    };
+
+    if (application.registeredNodes) {
+        var nodeRegistrations = [];
+        for (node in application.registeredNodes) {
+            reg = {
+                host: node,
+                lastRegistration: new Date(application.registeredNodes[node] * 1000)
+            }
+            nodeRegistrations.push(reg);
+        }
+
+        $scope.nodeRegistrations = nodeRegistrations;
+    };
+});
+
+module.controller('ApplicationClusteringNodeCtrl', function($scope, application, Application, ApplicationClusterNode, realm, $location, $routeParams, Notifications) {
+    $scope.application = application;
+    $scope.realm = realm;
+    $scope.create = !$routeParams.node;
+
+    $scope.save = function() {
+        console.log('registerNode: ' + $scope.node.host);
+        ApplicationClusterNode.save({ realm : realm.realm, application : application.id , node: $scope.node.host }, function() {
+            Notifications.success('Node ' + $scope.node.host + ' registered successfully.');
+            $location.url('/realms/' + realm.realm + '/applications/' + application.id +  '/clustering');
+        });
+    }
+
+    $scope.unregisterNode = function() {
+        console.log('unregisterNode: ' + $scope.node.host);
+        ApplicationClusterNode.remove({ realm : realm.realm, application : application.id , node: $scope.node.host }, function() {
+            Notifications.success('Node ' + $scope.node.host + ' unregistered successfully.');
+            $location.url('/realms/' + realm.realm + '/applications/' + application.id +  '/clustering');
+        });
+    }
+
+    if ($scope.create) {
+        $scope.node = {}
+        $scope.registered = false;
+    } else {
+        var lastRegTime = application.registeredNodes[$routeParams.node];
+
+        if (lastRegTime) {
+            $scope.registered = true;
+            $scope.node = {
+                host: $routeParams.node,
+                lastRegistration: new Date(lastRegTime * 1000)
+            }
+
+        } else {
+            $scope.registered = false;
+            $scope.node = {
+                host: $routeParams.node
+            }
+        }
+    }
+});
+
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 86e9836..c8c37ba 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -766,10 +766,18 @@ module.controller('RealmSessionStatsCtrl', function($scope, realm, stats, RealmA
     console.log(stats);
 
     $scope.logoutAll = function() {
-        RealmLogoutAll.save({realm : realm.realm}, function () {
-            Notifications.success('Logged out all users');
-            RealmApplicationSessionStats.get({realm: realm.realm}, function(updated) {
-                Notifications.success('Logged out all users');
+        RealmLogoutAll.save({realm : realm.realm}, function (globalReqResult) {
+            var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
+            var failedCount  = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
+
+            if (failedCount > 0) {
+                var msgStart = successCount>0 ? 'Successfully logout all users under: ' + globalReqResult.successRequests + ' . ' : '';
+                Notifications.error(msgStart + 'Failed to logout users under: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
+            } else {
+                Notifications.success('Successfully logout all users from the realm');
+            }
+
+            RealmApplicationSessionStats.query({realm: realm.realm}, function(updated) {
                 $scope.stats = updated;
             })
         });
@@ -809,13 +817,23 @@ module.controller('RealmRevocationCtrl', function($scope, Realm, RealmPushRevoca
     }
     $scope.setNotBeforeNow = function() {
         Realm.update({ realm: realm.realm, notBefore : new Date().getTime()/1000}, function () {
-            Notifications.success('Not Before cleared for realm.');
+            Notifications.success('Not Before set for realm.');
             reset();
         });
     }
     $scope.pushRevocation = function() {
-        RealmPushRevocation.save({ realm: realm.realm}, function () {
-            Notifications.success('Push sent for realm.');
+        RealmPushRevocation.save({ realm: realm.realm}, function (globalReqResult) {
+            var successCount = globalReqResult.successRequests ? globalReqResult.successRequests.length : 0;
+            var failedCount  = globalReqResult.failedRequests ? globalReqResult.failedRequests.length : 0;
+
+            if (successCount==0 && failedCount==0) {
+                Notifications.warn('No push sent. No admin URI configured or no registered cluster nodes available');
+            } else if (failedCount > 0) {
+                var msgStart = successCount>0 ? 'Successfully push notBefore to: ' + globalReqResult.successRequests + ' . ' : '';
+                Notifications.error(msgStart + 'Failed to push notBefore to: ' + globalReqResult.failedRequests + '. Verify availability of failed hosts and try again');
+            } else {
+                Notifications.success('Successfully push notBefore to all configured applications');
+            }
         });
     }
 
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index b87310b..ee4d635 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -693,6 +693,20 @@ module.factory('ApplicationPushRevocation', function($resource) {
     });
 });
 
+module.factory('ApplicationClusterNode', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/nodes/:node', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+
+module.factory('ApplicationTestNodesAvailable', function($resource) {
+    return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/test-nodes-available', {
+        realm : '@realm',
+        application : "@application"
+    });
+});
+
 module.factory('ApplicationCertificate', function($resource) {
     return $resource(authUrl + '/admin/realms/:realm/applications-by-id/:application/certificates', {
             realm : '@realm',
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering.html
new file mode 100644
index 0000000..3d5c850
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering.html
@@ -0,0 +1,75 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <kc-navigation-application></kc-navigation-application>
+    <div id="content">
+        <ol class="breadcrumb" data-ng-hide="create">
+            <li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
+            <li class="active">Clustering</li>
+        </ol>
+        <h2 data-ng-hide="create"><span>{{application.name}}</span> Clustering</h2>
+        <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications">
+            <legend><span class="text">Basic configuration</span></legend>
+            <fieldset >
+                <div class="form-group clearfix">
+                    <label class="col-sm-2 control-label" for="nodeReRegistrationTimeout">Node Re-registration Timeout</label>
+                    <div class="col-sm-5">
+                        <div class="row">
+                            <div class="col-sm-4">
+                                <input class="form-control" type="number" required
+                                       max="31536000" data-ng-model="application.nodeReRegistrationTimeout"
+                                       id="nodeReRegistrationTimeout" name="nodeReRegistrationTimeout"/>
+                            </div>
+                            <div class="col-sm-4 select-kc">
+                                <select name="nodeReRegistrationTimeoutUnit" data-ng-model="application.nodeReRegistrationTimeoutUnit" >
+                                    <option data-ng-selected="!application.nodeReRegistrationTimeoutUnit">Seconds</option>
+                                    <option>Minutes</option>
+                                    <option>Hours</option>
+                                    <option>Days</option>
+                                </select>
+                            </div>
+                        </div>
+                    </div>
+                    <span tooltip-placement="right" tooltip="Interval to specify max time for registered application cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak" class="fa fa-info-circle"></span>
+                </div>
+                <div class="pull-right form-actions" data-ng-show="access.manageRealm">
+                    <button data-kc-reset data-ng-show="changed">Clear changes</button>
+                    <button data-kc-save data-ng-show="changed">Save</button>
+                </div>
+            </fieldset>
+
+            <fieldset>
+                <legend><span class="text">Registered cluster nodes</span></legend>
+                <table class="table table-striped table-bordered">
+                    <thead>
+                        <tr>
+                            <th class="kc-table-actions" colspan="3" data-ng-show="access.manageApplications">
+                                <div class="pull-right">
+                                    <a class="btn btn-primary" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
+                                       tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Register node manually</a>
+                                </div>
+                            </th>
+                        </tr>
+                        <tr data-ng-hide="!nodeRegistrations || nodeRegistrations.length == 0">
+                            <th>Node host</th>
+                            <th>Last registration</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr ng-repeat="node in nodeRegistrations">
+                            <td><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering/{{node.host}}">{{node.host}}</a></td>
+                            <td>{{node.lastRegistration}}</td>
+                        </tr>
+                        <tr data-ng-show="!nodeRegistrations || nodeRegistrations.length == 0">
+                            <td>No registered cluster nodes available</td>
+                        </tr>
+                    </tbody>
+                </table>
+                <div class="pull-right form-actions" data-ng-show="access.manageRealm && nodeRegistrations && nodeRegistrations.length > 0">
+                    <a class="btn btn-primary" data-ng-click="testNodesAvailable()">Test cluster availability</a>
+                </div>
+            </fieldset>
+        </form>
+
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering-node.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering-node.html
new file mode 100644
index 0000000..c456ac2
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/application-clustering-node.html
@@ -0,0 +1,36 @@
+<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
+<div id="content-area" class="col-md-9" role="main">
+    <kc-navigation-application></kc-navigation-application>
+    <div id="content">
+        <ol class="breadcrumb">
+            <li><a href="#/realms/{{realm.realm}}/applications">Applications</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">{{application.name}}</a></li>
+            <li><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
+            <li class="active">{{node.host}}</li>
+        </ol>
+        <h2 data-ng-show="create || registered"><span>{{application.name}} Clustering</span></h2>
+        <h2 data-ng-hide="create || registered">Cluster node on host <span>{{node.host}}</span> not registered!</h2>
+        <form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageApplications" data-ng-show="create || registered">
+            <fieldset >
+                <legend><span class="text">Configuration of cluster node</span></legend>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="host">Host</label>
+                    <div class="col-sm-6">
+                        <input ng-disabled="!create" class="form-control" type="text" id="host" name="host" data-ng-model="node.host">
+                    </div>
+                </div>
+                <div class="form-group">
+                    <label class="col-sm-2 control-label" for="lastRegistration">Last Registration</label>
+                    <div class="col-sm-6">
+                        <input ng-disabled="true" class="form-control" type="text" id="lastRegistration" name="lastRegistration" data-ng-model="node.lastRegistration">
+                    </div>
+                </div>
+            </fieldset>
+            <div class="pull-right form-actions" data-ng-show="access.manageRealm">
+                <button data-kc-save   data-ng-show="create">Register node</button>
+                <button data-kc-delete data-ng-hide="create" data-ng-click="unregisterNode()">Unregister node</button>
+            </div>
+
+        </form>
+    </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-smtp.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-smtp.html
index e3e02ad..f19e3c4 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-smtp.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-smtp.html
@@ -15,9 +15,9 @@
                     </div>
                 </div>
                 <div class="form-group clearfix">
-                    <label class="col-sm-2 control-label" for="smtpPort">Port <span class="required">*</span></label>
+                    <label class="col-sm-2 control-label" for="smtpPort">Port</label>
                     <div class="col-sm-4">
-                        <input class="form-control" id="smtpPort" type="number" ng-model="realm.smtpServer.port" placeholder="SMTP Port (defaults to 25)" required>
+                        <input class="form-control" id="smtpPort" type="number" ng-model="realm.smtpServer.port" placeholder="SMTP Port (defaults to 25)">
                     </div>
                 </div>
                 <div class="form-group clearfix">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
index d89d91c..d8590a2 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation-application.html
@@ -1,11 +1,12 @@
 <ul class="nav nav-tabs nav-tabs-pf"  data-ng-show="!create">
     <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}">Settings</a></li>
-    <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.bearerOnly && !application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
+    <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!application.publicClient && application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/credentials">Credentials</a></li>
     <li ng-class="{active: path[4] == 'certificate'}" data-ng-show="application.protocol == 'saml' && application.attributes['samlClientSignature'] == 'true'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/certificate">Application Keys</a></li>
     <li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/roles">Roles</a></li>
     <li ng-class="{active: path[4] == 'claims'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/claims">Claims</a></li>
     <li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
     <li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/revocation">Revocation</a></li>
     <li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!application.bearerOnly"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/sessions">Sessions</a></li>
+    <li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!application.publicClient"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/clustering">Clustering</a></li>
     <li ng-class="{active: path[4] == 'installation'}" data-ng-show="application.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/applications/{{application.id}}/installation">Installation</a></li>
 </ul>
\ No newline at end of file
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
index efc5463..a55fb97 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java
@@ -7,12 +7,10 @@ import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.representations.adapters.action.AdminAction;
 import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
+import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.util.JsonSerialization;
 import org.keycloak.util.StreamUtil;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -59,6 +57,9 @@ public class PreAuthActionsHandler {
         } else if (requestUri.endsWith(AdapterConstants.K_VERSION)) {
             handleVersion();
             return true;
+        } else if (requestUri.endsWith(AdapterConstants.K_TEST_AVAILABLE)) {
+            handleTestAvailable();
+            return true;
         }
         return false;
     }
@@ -144,6 +145,22 @@ public class PreAuthActionsHandler {
         }
     }
 
+    protected void handleTestAvailable()  {
+        if (log.isTraceEnabled()) {
+            log.trace("K_TEST_AVAILABLE sent");
+        }
+        try {
+            JWSInput token = verifyAdminRequest();
+            if (token == null) {
+                return;
+            }
+            TestAvailabilityAction action = JsonSerialization.readValue(token.getContent(), TestAvailabilityAction.class);
+            validateAction(action);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     protected JWSInput verifyAdminRequest() throws Exception {
         if (!facade.getRequest().isSecure() && deployment.getSslRequired().isRequired(facade.getRequest().getRemoteAddr())) {
             log.warn("SSL is required for adapter admin action");
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
index 65b011f..ee27557 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
@@ -1,6 +1,5 @@
 package org.keycloak.admin.client.resource;
 
-import org.keycloak.representations.adapters.action.UserStats;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.SocialLinkRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
@@ -17,7 +16,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.util.List;
-import java.util.Map;
 
 /**
  * @author rodrigo.sasaki@icarros.com.br
@@ -52,10 +50,6 @@ public interface UserResource {
     public void resetPasswordEmail();
 
     @GET
-    @Path("session-stats")
-    public Map<String, UserStats> getUserStats();
-
-    @GET
     @Path("sessions")
     public List<UserSessionRepresentation> getUserSessions();
 
diff --git a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
index ab4fb3f..b21653e 100755
--- a/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
+++ b/integration/as7-eap6/adapter/src/main/java/org/keycloak/adapters/as7/KeycloakAuthenticatorValve.java
@@ -67,7 +67,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
             if (session != null) {
                 session.removeNote(KeycloakSecurityContext.class.getName());
                 if (ksc instanceof RefreshableKeycloakSecurityContext) {
-                    ((RefreshableKeycloakSecurityContext)ksc).logout(deploymentContext.getDeployment());
+                    CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
+                    ((RefreshableKeycloakSecurityContext)ksc).logout(deploymentContext.resolveDeployment(facade));
                 }
             }
         }
diff --git a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java
index de12389..0d45009 100755
--- a/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java
+++ b/integration/tomcat7/adapter/src/main/java/org/keycloak/adapters/tomcat7/KeycloakAuthenticatorValve.java
@@ -74,7 +74,8 @@ public class KeycloakAuthenticatorValve extends FormAuthenticator implements Lif
             if (session != null) {
                 session.removeNote(KeycloakSecurityContext.class.getName());
                 try {
-                    ServerRequest.invokeLogout(deploymentContext.getDeployment(), ksc.getToken().getSessionState());
+                    CatalinaHttpFacade facade = new CatalinaHttpFacade(request, null);
+                    ServerRequest.invokeLogout(deploymentContext.resolveDeployment(facade), ksc.getToken().getSessionState());
                 } catch (Exception e) {
                 	log.severe("failed to invoke remote logout. " + e.getMessage());
                 }
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
index 9b2e157..7e1ee37 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/ServletKeycloakAuthMech.java
@@ -79,7 +79,8 @@ public class ServletKeycloakAuthMech extends UndertowKeycloakAuthMech {
                 session.removeAttribute(KeycloakSecurityContext.class.getName());
                 session.removeAttribute(KeycloakUndertowAccount.class.getName());
                 if (account.getKeycloakSecurityContext() != null) {
-                    account.getKeycloakSecurityContext().logout(deploymentContext.getDeployment());
+                    UndertowHttpFacade facade = new UndertowHttpFacade(notification.getExchange());
+                    account.getKeycloakSecurityContext().logout(deploymentContext.resolveDeployment(facade));
                 }
             }
         };
diff --git a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
index e505090..d1b9e4e 100755
--- a/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
+++ b/integration/undertow/src/main/java/org/keycloak/adapters/undertow/UndertowKeycloakAuthMech.java
@@ -66,7 +66,8 @@ public abstract class UndertowKeycloakAuthMech implements AuthenticationMechanis
                 if (account == null) return;
                 session.removeAttribute(KeycloakUndertowAccount.class.getName());
                 if (account.getKeycloakSecurityContext() != null) {
-                    account.getKeycloakSecurityContext().logout(deploymentContext.getDeployment());
+                    UndertowHttpFacade facade = new UndertowHttpFacade(notification.getExchange());
+                    account.getKeycloakSecurityContext().logout(deploymentContext.resolveDeployment(facade));
                 }
             }
         };
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
index d713450..ef190d9 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OpenIDConnectService.java
@@ -44,6 +44,7 @@ import org.keycloak.services.resources.flows.Urls;
 import org.keycloak.util.Base64Url;
 import org.keycloak.util.BasicAuthHelper;
 import org.keycloak.util.StreamUtil;
+import org.keycloak.util.UriUtils;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.GET;
@@ -188,6 +189,10 @@ public class OpenIDConnectService {
     @Produces(MediaType.TEXT_HTML)
     public Response getLoginStatusIframe(@QueryParam("client_id") String client_id,
                                          @QueryParam("origin") String origin) {
+        if (!UriUtils.isOrigin(origin)) {
+            throw new BadRequestException("Invalid origin");
+        }
+
         ClientModel client = realm.findClient(client_id);
         if (client == null) {
             throw new NotFoundException("could not find client: " + client_id);
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
index 32305b1..46bba42 100755
--- a/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientSessionCode.java
@@ -102,10 +102,9 @@ public class ClientSessionCode {
         Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
         for (String roleId : clientSession.getRoles()) {
             RoleModel role = realm.getRoleById(roleId);
-            if (role == null) {
-                new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid role " + roleId);
+            if (role != null) {
+                requestedRoles.add(realm.getRoleById(roleId));
             }
-            requestedRoles.add(realm.getRoleById(roleId));
         }
         return requestedRoles;
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
index d959883..834fca8 100755
--- a/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ResourceAdminManager.java
@@ -15,9 +15,10 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.adapters.action.GlobalRequestResult;
 import org.keycloak.representations.adapters.action.LogoutAction;
 import org.keycloak.representations.adapters.action.PushNotBeforeAction;
-import org.keycloak.representations.adapters.action.UserStats;
+import org.keycloak.representations.adapters.action.TestAvailabilityAction;
 import org.keycloak.services.util.HttpClientBuilder;
 import org.keycloak.services.util.ResolveRelative;
 import org.keycloak.util.KeycloakUriBuilder;
@@ -64,6 +65,8 @@ public class ResourceAdminManager {
         return StringPropertyReplacer.replaceProperties(absoluteURI);
     }
 
+    // For non-cluster setup, return just single configured managementUrls
+    // For cluster setup, return the management Urls corresponding to all registered cluster nodes
     private List<String> getAllManagementUrls(URI requestUri, ApplicationModel application) {
         String baseMgmtUrl = getManagementUrl(requestUri, application);
         if (baseMgmtUrl == null) {
@@ -211,49 +214,55 @@ public class ResourceAdminManager {
 
     // Methods for logout all
 
-    public void logoutAll(URI requestUri, RealmModel realm) {
+    public GlobalRequestResult logoutAll(URI requestUri, RealmModel realm) {
         ApacheHttpClient4Executor executor = createExecutor();
 
         try {
             realm.setNotBefore(Time.currentTime());
             List<ApplicationModel> resources = realm.getApplications();
             logger.debugv("logging out {0} resources ", resources.size());
+
+            GlobalRequestResult finalResult = new GlobalRequestResult();
             for (ApplicationModel resource : resources) {
-                logoutApplication(requestUri, realm, resource, executor, realm.getNotBefore());
+                GlobalRequestResult currentResult = logoutApplication(requestUri, realm, resource, executor, realm.getNotBefore());
+                finalResult.addAll(currentResult);
             }
+            return finalResult;
         } finally {
             executor.getHttpClient().getConnectionManager().shutdown();
         }
     }
 
-    public void logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource) {
+    public GlobalRequestResult logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource) {
         ApacheHttpClient4Executor executor = createExecutor();
         try {
             resource.setNotBefore(Time.currentTime());
-            logoutApplication(requestUri, realm, resource, executor, resource.getNotBefore());
+            return logoutApplication(requestUri, realm, resource, executor, resource.getNotBefore());
         } finally {
             executor.getHttpClient().getConnectionManager().shutdown();
         }
     }
 
 
-    protected boolean logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, ApacheHttpClient4Executor executor, int notBefore) {
+    protected GlobalRequestResult logoutApplication(URI requestUri, RealmModel realm, ApplicationModel resource, ApacheHttpClient4Executor executor, int notBefore) {
         List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
         if (mgmtUrls.isEmpty()) {
             logger.debug("No management URL or no registered cluster nodes for the application " + resource.getName());
-            return false;
+            return new GlobalRequestResult();
         }
 
         logger.info("Send logoutApplication for URLs: " + mgmtUrls);
 
         // Propagate this to all hosts
-        boolean anyFailed = false;
+        GlobalRequestResult result = new GlobalRequestResult();
         for (String mgmtUrl : mgmtUrls) {
-            if (!sendLogoutRequest(realm, resource, null, executor, notBefore, mgmtUrl)) {
-                anyFailed = true;
+            if (sendLogoutRequest(realm, resource, null, executor, notBefore, mgmtUrl)) {
+                result.addSuccessRequest(mgmtUrl);
+            } else {
+                result.addFailedRequest(mgmtUrl);
             }
         }
-        return !anyFailed;
+        return result;
     }
 
     protected boolean sendLogoutRequest(RealmModel realm, ApplicationModel resource, List<String> adapterSessionIds, ApacheHttpClient4Executor client, int notBefore, String managementUrl) {
@@ -263,60 +272,65 @@ public class ResourceAdminManager {
         ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_LOGOUT).build().toString());
         ClientResponse response;
         try {
-            response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post(UserStats.class);
+            response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
         } catch (Exception e) {
             logger.warn("Logout for application '" + resource.getName() + "' failed", e);
             return false;
         }
         try {
-            boolean success = response.getStatus() == 204;
-            logger.debug("logout success.");
+            boolean success = response.getStatus() == 204 || response.getStatus() == 200;
+            logger.debugf("logout success for %s: %s", managementUrl, success);
             return success;
         } finally {
             response.releaseConnection();
         }
     }
 
-    public void pushRealmRevocationPolicy(URI requestUri, RealmModel realm) {
+    public GlobalRequestResult pushRealmRevocationPolicy(URI requestUri, RealmModel realm) {
         ApacheHttpClient4Executor executor = createExecutor();
 
         try {
+            GlobalRequestResult finalResult = new GlobalRequestResult();
             for (ApplicationModel application : realm.getApplications()) {
-                pushRevocationPolicy(requestUri, realm, application, realm.getNotBefore(), executor);
+                GlobalRequestResult currentResult = pushRevocationPolicy(requestUri, realm, application, realm.getNotBefore(), executor);
+                finalResult.addAll(currentResult);
             }
+            return finalResult;
         } finally {
             executor.getHttpClient().getConnectionManager().shutdown();
         }
     }
 
-    public void pushApplicationRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel application) {
+    public GlobalRequestResult pushApplicationRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel application) {
         ApacheHttpClient4Executor executor = createExecutor();
 
         try {
-            pushRevocationPolicy(requestUri, realm, application, application.getNotBefore(), executor);
+            return pushRevocationPolicy(requestUri, realm, application, application.getNotBefore(), executor);
         } finally {
             executor.getHttpClient().getConnectionManager().shutdown();
         }
     }
 
 
-    protected boolean pushRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor executor) {
+    protected GlobalRequestResult pushRevocationPolicy(URI requestUri, RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor executor) {
         List<String> mgmtUrls = getAllManagementUrls(requestUri, resource);
         if (mgmtUrls.isEmpty()) {
-            logger.debug("No management URL or no registered cluster nodes for the application " + resource.getName());
-            return false;
+            logger.debugf("No management URL or no registered cluster nodes for the application %s", resource.getName());
+            return new GlobalRequestResult();
         }
 
         logger.info("Sending push revocation to URLS: " + mgmtUrls);
 
         // Propagate this to all hosts
-        boolean anyFailed= false;
+        GlobalRequestResult result = new GlobalRequestResult();
         for (String mgmtUrl : mgmtUrls) {
-            if (!sendPushRevocationPolicyRequest(realm, resource, notBefore, executor, mgmtUrl)) {
-                anyFailed = true;
+            if (sendPushRevocationPolicyRequest(realm, resource, notBefore, executor, mgmtUrl)) {
+                result.addSuccessRequest(mgmtUrl);
+            } else {
+                result.addFailedRequest(mgmtUrl);
             }
         }
-        return !anyFailed;
+        return result;
     }
 
     protected boolean sendPushRevocationPolicyRequest(RealmModel realm, ApplicationModel resource, int notBefore, ApacheHttpClient4Executor client, String managementUrl) {
@@ -332,11 +346,60 @@ public class ResourceAdminManager {
             return false;
         }
         try {
-            boolean success = response.getStatus() == 204;
-            logger.debug("pushRevocation success.");
+            boolean success = response.getStatus() == 204 || response.getStatus() == 200;
+            logger.debugf("pushRevocation success for %s: %s", managementUrl, success);
             return success;
         } finally {
             response.releaseConnection();
         }
     }
+
+    public GlobalRequestResult testNodesAvailability(URI requestUri, RealmModel realm, ApplicationModel application) {
+        List<String> mgmtUrls = getAllManagementUrls(requestUri, application);
+        if (mgmtUrls.isEmpty()) {
+            logger.debug("No management URL or no registered cluster nodes for the application " + application.getName());
+            return new GlobalRequestResult();
+        }
+
+        ApacheHttpClient4Executor executor = createExecutor();
+
+        try {
+            logger.info("Sending test nodes availability: " + mgmtUrls);
+
+            // Propagate this to all hosts
+            GlobalRequestResult result = new GlobalRequestResult();
+            for (String mgmtUrl : mgmtUrls) {
+                if (sendTestNodeAvailabilityRequest(realm, application, executor, mgmtUrl)) {
+                    result.addSuccessRequest(mgmtUrl);
+                } else {
+                    result.addFailedRequest(mgmtUrl);
+                }
+            }
+            return result;
+        } finally {
+            executor.getHttpClient().getConnectionManager().shutdown();
+        }
+    }
+
+    protected boolean sendTestNodeAvailabilityRequest(RealmModel realm, ApplicationModel application, ApacheHttpClient4Executor client, String managementUrl) {
+        TestAvailabilityAction adminAction = new TestAvailabilityAction(TokenIdGenerator.generateId(), Time.currentTime() + 30, application.getName());
+        String token = new TokenManager().encodeToken(realm, adminAction);
+        logger.infov("testNodes availability resource: {0} url: {1}", application.getName(), managementUrl);
+        ClientRequest request = client.createRequest(UriBuilder.fromUri(managementUrl).path(AdapterConstants.K_TEST_AVAILABLE).build().toString());
+        ClientResponse response;
+        try {
+            response = request.body(MediaType.TEXT_PLAIN_TYPE, token).post();
+        } catch (Exception e) {
+            logger.warn("Availability test failed for uri '" + managementUrl + "'", e);
+            return false;
+        }
+        try {
+            boolean success = response.getStatus() == 204 || response.getStatus() == 200;
+            logger.debugf("testAvailability success for %s: %s", managementUrl, success);
+            return success;
+        } finally {
+            response.releaseConnection();
+        }
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 5ba6cae..ff22dc2 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -84,6 +84,7 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import javax.ws.rs.core.Variant;
+import java.lang.reflect.Method;
 import java.net.URI;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -99,6 +100,16 @@ public class AccountService {
 
     private static final Logger logger = Logger.getLogger(AccountService.class);
 
+    private static Set<String> VALID_PATHS = new HashSet<String>();
+    static {
+        for (Method m : AccountService.class.getMethods()) {
+            Path p = m.getAnnotation(Path.class);
+            if (p != null) {
+                VALID_PATHS.add(p.value());
+            }
+        }
+    }
+
     private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_SOCIAL_LINK, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
             EventType.SEND_VERIFY_EMAIL, EventType.SOCIAL_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL};
 
@@ -715,6 +726,9 @@ public class AccountService {
                 logger.debug("error from oauth");
                 throw new ForbiddenException("error");
             }
+            if (path != null && !VALID_PATHS.contains(path)) {
+                throw new BadRequestException("Invalid path");
+            }
             if (!realm.isEnabled()) {
                 logger.debug("realm not enabled");
                 throw new ForbiddenException();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
index 0c84d11..5e97698 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ApplicationResource.java
@@ -2,6 +2,7 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.KeycloakSession;
@@ -13,6 +14,7 @@ import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.representations.adapters.action.GlobalRequestResult;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -22,6 +24,7 @@ import org.keycloak.services.managers.ResourceAdminManager;
 import org.keycloak.services.resources.KeycloakApplication;
 import org.keycloak.services.resources.flows.Flows;
 import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.Time;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
@@ -281,9 +284,9 @@ public class ApplicationResource {
      */
     @Path("push-revocation")
     @POST
-    public void pushRevocation() {
+    public GlobalRequestResult pushRevocation() {
         auth.requireManage();
-        new ResourceAdminManager().pushApplicationRevocationPolicy(uriInfo.getRequestUri(), realm, application);
+        return new ResourceAdminManager().pushApplicationRevocationPolicy(uriInfo.getRequestUri(), realm, application);
     }
 
     /**
@@ -333,9 +336,9 @@ public class ApplicationResource {
      */
     @Path("logout-all")
     @POST
-    public void logoutAll() {
+    public GlobalRequestResult logoutAll() {
         auth.requireManage();
-        new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application);
+        return new ResourceAdminManager().logoutApplication(uriInfo.getRequestUri(), realm, application);
     }
 
     /**
@@ -354,10 +357,58 @@ public class ApplicationResource {
         new ResourceAdminManager().logoutUserFromApplication(uriInfo.getRequestUri(), realm, application, user, session);
     }
 
+    /**
+     * Manually register cluster node to this application - usually it's not needed to call this directly as adapter should handle
+     * by sending registration request to Keycloak
+     *
+     * @param formParams
+     */
+    @Path("nodes")
+    @POST
+    @Consumes("application/json")
+    public void registerNode(Map<String, String> formParams) {
+        auth.requireManage();
+        String node = formParams.get("node");
+        if (node == null) {
+            throw new BadRequestException("Node not found in params");
+        }
+        logger.info("Register node: " + node);
+        application.registerNode(node, Time.currentTime());
+    }
 
+    /**
+     * Unregister cluster node from this application
+     *
+     * @param node
+     */
+    @Path("nodes/{node}")
+    @DELETE
+    @NoCache
+    public void unregisterNode(final @PathParam("node") String node) {
+        auth.requireManage();
+        logger.info("Unregister node: " + node);
 
+        Integer time = application.getRegisteredNodes().get(node);
+        if (time == null) {
+            throw new NotFoundException("Application does not have a node " + node);
+        }
 
+        application.unregisterNode(node);
+    }
 
+    /**
+     * Test if registered cluster nodes are available by sending 'ping' request to all of them
+     *
+     * @return
+     */
+    @Path("test-nodes-available")
+    @GET
+    @NoCache
+    public GlobalRequestResult testNodesAvailable() {
+        auth.requireManage();
+        logger.info("Test availability of cluster nodes");
 
+        return new ResourceAdminManager().testNodesAvailability(uriInfo.getRequestUri(), realm, application);
+    }
 
 }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 1a45769..bc4167e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -19,6 +19,7 @@ import org.keycloak.models.cache.CacheUserProvider;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.representations.adapters.action.GlobalRequestResult;
 import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.services.managers.LDAPConnectionTestManager;
@@ -255,9 +256,9 @@ public class RealmAdminResource {
      */
     @Path("push-revocation")
     @POST
-    public void pushRevocation() {
+    public GlobalRequestResult pushRevocation() {
         auth.requireManage();
-        new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
+        return new ResourceAdminManager().pushRealmRevocationPolicy(uriInfo.getRequestUri(), realm);
     }
 
     /**
@@ -267,10 +268,10 @@ public class RealmAdminResource {
      */
     @Path("logout-all")
     @POST
-    public void logoutAll() {
+    public GlobalRequestResult logoutAll() {
         auth.requireManage();
         session.sessions().removeUserSessions(realm);
-        new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
+        return new ResourceAdminManager().logoutAll(uriInfo.getRequestUri(), realm);
     }
 
     /**
diff --git a/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java b/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
index 6cc66ee..c846df2 100755
--- a/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/QRCodeResource.java
@@ -43,14 +43,22 @@ public class QRCodeResource {
 
         if (size != null) {
             String[] s = size.split("x");
-            width = Integer.parseInt(s[0]);
-            height = Integer.parseInt(s[1]);
+            try {
+                width = Integer.parseInt(s[0]);
+                height = Integer.parseInt(s[1]);
+            } catch (Throwable t) {
+                return Response.status(Response.Status.BAD_REQUEST).build();
+            }
         }
 
         if (contents == null) {
             return Response.status(Response.Status.BAD_REQUEST).build();
         }
 
+        if (width > 1000 || height > 1000 || contents.length() > 1000) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
         QRCodeWriter writer = new QRCodeWriter();
         final BitMatrix bitMatrix = writer.encode(contents, BarcodeFormat.QR_CODE, width, height);
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
index 0c3dfb8..f1e1c0b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/RelativeUriAdapterTest.java
@@ -158,4 +158,27 @@ public class RelativeUriAdapterTest {
 
 
     }
+
+    @Test
+    public void testServletRequestLogout() throws Exception {
+        driver.navigate().to("http://localhost:8081/customer-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+        loginPage.login("bburke@redhat.com", "password");
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/customer-portal");
+        Assert.assertTrue(driver.getPageSource().contains("Bill Burke"));
+
+        driver.navigate().to("http://localhost:8081/product-portal");
+        Assert.assertEquals(driver.getCurrentUrl(), "http://localhost:8081/product-portal");
+        Assert.assertTrue(driver.getPageSource().contains("iPhone"));
+
+        // test logout
+        driver.navigate().to("http://localhost:8081/customer-portal/logout");
+
+        driver.navigate().to("http://localhost:8081/customer-portal");
+        String currentUrl = driver.getCurrentUrl();
+        Assert.assertTrue(currentUrl.startsWith(LOGIN_URL));
+        driver.navigate().to("http://localhost:8081/product-portal");
+        Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
+    }
+
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index 144cc46..27c2e51 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -34,6 +34,7 @@ import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.protocol.oidc.OpenIDConnectService;
 import org.keycloak.representations.AccessToken;
@@ -235,6 +236,42 @@ public class AccessTokenTest {
     }
 
     @Test
+    public void accessTokenCodeRoleMissing() {
+        keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                RoleModel role = appRealm.addRole("tmp-role");
+                session.users().getUserByUsername("test-user@localhost", appRealm).grantRole(role);
+            }
+        });
+
+        oauth.doLogin("test-user@localhost", "password");
+
+        Event loginEvent = events.expectLogin().assertEvent();
+
+        loginEvent.getDetails().get(Details.CODE_ID);
+
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+        keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                appRealm.removeRole(appRealm.getRole("tmp-role"));
+            }
+        });
+
+        OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
+
+        Assert.assertEquals(200, response.getStatusCode());
+
+        AccessToken token = oauth.verifyToken(response.getAccessToken());
+        Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
+        Assert.assertTrue(token.getRealmAccess().isUserInRole("user"));
+
+        events.clear();
+    }
+
+    @Test
     public void accessTokenCodeHasRequiredAction() {
         keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
             @Override