Migrate SAML adapter tests

This commit is contained in:
mhajas 2016-08-02 13:14:47 +02:00
parent fd983690e2
commit 285a99d903
34 changed files with 1479 additions and 182 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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";

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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"));

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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";
Element filter = webXmlDoc.createElement("filter");
Element filterName = webXmlDoc.createElement("filter-name");
Element filterClass = webXmlDoc.createElement("filter-class");
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");
Element urlPattern = webXmlDoc.createElement("url-pattern");
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()) {
filter += "<dispatcher>" + testClass.getAnnotation(UseServletFilter.class).dispatcherType() + "</dispatcher>\n";
Element dispatcher = webXmlDoc.createElement("dispatcher");
dispatcher.setTextContent(testClass.getAnnotation(UseServletFilter.class).dispatcherType());
filterMapping.appendChild(dispatcher);
}
filter += "</filter-mapping>\n";
webXmlContent = webXmlContent.replace("</module-name>", "</module-name> " + filter);
//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());
appendChildInDocument(webXmlDoc, "web-app", filterMapping);
//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();
webXmlContent = webXmlContent.substring(0, start) + webXmlContent.substring(end);
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);
}
}

View file

@ -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);
}
}

View file

@ -1,87 +0,0 @@
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.PackagingType;
import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependency;
import org.jboss.shrinkwrap.resolver.api.maven.coordinate.MavenDependencyExclusion;
import java.io.File;
import java.util.Collections;
import java.util.Set;
/**
* @author mhajas
*/
public class SAMLFilterDependency implements MavenDependency {
private static File[] files;
protected final Logger log = org.jboss.logging.Logger.getLogger(this.getClass());
@Override
public Set<MavenDependencyExclusion> getExclusions() {
return Collections.EMPTY_SET;
}
@Override
public ScopeType getScope() {
return ScopeType.COMPILE;
}
@Override
public boolean isOptional() {
return false;
}
@Override
public PackagingType getPackaging() {
return PackagingType.JAR;
}
@Override
public PackagingType getType() {
return PackagingType.JAR;
}
@Override
public String getClassifier() {
return null;
}
@Override
public String getVersion() {
return System.getProperty("project.version");
}
@Override
public String getGroupId() {
return "org.keycloak";
}
@Override
public String getArtifactId() {
return "keycloak-saml-servlet-filter-adapter";
}
@Override
public String toCanonicalForm() {
return getGroupId() + ":" + getArtifactId() + ":" + getVersion();
}
private void resolve() {
log.info("Resolving SAMLFilter dependencies");
files = Maven.configureResolver().addDependency(this)
.resolve().withTransitivity().asFile();
log.info("Resolving dependencies is finished with " + files.length + " files");
}
public File[] getDependencies() {
if (files == null) {
resolve();
}
return files;
}
}

View file

@ -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)) {

View file

@ -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()) {

View file

@ -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)

View file

@ -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() {
}
}

View file

@ -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;
@ -70,6 +85,9 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
@Page
protected SalesPostServlet salesPostServletPage;
@Page
private SalesPost2Servlet salesPost2ServletPage;
@Page
protected SalesPostEncServlet salesPostEncServletPage;
@ -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);
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
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();
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -67,6 +67,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,

View file

@ -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>

View file

@ -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 {
}

View file

@ -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>

View file

@ -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-->