#include "internal.h"
#include "exceptions.h"
+#include "saml/binding/SAMLArtifact.h"
#include "saml2/metadata/ChainingMetadataProvider.h"
#include <xercesc/util/XMLUniDefs.hpp>
};
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)
);
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);
(*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());
}
}
}
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;
}
-const EntityDescriptor* ChainingMetadataProvider::getEntityDescriptor(const char* id, bool requireValidMetadata) const
+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.
- const EntityDescriptor* ret=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(id,requireValidMetadata)) {
- // Save locked provider.
- m_tlsKey->setData(*i);
- return ret;
- }
- (*i)->unlock();
- }
+ 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;
+ }
- return NULL;
-}
+ // 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();
+ }
-const EntityDescriptor* ChainingMetadataProvider::getEntityDescriptor(const SAMLArtifact* artifact) const
-{
- // Clear any existing lock.
- const_cast<ChainingMetadataProvider*>(this)->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;
+ }
- // Do a search.
- const EntityDescriptor* ret=NULL;
- for (vector<MetadataProvider*>::const_iterator i=m_providers.begin(); i!=m_providers.end(); ++i) {
- (*i)->lock();
- if (ret=(*i)->getEntityDescriptor(artifact)) {
- // Save locked provider.
- m_tlsKey->setData(*i);
- return ret;
+ // 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();
}
- return NULL;
+ // Preserve any lock we're holding.
+ if (held)
+ m_tlsKey->setData(held);
+ return ret;
}
const Credential* ChainingMetadataProvider::resolve(const CredentialCriteria* criteria) const