Add option to search all providers and warn on duplicates.
authorScott Cantor <cantor.2@osu.edu>
Thu, 8 Nov 2007 20:52:06 +0000 (20:52 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 8 Nov 2007 20:52:06 +0000 (20:52 +0000)
saml/saml2/metadata/ChainingMetadataProvider.h
saml/saml2/metadata/impl/ChainingMetadataProvider.cpp

index 2e24646..1310374 100644 (file)
@@ -24,6 +24,7 @@
 #define __saml_chainmeta_h__
 
 #include <saml/saml2/metadata/ObservableMetadataProvider.h>
+#include <xmltooling/logging.h>
 #include <xmltooling/util/Threads.h>
 
 namespace opensaml {
@@ -103,8 +104,10 @@ namespace opensaml {
                 ) const;
 
         private:
+            bool m_firstMatch;
             xmltooling::ThreadKey* m_tlsKey;
             std::vector<MetadataProvider*> m_providers;
+            xmltooling::logging::Category& m_log;
         };
 
 #if defined (_MSC_VER)
index 9f90400..f1e0a09 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "internal.h"
 #include "exceptions.h"
+#include "saml/binding/SAMLArtifact.h"
 #include "saml2/metadata/ChainingMetadataProvider.h"
 
 #include <xercesc/util/XMLUniDefs.hpp>
@@ -46,17 +47,22 @@ namespace opensaml {
 };
 
 static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
+static const XMLCh precedence[] =           UNICODE_LITERAL_10(p,r,e,c,e,d,e,n,c,e);
+static const XMLCh last[] =                 UNICODE_LITERAL_4(l,a,s,t);
 static const XMLCh type[] =                 UNICODE_LITERAL_4(t,y,p,e);
 
-ChainingMetadataProvider::ChainingMetadataProvider(const DOMElement* e) : ObservableMetadataProvider(e), m_tlsKey(NULL)
+ChainingMetadataProvider::ChainingMetadataProvider(const DOMElement* e)
+    : ObservableMetadataProvider(e), m_firstMatch(true), m_tlsKey(NULL), m_log(Category::getInstance(SAML_LOGCAT".Metadata.Chaining"))
 {
-    Category& log=Category::getInstance(SAML_LOGCAT".Metadata.Chaining");
+    if (XMLString::equals(e ? e->getAttributeNS(NULL, precedence) : NULL, last))
+        m_firstMatch = false;
+
     e = e ? XMLHelper::getFirstChildElement(e, _MetadataProvider) : NULL;
     while (e) {
         auto_ptr_char temp(e->getAttributeNS(NULL,type));
         if (temp.get() && *temp.get()) {
             try {
-                log.info("building MetadataProvider of type %s", temp.get());
+                m_log.info("building MetadataProvider of type %s", temp.get());
                 auto_ptr<MetadataProvider> provider(
                     SAMLConfig::getConfig().MetadataProviderManager.newPlugin(temp.get(), e)
                     );
@@ -67,7 +73,7 @@ ChainingMetadataProvider::ChainingMetadataProvider(const DOMElement* e) : Observ
                 provider.release();
             }
             catch (exception& ex) {
-                log.error("error building MetadataProvider: %s", ex.what());
+                m_log.error("error building MetadataProvider: %s", ex.what());
             }
         }
         e = XMLHelper::getNextSiblingElement(e, _MetadataProvider);
@@ -93,7 +99,7 @@ void ChainingMetadataProvider::init()
             (*i)->init();
         }
         catch (exception& ex) {
-            Category::getInstance(SAML_LOGCAT".Metadata").error("failure initializing MetadataProvider: %s", ex.what());
+            m_log.error("failure initializing MetadataProvider: %s", ex.what());
         }
     }
 }
@@ -124,39 +130,145 @@ const EntitiesDescriptor* ChainingMetadataProvider::getEntitiesDescriptor(const
     const_cast<ChainingMetadataProvider*>(this)->unlock();
 
     // Do a search.
+    MetadataProvider* held = NULL;
     const EntitiesDescriptor* ret=NULL;
+    const EntitiesDescriptor* cur=NULL;
     for (vector<MetadataProvider*>::const_iterator i=m_providers.begin(); i!=m_providers.end(); ++i) {
         (*i)->lock();
-        if (ret=(*i)->getEntitiesDescriptor(name,requireValidMetadata)) {
-            // Save locked provider.
-            m_tlsKey->setData(*i);
-            return ret;
+        if (cur=(*i)->getEntitiesDescriptor(name,requireValidMetadata)) {
+            // Are we using a first match policy?
+            if (m_firstMatch) {
+                // Save locked provider.
+                m_tlsKey->setData(*i);
+                return cur;
+            }
+
+            // Using last match wins. Did we already have one?
+            if (held) {
+                m_log.warn("found duplicate EntitiesDescriptor (%s), using last matching copy", name);
+                held->unlock();
+            }
+
+            // Save off the latest match.
+            held = *i;
+            ret = cur;
+        }
+        else {
+            // No match, so just unlock this one and move on.
+            (*i)->unlock();
         }
-        (*i)->unlock();
     }
 
-    return NULL;
+    // Preserve any lock we're holding.
+    if (held)
+        m_tlsKey->setData(held);
+    return ret;
 }
 
 pair<const EntityDescriptor*,const RoleDescriptor*> ChainingMetadataProvider::getEntityDescriptor(const Criteria& criteria) const
 {
+    bool bRole = (criteria.role && criteria.protocol);  // searching for role also?
+
     // Clear any existing lock.
     const_cast<ChainingMetadataProvider*>(this)->unlock();
 
     // Do a search.
-    pair<const EntityDescriptor*,const RoleDescriptor*> ret;
-    ret.first = NULL;
-    ret.second = NULL;
+    MetadataProvider* held = NULL;
+    pair<const EntityDescriptor*,const RoleDescriptor*> ret = pair<const EntityDescriptor*,const RoleDescriptor*>(NULL,NULL);
+    pair<const EntityDescriptor*,const RoleDescriptor*> cur = ret;
     for (vector<MetadataProvider*>::const_iterator i=m_providers.begin(); i!=m_providers.end(); ++i) {
         (*i)->lock();
-        if ((ret=(*i)->getEntityDescriptor(criteria)).first) {
-            // Save locked provider.
-            m_tlsKey->setData(*i);
-            return ret;
+        cur = (*i)->getEntityDescriptor(criteria);
+        if (cur.first) {
+            if (bRole) {
+                // We want a role also. Did we find one?
+                if (cur.second) {
+                    // Are we using a first match policy?
+                    if (m_firstMatch) {
+                        // Save locked provider.
+                        m_tlsKey->setData(*i);
+                        return cur;
+                    }
+
+                    // Using last match wins. Did we already have one?
+                    if (held) {
+                        if (ret.second) {
+                            // We had a "complete" match, so log it.
+                            if (criteria.entityID_ascii) {
+                                m_log.warn("found duplicate EntityDescriptor (%s) with role (%s), using last matching copy",
+                                    criteria.entityID_ascii, criteria.role->toString().c_str());
+                            }
+                            else if (criteria.entityID_unicode) {
+                                auto_ptr_char temp(criteria.entityID_unicode);
+                                m_log.warn("found duplicate EntityDescriptor (%s) with role (%s), using last matching copy",
+                                    temp.get(), criteria.role->toString().c_str());
+                            }
+                            else if (criteria.artifact) {
+                                m_log.warn("found duplicate EntityDescriptor for artifact source (%s) with role (%s), using last matching copy",
+                                    criteria.artifact->getSource().c_str(), criteria.role->toString().c_str());
+                            }
+                        }
+                        held->unlock();
+                    }
+
+                    // Save off the latest match.
+                    held = *i;
+                    ret = cur;
+                }
+                else {
+                    // We didn't find the role, so we're going to keep looking,
+                    // but save this one if we didn't have the role yet.
+                    if (ret.second) {
+                        // We already had a role, so let's stick with that.
+                        (*i)->unlock();
+                    }
+                    else {
+                        // This is at least as good, so toss anything we had and keep it.
+                        if (held)
+                            held->unlock();
+                        held = *i;
+                        ret = cur;
+                    }
+                }
+            }
+            else {
+                // Are we using a first match policy?
+                if (m_firstMatch) {
+                    // Save locked provider.
+                    m_tlsKey->setData(*i);
+                    return cur;
+                }
+
+                // Using last match wins. Did we already have one?
+                if (held) {
+                    if (criteria.entityID_ascii) {
+                        m_log.warn("found duplicate EntityDescriptor (%s), using last matching copy", criteria.entityID_ascii);
+                    }
+                    else if (criteria.entityID_unicode) {
+                        auto_ptr_char temp(criteria.entityID_unicode);
+                        m_log.warn("found duplicate EntityDescriptor (%s), using last matching copy", temp.get());
+                    }
+                    else if (criteria.artifact) {
+                        m_log.warn("found duplicate EntityDescriptor for artifact source (%s), using last matching copy",
+                            criteria.artifact->getSource().c_str());
+                    }
+                    held->unlock();
+                }
+
+                // Save off the latest match.
+                held = *i;
+                ret = cur;
+            }
+        }
+        else {
+            // No match, so just unlock this one and move on.
+            (*i)->unlock();
         }
-        (*i)->unlock();
     }
 
+    // Preserve any lock we're holding.
+    if (held)
+        m_tlsKey->setData(held);
     return ret;
 }