keycloak-memoizeit

Changes

model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientInitialAccessAdapter.java 86(+0 -86)

model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/mapreduce/ClientInitialAccessMapper.java 96(+0 -96)

model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/stream/ClientInitialAccessPredicate.java 76(+0 -76)

Details

diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
index 1613593..08ce4a9 100644
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
@@ -364,26 +364,26 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
 
         if (deployment.getIDP().getSingleSignOnService().validateAssertionSignature()) {
             try {
-                validateSamlSignature(new SAMLDocumentHolder(buildAssertionDocument(responseHolder, assertion)), postBinding, GeneralConstants.SAML_RESPONSE_KEY);
-            } catch (VerificationException e) {
-                log.error("Failed to verify saml assertion signature", e);
+                if (!AssertionUtil.isSignatureValid(getAssertionFromResponse(responseHolder), deployment.getIDP().getSignatureValidationKeyLocator())) {
+                    log.error("Failed to verify saml assertion signature");
 
-                challenge = new AuthChallenge() {
+                    challenge = new AuthChallenge() {
 
-                    @Override
-                    public boolean challenge(HttpFacade exchange) {
-                        SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE, responseType);
-                        exchange.getRequest().setError(error);
-                        exchange.getResponse().sendError(403);
-                        return true;
-                    }
+                        @Override
+                        public boolean challenge(HttpFacade exchange) {
+                            SamlAuthenticationError error = new SamlAuthenticationError(SamlAuthenticationError.Reason.INVALID_SIGNATURE, responseType);
+                            exchange.getRequest().setError(error);
+                            exchange.getResponse().sendError(403);
+                            return true;
+                        }
 
-                    @Override
-                    public int getResponseCode() {
-                        return 403;
-                    }
-                };
-                return AuthOutcome.FAILED;
+                        @Override
+                        public int getResponseCode() {
+                            return 403;
+                        }
+                    };
+                    return AuthOutcome.FAILED;
+                }
             } catch (Exception e) {
                 log.error("Error processing validation of SAML assertion: " + e.getMessage());
                 challenge = new AuthChallenge() {
@@ -504,19 +504,16 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
           && Objects.equals(responseType.getStatus().getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
     }
 
-    private Document buildAssertionDocument(final SAMLDocumentHolder responseHolder, AssertionType assertion) throws ConfigurationException, ProcessingException {
-        Element encryptedAssertion = org.keycloak.saml.common.util.DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
+    private Element getAssertionFromResponse(final SAMLDocumentHolder responseHolder) throws ConfigurationException, ProcessingException {
+        Element encryptedAssertion = DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
         if (encryptedAssertion != null) {
             // encrypted assertion.
             // We'll need to decrypt it first.
             Document encryptedAssertionDocument = DocumentUtil.createDocument();
             encryptedAssertionDocument.appendChild(encryptedAssertionDocument.importNode(encryptedAssertion, true));
-            Element assertionElement = XMLEncryptionUtil.decryptElementInDocument(encryptedAssertionDocument, deployment.getDecryptionKey());
-            Document assertionDocument = DocumentUtil.createDocument();
-            assertionDocument.appendChild(assertionDocument.importNode(assertionElement, true));
-            return assertionDocument;
+            return XMLEncryptionUtil.decryptElementInDocument(encryptedAssertionDocument, deployment.getDecryptionKey());
         }
-        return AssertionUtil.asDocument(assertion);
+        return DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
     }
 
     private String getAttributeValue(Object attrValue) {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
index 5fe725f..1d7ce64 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RealmCacheSession.java
@@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
 
 import org.jboss.logging.Logger;
 import org.keycloak.cluster.ClusterProvider;
+import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
 import org.keycloak.migration.MigrationModel;
 import org.keycloak.models.ClientModel;
@@ -1059,4 +1060,34 @@ public class RealmCacheSession implements CacheRealmProvider {
         return adapter;
     }
 
+    // Don't cache ClientInitialAccessModel for now
+    @Override
+    public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {
+        return getDelegate().createClientInitialAccessModel(realm, expiration, count);
+    }
+
+    @Override
+    public ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id) {
+        return getDelegate().getClientInitialAccessModel(realm, id);
+    }
+
+    @Override
+    public void removeClientInitialAccessModel(RealmModel realm, String id) {
+        getDelegate().removeClientInitialAccessModel(realm, id);
+    }
+
+    @Override
+    public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
+        return getDelegate().listClientInitialAccess(realm);
+    }
+
+    @Override
+    public void removeExpiredClientInitialAccess() {
+        getDelegate().removeExpiredClientInitialAccess();
+    }
+
+    @Override
+    public void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess) {
+        getDelegate().decreaseRemainingCount(realm, clientInitialAccess);
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
index 0476698..202a051 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/sessions/infinispan/InfinispanUserSessionProvider.java
@@ -22,7 +22,6 @@ import org.infinispan.CacheStream;
 import org.infinispan.context.Flag;
 import org.jboss.logging.Logger;
 import org.keycloak.common.util.Time;
-import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.AuthenticatedClientSessionModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -32,23 +31,19 @@ import org.keycloak.models.UserModel;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.UserSessionProvider;
 import org.keycloak.models.session.UserSessionPersisterProvider;
-import org.keycloak.models.sessions.infinispan.entities.ClientInitialAccessEntity;
 import org.keycloak.models.sessions.infinispan.entities.AuthenticatedClientSessionEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureEntity;
 import org.keycloak.models.sessions.infinispan.entities.LoginFailureKey;
 import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
 import org.keycloak.models.sessions.infinispan.entities.UserSessionEntity;
-import org.keycloak.models.sessions.infinispan.stream.ClientInitialAccessPredicate;
 import org.keycloak.models.sessions.infinispan.stream.Comparators;
 import org.keycloak.models.sessions.infinispan.stream.Mappers;
 import org.keycloak.models.sessions.infinispan.stream.SessionPredicate;
 import org.keycloak.models.sessions.infinispan.stream.UserLoginFailurePredicate;
 import org.keycloak.models.sessions.infinispan.stream.UserSessionPredicate;
-import org.keycloak.models.utils.KeycloakModelUtils;
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -271,7 +266,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         log.debugf("Removing expired sessions");
         removeExpiredUserSessions(realm);
         removeExpiredOfflineUserSessions(realm);
-        removeExpiredClientInitialAccess(realm);
     }
 
     private void removeExpiredUserSessions(RealmModel realm) {
@@ -317,14 +311,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         log.debugf("Removed %d expired offline user sessions for realm '%s'", counter, realm.getName());
     }
 
-    private void removeExpiredClientInitialAccess(RealmModel realm) {
-        Iterator<String> itr = sessionCache.getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL)
-                .entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId()).expired(Time.currentTime())).map(Mappers.sessionId()).iterator();
-        while (itr.hasNext()) {
-            tx.remove(sessionCache, itr.next());
-        }
-    }
-
     @Override
     public void removeUserSessions(RealmModel realm) {
         removeUserSessions(realm, false);
@@ -417,19 +403,6 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         return models;
     }
 
-    List<ClientInitialAccessModel> wrapClientInitialAccess(RealmModel realm, Collection<ClientInitialAccessEntity> entities) {
-        List<ClientInitialAccessModel> models = new LinkedList<>();
-        for (ClientInitialAccessEntity e : entities) {
-            models.add(wrap(realm, e));
-        }
-        return models;
-    }
-
-    ClientInitialAccessAdapter wrap(RealmModel realm, ClientInitialAccessEntity entity) {
-        Cache<String, SessionEntity> cache = getCache(false);
-        return entity != null ? new ClientInitialAccessAdapter(session, this, cache, realm, entity) : null;
-    }
-
     UserLoginFailureModel wrap(LoginFailureKey key, LoginFailureEntity entity) {
         return entity != null ? new UserLoginFailureAdapter(this, loginFailureCache, key, entity) : null;
     }
@@ -565,48 +538,4 @@ public class InfinispanUserSessionProvider implements UserSessionProvider {
         return new AuthenticatedClientSessionAdapter(entity, clientSession.getClient(), importedUserSession, this, importedUserSession.getCache());
     }
 
-    @Override
-    public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {
-        String id = KeycloakModelUtils.generateId();
-
-        ClientInitialAccessEntity entity = new ClientInitialAccessEntity();
-        entity.setId(id);
-        entity.setRealm(realm.getId());
-        entity.setTimestamp(Time.currentTime());
-        entity.setExpiration(expiration);
-        entity.setCount(count);
-        entity.setRemainingCount(count);
-
-        tx.put(sessionCache, id, entity);
-
-        return wrap(realm, entity);
-    }
-
-    @Override
-    public ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id) {
-        Cache<String, SessionEntity> cache = getCache(false);
-        ClientInitialAccessEntity entity = (ClientInitialAccessEntity) tx.get(cache, id); // Chance created in this transaction
-
-        if (entity == null) {
-            entity = (ClientInitialAccessEntity) cache.get(id);
-        }
-
-        return wrap(realm, entity);
-    }
-
-    @Override
-    public void removeClientInitialAccessModel(RealmModel realm, String id) {
-        tx.remove(getCache(false), id);
-    }
-
-    @Override
-    public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
-        Iterator<Map.Entry<String, SessionEntity>> itr = sessionCache.entrySet().stream().filter(ClientInitialAccessPredicate.create(realm.getId())).iterator();
-        List<ClientInitialAccessModel> list = new LinkedList<>();
-        while (itr.hasNext()) {
-            list.add(wrap(realm, (ClientInitialAccessEntity) itr.next().getValue()));
-        }
-        return list;
-    }
-
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
index 98f3152..219b9a5 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaRealmProvider.java
@@ -18,8 +18,10 @@
 package org.keycloak.models.jpa;
 
 import org.jboss.logging.Logger;
+import org.keycloak.common.util.Time;
 import org.keycloak.connections.jpa.util.JpaUtils;
 import org.keycloak.migration.MigrationModel;
+import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.ClientTemplateModel;
 import org.keycloak.models.GroupModel;
@@ -29,6 +31,7 @@ import org.keycloak.models.RealmProvider;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.jpa.entities.ClientEntity;
+import org.keycloak.models.jpa.entities.ClientInitialAccessEntity;
 import org.keycloak.models.jpa.entities.ClientTemplateEntity;
 import org.keycloak.models.jpa.entities.GroupEntity;
 import org.keycloak.models.jpa.entities.RealmEntity;
@@ -152,6 +155,8 @@ public class JpaRealmProvider implements RealmProvider {
             removeRole(adapter, role);
         }
 
+        num = em.createNamedQuery("removeClientInitialAccessByRealm")
+                .setParameter("realm", realm).executeUpdate();
 
         em.remove(realm);
 
@@ -537,4 +542,82 @@ public class JpaRealmProvider implements RealmProvider {
         ClientTemplateAdapter adapter = new ClientTemplateAdapter(realm, em, session, app);
         return adapter;
     }
+
+    @Override
+    public ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count) {
+        RealmEntity realmEntity = em.find(RealmEntity.class, realm.getId());
+
+        ClientInitialAccessEntity entity = new ClientInitialAccessEntity();
+        entity.setId(KeycloakModelUtils.generateId());
+        entity.setRealm(realmEntity);
+
+        entity.setCount(count);
+        entity.setRemainingCount(count);
+
+        int currentTime = Time.currentTime();
+        entity.setTimestamp(currentTime);
+        entity.setExpiration(expiration);
+
+        em.persist(entity);
+
+        return entityToModel(entity);
+    }
+
+    @Override
+    public ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id) {
+        ClientInitialAccessEntity entity = em.find(ClientInitialAccessEntity.class, id);
+        if (entity == null) {
+            return null;
+        } else {
+            return entityToModel(entity);
+        }
+    }
+
+    @Override
+    public void removeClientInitialAccessModel(RealmModel realm, String id) {
+        ClientInitialAccessEntity entity = em.find(ClientInitialAccessEntity.class, id);
+        if (entity != null) {
+            em.remove(entity);
+            em.flush();
+        }
+    }
+
+    @Override
+    public List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm) {
+        RealmEntity realmEntity = em.find(RealmEntity.class, realm.getId());
+
+        TypedQuery<ClientInitialAccessEntity> query = em.createNamedQuery("findClientInitialAccessByRealm", ClientInitialAccessEntity.class);
+        query.setParameter("realm", realmEntity);
+        List<ClientInitialAccessEntity> entities = query.getResultList();
+
+        return entities.stream()
+                .map(entity -> entityToModel(entity))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public void removeExpiredClientInitialAccess() {
+        int currentTime = Time.currentTime();
+
+        em.createNamedQuery("removeExpiredClientInitialAccess")
+                .setParameter("currentTime", currentTime)
+                .executeUpdate();
+    }
+
+    @Override
+    public void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess) {
+        em.createNamedQuery("decreaseClientInitialAccessRemainingCount")
+                .setParameter("id", clientInitialAccess.getId())
+                .executeUpdate();
+    }
+
+    private ClientInitialAccessModel entityToModel(ClientInitialAccessEntity entity) {
+        ClientInitialAccessModel model = new ClientInitialAccessModel();
+        model.setId(entity.getId());
+        model.setCount(entity.getCount());
+        model.setRemainingCount(entity.getRemainingCount());
+        model.setExpiration(entity.getExpiration());
+        model.setTimestamp(entity.getTimestamp());
+        return model;
+    }
 }
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-3.2.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-3.2.0.xml
index b3872b8..bd55645 100644
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-3.2.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-3.2.0.xml
@@ -22,6 +22,22 @@
         <dropPrimaryKey constraintName="CONSTRAINT_OFFL_CL_SES_PK2" tableName="OFFLINE_CLIENT_SESSION" />
         <dropColumn tableName="OFFLINE_CLIENT_SESSION" columnName="CLIENT_SESSION_ID" />
         <addPrimaryKey columnNames="USER_SESSION_ID,CLIENT_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK3" tableName="OFFLINE_CLIENT_SESSION"/>
+
+        <createTable tableName="CLIENT_INITIAL_ACCESS">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+
+            <column name="TIMESTAMP" type="INT"/>
+            <column name="EXPIRATION" type="INT"/>
+            <column name="COUNT" type="INT"/>
+            <column name="REMAINING_COUNT" type="INT"/>
+        </createTable>
+
+        <addPrimaryKey columnNames="ID" constraintName="CNSTR_CLIENT_INIT_ACC_PK" tableName="CLIENT_INITIAL_ACCESS"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT_INITIAL_ACCESS" constraintName="FK_CLIENT_INIT_ACC_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
+
     </changeSet>
 
     <changeSet author="glavoie@gmail.com" id="3.2.0.idx">
@@ -158,5 +174,9 @@
         <createIndex indexName="IDX_WEB_ORIG_CLIENT" tableName="WEB_ORIGINS">
             <column name="CLIENT_ID" type="VARCHAR(36)"/>
         </createIndex>
+
+        <createIndex indexName="IDX_CLIENT_INIT_ACC_REALM" tableName="CLIENT_INITIAL_ACCESS">
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+        </createIndex>
      </changeSet>
 </databaseChangeLog>
diff --git a/model/jpa/src/main/resources/META-INF/persistence.xml b/model/jpa/src/main/resources/META-INF/persistence.xml
index 5d3fa81..f23198c 100755
--- a/model/jpa/src/main/resources/META-INF/persistence.xml
+++ b/model/jpa/src/main/resources/META-INF/persistence.xml
@@ -56,6 +56,7 @@
         <class>org.keycloak.models.jpa.entities.UserGroupMembershipEntity</class>
         <class>org.keycloak.models.jpa.entities.ClientTemplateEntity</class>
         <class>org.keycloak.models.jpa.entities.TemplateScopeMappingEntity</class>
+        <class>org.keycloak.models.jpa.entities.ClientInitialAccessEntity</class>
 
         <!-- JpaAuditProviders -->
         <class>org.keycloak.events.jpa.EventEntity</class>
diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
index b5f2232..f516124 100755
--- a/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/common/util/DocumentUtil.java
@@ -52,6 +52,7 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.Objects;
 
 /**
  * Utility dealing with DOM
@@ -554,4 +555,33 @@ public class DocumentUtil {
 
         return documentBuilderFactory;
     }
+
+    /**
+     * Get a (direct) child {@linkplain Element} from the parent {@linkplain Element}. 
+     *
+     * @param parent parent element
+     * @param targetNamespace namespace URI
+     * @param targetLocalName local name
+     * @return a child element matching the target namespace and localname, where {@linkplain Element#getParentNode()} is the parent input parameter
+     * @return
+     */
+    
+    public static Element getDirectChildElement(Element parent, String targetNamespace, String targetLocalName) {
+        Node child = parent.getFirstChild();
+        
+        while(child != null) {
+            if(child instanceof Element) {
+                Element childElement = (Element)child;
+                
+                String ns = childElement.getNamespaceURI();
+                String localName = childElement.getLocalName();
+                
+                if(Objects.equals(targetNamespace, ns) && Objects.equals(targetLocalName, localName)) {
+                    return childElement;
+                }
+            }
+            child = child.getNextSibling();
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
index ef3e3bd..09ce9d6 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java
@@ -49,8 +49,6 @@ public class SAML2Signature {
 
     private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger();
 
-    private static final String ID_ATTRIBUTE_NAME = "ID";
-
     private String signatureMethod = SignatureMethod.RSA_SHA1;
 
     private String digestMethod = DigestMethod.SHA1;
@@ -156,7 +154,7 @@ public class SAML2Signature {
      */
     public void signSAMLDocument(Document samlDocument, String keyName, KeyPair keypair, String canonicalizationMethodType) throws ProcessingException {
         // Get the ID from the root
-        String id = samlDocument.getDocumentElement().getAttribute(ID_ATTRIBUTE_NAME);
+        String id = samlDocument.getDocumentElement().getAttribute(JBossSAMLConstants.ID.get());
         try {
             sign(samlDocument, id, keyName, keypair, canonicalizationMethodType);
         } catch (ParserConfigurationException | GeneralSecurityException | MarshalException | XMLSignatureException e) {
@@ -210,18 +208,20 @@ public class SAML2Signature {
      *
      * @param document SAML document to have its ID attribute configured.
      */
-    private void configureIdAttribute(Document document) {
+    public static void configureIdAttribute(Document document) {
         // Estabilish the IDness of the ID attribute.
-        document.getDocumentElement().setIdAttribute(ID_ATTRIBUTE_NAME, true);
+        configureIdAttribute(document.getDocumentElement());
 
         NodeList nodes = document.getElementsByTagNameNS(JBossSAMLURIConstants.ASSERTION_NSURI.get(),
                 JBossSAMLConstants.ASSERTION.get());
 
         for (int i = 0; i < nodes.getLength(); i++) {
-            Node n = nodes.item(i);
-            if (n instanceof Element) {
-                ((Element) n).setIdAttribute(ID_ATTRIBUTE_NAME, true);
-            }
+            configureIdAttribute((Element) nodes.item(i));
         }
     }
+    
+    public static void configureIdAttribute(Element element) {
+        element.setIdAttribute(JBossSAMLConstants.ID.get(), true);
+    }
+
 }
\ No newline at end of file
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
index bb15d23..244fb7d 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java
@@ -49,11 +49,12 @@ import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
 import org.keycloak.saml.processing.core.saml.v2.writers.SAMLAssertionWriter;
 import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
 import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
-
+import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
+import javax.xml.crypto.dsig.XMLSignature;
 import javax.xml.datatype.XMLGregorianCalendar;
 import javax.xml.namespace.QName;
 import java.io.ByteArrayInputStream;
@@ -267,42 +268,56 @@ public class AssertionUtil {
     }
 
     /**
-     * Given an assertion element, validate the signature
+     * Given an {@linkplain Element}, validate the Signature direct child element
      *
-     * @param assertionElement
+     * @param element parent {@linkplain Element}
      * @param publicKey the {@link PublicKey}
      *
-     * @return
+     * @return true if signature is present and valid
      */
-    public static boolean isSignatureValid(Element assertionElement, PublicKey publicKey) {
-        try {
-            Document doc = DocumentUtil.createDocument();
-            Node n = doc.importNode(assertionElement, true);
-            doc.appendChild(n);
-
-            return new SAML2Signature().validate(doc, new HardcodedKeyLocator(publicKey));
-        } catch (Exception e) {
-            logger.signatureAssertionValidationError(e);
-        }
-        return false;
+    public static boolean isSignatureValid(Element element, PublicKey publicKey) {
+        return isSignatureValid(element, new HardcodedKeyLocator(publicKey));
     }
 
     /**
-     * Given an assertion element, validate the signature.
+     * Given an {@linkplain Element}, validate the Signature direct child element
+     *
+     * @param element parent {@linkplain Element}
+     * @param keyLocator the {@link KeyLocator}
+     *
+     * @return true if signature is present and valid
      */
-    public static boolean isSignatureValid(Element assertionElement, KeyLocator keyLocator) {
+    
+    public static boolean isSignatureValid(Element element, KeyLocator keyLocator) {
         try {
-            Document doc = DocumentUtil.createDocument();
-            Node n = doc.importNode(assertionElement, true);
-            doc.appendChild(n);
-
-            return new SAML2Signature().validate(doc, keyLocator);
+            SAML2Signature.configureIdAttribute(element);
+            
+            Element signature = getSignature(element);
+            if(signature != null) {
+                return XMLSignatureUtil.validateSingleNode(signature, keyLocator);
+            }
         } catch (Exception e) {
             logger.signatureAssertionValidationError(e);
         }
         return false;
     }
+    
+    /**
+     * 
+     * Given an {@linkplain Element}, check if there is a Signature direct child element
+     * 
+     * @param element parent {@linkplain Element}
+     * @return true if signature is present
+     */
 
+    public static boolean isSignedElement(Element element) {
+        return getSignature(element) != null;
+    }
+    
+    protected static Element getSignature(Element element) {
+        return DocumentUtil.getDirectChildElement(element, XMLSignature.XMLNS, "Signature");
+    }
+    
     /**
      * Check whether the assertion has expired
      *
@@ -570,8 +585,8 @@ public class AssertionUtil {
 
     /**
      * This method modifies the given responseType, and replaces the encrypted assertion with a decrypted version.
-     *
-     * It returns the assertion element as it was decrypted. This can be used in sginature verification.
+     * @param responseType a response containg an encrypted assertion
+     * @return the assertion element as it was decrypted. This can be used in signature verification.
      */
     public static Element decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ParsingException, ProcessingException, ConfigurationException {
         SAML2Response saml2Response = new SAML2Response();
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
index 53228c9..7093a20 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
@@ -468,7 +468,7 @@ public class XMLSignatureUtil {
         return true;
     }
 
-    private static boolean validateSingleNode(Node signatureNode, final KeyLocator locator) throws MarshalException, XMLSignatureException {
+    public static boolean validateSingleNode(Node signatureNode, final KeyLocator locator) throws MarshalException, XMLSignatureException {
         KeySelectorUtilizingKeyNameHint sel = new KeySelectorUtilizingKeyNameHint(locator);
         try {
             if (validateUsingKeySelector(signatureNode, sel)) {
diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java
new file mode 100644
index 0000000..b60bb4b
--- /dev/null
+++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtilTest.java
@@ -0,0 +1,58 @@
+package org.keycloak.saml.processing.core.saml.v2.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.util.Arrays;
+import org.junit.Test;
+import org.keycloak.common.util.Base64;
+import org.keycloak.common.util.DerUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+public class AssertionUtilTest {
+
+    private static final String PRIVATE_KEY = "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==";
+
+    /**
+     * The public certificate that corresponds to {@link #PRIVATE_KEY}.
+     */
+    private static final String PUBLIC_CERT = "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin";
+
+    @Test
+    public void testSaml20Signed() throws Exception {
+        
+        X509Certificate decodeCertificate = DerUtils.decodeCertificate(new ByteArrayInputStream(Base64.decode(PUBLIC_CERT)));
+        
+        try (InputStream st = AssertionUtilTest.class.getResourceAsStream("saml20-signed-response.xml")) {
+            Document document = DocumentUtil.getDocument(st);
+            
+            Element assertion = DocumentUtil.getDirectChildElement(document.getDocumentElement(), "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion");
+            
+            assertTrue(AssertionUtil.isSignatureValid(assertion, decodeCertificate.getPublicKey()));
+            
+            // test manipulation of signature
+            Element signatureElement = AssertionUtil.getSignature(assertion);
+            byte[] validSignature = Base64.decode(signatureElement.getTextContent());
+            
+            // change the signature value slightly
+            byte[] invalidSignature = Arrays.clone(validSignature);
+            invalidSignature[0] ^= invalidSignature[0];
+            signatureElement.setTextContent(Base64.encodeBytes(invalidSignature));
+            
+            // check that signature now is invalid
+            assertFalse(AssertionUtil.isSignatureValid(document.getDocumentElement(), decodeCertificate.getPublicKey()));
+            
+            // restore valid signature, but remove Signature element, check that still invalid
+            signatureElement.setTextContent(Base64.encodeBytes(validSignature));
+
+            assertion.removeChild(signatureElement);
+            assertFalse(AssertionUtil.isSignatureValid(document.getDocumentElement(), decodeCertificate.getPublicKey()));
+        }
+    }
+
+}
diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml
new file mode 100644
index 0000000..998520a
--- /dev/null
+++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/saml/v2/util/saml20-signed-response.xml
@@ -0,0 +1 @@
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Destination="http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider/endpoint" ID="ID_9aca1381-6265-434c-98a5-d89236d32ea0" InResponseTo="ID_2d81575a-0bde-488a-b69d-0b78c0fcf521" IssueInstant="2017-05-04T22:18:13.550Z" Version="2.0"><saml:Issuer>http://localhost:8080/auth/realms/saml-broker-realm</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="ID_e9d22a60-7954-48b2-ad97-700d70e332f5" IssueInstant="2017-05-04T22:18:13.546Z" Version="2.0"><saml:Issuer>http://localhost:8080/auth/realms/saml-broker-realm</saml:Issuer><dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI="#ID_e9d22a60-7954-48b2-ad97-700d70e332f5"><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><dsig:DigestValue>cCSNXxLmu411weW1kRpie4C9yaBg2In6V4oEuqya0Eo=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>Qe6ZqgSwFH31UTu+zHqr1/UsafH0luxP5OH/cqyHm07Kf/Fp/fm9mnHJ0kGoUn0SUo7xWvwy8AzUfPXWMYS3kDyhUsPzgz0CnCzzfTz3koKFczgyIQ8sokIDv0cTp3z1qCUVWV0CEPzhtWlaIus2W89TEi/h9KjYrkeGl3+cpm8BPEAt4EP8Oht5czK2haIfPMDUm5Y7uw/FCSsvSfFyrlJ0jR/YMeP9PP0InYYegI9QQgvXKRm6DZSNZgKYFpprc12v6vv/zTaMm5fbuuy1wNDuDTB8EF6K1yrq21DatJXUKE1oOMBrkOvbFJNtgHlQviz1OssAqzHlf0NQPIAEig==</dsig:SignatureValue><dsig:KeyInfo><dsig:KeyName>IzH2UxfMxovYTEHn4Bh-EAj-Zrvldukl_5Snu0RA0B8</dsig:KeyName><dsig:X509Data><dsig:X509Certificate>MIICsTCCAZkCBgFb1GERYzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFzYW1sLWJyb2tlci1yZWFsbTAeFw0xNzA1MDQxNjUxMjJaFw0yNzA1MDQxNjUzMDJaMBwxGjAYBgNVBAMMEXNhbWwtYnJva2VyLXJlYWxtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCBuWKmcVPX4sZtpvBp8BgQwH51l5tKPuQq66/4JH40hzkrLOGcVEOafIsGoOWi8HsZWZu+APwhSrnRNd7yMV+NJyw5W1DKjhdUPWnzPNy+UMAcoUBFhXDIWog0qxgGvTdoe1lfHryUQt1cd95SFVIerJA93nFbSOoMB+N7TmfQm+sNu2pJ2tr6mx3wGCXMnWf29gwhCI3wV19hh4KugnMIEStjvQoRyh2yna64BrR3eaUyhU/Bdrq2VXLNU/9WXg9gbRLUEWkMUPKOeQ5cGCgc4JFyFXRo5ExkzmvP9vwBRtjQulk5QKqfYo251mKvTQgO7K8d4CzVS/4+bpgKvZAM</dsig:X509Certificate></dsig:X509Data><dsig:KeyValue><dsig:RSAKeyValue><dsig:Modulus>gj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQ==</dsig:Modulus><dsig:Exponent>AQAB</dsig:Exponent></dsig:RSAKeyValue></dsig:KeyValue></dsig:KeyInfo></dsig:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">G-ebb3a66f-686f-4bb9-8a8b-20b566ca747b</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="ID_2d81575a-0bde-488a-b69d-0b78c0fcf521" NotOnOrAfter="2017-05-04T22:23:11.546Z" Recipient="http://localhost:8080/auth/realms/saml-broker-authentication-realm/broker/saml-identity-provider/endpoint"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2017-05-04T22:18:11.546Z" NotOnOrAfter="2017-05-04T22:19:11.546Z"><saml:AudienceRestriction><saml:Audience>http://localhost:8080/auth/realms/saml-broker-authentication-realm</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2017-05-04T22:18:13.551Z" SessionIndex="c0cc880f-83d7-461b-b299-9fb3354f598c"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">manager</saml:AttributeValue></saml:Attribute><saml:Attribute Name="Role" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic"><saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">user</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>
\ No newline at end of file
diff --git a/server-spi/src/main/java/org/keycloak/models/ClientInitialAccessModel.java b/server-spi/src/main/java/org/keycloak/models/ClientInitialAccessModel.java
old mode 100755
new mode 100644
index 149b6aa..24b5546
--- a/server-spi/src/main/java/org/keycloak/models/ClientInitialAccessModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/ClientInitialAccessModel.java
@@ -20,20 +20,55 @@ package org.keycloak.models;
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
-public interface ClientInitialAccessModel {
+public class ClientInitialAccessModel {
 
-    String getId();
+    private String id;
 
-    RealmModel getRealm();
+    private int timestamp;
 
-    int getTimestamp();
+    private int expiration;
 
-    int getExpiration();
+    private int count;
 
-    int getCount();
+    private int remainingCount;
 
-    int getRemainingCount();
+    public String getId() {
+        return id;
+    }
 
-    void decreaseRemainingCount();
+    public void setId(String id) {
+        this.id = id;
+    }
 
+    public int getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(int timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    public int getExpiration() {
+        return expiration;
+    }
+
+    public void setExpiration(int expiration) {
+        this.expiration = expiration;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+    public void setCount(int count) {
+        this.count = count;
+    }
+
+    public int getRemainingCount() {
+        return remainingCount;
+    }
+
+    public void setRemainingCount(int remainingCount) {
+        this.remainingCount = remainingCount;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
index 4e6070f..f3a26f1 100755
--- a/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/RealmProvider.java
@@ -90,4 +90,11 @@ public interface RealmProvider extends Provider {
     List<RealmModel> getRealms();
     boolean removeRealm(String id);
     void close();
+
+    ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count);
+    ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id);
+    void removeClientInitialAccessModel(RealmModel realm, String id);
+    List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm);
+    void removeExpiredClientInitialAccess();
+    void decreaseRemainingCount(RealmModel realm, ClientInitialAccessModel clientInitialAccess); // Separate provider method to ensure we decrease remainingCount atomically instead of doing classic update
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
index d474e89..848c098 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserSessionProvider.java
@@ -72,11 +72,6 @@ public interface UserSessionProvider extends Provider {
     /** Triggered by persister during pre-load. It optionally imports authenticatedClientSessions too if requested. Otherwise the imported UserSession will have empty list of AuthenticationSessionModel **/
     UserSessionModel importUserSession(UserSessionModel persistentUserSession, boolean offline, boolean importAuthenticatedClientSessions);
 
-    ClientInitialAccessModel createClientInitialAccessModel(RealmModel realm, int expiration, int count);
-    ClientInitialAccessModel getClientInitialAccessModel(RealmModel realm, String id);
-    void removeClientInitialAccessModel(RealmModel realm, String id);
-    List<ClientInitialAccessModel> listClientInitialAccess(RealmModel realm);
-
     void close();
 
 }
diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index dc38d59..4451b8c 100755
--- a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -372,13 +372,13 @@ public class SAMLEndpoint {
                     assertionElement = DocumentUtil.getElement(holder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
                 }
 
-                if (config.isWantAssertionsSigned() && config.isValidateSignature()) {
-                    if (!AssertionUtil.isSignatureValid(assertionElement, getIDPKeyLocator())) {
-                        logger.error("validation failed");
-                        event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
-                        event.error(Errors.INVALID_SIGNATURE);
-                        return ErrorPage.error(session, Messages.INVALID_REQUESTER);
-                    }
+                boolean signed = AssertionUtil.isSignedElement(assertionElement);
+                if ((config.isWantAssertionsSigned() && !signed)
+                        || (signed && config.isValidateSignature() && !AssertionUtil.isSignatureValid(assertionElement, getIDPKeyLocator()))) {
+                    logger.error("validation failed");
+                    event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
+                    event.error(Errors.INVALID_SIGNATURE);
+                    return ErrorPage.error(session, Messages.INVALID_REQUESTER);
                 }
 
                 AssertionType assertion = responseType.getAssertions().get(0).getAssertion();
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
index 2974b6c..da693aa 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -23,6 +23,7 @@ import org.keycloak.models.ClientInitialAccessModel;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
 import org.keycloak.models.utils.ModelToRepresentation;
 import org.keycloak.models.utils.RepresentationToModel;
 import org.keycloak.representations.idm.ClientRepresentation;
@@ -65,7 +66,8 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
         }
 
         try {
-            ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
+            RealmModel realm = session.getContext().getRealm();
+            ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true);
 
             ClientRegistrationPolicyManager.triggerAfterRegister(context, registrationAuth, clientModel);
 
@@ -78,7 +80,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
 
             if (auth.isInitialAccessToken()) {
                 ClientInitialAccessModel initialAccessModel = auth.getInitialAccessModel();
-                initialAccessModel.decreaseRemainingCount();
+                session.realms().decreaseRemainingCount(realm, initialAccessModel);
             }
 
             event.client(client.getClientId()).success();
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
index fa2f9a2..dfed5aa 100644
--- a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationAuth.java
@@ -88,7 +88,7 @@ public class ClientRegistrationAuth {
         jwt = tokenVerification.getJwt();
 
         if (isInitialAccessToken()) {
-            initialAccessModel = session.sessions().getClientInitialAccessModel(session.getContext().getRealm(), jwt.getId());
+            initialAccessModel = session.realms().getClientInitialAccessModel(session.getContext().getRealm(), jwt.getId());
             if (initialAccessModel == null) {
                 throw unauthorized("Initial Access Token not found");
             }
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
index 65941af..609b9b0 100644
--- a/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientInitialAccessResource.java
@@ -81,7 +81,7 @@ public class ClientInitialAccessResource {
         int expiration = config.getExpiration() != null ? config.getExpiration() : 0;
         int count = config.getCount() != null ? config.getCount() : 1;
 
-        ClientInitialAccessModel clientInitialAccessModel = session.sessions().createClientInitialAccessModel(realm, expiration, count);
+        ClientInitialAccessModel clientInitialAccessModel = session.realms().createClientInitialAccessModel(realm, expiration, count);
 
         adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, clientInitialAccessModel.getId()).representation(config).success();
 
@@ -101,7 +101,7 @@ public class ClientInitialAccessResource {
     public List<ClientInitialAccessPresentation> list() {
         auth.clients().requireView();
 
-        List<ClientInitialAccessModel> models = session.sessions().listClientInitialAccess(realm);
+        List<ClientInitialAccessModel> models = session.realms().listClientInitialAccess(realm);
         List<ClientInitialAccessPresentation> reps = new LinkedList<>();
         for (ClientInitialAccessModel m : models) {
             ClientInitialAccessPresentation r = wrap(m);
@@ -115,7 +115,7 @@ public class ClientInitialAccessResource {
     public void delete(final @PathParam("id") String id) {
         auth.clients().requireManage();
 
-        session.sessions().removeClientInitialAccessModel(realm, id);
+        session.realms().removeClientInitialAccessModel(realm, id);
         adminEvent.operation(OperationType.DELETE).resourcePath(uriInfo).success();
     }
 
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index c16bb6a..42a2fd8 100644
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -47,6 +47,7 @@ import org.keycloak.services.managers.ApplianceBootstrap;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.UserStorageSyncManager;
 import org.keycloak.services.resources.admin.AdminRoot;
+import org.keycloak.services.scheduled.ClearExpiredClientInitialAccessTokens;
 import org.keycloak.services.scheduled.ClearExpiredEvents;
 import org.keycloak.services.scheduled.ClearExpiredUserSessions;
 import org.keycloak.services.scheduled.ClusterAwareScheduledTaskRunner;
@@ -331,6 +332,7 @@ public class KeycloakApplication extends Application {
         try {
             TimerProvider timer = session.getProvider(TimerProvider.class);
             timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredEvents(), interval), interval, "ClearExpiredEvents");
+            timer.schedule(new ClusterAwareScheduledTaskRunner(sessionFactory, new ClearExpiredClientInitialAccessTokens(), interval), interval, "ClearExpiredClientInitialAccessTokens");
             timer.schedule(new ScheduledTaskRunner(sessionFactory, new ClearExpiredUserSessions()), interval, "ClearExpiredUserSessions");
             new UserStorageSyncManager().bootstrapPeriodic(sessionFactory, timer);
         } finally {
diff --git a/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredClientInitialAccessTokens.java b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredClientInitialAccessTokens.java
new file mode 100644
index 0000000..94a4f6b
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/scheduled/ClearExpiredClientInitialAccessTokens.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.services.scheduled;
+
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.timer.ScheduledTask;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ClearExpiredClientInitialAccessTokens implements ScheduledTask {
+
+    @Override
+    public void run(KeycloakSession session) {
+        session.realms().removeExpiredClientInitialAccess();
+    }
+}
diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md
index 02cb7d5..cd818c2 100644
--- a/testsuite/integration-arquillian/HOW-TO-RUN.md
+++ b/testsuite/integration-arquillian/HOW-TO-RUN.md
@@ -45,7 +45,23 @@ and adapter are all in the same JVM and you can debug them easily. If it is not 
    
 and you will be able to attach remote debugger to the test. Unfortunately server and adapter are running in different JVMs, so this won't help to debug those. 
 
-TODO: Improve and add more info about Wildfly debugging...
+### JBoss auth server debugging
+
+When tests are run on JBoss based container (WildFly/EAP) there is possibility to attach a debugger, by default on localhost:5005.
+
+The server won't wait to attach the debugger. There are some properties what can change the default behaviour.
+
+    -Dauth.server.debug.port=$PORT
+    -Dauth.server.debug.suspend=y
+
+More info: http://javahowto.blogspot.cz/2010/09/java-agentlibjdwp-for-attaching.html
+
+### JBoss app server debugging
+
+Analogically, there is the same behaviour for JBoss based app server as for auth server. The default port is set to 5006. There are app server properties.
+
+    -Dapp.server.debug.port=$PORT
+    -Dapp.server.debug.suspend=y    
 
 ## Testsuite logging
 
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
index b868827..f18bbbd 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/rest/TestingResourceProvider.java
@@ -164,6 +164,8 @@ public class TestingResourceProvider implements RealmResourceProvider {
 
         session.sessions().removeExpired(realm);
         session.authenticationSessions().removeExpired(realm);
+        session.realms().removeExpiredClientInitialAccess();
+
         return Response.ok().build();
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
index 6f5e65e..c71d12f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/ExportAuthorizationSettingsTest.java
@@ -22,7 +22,6 @@ import java.util.List;
 import java.util.Map;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.AuthorizationResource;
 import org.keycloak.admin.client.resource.ClientResource;
@@ -120,7 +119,6 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
     
     //KEYCLOAK-4983
     @Test
-    @Ignore
     public void testRoleBasedPolicyWithMultipleRoles() {
         ClientResource clientResource = getClientResource();
 
@@ -152,31 +150,17 @@ public class ExportAuthorizationSettingsTest extends AbstractAuthorizationTest {
         //export authorization settings
         ResourceServerRepresentation exportSettings = authorizationResource.exportSettings();
         
-        //delete test-resource-server client
-        testRealmResource().clients().get(clientResource.toRepresentation().getId()).remove();
-        
-        //clear cache
-        testRealmResource().clearRealmCache();
-        //workaround for the fact that clearing realm cache doesn't clear authz cache
-        testingClient.testing("test").cache("authorization").clear();
-        
-        //create new client
-        ClientRepresentation client = ClientBuilder.create()
-                .clientId(RESOURCE_SERVER_CLIENT_ID)
-                .authorizationServicesEnabled(true)
-                .serviceAccountsEnabled(true)
-                .build();
-        testRealmResource().clients().create(client).close();
-        
-        //import exported settings
-        AuthorizationResource authorization = testRealmResource().clients().get(getClientByClientId(RESOURCE_SERVER_CLIENT_ID).getId()).authorization();
-        authorization.importSettings(exportSettings);
-        
-        //check imported settings - TODO
-        PolicyRepresentation result = authorization.policies().findByName("role-based-policy");
-        Map<String, String> config1 = result.getConfig();
-        ResourceServerRepresentation settings = authorization.getSettings();
-        System.out.println("");
+        boolean found = false;
+        for (PolicyRepresentation p : exportSettings.getPolicies()) {
+            if (p.getName().equals("role-based-policy")) {
+                found = true;
+                Assert.assertTrue(p.getConfig().get("roles").contains("test-client-1/client-role") && 
+                        p.getConfig().get("roles").contains("test-client-2/client-role"));
+            }
+        }
+        if (!found) {
+            Assert.fail("Policy \"role-based-policy\" was not found in exported settings.");
+        }
     }
     
     private ClientRepresentation getClientByClientId(String clientId) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java
index 055bfa2..064e7b8 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/InitialAccessTokenResourceTest.java
@@ -20,14 +20,17 @@ package org.keycloak.testsuite.admin;
 import org.junit.Before;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientInitialAccessResource;
+import org.keycloak.client.registration.ClientRegistrationException;
 import org.keycloak.common.util.Time;
 import org.keycloak.events.admin.OperationType;
 import org.keycloak.events.admin.ResourceType;
 import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
 import org.keycloak.representations.idm.ClientInitialAccessPresentation;
+import org.keycloak.testsuite.Assert;
 import org.keycloak.testsuite.util.AdminEventPaths;
 
 import java.util.List;
+import java.util.stream.Collectors;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -88,4 +91,40 @@ public class InitialAccessTokenResourceTest extends AbstractAdminTest {
         assertEquals(5, list.get(0).getCount() + list.get(1).getCount());
     }
 
+
+    @Test
+    public void testPeriodicExpiration() throws ClientRegistrationException, InterruptedException {
+        ClientInitialAccessPresentation response1 = resource.create(new ClientInitialAccessCreatePresentation(1, 1));
+        ClientInitialAccessPresentation response2 = resource.create(new ClientInitialAccessCreatePresentation(1000, 1));
+        ClientInitialAccessPresentation response3 = resource.create(new ClientInitialAccessCreatePresentation(1000, 0));
+        ClientInitialAccessPresentation response4 = resource.create(new ClientInitialAccessCreatePresentation(0, 1));
+
+        List<ClientInitialAccessPresentation> list = resource.list();
+        assertEquals(4, list.size());
+
+        setTimeOffset(10);
+
+        testingClient.testing().removeExpired(REALM_NAME);
+
+        list = resource.list();
+        assertEquals(2, list.size());
+
+        List<String> remainingIds = list.stream()
+                .map(initialAccessPresentation -> initialAccessPresentation.getId())
+                .collect(Collectors.toList());
+
+        Assert.assertNames(remainingIds, response2.getId(), response4.getId());
+
+        setTimeOffset(2000);
+
+        testingClient.testing().removeExpired(REALM_NAME);
+
+        list = resource.list();
+        assertEquals(1, list.size());
+        Assert.assertEquals(list.get(0).getId(), response4.getId());
+
+        // Cleanup
+        realm.clientInitialAccess().delete(response4.getId());
+    }
+
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java
new file mode 100644
index 0000000..5f9fd0e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedDocumentOnlyBrokerTest.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.broker;
+
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.arquillian.SuiteContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
+
+public class KcSamlSignedDocumentOnlyBrokerTest extends KcSamlBrokerTest {
+
+    public static class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
+
+        @Override
+        public RealmRepresentation createProviderRealm() {
+            RealmRepresentation realm = super.createProviderRealm();
+
+            realm.setPublicKey(REALM_PUBLIC_KEY);
+            realm.setPrivateKey(REALM_PRIVATE_KEY);
+
+            return realm;
+        }
+
+        @Override
+        public RealmRepresentation createConsumerRealm() {
+            RealmRepresentation realm = super.createConsumerRealm();
+
+            realm.setPublicKey(REALM_PUBLIC_KEY);
+            realm.setPrivateKey(REALM_PRIVATE_KEY);
+
+            return realm;
+        }
+
+        @Override
+        public List<ClientRepresentation> createProviderClients(SuiteContext suiteContext) {
+            List<ClientRepresentation> clientRepresentationList = super.createProviderClients(suiteContext);
+
+            for (ClientRepresentation client : clientRepresentationList) {
+                client.setClientAuthenticatorType("client-secret");
+                client.setSurrogateAuthRequired(false);
+
+                Map<String, String> attributes = client.getAttributes();
+                if (attributes == null) {
+                    attributes = new HashMap<>();
+                    client.setAttributes(attributes);
+                }
+
+                attributes.put("saml.assertion.signature", "false");
+                attributes.put("saml.server.signature", "true");
+                attributes.put("saml.client.signature", "true");
+                attributes.put("saml.signature.algorithm", "RSA_SHA256");
+                attributes.put("saml.signing.private.key", IDP_SAML_SIGN_KEY);
+                attributes.put("saml.signing.certificate", IDP_SAML_SIGN_CERT);
+            }
+
+            return clientRepresentationList;
+        }
+
+        @Override
+        public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
+            IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
+
+            Map<String, String> config = result.getConfig();
+
+            config.put("validateSignature", "true");
+            config.put("wantAssertionsSigned", "false");
+            config.put("wantAuthnRequestsSigned", "true");
+            config.put("signingCertificate", IDP_SAML_SIGN_CERT);
+
+            return result;
+        }
+    }
+
+    @Override
+    protected BrokerConfiguration getBrokerConfiguration() {
+        return KcSamlSignedBrokerConfiguration.INSTANCE;
+    }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
index aa8649e..f9919d5 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
@@ -74,6 +74,7 @@
                 ${auth.server.feature}
             </property>
             <property name="javaVmArguments">
+                ${auth.server.jboss.jvm.debug.args}
                 ${auth.server.memory.settings}
                 -Djava.net.preferIPv4Stack=true
             </property>
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/common/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/other/adapters/jboss/common/xslt/arquillian.xsl
index 30e7dba..67a494a 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/common/xslt/arquillian.xsl
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/common/xslt/arquillian.xsl
@@ -39,6 +39,7 @@
                         ${adapter.test.props}
                     </property>
                     <property name="javaVmArguments">
+                        ${app.server.jboss.jvm.debug.args}
                         ${app.server.memory.settings}
                         -Djava.net.preferIPv4Stack=true
                     </property>
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index e90f353..70533a9 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -52,6 +52,11 @@
         <app.server.startup.timeout>60</app.server.startup.timeout>
         <app.server.memory.settings>-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m</app.server.memory.settings>
 
+        <!--debug properties-->
+        <app.server.debug.port>5006</app.server.debug.port>
+        <app.server.debug.suspend>n</app.server.debug.suspend>
+        <app.server.jboss.jvm.debug.args>-agentlib:jdwp=transport=dt_socket,server=y,suspend=${app.server.debug.suspend},address=${app.server.host}:${app.server.debug.port}</app.server.jboss.jvm.debug.args>
+        
         <app.server.ssl.required>false</app.server.ssl.required>
 
         <app.server.reverse-proxy.port.offset>500</app.server.reverse-proxy.port.offset>
@@ -205,7 +210,8 @@
 
                                 <app.server.startup.timeout>${app.server.startup.timeout}</app.server.startup.timeout>
                                 <app.server.memory.settings>${app.server.memory.settings}</app.server.memory.settings>
-
+                                <app.server.jboss.jvm.debug.args>${app.server.jboss.jvm.debug.args}</app.server.jboss.jvm.debug.args>
+                                
                                 <app.server.reverse-proxy.port.offset>${app.server.reverse-proxy.port.offset}</app.server.reverse-proxy.port.offset>
 
                                 <app.server.1.port.offset>${app.server.1.port.offset}</app.server.1.port.offset>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index e5bba34..24e67c9 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -64,6 +64,11 @@
         <auth.server.jboss.skip.unpack>${auth.server.undertow}</auth.server.jboss.skip.unpack>
         <auth.server.jboss.startup.timeout>300</auth.server.jboss.startup.timeout>
         
+        <!--debug properties-->
+        <auth.server.debug.port>5005</auth.server.debug.port>
+        <auth.server.debug.suspend>n</auth.server.debug.suspend>
+        <auth.server.jboss.jvm.debug.args>-agentlib:jdwp=transport=dt_socket,server=y,suspend=${auth.server.debug.suspend},address=${auth.server.host}:${auth.server.debug.port}</auth.server.jboss.jvm.debug.args>
+        
         <auth.server.remote>false</auth.server.remote>
         <auth.server.profile/>
         <auth.server.feature/>
@@ -226,6 +231,7 @@
                             <auth.server.config.property.name>${auth.server.config.property.name}</auth.server.config.property.name>
                             <auth.server.config.property.value>${auth.server.config.property.value}</auth.server.config.property.value>
                             <auth.server.adapter.impl.class>${auth.server.adapter.impl.class}</auth.server.adapter.impl.class>
+                            <auth.server.jboss.jvm.debug.args>${auth.server.jboss.jvm.debug.args}</auth.server.jboss.jvm.debug.args>
                             
                             <auth.server.profile>${auth.server.profile}</auth.server.profile>
                             <auth.server.feature>${auth.server.feature}</auth.server.feature>