You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by co...@apache.org on 2017/08/02 16:10:08 UTC
[1/3] syncope git commit: Adding SAML SSO Response tests
Repository: syncope
Updated Branches:
refs/heads/master bfce0046e -> acf98a478
Adding SAML SSO Response tests
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/acf98a47
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/acf98a47
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/acf98a47
Branch: refs/heads/master
Commit: acf98a478a15b6ec32d8e0c084236d5523f3118c
Parents: a11cd34
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Aug 2 16:42:59 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Aug 2 17:10:05 2017 +0100
----------------------------------------------------------------------
fit/core-reference/pom.xml | 2 +
.../syncope/fit/core/SAML2CallbackHandler.java | 121 ++++++++++
.../apache/syncope/fit/core/SAML2ITCase.java | 234 ++++++++++++++++++-
.../core/SAML2PResponseComponentBuilder.java | 142 +++++++++++
fit/core-reference/src/test/resources/fediz.xml | 47 ++++
.../src/test/resources/stsrealm_a.jks | Bin 0 -> 2061 bytes
6 files changed, 545 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 5762a33..0772ccb 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -363,6 +363,7 @@ under the License.
<filtering>true</filtering>
<excludes>
<exclude>keystore</exclude>
+ <exclude>**/*.jks</exclude>
</excludes>
</testResource>
<testResource>
@@ -370,6 +371,7 @@ under the License.
<filtering>false</filtering>
<includes>
<include>keystore</include>
+ <include>**/*.jks</include>
</includes>
</testResource>
<testResource>
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2CallbackHandler.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2CallbackHandler.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2CallbackHandler.java
new file mode 100644
index 0000000..f80d4b9
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2CallbackHandler.java
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.syncope.fit.core;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.bean.AuthenticationStatementBean;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.bean.SubjectBean;
+import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
+import org.apache.wss4j.common.saml.bean.Version;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+
+/**
+ * A Callback Handler implementation for a SAML 2 assertion.
+ */
+public class SAML2CallbackHandler implements CallbackHandler {
+ private String subjectName = "uid=joe,ou=people,ou=saml-demo,o=example.com";
+ private String subjectQualifier = "www.example.com";
+ private String issuer;
+ private ConditionsBean conditions;
+ private SubjectConfirmationDataBean subjectConfirmationData;
+ private String subjectConfirmationMethod = SAML2Constants.CONF_BEARER;
+
+ public void handle(Callback[] callbacks)
+ throws IOException, UnsupportedCallbackException {
+ for (int i = 0; i < callbacks.length; i++) {
+ if (callbacks[i] instanceof SAMLCallback) {
+ SAMLCallback callback = (SAMLCallback) callbacks[i];
+ callback.setSamlVersion(Version.SAML_20);
+ callback.setIssuer(issuer);
+ if (conditions != null) {
+ callback.setConditions(conditions);
+ }
+
+ SubjectBean subjectBean =
+ new SubjectBean(
+ subjectName, subjectQualifier, subjectConfirmationMethod
+ );
+ subjectBean.setSubjectConfirmationData(subjectConfirmationData);
+ callback.setSubject(subjectBean);
+ AuthenticationStatementBean authBean = new AuthenticationStatementBean();
+ authBean.setAuthenticationMethod("Password");
+ callback.setAuthenticationStatementData(Collections.singletonList(authBean));
+ } else {
+ throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+ }
+ }
+ }
+
+ public String getSubjectName() {
+ return subjectName;
+ }
+
+ public void setSubjectName(String subjectName) {
+ this.subjectName = subjectName;
+ }
+
+ public String getSubjectQualifier() {
+ return subjectQualifier;
+ }
+
+ public void setSubjectQualifier(String subjectQualifier) {
+ this.subjectQualifier = subjectQualifier;
+ }
+
+ public String getIssuer() {
+ return issuer;
+ }
+
+ public void setIssuer(String issuer) {
+ this.issuer = issuer;
+ }
+
+ public ConditionsBean getConditions() {
+ return conditions;
+ }
+
+ public void setConditions(ConditionsBean conditions) {
+ this.conditions = conditions;
+ }
+
+ public SubjectConfirmationDataBean getSubjectConfirmationData() {
+ return subjectConfirmationData;
+ }
+
+ public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData) {
+ this.subjectConfirmationData = subjectConfirmationData;
+ }
+
+ public String getSubjectConfirmationMethod() {
+ return subjectConfirmationMethod;
+ }
+
+ public void setSubjectConfirmationMethod(String subjectConfirmationMethod) {
+ this.subjectConfirmationMethod = subjectConfirmationMethod;
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
index fd34bbf..55838b8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2ITCase.java
@@ -28,27 +28,54 @@ import static org.junit.Assert.fail;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.util.Collections;
import java.util.Optional;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.apache.commons.codec.binary.Base64;
+import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.saml.sso.SAMLProtocolResponseValidator;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.to.ItemTO;
import org.apache.syncope.common.lib.to.SAML2IdPTO;
+import org.apache.syncope.common.lib.to.SAML2LoginResponseTO;
+import org.apache.syncope.common.lib.to.SAML2ReceivedResponseTO;
import org.apache.syncope.common.lib.to.SAML2RequestTO;
import org.apache.syncope.common.rest.api.service.SAML2SPService;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.SAML2SPDetector;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.Merlin;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.bean.AudienceRestrictionBean;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.bean.SubjectConfirmationDataBean;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+import org.apache.wss4j.common.util.DOM2Writer;
+import org.apache.wss4j.common.util.Loader;
+import org.apache.wss4j.dom.WSConstants;
+import org.apache.wss4j.dom.engine.WSSConfig;
+import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.core.Status;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
public class SAML2ITCase extends AbstractITCase {
@@ -59,6 +86,9 @@ public class SAML2ITCase extends AbstractITCase {
anonymous = new SyncopeClientFactoryBean().
setAddress(ADDRESS).
create(new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
+
+ WSSConfig.init();
+ OpenSAMLUtil.initSamlEngine();
}
@BeforeClass
@@ -75,6 +105,7 @@ public class SAML2ITCase extends AbstractITCase {
try {
saml2IdPService.importFromMetadata(SAML2ITCase.class.getResourceAsStream("/ssocircle.xml"));
saml2IdPService.importFromMetadata(SAML2ITCase.class.getResourceAsStream("/testshib-providers.xml"));
+ saml2IdPService.importFromMetadata(SAML2ITCase.class.getResourceAsStream("/fediz.xml"));
} catch (Exception e) {
LOG.error("Unexpected error while importing SAML 2.0 IdP metadata", e);
} finally {
@@ -83,7 +114,7 @@ public class SAML2ITCase extends AbstractITCase {
type(clientFactory.getContentType().getMediaType());
}
- assertEquals(2, saml2IdPService.list().size());
+ assertEquals(3, saml2IdPService.list().size());
}
@AfterClass
@@ -160,4 +191,205 @@ public class SAML2ITCase extends AbstractITCase {
assertEquals("EmailAddress", ssoCircle.getConnObjectKeyItem().getExtAttrName());
}
+ @Test
+ public void validateLoginResponse() throws Exception {
+ Assume.assumeTrue(SAML2SPDetector.isSAML2SPAvailable());
+
+ // Get a valid login request for the Fediz realm
+ SAML2SPService saml2Service = anonymous.getService(SAML2SPService.class);
+ SAML2RequestTO loginRequest =
+ saml2Service.createLoginRequest(ADDRESS, "urn:org:apache:cxf:fediz:idp:realm-A");
+ assertNotNull(loginRequest);
+
+ assertEquals("https://localhost:8443/fediz-idp/saml/up", loginRequest.getIdpServiceAddress());
+ assertNotNull(loginRequest.getContent());
+ assertTrue(Base64.isBase64(loginRequest.getContent()));
+ assertNotNull(loginRequest.getRelayState());
+
+ // Check a null relaystate
+ SAML2ReceivedResponseTO response = new SAML2ReceivedResponseTO();
+ try {
+ saml2Service.validateLoginResponse(response);
+ fail("Failure expected on no Relay State");
+ } catch (SyncopeClientException ex) {
+ assertTrue(ex.getMessage().contains("No Relay State was provided"));
+ }
+
+ // Check a null Response
+ response.setRelayState(loginRequest.getRelayState());
+ try {
+ saml2Service.validateLoginResponse(response);
+ fail("Failure expected on no SAML Response");
+ } catch (SyncopeClientException ex) {
+ assertTrue(ex.getMessage().contains("No SAML Response was provided"));
+ }
+
+ // Create a SAML Response using WSS4J
+ Document doc = DOMUtils.newDocument();
+ JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
+ String inResponseTo = relayState.getJwtClaims().getSubject();
+
+ org.opensaml.saml.saml2.core.Response samlResponse = createResponse(doc, inResponseTo);
+ Element responseElement = OpenSAMLUtil.toDom(samlResponse, doc);
+ String responseStr = DOM2Writer.nodeToString(responseElement);
+
+ // Validate the SAML Response
+ response.setSamlResponse(java.util.Base64.getEncoder().encodeToString(responseStr.getBytes()));
+ SAML2LoginResponseTO loginResponse = saml2Service.validateLoginResponse(response);
+ assertNotNull(loginResponse.getAccessToken());
+ assertEquals("puccini", loginResponse.getNameID());
+ }
+
+ @Test
+ @org.junit.Ignore
+ public void testUnsignedAssertionInLoginResponse() throws Exception {
+ Assume.assumeTrue(SAML2SPDetector.isSAML2SPAvailable());
+
+ // Get a valid login request for the Fediz realm
+ SAML2SPService saml2Service = anonymous.getService(SAML2SPService.class);
+ SAML2RequestTO loginRequest =
+ saml2Service.createLoginRequest(ADDRESS, "urn:org:apache:cxf:fediz:idp:realm-A");
+ assertNotNull(loginRequest);
+
+ SAML2ReceivedResponseTO response = new SAML2ReceivedResponseTO();
+ response.setRelayState(loginRequest.getRelayState());
+
+ // Create a SAML Response using WSS4J
+ Document doc = DOMUtils.newDocument();
+ JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
+ String inResponseTo = relayState.getJwtClaims().getSubject();
+
+ org.opensaml.saml.saml2.core.Response samlResponse =
+ createResponse(doc, inResponseTo, false, SAML2Constants.CONF_SENDER_VOUCHES);
+ Element responseElement = OpenSAMLUtil.toDom(samlResponse, doc);
+ String responseStr = DOM2Writer.nodeToString(responseElement);
+
+ // Validate the SAML Response
+ response.setSamlResponse(java.util.Base64.getEncoder().encodeToString(responseStr.getBytes()));
+ try {
+ saml2Service.validateLoginResponse(response);
+ fail("Failure expected on an unsigned Assertion");
+ } catch (SyncopeClientException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ @org.junit.Ignore
+ public void testLoginResponseWrappingAttack() throws Exception {
+ Assume.assumeTrue(SAML2SPDetector.isSAML2SPAvailable());
+
+ // Get a valid login request for the Fediz realm
+ SAML2SPService saml2Service = anonymous.getService(SAML2SPService.class);
+ SAML2RequestTO loginRequest =
+ saml2Service.createLoginRequest(ADDRESS, "urn:org:apache:cxf:fediz:idp:realm-A");
+ assertNotNull(loginRequest);
+
+ SAML2ReceivedResponseTO response = new SAML2ReceivedResponseTO();
+ response.setRelayState(loginRequest.getRelayState());
+
+ // Create a SAML Response using WSS4J
+ Document doc = DOMUtils.newDocument();
+ JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
+ String inResponseTo = relayState.getJwtClaims().getSubject();
+
+ org.opensaml.saml.saml2.core.Response samlResponse = createResponse(doc, inResponseTo);
+ Element responseElement = OpenSAMLUtil.toDom(samlResponse, doc);
+
+ doc.appendChild(responseElement);
+ assertNotNull(responseElement);
+
+ // Get Assertion Element
+ Element assertionElement =
+ (Element)responseElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Assertion").item(0);
+ assertNotNull(assertionElement);
+
+ // Clone it, strip the Signature, modify the Subject, change Subj Conf
+ Element clonedAssertion = (Element)assertionElement.cloneNode(true);
+ clonedAssertion.setAttributeNS(null, "ID", "_12345623562");
+ Element sigElement =
+ (Element)clonedAssertion.getElementsByTagNameNS(WSConstants.SIG_NS, "Signature").item(0);
+ clonedAssertion.removeChild(sigElement);
+
+ Element subjElement =
+ (Element)clonedAssertion.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "Subject").item(0);
+ Element subjNameIdElement =
+ (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "NameID").item(0);
+ subjNameIdElement.setTextContent("verdi");
+
+ Element subjConfElement =
+ (Element)subjElement.getElementsByTagNameNS(SAMLConstants.SAML20_NS, "SubjectConfirmation").item(0);
+ subjConfElement.setAttributeNS(null, "Method", SAML2Constants.CONF_SENDER_VOUCHES);
+
+ // Now insert the modified cloned Assertion into the Response after the other assertion
+ responseElement.insertBefore(clonedAssertion, null);
+
+ String responseStr = DOM2Writer.nodeToString(responseElement);
+
+ // Validate the SAML Response
+ response.setSamlResponse(java.util.Base64.getEncoder().encodeToString(responseStr.getBytes()));
+ SAML2LoginResponseTO loginResponse = saml2Service.validateLoginResponse(response);
+ assertNotNull(loginResponse.getAccessToken());
+ assertEquals("puccini", loginResponse.getNameID());
+ }
+
+ private org.opensaml.saml.saml2.core.Response createResponse(Document doc, String inResponseTo) throws Exception {
+ return createResponse(doc, inResponseTo, true, SAML2Constants.CONF_BEARER);
+ }
+
+ private org.opensaml.saml.saml2.core.Response createResponse(Document doc, String inResponseTo,
+ boolean signAssertion, String subjectConfMethod) throws Exception {
+ Status status =
+ SAML2PResponseComponentBuilder.createStatus(
+ SAMLProtocolResponseValidator.SAML2_STATUSCODE_SUCCESS, null
+ );
+ org.opensaml.saml.saml2.core.Response response =
+ SAML2PResponseComponentBuilder.createSAMLResponse(
+ inResponseTo, "urn:org:apache:cxf:fediz:idp:realm-A", status
+ );
+ response.setDestination("http://recipient.apache.org");
+
+ // Create an AuthenticationAssertion
+ SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+ callbackHandler.setIssuer("urn:org:apache:cxf:fediz:idp:realm-A");
+ callbackHandler.setSubjectName("puccini");
+ callbackHandler.setSubjectConfirmationMethod(subjectConfMethod);
+
+ SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+ subjectConfirmationData.setAddress("http://apache.org");
+ subjectConfirmationData.setInResponseTo("12345");
+ subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+ subjectConfirmationData.setRecipient("http://recipient.apache.org");
+ callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setNotBefore(new DateTime());
+ conditions.setNotAfter(new DateTime().plusMinutes(5));
+
+ AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+ audienceRestriction.setAudienceURIs(Collections.singletonList("http://service.apache.org"));
+ conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+ callbackHandler.setConditions(conditions);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ if (signAssertion) {
+ Crypto issuerCrypto = new Merlin();
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ ClassLoader loader = Loader.getClassLoader(SAML2ITCase.class);
+ InputStream input = Merlin.loadInputStream(loader, "stsrealm_a.jks");
+ keyStore.load(input, "storepass".toCharArray());
+ ((Merlin)issuerCrypto).setKeyStore(keyStore);
+
+ assertion.signAssertion("realma", "realma", issuerCrypto, false);
+ }
+
+ response.getAssertions().add(assertion.getSaml2());
+
+ return response;
+ }
+
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2PResponseComponentBuilder.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2PResponseComponentBuilder.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2PResponseComponentBuilder.java
new file mode 100644
index 0000000..75ad465
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SAML2PResponseComponentBuilder.java
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.syncope.fit.core;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.common.SAMLVersion;
+import org.opensaml.saml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml.saml2.core.Issuer;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.saml.saml2.core.StatusCode;
+import org.opensaml.saml.saml2.core.StatusMessage;
+
+/**
+* A (basic) set of utility methods to construct SAML 2.0 Protocol Response statements
+*/
+public final class SAML2PResponseComponentBuilder {
+
+ private static SAMLObjectBuilder<Response> responseBuilder;
+
+ private static SAMLObjectBuilder<Issuer> issuerBuilder;
+
+ private static SAMLObjectBuilder<Status> statusBuilder;
+
+ private static SAMLObjectBuilder<StatusCode> statusCodeBuilder;
+
+ private static SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
+
+ private static SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
+
+ private static XMLObjectBuilderFactory builderFactory =
+ XMLObjectProviderRegistrySupport.getBuilderFactory();
+
+ private SAML2PResponseComponentBuilder() {
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Response createSAMLResponse(
+ String inResponseTo,
+ String issuer,
+ Status status
+ ) {
+ if (responseBuilder == null) {
+ responseBuilder = (SAMLObjectBuilder<Response>)
+ builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
+ }
+ Response response = responseBuilder.buildObject();
+
+ response.setID(UUID.randomUUID().toString());
+ response.setIssueInstant(new DateTime());
+ response.setInResponseTo(inResponseTo);
+ response.setIssuer(createIssuer(issuer));
+ response.setStatus(status);
+ response.setVersion(SAMLVersion.VERSION_20);
+
+ return response;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Issuer createIssuer(
+ String issuerValue
+ ) {
+ if (issuerBuilder == null) {
+ issuerBuilder = (SAMLObjectBuilder<Issuer>)
+ builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
+ }
+ Issuer issuer = issuerBuilder.buildObject();
+ issuer.setValue(issuerValue);
+
+ return issuer;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Status createStatus(
+ String statusCodeValue,
+ String statusMessage
+ ) {
+ if (statusBuilder == null) {
+ statusBuilder = (SAMLObjectBuilder<Status>)
+ builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusCodeBuilder == null) {
+ statusCodeBuilder = (SAMLObjectBuilder<StatusCode>)
+ builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
+ }
+ if (statusMessageBuilder == null) {
+ statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>)
+ builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
+ }
+
+ Status status = statusBuilder.buildObject();
+
+ StatusCode statusCode = statusCodeBuilder.buildObject();
+ statusCode.setValue(statusCodeValue);
+ status.setStatusCode(statusCode);
+
+ if (statusMessage != null) {
+ StatusMessage statusMessageObject = statusMessageBuilder.buildObject();
+ statusMessageObject.setMessage(statusMessage);
+ status.setStatusMessage(statusMessageObject);
+ }
+
+ return status;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static AuthnContextClassRef createAuthnContextClassRef(String newAuthnContextClassRef) {
+ if (authnContextClassRefBuilder == null) {
+ authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>)
+ builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+ }
+
+ AuthnContextClassRef authnContextClassRef = authnContextClassRefBuilder.buildObject();
+ authnContextClassRef.setAuthnContextClassRef(newAuthnContextClassRef);
+
+ return authnContextClassRef;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/src/test/resources/fediz.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/fediz.xml b/fit/core-reference/src/test/resources/fediz.xml
new file mode 100644
index 0000000..cbc8faa
--- /dev/null
+++ b/fit/core-reference/src/test/resources/fediz.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+-->
+<EntityDescriptor entityID="urn:org:apache:cxf:fediz:idp:realm-A" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
+ <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data>
+ <ds:X509Certificate>
+MIICwTCCAamgAwIBAgIEINqJ9TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDEwZSRUFMTUEwHhcN
+MTUwNjEwMTU0NDE3WhcNMjUwNDE4MTU0NDE3WjARMQ8wDQYDVQQDEwZSRUFMTUEwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCJDSXn2lDR+JM+AsJarFG3/XGH7K+9AfAbQIz2IgB9MCpO
+KVWTUPCvuo1I+Fp5nEGreuHYLEwgIiam3o+C9tvpLgtDDaDkmXjDzkWpk8z6+im72HZ/ODF93Rqw
+jIiY5ZCzgDumFyPzdKiGwChThamidy+rd6oheSoi6qRVSMMcnwiEUmvkfFvV3izXRqeT5nGQwsin
+y9mCEiGx8jkfxP++H0RQjVjhOwzfQ7epsR7dTQNf2ZhkBR3o6wKV9QnF2IBWHZpA9EK58rWU9H6j
+G7b631rYvwsbOUF9HcZ8DI2BFh+4p18jDN/fnjNGSLr9rYOExpsIiF1cHBK7Tr7WwCmDAgMBAAGj
+ITAfMB0GA1UdDgQWBBRHy0qYoLm9jx/1L6r61NznHKun2jANBgkqhkiG9w0BAQsFAAOCAQEAR9rU
+5Sp1FsOErdvKNFqeaKl0oq6Fuz7BWcGm2kK6+1ZbWE8IOv6Vh+BlLuOe5hF7aLUbm8UIjhKsmg0M
+Ey5MBwkBZktT1qhQteMuiKgYR7CxayCxO0f125RYvvwntJa5rI7bUrzOqX29VQD1qQ/Tb+08fULT
+L7oURP+g88Ff99dn3IpO4VZxZdsbl4+KZRtqQvPAdXNYjOajJtPzS489+/DtfWJ6wPm/7YZ4did4
+1fYcrdwyEZ15L0/5i931z7sztNickm5WhO40qEVDKN6KrlV2Eyea0+933v2Pwe4resTlko9G2T5h
+dEaSbvht2Q/JOMMmT91daeto2oS8HTKhTA==
+ </ds:X509Certificate>
+ </ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>
+ <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://localhost:8443/fediz-idp/saml/up"/>
+ <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://localhost:8443/fediz-idp/saml/up"/>
+ </IDPSSODescriptor>
+</EntityDescriptor>
http://git-wip-us.apache.org/repos/asf/syncope/blob/acf98a47/fit/core-reference/src/test/resources/stsrealm_a.jks
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/stsrealm_a.jks b/fit/core-reference/src/test/resources/stsrealm_a.jks
new file mode 100644
index 0000000..fde2928
Binary files /dev/null and b/fit/core-reference/src/test/resources/stsrealm_a.jks differ
[3/3] syncope git commit: NPE guards for both the RelayState and
Response
Posted by co...@apache.org.
NPE guards for both the RelayState and Response
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/c7a50578
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/c7a50578
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/c7a50578
Branch: refs/heads/master
Commit: c7a505783c87c7b595f20512c706ba7f4edbfa37
Parents: bfce004
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Aug 2 15:31:47 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Aug 2 17:10:05 2017 +0100
----------------------------------------------------------------------
.../main/java/org/apache/syncope/core/logic/SAML2SPLogic.java | 6 ++++++
1 file changed, 6 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/c7a50578/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
----------------------------------------------------------------------
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
index 222d3cf..28a1ef0 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/SAML2SPLogic.java
@@ -421,6 +421,9 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
check();
// 1. first checks for the provided relay state
+ if (response.getRelayState() == null) {
+ throw new IllegalArgumentException("No Relay State was provided");
+ }
JwsJwtCompactConsumer relayState = new JwsJwtCompactConsumer(response.getRelayState());
if (!relayState.verifySignatureWith(jwsSignatureVerifier)) {
throw new IllegalArgumentException("Invalid signature found in Relay State");
@@ -429,6 +432,9 @@ public class SAML2SPLogic extends AbstractSAML2Logic<AbstractBaseBean> {
relayState.getJwtClaims().getClaim(JWT_CLAIM_IDP_DEFLATE).toString());
// 2. parse the provided SAML response
+ if (response.getSamlResponse() == null) {
+ throw new IllegalArgumentException("No SAML Response was provided");
+ }
Response samlResponse;
try {
XMLObject responseObject = saml2rw.read(useDeflateEncoding, response.getSamlResponse());
[2/3] syncope git commit: Switch to use different ports to avoid
conflict with other Tomcat deployments
Posted by co...@apache.org.
Switch to use different ports to avoid conflict with other Tomcat deployments
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/a11cd34e
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/a11cd34e
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/a11cd34e
Branch: refs/heads/master
Commit: a11cd34eb3abba039a103a6429a6b9445234ef9d
Parents: c7a5057
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Aug 2 16:42:31 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Aug 2 17:10:05 2017 +0100
----------------------------------------------------------------------
standalone/pom.xml | 2 ++
1 file changed, 2 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/a11cd34e/standalone/pom.xml
----------------------------------------------------------------------
diff --git a/standalone/pom.xml b/standalone/pom.xml
index 89dd1bc..e045bf1 100644
--- a/standalone/pom.xml
+++ b/standalone/pom.xml
@@ -187,6 +187,8 @@ under the License.
<copy file="src/main/resources/setenv.sh" todir="${work.dir}/apache-tomcat-${tomcat.version}/bin" />
<copy file="src/main/resources/setenv.bat" todir="${work.dir}/apache-tomcat-${tomcat.version}/bin" />
<replace file="${work.dir}/apache-tomcat-${tomcat.version}/conf/server.xml" token="8080" value="${cargo.servlet.port}" />
+ <replace file="${work.dir}/apache-tomcat-${tomcat.version}/conf/server.xml" token="8009" value="${cargo.tomcat.ajp.port}" />
+ <replace file="${work.dir}/apache-tomcat-${tomcat.version}/conf/server.xml" token="8005" value="${cargo.rmi.port}" />
</target>
</configuration>
</execution>