Change API to permit session initiators to modify the entityID mid-chain.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 8 Feb 2008 19:55:16 +0000 (19:55 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 8 Feb 2008 19:55:16 +0000 (19:55 +0000)
Add Transform initiator to do pattern-based conversions.

git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2729 cb58f699-b61c-0410-a6fe-9272a202ed29

adfs/adfs.cpp
shibsp/handler/SessionInitiator.h
shibsp/handler/impl/ChainingSessionInitiator.cpp
shibsp/handler/impl/SAML2SessionInitiator.cpp
shibsp/handler/impl/SAMLDSSessionInitiator.cpp
shibsp/handler/impl/SessionInitiator.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp
shibsp/handler/impl/TransformSessionInitiator.cpp [new file with mode: 0644]
shibsp/handler/impl/WAYFSessionInitiator.cpp
shibsp/shibsp-lite.vcproj
shibsp/shibsp.vcproj

index b2be93b..ea2a356 100644 (file)
@@ -128,7 +128,7 @@ namespace {
         }
 
         void receive(DDF& in, ostream& out);
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
     private:
         pair<bool,long> doRequest(
@@ -307,10 +307,10 @@ extern "C" void ADFS_EXPORTS xmltooling_extension_term()
     */
 }
 
-pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // We have to know the IdP to function.
-    if (!entityID || !*entityID)
+    if (entityID.empty())
         return make_pair(false,0L);
 
     string target;
@@ -372,16 +372,16 @@ pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, const char* entity
             target = option;
     }
 
-    m_log.debug("attempting to initiate session using ADFS with provider (%s)", entityID);
+    m_log.debug("attempting to initiate session using ADFS with provider (%s)", entityID.c_str());
 
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
-        return doRequest(app, request, entityID, ACSloc.c_str(), target);
+        return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), target);
 
     // Remote the call.
     DDF out,in = DDF(m_address.c_str()).structure();
     DDFJanitor jin(in), jout(out);
     in.addmember("application_id").string(app.getId());
-    in.addmember("entity_id").string(entityID);
+    in.addmember("entity_id").string(entityID.c_str());
     in.addmember("acsLocation").string(ACSloc.c_str());
     if (!target.empty())
         in.addmember("RelayState").string(target.c_str());
index bd939dc..a201c0a 100644 (file)
@@ -52,7 +52,7 @@ namespace shibsp {
          * @param isHandler true iff executing in the context of a direct handler invocation
          * @return  a pair containing a "request completed" indicator and a server-specific response code
          */
-        virtual std::pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const=0;
+        virtual std::pair<bool,long> run(SPRequest& request, std::string& entityID, bool isHandler=true) const=0;
 
         std::pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
 
@@ -80,6 +80,9 @@ namespace shibsp {
 
     /** SessionInitiator that supports Shibboleth V1 WAYF redirects when no IdP is supplied. */
     #define WAYF_SESSION_INITIATOR "WAYF"
+    
+    /** SessionInitiator that attempts a sequence of transforms of an input until an entityID is found. */
+    #define TRANSFORM_SESSION_INITIATOR "Transform"
 };
 
 #endif /* __shibsp_initiator_h__ */
index a5f1235..dcd29d6 100644 (file)
@@ -48,7 +48,7 @@ namespace shibsp {
             for_each(m_handlers.begin(), m_handlers.end(), xmltooling::cleanup<SessionInitiator>());
         }
         
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
 #ifndef SHIBSP_LITE
         void generateMetadata(opensaml::saml2md::SPSSODescriptor& role, const char* handlerURL) const {
@@ -108,7 +108,7 @@ ChainingSessionInitiator::ChainingSessionInitiator(const DOMElement* e, const ch
     }
 }
 
-pair<bool,long> ChainingSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> ChainingSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     pair<bool,long> ret;
     for (vector<SessionInitiator*>::const_iterator i = m_handlers.begin(); i!=m_handlers.end(); ++i) {
index 6556f0a..644772c 100644 (file)
@@ -71,7 +71,7 @@ namespace shibsp {
         
         void setParent(const PropertySet* parent);
         void receive(DDF& in, ostream& out);
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
     private:
         pair<bool,long> doRequest(
@@ -204,7 +204,7 @@ void SAML2SessionInitiator::setParent(const PropertySet* parent)
     }
 }
 
-pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // First check for ECP support, since that doesn't require an IdP to be known.
     bool ECP = false;
@@ -215,7 +215,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
     }
 
     // We have to know the IdP to function unless this is ECP.
-    if (!ECP && (!entityID || !*entityID))
+    if (!ECP && (entityID.empty()))
         return make_pair(false,0L);
 
     string target;
@@ -308,7 +308,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
     if (ECP)
         m_log.debug("attempting to initiate session using SAML 2.0 Enhanced Client Profile");
     else
-        m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID);
+        m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID.c_str());
 
     if (!ACS) {
         if (ECP) {
@@ -345,7 +345,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
                     target = option;
             }
             return doRequest(
-                app, request, entityID,
+                app, request, entityID.c_str(),
                 ACS ? ACS->getXMLString("index").second : NULL, NULL, NULL,
                 isPassive, forceAuthn,
                 acClass.first ? acClass.second : NULL,
@@ -370,7 +370,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
         }
 
         return doRequest(
-            app, request, entityID,
+            app, request, entityID.c_str(),
             NULL, ACSloc.c_str(), ACS ? ACS->getXMLString("Binding").second : NULL,
             isPassive, forceAuthn,
             acClass.first ? acClass.second : NULL,
@@ -383,8 +383,8 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
     DDF out,in = DDF(m_address.c_str()).structure();
     DDFJanitor jin(in), jout(out);
     in.addmember("application_id").string(app.getId());
-    if (entityID)
-        in.addmember("entity_id").string(entityID);
+    if (!entityID.empty())
+        in.addmember("entity_id").string(entityID.c_str());
     if (isPassive)
         in.addmember("isPassive").integer(1);
     else if (forceAuthn)
index 6084d76..90d189c 100644 (file)
@@ -66,7 +66,7 @@ namespace shibsp {
         }
         virtual ~SAMLDSSessionInitiator() {}
         
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
 #ifndef SHIBSP_LITE
         void generateMetadata(SPSSODescriptor& role, const char* handlerURL) const {
@@ -111,11 +111,11 @@ namespace shibsp {
 
 };
 
-pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> SAMLDSSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a DS
     // anytime the IdP's metadata was wrong.
-    if (entityID && *entityID)
+    if (!entityID.empty())
         return make_pair(false,0L);
 
     string target;
index 48f71b1..3bdefc1 100644 (file)
@@ -34,6 +34,7 @@ namespace shibsp {
     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAML2SessionInitiatorFactory;
     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory WAYFSessionInitiatorFactory;
     SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory SAMLDSSessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager< SessionInitiator,string,pair<const DOMElement*,const char*> >::Factory TransformSessionInitiatorFactory;
 };
 
 void SHIBSP_API shibsp::registerSessionInitiators()
@@ -43,7 +44,7 @@ void SHIBSP_API shibsp::registerSessionInitiators()
     conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory);
     conf.SessionInitiatorManager.registerFactory(SAML2_SESSION_INITIATOR, SAML2SessionInitiatorFactory);
     conf.SessionInitiatorManager.registerFactory(WAYF_SESSION_INITIATOR, WAYFSessionInitiatorFactory);
-    conf.SessionInitiatorManager.registerFactory(SAMLDS_SESSION_INITIATOR, SAMLDSSessionInitiatorFactory);
+    conf.SessionInitiatorManager.registerFactory(TRANSFORM_SESSION_INITIATOR, TransformSessionInitiatorFactory);
 }
 
 pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
@@ -65,5 +66,6 @@ pair<bool,long> SessionInitiator::run(SPRequest& request, bool isHandler) const
     if (!entityID || !*entityID)
         entityID=getString("entityID").second;
 
-    return run(request, entityID, isHandler);
+    string copy(entityID ? entityID : "");
+    return run(request, copy, isHandler);
 }
index 8d5e065..38fb2d3 100644 (file)
@@ -66,7 +66,7 @@ namespace shibsp {
         
         void setParent(const PropertySet* parent);
         void receive(DDF& in, ostream& out);
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
     private:
         pair<bool,long> doRequest(
@@ -103,10 +103,10 @@ void Shib1SessionInitiator::setParent(const PropertySet* parent)
     }
 }
 
-pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // We have to know the IdP to function.
-    if (!entityID || !*entityID)
+    if (entityID.empty())
         return make_pair(false,0L);
 
     string target;
@@ -162,16 +162,16 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, const char* entit
             target = option;
     }
 
-    m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID);
+    m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID.c_str());
 
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
-        return doRequest(app, request, entityID, ACSloc.c_str(), target);
+        return doRequest(app, request, entityID.c_str(), ACSloc.c_str(), target);
 
     // Remote the call.
     DDF out,in = DDF(m_address.c_str()).structure();
     DDFJanitor jin(in), jout(out);
     in.addmember("application_id").string(app.getId());
-    in.addmember("entity_id").string(entityID);
+    in.addmember("entity_id").string(entityID.c_str());
     in.addmember("acsLocation").string(ACSloc.c_str());
     if (!target.empty())
         in.addmember("RelayState").string(target.c_str());
diff --git a/shibsp/handler/impl/TransformSessionInitiator.cpp b/shibsp/handler/impl/TransformSessionInitiator.cpp
new file mode 100644 (file)
index 0000000..6b377ac
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ *  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.
+ */
+
+/**
+ * TransformSessionInitiator.cpp
+ * 
+ * Support for mapping input into an entityID using a transform.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "ServiceProvider.h"
+#include "SPRequest.h"
+#include "handler/AbstractHandler.h"
+#include "handler/RemotedHandler.h"
+#include "handler/SessionInitiator.h"
+#include "util/SPConstants.h"
+
+#ifndef SHIBSP_LITE
+# include <saml/saml2/metadata/Metadata.h>
+#endif
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/signature/KeyInfo.h>
+#include <xmltooling/util/URLEncoder.h>
+
+using namespace shibsp;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_DLLLOCAL TransformSINodeFilter : public DOMNodeFilter
+    {
+    public:
+        short acceptNode(const DOMNode* node) const {
+            if (XMLString::equals(node->getLocalName(), xmlsignature::Transform::LOCAL_NAME))
+                return FILTER_REJECT;
+            return FILTER_ACCEPT;
+        }
+    };
+
+    static SHIBSP_DLLLOCAL TransformSINodeFilter g_TSINFilter;
+
+    class SHIBSP_DLLLOCAL TransformSessionInitiator : public SessionInitiator, public AbstractHandler, public RemotedHandler
+    {
+    public:
+        TransformSessionInitiator(const DOMElement* e, const char* appId)
+                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.Transform"), &g_TSINFilter), m_appId(appId) {
+            // If Location isn't set, defer address registration until the setParent call.
+            pair<bool,const char*> loc = getString("Location");
+            if (loc.first) {
+                string address = m_appId + loc.second + "::run::TransformSI";
+                setAddress(address.c_str());
+            }
+
+#ifndef SHIBSP_LITE
+            if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+                e = XMLHelper::getFirstChildElement(e, xmlsignature::Transform::LOCAL_NAME);
+                while (e) {
+                    if (e->hasChildNodes()) {
+                        auto_ptr_char temp(e->getFirstChild()->getNodeValue());
+                        m_transforms.push_back(temp.get());
+                    }
+                    e = XMLHelper::getNextSiblingElement(e, xmlsignature::Transform::LOCAL_NAME);
+                }
+            }
+#endif
+        }
+
+        virtual ~TransformSessionInitiator() {}
+        
+        void setParent(const PropertySet* parent);
+        void receive(DDF& in, ostream& out);
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
+
+    private:
+        void doRequest(const Application& application, string& entityID) const;
+        string m_appId;
+#ifndef SHIBSP_LITE
+        vector<string> m_transforms;
+#endif
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    SessionInitiator* SHIBSP_DLLLOCAL TransformSessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new TransformSessionInitiator(p.first, p.second);
+    }
+
+};
+
+void TransformSessionInitiator::setParent(const PropertySet* parent)
+{
+    DOMPropertySet::setParent(parent);
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::TransformSI";
+        setAddress(address.c_str());
+    }
+    else {
+        m_log.warn("no Location property in Transform SessionInitiator (or parent), can't register as remoted handler");
+    }
+}
+
+pair<bool,long> TransformSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
+{
+    // We have to have a candidate name to function.
+    if (entityID.empty())
+        return make_pair(false,0L);
+
+    string target;
+    const Application& app=request.getApplication();
+
+    m_log.debug("attempting to transform input (%s) into a valid entityID", entityID.c_str());
+
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
+        doRequest(app, entityID);
+    else {
+        // Remote the call.
+        DDF out,in = DDF(m_address.c_str()).structure();
+        DDFJanitor jin(in), jout(out);
+        in.addmember("application_id").string(app.getId());
+        in.addmember("entity_id").string(entityID.c_str());
+    
+        // Remote the processing.
+        out = request.getServiceProvider().getListenerService()->send(in);
+        if (out.isstring())
+            entityID = out.string();
+    }
+    
+    return make_pair(false,0L);
+}
+
+void TransformSessionInitiator::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) to generate AuthnRequest", aid ? aid : "(missing)");
+        throw ConfigurationException("Unable to locate application for new session, deleted?");
+    }
+
+    const char* entityID = in["entity_id"].string();
+    if (!entityID)
+        throw ConfigurationException("No entityID parameter supplied to remoted SessionInitiator.");
+
+    string copy(entityID);
+    doRequest(*app, copy);
+    DDF ret = DDF(NULL).string(copy.c_str());
+    DDFJanitor jout(ret);
+    out << ret;
+}
+
+void TransformSessionInitiator::doRequest(const Application& application, string& entityID) const
+{
+#ifndef SHIBSP_LITE
+    MetadataProvider* m=application.getMetadataProvider();
+    Locker locker(m);
+
+    // First check the original value, it might be valid already.
+    MetadataProvider::Criteria mc(entityID.c_str(), &IDPSSODescriptor::ELEMENT_QNAME);
+    pair<const EntityDescriptor*,const RoleDescriptor*> entity = m->getEntityDescriptor(mc);
+    if (entity.first)
+        return;
+
+    // Guess not, try each transform.
+    string transform;
+    for (vector<string>::const_iterator t = m_transforms.begin(); t != m_transforms.end(); ++t) {
+        transform = *t;
+        string::size_type pos = transform.find("$entityID");
+        if (pos == string::npos)
+            continue;
+        transform.replace(pos, 9, entityID);
+        m_log.debug("attempting lookup with entityID (%s)", transform.c_str());
+    
+        mc.entityID_ascii = transform.c_str();
+        entity = m->getEntityDescriptor(mc);
+        if (entity.first) {
+            m_log.info("transformed entityID from (%s) to (%s)", entityID.c_str(), transform.c_str());
+            entityID = transform;
+            return;
+        }
+    }
+
+    m_log.warn("unable to find a valid entityID based on the supplied input");
+#endif
+}
index 60c6352..19510ec 100644 (file)
@@ -55,7 +55,7 @@ namespace shibsp {
         }
         virtual ~WAYFSessionInitiator() {}
         
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
     private:
         const char* m_url;
@@ -72,11 +72,11 @@ namespace shibsp {
 
 };
 
-pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF
     // anytime the IdP's metadata was wrong.
-    if (entityID && *entityID)
+    if (!entityID.empty())
         return make_pair(false,0L);
 
     string target;
index 498c712..cdfd090 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\TransformSessionInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\WAYFSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
index 38ae4c3..d638725 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\TransformSessionInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\WAYFSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r