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>