From: Scott Cantor Date: Thu, 3 Aug 2006 01:00:26 +0000 (+0000) Subject: Implement metadata lookup by artifact, refactored metadata indexing. X-Git-Tag: 2.0-alpha1~208 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=commitdiff_plain;h=0b9c2825fb2fad1b6eadde350c799c10e31ace78 Implement metadata lookup by artifact, refactored metadata indexing. --- diff --git a/saml/SAMLArtifact.h b/saml/SAMLArtifact.h index 5281205..18ebcaf 100644 --- a/saml/SAMLArtifact.h +++ b/saml/SAMLArtifact.h @@ -63,7 +63,7 @@ namespace opensaml { /** * Returns the binary type code of the artifact. - * The result is NOT null-terminated. + * The result MAY contain embedded null characters. * * @return the binary type code */ @@ -73,7 +73,7 @@ namespace opensaml { /** * Returns the binary artifact data following the type code. - * The result is NOT null-terminated. + * The result MAY contain embedded null characters. * * @return the binary artifact data */ @@ -82,18 +82,18 @@ namespace opensaml { } /** - * Returns the binary data that identifies the source of the artifact. - * The exact form this takes depends on the type. - * The result is NOT null-terminated. + * Returns a string that identifies the source of the artifact. + * The exact form this takes depends on the type but should match + * the syntax needed for metadata lookup. * - * @return the binary source data + * @return null-terminated source string */ virtual std::string getSource() const=0; /** * Returns the binary data that references the message (2.0) or assertion (1.x) * The exact form this takes depends on the type. - * The result is NOT null-terminated. + * The result MAY contain embedded null characters. * * @return the binary reference data */ diff --git a/saml/saml1/core/SAMLArtifactType0001.h b/saml/saml1/core/SAMLArtifactType0001.h index d61020c..2fdf887 100644 --- a/saml/saml1/core/SAMLArtifactType0001.h +++ b/saml/saml1/core/SAMLArtifactType0001.h @@ -64,6 +64,16 @@ namespace opensaml { } virtual std::string getSource() const { + return toHex(getSourceID()); + } + + /** + * Returns the binary data that identifies the source. + * The result MAY contain embedded null characters. + * + * @return the binary source ID + */ + virtual std::string getSourceID() const { return m_raw.substr(TYPECODE_LENGTH,SOURCEID_LENGTH); // bytes 3-22 } diff --git a/saml/saml2/core/SAML2ArtifactType0004.h b/saml/saml2/core/SAML2ArtifactType0004.h index 7a0375c..caa429d 100644 --- a/saml/saml2/core/SAML2ArtifactType0004.h +++ b/saml/saml2/core/SAML2ArtifactType0004.h @@ -66,6 +66,16 @@ namespace opensaml { } virtual std::string getSource() const { + return toHex(getSourceID()); + } + + /** + * Returns the binary data that identifies the source. + * The result MAY contain embedded null characters. + * + * @return the binary source ID + */ + virtual std::string getSourceID() const { return m_raw.substr(TYPECODE_LENGTH + INDEX_LENGTH, SOURCEID_LENGTH); // bytes 5-24 } diff --git a/saml/saml2/metadata/MetadataProvider.h b/saml/saml2/metadata/MetadataProvider.h index 4e24bd7..7f5d386 100644 --- a/saml/saml2/metadata/MetadataProvider.h +++ b/saml/saml2/metadata/MetadataProvider.h @@ -27,6 +27,8 @@ #include namespace opensaml { + + class SAML_API SAMLArtifact; namespace saml2md { @@ -34,7 +36,8 @@ namespace opensaml { * Supplies an individual source of metadata. * * The source can be a local file, remote service, or the result of a - * dynamic lookup, can include local caching, etc. + * dynamic lookup, can include local caching, etc. Providers + * MUST be locked before any lookup operations. */ class SAML_API MetadataProvider : public virtual xmltooling::Lockable { @@ -119,7 +122,7 @@ namespace opensaml { * * @return the entity's metadata or NULL if there is no metadata or no valid metadata */ - virtual const EntityDescriptor* getEntityDescriptor(const XMLCh* id, bool requireValidMetadata=true) const=0; + virtual const EntityDescriptor* getEntityDescriptor(const XMLCh* id, bool requireValidMetadata=true) const; /** * Gets the metadata for a given entity. If a valid entity is returned, @@ -131,7 +134,18 @@ namespace opensaml { * * @return the entity's metadata or NULL if there is no metadata or no valid metadata */ - virtual const EntityDescriptor* getEntityDescriptor(const char* id, bool requireValidMetadata=true) const=0; + virtual const EntityDescriptor* getEntityDescriptor(const char* id, bool requireValidMetadata=true) const; + + /** + * Gets the metadata for an entity that issued a SAML artifact. If a valid entity is returned, + * the provider will be left in a locked state. The caller MUST unlock the + * provider when finished with the entity. + * + * @param artifact a SAML artifact to find the issuer of + * + * @return the entity's metadata or NULL if there is no valid metadata + */ + virtual const EntityDescriptor* getEntityDescriptor(const SAMLArtifact* artifact) const; /** * Gets the metadata for a given group of entities. If a valid group is returned, @@ -143,7 +157,7 @@ namespace opensaml { * * @return the group's metadata or NULL if there is no metadata or no valid metadata */ - virtual const EntitiesDescriptor* getEntitiesDescriptor(const XMLCh* name, bool requireValidMetadata=true) const=0; + virtual const EntitiesDescriptor* getEntitiesDescriptor(const XMLCh* name, bool requireValidMetadata=true) const; /** * Gets the metadata for a given group of entities. If a valid group is returned, @@ -155,7 +169,7 @@ namespace opensaml { * * @return the group's metadata or NULL if there is no metadata or no valid metadata */ - virtual const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const=0; + virtual const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const; protected: /** @@ -164,9 +178,37 @@ namespace opensaml { * @param xmlObject the metadata to be filtered */ void doFilters(xmltooling::XMLObject& xmlObject) const; + + /** + * Loads an entity into the cache for faster lookup. This includes + * processing known reverse lookup strategies for artifacts. + * + * @param site entity definition + * @param validUntil expiration time of the entity definition + */ + virtual void index(EntityDescriptor* site, time_t validUntil); + + /** + * Loads a group of entities into the cache for faster lookup. + * + * @param group group definition + * @param validUntil expiration time of the group definition + */ + virtual void index(EntitiesDescriptor* group, time_t validUntil); + + /** + * Clear the cache of known entities and groups. + */ + virtual void clearIndex(); private: std::vector m_filters; + + typedef std::multimap sitemap_t; + typedef std::multimap groupmap_t; + sitemap_t m_sites; + sitemap_t m_sources; + groupmap_t m_groups; }; /** diff --git a/saml/saml2/metadata/impl/FilesystemMetadataProvider.cpp b/saml/saml2/metadata/impl/FilesystemMetadataProvider.cpp index e9d3dbf..51c2b83 100644 --- a/saml/saml2/metadata/impl/FilesystemMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/FilesystemMetadataProvider.cpp @@ -55,10 +55,6 @@ namespace opensaml { void init(); - const EntityDescriptor* getEntityDescriptor(const XMLCh* id, bool requireValidMetadata=true) const; - const EntityDescriptor* getEntityDescriptor(const char* id, bool requireValidMetadata=true) const; - const EntitiesDescriptor* getEntitiesDescriptor(const XMLCh* name, bool requireValidMetadata=true) const; - const EntitiesDescriptor* getEntitiesDescriptor(const char* name, bool requireValidMetadata=true) const; const XMLObject* getMetadata() const { return m_object; } @@ -66,16 +62,7 @@ namespace opensaml { private: XMLObject* load() const; void index(); - void index(EntityDescriptor* site, time_t validUntil=SAMLTIME_MAX); - void index(EntitiesDescriptor* group, time_t validUntil=SAMLTIME_MAX); - // index of loaded metadata - typedef multimap sitemap_t; - typedef multimap groupmap_t; - sitemap_t m_sites; - sitemap_t m_sources; - groupmap_t m_groups; - const DOMElement* m_root; // survives only until init() method is done std::string m_source; time_t m_filestamp; @@ -263,87 +250,11 @@ Lockable* FilesystemMetadataProvider::lock() void FilesystemMetadataProvider::index() { - m_sources.clear(); - m_sites.clear(); - m_groups.clear(); - EntitiesDescriptor* group=dynamic_cast(m_object); if (group) { - index(group); + MetadataProvider::index(group, SAMLTIME_MAX); return; } EntityDescriptor* site=dynamic_cast(m_object); - index(site); -} - -void FilesystemMetadataProvider::index(EntityDescriptor* site, time_t validUntil) -{ - if (validUntil < site->getValidUntilEpoch()) - site->setValidUntil(validUntil); - - auto_ptr_char id(site->getEntityID()); - if (id.get()) { - m_sites.insert(make_pair(id.get(),site)); - } -} - -void FilesystemMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) -{ - if (validUntil < group->getValidUntilEpoch()) - group->setValidUntil(validUntil); - - auto_ptr_char name(group->getName()); - if (name.get()) { - m_groups.insert(make_pair(name.get(),group)); - } - - const vector& groups=const_cast(group)->getEntitiesDescriptors(); - for (vector::const_iterator i=groups.begin(); i!=groups.end(); i++) - index(*i,group->getValidUntilEpoch()); - - const vector& sites=const_cast(group)->getEntityDescriptors(); - for (vector::const_iterator j=sites.begin(); j!=sites.end(); j++) - index(*j,group->getValidUntilEpoch()); -} - -const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const -{ - pair range=m_groups.equal_range(name); - - time_t now=time(NULL); - for (groupmap_t::const_iterator i=range.first; i!=range.second; i++) - if (now < i->second->getValidUntilEpoch()) - return i->second; - - if (!strict && range.first!=range.second) - return range.first->second; - - return NULL; -} - -const EntitiesDescriptor* FilesystemMetadataProvider::getEntitiesDescriptor(const XMLCh* name, bool strict) const -{ - auto_ptr_char temp(name); - return getEntitiesDescriptor(temp.get(),strict); -} - -const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const char* name, bool strict) const -{ - pair range=m_sites.equal_range(name); - - time_t now=time(NULL); - for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) - if (now < i->second->getValidUntilEpoch()) - return i->second; - - if (!strict && range.first!=range.second) - return range.first->second; - - return NULL; -} - -const EntityDescriptor* FilesystemMetadataProvider::getEntityDescriptor(const XMLCh* name, bool strict) const -{ - auto_ptr_char temp(name); - return getEntityDescriptor(temp.get(),strict); + MetadataProvider::index(site, SAMLTIME_MAX); } diff --git a/saml/saml2/metadata/impl/MetadataProvider.cpp b/saml/saml2/metadata/impl/MetadataProvider.cpp index b0bf178..2a7052e 100644 --- a/saml/saml2/metadata/impl/MetadataProvider.cpp +++ b/saml/saml2/metadata/impl/MetadataProvider.cpp @@ -21,16 +21,40 @@ */ #include "internal.h" +#include "SAMLArtifact.h" #include "saml2/metadata/MetadataProvider.h" #include #include using namespace opensaml::saml2md; +using namespace opensaml; using namespace xmltooling; using namespace log4cpp; using namespace std; +namespace opensaml { + namespace saml2md { + SAML_DLLLOCAL PluginManager::Factory FilesystemMetadataProviderFactory; + SAML_DLLLOCAL PluginManager::Factory BlacklistMetadataFilterFactory; + SAML_DLLLOCAL PluginManager::Factory WhitelistMetadataFilterFactory; + }; +}; + +void SAML_API opensaml::saml2md::registerMetadataProviders() +{ + SAMLConfig& conf=SAMLConfig::getConfig(); + conf.MetadataProviderManager.registerFactory(FILESYSTEM_METADATA_PROVIDER, FilesystemMetadataProviderFactory); + conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.metadata.provider.XMLMetadata", FilesystemMetadataProviderFactory); + conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.common.provider.XMLMetadata", FilesystemMetadataProviderFactory); +} + +void SAML_API opensaml::saml2md::registerMetadataFilters() +{ + SAMLConfig::getConfig().MetadataFilterManager.registerFactory(BLACKLIST_METADATA_FILTER, BlacklistMetadataFilterFactory); + SAMLConfig::getConfig().MetadataFilterManager.registerFactory(WHITELIST_METADATA_FILTER, WhitelistMetadataFilterFactory); +} + static const XMLCh Blacklist[] = UNICODE_LITERAL_23(B,l,a,c,k,l,i,s,t,M,e,t,a,d,a,t,a,F,i,l,t,e,r); static const XMLCh Exclude[] = UNICODE_LITERAL_7(E,x,c,l,u,d,e); static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); @@ -93,24 +117,137 @@ void MetadataProvider::doFilters(XMLObject& xmlObject) const } } -namespace opensaml { - namespace saml2md { - SAML_DLLLOCAL PluginManager::Factory FilesystemMetadataProviderFactory; - SAML_DLLLOCAL PluginManager::Factory BlacklistMetadataFilterFactory; - SAML_DLLLOCAL PluginManager::Factory WhitelistMetadataFilterFactory; - }; -}; +void MetadataProvider::index(EntityDescriptor* site, time_t validUntil) +{ + if (validUntil < site->getValidUntilEpoch()) + site->setValidUntil(validUntil); -void SAML_API opensaml::saml2md::registerMetadataProviders() + auto_ptr_char id(site->getEntityID()); + if (id.get()) { + m_sites.insert(make_pair(id.get(),site)); + } + + // Process each IdP role. + const vector& roles=const_cast(site)->getIDPSSODescriptors(); + for (vector::const_iterator i=roles.begin(); i!=roles.end(); i++) { + // SAML 1.x? + if ((*i)->hasSupport(SAMLConstants::SAML10_PROTOCOL_ENUM) || (*i)->hasSupport(SAMLConstants::SAML11_PROTOCOL_ENUM)) { + // Check for SourceID extension element. + const Extensions* exts=(*i)->getExtensions(); + if (exts) { + const list& children=exts->getXMLObjects(); + for (list::const_iterator ext=children.begin(); ext!=children.end(); ext++) { + SourceID* sid=dynamic_cast(*ext); + if (sid) { + auto_ptr_char sourceid(sid->getID()); + if (sourceid.get()) { + m_sources.insert(pair(sourceid.get(),site)); + break; + } + } + } + } + + // Hash the ID. + m_sources.insert( + pair(SAMLConfig::getConfig().hashSHA1(id.get(), true),site) + ); + + // Load endpoints for type 0x0002 artifacts. + const vector& locs=const_cast(*i)->getArtifactResolutionServices(); + for (vector::const_iterator loc=locs.begin(); loc!=locs.end(); loc++) { + auto_ptr_char location((*loc)->getLocation()); + if (location.get()) + m_sources.insert(pair(location.get(),site)); + } + } + + // SAML 2.0? + if ((*i)->hasSupport(SAMLConstants::SAML20P_NS)) { + // Hash the ID. + m_sources.insert( + pair(SAMLConfig::getConfig().hashSHA1(id.get(), true),site) + ); + } + } +} + +void MetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) { - SAMLConfig& conf=SAMLConfig::getConfig(); - conf.MetadataProviderManager.registerFactory(FILESYSTEM_METADATA_PROVIDER, FilesystemMetadataProviderFactory); - conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.metadata.provider.XMLMetadata", FilesystemMetadataProviderFactory); - conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.common.provider.XMLMetadata", FilesystemMetadataProviderFactory); + if (validUntil < group->getValidUntilEpoch()) + group->setValidUntil(validUntil); + + auto_ptr_char name(group->getName()); + if (name.get()) { + m_groups.insert(make_pair(name.get(),group)); + } + + const vector& groups=const_cast(group)->getEntitiesDescriptors(); + for (vector::const_iterator i=groups.begin(); i!=groups.end(); i++) + index(*i,group->getValidUntilEpoch()); + + const vector& sites=const_cast(group)->getEntityDescriptors(); + for (vector::const_iterator j=sites.begin(); j!=sites.end(); j++) + index(*j,group->getValidUntilEpoch()); } -void SAML_API opensaml::saml2md::registerMetadataFilters() +void MetadataProvider::clearIndex() { - SAMLConfig::getConfig().MetadataFilterManager.registerFactory(BLACKLIST_METADATA_FILTER, BlacklistMetadataFilterFactory); - SAMLConfig::getConfig().MetadataFilterManager.registerFactory(WHITELIST_METADATA_FILTER, WhitelistMetadataFilterFactory); + m_sources.clear(); + m_sites.clear(); + m_groups.clear(); +} + +const EntitiesDescriptor* MetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const +{ + pair range=m_groups.equal_range(name); + + time_t now=time(NULL); + for (groupmap_t::const_iterator i=range.first; i!=range.second; i++) + if (now < i->second->getValidUntilEpoch()) + return i->second; + + if (!strict && range.first!=range.second) + return range.first->second; + + return NULL; +} + +const EntitiesDescriptor* MetadataProvider::getEntitiesDescriptor(const XMLCh* name, bool strict) const +{ + auto_ptr_char temp(name); + return getEntitiesDescriptor(temp.get(),strict); +} + +const EntityDescriptor* MetadataProvider::getEntityDescriptor(const char* name, bool strict) const +{ + pair range=m_sites.equal_range(name); + + time_t now=time(NULL); + for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) + if (now < i->second->getValidUntilEpoch()) + return i->second; + + if (!strict && range.first!=range.second) + return range.first->second; + + return NULL; +} + +const EntityDescriptor* MetadataProvider::getEntityDescriptor(const XMLCh* name, bool strict) const +{ + auto_ptr_char temp(name); + return getEntityDescriptor(temp.get(),strict); +} + +const EntityDescriptor* MetadataProvider::getEntityDescriptor(const SAMLArtifact* artifact) const +{ + pair range=m_sources.equal_range(artifact->getSource()); + + time_t now=time(NULL); + for (sitemap_t::const_iterator i=range.first; i!=range.second; i++) + if (now < i->second->getValidUntilEpoch()) + return i->second; + + return NULL; } diff --git a/samltest/SAMLArtifactType0002Test.h b/samltest/SAMLArtifactType0002Test.h index 8556105..45365d8 100644 --- a/samltest/SAMLArtifactType0002Test.h +++ b/samltest/SAMLArtifactType0002Test.h @@ -34,7 +34,7 @@ public: auto_ptr artifact(new SAMLArtifactType0002(providerIdStr)); auto_ptr tempArtifact(SAMLArtifact::parse(artifact->encode().c_str())); - TS_ASSERT_SAME_DATA(artifact->getSource().c_str(),tempArtifact->getSource().c_str(),artifact->getSource().length()); + TS_ASSERT_EQUALS(artifact->getSource(),tempArtifact->getSource()); TS_ASSERT_EQUALS(artifact->getMessageHandle(),tempArtifact->getMessageHandle()); TS_ASSERT_THROWS(auto_ptr bogus1(new SAMLArtifactType0002(providerIdStr, artifact->getMessageHandle() + artifact->getMessageHandle())), ArtifactException); diff --git a/samltest/saml2/metadata/FilesystemMetadataProviderTest.h b/samltest/saml2/metadata/FilesystemMetadataProviderTest.h index a6da0a7..a9c1f83 100644 --- a/samltest/saml2/metadata/FilesystemMetadataProviderTest.h +++ b/samltest/saml2/metadata/FilesystemMetadataProviderTest.h @@ -15,9 +15,11 @@ */ #include "internal.h" +#include #include using namespace opensaml::saml2md; +using namespace opensaml::saml2p; class FilesystemMetadataProviderTest : public CxxTest::TestSuite, public SAMLObjectBaseTestCase { XMLCh* entityID; @@ -66,6 +68,13 @@ public: TSM_ASSERT_EQUALS("Unexpected number of roles", 1, descriptor->getIDPSSODescriptors().size()); TSM_ASSERT("Role lookup failed", descriptor->getIDPSSODescriptor(supportedProtocol)!=NULL); TSM_ASSERT("Role lookup failed", descriptor->getIDPSSODescriptor(supportedProtocol2)!=NULL); + + auto_ptr artifact( + new SAML2ArtifactType0004(SAMLConfig::getConfig().hashSHA1("urn:mace:incommon:washington.edu"),1) + ); + descriptor = metadataProvider->getEntityDescriptor(artifact.get()); + TSM_ASSERT("Retrieved entity descriptor was null", descriptor!=NULL); + assertEquals("Entity's ID does not match requested ID", entityID, descriptor->getEntityID()); } void testFilesystemWithBlacklists() {