cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject [3/5] cxf-fediz git commit: Create a proper SAML Response for SAML SSO
Date Fri, 18 Mar 2016 17:06:37 GMT
Create a proper SAML Response for SAML SSO


Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/dcbbe664
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/dcbbe664
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/dcbbe664

Branch: refs/heads/master
Commit: dcbbe664c01d78ca73b3a045292c5adce2d8d3e5
Parents: 5d8fb36
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Fri Mar 18 16:50:29 2016 +0000
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Fri Mar 18 16:50:29 2016 +0000

----------------------------------------------------------------------
 .../idp/beans/samlsso/AuthnRequestParser.java   |  28 +++
 .../idp/beans/samlsso/SamlResponseCreator.java  | 204 +++++++++++++++++++
 .../idp/samlsso/SAML2CallbackHandler.java       | 124 +++++++++++
 .../samlsso/SAML2PResponseComponentBuilder.java | 127 ++++++++++++
 .../WEB-INF/flows/saml-validate-request.xml     |  17 +-
 .../apache/cxf/fediz/systests/idp/IdpTest.java  |  16 +-
 6 files changed, 510 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
index 52bfecd..db8a013 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java
@@ -89,6 +89,34 @@ public class AuthnRequestParser {
         return null;
     }
     
+    public String retrieveRequestId(RequestContext context) {
+        AuthnRequest authnRequest = 
+            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+
+        if (authnRequest != null && authnRequest.getID() != null) {
+            String id = authnRequest.getID();
+            LOG.debug("Parsed SAML AuthnRequest Id: {}", id);
+            return id;
+        }
+        
+        LOG.debug("No AuthnRequest available to be parsed");
+        return null;
+    }
+    
+    public String retrieveRequestIssuer(RequestContext context) {
+        AuthnRequest authnRequest = 
+            (AuthnRequest)WebUtils.getAttributeFromFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST);
+
+        if (authnRequest != null && authnRequest.getIssuer() != null) {
+            String issuer = authnRequest.getIssuer().getValue();
+            LOG.debug("Parsed SAML AuthnRequest Issuer: {}", issuer);
+            return issuer;
+        }
+        
+        LOG.debug("No AuthnRequest available to be parsed");
+        return null;
+    }
+    
     private AuthnRequest extractRequest(String samlRequest) throws Exception {
         byte[] deflatedToken = Base64Utility.decode(samlRequest);
         InputStream tokenStream = new DeflateEncoderDecoder().inflateToken(deflatedToken);

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
new file mode 100644
index 0000000..2d8da15
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java
@@ -0,0 +1,204 @@
+/**
+ * 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.cxf.fediz.service.idp.beans.samlsso;
+
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.fediz.core.exception.ProcessingException;
+import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE;
+import org.apache.cxf.fediz.core.util.CertsUtils;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.samlsso.SAML2CallbackHandler;
+import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder;
+import org.apache.cxf.fediz.service.idp.util.WebUtils;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
+import org.apache.wss4j.common.crypto.CertificateStore;
+import org.apache.wss4j.common.crypto.Crypto;
+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.util.DOM2Writer;
+import org.apache.wss4j.dom.WSConstants;
+import org.joda.time.DateTime;
+import org.opensaml.saml.saml2.core.AttributeStatement;
+import org.opensaml.saml.saml2.core.Response;
+import org.opensaml.saml.saml2.core.Status;
+import org.opensaml.saml.saml2.core.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.webflow.execution.RequestContext;
+
+/**
+ * Insert the SAML Token received from the STS into a SAML Response
+ */
+@Component
+public class SamlResponseCreator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SamlResponseCreator.class);
+    private boolean supportDeflateEncoding;
+
+    public String createSAMLResponse(RequestContext context, Idp idp, Element rpToken,
+                                     String consumerURL, String requestId, String requestIssuer)

+                                         throws ProcessingException {
+        List<Element> samlTokens = 
+            DOMUtils.findAllElementsByTagNameNS(rpToken, WSConstants.SAML2_NS, "Assertion");
+        if (samlTokens.isEmpty() || samlTokens.size() != 1) {
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+        
+        try {
+            SamlAssertionWrapper wrapper = new SamlAssertionWrapper(samlTokens.get(0));
+            Subject subject = wrapper.getSaml2().getSubject();
+            List<AttributeStatement> attributeStatements = wrapper.getSaml2().getAttributeStatements();
+            
+            Element response = createResponse(context, idp, requestId, consumerURL, requestIssuer,
+                                              subject, attributeStatements);
+            return encodeResponse(response);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            LOG.warn("Error marshalling SAML Token: {}", ex.getMessage());
+            throw new ProcessingException(TYPE.BAD_REQUEST);
+        }
+    }
+    
+    protected Element createResponse(RequestContext context, Idp idp, String requestID, 
+                                     String racs, String requestIssuer,
+                                     Subject subject,
+                                     List<AttributeStatement> attributeStatements)
throws Exception {
+        DocumentBuilderFactory docBuilderFactory;
+        docBuilderFactory = DocumentBuilderFactory.newInstance();
+        docBuilderFactory.setNamespaceAware(true);
+        
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.newDocument();
+        
+        Status status = 
+            SAML2PResponseComponentBuilder.createStatus(
+                "urn:oasis:names:tc:SAML:2.0:status:Success", null
+            );
+        Response response = 
+            SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(),
status);
+        
+        // Create an AuthenticationAssertion
+        SAML2CallbackHandler callbackHandler = new SAML2CallbackHandler();
+        callbackHandler.setIssuer(idp.getRealm());
+        callbackHandler.setSubject(subject);
+        
+        // Subject Confirmation Data
+        SubjectConfirmationDataBean subjectConfirmationData = new SubjectConfirmationDataBean();
+        subjectConfirmationData.setAddress(WebUtils.getHttpServletRequest(context).getRemoteAddr());
+        subjectConfirmationData.setInResponseTo(requestID);
+        subjectConfirmationData.setNotAfter(new DateTime().plusMinutes(5));
+        subjectConfirmationData.setRecipient(racs);
+        callbackHandler.setSubjectConfirmationData(subjectConfirmationData);
+        
+        // Audience Restriction
+        ConditionsBean conditions = new ConditionsBean();
+        conditions.setTokenPeriodMinutes(5);
+        
+        AudienceRestrictionBean audienceRestriction = new AudienceRestrictionBean();
+        audienceRestriction.setAudienceURIs(Collections.singletonList(requestIssuer));
+        conditions.setAudienceRestrictions(Collections.singletonList(audienceRestriction));
+        callbackHandler.setConditions(conditions);
+        
+        SAMLCallback samlCallback = new SAMLCallback();
+        SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+        SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+        
+        Crypto issuerCrypto = getCrypto(idp.getCertificate());
+        assertion.signAssertion(issuerCrypto.getDefaultX509Identifier(), idp.getCertificatePassword(),

+                                issuerCrypto, false);
+        
+        response.getAssertions().add(assertion.getSaml2());
+        
+        Element policyElement = OpenSAMLUtil.toDom(response, doc);
+        doc.appendChild(policyElement);
+        
+        return policyElement;
+    }
+
+    protected String encodeResponse(Element response) throws IOException {
+        String responseMessage = DOM2Writer.nodeToString(response);
+        LOG.debug("Created Response: {}", responseMessage);
+
+        if (supportDeflateEncoding) {
+            DeflateEncoderDecoder encoder = new DeflateEncoderDecoder();
+            byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes("UTF-8"));
+
+            return Base64Utility.encode(deflatedBytes);
+        }
+        
+        return Base64Utility.encode(responseMessage.getBytes());
+    }
+    
+    private Crypto getCrypto(String certificate) throws ProcessingException {
+        if (certificate == null) {
+            return null;
+        }
+        
+        boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
+        if (isCertificateLocation) {
+            try {
+                X509Certificate cert = CertsUtils.getX509Certificate(certificate);
+                if (cert == null) {
+                    return null;
+                }
+                return new CertificateStore(new X509Certificate[]{cert});
+            } catch (CertificateException ex) {
+                // Maybe it's a WSS4J properties file...
+                return CertsUtils.createCrypto(certificate);
+            }
+        } 
+        
+        // Here the certificate is encoded in the configuration file
+        X509Certificate cert;
+        try {
+            cert = CertsUtils.parseCertificate(certificate);
+        } catch (Exception ex) {
+            LOG.error("Failed to parse trusted certificate", ex);
+            throw new ProcessingException("Failed to parse trusted certificate");
+        }
+        return new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
+    }
+
+    public boolean isSupportDeflateEncoding() {
+        return supportDeflateEncoding;
+    }
+
+    public void setSupportDeflateEncoding(boolean supportDeflateEncoding) {
+        this.supportDeflateEncoding = supportDeflateEncoding;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java
new file mode 100644
index 0000000..1475dd4
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2CallbackHandler.java
@@ -0,0 +1,124 @@
+/**
+ * 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.cxf.fediz.service.idp.samlsso;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+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.AttributeBean;
+import org.apache.wss4j.common.saml.bean.AttributeStatementBean;
+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;
+import org.opensaml.saml.saml2.core.Subject;
+
+/**
+ * A Callback Handler implementation for a SAML 2 assertion. By default it creates a SAML
2.0 Assertion with
+ * an AuthenticationStatement. If a list of roles are also supplied, it will insert them
as part of an 
+ * AttributeStatement.
+ */
+public class SAML2CallbackHandler implements CallbackHandler {
+    
+    private Subject subject;
+    private String confirmationMethod = SAML2Constants.CONF_BEARER;
+    private String issuer;
+    private ConditionsBean conditions;
+    private SubjectConfirmationDataBean subjectConfirmationData;
+    private List<Object> roles = new ArrayList<>();
+    
+    private void createAndSetStatement(SAMLCallback callback) {
+        AuthenticationStatementBean authBean = new AuthenticationStatementBean();
+        authBean.setAuthenticationMethod("Password");
+        callback.setAuthenticationStatementData(Collections.singletonList(authBean));
+
+        if (!roles.isEmpty()) {
+            AttributeStatementBean attrBean = new AttributeStatementBean();
+            AttributeBean attributeBean = new AttributeBean();
+            attributeBean.setQualifiedName("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role");
+            attributeBean.setNameFormat(SAML2Constants.ATTRNAME_FORMAT_UNSPECIFIED);
+            attributeBean.setAttributeValues(roles);
+                
+            attrBean.setSamlAttributes(Collections.singletonList(attributeBean));
+            callback.setAttributeStatementData(Collections.singletonList(attrBean));
+        }
+    }
+    
+    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(
+                        subject.getNameID().getValue(), subject.getNameID().getNameQualifier(),
confirmationMethod
+                    );
+                subjectBean.setSubjectNameIDFormat(subject.getNameID().getFormat());
+                subjectBean.setSubjectConfirmationData(subjectConfirmationData);
+
+                callback.setSubject(subjectBean);
+                createAndSetStatement(callback);
+            } else {
+                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
+            }
+        }
+    }
+    
+    public void setSubjectConfirmationData(SubjectConfirmationDataBean subjectConfirmationData)
{
+        this.subjectConfirmationData = subjectConfirmationData;
+    }
+    
+    public void setConditions(ConditionsBean conditionsBean) {
+        this.conditions = conditionsBean;
+    }
+    
+    public void setConfirmationMethod(String confMethod) {
+        confirmationMethod = confMethod;
+    }
+    
+    public void setIssuer(String issuer) {
+        this.issuer = issuer;
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    public void setSubject(Subject subject) {
+        this.subject = subject;
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
new file mode 100644
index 0000000..7e64cfa
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/samlsso/SAML2PResponseComponentBuilder.java
@@ -0,0 +1,127 @@
+/**
+ * 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.cxf.fediz.service.idp.samlsso;
+
+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.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 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;
+    }
+    
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml
index 1054cbb..701db0b 100644
--- a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml
+++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml
@@ -71,18 +71,31 @@
                       result="flowScope.consumerURL"/>
         </on-entry>
         <evaluate expression="signinParametersCacheAction.storeRPConfigInSession(flowRequestContext)"/>
-        <transition to="formResponseView" />
+        <transition to="produceSAMLResponse" />
         <transition on-exception="org.apache.cxf.fediz.core.exception.ProcessingException"
to="viewBadRequest" />
         <transition on-exception="java.lang.Throwable" to="scInternalServerError" />
     </action-state>
     
+    <action-state id="produceSAMLResponse">
+        <on-entry>
+            <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)"

+                      result="flowScope.requestId"/>
+           <evaluate expression="authnRequestParser.retrieveRequestIssuer(flowRequestContext)"

+                      result="flowScope.requestIssuer"/>
+        </on-entry>
+        <evaluate expression="samlResponseCreator.createSAMLResponse(flowRequestContext,
flowScope.idpConfig, flowScope.rpTokenElement,
+                                                                     flowScope.consumerURL,
flowScope.requestId, flowScope.requestIssuer)"
+                      result="flowScope.rpResponse"/>                                
              
+        <transition to="formResponseView" />
+    </action-state>
+    
     <!-- normal exit point for login -->
     <!-- browser redirection (self-submitted form 'samlsigninresponseform.jsp') -->
     <end-state id="formResponseView" view="samlsigninresponseform">
         <on-entry>
             <evaluate expression="flowScope.consumerURL" result="requestScope.samlAction"
/>
             <evaluate expression="flowScope.RelayState" result="requestScope.relayState"
/>
-            <evaluate expression="flowScope.rpToken" result="requestScope.samlResponse"
/>
+            <evaluate expression="flowScope.rpResponse" result="requestScope.samlResponse"
/>
         </on-entry>
     </end-state>
 

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/dcbbe664/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java
----------------------------------------------------------------------
diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java
b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java
index ce6d541..c245bb1 100644
--- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java
+++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java
@@ -19,8 +19,11 @@
 
 package org.apache.cxf.fediz.systests.idp;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URLEncoder;
 import java.util.UUID;
 
@@ -41,6 +44,7 @@ import org.apache.cxf.fediz.core.util.DOMUtils;
 import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder;
 import org.apache.cxf.rs.security.saml.sso.DefaultAuthnRequestBuilder;
 import org.apache.cxf.rs.security.saml.sso.SSOConstants;
+import org.apache.cxf.staxutils.StaxUtils;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
@@ -49,6 +53,7 @@ import org.apache.wss4j.dom.engine.WSSConfig;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
+import org.opensaml.core.xml.XMLObject;
 import org.opensaml.saml.saml2.core.AuthnRequest;
 
 /**
@@ -182,7 +187,7 @@ public class IdpTest {
         final HtmlPage idpPage = webClient.getPage(url);
         webClient.getOptions().setJavaScriptEnabled(true);
         Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText());
-
+        
         // Parse the form to get the token (SAMLResponse)
         DomNodeList<DomElement> results = idpPage.getElementsByTagName("input");
 
@@ -208,8 +213,7 @@ public class IdpTest {
         String action = formResult.getAttributeNS(null, "action");
         Assert.assertTrue(action.equals(consumerURL));
         
-        /*
-        // Decode response
+        // Decode + verify response
         byte[] deflatedToken = Base64Utility.decode(samlResponse);
         InputStream inputStream = new ByteArrayInputStream(deflatedToken);
         
@@ -217,7 +221,11 @@ public class IdpTest {
         
         XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement());
         Assert.assertTrue((responseObject instanceof org.opensaml.saml.saml2.core.Response));
-*/
+        
+        org.opensaml.saml.saml2.core.Response samlResponseObject = 
+            (org.opensaml.saml.saml2.core.Response)responseObject;
+        Assert.assertTrue(authnRequest.getID().equals(samlResponseObject.getInResponseTo()));
+
         webClient.close();
     }
     


Mime
View raw message