keycloak-aplcache

Migrate SAML adapter tests

8/2/2016 8:14:47 AM

Changes

testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SAMLFilterDependency.java 87(+0 -87)

Details

diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java
new file mode 100644
index 0000000..8d10b22
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BadAssertionSalesPostSig.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class BadAssertionSalesPostSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "bad-assertion-sales-post-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java
new file mode 100644
index 0000000..c8a0d66
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeServlet.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class EmployeeServlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "employee";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
index 6e66e2c..7edc5c5 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
@@ -17,18 +17,18 @@
 
 package org.keycloak.testsuite.adapter.page;
 
-import java.net.URL;
 import org.jboss.arquillian.container.test.api.OperateOnDeployment;
 import org.jboss.arquillian.test.api.ArquillianResource;
-import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
+import java.net.URL;
+
 /**
  *
  * @author tkyjovsk
  */
-public class InputPortal extends AbstractPageWithInjectedUrl {
+public class InputPortal extends SAMLServlet {
 
     public static final String DEPLOYMENT_NAME = "input-portal";
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java
new file mode 100644
index 0000000..78ff38c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MissingAssertionSig.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class MissingAssertionSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "missing-assertion-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java
new file mode 100644
index 0000000..d821d86
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPost2Servlet.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class SalesPost2Servlet extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "sales-post2";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java
new file mode 100644
index 0000000..a4522b4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SalesPostAssertionAndResponseSig.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+
+import java.net.URL;
+
+/**
+ * @author mhajas
+ */
+public class SalesPostAssertionAndResponseSig extends SAMLServlet {
+    public static final String DEPLOYMENT_NAME = "sales-post-assertion-and-response-sig";
+
+    @ArquillianResource
+    @OperateOnDeployment(DEPLOYMENT_NAME)
+    private URL url;
+
+    @Override
+    public URL getInjectedUrl() {
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
index fe856dc..cc4a419 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SAMLServlet.java
@@ -40,8 +40,8 @@ public abstract class SAMLServlet extends AbstractPageWithInjectedUrl {
         }
     }
 
-    public void checkRolesEndPoint() {
-        driver.navigate().to(getUriBuilder().build().toASCIIString() + "/checkRoles");
+    public void checkRolesEndPoint(boolean value) {
+        driver.navigate().to(getUriBuilder().build().toASCIIString() + "/" + (value ? "" : "un") + "checkRoles");
         pause(300);
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
index a2038bf..5fb04e9 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
@@ -17,6 +17,8 @@
 
 package org.keycloak.testsuite.adapter.servlet;
 
+import org.junit.Assert;
+
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
@@ -31,6 +33,8 @@ import java.io.PrintWriter;
 @WebServlet("/input-portal")
 public class InputServlet extends HttpServlet {
 
+    private static final String FORM_URLENCODED = "application/x-www-form-urlencoded";
+
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String appBase;
@@ -41,6 +45,16 @@ public class InputServlet extends HttpServlet {
         }
         String actionUrl = appBase + "/input-portal/secured/post";
 
+        if (req.getRequestURI().endsWith("insecure")) {
+            if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertNotNull(req.getUserPrincipal());
+            resp.setContentType("text/html");
+            PrintWriter pw = resp.getWriter();
+            pw.printf("<html><head><title>Input Servlet</title></head><body>%s\n", "Insecure Page");
+            if (req.getUserPrincipal() != null) pw.printf("UserPrincipal: " + req.getUserPrincipal().getName());
+            pw.print("</body></html>");
+            pw.flush();
+            return;
+        }
 
         resp.setContentType("text/html");
         PrintWriter pw = resp.getWriter();
@@ -56,6 +70,16 @@ public class InputServlet extends HttpServlet {
 
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        if (!FORM_URLENCODED.equals(req.getContentType())) {
+            resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+            PrintWriter pw = resp.getWriter();
+            resp.setContentType("text/plain");
+            pw.printf("Expecting content type " + FORM_URLENCODED +
+                    ", received " + req.getContentType() + " instead");
+            pw.flush();
+            return;
+        }
+
         resp.setContentType("text/plain");
         PrintWriter pw = resp.getWriter();
         pw.printf("parameter="+req.getParameter("parameter"));
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java
new file mode 100755
index 0000000..21f07b3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SamlSPFacade.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+* @version $Revision: 1 $
+*/
+public class SamlSPFacade extends HttpServlet {
+    public static String samlResponse;
+    public static String RELAY_STATE = "http://test.com/foo/bar";
+    public static String sentRelayState;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handler(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        handler(req, resp);
+    }
+
+    private void handler(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+        System.out.println("In SamlSPFacade Servlet handler()");
+        if (req.getParameterMap().isEmpty()) {
+            System.out.println("ParameterMap is empty, redirecting to keycloak server ");
+            resp.setStatus(302);
+            // Redirect
+            // UriBuilder builder = UriBuilder.fromUri("http://localhost:8081/auth/realms/demo/protocol/saml?SAMLRequest=jVLRTsIwFP2Vpe%2BjG4wxG0YyWYxL0BBAH3wx3XYnTbp29nYof%2B8YEvEBNOlD03vOveec2ynyWjYsae1WreC9BbTOZy0Vsr4Qk9YopjkKZIrXgMwWbJ08LNhw4LHGaKsLLcmRch3MEcFYoRVxktN1rhW2NZg1mJ0o4Gm1iMnW2oZRKnXB5VajZZEX%2BRTqRuo9ACVO2mkUih%2F4l9C8s0MNcFkjLaHW9KSUHlwR506bAnrPMam4RCBOlsYkS1%2BD3MvLcDJxAx9KN4jCkXszrG5cP%2BCVH4y8IM8PYFx2dsQOfuiILWQKLVc2JkPPH7te6HrRxh%2BzUdidwSSIXoiz%2FBZyK1Qp1Nv1yPIjCNn9ZrN0V1AKA4UlzjMY7N13IDKbHjyxXoA5291%2FtzH7I%2FApPet%2FHNawx65hli61FMXeSaTUH%2FMubtvlYU0LfcA1t5cl%2BAO%2FfxGlW%2FVQ1ipsoBCVgJLQ2XHo7385%2BwI%3D");
+            UriBuilder builder = UriBuilder.fromUri("http://localhost:8180/auth/realms/demo/protocol/saml?SAMLRequest=jZJdS8MwFIbvBf9DyX2XNG62hnUwHeLAj7JNL7yRmJ65QJrUnNSPf29WHQp%2BIOQiJM%2FJed%2F3ZIyyMa2YdmFjF%2FDYAYbkpTEWRX9Rks5b4SRqFFY2gCIosZxenAs%2BYKL1LjjlDHkv%2BRuWiOCDdpYk0932xFnsGvBL8E9awfXivCSbEFpBqXFKmo3DIApeMApNa9wrACXJLGrUVm7rf6KzSMtoh3qQpkFaQ%2BPoTinduiLJqfMKes8lWUuDQJL5rCTz2d2wLmCkgKc5Z4fpMOf3qSyO8pTXxUHOjphibBRhrKId%2FQSf5YgdzC0GaUNJOMtGKTtI2eGKcxFXlg%2BK0fCWJNWHkGNta20f%2Fo7s%2Fh1CcbZaVWl1tVyR5AY89s4jQCb7e%2BOtI9G3918m999ZTL4HyIrsM%2B4x%2FfL%2Brl0rLuOT81nljFavydQY93wS4w4xj%2BA76ANuZPhdRDbI%2BhNdp%2BseFZ3FFpRea6gJ3Tai33%2Fm5A0%3D");
+            builder.queryParam("RelayState", RELAY_STATE);
+            resp.setHeader("Location", builder.build().toString());
+            return;
+        }
+
+        System.out.println("Response was received");
+        samlResponse = req.getParameter("SAMLResponse");
+        sentRelayState = req.getParameter("RelayState");
+
+        PrintWriter pw = resp.getWriter();
+        pw.println("Relay state: " + sentRelayState);
+        pw.println("SAML response: " + samlResponse);
+        pw.flush();
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
index 58feae3..6e70f15 100755
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
@@ -19,6 +19,10 @@ package org.keycloak.testsuite.adapter.servlet;
 
 
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.keycloak.adapters.saml.SamlAuthenticationError;
+import org.keycloak.adapters.saml.SamlPrincipal;
+import org.keycloak.adapters.spi.AuthenticationError;
+import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
@@ -28,6 +32,9 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import java.io.IOException;
 import java.security.Principal;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -38,6 +45,9 @@ import java.security.Principal;
 public class SendUsernameServlet {
 
     private static boolean checkRoles = false;
+    private static SamlAuthenticationError authError;
+    private static Principal sentPrincipal;
+    private static List<String> checkRolesList = Collections.singletonList("manager");
 
     @Context
     private HttpServletRequest httpServletRequest;
@@ -62,7 +72,15 @@ public class SendUsernameServlet {
             throw new RuntimeException("User: " + httpServletRequest.getUserPrincipal() + " do not have required role");
         }
 
-        return Response.ok(getOutput(), MediaType.TEXT_PLAIN).build();
+        return Response.ok(getOutput(), MediaType.TEXT_HTML_TYPE).build();
+    }
+
+    @GET
+    @Path("getAttributes")
+    public Response getSentPrincipal() throws IOException {
+        System.out.println("In SendUsername Servlet getSentPrincipal()");
+
+        return Response.ok(getAttributes(), MediaType.TEXT_HTML_TYPE).build();
     }
 
     @GET
@@ -79,6 +97,23 @@ public class SendUsernameServlet {
         return doPost(checkRolesFlag);
     }
 
+    @POST
+    @Path("error.html")
+    public Response errorPagePost() {
+        authError = (SamlAuthenticationError) httpServletRequest.getAttribute(AuthenticationError.class.getName());
+        Integer statusCode = (Integer) httpServletRequest.getAttribute("javax.servlet.error.status_code");
+        System.out.println("In SendUsername Servlet errorPage() status code: " + statusCode);
+
+        return Response.ok(getErrorOutput(statusCode), MediaType.TEXT_HTML_TYPE).build();
+    }
+
+    @GET
+    @Path("error.html")
+    public Response errorPageGet() {
+        return errorPagePost();
+    }
+
+
     @GET
     @Path("checkRoles")
     public String checkRolesEndPoint() {
@@ -87,8 +122,35 @@ public class SendUsernameServlet {
         return "Roles will be checked";
     }
 
+    @GET
+    @Path("uncheckRoles")
+    public String uncheckRolesEndPoint() {
+        checkRoles = false;
+        System.out.println("Setting checkRoles to false");
+        checkRolesList = Collections.singletonList("manager");
+        return "Roles will not be checked";
+    }
+
+    @GET
+    @Path("setCheckRoles")
+    public String setCheckRoles(@QueryParam("roles") String roles) {
+        checkRolesList = Arrays.asList(roles.split(","));
+        checkRoles = true;
+        System.out.println("Setting checkRolesList to " + checkRolesList.toString());
+        return "These roles will be checked: " + checkRolesList.toString();
+    }
+
+
     private boolean checkRoles() {
-        return httpServletRequest.isUserInRole("manager");
+        for (String role : checkRolesList) {
+            System.out.println("In checkRoles() checking role " + role + " for user " + httpServletRequest.getUserPrincipal().getName());
+            if (!httpServletRequest.isUserInRole(role)) {
+                System.out.println("User is not in role " + role);
+                return false;
+            }
+        }
+
+        return true;
     }
 
     private String getOutput() {
@@ -102,6 +164,31 @@ public class SendUsernameServlet {
             return output + "null";
         }
 
+        sentPrincipal = principal;
+
         return output + principal.getName();
     }
+
+    private String getErrorOutput(Integer statusCode) {
+        String output = "<html><head><title>Error Page</title></head><body><h1>There was an error</h1>";
+        if (statusCode != null)
+            output += "<br/>HTTP status code: " + statusCode;
+        if (authError != null)
+            output += "<br/>Error info: " + authError.toString();
+        return output + "</body></html>";
+    }
+
+    private String getAttributes() {
+        SamlPrincipal principal = (SamlPrincipal) sentPrincipal;
+        String output = "attribute email: " + principal.getAttribute(X500SAMLProfileConstants.EMAIL.get());
+        output += "<br /> topAttribute: " + principal.getAttribute("topAttribute");
+        output += "<br /> level2Attribute: " + principal.getAttribute("level2Attribute");
+        output += "<br /> group: " + principal.getAttributes("group").toString();
+        output += "<br /> friendlyAttribute email: " + principal.getFriendlyAttribute("email");
+        output += "<br /> phone: " + principal.getAttribute("phone");
+        output += "<br /> friendlyAttribute phone: " + principal.getFriendlyAttribute("phone");
+        output += "<br /> hardcoded-attribute: " + principal.getAttribute("hardcoded-attribute");
+
+        return output;
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
index 68d7183..9cd7625 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -17,7 +17,6 @@
 
 package org.keycloak.testsuite.arquillian;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.tools.ant.DirectoryScanner;
 import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
 import org.jboss.arquillian.test.spi.TestClass;
@@ -31,6 +30,7 @@ import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
 import org.keycloak.testsuite.util.IOUtil;
 import org.keycloak.util.JsonSerialization;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 
 import javax.xml.transform.TransformerException;
 import java.io.File;
@@ -112,6 +112,12 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
                     log.error("Can't transform document to String");
                     throw new RuntimeException(e);
                 }
+
+                // For running SAML tests it is necessary to have few dependencies on app-server side.
+                // Few of them are not in adapter zip so we need to add them manually here
+                log.info("Adding SAMLFilter dependencies to " + archive.getName());
+                ((WebArchive) archive).addAsLibraries(KeycloakDependenciesResolver.resolveDependencies("org.keycloak:keycloak-saml-servlet-filter-adapter:" + System.getProperty("project.version")));
+
             } else { // OIDC adapter config
                 try {
                     AdapterConfig adapterConfig = loadJson(archive.get(adapterConfigPath)
@@ -156,45 +162,58 @@ public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
 
     protected void modifyWebXml(Archive<?> archive, TestClass testClass) {
         try {
-            String webXmlContent = IOUtils.toString(
+            Document webXmlDoc = loadXML(
                     archive.get(WEBXML_PATH).getAsset().openStream());
             if (isTomcatAppServer(testClass.getJavaClass())) {
-                webXmlContent = webXmlContent.replace("<auth-method>KEYCLOAK</auth-method>", "<auth-method>BASIC</auth-method>");
+                modifyDocElementValue(webXmlDoc, "auth-method", "KEYCLOAK", "BASIC");
             }
 
             if (testClass.getJavaClass().isAnnotationPresent(UseServletFilter.class)) {
                 //We need to add filter declaration to web.xml
                 log.info("Adding filter to " + testClass.getAnnotation(UseServletFilter.class).filterClass() + " with mapping " + testClass.getAnnotation(UseServletFilter.class).filterPattern() + " for " + archive.getName());
-                String filter = "\n<filter>\n" +
-                        "<filter-name>" + testClass.getAnnotation(UseServletFilter.class).filterName() + "</filter-name>\n" +
-                        "<filter-class>" + testClass.getAnnotation(UseServletFilter.class).filterClass() + "</filter-class>\n" +
-                        "</filter>\n" +
-                        "\n<filter-mapping>\n" +
-                        "<filter-name>" + testClass.getAnnotation(UseServletFilter.class).filterName() + "</filter-name>\n" +
-                        "<url-pattern>" + testClass.getAnnotation(UseServletFilter.class).filterPattern() + "</url-pattern>\n";
-                if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
-                    filter += "<dispatcher>" + testClass.getAnnotation(UseServletFilter.class).dispatcherType() + "</dispatcher>\n";
-                }
-                filter += "</filter-mapping>\n";
 
-                webXmlContent = webXmlContent.replace("</module-name>", "</module-name> " + filter);
+                Element filter = webXmlDoc.createElement("filter");
+                Element filterName = webXmlDoc.createElement("filter-name");
+                Element filterClass = webXmlDoc.createElement("filter-class");
 
-                //Also we need to add all dependencies within war lib directory, because filter needs to work without installed adapter
-                log.info("Adding SAMLFilter dependencies to " + archive.getName());
-                ((WebArchive) archive).addAsLibraries(new SAMLFilterDependency().getDependencies());
+                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+                filterClass.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterClass());
 
+                filter.appendChild(filterName);
+                filter.appendChild(filterClass);
+                appendChildInDocument(webXmlDoc, "web-app", filter);
+
+                Element filterMapping = webXmlDoc.createElement("filter-mapping");
 
-                //finally we need to remove all keycloak related configuration from web.xml
-                int start = webXmlContent.indexOf("<security-constraint>");
-                int end = webXmlContent.indexOf("</security-role>") + "</security-role>".length();
 
+                Element urlPattern = webXmlDoc.createElement("url-pattern");
 
-                webXmlContent = webXmlContent.substring(0, start) + webXmlContent.substring(end);
+                filterName = webXmlDoc.createElement("filter-name");
+
+                filterName.setTextContent(testClass.getAnnotation(UseServletFilter.class).filterName());
+                urlPattern.setTextContent(getElementTextContent(webXmlDoc, "web-app/security-constraint/web-resource-collection/url-pattern"));
+
+                filterMapping.appendChild(filterName);
+                filterMapping.appendChild(urlPattern);
+
+                if (!testClass.getAnnotation(UseServletFilter.class).dispatcherType().isEmpty()) {
+                    Element dispatcher = webXmlDoc.createElement("dispatcher");
+                    dispatcher.setTextContent(testClass.getAnnotation(UseServletFilter.class).dispatcherType());
+                    filterMapping.appendChild(dispatcher);
+                }
+                appendChildInDocument(webXmlDoc, "web-app", filterMapping);
+
+                //finally we need to remove all keycloak related configuration from web.xml
+                removeElementFromDoc(webXmlDoc, "web-app", "security-constraint");
+                removeElementFromDoc(webXmlDoc, "web-app", "login-config");
+                removeElementFromDoc(webXmlDoc, "web-app", "security-role");
             }
 
-            archive.add(new StringAsset((webXmlContent)), WEBXML_PATH);
-        } catch (IOException ex) {
-            throw new RuntimeException("Cannot load web.xml from archive.");
+
+            archive.add(new StringAsset((documentToString(webXmlDoc))), WEBXML_PATH);
+        } catch (TransformerException e) {
+            log.error("Can't transform document to String");
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java
new file mode 100644
index 0000000..2f62c2b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakDependenciesResolver.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.arquillian;
+
+import org.jboss.logging.Logger;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.PomEquippedResolveStage;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencies;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author mhajas
+ */
+public class KeycloakDependenciesResolver {
+
+    private static Map<String, File[]> dependencies = new HashMap<>();
+
+    protected static final Logger log = org.jboss.logging.Logger.getLogger(KeycloakDependenciesResolver.class);
+
+    public static File[] resolveDependencies(String canonicalForm) {
+        if (dependencies.containsKey(canonicalForm)) {
+            return dependencies.get(canonicalForm);
+        }
+
+        log.info("Resolving " + canonicalForm + "'s dependencies");
+        PomEquippedResolveStage resolver = Maven.configureResolverViaPlugin();
+
+        File[] files = resolver.addDependency(MavenDependencies.createDependency(canonicalForm, ScopeType.COMPILE, false))
+                .resolve().withTransitivity().asFile();
+
+        dependencies.put(canonicalForm, files);
+
+        log.info("Resolving dependencies is finished with " + files.length + " files");
+
+        return dependencies.get(canonicalForm);
+    }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
index d872914..89084d4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -16,9 +16,11 @@
  */
 package org.keycloak.testsuite.util;
 
+import org.jboss.logging.Logger;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.util.JsonSerialization;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
@@ -33,7 +35,6 @@ import javax.xml.transform.dom.DOMSource;
 import javax.xml.transform.stream.StreamResult;
 import java.io.*;
 import java.util.concurrent.TimeUnit;
-import org.jboss.logging.Logger;
 
 /**
  *
@@ -93,18 +94,99 @@ public class IOUtil {
     public static void modifyDocElementAttribute(Document doc, String tagName, String attributeName, String regex, String replacement) {
         NodeList nodes = doc.getElementsByTagName(tagName);
         if (nodes.getLength() != 1) {
-            System.out.println("Not able to find element: " + tagName);
+            log.warn("Not able or ambiguous to find element: " + tagName);
             return;
         }
 
         Node node = nodes.item(0).getAttributes().getNamedItem(attributeName);
         if (node == null) {
-            System.out.println("Not able to find attribute " + attributeName + " within element: " + tagName);
+            log.warn("Not able to find attribute " + attributeName + " within element: " + tagName);
+            return;
+        }
+        node.setTextContent(node.getTextContent().replace(regex, replacement));
+    }
+
+    public static void modifyDocElementValue(Document doc, String tagName, String regex, String replacement) {
+        NodeList nodes = doc.getElementsByTagName(tagName);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + tagName);
+            return;
+        }
+
+        Node node = nodes.item(0);
+        if (node == null) {
+            log.warn("Not able to find element: " + tagName);
             return;
         }
+
         node.setTextContent(node.getTextContent().replace(regex, replacement));
     }
 
+    public static void removeElementFromDoc(Document doc, String parentTag, String removeNode) {
+        NodeList nodes = doc.getElementsByTagName(parentTag);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + parentTag);
+            return;
+        }
+
+        Element parentElement = (Element) nodes.item(0);
+        if (parentElement == null) {
+            log.warn("Not able to find element: " + parentTag);
+            return;
+        }
+
+        NodeList removeNodes = parentElement.getElementsByTagName(removeNode);
+        if (removeNodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + removeNode + " within node " + parentTag);
+            return;
+        }
+
+        Element removeElement = (Element) removeNodes.item(0);
+        if (removeElement == null) {
+            log.warn("Not able to find element: " + removeNode + " within node " + parentTag);
+            return;
+        }
+
+        parentElement.removeChild(removeElement);
+    }
+
+    public static String getElementTextContent(Document doc, String path) {
+        String[] pathSegments = path.split("/");
+
+        Element currentElement = (Element) doc.getElementsByTagName(pathSegments[0]).item(0);
+        if (currentElement == null) {
+            log.warn("Not able to find element: " + pathSegments[0] + " in document");
+            return null;
+        }
+
+        for (int i = 1; i < pathSegments.length; i++) {
+            currentElement = (Element) currentElement.getElementsByTagName(pathSegments[i]).item(0);
+
+            if (currentElement == null) {
+                log.warn("Not able to find element: " + pathSegments[i] + " in " + pathSegments[i - 1]);
+                return null;
+            }
+        }
+
+        return currentElement.getTextContent();
+    }
+
+    public static void appendChildInDocument(Document doc, String parentTag, Element node) {
+        NodeList nodes = doc.getElementsByTagName(parentTag);
+        if (nodes.getLength() != 1) {
+            log.warn("Not able or ambiguous to find element: " + parentTag);
+            return;
+        }
+
+        Element parentElement = (Element) nodes.item(0);
+        if (parentElement == null) {
+            log.warn("Not able to find element: " + parentTag);
+            return;
+        }
+
+        parentElement.appendChild(node);
+    }
+
     public static void execCommand(String command, File dir) throws IOException, InterruptedException {
         Process process = Runtime.getRuntime().exec(command, null, dir);
         if (process.waitFor(10, TimeUnit.SECONDS)) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
index 0ef7215..63821b3 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
@@ -31,6 +31,7 @@ import java.io.IOException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  *
@@ -63,10 +64,12 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
                 modifyClientUrls(tr, appServerContextRootPage.toString(), "");
                 modifyClientWebOrigins(tr, "8080", System.getProperty("auth.server.http.port", null));
                 modifySamlMasterURLs(tr, "/", "http://localhost:" + System.getProperty("auth.server.http.port", null) + "/");
+                modifySAMLClientsAttributes(tr, "8080", System.getProperty("auth.server.http.port", "8180"));
             } else {
                 modifyClientRedirectUris(tr, "^(/.*/\\*)", appServerContextRootPage.toString() + "$1");
                 modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
                 modifySamlMasterURLs(tr, "8080", System.getProperty("auth.server.http.port", null));
+                modifySAMLClientsAttributes(tr, "8080", System.getProperty("app.server.http.port", "8280"));
             }
             if ("true".equals(System.getProperty("auth.server.ssl.required"))) {
                 tr.setSslRequired("all");
@@ -125,6 +128,19 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
         }
     }
 
+    protected void modifySAMLClientsAttributes(RealmRepresentation realm, String regex, String replacement) {
+        if (realm.getClients() != null) {
+            for (ClientRepresentation client : realm.getClients()) {
+                if (client.getProtocol() != null && client.getProtocol().equals("saml")) {
+                    log.info("Modifying attributes of SAML client: " + client.getClientId());
+                    for (Map.Entry<String, String> entry : client.getAttributes().entrySet()) {
+                        client.getAttributes().put(entry.getKey(), entry.getValue().replaceAll(regex, replacement));
+                    }
+                }
+            }
+        }
+    }
+
     protected void modifySamlMasterURLs(RealmRepresentation realm, String regex, String replacement) {
         if (realm.getClients() != null) {
             for (ClientRepresentation client : realm.getClients()) {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
index badf51c..127c863 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -25,12 +25,11 @@ import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.testsuite.util.WaitUtils;
 import org.openqa.selenium.By;
 
+import javax.ws.rs.core.UriBuilder;
 import java.io.IOException;
 import java.net.URL;
 import java.util.List;
 
-import javax.ws.rs.core.UriBuilder;
-
 import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
 import static org.keycloak.testsuite.util.IOUtil.loadRealm;
 
@@ -66,15 +65,15 @@ public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
     }
 
     protected static WebArchive samlServletDeployment(String name, Class... servletClasses) {
-        return samlServletDeployment(name, "keycloak-saml.xml", servletClasses);
+        return samlServletDeployment(name, "web.xml", servletClasses);
     }
 
-    protected static WebArchive samlServletDeployment(String name, String adapterConfig ,Class... servletClasses) {
+    protected static WebArchive samlServletDeployment(String name, String webXMLPath, Class... servletClasses) {
         String baseSAMLPath = "/adapter-test/keycloak-saml/";
         String webInfPath = baseSAMLPath + name + "/WEB-INF/";
 
-        URL keycloakSAMLConfig = AbstractServletsAdapterTest.class.getResource(webInfPath + adapterConfig);
-        URL webXML = AbstractServletsAdapterTest.class.getResource(baseSAMLPath + "web.xml");
+        URL keycloakSAMLConfig = AbstractServletsAdapterTest.class.getResource(webInfPath + "keycloak-saml.xml");
+        URL webXML = AbstractServletsAdapterTest.class.getResource(baseSAMLPath + webXMLPath);
 
         WebArchive deployment = ShrinkWrap.create(WebArchive.class, name + ".war")
                 .addClasses(servletClasses)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
index 0df65a4..268285a 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLFilterServletAdapterTest.java
@@ -2,6 +2,8 @@ package org.keycloak.testsuite.adapter.servlet;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
 import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
 
 /**
@@ -25,11 +27,12 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesPostSigEmailServletPage.checkRoles(true);
         salesPostSigPersistentServletPage.checkRoles(true);
         salesPostSigTransientServletPage.checkRoles(true);
-        employee2ServletPage.navigateTo();
+        salesPostAssertionAndResponseSigPage.checkRoles(true);
 
         //using endpoint instead of query param because we are not able to put query param to IDP initiated login
+        employee2ServletPage.navigateTo();
         testRealmLoginPage.form().login(bburkeUser);
-        employee2ServletPage.checkRolesEndPoint();
+        employee2ServletPage.checkRolesEndPoint(true);
         employee2ServletPage.logout();
 
         forbiddenIfNotAuthenticated = false;
@@ -51,4 +54,18 @@ public abstract class AbstractSAMLFilterServletAdapterTest extends AbstractSAMLS
         salesPostSigPersistentServletPage.checkRoles(false);
         salesPostSigTransientServletPage.checkRoles(false);
     }
+
+    @Test
+    @Override
+    @Ignore
+    public void testSavedPostRequest() {
+
+    }
+
+    @Test
+    @Override
+    @Ignore
+    public void testErrorHandling() {
+
+    }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
index ddc23b7..2973caa 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java
@@ -20,11 +20,19 @@ package org.keycloak.testsuite.adapter.servlet;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.arquillian.graphene.page.Page;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
 import org.junit.Test;
 import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.ProtocolMappersResource;
+import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
+import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ProtocolMapperRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.saml.BaseSAML2BindingBuilder;
+import org.keycloak.saml.SAML2ErrorResponseBuilder;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
 import org.keycloak.testsuite.adapter.page.*;
 import org.keycloak.testsuite.admin.ApiUtil;
@@ -35,8 +43,15 @@ import org.keycloak.testsuite.util.IOUtil;
 import org.openqa.selenium.By;
 import org.w3c.dom.Document;
 
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
 import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -71,6 +86,9 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
     protected SalesPostServlet salesPostServletPage;
 
     @Page
+    private SalesPost2Servlet salesPost2ServletPage;
+
+    @Page
     protected SalesPostEncServlet salesPostEncServletPage;
 
     @Page
@@ -93,6 +111,26 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     protected boolean forbiddenIfNotAuthenticated = true;
 
+    @Page
+    protected SalesPostAssertionAndResponseSig salesPostAssertionAndResponseSigPage;
+
+    @Page
+    protected BadAssertionSalesPostSig badAssertionSalesPostSigPage;
+
+    @Page
+    protected MissingAssertionSig missingAssertionSigPage;
+
+    @Page
+    protected EmployeeServlet employeeServletPage;
+
+    @Page
+    private InputPortal inputPortalPage;
+
+    @Page
+    private SAMLIDPInitiatedLogin samlidpInitiatedLoginPage;
+
+    public static final String FORBIDDEN_TEXT = "HTTP status code: 403";
+
     @Deployment(name = BadClientSalesPostSigServlet.DEPLOYMENT_NAME)
     protected static WebArchive badClientSalesPostSig() {
         return samlServletDeployment(BadClientSalesPostSigServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
@@ -158,6 +196,36 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         return samlServletDeployment(SalesPostSigTransientServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
     }
 
+    @Deployment(name = InputPortal.DEPLOYMENT_NAME)
+    protected static WebArchive inputPortal() {
+        return samlServletDeployment(InputPortal.DEPLOYMENT_NAME, "input-portal/WEB-INF/web.xml" , InputServlet.class);
+    }
+
+    @Deployment(name = SalesPost2Servlet.DEPLOYMENT_NAME)
+    protected static WebArchive salesPost2() {
+        return samlServletDeployment(SalesPost2Servlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = SalesPostAssertionAndResponseSig.DEPLOYMENT_NAME)
+    protected static WebArchive salesPostAssertionAndResponseSig() {
+        return samlServletDeployment(SalesPostAssertionAndResponseSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = BadAssertionSalesPostSig.DEPLOYMENT_NAME)
+    protected static WebArchive badAssertionSalesPostSig() {
+        return samlServletDeployment(BadAssertionSalesPostSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = MissingAssertionSig.DEPLOYMENT_NAME)
+    protected static WebArchive missingAssertionSig() {
+        return samlServletDeployment(MissingAssertionSig.DEPLOYMENT_NAME, SendUsernameServlet.class);
+    }
+
+    @Deployment(name = EmployeeServlet.DEPLOYMENT_NAME)
+    protected static WebArchive employeeServlet() {
+        return samlServletDeployment(EmployeeServlet.DEPLOYMENT_NAME, "employee/WEB-INF/web.xml", SamlSPFacade.class);
+    }
+
     @Override
     public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
         testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
@@ -171,38 +239,54 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         testRealmSAMLPostLoginPage.setAuthRealm(SAMLSERVLETDEMO);
     }
 
-    private void assertForbidden(AbstractPage page) {
+    private void assertForbidden(AbstractPage page, String expectedNotContains) {
         page.navigateTo();
-        waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        waitUntilElement(By.xpath("//body")).text().not().contains(expectedNotContains);
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
-    private void assertSuccessfullyLoggedIn(AbstractPage page) {
+    private void assertSuccessfullyLoggedIn(AbstractPage page, String expectedText) {
         page.navigateTo();
-        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        waitUntilElement(By.xpath("//body")).text().contains(expectedText);
     }
 
-    private void assertForbiddenLogin(AbstractPage page, String username, String password, Login loginPage) {
+    private void assertForbiddenLogin(AbstractPage page, String username, String password, Login loginPage, String expectedNotContains) {
         page.navigateTo();
         assertCurrentUrlStartsWith(loginPage);
         loginPage.form().login(username, password);
-        waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
+        waitUntilElement(By.xpath("//body")).text().not().contains(expectedNotContains);
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
-    private void assertSuccessfulLogin(AbstractPage page, UserRepresentation user, Login loginPage) {
+    private void assertSuccessfulLogin(AbstractPage page, UserRepresentation user, Login loginPage, String expectedString) {
         page.navigateTo();
         assertCurrentUrlStartsWith(loginPage);
         loginPage.form().login(user);
-        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        waitUntilElement(By.xpath("//body")).text().contains(expectedString);
     }
 
     private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage) {
-        assertSuccessfulLogin(page, bburkeUser, loginPage);
+        testSuccessfulAndUnauthorizedLogin(page, loginPage, "principal=bburke");
+    }
+
+    private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage, String expectedText) {
+        testSuccessfulAndUnauthorizedLogin(page, loginPage, expectedText, "principal=");
+    }
+
+    private void testSuccessfulAndUnauthorizedLogin(SAMLServlet page, Login loginPage, String expectedText, String expectedNotContains) {
+        assertSuccessfulLogin(page, bburkeUser, loginPage, expectedText);
         page.logout();
-        assertForbiddenLogin(page, "unauthorized", "password", loginPage);
+        checkLoggedOut(page, loginPage);
+        assertForbiddenLogin(page, "unauthorized", "password", loginPage, expectedNotContains);
         page.logout();
+        checkLoggedOut(page, loginPage);
+    }
+
+    private void checkLoggedOut(AbstractPage page, Login loginPage) {
+        page.navigateTo();
+        waitUntilElement(By.xpath("//body")).is().present();
+        assertCurrentUrlStartsWith(loginPage);
     }
 
     @Test
@@ -221,38 +305,35 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     @Test
     public void unauthorizedSSOTest() {
-        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
-        assertForbidden(employee2ServletPage);
-        assertForbidden(employeeSigFrontServletPage);
-        assertForbidden(salesPostSigPersistentServletPage);
+        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
+        assertForbidden(employee2ServletPage, "principal=");
+        assertForbidden(employeeSigFrontServletPage, "principal=");
+        assertForbidden(salesPostSigPersistentServletPage, "principal=");
         salesPostServletPage.logout();
+        checkLoggedOut(salesPostServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void singleLoginAndLogoutSAMLTest() {
-        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage);
-        assertSuccessfullyLoggedIn(salesPostSigServletPage);
-        assertSuccessfullyLoggedIn(employee2ServletPage);
-        assertSuccessfullyLoggedIn(salesPostEncServletPage);
+        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(salesPostSigServletPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(employee2ServletPage, "principal=bburke");
+        assertSuccessfullyLoggedIn(salesPostEncServletPage, "principal=bburke");
 
         employeeSigFrontServletPage.logout();
 
-        employeeSigFrontServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLRedirectLoginPage);
-
-        employeeSigServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLRedirectLoginPage);
+        checkLoggedOut(employeeSigFrontServletPage, testRealmSAMLRedirectLoginPage);
+        checkLoggedOut(employeeSigServletPage, testRealmSAMLRedirectLoginPage);
 
         salesPostPassiveServletPage.navigateTo();
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
 
-        salesPostSigEmailServletPage.navigateTo();
-        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        checkLoggedOut(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
@@ -268,7 +349,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
         waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
         //Different 403 status page on EAP and Wildfly
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
     }
 
     @Test
@@ -328,14 +409,14 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
             //Different 403 status page on EAP and Wildfly
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
 
-        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage);
+        assertSuccessfulLogin(salesPostServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
 
-        assertSuccessfullyLoggedIn(salesPostPassiveServletPage);
+        assertSuccessfullyLoggedIn(salesPostPassiveServletPage, "principal=bburke");
 
         salesPostPassiveServletPage.logout();
         salesPostPassiveServletPage.navigateTo();
@@ -343,12 +424,13 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         if (forbiddenIfNotAuthenticated) {
             waitUntilElement(By.xpath("//body")).text().not().contains("principal=");
             //Different 403 status page on EAP and Wildfly
-            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("<body></body>") || driver.getPageSource().equals(""));
+            assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
         } else {
             waitUntilElement(By.xpath("//body")).text().contains("principal=null");
         }
-        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
-        assertForbidden(salesPostPassiveServletPage);
+
+        assertForbiddenLogin(salesPostServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
+        assertForbidden(salesPostPassiveServletPage, "principal=");
 
         salesPostPassiveServletPage.logout();
     }
@@ -360,7 +442,7 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
 
     @Test
     public void salesPostSigEmailTest() {
-        testSuccessfulAndUnauthorizedLogin(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage);
+        testSuccessfulAndUnauthorizedLogin(salesPostSigEmailServletPage, testRealmSAMLPostLoginPage, "principal=bburke@redhat.com");
     }
 
     @Test
@@ -371,9 +453,11 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         waitUntilElement(By.xpath("//body")).text().contains("principal=G-");
 
         salesPostSigPersistentServletPage.logout();
+        checkLoggedOut(salesPostSigPersistentServletPage, testRealmSAMLPostLoginPage);
 
-        assertForbiddenLogin(salesPostSigPersistentServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
+        assertForbiddenLogin(salesPostSigPersistentServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
         salesPostSigPersistentServletPage.logout();
+        checkLoggedOut(salesPostSigPersistentServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
@@ -384,36 +468,260 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
         waitUntilElement(By.xpath("//body")).text().contains("principal=G-");
 
         salesPostSigTransientServletPage.logout();
+        checkLoggedOut(salesPostSigTransientServletPage, testRealmSAMLPostLoginPage);
 
-        assertForbiddenLogin(salesPostSigTransientServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage);
+        assertForbiddenLogin(salesPostSigTransientServletPage, "unauthorized", "password", testRealmSAMLPostLoginPage, "principal=");
         salesPostSigTransientServletPage.logout();
+        checkLoggedOut(salesPostSigTransientServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void idpInitiatedLogin() {
-        samlidpInitiatedLogin.setAuthRealm(SAMLSERVLETDEMO);
-        samlidpInitiatedLogin.setUrlName("employee2");
-        samlidpInitiatedLogin.navigateTo();
-        samlidpInitiatedLogin.form().login(bburkeUser);
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("employee2");
+        samlidpInitiatedLoginPage.navigateTo();
+        samlidpInitiatedLoginPage.form().login(bburkeUser);
 
         waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
 
-        assertSuccessfullyLoggedIn(salesPostSigServletPage);
+        assertSuccessfullyLoggedIn(salesPostSigServletPage, "principal=bburke");
 
         employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
     }
 
     @Test
     public void idpInitiatedUnauthorizedLoginTest() {
-        samlidpInitiatedLogin.setAuthRealm(SAMLSERVLETDEMO);
-        samlidpInitiatedLogin.setUrlName("employee2");
-        samlidpInitiatedLogin.navigateTo();
-        samlidpInitiatedLogin.form().login("unauthorized", "password");
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("employee2");
+        samlidpInitiatedLoginPage.navigateTo();
+        samlidpInitiatedLoginPage.form().login("unauthorized", "password");
 
         waitUntilElement(By.xpath("//body")).text().not().contains("bburke");
-        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains("Status 403"));
+        assertTrue(driver.getPageSource().contains("Forbidden") || driver.getPageSource().contains(FORBIDDEN_TEXT));
+
+        assertForbidden(employee2ServletPage, "principal=");
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void testSavedPostRequest() {
+        inputPortalPage.navigateTo();
+        assertCurrentUrlStartsWith(inputPortalPage);
+        inputPortalPage.execute("hello");
+
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmLoginPage.form().login("bburke@redhat.com", "password");
+        Assert.assertEquals(driver.getCurrentUrl(), inputPortalPage + "/secured/post");
+        waitUntilElement(By.xpath("//body")).text().contains("parameter=hello");
+
+        // test that user principal and KeycloakSecurityContext available
+        driver.navigate().to(inputPortalPage + "/insecure");
+        waitUntilElement(By.xpath("//body")).text().contains("Insecure Page");
+
+        if (System.getProperty("insecure.user.principal.unsupported") == null) waitUntilElement(By.xpath("//body")).text().contains("UserPrincipal");
+
+        // test logout
+
+        inputPortalPage.logout();
+
+        // test unsecured POST KEYCLOAK-901
 
-        assertForbidden(employee2ServletPage);
+        Client client = ClientBuilder.newClient();
+        Form form = new Form();
+        form.param("parameter", "hello");
+        String text = client.target(inputPortalPage + "/unsecured").request().post(Entity.form(form), String.class);
+        Assert.assertTrue(text.contains("parameter=hello"));
+        client.close();
+    }
+
+    @Test
+    public void testPostSimpleLoginLogoutIdpInitiatedRedirectTo() {
+        samlidpInitiatedLoginPage.setAuthRealm(SAMLSERVLETDEMO);
+        samlidpInitiatedLoginPage.setUrlName("sales-post2");
+        samlidpInitiatedLoginPage.navigateTo();
+
+        samlidpInitiatedLoginPage.form().login(bburkeUser);
+        assertCurrentUrlStartsWith(salesPost2ServletPage);
+        assertTrue(driver.getCurrentUrl().endsWith("/foo"));
+        waitUntilElement(By.xpath("//body")).text().contains("principal=bburke");
+        salesPost2ServletPage.logout();
+        checkLoggedOut(salesPost2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void salesPostAssertionAndResponseSigTest() {
+        testSuccessfulAndUnauthorizedLogin(salesPostAssertionAndResponseSigPage, testRealmSAMLPostLoginPage);
+    }
+
+    @Test
+    public void testPostBadAssertionSignature() {
+        badAssertionSalesPostSigPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+
+        waitUntilElement(By.xpath("//body")).text().contains("Error info: SamlAuthenticationError [reason=INVALID_SIGNATURE, status=null]");
+        assertEquals(driver.getCurrentUrl(), badAssertionSalesPostSigPage + "/saml");
+    }
+
+    @Test
+    public void testMissingAssertionSignature() {
+        missingAssertionSigPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+
+        waitUntilElement(By.xpath("//body")).text().contains("Error info: SamlAuthenticationError [reason=INVALID_SIGNATURE, status=null]");
+        assertEquals(driver.getCurrentUrl(), missingAssertionSigPage + "/saml");
+    }
+
+    @Test
+    public void testErrorHandling() throws Exception {
+        Client client = ClientBuilder.newClient();
+        // make sure
+        Response response = client.target(employeeSigServletPage.toString()).request().get();
+        response.close();
+        SAML2ErrorResponseBuilder builder = new SAML2ErrorResponseBuilder()
+                .destination(employeeSigServletPage.toString() + "/saml")
+                .issuer("http://localhost:" + System.getProperty("auth.server.http.port", "8180") + "/realms/demo")
+                .status(JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
+        BaseSAML2BindingBuilder binding = new BaseSAML2BindingBuilder()
+                .relayState(null);
+        Document document = builder.buildDocument();
+        URI uri = binding.redirectBinding(document).generateURI(employeeSigServletPage.toString() + "/saml", false);
+        response = client.target(uri).request().get();
+        String errorPage = response.readEntity(String.class);
+        response.close();
+        Assert.assertTrue(errorPage.contains("Error info: SamlAuthenticationError [reason=ERROR_STATUS"));
+        Assert.assertFalse(errorPage.contains("status=null"));
+        client.close();
+    }
+
+    @Test
+    public void testRelayStateEncoding() throws Exception {
+        // this test has a hardcoded SAMLRequest and we hack a SP face servlet to get the SAMLResponse so we can look
+        // at the relay state
+        employeeServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("bburke", "password");
+        assertCurrentUrlStartsWith(employeeServletPage);
+        waitUntilElement(By.xpath("//body")).text().contains("Relay state: " + SamlSPFacade.RELAY_STATE);
+        waitUntilElement(By.xpath("//body")).text().not().contains("SAML response: null");
+    }
+
+    @Test
+    public void testAttributes() throws Exception {
+        ClientResource clientResource = ApiUtil.findClientResourceByClientId(testRealmResource(), "http://localhost:8081/employee2/");
+        ProtocolMappersResource protocolMappersResource = clientResource.getProtocolMappers();
+
+        Map<String, String> config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("user.attribute", "topAttribute");
+        config.put("attribute.name", "topAttribute");
+        createProtocolMapper(protocolMappersResource, "topAttribute", "saml", "saml-user-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("user.attribute", "level2Attribute");
+        config.put("attribute.name", "level2Attribute");
+        createProtocolMapper(protocolMappersResource, "level2Attribute", "saml", "saml-user-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.nameformat", "Basic");
+        config.put("single", "true");
+        config.put("attribute.name", "group");
+        createProtocolMapper(protocolMappersResource, "groups", "saml", "saml-group-membership-mapper", config);
+
+        setRolesToCheck("manager,user");
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login("level2GroupUser", "password");
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("topAttribute: true");
+        waitUntilElement(By.xpath("//body")).text().contains("level2Attribute: true");
+        waitUntilElement(By.xpath("//body")).text().contains("attribute email: level2@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().not().contains("group: []");
+        waitUntilElement(By.xpath("//body")).text().not().contains("group: null");
+        waitUntilElement(By.xpath("//body")).text().contains("group: [level2]");
+
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+
+        setRolesToCheck("manager,employee,user");
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("attribute email: bburke@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().contains("friendlyAttribute email: bburke@redhat.com");
+        waitUntilElement(By.xpath("//body")).text().contains("phone: 617");
+        waitUntilElement(By.xpath("//body")).text().contains("friendlyAttribute phone: null");
+
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+
+        config = new LinkedHashMap<>();
+        config.put("attribute.value", "hard");
+        config.put("attribute.nameformat", "Basic");
+        config.put("attribute.name", "hardcoded-attribute");
+        createProtocolMapper(protocolMappersResource, "hardcoded-attribute", "saml", "saml-hardcode-attribute-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("role", "hardcoded-role");
+        createProtocolMapper(protocolMappersResource, "hardcoded-role", "saml", "saml-hardcode-role-mapper", config);
+
+        config = new LinkedHashMap<>();
+        config.put("new.role.name", "pee-on");
+        config.put("role", "http://localhost:8081/employee/.employee");
+        createProtocolMapper(protocolMappersResource, "renamed-employee-role", "saml", "saml-role-name-mapper", config);
+
+        for (ProtocolMapperRepresentation mapper : clientResource.toRepresentation().getProtocolMappers()) {
+            if (mapper.getName().equals("role-list")) {
+                protocolMappersResource.delete(mapper.getId());
+
+                mapper.setId(null);
+                mapper.getConfig().put(RoleListMapper.SINGLE_ROLE_ATTRIBUTE, "true");
+                mapper.getConfig().put(AttributeStatementHelper.SAML_ATTRIBUTE_NAME, "memberOf");
+                protocolMappersResource.createMapper(mapper);
+            }
+        }
+
+        setRolesToCheck("pee-on,el-jefe,manager,hardcoded-role");
+
+        config = new LinkedHashMap<>();
+        config.put("new.role.name", "el-jefe");
+        config.put("role", "user");
+        createProtocolMapper(protocolMappersResource, "renamed-role", "saml", "saml-role-name-mapper", config);
+
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+
+        driver.navigate().to(employee2ServletPage.toString() + "/getAttributes");
+        waitUntilElement(By.xpath("//body")).text().contains("hardcoded-attribute: hard");
+        employee2ServletPage.checkRolesEndPoint(false);
+        employee2ServletPage.logout();
+        checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
+    }
+
+    private void createProtocolMapper(ProtocolMappersResource resource, String name, String protocol, String protocolMapper, Map<String, String> config) {
+        ProtocolMapperRepresentation representation = new ProtocolMapperRepresentation();
+        representation.setName(name);
+        representation.setProtocol(protocol);
+        representation.setProtocolMapper(protocolMapper);
+        representation.setConfig(config);
+        resource.createMapper(representation);
+    }
+
+    private void setRolesToCheck(String roles) {
+        employee2ServletPage.navigateTo();
+        assertCurrentUrlStartsWith(testRealmSAMLPostLoginPage);
+        testRealmSAMLPostLoginPage.form().login(bburkeUser);
+        driver.navigate().to(employee2ServletPage.toString() + "/setCheckRoles?roles=" + roles);
         employee2ServletPage.logout();
     }
 }
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..2ea7ff7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,62 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/bad-assertion-sales-post-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8081/bad-realm-sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8081/bad-realm-sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService signRequest="true"
+                                 validateAssertionSignature="true"
+                                 requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    validateRequestSignature="true"
+                    validateResponseSignature="true"
+                    signRequest="true"
+                    signResponse="true"
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..215384c
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..96f0b86
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,42 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/employee/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="memberOf"/>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml
new file mode 100644
index 0000000..0199588
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee/WEB-INF/web.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>%CONTEXT_PATH%</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.SamlSPFacade</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>manager</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK-SAML</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>manager</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..e1cd76a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/input-portal/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..865dc8a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/input-portal/WEB-INF/web.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+         version="3.0">
+
+    <module-name>%CONTEXT_PATH%</module-name>
+
+    <servlet>
+        <servlet-name>Servlet</servlet-name>
+        <servlet-class>org.keycloak.testsuite.adapter.servlet.InputServlet</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>Servlet</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Users</web-resource-name>
+            <url-pattern>/secured/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>manager</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <login-config>
+        <auth-method>KEYCLOAK-SAML</auth-method>
+        <realm-name>demo</realm-name>
+    </login-config>
+
+    <security-role>
+        <role-name>manager</role-name>
+    </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..936fb21
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,60 @@
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/missing-assertion-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signaturesRequired="true">
+        <SingleSignOnService requestBinding="POST"
+                             bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                             validateAssertionSignature="true"
+                             validateResponseSignature="false"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/missing-assertion-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..eae5b14
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post2/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/sales-post2/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp">
+            <SingleSignOnService requestBinding="POST"
+                                 bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml
new file mode 100755
index 0000000..94c3f4d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keycloak-saml.xml
@@ -0,0 +1,60 @@
+<!--
+  ~ JBoss, Home of Professional Open Source.
+  ~ Copyright 2016 Red Hat, Inc., and individual 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.
+  -->
+
+<keycloak-saml-adapter>
+    <SP entityID="http://localhost:8081/sales-post-assertion-and-response-sig/"
+        sslPolicy="EXTERNAL"
+        nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+        logoutPage="/logout.jsp"
+        forceAuthentication="false">
+        <Keys>
+            <Key signing="true" >
+                <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                    <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+                    <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+                </KeyStore>
+            </Key>
+        </Keys>
+        <PrincipalNameMapping policy="FROM_NAME_ID"/>
+        <RoleIdentifiers>
+            <Attribute name="Role"/>
+        </RoleIdentifiers>
+        <IDP entityID="idp"
+             signaturesRequired="true">
+        <SingleSignOnService requestBinding="POST"
+                             bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                             validateAssertionSignature="true"
+                             validateResponseSignature="true"
+                    />
+
+            <SingleLogoutService
+                    requestBinding="POST"
+                    responseBinding="POST"
+                    postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
+                    />
+            <Keys>
+                <Key signing="true">
+                    <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+                        <Certificate alias="demo"/>
+                    </KeyStore>
+                </Key>
+            </Keys>
+        </IDP>
+     </SP>
+</keycloak-saml-adapter>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks
new file mode 100755
index 0000000..144830b
Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/sales-post-assertion-and-response-sig/WEB-INF/keystore.jks differ
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
index 5a1d2c6..0e25d89 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/testsaml.json
@@ -68,6 +68,101 @@
     ],
     "clients": [
         {
+            "clientId": "http://localhost:8081/missing-assertion-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/missing-assertion-sig",
+            "redirectUris": [
+                "http://localhost:8080/missing-assertion-sig/*"
+            ],
+            "attributes": {
+                "saml_assertion_consumer_url_post": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/missing-assertion-sig/saml",
+                "saml.server.signature": "true",
+                "saml.assertion.signature": "false",
+                "saml.signature.algorithm": "RSA_SHA256",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/bad-assertion-sales-post-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/bad-assertion-sales-post-sig/",
+            "adminUrl": "http://localhost:8080/bad-assertion-sales-post-sig/saml",
+            "redirectUris": [
+                "http://localhost:8080/bad-assertion-sales-post-sig/*"
+            ],
+            "attributes": {
+                "saml.assertion.signature": "true",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/input-portal/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "protocol": "saml",
+            "baseUrl": "http://localhost:8080/input-portal",
+            "redirectUris": [
+                "http://localhost:8080/input-portal/*"
+            ],
+            "attributes": {
+                "saml.authnstatement": "true",
+                "saml_assertion_consumer_url_post": "http://localhost:8080/input-portal/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/input-portal/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/input-portal/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/input-portal/saml"
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/sales-post-assertion-and-response-sig/",
+            "enabled": true,
+            "protocol": "saml",
+            "fullScopeAllowed": true,
+            "baseUrl": "http://localhost:8080/sales-post-assertion-and-response-sig",
+            "redirectUris": [
+                "http://localhost:8080/sales-post-assertion-and-response-sig/*"
+            ],
+            "attributes": {
+                "saml_assertion_consumer_url_post": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_assertion_consumer_url_redirect": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml_single_logout_service_url_redirect": "http://localhost:8080/sales-post-assertion-and-response-sig/saml",
+                "saml.server.signature": "true",
+                "saml.assertion.signature": "true",
+                "saml.signature.algorithm": "RSA_SHA256",
+                "saml.client.signature": "true",
+                "saml.authnstatement": "true",
+                "saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
+            }
+        },
+        {
+            "clientId": "http://localhost:8081/sales-post2/",
+            "enabled": true,
+            "fullScopeAllowed": true,
+            "protocol": "saml",
+            "baseUrl": "http://localhost:8080/sales-post2",
+            "redirectUris": [
+                "http://localhost:8080/sales-post2/*"
+            ],
+            "attributes": {
+                "saml.authnstatement": "true",
+                "saml_assertion_consumer_url_post": "http://localhost:8080/sales-post2/saml",
+                "saml_single_logout_service_url_post": "http://localhost:8080/sales-post2/saml",
+                "saml_idp_initiated_sso_url_name": "sales-post2",
+                "saml_idp_initiated_sso_relay_state": "redirectTo=/foo"
+            }
+        },
+        {
             "clientId": "http://localhost:8081/sales-post/",
             "enabled": true,
             "fullScopeAllowed": true,
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
index 4207f91..aa2c4b7 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/web.xml
@@ -28,6 +28,10 @@
         <url-pattern>/*</url-pattern>
     </servlet-mapping>
 
+    <error-page>
+        <location>/error.html</location>
+    </error-page>
+
     <security-constraint>
         <web-resource-collection>
             <web-resource-name>Application</web-resource-name>
diff --git a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
index 627e5f3..1dcf846 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
+++ b/testsuite/integration-arquillian/tests/other/adapters/jboss/eap6/src/test/java/org/keycloak/testsuite/adapter/EAP6SAMLFilterAdapterTest.java
@@ -8,5 +8,5 @@ import org.keycloak.testsuite.arquillian.annotation.UseServletFilter;
  * @author mhajas
  */
 @AppServerContainer("app-server-eap6")
-public class EAPSAMLFilterAdapterTest extends AbstractSAMLFilterServletAdapterTest {
+public class EAP6SAMLFilterAdapterTest extends AbstractSAMLFilterServletAdapterTest {
 }
diff --git a/testsuite/integration-arquillian/tests/other/adapters/pom.xml b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
index 9c49390..15fc1c4 100644
--- a/testsuite/integration-arquillian/tests/other/adapters/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/adapters/pom.xml
@@ -172,6 +172,18 @@
                             </systemPropertyVariables>
                         </configuration>
                     </plugin>
+                    <plugin>
+                        <groupId>org.jboss.shrinkwrap.resolver</groupId>
+                        <artifactId>shrinkwrap-resolver-maven-plugin</artifactId>
+                        <version>${version.shrinkwrap.resolvers}</version>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>propagate-execution-context</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
                 </plugins>
             </build>
         </profile>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 6c25059..de3d65f 100755
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -780,6 +780,10 @@
                     <groupId>org.keycloak</groupId>
                     <artifactId>keycloak-adapter-spi</artifactId>
                 </dependency>
+                <dependency>
+                    <groupId>org.keycloak</groupId>
+                    <artifactId>keycloak-saml-adapter-api-public</artifactId>
+                </dependency>
 
                 <!--UNDERTOW-->