Assertion export and lookup service using URI binding.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Tue, 29 May 2007 02:16:28 +0000 (02:16 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Tue, 29 May 2007 02:16:28 +0000 (02:16 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2267 cb58f699-b61c-0410-a6fe-9272a202ed29

configs/shibboleth.xml.in
schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/Makefile.am
shibsp/ServiceProvider.cpp
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionLookup.cpp [new file with mode: 0644]
shibsp/handler/impl/SAML2ArtifactResolution.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp-lite.vcproj
shibsp/shibsp.vcproj
shibsp/util/SPConstants.h

index df65ed2..f7d7ebc 100644 (file)
                        </SessionInitiator>
                        
                        <!--
-                       md:AssertionConsumerService elements handle specific SSO protocol bindings,
+                       md:AssertionConsumerService locations handle specific SSO protocol bindings,
                        such as SAML 2.0 POST or SAML 1.1 Artifact. The isDefault and index attributes
                        are used when sessions are initiated to determine how to tell the IdP where and
                        how to return the response.
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01"/>
 
                        <!--
-                       md:ArtifactResolutionService elements resolve artifacts issued when using the
+                       md:ArtifactResolutionService locations resolve artifacts issued when using the
                        SAML 2.0 HTTP-Artifact binding on outgoing messages, generally uses SOAP.
                        -->
             <md:ArtifactResolutionService Location="/SOAP/Artifact" index="1"
index ab2da76..f36f284 100644 (file)
                        </choice>\r
                        <attribute name="handlerURL" type="anyURI"/>\r
                        <attribute name="handlerSSL" type="boolean" default="true"/>\r
+                       <attribute name="exportLocation" type="conf:string"/>\r
+                       <attribute name="exportACL" type="conf:listOfStrings" default="127.0.0.1"/>\r
                        <attribute name="redirectErrors" type="anyURI"/>\r
                        <attribute name="cookieName" type="conf:string"/>\r
                        <attribute name="cookieProps" type="conf:string"/>\r
index 4acf181..b35e327 100644 (file)
@@ -103,6 +103,7 @@ common_sources = \
        attribute/Attribute.cpp \
        handler/impl/AbstractHandler.cpp \
        handler/impl/AssertionConsumerService.cpp \
+       handler/impl/AssertionLookup.cpp \
        handler/impl/ChainingSessionInitiator.cpp \
        handler/impl/RemotedHandler.cpp \
        handler/impl/SAML1Consumer.cpp \
index e301bdf..66f125c 100644 (file)
@@ -35,6 +35,7 @@
 #include <sstream>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/URLEncoder.h>
 #include <xmltooling/util/XMLHelper.h>
 
 using namespace shibsp;
@@ -91,6 +92,7 @@ namespace shibsp {
         request.clearHeader("Shib-AuthnContext-Class");
         request.clearHeader("Shib-AuthnContext-Decl");
         request.clearHeader("Shib-Attributes");
+        request.clearHeader("Shib-Assertion-Count");
         //request.clearHeader("Shib-Application-ID");   handle inside app method
         request.getApplication().clearAttributeHeaders(request);
     }
@@ -349,9 +351,26 @@ pair<bool,long> ServiceProvider::doExport(SPRequest& request, bool requireSessio
         // Maybe export the assertion keys.
         pair<bool,bool> exp=settings.first->getBool("exportAssertion");
         if (exp.first && exp.second) {
-            //setHeader("Shib-Attributes", reinterpret_cast<char*>(serialized));
-            // TODO: export lookup URLs to access assertions by ID
-            const vector<const char*>& tokens = session->getAssertionIDs();
+            const PropertySet* sessions=app->getPropertySet("Sessions");
+            pair<bool,const char*> exportLocation = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);
+            if (!exportLocation.first)
+                request.log(SPRequest::SPWarn, "can't export assertions without an exportLocation Sessions property");
+            else {
+                const URLEncoder* encoder = XMLToolingConfig::getConfig().getURLEncoder();
+                string exportName = "Shib-Assertion-00";
+                const char* handlerURL=request.getHandlerURL(targetURL.c_str());
+                string baseURL = string(handlerURL) + exportLocation.second + "?key=" + session->getID() + "&ID=";
+                const vector<const char*>& tokens = session->getAssertionIDs();
+                vector<const char*>::size_type count = 0;
+                for (vector<const char*>::const_iterator tokenids = tokens.begin(); tokenids!=tokens.end(); ++tokenids) {
+                    count++;
+                    *(exportName.rbegin()) = '0' + (count%10);
+                    *(++exportName.rbegin()) = '0' + (count/10);
+                    string fullURL = baseURL + encoder->encode(*tokenids);
+                    request.setHeader(exportName.c_str(), fullURL.c_str());
+                }
+                request.setHeader("Shib-Assertion-Count", exportName.c_str() + 15);
+            }
         }
 
         // Export the attributes.
index 1054f4c..c8710cd 100644 (file)
@@ -55,6 +55,7 @@ namespace shibsp {
     SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML1ConsumerFactory;
     SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML2ConsumerFactory;
     SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory SAML2ArtifactResolutionFactory;
+    SHIBSP_DLLLOCAL PluginManager<Handler,string,pair<const DOMElement*,const char*>>::Factory AssertionLookupFactory;
 };
 
 void SHIBSP_API shibsp::registerHandlers()
@@ -68,6 +69,8 @@ void SHIBSP_API shibsp::registerHandlers()
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);
 
     conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);
+
+    conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory);
 }
 
 AbstractHandler::AbstractHandler(
diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp
new file mode 100644 (file)
index 0000000..5b8f006
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ *  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.
+ */
+
+/**
+ * AssertionLookup.cpp
+ * 
+ * Handler for looking assertions in SessionCache
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "handler/AbstractHandler.h"
+#include "handler/RemotedHandler.h"
+#include "util/SPConstants.h"
+
+using namespace shibspconstants;
+using namespace shibsp;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_API AssertionLookup : public AbstractHandler, public RemotedHandler 
+    {
+    public:
+        AssertionLookup(const DOMElement* e, const char* appId);
+        virtual ~AssertionLookup() {}
+
+        pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+        void receive(DDF& in, ostream& out);
+
+    private:
+        pair<bool,long> processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const;
+
+        set<string> m_acl;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL AssertionLookupFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new AssertionLookup(p.first, p.second);
+    }
+
+};
+
+AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".AssertionLookup"))
+{
+    setAddress("run::AssertionLookup");
+    if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+        pair<bool,const char*> acl = getString("exportACL");
+        if (!acl.first) {
+            m_acl.insert("127.0.0.1");
+            return;
+        }
+        string aclbuf=acl.second;
+        int j = 0;
+        for (unsigned int i=0;  i < aclbuf.length();  i++) {
+            if (aclbuf.at(i)==' ') {
+                m_acl.insert(aclbuf.substr(j, i-j));
+                j = i+1;
+            }
+        }
+        m_acl.insert(aclbuf.substr(j, aclbuf.length()-j));
+    }
+}
+
+pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
+{
+    string relayState;
+    SPConfig& conf = SPConfig::getConfig();
+    if (conf.isEnabled(SPConfig::InProcess)) {
+        if (m_acl.count(request.getRemoteAddr()) == 0) {
+            m_log.error("request for assertion lookup blocked from invalid address (%s)", request.getRemoteAddr());
+            istringstream msg("Assertion Lookup Blocked");
+            return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_FORBIDDEN));
+        }
+    }
+    
+    try {
+        if (conf.isEnabled(SPConfig::OutOfProcess)) {
+            // When out of process, we run natively and directly process the message.
+            return processMessage(request.getApplication(), request, request);
+        }
+        else {
+            // When not out of process, we remote all the message processing.
+            DDF out,in = wrap(request, NULL, true);
+            DDFJanitor jin(in), jout(out);
+            
+            in.addmember("application_id").string(request.getApplication().getId());
+            out=request.getServiceProvider().getListenerService()->send(in);
+            return unwrap(request, out);
+        }
+    }
+    catch (exception& ex) {
+        m_log.error("error while processing request: %s", ex.what());
+        istringstream msg("Assertion Lookup Failed");
+        return make_pair(true,request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
+    }
+}
+
+void AssertionLookup::receive(DDF& in, ostream& out)
+{
+    // Find application.
+    const char* aid=in["application_id"].string();
+    const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
+    if (!app) {
+        // Something's horribly wrong.
+        m_log.error("couldn't find application (%s) for assertion lookup", aid ? aid : "(missing)");
+        throw ConfigurationException("Unable to locate application for assertion lookup, deleted?");
+    }
+    
+    // Unpack the request.
+    auto_ptr<HTTPRequest> req(getRequest(in));
+    //m_log.debug("found %d client certificates", req->getClientCertificates().size());
+
+    // Wrap a response shim.
+    DDF ret(NULL);
+    DDFJanitor jout(ret);
+    auto_ptr<HTTPResponse> resp(getResponse(ret));
+        
+    // Since we're remoted, the result should either be a throw, a false/0 return,
+    // which we just return as an empty structure, or a response/redirect,
+    // which we capture in the facade and send back.
+    processMessage(*app, *req.get(), *resp.get());
+    out << ret;
+}
+
+pair<bool,long> AssertionLookup::processMessage(const Application& application, HTTPRequest& httpRequest, HTTPResponse& httpResponse) const
+{
+#ifndef SHIBSP_LITE
+    const char* key = httpRequest.getParameter("key");
+    const char* ID = httpRequest.getParameter("ID");
+    if (!key || !*key || !ID || !*ID) {
+        m_log.error("assertion lookup request failed, missing required parameters");
+        throw FatalProfileException("Missing key or ID parameters.");
+    }
+
+    m_log.debug("processing assertion lookup request (session: %s, assertion: %s)", key, ID);
+
+    // The cache will either silently pass a session or NULL back, or throw an exception out.
+    Session* session = application.getServiceProvider().getSessionCache()->find(key, application);
+    if (!session) {
+        m_log.error("valid session (%s) not found for assertion lookup", key);
+        throw FatalProfileException("Session key not found.");
+    }
+
+    Locker locker(session, false);
+
+    const Assertion* assertion = session->getAssertion(ID);
+    if (!assertion) {
+        m_log.error("assertion (%s) not found in session (%s)", ID, key);
+        throw FatalProfileException("Assertion not found.");
+    }
+
+    stringstream s;\r
+    s << *assertion;\r
+    httpResponse.setContentType("application/samlassertion+xml");
+    return make_pair(true, httpResponse.sendResponse(s));
+#else
+    return make_pair(false,0);
+#endif
+}
index e61ade5..c6de827 100644 (file)
@@ -57,9 +57,6 @@ using namespace std;
 
 namespace shibsp {
 
-    class SHIBSP_API Attribute;
-    class SHIBSP_API ResolutionContext;
-
 #if defined (_MSC_VER)
     #pragma warning( push )
     #pragma warning( disable : 4250 )
@@ -145,7 +142,6 @@ pair<bool,long> SAML2ArtifactResolution::run(SPRequest& request, bool isHandler)
     try {
         if (conf.isEnabled(SPConfig::OutOfProcess)) {
             // When out of process, we run natively and directly process the message.
-            string entityID;
             return processMessage(request.getApplication(), request, request);
         }
         else {
@@ -199,7 +195,7 @@ void SAML2ArtifactResolution::receive(DDF& in, ostream& out)
     if (!app) {
         // Something's horribly wrong.
         m_log.error("couldn't find application (%s) for artifact resolution", aid ? aid : "(missing)");
-        throw ConfigurationException("Unable to locate application for new session, deleted?");
+        throw ConfigurationException("Unable to locate application for artifact resolution, deleted?");
     }
     
     // Unpack the request.
index 187394f..b886e93 100644 (file)
@@ -55,7 +55,7 @@
 # include <saml/saml2/binding/SAML2ArtifactType0004.h>
 # include <saml/saml2/metadata/ChainingMetadataProvider.h>
 # include <xmltooling/security/ChainingTrustEngine.h>
-#include <xmltooling/util/ReplayCache.h>
+# include <xmltooling/util/ReplayCache.h>
 using namespace opensaml::saml2;
 using namespace opensaml::saml2p;
 using namespace opensaml::saml2md;
@@ -499,10 +499,28 @@ XMLApplication::XMLApplication(
             }
         }
 
+        Handler* handler=NULL;
         const PropertySet* sessions = getPropertySet("Sessions");
 
-        // Process handlers.
-        Handler* handler=NULL;
+        // Process assertion export handler.
+        pair<bool,const char*> location = sessions ? sessions->getString("exportLocation") : pair<bool,const char*>(false,NULL);
+        if (location.first) {
+            try {
+                handler = conf.HandlerManager.newPlugin(samlconstants::SAML20_BINDING_URI, make_pair(sessions->getElement(), getId()));
+                m_handlers.push_back(handler);
+
+                // Insert into location map.
+                if (*location.second == '/')
+                    m_handlerMap[location.second]=handler;
+                else
+                    m_handlerMap[string("/") + location.second]=handler;
+            }
+            catch (exception& ex) {
+                log.error("caught exception installing assertion lookup handler: %s", ex.what());
+            }
+        }
+
+        // Process other handlers.
         bool hardACS=false, hardSessionInit=false, hardArt=false;
         const DOMElement* child = sessions ? XMLHelper::getFirstChildElement(sessions->getElement()) : NULL;
         while (child) {
@@ -613,7 +631,7 @@ XMLApplication::XMLApplication(
                 m_handlers.push_back(handler);
 
                 // Insert into location map.
-                pair<bool,const char*> location=handler->getString("Location");
+                location=handler->getString("Location");
                 if (location.first && *location.second == '/')
                     m_handlerMap[location.second]=handler;
                 else if (location.first)
index cfbfdbe..2b49d94 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\AssertionLookup.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\ChainingSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
index f1da7e9..ac0b337 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\AssertionLookup.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\ChainingSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
index 98c8b37..31c2097 100644 (file)
@@ -27,7 +27,7 @@
 #ifndef SHIBSP_LITE
 # include <saml/util/SAMLConstants.h>
 #else
-# include <xmltooling/util/XMLConstants.h>
+# include <shibsp/lite/SAMLConstants.h>
 #endif
 
 /**