ECP message encoder (requests via PAOS, responses via SOAP).
authorcantor <cantor@fb386ef7-a10c-0410-8ebf-fd3f8e989ab0>
Tue, 6 Nov 2007 20:58:26 +0000 (20:58 +0000)
committercantor <cantor@fb386ef7-a10c-0410-8ebf-fd3f8e989ab0>
Tue, 6 Nov 2007 20:58:26 +0000 (20:58 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-opensaml2/trunk@332 fb386ef7-a10c-0410-8ebf-fd3f8e989ab0

saml/Makefile.am
saml/binding/impl/MessageEncoder.cpp
saml/saml2/binding/impl/SAML2ECPEncoder.cpp [new file with mode: 0644]
saml/util/SAMLConstants.cpp
saml/util/SAMLConstants.h

index 5b934a9..9c6cab4 100644 (file)
@@ -157,6 +157,7 @@ libsaml_la_SOURCES = \
        saml2/binding/impl/SAML2RedirectEncoder.cpp \
        saml2/binding/impl/SAML2SOAPDecoder.cpp \
        saml2/binding/impl/SAML2SOAPEncoder.cpp \
+       saml2/binding/impl/SAML2ECPEncoder.cpp \
        saml2/binding/impl/SAML2SOAPClient.cpp \
        saml2/profile/Assertion20Validator.cpp \
        saml2/profile/BrowserSSOProfile20Validator.cpp \
index 17fea00..6799c81 100644 (file)
@@ -45,6 +45,7 @@ namespace opensaml {
         SAML_DLLLOCAL PluginManager< MessageEncoder,string,pair<const DOMElement*,const XMLCh*> >::Factory SAML2POSTSimpleSignEncoderFactory;
         SAML_DLLLOCAL PluginManager< MessageEncoder,string,pair<const DOMElement*,const XMLCh*> >::Factory SAML2RedirectEncoderFactory;
         SAML_DLLLOCAL PluginManager< MessageEncoder,string,pair<const DOMElement*,const XMLCh*> >::Factory SAML2SOAPEncoderFactory;
+        SAML_DLLLOCAL PluginManager< MessageEncoder,string,pair<const DOMElement*,const XMLCh*> >::Factory SAML2ECPEncoderFactory;
     };
 };
 
@@ -59,4 +60,5 @@ void SAML_API opensaml::registerMessageEncoders()
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN, saml2p::SAML2POSTSimpleSignEncoderFactory);
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_REDIRECT, saml2p::SAML2RedirectEncoderFactory);
     conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_SOAP, saml2p::SAML2SOAPEncoderFactory);
+    conf.MessageEncoderManager.registerFactory(samlconstants::SAML20_BINDING_PAOS, saml2p::SAML2ECPEncoderFactory);
 }
diff --git a/saml/saml2/binding/impl/SAML2ECPEncoder.cpp b/saml/saml2/binding/impl/SAML2ECPEncoder.cpp
new file mode 100644 (file)
index 0000000..99c4ddd
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SAML2ECPEncoder.cpp
+ * 
+ * SAML 2.0 ECP profile message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "binding/MessageEncoder.h"
+#include "signature/ContentReference.h"
+#include "saml1/core/Protocols.h"
+#include "saml2/core/Protocols.h"
+
+#include <sstream>
+#include <xmltooling/logging.h>
+#include <xmltooling/impl/AnyElement.h>
+#include <xmltooling/io/HTTPResponse.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/soap/SOAP.h>
+
+using namespace samlconstants;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmlconstants;
+using namespace xmlsignature;
+using namespace soap11;
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace std;
+
+namespace opensaml {
+    namespace saml2p {              
+        
+        static const XMLCh ProviderName[] = UNICODE_LITERAL_12(P,r,o,v,i,d,e,r,N,a,m,e);
+
+        class SAML_DLLLOCAL SAML2ECPEncoder : public MessageEncoder
+        {
+        public:
+            SAML2ECPEncoder(const DOMElement* e, const XMLCh* ns) : m_actor("http://schemas.xmlsoap.org/soap/actor/next"),
+                    m_providerName(e ? e->getAttributeNS(ns, ProviderName) : NULL), m_idpList(NULL) {
+                DOMElement* child = e ? XMLHelper::getFirstChildElement(e, SAML20P_NS, IDPList::LOCAL_NAME) : NULL;
+                if (child)
+                    m_idpList = dynamic_cast<IDPList*>(XMLObjectBuilder::buildOneFromElement(child));
+            }
+            virtual ~SAML2ECPEncoder() {
+                delete m_idpList;
+            }
+
+            long encode(
+                GenericResponse& genericResponse,
+                XMLObject* xmlObject,
+                const char* destination,
+                const EntityDescriptor* recipient=NULL,
+                const char* relayState=NULL,
+                const ArtifactGenerator* artifactGenerator=NULL,
+                const Credential* credential=NULL,
+                const XMLCh* signatureAlg=NULL,
+                const XMLCh* digestAlg=NULL
+                ) const;
+            
+        private:
+            auto_ptr_XMLCh m_actor;
+            const XMLCh* m_providerName;
+            IDPList* m_idpList;
+            AnyElementBuilder m_anyBuilder;
+        };
+
+        MessageEncoder* SAML_DLLLOCAL SAML2ECPEncoderFactory(const pair<const DOMElement*,const XMLCh*>& p)
+        {
+            return new SAML2ECPEncoder(p.first, p.second);
+        }
+    };
+};
+
+long SAML2ECPEncoder::encode(
+    GenericResponse& genericResponse,
+    XMLObject* xmlObject,
+    const char* destination,
+    const EntityDescriptor* recipient,
+    const char* relayState,
+    const ArtifactGenerator* artifactGenerator,
+    const Credential* credential,
+    const XMLCh* signatureAlg,
+    const XMLCh* digestAlg
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("encode");
+#endif
+    Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML2ECP");
+
+    log.debug("validating input");
+    if (xmlObject->getParent())
+        throw BindingException("Cannot encode XML content with parent.");
+
+    Response* response = NULL;
+    AuthnRequest* request = dynamic_cast<AuthnRequest*>(xmlObject);
+    if (!request) {
+        response = dynamic_cast<Response*>(xmlObject);
+        if (!response)
+            throw BindingException("XML content for SAML 2.0 ECP Encoder must be a SAML 2.0 AuthnRequest or Response.");
+    }
+    
+    if (request && !request->getAssertionConsumerServiceURL())
+        throw BindingException("AuthnRequest must carry an AssertionConsumerServiceURL by value.");
+    else if (response && !response->getDestination())
+        throw BindingException("Response must carry a Destination attribute.");
+    
+    // PAOS request leg is a custom MIME type, SOAP response leg is just text/xml.
+    genericResponse.setContentType(request ? "application/vnd.paos+xml" : "text/xml");
+    HTTPResponse* httpResponse = dynamic_cast<HTTPResponse*>(&genericResponse);
+    if (httpResponse) {
+        httpResponse->setResponseHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
+        httpResponse->setResponseHeader("Pragma", "no-cache");
+    }
+
+    // Wrap it in a SOAP envelope.
+    Envelope* env = EnvelopeBuilder::buildEnvelope();
+    Header* header = HeaderBuilder::buildHeader();
+    env->setHeader(header);
+    Body* body = BodyBuilder::buildBody();
+    env->setBody(body);
+    body->getUnknownXMLObjects().push_back(xmlObject);
+
+    ElementProxy* hdrblock;
+    QName qMU(SOAP11ENV_NS, Header::MUSTUNDERSTAND_ATTRIB_NAME, SOAP11ENV_PREFIX);
+    QName qActor(SOAP11ENV_NS, Header::ACTOR_ATTRIB_NAME, SOAP11ENV_PREFIX);
+    
+    if (request) {
+        // Create paos:Request header.
+        static const XMLCh service[] = UNICODE_LITERAL_7(s,e,r,v,i,c,e);
+        static const XMLCh responseConsumerURL[] = UNICODE_LITERAL_19(r,e,s,p,o,n,s,e,C,o,n,s,u,m,e,r,U,R,L);
+        hdrblock = dynamic_cast<ElementProxy*>(m_anyBuilder.buildObject(PAOS_NS, saml1p::Request::LOCAL_NAME, PAOS_PREFIX));
+        hdrblock->setAttribute(qMU, XML_ONE);
+        hdrblock->setAttribute(qActor, m_actor.get());
+        hdrblock->setAttribute(QName(NULL, service), SAML20ECP_NS);
+        hdrblock->setAttribute(QName(NULL, responseConsumerURL), request->getAssertionConsumerServiceURL());
+        header->getUnknownXMLObjects().push_back(hdrblock);
+
+        // Create ecp:Request header.
+        static const XMLCh IsPassive[] = UNICODE_LITERAL_9(I,s,P,a,s,s,i,v,e);
+        hdrblock = dynamic_cast<ElementProxy*>(m_anyBuilder.buildObject(SAML20ECP_NS, saml1p::Request::LOCAL_NAME, SAML20ECP_PREFIX));
+        hdrblock->setAttribute(qMU, XML_ONE);
+        hdrblock->setAttribute(qActor, m_actor.get());
+        if (!request->IsPassive())
+            hdrblock->setAttribute(QName(NULL,IsPassive), XML_ZERO);
+        if (m_providerName)
+            hdrblock->setAttribute(QName(NULL,ProviderName), m_providerName);
+        hdrblock->getUnknownXMLObjects().push_back(request->getIssuer()->clone());
+        if (request->getScoping() && request->getScoping()->getIDPList())
+            hdrblock->getUnknownXMLObjects().push_back(request->getScoping()->getIDPList()->clone());
+        else if (m_idpList)
+            hdrblock->getUnknownXMLObjects().push_back(m_idpList->clone());
+        header->getUnknownXMLObjects().push_back(hdrblock);
+    }
+    else {
+        // Create ecp:Response header.
+        hdrblock = dynamic_cast<ElementProxy*>(m_anyBuilder.buildObject(SAML20ECP_NS, Response::LOCAL_NAME, SAML20ECP_PREFIX));
+        hdrblock->setAttribute(qMU, XML_ONE);
+        hdrblock->setAttribute(qActor, m_actor.get());
+        hdrblock->setAttribute(QName(NULL,AuthnRequest::ASSERTIONCONSUMERSERVICEURL_ATTRIB_NAME), response->getDestination());
+        header->getUnknownXMLObjects().push_back(hdrblock);
+    }
+    
+    if (relayState) {
+        // Create ecp:RelayState header.
+        static const XMLCh RelayState[] = UNICODE_LITERAL_10(R,e,l,a,y,S,t,a,t,e);
+        hdrblock = dynamic_cast<ElementProxy*>(m_anyBuilder.buildObject(SAML20ECP_NS, RelayState, SAML20ECP_PREFIX));
+        hdrblock->setAttribute(qMU, XML_ONE);
+        hdrblock->setAttribute(qActor, m_actor.get());
+        auto_ptr_XMLCh rs(relayState);
+        hdrblock->setTextContent(rs.get());
+        header->getUnknownXMLObjects().push_back(hdrblock);
+    }
+    
+    try {
+        DOMElement* rootElement = NULL;
+        if (credential) {
+            if (request->getSignature()) {
+                log.debug("message already signed, skipping signature operation");
+                rootElement = env->marshall();
+            }
+            else {
+                log.debug("signing the message and marshalling the envelope");
+    
+                // Build a Signature.
+                Signature* sig = SignatureBuilder::buildSignature();
+                request->setSignature(sig);    
+                if (signatureAlg)
+                    sig->setSignatureAlgorithm(signatureAlg);
+                if (digestAlg) {
+                    opensaml::ContentReference* cr = dynamic_cast<opensaml::ContentReference*>(sig->getContentReference());
+                    if (cr)
+                        cr->setDigestAlgorithm(digestAlg);
+                }
+        
+                // Sign message while marshalling.
+                vector<Signature*> sigs(1,sig);
+                rootElement = env->marshall((DOMDocument*)NULL,&sigs,credential);
+            }
+        }
+        else {
+            log.debug("marshalling the envelope");
+            rootElement = env->marshall();
+        }
+
+        string xmlbuf;
+        XMLHelper::serialize(rootElement, xmlbuf);
+        istringstream s(xmlbuf);
+        log.debug("sending serialized envelope");
+        long ret = genericResponse.sendResponse(s);
+    
+        // Cleanup by destroying XML.
+        delete env;
+        return ret;
+    }
+    catch (XMLToolingException&) {
+        // A bit weird...we have to "revert" things so that the message is isolated
+        // so the caller can free it.
+        xmlObject->getParent()->detach();
+        xmlObject->detach();
+        throw;
+    }
+}
index b39518c..b43dfd4 100644 (file)
@@ -192,6 +192,8 @@ const char samlconstants::SAML1_PROFILE_BROWSER_POST[] = "urn:oasis:names:tc:SAM
 
 const char samlconstants::SAML20_BINDING_SOAP[] = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP";
 
+const char samlconstants::SAML20_BINDING_PAOS[] = "urn:oasis:names:tc:SAML:2.0:bindings:PAOS";
+
 const char samlconstants::SAML20_BINDING_URI[] = "urn:oasis:names:tc:SAML:2.0:bindings:URI";
 
 const char samlconstants::SAML20_BINDING_HTTP_ARTIFACT[] = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact";
index 608be0f..5b6774c 100644 (file)
@@ -135,6 +135,9 @@ namespace samlconstants {
     /** SAML 2.0 SOAP binding ("urn:oasis:names:tc:SAML:2.0:bindings:SOAP") */
     extern SAML_API const char SAML20_BINDING_SOAP[];
 
+    /** SAML 2.0 PAOS binding ("urn:oasis:names:tc:SAML:2.0:bindings:PAOS") */
+    extern SAML_API const char SAML20_BINDING_PAOS[];
+    
     /** SAML 2.0 URI binding ("urn:oasis:names:tc:SAML:2.0:bindings:URI") */
     extern SAML_API const char SAML20_BINDING_URI[];