keycloak-aplcache
Changes
saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/LoginBuilder.java 34(+32 -2)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java 5(+4 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java 146(+146 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClientBuilder.java 18(+18 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java 15(+14 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java 94(+94 -0)
Details
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java
index d2a59b9..a3b2608 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java
@@ -101,7 +101,7 @@ public class SAMLResponseWriter extends BaseWriter {
for (ResponseType.RTChoiceType choiceType : choiceTypes) {
AssertionType assertion = choiceType.getAssertion();
if (assertion != null) {
- assertionWriter.write(assertion);
+ assertionWriter.write(assertion, forceWriteDsigNamespace);
}
EncryptedAssertionType encryptedAssertion = choiceType.getEncryptedAssertion();
diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
index 7093a20..85ddf2c 100755
--- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
+++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java
@@ -367,7 +367,9 @@ public class XMLSignatureUtil {
public static Document sign(Document doc, String keyName, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI,
X509Certificate x509Certificate, String canonicalizationMethodType)
throws GeneralSecurityException, MarshalException, XMLSignatureException {
- logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
+ if (logger.isTraceEnabled()) {
+ logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
+ }
PrivateKey signingKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
@@ -398,7 +400,9 @@ public class XMLSignatureUtil {
String referenceURI = dto.getReferenceURI();
String signatureMethod = dto.getSignatureMethod();
- logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
+ if (logger.isTraceEnabled()) {
+ logger.trace("Document to be signed=" + DocumentUtil.asString(doc));
+ }
PrivateKey signingKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
@@ -407,6 +411,10 @@ public class XMLSignatureUtil {
signImpl(dsc, digestMethod, signatureMethod, referenceURI, keyName, publicKey, dto.getX509Certificate(), canonicalizationMethodType);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Signed document=" + DocumentUtil.asString(doc));
+ }
+
return doc;
}
diff --git a/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java b/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java
index c8647f3..5f7e0e4 100755
--- a/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java
+++ b/services/src/test/java/org/keycloak/test/broker/saml/SAMLDataMarshallerTest.java
@@ -129,11 +129,16 @@ public class SAMLDataMarshallerTest {
ResponseType response = (ResponseType) parsedObject;
SAMLDataMarshaller serializer = new SAMLDataMarshaller();
- String serialized = serializer.serialize(response.getAssertions().get(0).getAssertion());
+ String serializedResponse = serializer.serialize(response);
+ String serializedAssertion = serializer.serialize(response.getAssertions().get(0).getAssertion());
- AssertionType deserialized = serializer.deserialize(serialized, AssertionType.class);
- assertThat(deserialized, CoreMatchers.notNullValue());
- assertThat(deserialized.getID(), CoreMatchers.is("id-4r-Xj702KQsM0gJyu3Fqpuwfe-LvDrEcQZpxKrhC"));
+ ResponseType deserializedResponse = serializer.deserialize(serializedResponse, ResponseType.class);
+ assertThat(deserializedResponse, CoreMatchers.notNullValue());
+ assertThat(deserializedResponse.getID(), CoreMatchers.is("id-EYgqtumZ-P-Ph7t37f-brUKMwB5MKix0sNjr-0YV"));
+
+ AssertionType deserializedAssertion = serializer.deserialize(serializedAssertion, AssertionType.class);
+ assertThat(deserializedAssertion, CoreMatchers.notNullValue());
+ assertThat(deserializedAssertion.getID(), CoreMatchers.is("id-4r-Xj702KQsM0gJyu3Fqpuwfe-LvDrEcQZpxKrhC"));
}
}
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/LoginBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/LoginBuilder.java
index 4e5713b..5e59df4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/LoginBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/LoginBuilder.java
@@ -18,6 +18,7 @@ package org.keycloak.testsuite.util.saml;
import org.keycloak.testsuite.util.SamlClientBuilder;
import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.admin.Users;
import org.keycloak.testsuite.util.SamlClient.Step;
import java.io.UnsupportedEncodingException;
import java.net.URI;
@@ -36,6 +37,7 @@ import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
+import org.hamcrest.Matchers;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import static org.hamcrest.Matchers.containsString;
@@ -52,6 +54,7 @@ public class LoginBuilder implements Step {
private final SamlClientBuilder clientBuilder;
private UserRepresentation user;
private boolean sso = false;
+ private String idpAlias;
public LoginBuilder(SamlClientBuilder clientBuilder) {
this.clientBuilder = clientBuilder;
@@ -66,7 +69,7 @@ public class LoginBuilder implements Step {
String loginPageText = EntityUtils.toString(currentResponse.getEntity(), "UTF-8");
assertThat(loginPageText, containsString("login"));
- return handleLoginPage(loginPageText);
+ return handleLoginPage(loginPageText, currentURI);
}
}
@@ -79,12 +82,30 @@ public class LoginBuilder implements Step {
return this;
}
+ public LoginBuilder user(String userName, String password) {
+ this.user = new UserRepresentation();
+ this.user.setUsername(userName);
+ Users.setPasswordFor(user, password);
+ return this;
+ }
+
public LoginBuilder sso(boolean sso) {
this.sso = sso;
return this;
}
/**
+ * When the step is executed and {@code idpAlias} is not {@code null}, it attempts to find and follow the link to
+ * identity provider with the given alias.
+ * @param idpAlias
+ * @return
+ */
+ public LoginBuilder idp(String idpAlias) {
+ this.idpAlias = idpAlias;
+ return this;
+ }
+
+ /**
* Prepares a GET/POST request for logging the given user into the given login page. The login page is expected
* to have at least input fields with id "username" and "password".
*
@@ -92,7 +113,16 @@ public class LoginBuilder implements Step {
* @param loginPage
* @return
*/
- private HttpUriRequest handleLoginPage(String loginPage) {
+ private HttpUriRequest handleLoginPage(String loginPage, URI currentURI) {
+ if (idpAlias != null) {
+ org.jsoup.nodes.Document theLoginPage = Jsoup.parse(loginPage);
+ Element zocialLink = theLoginPage.getElementById("zocial-" + this.idpAlias);
+ assertThat("Unknown idp: " + this.idpAlias, zocialLink, Matchers.notNullValue());
+ final String link = zocialLink.attr("href");
+ assertThat("Invalid idp link: " + this.idpAlias, link, Matchers.notNullValue());
+ return new HttpGet(currentURI.resolve(link));
+ }
+
return handleLoginPage(user, loginPage);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java
index e29091b..5c80786 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/ModifySamlResponseStepBuilder.java
@@ -43,6 +43,7 @@ import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
+import org.hamcrest.Matchers;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
@@ -154,7 +155,9 @@ public class ModifySamlResponseStepBuilder extends SamlDocumentStepBuilder<SAML2
private HttpUriRequest handlePostBinding(CloseableHttpResponse currentResponse) throws Exception {
assertThat(currentResponse, statusCodeIsHC(Status.OK));
- org.jsoup.nodes.Document theResponsePage = Jsoup.parse(EntityUtils.toString(currentResponse.getEntity()));
+ final String htmlBody = EntityUtils.toString(currentResponse.getEntity());
+ assertThat(htmlBody, Matchers.containsString("SAML"));
+ org.jsoup.nodes.Document theResponsePage = Jsoup.parse(htmlBody);
Elements samlResponses = theResponsePage.select("input[name=SAMLResponse]");
Elements samlRequests = theResponsePage.select("input[name=SAMLRequest]");
Elements forms = theResponsePage.select("form");
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java
new file mode 100644
index 0000000..e928c9c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/saml/UpdateProfileBuilder.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2017 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.testsuite.util.saml;
+
+import org.keycloak.testsuite.util.SamlClientBuilder;
+import org.keycloak.testsuite.util.SamlClient.Step;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+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.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.jboss.logging.Logger;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Element;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.keycloak.testsuite.util.Matchers.statusCodeIsHC;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class UpdateProfileBuilder implements Step {
+
+ private final SamlClientBuilder clientBuilder;
+ private final Map<String, String> parameters = new HashMap<>();
+
+ private static final Logger LOG = Logger.getLogger(UpdateProfileBuilder.class);
+
+ public UpdateProfileBuilder(SamlClientBuilder clientBuilder) {
+ this.clientBuilder = clientBuilder;
+ }
+
+ @Override
+ public HttpUriRequest perform(CloseableHttpClient client, URI currentURI, CloseableHttpResponse currentResponse, HttpClientContext context) throws Exception {
+ assertThat(currentResponse, statusCodeIsHC(Response.Status.OK));
+ String loginPageText = EntityUtils.toString(currentResponse.getEntity(), "UTF-8");
+ assertThat(loginPageText, containsString("Update Account Information"));
+
+ return handleUpdateProfile(loginPageText, currentURI);
+ }
+
+ public SamlClientBuilder build() {
+ return this.clientBuilder;
+ }
+
+ public UpdateProfileBuilder param(String paramName, String paramValue) {
+ if (paramValue != null) {
+ this.parameters.put(paramName, paramValue);
+ } else {
+ this.parameters.remove(paramName);
+ }
+ return this;
+ }
+
+ public UpdateProfileBuilder firstName(String firstName) {
+ return param("firstName", firstName);
+ }
+
+ public UpdateProfileBuilder lastName(String lastName) {
+ return param("lastName", lastName);
+ }
+
+ public UpdateProfileBuilder username(String username) {
+ return param("username", username);
+ }
+
+ public UpdateProfileBuilder email(String email) {
+ return param("email", email);
+ }
+
+ public HttpUriRequest handleUpdateProfile(String loginPage, URI currentURI) {
+ org.jsoup.nodes.Document theUpdateProfilePage = Jsoup.parse(loginPage);
+ Set<String> unusedParams = new HashSet<>(this.parameters.keySet());
+
+ List<NameValuePair> parameters = new LinkedList<>();
+ for (Element form : theUpdateProfilePage.getElementsByTag("form")) {
+ String method = form.attr("method");
+ String action = form.attr("action");
+ boolean isPost = method != null && "post".equalsIgnoreCase(method);
+
+ for (Element input : form.getElementsByTag("input")) {
+ if (this.parameters.containsKey(input.attr("name"))) {
+ parameters.add(new BasicNameValuePair(input.attr("name"), this.parameters.get(input.attr("name"))));
+ unusedParams.remove(input.attr("name"));
+ }
+ }
+
+ if (! unusedParams.isEmpty()) {
+ LOG.warnf("Unused parameter names at Update Profile page: %s", unusedParams);
+ }
+
+ if (isPost) {
+ HttpPost res = new HttpPost(action);
+
+ UrlEncodedFormEntity formEntity;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ res.setEntity(formEntity);
+
+ return res;
+ } else {
+ UriBuilder b = UriBuilder.fromPath(action);
+ for (NameValuePair parameter : parameters) {
+ b.queryParam(parameter.getName(), parameter.getValue());
+ }
+ return new HttpGet(b.build());
+ }
+ }
+
+ throw new IllegalArgumentException("Invalid update profile form: " + loginPage);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClientBuilder.java
index 3879447..41b0fdf 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SamlClientBuilder.java
@@ -31,13 +31,16 @@ import org.keycloak.testsuite.util.saml.CreateAuthnRequestStepBuilder;
import org.keycloak.testsuite.util.saml.CreateLogoutRequestStepBuilder;
import org.keycloak.testsuite.util.saml.IdPInitiatedLoginBuilder;
import org.keycloak.testsuite.util.saml.LoginBuilder;
+import org.keycloak.testsuite.util.saml.UpdateProfileBuilder;
import org.keycloak.testsuite.util.saml.ModifySamlResponseStepBuilder;
import org.keycloak.testsuite.util.saml.RequiredConsentBuilder;
+import javax.ws.rs.core.Response.Status;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.w3c.dom.Document;
+import static org.hamcrest.Matchers.notNullValue;
/**
*
@@ -157,6 +160,11 @@ public class SamlClientBuilder {
return addStepBuilder(new LoginBuilder(this));
}
+ /** Handles update profile page after login */
+ public UpdateProfileBuilder updateProfile() {
+ return addStepBuilder(new UpdateProfileBuilder(this));
+ }
+
/** Starts IdP-initiated flow for the given client */
public IdPInitiatedLoginBuilder idpInitiatedLogin(URI authServerSamlUrl, String clientId) {
return addStepBuilder(new IdPInitiatedLoginBuilder(authServerSamlUrl, clientId, this));
@@ -191,4 +199,14 @@ public class SamlClientBuilder {
return this;
}
+ public SamlClientBuilder followOneRedirect() {
+ return
+ doNotFollowRedirects()
+ .addStep((client, currentURI, currentResponse, context) -> {
+ Assert.assertThat(currentResponse, Matchers.statusCodeIsHC(Status.FOUND));
+ Assert.assertThat("Location header not found", currentResponse.getFirstHeader("Location"), notNullValue());
+ return new HttpGet(currentResponse.getFirstHeader("Location").getValue());
+ });
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
index 3f2d1d7..e544c4b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
@@ -17,6 +17,8 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.SuiteContext;
+import org.keycloak.testsuite.saml.AbstractSamlTest;
+import org.keycloak.testsuite.util.ClientBuilder;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -122,7 +124,18 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
@Override
public List<ClientRepresentation> createConsumerClients(SuiteContext suiteContext) {
- return null;
+ return Arrays.asList(
+ ClientBuilder.create()
+ .clientId(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST)
+ .enabled(true)
+ .fullScopeEnabled(true)
+ .protocol(SamlProtocol.LOGIN_PROTOCOL)
+ .baseUrl("http://localhost:8080/sales-post")
+ .addRedirectUri("http://localhost:8080/sales-post/*")
+ .attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE)
+ .attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE)
+ .build()
+ );
}
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java
index b5ded3e..739224c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerTest.java
@@ -4,7 +4,12 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.keycloak.broker.saml.mappers.AttributeToRoleMapper;
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
+import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
+import org.keycloak.services.resources.RealmsResource;
+import java.net.URI;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriBuilderException;
public class KcSamlBrokerTest extends AbstractBrokerTest {
@@ -35,4 +40,10 @@ public class KcSamlBrokerTest extends AbstractBrokerTest {
return Lists.newArrayList(attrMapper1, attrMapper2);
}
+
+ protected URI getAuthServerSamlEndpoint(String realm) throws IllegalArgumentException, UriBuilderException {
+ return RealmsResource
+ .protocolUrl(UriBuilder.fromUri(getAuthServerRoot()))
+ .build(realm, SamlProtocol.LOGIN_PROTOCOL);
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
index ef23a9a..f53e90f 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlSignedBrokerTest.java
@@ -2,24 +2,42 @@ package org.keycloak.testsuite.broker;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
+import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
import org.keycloak.protocol.saml.SamlConfigAttributes;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
+import org.keycloak.saml.common.util.DocumentUtil;
+import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
+import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.testsuite.arquillian.SuiteContext;
+import org.keycloak.testsuite.saml.AbstractSamlTest;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
+import org.keycloak.testsuite.util.SamlClient;
+import org.keycloak.testsuite.util.SamlClient.Binding;
+import org.keycloak.testsuite.util.SamlClientBuilder;
import java.io.Closeable;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
+import static org.keycloak.testsuite.util.Matchers.isSamlResponse;
public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
@@ -138,4 +156,80 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
errorPage.assertCurrent();
}
}
+
+ private Document extractNamespacesToTopLevelElement(Document original) {
+ HashMap<String, String> namespaces = new HashMap<>();
+ enumerateAndRemoveNamespaces(original.getDocumentElement(), namespaces);
+
+ log.infof("Namespaces: %s", namespaces);
+ log.infof("Document: %s", DocumentUtil.asString(original));
+
+ Element rootNode = original.getDocumentElement();
+ for (Entry<String, String> me : namespaces.entrySet()) {
+ rootNode.setAttribute(me.getKey(), me.getValue());
+ }
+
+ log.infof("Updated document: %s", DocumentUtil.asString(original));
+
+ return original;
+ }
+
+ private void enumerateAndRemoveNamespaces(Element documentElement, HashMap<String, String> namespaces) {
+ final NamedNodeMap attrs = documentElement.getAttributes();
+ if (attrs != null) {
+ final Set<String> found = new HashSet<>();
+
+ for (int i = attrs.getLength() - 1; i >= 0; i--) {
+ Node item = attrs.item(i);
+ String nodeName = item.getNodeName();
+ if (nodeName != null && nodeName.startsWith("xmlns:")) {
+ namespaces.put(nodeName, item.getNodeValue());
+ found.add(nodeName);
+ }
+ }
+
+ found.forEach(documentElement::removeAttribute);
+ }
+
+ NodeList childNodes = documentElement.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i ++) {
+ Node childNode = childNodes.item(i);
+ if (childNode instanceof Element) {
+ enumerateAndRemoveNamespaces((Element) childNode, namespaces);
+ }
+ }
+ }
+
+ // KEYCLOAK-5581
+ @Test
+ public void loginUserAllNamespacesInTopElement() throws Exception {
+ AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST, AbstractSamlTest.SAML_ASSERTION_CONSUMER_URL_SALES_POST, null);
+
+ Document doc = extractNamespacesToTopLevelElement(SAML2Request.convert(loginRep));
+
+ SAMLDocumentHolder samlResponse = new SamlClientBuilder()
+ .authnRequest(getAuthServerSamlEndpoint(bc.consumerRealmName()), doc, Binding.POST).build() // Request to consumer IdP
+ .login().idp(bc.getIDPAlias()).build()
+
+ .processSamlResponse(Binding.POST) // AuthnRequest to producer IdP
+ .targetAttributeSamlRequest()
+ .transformDocument(this::extractNamespacesToTopLevelElement)
+ .build()
+
+ .login().user(bc.getUserLogin(), bc.getUserPassword()).build()
+
+ .processSamlResponse(Binding.POST) // Response from producer IdP
+ .transformDocument(this::extractNamespacesToTopLevelElement)
+ .build()
+
+ // first-broker flow
+ .updateProfile().firstName("a").lastName("b").email(bc.getUserEmail()).username(bc.getUserLogin()).build()
+ .followOneRedirect()
+
+ .getSamlResponse(Binding.POST); // Response from consumer IdP
+
+ Assert.assertThat(samlResponse, Matchers.notNullValue());
+ Assert.assertThat(samlResponse.getSamlObject(), isSamlResponse(JBossSAMLURIConstants.STATUS_SUCCESS));
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
index dec091b..fbc1e15 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/saml/AbstractSamlTest.java
@@ -19,26 +19,26 @@ import static org.keycloak.testsuite.util.IOUtil.loadRealm;
*/
public abstract class AbstractSamlTest extends AbstractAuthTest {
- protected static final String REALM_NAME = "demo";
- protected static final String REALM_PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
- protected static final String REALM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
-
- protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
- protected static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
-
- protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST2 = "http://localhost:8080/sales-post2/";
- protected static final String SAML_CLIENT_ID_SALES_POST2 = "http://localhost:8081/sales-post2/";
-
- protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_SIG = "http://localhost:8080/sales-post-sig/";
- protected static final String SAML_CLIENT_ID_SALES_POST_SIG = "http://localhost:8081/sales-post-sig/";
- protected static final String SAML_URL_SALES_POST_SIG = "http://localhost:8080/sales-post-sig/";
- protected static final String SAML_CLIENT_SALES_POST_SIG_PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANUbxrvEY3pkiQNt55zJLKBwN+zKmNQw08ThAmOKzwHfXoK+xlDSFxNMtTKJGkeUdnKzaTfESEcEfKYULUA41y/NnOlvjS0CEsc7Wq0Ce63TSSGMB2NHea4tV0aQz/MwLsbmz2IjAFWHA5CHL5WwacIf3UTOSNnhJUSvnkomjJAlAgMBAAECgYANpO2gb/5+g5lSIuNFYov86bJq8r2+ODIW1OE2Rljioc6HSHeiDRF1JuAjECwikRrUVTBTZbnK8jqY14neJsWAKBzGo+ToaQALsNZ9B91DxxL50K5oVOzw5shAS9TnRjN40+KIXFED4ydq4JRdoqb8+cN+N3i0+Cu7tdm+UaHTAQJBAOwFs3ZwqQEqmv9vmgmIFwFpJm1aIw25gEOf3Hy45GP4bL/j0FQgwcXYRbLE5bPqhw/liLKc1GQ97bVm6zs8SvUCQQDnJZA6TFRMiDjezinE1J4e0v4RupyDniVjbE5ArTK5/FRVkjw4Ny0AqZUEyIIqlTeZlCq45pCJy4a2hymDGVJxAj9gzfXNnmezEsZ//kYvoqHM8lPQhifaeTsigW7tuOf0GPCBw+6uksDnZM0xhZCxOoArBPoMSEbU1pGo1Y2lvhUCQF6E5sBgHAybm53Ich4Rz4LNRqWbSIstrR5F2I3sBRU2kInZXZSjQ1zE+7HUCB4/nFfJ1dp8NdiTCEg1Zw072pECQQDnxyQALmWhQbBTl0tq6CwYf9rZDwBzxuY+CXB8Ky1gOmXwan96KZvV4rK8MQQs6HIiYC/j+5lX3A3zlXTFldaz";
- protected static final String SAML_CLIENT_SALES_POST_SIG_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB";
-
- protected static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_ENC = "http://localhost:8080/sales-post-enc/";
- protected static final String SAML_CLIENT_ID_SALES_POST_ENC = "http://localhost:8081/sales-post-enc/";
- protected static final String SAML_CLIENT_SALES_POST_ENC_PRIVATE_KEY = "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t";
- protected static final String SAML_CLIENT_SALES_POST_ENC_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB";
+ public static final String REALM_NAME = "demo";
+ public static final String REALM_PRIVATE_KEY = "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=";
+ public static final String REALM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
+
+ public static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST = "http://localhost:8080/sales-post/";
+ public static final String SAML_CLIENT_ID_SALES_POST = "http://localhost:8081/sales-post/";
+
+ public static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST2 = "http://localhost:8080/sales-post2/";
+ public static final String SAML_CLIENT_ID_SALES_POST2 = "http://localhost:8081/sales-post2/";
+
+ public static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_SIG = "http://localhost:8080/sales-post-sig/";
+ public static final String SAML_CLIENT_ID_SALES_POST_SIG = "http://localhost:8081/sales-post-sig/";
+ public static final String SAML_URL_SALES_POST_SIG = "http://localhost:8080/sales-post-sig/";
+ public static final String SAML_CLIENT_SALES_POST_SIG_PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANUbxrvEY3pkiQNt55zJLKBwN+zKmNQw08ThAmOKzwHfXoK+xlDSFxNMtTKJGkeUdnKzaTfESEcEfKYULUA41y/NnOlvjS0CEsc7Wq0Ce63TSSGMB2NHea4tV0aQz/MwLsbmz2IjAFWHA5CHL5WwacIf3UTOSNnhJUSvnkomjJAlAgMBAAECgYANpO2gb/5+g5lSIuNFYov86bJq8r2+ODIW1OE2Rljioc6HSHeiDRF1JuAjECwikRrUVTBTZbnK8jqY14neJsWAKBzGo+ToaQALsNZ9B91DxxL50K5oVOzw5shAS9TnRjN40+KIXFED4ydq4JRdoqb8+cN+N3i0+Cu7tdm+UaHTAQJBAOwFs3ZwqQEqmv9vmgmIFwFpJm1aIw25gEOf3Hy45GP4bL/j0FQgwcXYRbLE5bPqhw/liLKc1GQ97bVm6zs8SvUCQQDnJZA6TFRMiDjezinE1J4e0v4RupyDniVjbE5ArTK5/FRVkjw4Ny0AqZUEyIIqlTeZlCq45pCJy4a2hymDGVJxAj9gzfXNnmezEsZ//kYvoqHM8lPQhifaeTsigW7tuOf0GPCBw+6uksDnZM0xhZCxOoArBPoMSEbU1pGo1Y2lvhUCQF6E5sBgHAybm53Ich4Rz4LNRqWbSIstrR5F2I3sBRU2kInZXZSjQ1zE+7HUCB4/nFfJ1dp8NdiTCEg1Zw072pECQQDnxyQALmWhQbBTl0tq6CwYf9rZDwBzxuY+CXB8Ky1gOmXwan96KZvV4rK8MQQs6HIiYC/j+5lX3A3zlXTFldaz";
+ public static final String SAML_CLIENT_SALES_POST_SIG_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB";
+
+ public static final String SAML_ASSERTION_CONSUMER_URL_SALES_POST_ENC = "http://localhost:8080/sales-post-enc/";
+ public static final String SAML_CLIENT_ID_SALES_POST_ENC = "http://localhost:8081/sales-post-enc/";
+ public static final String SAML_CLIENT_SALES_POST_ENC_PRIVATE_KEY = "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t";
+ public static final String SAML_CLIENT_SALES_POST_ENC_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB";
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
index 842e406..870d059 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/ClientBuilder.java
@@ -176,6 +176,16 @@ public class ClientBuilder {
return this;
}
+ public ClientBuilder protocol(String protocol) {
+ rep.setProtocol(protocol);
+ return this;
+ }
+
+ public ClientBuilder enabled(Boolean enabled) {
+ rep.setEnabled(enabled);
+ return this;
+ }
+
public ClientBuilder authorizationServicesEnabled(boolean enable) {
rep.setAuthorizationServicesEnabled(enable);
return this;