Header clearing infrastructure.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 23 May 2007 20:25:24 +0000 (20:25 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 23 May 2007 20:25:24 +0000 (20:25 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2259 cb58f699-b61c-0410-a6fe-9272a202ed29

schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/Application.h
shibsp/ServiceProvider.cpp
shibsp/attribute/resolver/AttributeExtractor.h
shibsp/attribute/resolver/AttributeResolver.h
shibsp/attribute/resolver/impl/ChainingAttributeResolver.cpp
shibsp/attribute/resolver/impl/QueryAttributeResolver.cpp
shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
shibsp/handler/impl/RemotedHandler.cpp
shibsp/impl/XMLServiceProvider.cpp

index 5d9118d..0bf4630 100644 (file)
                                <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
                        </sequence>
                        <attribute name="logger" type="anyURI"/>
+                       <attribute name="unsetHeaderValue" type="string" use="optional"/>\r
                        <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
                        <attribute name="policyId" type="conf:string" use="required"/>\r
                        <attribute name="homeURL" type="anyURI" default="/"/>\r
                        <attribute name="REMOTE_USER" type="conf:listOfStrings"/>\r
+                       <attribute name="unsetHeaders" type="conf:listOfStrings"/>\r
                <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
                        <attribute name="policyId" type="conf:string"/>\r
                        <attribute name="homeURL" type="anyURI" default="/"/>\r
                        <attribute name="REMOTE_USER" type="conf:listOfStrings"/>\r
+                       <attribute name="unsetHeaders" type="conf:listOfStrings"/>\r
                        <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
        </element>\r
index 2b38de0..e76b839 100644 (file)
@@ -42,6 +42,7 @@ namespace shibsp {
     class SHIBSP_API Handler;
     class SHIBSP_API ServiceProvider;
     class SHIBSP_API SessionInitiator;
+    class SHIBSP_API SPRequest;
 
     /**
      * Interface to a Shibboleth Application instance.
@@ -156,6 +157,13 @@ namespace shibsp {
         virtual const std::set<std::string>& getRemoteUserAttributeIds() const=0;
 
         /**
+         * Clears any headers that may be used to hold attributes after export.
+         *
+         * @param request   SP request to clear
+         */
+        virtual void clearAttributeHeaders(SPRequest& request) const=0;
+
+        /**
          * Returns the default SessionInitiator when automatically requesting a session.
          * 
          * @return the default SessionInitiator, or NULL
index f971e4d..7067a5c 100644 (file)
@@ -86,27 +86,13 @@ namespace shibsp {
     }
     
     void SHIBSP_DLLLOCAL clearHeaders(SPRequest& request) {
-        // Clear invariant stuff.
         request.clearHeader("Shib-Identity-Provider");
         request.clearHeader("Shib-Authentication-Method");
         request.clearHeader("Shib-AuthnContext-Class");
         request.clearHeader("Shib-AuthnContext-Decl");
         request.clearHeader("Shib-Attributes");
-        request.clearHeader("Shib-Application-ID");
-    
-        // TODO: Figure out a way to clear attribute headers...
-        /*
-        AttributeExtractor* extractor = request.getApplication().getAttributeExtractor();
-        if (extractor) {
-            Locker locker(extractor);
-            extractor->clearHeaders(request);
-        }
-        AttributeResolver* resolver = request.getApplication().getAttributeResolver();
-        if (resolver) {
-            Locker locker(resolver);
-            resolver->clearHeaders(request);
-        }
-        */
+        //request.clearHeader("Shib-Application-ID");   handle inside app method
+        request.getApplication().clearAttributeHeaders(request);
     }
 };
 
index 4ae7d60..7975f8c 100644 (file)
@@ -59,6 +59,13 @@ namespace shibsp {
             const xmltooling::XMLObject& xmlObject,
             std::multimap<std::string,Attribute*>& attributes
             ) const=0;
+
+        /**
+         * Populates an array with the set of Attribute IDs that might be generated.
+         *
+         * @param attributes    array to populate
+         */
+        virtual void getAttributeIds(std::vector<std::string>& attributes) const=0;
     };
 
     /**
index 2e143b9..be1a6ba 100644 (file)
@@ -95,6 +95,13 @@ namespace shibsp {
          * @throws AttributeResolutionException thrown if there is a problem resolving the attributes for the subject
          */
         virtual void resolveAttributes(ResolutionContext& ctx) const=0;
+
+        /**
+         * Populates an array with the set of Attribute IDs that might be generated.
+         *
+         * @param attributes    array to populate
+         */
+        virtual void getAttributeIds(std::vector<std::string>& attributes) const=0;
     };
 
 #if defined (_MSC_VER)
index f2ef837..073e9e3 100644 (file)
@@ -75,7 +75,7 @@ namespace shibsp {
         void unlock() {
             for_each(m_resolvers.begin(), m_resolvers.end(), mem_fun(&AttributeResolver::unlock));
         }
-        
+
         ResolutionContext* createResolutionContext(
             const Application& application,
             const EntityDescriptor* issuer,
@@ -102,6 +102,11 @@ namespace shibsp {
 
         void resolveAttributes(ResolutionContext& ctx) const;
 
+        void getAttributeIds(vector<string>& attributes) const {
+            for (vector<AttributeResolver*>::const_iterator i=m_resolvers.begin(); i!=m_resolvers.end(); ++i)
+                (*i)->getAttributeIds(attributes);
+        }
+        
     private:
         vector<AttributeResolver*> m_resolvers;
     };
index 044230b..a6d348a 100644 (file)
@@ -184,6 +184,10 @@ namespace shibsp {
 
         void resolveAttributes(ResolutionContext& ctx) const;
 
+        void getAttributeIds(vector<string>& attributes) const {
+            // Nothing to do, only the extractor would actually generate them.
+        }
+
     private:
         bool SAML1Query(QueryContext& ctx) const;
         bool SAML2Query(QueryContext& ctx) const;
index e99c101..1604055 100644 (file)
@@ -80,12 +80,9 @@ namespace shibsp {
             const Application& application, const char* assertingParty, const saml2::Attribute& attr, multimap<string,Attribute*>& attributes
             ) const;
 
-        /*
-        void clearHeaders(SPRequest& request) const {
-            for (vector<string>::const_iterator i = m_attributeIds.begin(); i!=m_attributeIds.end(); ++i)
-                request.clearHeader(i->c_str());
+        void getAttributeIds(vector<string>& attributes) const {
+            attributes.insert(attributes.end(), m_attributeIds.begin(), m_attributeIds.end());
         }
-        */
 
     private:
         Category& m_log;
@@ -96,7 +93,7 @@ namespace shibsp {
         typedef map< pair<string,string>,pair<AttributeDecoder*,string> > attrmap_t;
 #endif
         attrmap_t m_attrMap;
-        //vector<string> m_attributeIds;
+        vector<string> m_attributeIds;
     };
     
     class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile
@@ -113,12 +110,10 @@ namespace shibsp {
             const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, multimap<string,Attribute*>& attributes
             ) const;
 
-        /*
-        void clearHeaders(SPRequest& request) const {
+        void getAttributeIds(std::vector<std::string>& attributes) const {
             if (m_impl)
-                m_impl->clearHeaders(request);
+                m_impl->getAttributeIds(attributes);
         }
-        */
 
     protected:
         pair<bool,DOMElement*> load();
@@ -230,7 +225,7 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) : m_log(l
         
         decl.first = decoder;
         decl.second = id.get();
-        //m_attributeIds.push_back(id.get());
+        m_attributeIds.push_back(id.get());
         
         child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);
     }
index 906d560..a4a88ae 100644 (file)
@@ -214,7 +214,7 @@ 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::OutOfProcess)) {
+    if (conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess)) {
         ListenerService* listener = conf.getServiceProvider()->getListenerService(false);
         if (listener)
             listener->regListener(m_address.c_str(),this);
@@ -227,7 +227,7 @@ RemotedHandler::~RemotedHandler()
 {
     SPConfig& conf = SPConfig::getConfig();
     ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
-    if (listener && conf.isEnabled(SPConfig::OutOfProcess))
+    if (listener && conf.isEnabled(SPConfig::OutOfProcess) && !conf.isEnabled(SPConfig::InProcess))
         listener->unregListener(m_address.c_str(),this);
 }
 
index ef119e2..1cc0e45 100644 (file)
@@ -74,7 +74,7 @@ namespace {
     static vector<const Handler*> g_noHandlers;\r
 \r
     // Application configuration wrapper\r
-    class SHIBSP_DLLLOCAL XMLApplication : public virtual Application, public DOMPropertySet, public DOMNodeFilter\r
+    class SHIBSP_DLLLOCAL XMLApplication : public virtual Application, public Remoted, public DOMPropertySet, public DOMNodeFilter\r
     {\r
     public:\r
         XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);\r
@@ -114,9 +114,10 @@ namespace {
         }\r
 #endif\r
         const set<string>& getRemoteUserAttributeIds() const {\r
-            return (m_attributeIds.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_attributeIds;\r
+            return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;\r
         }\r
 \r
+        void clearAttributeHeaders(SPRequest& request) const;\r
         const SessionInitiator* getDefaultSessionInitiator() const;\r
         const SessionInitiator* getSessionInitiatorById(const char* id) const;\r
         const Handler* getDefaultAssertionConsumerService() const;\r
@@ -124,6 +125,17 @@ namespace {
         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;\r
         const Handler* getHandler(const char* path) const;\r
 \r
+        void receive(DDF& in, ostream& out) {\r
+            DDF header;\r
+            DDF ret=DDF(NULL).list();
+            DDFJanitor jret(ret);
+            for (vector<string>::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i) {
+                header = DDF(NULL).string(i->c_str());
+                ret.add(header);
+            }
+            out << ret;
+        }\r
+\r
         // Provides filter to exclude special config elements.\r
         short acceptNode(const DOMNode* node) const;\r
     \r
@@ -141,7 +153,9 @@ namespace {
         CredentialResolver* m_credResolver;\r
         vector<const XMLCh*> m_audiences;\r
 #endif\r
-        set<string> m_attributeIds;\r
+        set<string> m_remoteUsers;\r
+        mutable vector<string> m_unsetHeaders;\r
+        RWLock* m_unsetLock;\r
 \r
         // manage handler objects\r
         vector<Handler*> m_handlers;\r
@@ -386,7 +400,7 @@ XMLApplication::XMLApplication(
 #ifndef SHIBSP_LITE\r
         m_metadata(NULL), m_trust(NULL), m_attrExtractor(NULL), m_attrFilter(NULL), m_attrResolver(NULL), m_credResolver(NULL), m_partyDefault(NULL),\r
 #endif\r
-        m_sessionInitDefault(NULL), m_acsDefault(NULL)\r
+        m_sessionInitDefault(NULL), m_unsetLock(NULL), m_acsDefault(NULL)\r
 {\r
 #ifdef _DEBUG\r
     xmltooling::NDC ndc("XMLApplication");\r
@@ -405,28 +419,55 @@ XMLApplication::XMLApplication(
 #endif\r
         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
 \r
-        m_hash=getId();\r
-        m_hash+=getString("entityID").second;\r
-        // TODO: some kind of non-hash method\r
-        //m_hash=samlConf.hashSHA1(m_hash.c_str(), true);\r
-\r
-        pair<bool,const char*> attributes = getString("REMOTE_USER");\r
-        if (attributes.first) {\r
-            char* dup = strdup(attributes.second);\r
-            char* pos;\r
-            char* start = dup;\r
-            while (start && *start) {\r
-                while (*start && isspace(*start))\r
-                    start++;\r
-                if (!*start)\r
-                    break;\r
-                pos = strchr(start,' ');\r
-                if (pos)\r
-                    *pos=0;\r
-                m_attributeIds.insert(start);\r
-                start = pos ? pos+1 : NULL;\r
+        // This used to be an actual hash, but now it's just a hex-encode to avoid xmlsec.\r
+        static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\r
+        string tohash=getId();\r
+        tohash+=getString("entityID").second;\r
+        for (const char* ch = tohash.c_str(); *ch; ++ch) {\r
+            m_hash += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);\r
+            m_hash += (DIGITS[0x0F & *ch]);\r
+        }\r
+\r
+        // Load attribute ID lists for REMOTE_USER and header clearing.\r
+        if (conf.isEnabled(SPConfig::InProcess)) {\r
+            pair<bool,const char*> attributes = getString("REMOTE_USER");\r
+            if (attributes.first) {\r
+                char* dup = strdup(attributes.second);\r
+                char* pos;\r
+                char* start = dup;\r
+                while (start && *start) {\r
+                    while (*start && isspace(*start))\r
+                        start++;\r
+                    if (!*start)\r
+                        break;\r
+                    pos = strchr(start,' ');\r
+                    if (pos)\r
+                        *pos=0;\r
+                    m_remoteUsers.insert(start);\r
+                    start = pos ? pos+1 : NULL;\r
+                }\r
+                free(dup);\r
+            }\r
+\r
+            attributes = getString("unsetHeaders");\r
+            if (attributes.first) {\r
+                char* dup = strdup(attributes.second);\r
+                char* pos;\r
+                char* start = dup;\r
+                while (start && *start) {\r
+                    while (*start && isspace(*start))\r
+                        start++;\r
+                    if (!*start)\r
+                        break;\r
+                    pos = strchr(start,' ');\r
+                    if (pos)\r
+                        *pos=0;\r
+                    m_unsetHeaders.push_back(start);\r
+                    start = pos ? pos+1 : NULL;\r
+                }\r
+                free(dup);\r
+                m_unsetHeaders.push_back("Shib-Application-ID");\r
             }\r
-            free(dup);\r
         }\r
 \r
         const PropertySet* sessions = getPropertySet("Sessions");\r
@@ -612,6 +653,21 @@ XMLApplication::XMLApplication(
                     log.crit("error building AttributeResolver: %s", ex.what());\r
                 }\r
             }\r
+\r
+            if (m_unsetHeaders.empty()) {\r
+                if (m_attrExtractor) {\r
+                    Locker extlock(m_attrExtractor);\r
+                    m_attrExtractor->getAttributeIds(m_unsetHeaders);\r
+                }\r
+                if (m_attrResolver) {\r
+                    Locker reslock(m_attrResolver);\r
+                    m_attrResolver->getAttributeIds(m_unsetHeaders);\r
+                }\r
+                if (m_base && m_unsetHeaders.empty())\r
+                    m_unsetHeaders.insert(m_unsetHeaders.end(), m_base->m_unsetHeaders.begin(), m_base->m_unsetHeaders.end());\r
+                else\r
+                    m_unsetHeaders.push_back("Shib-Application-ID");\r
+            }\r
         }\r
 \r
         if (conf.isEnabled(SPConfig::Credentials)) {\r
@@ -642,7 +698,21 @@ XMLApplication::XMLApplication(
                 child = XMLHelper::getNextSiblingElement(child,RelyingParty);\r
             }\r
         }\r
-#endif        \r
+#endif\r
+\r
+        // In process only, we need a shared lock around accessing the header clearing list.\r
+        if (!conf.isEnabled(SPConfig::OutOfProcess)) {\r
+            m_unsetLock = RWLock::create();\r
+        }\r
+        else 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");
+        }
     }\r
     catch (exception&) {\r
         cleanup();\r
@@ -658,6 +728,12 @@ XMLApplication::XMLApplication(
 \r
 void XMLApplication::cleanup()\r
 {\r
+    ListenerService* listener=getServiceProvider().getListenerService(false);
+    if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess) && !SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+        string addr=string(getId()) + "::getHeaders::Application";
+        listener->unregListener(addr.c_str(),this);
+    }
+    delete m_unsetLock;\r
     for_each(m_handlers.begin(),m_handlers.end(),xmltooling::cleanup<Handler>());\r
 #ifndef SHIBSP_LITE\r
     delete m_partyDefault;\r
@@ -785,6 +861,45 @@ const Handler* XMLApplication::getHandler(const char* path) const
     return m_base ? m_base->getHandler(path) : NULL;\r
 }\r
 \r
+void XMLApplication::clearAttributeHeaders(SPRequest& request) const\r
+{\r
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {\r
+        for (vector<string>::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i)\r
+            request.clearHeader(i->c_str());\r
+        return;\r
+    }\r
+\r
+    m_unsetLock->rdlock();\r
+    if (m_unsetHeaders.empty()) {\r
+        // No headers yet, so we have to request them from the remote half.\r
+        m_unsetLock->unlock();\r
+        m_unsetLock->wrlock();\r
+        if (m_unsetHeaders.empty()) {\r
+            SharedLock wrlock(m_unsetLock, false);\r
+            string addr=string(getId()) + "::getHeaders::Application";\r
+            DDF out,in = DDF(addr.c_str());
+            DDFJanitor jin(in),jout(out);
+            out = getServiceProvider().getListenerService()->send(in);
+            if (out.islist()) {
+                DDF header = out.first();
+                while (header.isstring()) {
+                    m_unsetHeaders.push_back(header.string());
+                    header = out.next();
+                }
+            }
+        }\r
+        else {\r
+            m_unsetLock->unlock();\r
+        }\r
+        m_unsetLock->rdlock();\r
+    }\r
+\r
+    // Now holding read lock.\r
+    SharedLock unsetLock(m_unsetLock, false);\r
+    for (vector<string>::const_iterator i = m_unsetHeaders.begin(); i!=m_unsetHeaders.end(); ++i)\r
+        request.clearHeader(i->c_str());\r
+}\r
+\r
 short XMLConfigImpl::acceptNode(const DOMNode* node) const\r
 {\r
     if (!XMLString::equals(node->getNamespaceURI(),shibspconstants::SHIB2SPCONFIG_NS))\r