keycloak-aplcache
Changes
testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java 2(+2 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java 23(+14 -9)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java 76(+74 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java 32(+0 -32)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java 9(+9 -0)
Details
diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
index 5d29edb..d02ed75 100755
--- a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
+++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/SendUsernameServlet.java
@@ -85,6 +85,7 @@ public class SendUsernameServlet {
@Path("getAttributes")
public Response getSentPrincipal() throws IOException {
System.out.println("In SendUsername Servlet getSentPrincipal()");
+ sentPrincipal = httpServletRequest.getUserPrincipal();
return Response.ok(getAttributes()).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML_TYPE + ";charset=UTF-8").build();
@@ -190,6 +191,7 @@ public class SendUsernameServlet {
SamlPrincipal principal = (SamlPrincipal) sentPrincipal;
String output = "attribute email: " + principal.getAttribute(X500SAMLProfileConstants.EMAIL.get());
output += "<br /> topAttribute: " + principal.getAttribute("topAttribute");
+ output += "<br /> boolean-attribute: " + principal.getAttribute("boolean-attribute");
output += "<br /> level2Attribute: " + principal.getAttribute("level2Attribute");
output += "<br /> group: " + principal.getAttributes("group").toString();
output += "<br /> friendlyAttribute email: " + principal.getFriendlyAttribute("email");
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 9148ce1..5bdea6e 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
@@ -206,20 +206,25 @@ public class IOUtil {
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);
+ public static void appendChildInDocument(Document doc, String parentPath, Element node) {
+ String[] pathSegments = parentPath.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;
}
- Element parentElement = (Element) nodes.item(0);
- if (parentElement == null) {
- log.warn("Not able to find element: " + parentTag);
- return;
+ 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;
+ }
}
- parentElement.appendChild(node);
+ currentElement.appendChild(node);
}
public static void execCommand(String command, File dir) throws IOException, InterruptedException {
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 1d0b7f7..e728eaa 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
@@ -17,6 +17,13 @@
package org.keycloak.testsuite.adapter.servlet;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.shrinkwrap.api.spec.WebArchive;
@@ -28,10 +35,12 @@ import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RoleScopeResource;
import org.keycloak.common.util.KeyUtils;
import org.keycloak.common.util.PemUtils;
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
import org.keycloak.keys.Attributes;
import org.keycloak.keys.KeyProvider;
import org.keycloak.keys.ImportedRsaKeyProviderFactory;
import org.keycloak.protocol.saml.SamlConfigAttributes;
+import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
import org.keycloak.protocol.saml.mappers.RoleListMapper;
@@ -44,6 +53,9 @@ import org.keycloak.saml.BaseSAML2BindingBuilder;
import org.keycloak.saml.SAML2ErrorResponseBuilder;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
+import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
+import org.keycloak.services.resources.RealmsResource;
import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
import org.keycloak.testsuite.adapter.page.BadAssertionSalesPostSig;
import org.keycloak.testsuite.adapter.page.BadClientSalesPostSigServlet;
@@ -73,11 +85,12 @@ import org.keycloak.testsuite.auth.page.login.Login;
import org.keycloak.testsuite.auth.page.login.SAMLIDPInitiatedLogin;
import org.keycloak.testsuite.page.AbstractPage;
import org.keycloak.testsuite.util.IOUtil;
-import org.keycloak.testsuite.util.RealmBuilder;
+import org.keycloak.testsuite.util.SamlClient;
import org.keycloak.testsuite.util.UserBuilder;
import org.openqa.selenium.By;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import javax.ws.rs.client.Client;
@@ -87,6 +100,8 @@ import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriBuilderException;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
@@ -107,13 +122,15 @@ import java.util.stream.Collectors;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
-import static org.keycloak.testsuite.AbstractAuthTest.createUserRepresentation;
import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import static org.keycloak.testsuite.auth.page.AuthRealm.SAMLSERVLETDEMO;
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.IOUtil.loadXML;
import static org.keycloak.testsuite.util.IOUtil.modifyDocElementAttribute;
+import static org.keycloak.testsuite.util.Matchers.bodyHC;
+import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
+import static org.keycloak.testsuite.util.SamlClient.login;
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;
@@ -965,6 +982,61 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd
response.close();
}
+ @Test
+ //KEYCLOAK-4020
+ public void testBooleanAttribute() throws Exception {
+ AuthnRequestType req = SamlClient.createLoginRequestDocument("http://localhost:8081/employee2/", getAppServerSamlEndpoint(employee2ServletPage).toString(), getAuthServerSamlEndpoint(SAMLSERVLETDEMO));
+ Document doc = SAML2Request.convert(req);
+
+ SAMLDocumentHolder res = login(bburkeUser, getAuthServerSamlEndpoint(SAMLSERVLETDEMO), doc, null, SamlClient.Binding.POST, SamlClient.Binding.POST);
+ Document responseDoc = res.getSamlDocument();
+
+ Element attribute = responseDoc.createElement("saml:Attribute");
+ attribute.setAttribute("Name", "boolean-attribute");
+ attribute.setAttribute("NameFormat", "urn:oasis:names:tc:SAML:2.0:attrname-format:basic");
+
+ Element attributeValue = responseDoc.createElement("saml:AttributeValue");
+ attributeValue.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema");
+ attributeValue.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ attributeValue.setAttribute("xsi:type", "xs:boolean");
+ attributeValue.setTextContent("true");
+
+ attribute.appendChild(attributeValue);
+ IOUtil.appendChildInDocument(responseDoc, "samlp:Response/saml:Assertion/saml:AttributeStatement", attribute);
+
+ CloseableHttpResponse response = null;
+ try (CloseableHttpClient client = HttpClientBuilder.create().build()) {
+ HttpClientContext context = HttpClientContext.create();
+
+ HttpUriRequest post = SamlClient.Binding.POST.createSamlPostUnsignedRequest(getAppServerSamlEndpoint(employee2ServletPage), null, responseDoc);
+ response = client.execute(post, context);
+ assertThat(response, statusCodeIsHC(Response.Status.FOUND));
+ response.close();
+
+ HttpGet get = new HttpGet(employee2ServletPage.toString() + "/getAttributes");
+ response = client.execute(get);
+ assertThat(response, statusCodeIsHC(Response.Status.OK));
+ assertThat(response, bodyHC(containsString("boolean-attribute: true")));
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ if (response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ try { response.close(); } catch (IOException ex) { }
+ }
+ }
+ }
+
+ private URI getAuthServerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException {
+ return RealmsResource
+ .protocolUrl(UriBuilder.fromUri(getAuthServerRoot()))
+ .build(realm, SamlProtocol.LOGIN_PROTOCOL);
+ }
+
+ private URI getAppServerSamlEndpoint(SAMLServlet page) throws IllegalArgumentException, UriBuilderException {
+ return UriBuilder.fromPath(page.toString()).path("/saml").build();
+ }
+
private void validateXMLWithSchema(String xml, String schemaFileName) throws SAXException, IOException {
URL schemaFile = getClass().getResource(schemaFileName);
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
index e9b4ac6..3bf08c9 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AuthnRequestNameIdFormatTest.java
@@ -67,38 +67,6 @@ public class AuthnRequestNameIdFormatTest extends AbstractAuthTest {
private static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
private static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
- public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
- Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
- CloseableHttpResponse response = null;
- SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
- try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
- HttpClientContext context = HttpClientContext.create();
-
- HttpUriRequest post = requestBinding.createSamlRequest(samlEndpoint, relayState, samlRequest);
- response = client.execute(post, context);
-
- assertThat(response, statusCodeIsHC(Response.Status.OK));
- String loginPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
- response.close();
-
- assertThat(loginPageText, containsString("login"));
-
- HttpUriRequest loginRequest = handleLoginPage(user, loginPageText);
-
- strategy.setRedirectable(false);
- response = client.execute(loginRequest, context);
-
- return expectedResponseBinding.extractResponse(response);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- } finally {
- if (response != null) {
- EntityUtils.consumeQuietly(response.getEntity());
- try { response.close(); } catch (IOException ex) { }
- }
- }
- }
-
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
testRealms.add(loadRealm("/adapter-test/keycloak-saml/testsaml.json"));
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
index 7ff72a5..f88cfdf 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/Matchers.java
@@ -39,6 +39,15 @@ public class Matchers {
}
/**
+ * Matcher on HTTP body of a {@link Response} instance.
+ * @param matcher
+ * @return
+ */
+ public static Matcher<HttpResponse> bodyHC(Matcher<String> matcher) {
+ return new HttpResponseBodyMatcher(matcher);
+ }
+
+ /**
* Matcher on HTTP status code of a {@link Response} instance.
* @param matcher
* @return
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java
new file mode 100644
index 0000000..fa4731e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/matchers/HttpResponseBodyMatcher.java
@@ -0,0 +1,55 @@
+/*
+ * 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.util.matchers;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.util.EntityUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.io.IOException;
+
+/**
+ * Matcher for matching status code of {@link Response} instance.
+ * @author hmlnarik
+ */
+public class HttpResponseBodyMatcher extends BaseMatcher<HttpResponse> {
+
+ private final Matcher<String> matcher;
+
+ public HttpResponseBodyMatcher(Matcher<String> matcher) {
+ this.matcher = matcher;
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ try {
+ return (item instanceof HttpResponse) && this.matcher.matches(EntityUtils.toString(((HttpResponse) item).getEntity()));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("response body matches ").appendDescriptionOf(this.matcher);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
index 7af34f8..f4066f6 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/SamlClient.java
@@ -16,6 +16,9 @@
*/
package org.keycloak.testsuite.util;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.saml.BaseSAML2BindingBuilder;
@@ -107,6 +110,40 @@ public class SamlClient {
}
@Override
+ public HttpPost createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+ HttpPost post = new HttpPost(samlEndpoint);
+
+ List<NameValuePair> parameters = new LinkedList<>();
+
+ try {
+ parameters.add(
+ new BasicNameValuePair(GeneralConstants.SAML_RESPONSE_KEY,
+ new BaseSAML2BindingBuilder()
+ .postBinding(samlRequest)
+ .encoded())
+ );
+ } catch (IOException | ConfigurationException | ProcessingException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ if (relayState != null) {
+ parameters.add(new BasicNameValuePair(GeneralConstants.RELAY_STATE, relayState));
+ }
+
+ UrlEncodedFormEntity formEntity;
+
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+
+ post.setEntity(formEntity);
+
+ return post;
+ }
+
+ @Override
public URI getBindingUri() {
return URI.create(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
}
@@ -138,11 +175,17 @@ public class SamlClient {
public URI getBindingUri() {
return URI.create(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
}
+
+ @Override
+ public HttpUriRequest createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest) {
+ return null;
+ }
};
public abstract SAMLDocumentHolder extractResponse(CloseableHttpResponse response) throws IOException;
public abstract HttpUriRequest createSamlRequest(URI samlEndpoint, String relayState, Document samlRequest);
public abstract URI getBindingUri();
+ public abstract HttpUriRequest createSamlPostUnsignedRequest(URI samlEndpoint, String relayState, Document samlRequest);
}
public static class RedirectStrategyWithSwitchableFollowRedirect extends LaxRedirectStrategy {
@@ -263,4 +306,46 @@ public class SamlClient {
}
}
+ /**
+ * Send request for login form and then login using user param
+ * @param user
+ * @param samlEndpoint
+ * @param samlRequest
+ * @param relayState
+ * @param requestBinding
+ * @param expectedResponseBinding
+ * @return
+ */
+ public static SAMLDocumentHolder login(UserRepresentation user, URI samlEndpoint,
+ Document samlRequest, String relayState, Binding requestBinding, Binding expectedResponseBinding) {
+ CloseableHttpResponse response = null;
+ SamlClient.RedirectStrategyWithSwitchableFollowRedirect strategy = new SamlClient.RedirectStrategyWithSwitchableFollowRedirect();
+ try (CloseableHttpClient client = HttpClientBuilder.create().setRedirectStrategy(strategy).build()) {
+ HttpClientContext context = HttpClientContext.create();
+
+ HttpUriRequest post = requestBinding.createSamlRequest(samlEndpoint, relayState, samlRequest);
+ response = client.execute(post, context);
+
+ assertThat(response, statusCodeIsHC(Response.Status.OK));
+ String loginPageText = EntityUtils.toString(response.getEntity(), "UTF-8");
+ response.close();
+
+ assertThat(loginPageText, containsString("login"));
+
+ HttpUriRequest loginRequest = handleLoginPage(user, loginPageText);
+
+ strategy.setRedirectable(false);
+ response = client.execute(loginRequest, context);
+
+ return expectedResponseBinding.extractResponse(response);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ } finally {
+ if (response != null) {
+ EntityUtils.consumeQuietly(response.getEntity());
+ try { response.close(); } catch (IOException ex) { }
+ }
+ }
+ }
+
}