[cpp-sp] branch master updated: SSPCPP-799 - Listener remoting map needs to be synchronized SSPCPP-350 - Better modulariziation in vhosted environments

Scott Cantor cantor.2 at osu.edu
Wed May 9 16:07:29 EDT 2018


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository cpp-sp.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=7b43f446464aa631fc16d72e313f47286a37561f

The following commit(s) were added to refs/heads/master by this push:
       new  7b43f44   SSPCPP-799 - Listener remoting map needs to be synchronized SSPCPP-350 - Better modulariziation in vhosted environments
7b43f44 is described below

commit 7b43f446464aa631fc16d72e313f47286a37561f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed May 9 15:51:55 2018 -0400

    SSPCPP-799 - Listener remoting map needs to be synchronized
    SSPCPP-350 - Better modulariziation in vhosted environments
    
    Add locking to address maps.
    Fix problem with dereg for failed Application loads.
    Add a dynamic override lookup feature using external files.
    Fix some handlers registering non-app-specific addresses.
---
 schemas/shibboleth-3.0-native-sp-config.xsd |  13 +-
 shibsp/ServiceProvider.cpp                  |  31 -----
 shibsp/ServiceProvider.h                    |  18 +--
 shibsp/handler/impl/AssertionLookup.cpp     |   9 +-
 shibsp/handler/impl/ExternalAuthHandler.cpp |  31 +++--
 shibsp/handler/impl/RemotedHandler.cpp      |  14 +-
 shibsp/impl/XMLApplication.cpp              |  26 ++--
 shibsp/impl/XMLApplication.h                |   8 +-
 shibsp/impl/XMLServiceProvider.cpp          | 191 ++++++++++++++++++++++++++--
 shibsp/impl/XMLServiceProvider.h            |  27 +++-
 shibsp/remoting/ListenerService.h           |   9 +-
 shibsp/remoting/impl/ListenerService.cpp    |  23 ++--
 12 files changed, 288 insertions(+), 112 deletions(-)

diff --git a/schemas/shibboleth-3.0-native-sp-config.xsd b/schemas/shibboleth-3.0-native-sp-config.xsd
index 77c2073..79a237a 100644
--- a/schemas/shibboleth-3.0-native-sp-config.xsd
+++ b/schemas/shibboleth-3.0-native-sp-config.xsd
@@ -428,7 +428,8 @@
         <element name="AttributeResolver" type="conf:PluggableType"/>
         <element name="AttributeFilter" type="conf:PluggableType"/>
         <element name="CredentialResolver" type="conf:PluggableType"/>
-        <element name="ApplicationOverride" type="conf:ApplicationOverrideType"/>
+        <element ref="conf:ApplicationOverride"/>
+        <element name="ExternalApplicationOverrides" type="conf:ExternalApplicationOverridesType"/>
       </choice>
     </sequence>
     <attribute name="id" type="conf:string" fixed="default"/>
@@ -438,6 +439,8 @@
     <anyAttribute namespace="##other" processContents="lax"/>
   </complexType>
 
+  <element name="ApplicationOverride" type="conf:ApplicationOverrideType"/>
+
   <complexType name="ApplicationOverrideType">
     <annotation>
       <documentation>Container for application-specific overrides</documentation>
@@ -463,6 +466,14 @@
     <anyAttribute namespace="##other" processContents="lax"/>
   </complexType>
 
+  <complexType name="ExternalApplicationOverridesType">
+    <annotation>
+      <documentation>Externalized application overrides.</documentation>
+    </annotation>
+    <sequence/>
+    <attribute name="path" type="conf:string" use="required" />
+  </complexType>
+  
   <attributeGroup name="ApplicationGroup">
     <attribute name="homeURL" type="anyURI"/>
     <attribute name="policyId" type="conf:string"/>
diff --git a/shibsp/ServiceProvider.cpp b/shibsp/ServiceProvider.cpp
index ec13bfe..aacbba1 100644
--- a/shibsp/ServiceProvider.cpp
+++ b/shibsp/ServiceProvider.cpp
@@ -283,37 +283,6 @@ ServiceProvider::~ServiceProvider()
 {
 }
 
-Remoted* ServiceProvider::regListener(const char* address, Remoted* listener)
-{
-    Remoted* ret = nullptr;
-    map<string,Remoted*>::const_iterator i = m_listenerMap.find(address);
-    if (i != m_listenerMap.end())
-        ret = i->second;
-    m_listenerMap[address] = listener;
-    Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").info("registered remoted message endpoint (%s)",address);
-    return ret;
-}
-
-bool ServiceProvider::unregListener(const char* address, Remoted* current, Remoted* restore)
-{
-    map<string,Remoted*>::const_iterator i = m_listenerMap.find(address);
-    if (i != m_listenerMap.end() && i->second == current) {
-        if (restore)
-            m_listenerMap[address] = restore;
-        else
-            m_listenerMap.erase(address);
-        Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").info("unregistered remoted message endpoint (%s)",address);
-        return true;
-    }
-    return false;
-}
-
-Remoted* ServiceProvider::lookupListener(const char *address) const
-{
-    map<string,Remoted*>::const_iterator i = m_listenerMap.find(address);
-    return (i == m_listenerMap.end()) ? nullptr : i->second;
-}
-
 pair<bool,long> ServiceProvider::doAuthentication(SPRequest& request, bool handler) const
 {
 #ifdef _DEBUG
diff --git a/shibsp/ServiceProvider.h b/shibsp/ServiceProvider.h
index d386c88..2aca228 100644
--- a/shibsp/ServiceProvider.h
+++ b/shibsp/ServiceProvider.h
@@ -30,7 +30,6 @@
 #include <shibsp/util/PropertySet.h>
 
 #include <set>
-#include <vector>
 #include <xmltooling/Lockable.h>
 
 namespace xmltooling {
@@ -210,23 +209,21 @@ namespace shibsp {
         virtual std::pair<bool,long> doHandler(SPRequest& request) const;
 
         /**
-         * Register for a message. Returns existing remote service, allowing message hooking.
+         * Register for a message.
          *
-         * @param address   message address to register
-         * @param svc       pointer to remote service
-         * @return  previous service registered for message, if any
+         * @param address       message address to register
+         * @param svc           pointer to remote service
          */
-        virtual Remoted* regListener(const char* address, Remoted* svc);
+        virtual void regListener(const char* address, Remoted* svc)=0;
 
         /**
          * Unregisters service from an address, possibly restoring an original.
          *
          * @param address   message address to modify
          * @param current   pointer to unregistering service
-         * @param restore   service to "restore" registration for
          * @return  true iff the current service was still registered
          */
-        virtual bool unregListener(const char* address, Remoted* current, Remoted* restore=nullptr);
+        virtual bool unregListener(const char* address, Remoted* current)=0;
 
         /**
          * Returns current service registered at an address, if any.
@@ -234,14 +231,11 @@ namespace shibsp {
          * @param address message address to access
          * @return  registered service, or nullptr
          */
-        virtual Remoted* lookupListener(const char* address) const;
+        virtual Remoted* lookupListener(const char* address) const=0;
 
     protected:
         /** The AuthTypes to "recognize" (defaults to "shibboleth"). */
         std::set<std::string> m_authTypes;
-
-    private:
-        std::map<std::string,Remoted*> m_listenerMap;
     };
 
 #if defined (_MSC_VER)
diff --git a/shibsp/handler/impl/AssertionLookup.cpp b/shibsp/handler/impl/AssertionLookup.cpp
index 9c58b73..e1a02e4 100644
--- a/shibsp/handler/impl/AssertionLookup.cpp
+++ b/shibsp/handler/impl/AssertionLookup.cpp
@@ -86,7 +86,14 @@ namespace shibsp {
 AssertionLookup::AssertionLookup(const DOMElement* e, const char* appId)
     : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".Handler.AssertionLookup"), "exportACL", "127.0.0.1 ::1")
 {
-    setAddress("run::AssertionLookup");
+    pair<bool,const char*> prop = getString("Location");
+    if (!prop.first)
+        throw ConfigurationException("AssertionLookup handler requires Location property.");
+    string address(appId);
+    if (*prop.second != '/')
+        address += '/';
+    address += prop.second;
+    setAddress(address.c_str());
 }
 
 pair<bool,long> AssertionLookup::run(SPRequest& request, bool isHandler) const
diff --git a/shibsp/handler/impl/ExternalAuthHandler.cpp b/shibsp/handler/impl/ExternalAuthHandler.cpp
index 30e2519..d3204d9 100644
--- a/shibsp/handler/impl/ExternalAuthHandler.cpp
+++ b/shibsp/handler/impl/ExternalAuthHandler.cpp
@@ -165,7 +165,12 @@ namespace {
 ExternalAuth::ExternalAuth(const DOMElement* e, const char* appId)
     : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".Handler.ExternalAuth"), "acl", "127.0.0.1 ::1")
 {
-    setAddress("run::ExternalAuth");
+    pair<bool,const char*> prop = getString("Location");
+    if (!prop.first)
+        throw ConfigurationException("ExternalAuth handler requires Location property.");
+    string address(appId);
+    address += prop.second;
+    setAddress(address.c_str());
 }
 
 pair<bool,long> ExternalAuth::run(SPRequest& request, bool isHandler) const
@@ -202,7 +207,7 @@ pair<bool,long> ExternalAuth::run(SPRequest& request, bool isHandler) const
             return unwrap(request, out);
         }
     }
-    catch (std::exception& ex) {
+    catch (const std::exception& ex) {
         m_log.error("error while processing request: %s", ex.what());
         istringstream msg("External Authentication Failed");
         return make_pair(true, request.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_ERROR));
@@ -234,7 +239,7 @@ void ExternalAuth::receive(DDF& in, ostream& out)
     try {
         processMessage(*app, *req, *resp, in, &ret);
     }
-    catch (std::exception& ex) {
+    catch (const std::exception& ex) {
         m_log.error("raising exception: %s", ex.what());
         throw;
     }
@@ -489,7 +494,7 @@ pair<bool,long> ExternalAuth::processMessage(
                     }
                 }
             }
-            catch (std::exception&) {
+            catch (const std::exception&) {
                 for_each(resolvedAttributes.begin(), resolvedAttributes.end(), xmltooling::cleanup<shibsp::Attribute>());
                 throw;
             }
@@ -561,7 +566,7 @@ pair<bool,long> ExternalAuth::processMessage(
     try {
         recoverRelayState(application, httpRequest, httpResponse, target);
     }
-    catch (std::exception& ex) {
+    catch (const std::exception& ex) {
         m_log.error("error recovering relay state: %s", ex.what());
         target.erase();
     }
@@ -701,7 +706,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
                             *id = mprefix.second + *id;
                     }
                 }
-                catch (std::exception& ex) {
+                catch (const std::exception& ex) {
                     m_log.error("caught exception extracting attributes: %s", ex.what());
                 }
             }
@@ -713,7 +718,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
             try {
                 extractor->extractAttributes(application, request, issuer, *nameid, resolvedAttributes);
             }
-            catch (std::exception& ex) {
+            catch (const std::exception& ex) {
                 m_log.error("caught exception extracting attributes: %s", ex.what());
             }
         }
@@ -722,7 +727,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
             try {
                 extractor->extractAttributes(application, request, issuer, *statement, resolvedAttributes);
             }
-            catch (std::exception& ex) {
+            catch (const std::exception& ex) {
                 m_log.error("caught exception extracting attributes: %s", ex.what());
             }
         }
@@ -733,7 +738,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
                 try {
                     extractor->extractAttributes(application, request, issuer, *t, resolvedAttributes);
                 }
-                catch (std::exception& ex) {
+                catch (const std::exception& ex) {
                     m_log.error("caught exception extracting attributes: %s", ex.what());
                 }
             }
@@ -746,7 +751,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
             try {
                 filter->filterAttributes(fc, resolvedAttributes);
             }
-            catch (std::exception& ex) {
+            catch (const std::exception& ex) {
                 m_log.error("caught exception filtering attributes: %s", ex.what());
                 m_log.error("dumping extracted attributes due to filtering exception");
                 for_each(resolvedAttributes.begin(), resolvedAttributes.end(), xmltooling::cleanup<shibsp::Attribute>());
@@ -786,7 +791,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
             return ctx.release();
         }
     }
-    catch (std::exception& ex) {
+    catch (const std::exception& ex) {
         m_log.error("attribute resolution failed: %s", ex.what());
     }
 
@@ -794,7 +799,7 @@ ResolutionContext* ExternalAuth::resolveAttributes(
         try {
             return new DummyContext(resolvedAttributes);
         }
-        catch (bad_alloc&) {
+        catch (const bad_alloc&) {
             for_each(resolvedAttributes.begin(), resolvedAttributes.end(), xmltooling::cleanup<shibsp::Attribute>());
         }
     }
@@ -819,7 +824,7 @@ LoginEvent* ExternalAuth::newLoginEvent(const Application& application, const HT
             m_log.warn("unable to audit event, log event object was of an incorrect type");
         }
     }
-    catch (std::exception& ex) {
+    catch (const std::exception& ex) {
         m_log.warn("exception auditing event: %s", ex.what());
     }
     return nullptr;
diff --git a/shibsp/handler/impl/RemotedHandler.cpp b/shibsp/handler/impl/RemotedHandler.cpp
index 9d5556a..348d0c4 100644
--- a/shibsp/handler/impl/RemotedHandler.cpp
+++ b/shibsp/handler/impl/RemotedHandler.cpp
@@ -334,13 +334,8 @@ void RemotedHandler::setAddress(const char* address)
         throw ConfigurationException("Cannot register a remoting address twice for the same Handler.");
     m_address = address;
     SPConfig& conf = SPConfig::getConfig();
-    if (!conf.isEnabled(SPConfig::InProcess)) {
-        ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
-        if (listener)
-            listener->regListener(m_address.c_str(), this);
-        else
-            Category::getInstance(SHIBSP_LOGCAT ".Handler").info("no ListenerService available, handler remoting disabled");
-    }
+    if (conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
+        conf.getServiceProvider()->regListener(address, this);
 }
 
 set<string> RemotedHandler::m_remotedHeaders;
@@ -352,9 +347,8 @@ RemotedHandler::RemotedHandler()
 RemotedHandler::~RemotedHandler()
 {
     SPConfig& conf = SPConfig::getConfig();
-    ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
-    if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
-        listener->unregListener(m_address.c_str(),this);
+    if (conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
+        conf.getServiceProvider()->unregListener(m_address.c_str(), this);
 }
 
 void RemotedHandler::addRemotedHeader(const char* header)
diff --git a/shibsp/impl/XMLApplication.cpp b/shibsp/impl/XMLApplication.cpp
index 944f0fb..58c9e05 100644
--- a/shibsp/impl/XMLApplication.cpp
+++ b/shibsp/impl/XMLApplication.cpp
@@ -84,6 +84,7 @@ namespace {
     static const XMLCh Channel[]=               UNICODE_LITERAL_7(C,h,a,n,n,e,l);
     static const XMLCh _CredentialResolver[] =  UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);
     static const XMLCh _default[] =             UNICODE_LITERAL_7(d,e,f,a,u,l,t);
+    static const XMLCh ExternalApplicationOverrides[] = UNICODE_LITERAL_28(E,x,t,e,r,n,a,l,A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e,s);
     static const XMLCh _Handler[] =             UNICODE_LITERAL_7(H,a,n,d,l,e,r);
     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);
     static const XMLCh _index[] =               UNICODE_LITERAL_5(i,n,d,e,x);
@@ -107,8 +108,9 @@ XMLApplication::XMLApplication(
     const ServiceProvider* sp,
     const ProtocolProvider* pp,
     DOMElement* e,
-    const XMLApplication* base
-    ) : Application(sp), m_base(base), m_acsDefault(nullptr), m_sessionInitDefault(nullptr), m_artifactResolutionDefault(nullptr)
+    const XMLApplication* base,
+    DOMDocument* doc
+    ) : Application(sp), m_base(base), m_acsDefault(nullptr), m_sessionInitDefault(nullptr), m_artifactResolutionDefault(nullptr), m_doc(doc)
 {
 #ifdef _DEBUG
     xmltooling::NDC ndc("XMLApplication");
@@ -326,24 +328,19 @@ XMLApplication::XMLApplication(
 
     // Out of process only, we register a listener endpoint.
     if (!conf.isEnabled(SPConfig::InProcess)) {
-        ListenerService* listener = sp->getListenerService(false);
-        if (listener) {
-            string addr=string(getId()) + "::getHeaders::Application";
-            listener->regListener(addr.c_str(), this);
-        }
-        else {
-            log.info("no ListenerService available, Application remoting disabled");
-        }
+        string addr=string(getId()) + "::getHeaders::Application";
+        const_cast<ServiceProvider*>(sp)->regListener(addr.c_str(), this);
     }
 }
 
 XMLApplication::~XMLApplication()
 {
-    ListenerService* listener=getServiceProvider().getListenerService(false);
-    if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
         string addr=string(getId()) + "::getHeaders::Application";
-        listener->unregListener(addr.c_str(), this);
+        const_cast<ServiceProvider&>(getServiceProvider()).unregListener(addr.c_str(), this);
     }
+    if (m_doc)
+        m_doc->release();
 }
 
 template <class T> T* XMLApplication::doChainedPlugins(
@@ -1140,7 +1137,8 @@ DOMNodeFilter::FilterAction XMLApplication::acceptNode(const DOMNode* node) cons
         XMLString::equals(name, _CredentialResolver) ||
         XMLString::equals(name, _AttributeFilter) ||
         XMLString::equals(name, _AttributeExtractor) ||
-        XMLString::equals(name, _AttributeResolver)) {
+        XMLString::equals(name, _AttributeResolver) ||
+        XMLString::equals(name, ExternalApplicationOverrides)) {
         return FILTER_REJECT;
     }
 
diff --git a/shibsp/impl/XMLApplication.h b/shibsp/impl/XMLApplication.h
index 043f405..57edeae 100644
--- a/shibsp/impl/XMLApplication.h
+++ b/shibsp/impl/XMLApplication.h
@@ -77,7 +77,12 @@ namespace shibsp {
         : public Application, public Remoted, public DOMPropertySet, public xercesc::DOMNodeFilter
     {
     public:
-        XMLApplication(const ServiceProvider*, const ProtocolProvider*, xercesc::DOMElement*, const XMLApplication* base=nullptr);
+        XMLApplication(
+            const ServiceProvider*,
+            const ProtocolProvider*,
+            xercesc::DOMElement*,
+            const XMLApplication* base=nullptr,
+            xercesc::DOMDocument* doc=nullptr);
         virtual ~XMLApplication();
 
         const char* getHash() const {
@@ -221,6 +226,7 @@ namespace shibsp {
         } m_redirectLimit;
 
         std::vector<std::string> m_redirectWhitelist;
+        xercesc::DOMDocument* m_doc;
     };
 
 #if defined (_MSC_VER)
diff --git a/shibsp/impl/XMLServiceProvider.cpp b/shibsp/impl/XMLServiceProvider.cpp
index 6ef74b3..7661001 100644
--- a/shibsp/impl/XMLServiceProvider.cpp
+++ b/shibsp/impl/XMLServiceProvider.cpp
@@ -41,11 +41,14 @@
 #else
 # error "Supported logging library not available."
 #endif
+#include <fstream>
 #include <boost/algorithm/string.hpp>
 #include <boost/tuple/tuple.hpp>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/version.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/ParserPool.h>
+#include <xmltooling/util/PathResolver.h>
 #include <xmltooling/util/TemplateEngine.h>
 #include <xmltooling/util/Threads.h>
 #include <xmltooling/util/XMLHelper.h>
@@ -88,6 +91,7 @@ namespace {
     static const XMLCh _DataSealer[] =          UNICODE_LITERAL_10(D,a,t,a,S,e,a,l,e,r);
     static const XMLCh _default[] =             UNICODE_LITERAL_7(d,e,f,a,u,l,t);
     static const XMLCh _Extensions[] =          UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);
+    static const XMLCh ExternalApplicationOverrides[] = UNICODE_LITERAL_28(E,x,t,e,r,n,a,l,A,p,p,l,i,c,a,t,i,o,n,O,v,e,r,r,i,d,e,s);
     static const XMLCh _fatal[] =               UNICODE_LITERAL_5(f,a,t,a,l);
     static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);
     static const XMLCh InProcess[] =            UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);
@@ -304,7 +308,7 @@ void XMLConfigImpl::doCaching(const DOMElement* e, XMLConfig* conf, Category& lo
     }
 }
 
-XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log) : m_document(nullptr)
+XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer, Category& log) : m_document(nullptr), m_defaultApplication(nullptr)
 {
 #ifdef _DEBUG
     xmltooling::NDC ndc("XMLConfigImpl");
@@ -531,17 +535,16 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
     }
 #endif
 
-    scoped_ptr<ProtocolProvider> pp;
     if (conf.isEnabled(SPConfig::Handlers)) {
         if (child = XMLHelper::getLastChildElement(e, _ProtocolProvider)) {
             string t(XMLHelper::getAttrString(child, nullptr, _type));
             if (!t.empty()) {
                 log.info("building ProtocolProvider of type %s...", t.c_str());
-                pp.reset(conf.ProtocolProviderManager.newPlugin(t.c_str(), child));
+                m_protocolProvider.reset(conf.ProtocolProviderManager.newPlugin(t.c_str(), child));
             }
         }
     }
-    Locker pplocker(pp.get());
+    Locker pplocker(m_protocolProvider.get());
 
     // Load the default application.
     child = XMLHelper::getLastChildElement(e, ApplicationDefaults);
@@ -549,21 +552,38 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
         log.fatal("can't build default Application object, missing conf:ApplicationDefaults element?");
         throw ConfigurationException("can't build default Application object, missing conf:ApplicationDefaults element?");
     }
-    boost::shared_ptr<XMLApplication> defapp(new XMLApplication(outer, pp.get(), child));
+    boost::shared_ptr<XMLApplication> defapp(new XMLApplication(outer, m_protocolProvider.get(), child));
     m_appmap[defapp->getId()] = defapp;
+    m_defaultApplication = defapp.get();
 
     // Load any overrides.
-    child = XMLHelper::getFirstChildElement(child, ApplicationOverride);
-    while (child) {
-        boost::shared_ptr<XMLApplication> iapp(new XMLApplication(outer, pp.get(), child, defapp.get()));
+    DOMElement* override = XMLHelper::getFirstChildElement(child, ApplicationOverride);
+    while (override) {
+        boost::shared_ptr<XMLApplication> iapp(new XMLApplication(outer, m_protocolProvider.get(), override, defapp.get()));
         if (m_appmap.count(iapp->getId()))
             log.crit("found conf:ApplicationOverride element with duplicate id attribute (%s), skipping it", iapp->getId());
         else
             m_appmap[iapp->getId()] = iapp;
 
-        child = XMLHelper::getNextSiblingElement(child, ApplicationOverride);
+        override = XMLHelper::getNextSiblingElement(override, ApplicationOverride);
+    }
+
+    // Save off any external override paths.
+    override = XMLHelper::getFirstChildElement(child, ExternalApplicationOverrides);
+    while (override) {
+        string extoverridepath(XMLHelper::getAttrString(override, nullptr, _path));
+        XMLToolingConfig::getConfig().getPathResolver()->resolve(extoverridepath, PathResolver::XMLTOOLING_CFG_FILE);
+        if (!extoverridepath.empty()) {
+            log.info("adding external ApplicationOverride search path: %s", extoverridepath.c_str());
+            m_externalAppPaths.push_back(extoverridepath);
+        }
+
+        override = XMLHelper::getNextSiblingElement(override, ApplicationOverride);
     }
 
+    if (!m_externalAppPaths.empty())
+        m_appMapLock.reset(Mutex::create());
+
     // Check for extra AuthTypes to recognize.
     if (conf.isEnabled(SPConfig::InProcess)) {
         const PropertySet* inprocs = getPropertySet("InProcess");
@@ -579,6 +599,64 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
     }
 }
 
+boost::shared_ptr<Application> XMLConfigImpl::findExternalOverride(const char* id, const XMLConfig* config)
+{
+    Locker pplocker(m_protocolProvider.get());
+
+    for (vector<string>::const_iterator i = m_externalAppPaths.begin(); i != m_externalAppPaths.end(); ++i) {
+        string path(*i);
+        if (!ends_with(path, "/"))
+            path += '/';
+        path = path + id + "-override.xml";
+        try {
+            ifstream in(path.c_str());
+            if (in) {
+                DOMDocument* doc = XMLToolingConfig::getConfig().getValidatingParser().parse(in);
+                if (!XMLHelper::isNodeNamed(doc->getDocumentElement(), shibspconstants::SHIB3SPCONFIG_NS, ApplicationOverride)) {
+                    throw ConfigurationException("External override not rooted in conf:ApplicationOverride element.");
+                }
+
+                string id2(XMLHelper::getAttrString(doc->getDocumentElement(), nullptr, _id));
+                if (id2 != id)
+                    throw ConfigurationException("External override's id ($1) did not match the expected value", params(1, id2.c_str()));
+
+                boost::shared_ptr<XMLApplication> iapp(
+                    new XMLApplication(config, m_protocolProvider.get(), doc->getDocumentElement(), m_defaultApplication, doc)
+                    );
+                return iapp;
+            }
+        }
+        catch (const std::exception& ex) {
+            config->m_log.error("Exception creating ApplicationOverride: %s", ex.what());
+        }
+    }
+
+    return nullptr;
+}
+
+const Application* XMLConfig::getApplication(const char* applicationId) const
+{
+    Lock locker(m_impl->m_appMapLock);
+
+    map< string, boost::shared_ptr<Application> >::const_iterator i = m_impl->m_appmap.find(applicationId ? applicationId : "default");
+    Application* ret = (i != m_impl->m_appmap.end()) ? i->second.get() : nullptr;
+
+    if (!ret && m_impl->m_appMapLock && applicationId) {
+        m_log.info("application override (%s) not found, searching external sources", applicationId);
+        boost::shared_ptr<Application> newapp = m_impl->findExternalOverride(applicationId, this);
+        if (newapp) {
+            m_log.info("storing externally defined application override (%s)", applicationId);
+            ret = newapp.get();
+            m_impl->m_appmap[applicationId] = newapp;
+        }
+        else {
+            m_log.warn("application override (%s) not found in external sources", applicationId);
+        }
+    }
+
+    return ret;
+}
+
 #ifndef SHIBSP_LITE
 
 StorageService* XMLConfig::getStorageService(const char* id) const
@@ -721,6 +799,101 @@ void XMLConfig::receive(DDF& in, ostream& out)
 
 #endif
 
+void XMLConfig::regListener(const char* address, Remoted* listener)
+{
+    m_listenerLock->wrlock();
+    SharedLock locker(m_listenerLock, false);
+
+    map< string,pair<Remoted*,Remoted*> >::iterator i = m_listenerMap.find(address);
+    if (i != m_listenerMap.end()) {
+        if (!i->second.first) {
+            // First slot is null. Look for second slot and move up if needed.
+            if (i->second.second) {
+                i->second.first = i->second.second;
+                i->second.second = listener;
+                Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").debug("registered second remoted message endpoint (%s)",address);
+            }
+            else {
+                // Both slots null, so put into first slot.
+                i->second.first = listener;
+                Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").debug("registered remoted message endpoint (%s)",address);
+            }
+        }
+        else if (!i->second.second) {
+            // First slot occupied, so put into empty second slot.
+            i->second.second = listener;
+            Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").debug("registered second remoted message endpoint (%s)",address);
+        }
+        else {
+            // This should never happen...?
+            throw new ConfigurationException("Attempted to register more than two endpoints for a single listener address.");
+        }
+    }
+    else {
+        // Stick it in the first slot.
+        m_listenerMap[address] = pair<Remoted*, Remoted*>(listener, nullptr);
+        Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").debug("registered remoted message endpoint (%s)",address);
+    }
+}
+
+bool XMLConfig::unregListener(const char* address, Remoted* current)
+{
+    m_listenerLock->wrlock();
+    SharedLock locker(m_listenerLock, false);
+
+    map< string,pair<Remoted*,Remoted*> >::iterator i = m_listenerMap.find(address);
+    if (i != m_listenerMap.end()) {
+        if (i->second.first == current) {
+            if (i->second.second) {
+                // Promote second slot to first.
+                i->second.first = i->second.second;
+                i->second.second = nullptr;
+            }
+            else {
+                // Remove entirely.
+                m_listenerMap.erase(address);
+            }
+        }
+        else if (i->second.second = current) {
+            if (!i->second.first)
+                m_listenerMap.erase(address);
+            else
+                i->second.second = nullptr;
+        }
+        else {
+            return false;
+        }
+        Category::getInstance(SHIBSP_LOGCAT ".ServiceProvider").debug("unregistered remoted message endpoint (%s)", address);
+        return true;
+    }
+    return false;
+}
+
+Remoted* XMLConfig::lookupListener(const char* address) const
+{
+    SharedLock locker(m_listenerLock, true);
+    map< string,pair<Remoted*,Remoted*> >::const_iterator i = m_listenerMap.find(address);
+    if (i != m_listenerMap.end())
+        return i->second.first ? i->second.first : i->second.second;
+
+    const char* colons = strstr(address, "::");
+    if (colons) {
+        string appId(address, colons - address);
+        locker.release()->unlock();   // free up the listener map
+        getApplication(appId.c_str());
+        SharedLock sublocker(m_listenerLock, true); // relock and check again
+        i = m_listenerMap.find(address);
+        if (i != m_listenerMap.end())
+            return i->second.first ? i->second.first : i->second.second;
+    }
+    return nullptr;
+}
+
+XMLConfig::XMLConfig(const DOMElement* e)
+    : ReloadableXMLFile(e, xmltooling::logging::Category::getInstance(SHIBSP_LOGCAT ".Config")), m_listenerLock(RWLock::create())
+{
+}
+
 XMLConfig::~XMLConfig()
 {
     shutdown();
diff --git a/shibsp/impl/XMLServiceProvider.h b/shibsp/impl/XMLServiceProvider.h
index cfb1554..0b5e22b 100644
--- a/shibsp/impl/XMLServiceProvider.h
+++ b/shibsp/impl/XMLServiceProvider.h
@@ -44,6 +44,11 @@
 # include <boost/tuple/tuple.hpp>
 #endif
 
+namespace xmltooling {
+    class Mutex;
+    class RWLock;
+}
+
 namespace shibsp {
 
 #if defined (_MSC_VER)
@@ -66,8 +71,14 @@ namespace shibsp {
         boost::scoped_ptr<SecurityPolicyProvider> m_policy;
         std::vector< boost::tuple<std::string, std::string, std::string> > m_transportOptions;
 #endif
+        std::map<std::string,Remoted*> m_listenerMap;
         boost::scoped_ptr<RequestMapper> m_requestMapper;
+        boost::scoped_ptr<ProtocolProvider> m_protocolProvider;
+        boost::scoped_ptr<xmltooling::Mutex> m_appMapLock;
         std::map< std::string, boost::shared_ptr<Application> > m_appmap;
+        std::vector<std::string> m_externalAppPaths;
+
+        boost::shared_ptr<Application> findExternalOverride(const char*, const XMLConfig*);
 
         // Provides filter to exclude special config elements.
         xercesc::DOMNodeFilter::FilterAction acceptNode(const xercesc::DOMNode* node) const;
@@ -82,6 +93,7 @@ namespace shibsp {
         void doCaching(const xercesc::DOMElement*, XMLConfig*, xmltooling::logging::Category&);
 
         xercesc::DOMDocument* m_document;
+        const XMLApplication* m_defaultApplication;
     };
 
     class SHIBSP_DLLLOCAL XMLConfig : public ServiceProvider, public xmltooling::ReloadableXMLFile
@@ -90,7 +102,7 @@ namespace shibsp {
 #endif
     {
     public:
-        XMLConfig(const xercesc::DOMElement* e) : ReloadableXMLFile(e, xmltooling::logging::Category::getInstance(SHIBSP_LOGCAT ".Config")) {}
+        XMLConfig(const xercesc::DOMElement* e);
         virtual ~XMLConfig();
 
         void init() {
@@ -150,10 +162,7 @@ namespace shibsp {
             return m_impl->m_requestMapper.get();
         }
 
-        const Application* getApplication(const char* applicationId) const {
-            std::map< std::string, boost::shared_ptr<Application> >::const_iterator i = m_impl->m_appmap.find(applicationId ? applicationId : "default");
-            return (i != m_impl->m_appmap.end()) ? i->second.get() : nullptr;
-        }
+        const Application* getApplication(const char* applicationId) const;
 
 #ifndef SHIBSP_LITE
         SecurityPolicyProvider* getSecurityPolicyProvider(bool required=true) const {
@@ -165,11 +174,19 @@ namespace shibsp {
         bool setTransportOptions(xmltooling::SOAPTransport& transport) const;
 #endif
 
+        void regListener(const char* address, Remoted* svc);
+        bool unregListener(const char* address, Remoted* current);
+        Remoted* lookupListener(const char* address) const;
+
     protected:
         std::pair<bool,xercesc::DOMElement*> background_load();
 
     private:
         friend class XMLConfigImpl;
+
+        boost::scoped_ptr<xmltooling::RWLock> m_listenerLock;
+        std::map< std::string,std::pair<Remoted*,Remoted*> > m_listenerMap;
+
         // The order of these members actually matters. If we want to rely on auto-destruction, then
         // anything dependent on anything else has to come later in the object so it will pop first.
         // Storage is the lowest, then remoting, then the cache, and finally the rest.
diff --git a/shibsp/remoting/ListenerService.h b/shibsp/remoting/ListenerService.h
index 8f5b2ef..8e115b3 100644
--- a/shibsp/remoting/ListenerService.h
+++ b/shibsp/remoting/ListenerService.h
@@ -33,6 +33,7 @@
 #include <boost/scoped_ptr.hpp>
 
 namespace xmltooling {
+    class RWLock;
     class ThreadKey;
 }
 
@@ -111,26 +112,23 @@ namespace shibsp {
         DDF* getInput() const;
 
         // Remoted classes register and unregister for messages using these methods.
-        // Registration returns any existing listeners, allowing message hooking.
 
         /**
          * Register for a message. Returns existing remote service, allowing message hooking.
          *
          * @param address   message address to register
          * @param svc       pointer to remote service
-         * @return  previous service registered for message, if any
          */
-        virtual Remoted* regListener(const char* address, Remoted* svc);
+        virtual void regListener(const char* address, Remoted* svc);
 
         /**
          * Unregisters service from an address, possibly restoring an original.
          *
          * @param address   message address to modify
          * @param current   pointer to unregistering service
-         * @param restore   service to "restore" registration for
          * @return  true iff the current service was still registered
          */
-        virtual bool unregListener(const char* address, Remoted* current, Remoted* restore=nullptr);
+        virtual bool unregListener(const char* address, Remoted* current);
 
         /**
          * Returns current service registered at an address, if any.
@@ -169,6 +167,7 @@ namespace shibsp {
 
     private:
         std::map<std::string,Remoted*> m_listenerMap;
+        boost::scoped_ptr<xmltooling::RWLock> m_listenerLock;
         boost::scoped_ptr<xmltooling::ThreadKey> m_threadLocalKey;
     };
 
diff --git a/shibsp/remoting/impl/ListenerService.cpp b/shibsp/remoting/impl/ListenerService.cpp
index 386943e..f70b1a6 100644
--- a/shibsp/remoting/impl/ListenerService.cpp
+++ b/shibsp/remoting/impl/ListenerService.cpp
@@ -62,7 +62,7 @@ Remoted::~Remoted()
 {
 }
 
-ListenerService::ListenerService() : m_threadLocalKey(ThreadKey::create(nullptr))
+ListenerService::ListenerService() : m_listenerLock(RWLock::create()), m_threadLocalKey(ThreadKey::create(nullptr))
 {
 }
 
@@ -70,26 +70,28 @@ ListenerService::~ListenerService()
 {
 }
 
-Remoted* ListenerService::regListener(const char* address, Remoted* listener)
+void ListenerService::regListener(const char* address, Remoted* listener)
 {
+    m_listenerLock->wrlock();
+    SharedLock locker(m_listenerLock, false);
+
     Remoted* ret=nullptr;
     map<string,Remoted*>::const_iterator i=m_listenerMap.find(address);
     if (i!=m_listenerMap.end())
         ret=i->second;
     m_listenerMap[address]=listener;
-    Category::getInstance(SHIBSP_LOGCAT ".Listener").info("registered remoted message endpoint (%s)",address);
-    return ret;
+    Category::getInstance(SHIBSP_LOGCAT ".Listener").debug("registered remoted message endpoint (%s)",address);
 }
 
-bool ListenerService::unregListener(const char* address, Remoted* current, Remoted* restore)
+bool ListenerService::unregListener(const char* address, Remoted* current)
 {
+    m_listenerLock->wrlock();
+    SharedLock locker(m_listenerLock, false);
+
     map<string,Remoted*>::const_iterator i=m_listenerMap.find(address);
     if (i!=m_listenerMap.end() && i->second==current) {
-        if (restore)
-            m_listenerMap[address]=restore;
-        else
-            m_listenerMap.erase(address);
-        Category::getInstance(SHIBSP_LOGCAT ".Listener").info("unregistered remoted message endpoint (%s)",address);
+        m_listenerMap.erase(address);
+        Category::getInstance(SHIBSP_LOGCAT ".Listener").debug("unregistered remoted message endpoint (%s)",address);
         return true;
     }
     return false;
@@ -97,6 +99,7 @@ bool ListenerService::unregListener(const char* address, Remoted* current, Remot
 
 Remoted* ListenerService::lookup(const char *address) const
 {
+    SharedLock locker(m_listenerLock, true);
     map<string,Remoted*>::const_iterator i=m_listenerMap.find(address);
     return (i==m_listenerMap.end()) ? nullptr : i->second;
 }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list