https://issues.shibboleth.net/jira/browse/SSPCPP-254
authorScott Cantor <cantor.2@osu.edu>
Sat, 18 Sep 2010 02:54:22 +0000 (02:54 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sat, 18 Sep 2010 02:54:22 +0000 (02:54 +0000)
saml/Makefile.am
saml/saml.rc
saml/saml.vcxproj
saml/saml.vcxproj.filters
saml/saml2/metadata/DiscoverableMetadataProvider.h [new file with mode: 0644]
saml/saml2/metadata/impl/ChainingMetadataProvider.cpp
saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp [new file with mode: 0644]
saml/saml2/metadata/impl/XMLMetadataProvider.cpp

index addb22d..a0dd1dd 100644 (file)
@@ -84,6 +84,7 @@ saml2bindinclude_HEADERS = \
        
 saml2mdinclude_HEADERS = \
        saml2/metadata/AbstractMetadataProvider.h \
+       saml2/metadata/DiscoverableMetadataProvider.h \
        saml2/metadata/DynamicMetadataProvider.h \
        saml2/metadata/EndpointManager.h \
        saml2/metadata/Metadata.h \
@@ -142,6 +143,7 @@ libsaml_la_SOURCES = \
        saml2/metadata/impl/AbstractMetadataProvider.cpp \
        saml2/metadata/impl/BlacklistMetadataFilter.cpp \
        saml2/metadata/impl/ChainingMetadataProvider.cpp \
+       saml2/metadata/impl/DiscoverableMetadataProvider.cpp \
        saml2/metadata/impl/DynamicMetadataProvider.cpp \
        saml2/metadata/impl/EntityRoleMetadataFilter.cpp \
        saml2/metadata/impl/MetadataCredentialContext.cpp \
index b32b3f2..559837d 100644 (file)
@@ -53,7 +53,7 @@ BEGIN
 #else\r
             VALUE "InternalName", "saml2_4\0"\r
 #endif\r
-            VALUE "LegalCopyright", "Copyright © 2009 Internet2\0"\r
+            VALUE "LegalCopyright", "Copyright © 2010 Internet2\0"\r
             VALUE "LegalTrademarks", "\0"\r
 #ifdef _DEBUG\r
             VALUE "OriginalFilename", "saml2_4D.dll\0"\r
index 2c3416b..c817340 100644 (file)
     </Link>\r
   </ItemDefinitionGroup>\r
   <ItemGroup>\r
+    <ClCompile Include="saml2\metadata\impl\DiscoverableMetadataProvider.cpp" />\r
     <ClCompile Include="SAMLConfig.cpp" />\r
     <ClCompile Include="util\CommonDomainCookie.cpp" />\r
     <ClCompile Include="util\SAMLConstants.cpp" />\r
     <ClInclude Include="exceptions.h" />\r
     <ClInclude Include="internal.h" />\r
     <ClInclude Include="RootObject.h" />\r
+    <ClInclude Include="saml2\metadata\DiscoverableMetadataProvider.h" />\r
     <ClInclude Include="SAMLConfig.h" />\r
     <ClInclude Include="version.h" />\r
     <ClInclude Include="util\CommonDomainCookie.h" />\r
index 9c9d9bc..7bef27d 100644 (file)
     <ClCompile Include="profile\impl\IgnoreRule.cpp">\r
       <Filter>Source Files\profile\impl</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="saml2\metadata\impl\DiscoverableMetadataProvider.cpp">\r
+      <Filter>Source Files\saml2\metadata\impl</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="Assertion.h">\r
     <ClInclude Include="binding\SOAPClient.h">\r
       <Filter>Header Files\binding</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="saml2\metadata\DiscoverableMetadataProvider.h">\r
+      <Filter>Header Files\saml2\metadata</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="saml.rc">\r
diff --git a/saml/saml2/metadata/DiscoverableMetadataProvider.h b/saml/saml2/metadata/DiscoverableMetadataProvider.h
new file mode 100644 (file)
index 0000000..a9bdb5a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  Copyright 2010 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.
+ */
+
+/**
+ * @file saml/saml2/metadata/DiscoverableMetadataProvider.h
+ * 
+ * A metadata provider that provides a JSON feed of IdP discovery information.
+ */
+
+#ifndef __saml2_discometadataprov_h__
+#define __saml2_discometadataprov_h__
+
+#include <saml/saml2/metadata/MetadataProvider.h>
+
+namespace opensaml {
+    
+    namespace saml2md {
+        
+#if defined (_MSC_VER)
+        #pragma warning( push )
+        #pragma warning( disable : 4251 )
+#endif
+        /**
+         * A metadata provider that provides a JSON feed of IdP discovery information.
+         */
+        class SAML_API DiscoverableMetadataProvider : public virtual MetadataProvider
+        {
+        protected:
+            DiscoverableMetadataProvider();
+            
+            /**
+             * Generates a JSON feed of IdP discovery information for the current metadata.
+             * <p>The provider <strong>MUST</strong> be write-locked.
+             */
+            virtual void generateFeed();
+
+        public:
+            virtual ~DiscoverableMetadataProvider();
+
+            /**
+             * Returns the ETag associated with the cached feed.
+             * <p>The provider <strong>MUST</strong> be locked.
+             *
+             * @return the ETag value for the current feed state
+             */
+            virtual std::string getCacheTag() const;
+
+            /**
+             * Outputs the cached feed.
+             * <p>The provider <strong>MUST</strong> be locked.
+             *
+             * @param os    stream to output feed into
+             * @return a reference to the output stream
+             */
+            virtual std::ostream& outputFeed(std::ostream& os) const;
+
+        protected:
+            /** Storage for feed. */
+            std::string m_feed;
+
+            /** ETag for feed. */
+            mutable std::string m_feedTag;
+        };
+
+#if defined (_MSC_VER)
+        #pragma warning( pop )
+#endif
+
+    };
+};
+
+#endif /* __saml2_discometadataprov_h__ */
index 617c8e6..3e5f312 100644 (file)
@@ -24,6 +24,7 @@
 #include "exceptions.h"
 #include "saml/binding/SAMLArtifact.h"
 #include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/DiscoverableMetadataProvider.h"
 #include "saml2/metadata/ObservableMetadataProvider.h"
 #include "saml2/metadata/MetadataCredentialCriteria.h"
 
@@ -48,7 +49,7 @@ namespace opensaml {
         struct SAML_DLLLOCAL tracker_t;
         
         class SAML_DLLLOCAL ChainingMetadataProvider
-            : public ObservableMetadataProvider, public ObservableMetadataProvider::Observer {
+            : public DiscoverableMetadataProvider, public ObservableMetadataProvider, public ObservableMetadataProvider::Observer {
         public:
             ChainingMetadataProvider(const xercesc::DOMElement* e=nullptr);
             virtual ~ChainingMetadataProvider();
@@ -62,11 +63,42 @@ namespace opensaml {
             const XMLObject* getMetadata() const;
             const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const;
             pair<const EntityDescriptor*,const RoleDescriptor*> getEntityDescriptor(const Criteria& criteria) const;
-            void onEvent(const ObservableMetadataProvider& provider) const;
     
             const Credential* resolve(const CredentialCriteria* criteria=nullptr) const;
             vector<const Credential*>::size_type resolve(vector<const Credential*>& results, const CredentialCriteria* criteria=nullptr) const;
 
+            string getCacheTag() const {
+                Lock lock(m_trackerLock);
+                return m_feedTag;
+            }
+
+            ostream& outputFeed(ostream& os) const {
+                os << "[\n";
+                // Lock each provider in turn and suck in its feed.
+                for (vector<MetadataProvider*>::const_iterator m = m_providers.begin(); m != m_providers.end(); ++m) {
+                    DiscoverableMetadataProvider* d = dynamic_cast<DiscoverableMetadataProvider*>(*m);
+                    if (d) {
+                        Locker locker(d);
+                        d->outputFeed(os);
+                    }
+                }
+                os << "]\n";
+                return os;
+            }
+
+            void onEvent(const ObservableMetadataProvider& provider) const {
+                // Reset the cache tag for the feed.
+                Lock lock(m_trackerLock);
+                SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4);
+                m_feedTag = SAMLArtifact::toHex(m_feedTag);
+                emitChangeEvent();
+            }
+
+        protected:
+            void generateFeed() {
+                // No-op.
+            }
+
         private:
             bool m_firstMatch;
             mutable Mutex* m_trackerLock;
@@ -171,11 +203,6 @@ ChainingMetadataProvider::~ChainingMetadataProvider()
     for_each(m_providers.begin(), m_providers.end(), xmltooling::cleanup<MetadataProvider>());
 }
 
-void ChainingMetadataProvider::onEvent(const ObservableMetadataProvider& provider) const
-{
-    emitChangeEvent();
-}
-
 void ChainingMetadataProvider::init()
 {
     for (vector<MetadataProvider*>::const_iterator i=m_providers.begin(); i!=m_providers.end(); ++i) {
@@ -186,6 +213,10 @@ void ChainingMetadataProvider::init()
             m_log.crit("failure initializing MetadataProvider: %s", ex.what());
         }
     }
+
+    // Set an initial cache tag for the state of the plugins.
+    SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4);
+    m_feedTag = SAMLArtifact::toHex(m_feedTag);
 }
 
 Lockable* ChainingMetadataProvider::lock()
diff --git a/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp b/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp
new file mode 100644 (file)
index 0000000..eaec6cc
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ *  Copyright 2010 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.
+ */
+
+/**
+ * DiscoverableMetadataProvider.cpp
+ *
+ * A metadata provider that provides a JSON feed of IdP discovery information.
+ */
+
+#include "internal.h"
+#include "binding/SAMLArtifact.h"
+#include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/DiscoverableMetadataProvider.h"
+
+#include <fstream>
+#include <xmltooling/logging.h>
+#include <xmltooling/XMLToolingConfig.h>
+
+using namespace opensaml::saml2md;
+using namespace xmltooling;
+using namespace std;
+
+namespace {
+    void disco(string& s, const EntityDescriptor* entity) {
+        if (entity) {
+            const vector<IDPSSODescriptor*>& idps = entity->getIDPSSODescriptors();
+            if (!idps.empty()) {
+                auto_ptr_char entityid(entity->getEntityID());
+                // Open a struct and output id: entityID.
+                s += "{\n \"id\": \"";
+                s += entityid.get();
+                s += '\"';
+                for (vector<IDPSSODescriptor*>::const_iterator idp = idps.begin(); idp != idps.end(); ++idp) {
+                    if ((*idp)->getExtensions()) {
+                        const vector<XMLObject*>& exts =  const_cast<const Extensions*>((*idp)->getExtensions())->getUnknownXMLObjects();
+                        for (vector<XMLObject*>::const_iterator ext = exts.begin(); ext != exts.end(); ++ext) {
+                            const UIInfo* info = dynamic_cast<UIInfo*>(*ext);
+                            if (info) {
+                                const vector<DisplayName*>& dispnames = info->getDisplayNames();
+                                if (!dispnames.empty()) {
+                                    s += ",\n \"names\": [";
+                                    for (vector<DisplayName*>::const_iterator dispname = dispnames.begin(); dispname != dispnames.end(); ++dispname) {
+                                        if (dispname != dispnames.begin())
+                                            s += ',';
+                                        auto_ptr_char dn((*dispname)->getName());
+                                        auto_ptr_char lang((*dispname)->getLang());
+                                        s += "\n  {\n  \"name\": \"";
+                                        s += dn.get();
+                                        s += "\",\n  \"lang\": \"";
+                                        s += lang.get();
+                                        s += "\"\n  }";
+                                    }
+                                    s += "\n ]";
+                                }
+
+                                const vector<Description*>& descs = info->getDescriptions();
+                                if (!descs.empty()) {
+                                    s += ",\n \"descs\": [";
+                                    for (vector<Description*>::const_iterator desc = descs.begin(); desc != descs.end(); ++desc) {
+                                        if (desc != descs.begin())
+                                            s += ',';
+                                        auto_ptr_char d((*desc)->getDescription());
+                                        auto_ptr_char lang((*desc)->getLang());
+                                        s += "\n  {\n  \"desc\": \"";
+                                        s += d.get();
+                                        s += "\",\n  \"lang\": \"";
+                                        s += lang.get();
+                                        s += "\"\n  }";
+                                    }
+                                    s += "\n ]";
+                                }
+
+                                const vector<InformationURL*>& infurls = info->getInformationURLs();
+                                if (!infurls.empty()) {
+                                    s += ",\n \"infolinks\": [";
+                                    for (vector<InformationURL*>::const_iterator infurl = infurls.begin(); infurl != infurls.end(); ++infurl) {
+                                        if (infurl != infurls.begin())
+                                            s += ',';
+                                        auto_ptr_char iu((*infurl)->getURL());
+                                        auto_ptr_char lang((*infurl)->getLang());
+                                        s += "\n  {\n  \"url\": \"";
+                                        s += iu.get();
+                                        s += "\",\n  \"lang\": \"";
+                                        s += lang.get();
+                                        s += "\"\n  }";
+                                    }
+                                    s += "\n ]";
+                                }
+
+                                const vector<PrivacyStatementURL*>& privs = info->getPrivacyStatementURLs();
+                                if (!privs.empty()) {
+                                    s += ",\n \"privlinks\": [";
+                                    for (vector<PrivacyStatementURL*>::const_iterator priv = privs.begin(); priv != privs.end(); ++priv) {
+                                        if (priv != privs.begin())
+                                            s += ',';
+                                        auto_ptr_char pu((*priv)->getURL());
+                                        auto_ptr_char lang((*priv)->getLang());
+                                        s += "\n  {\n  \"url\": \"";
+                                        s += pu.get();
+                                        s += "\",\n  \"lang\": \"";
+                                        s += lang.get();
+                                        s += "\"\n  }";
+                                    }
+                                    s += "\n ]";
+                                }
+
+                                const vector<Logo*>& logos = info->getLogos();
+                                if (!logos.empty()) {
+                                    s += ",\n \"logos\": [";
+                                    for (vector<Logo*>::const_iterator logo = logos.begin(); logo != logos.end(); ++logo) {
+                                        if (logo != logos.begin())
+                                            s += ',';
+                                        s += "\n  {\n";
+                                        auto_ptr_char imgsrc((*logo)->getURL());
+                                        s += "  \"imgsrc\": \"";
+                                        s += imgsrc.get();
+                                        s += "\",\n  \"height\": \"";
+                                        s += (*logo)->getHeight().second;
+                                        s += "\",\n  \"width\": \"";
+                                        s += (*logo)->getWidth().second;
+                                        s += '\"';
+                                        if ((*logo)->getLang()) {
+                                            auto_ptr_char lang((*logo)->getLang());
+                                            s += ",\n  \"lang\": \"";
+                                            s += lang.get();
+                                            s += '\"';
+                                        }
+                                        s += "\n  }";
+                                    }
+                                    s += "\n ]";
+                                }
+                            }
+                        }
+                    }
+                }
+                // Close the struct;
+                s += "\n},\n";
+            }
+        }
+    }
+
+    void disco(string& s, const EntitiesDescriptor* group) {
+        if (group) {
+            const vector<EntitiesDescriptor*>& groups = group->getEntitiesDescriptors();
+            for (vector<EntitiesDescriptor*>::const_iterator i = groups.begin(); i != groups.end(); ++i)
+                disco(s, *i);
+
+            const vector<EntityDescriptor*>& sites = group->getEntityDescriptors();
+            for (vector<EntityDescriptor*>::const_iterator j = sites.begin(); j != sites.end(); ++j)
+                disco(s, *j);
+        }
+    }
+}
+
+DiscoverableMetadataProvider::DiscoverableMetadataProvider()
+{
+}
+
+DiscoverableMetadataProvider::~DiscoverableMetadataProvider()
+{
+}
+
+void DiscoverableMetadataProvider::generateFeed()
+{
+    m_feed = "[\n";
+    const XMLObject* object = getMetadata();
+    disco(m_feed, dynamic_cast<const EntitiesDescriptor*>(object));
+    disco(m_feed, dynamic_cast<const EntityDescriptor*>(object));
+    m_feed += " { }\n]\n";
+
+    SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4);
+    m_feedTag = SAMLArtifact::toHex(m_feedTag);
+}
+
+string DiscoverableMetadataProvider::getCacheTag() const
+{
+    return m_feedTag;
+}
+
+ostream& DiscoverableMetadataProvider::outputFeed(ostream& os) const
+{
+    return os << m_feed;
+}
index df85913..3d88264 100644 (file)
 #include "saml2/metadata/Metadata.h"
 #include "saml2/metadata/MetadataFilter.h"
 #include "saml2/metadata/AbstractMetadataProvider.h"
+#include "saml2/metadata/DiscoverableMetadataProvider.h"
 
 #include <fstream>
+#include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/io/HTTPResponse.h>
 #include <xmltooling/util/NDC.h>
+#include <xmltooling/util/PathResolver.h>
 #include <xmltooling/util/ReloadableXMLFile.h>
 #include <xmltooling/util/Threads.h>
 #include <xmltooling/validation/ValidatorSuite.h>
@@ -46,7 +49,8 @@ using namespace std;
 namespace opensaml {
     namespace saml2md {
 
-        class SAML_DLLLOCAL XMLMetadataProvider : public AbstractMetadataProvider, public ReloadableXMLFile
+        class SAML_DLLLOCAL XMLMetadataProvider
+            : public AbstractMetadataProvider, public DiscoverableMetadataProvider, public ReloadableXMLFile
         {
         public:
             XMLMetadataProvider(const DOMElement* e);
@@ -81,6 +85,7 @@ namespace opensaml {
             time_t computeNextRefresh();
 
             XMLObject* m_object;
+            bool m_discoveryFeed;
             double m_refreshDelayFactor;
             unsigned int m_backoffFactor;
             time_t m_minRefreshDelay,m_maxRefreshDelay,m_lastValidUntil;
@@ -91,6 +96,7 @@ namespace opensaml {
             return new XMLMetadataProvider(e);
         }
 
+        static const XMLCh discoveryFeed[] =        UNICODE_LITERAL_13(d,i,s,c,o,v,e,r,y,F,e,e,d);
         static const XMLCh minRefreshDelay[] =      UNICODE_LITERAL_15(m,i,n,R,e,f,r,e,s,h,D,e,l,a,y);
         static const XMLCh refreshDelayFactor[] =   UNICODE_LITERAL_18(r,e,f,r,e,s,h,D,e,l,a,y,F,a,c,t,o,r);
     };
@@ -102,7 +108,8 @@ namespace opensaml {
 
 XMLMetadataProvider::XMLMetadataProvider(const DOMElement* e)
     : AbstractMetadataProvider(e), ReloadableXMLFile(e, Category::getInstance(SAML_LOGCAT".MetadataProvider.XML"), false),
-        m_object(nullptr), m_refreshDelayFactor(0.75), m_backoffFactor(1),
+        m_object(nullptr), m_discoveryFeed(XMLHelper::getAttrBool(e, true, discoveryFeed)),
+        m_refreshDelayFactor(0.75), m_backoffFactor(1),
         m_minRefreshDelay(XMLHelper::getAttrInt(e, 600, minRefreshDelay)),
         m_maxRefreshDelay(m_reloadInterval), m_lastValidUntil(SAMLTIME_MAX)
 {
@@ -200,6 +207,8 @@ pair<bool,DOMElement*> XMLMetadataProvider::load(bool backup)
     m_object = xmlObject.release();
     m_lastValidUntil = SAMLTIME_MAX;
     index(m_lastValidUntil);
+    if (m_discoveryFeed)
+        generateFeed();
     if (changed)
         emitChangeEvent();
 
@@ -252,17 +261,6 @@ pair<bool,DOMElement*> XMLMetadataProvider::background_load()
     }
 }
 
-void XMLMetadataProvider::index(time_t& validUntil)
-{
-    clearDescriptorIndex();
-    EntitiesDescriptor* group=dynamic_cast<EntitiesDescriptor*>(m_object);
-    if (group) {
-        indexGroup(group, validUntil);
-        return;
-    }
-    indexEntity(dynamic_cast<EntityDescriptor*>(m_object), validUntil);
-}
-
 time_t XMLMetadataProvider::computeNextRefresh()
 {
     time_t now = time(nullptr);
@@ -290,3 +288,14 @@ time_t XMLMetadataProvider::computeNextRefresh()
         return ret;
     }
 }
+
+void XMLMetadataProvider::index(time_t& validUntil)
+{
+    clearDescriptorIndex();
+    EntitiesDescriptor* group=dynamic_cast<EntitiesDescriptor*>(m_object);
+    if (group) {
+        indexGroup(group, validUntil);
+        return;
+    }
+    indexEntity(dynamic_cast<EntityDescriptor*>(m_object), validUntil);
+}