From c089f03a9b08dc947a815be624654ca401c6b369 Mon Sep 17 00:00:00 2001 From: cantor Date: Wed, 29 Aug 2007 18:03:01 +0000 Subject: [PATCH] Dynamic metadata provider implementation. git-svn-id: https://svn.middleware.georgetown.edu/cpp-opensaml2/trunk@298 fb386ef7-a10c-0410-8ebf-fd3f8e989ab0 --- saml/Makefile.am | 2 + saml/saml.vcproj | 8 ++ saml/saml2/metadata/AbstractMetadataProvider.h | 19 +-- saml/saml2/metadata/DynamicMetadataProvider.h | 83 ++++++++++++ saml/saml2/metadata/MetadataProvider.h | 3 + .../metadata/impl/AbstractMetadataProvider.cpp | 23 +++- .../metadata/impl/ChainingMetadataProvider.cpp | 2 +- .../metadata/impl/DynamicMetadataProvider.cpp | 145 +++++++++++++++++++++ saml/saml2/metadata/impl/MetadataProvider.cpp | 4 +- 9 files changed, 274 insertions(+), 15 deletions(-) create mode 100644 saml/saml2/metadata/DynamicMetadataProvider.h create mode 100644 saml/saml2/metadata/impl/DynamicMetadataProvider.cpp diff --git a/saml/Makefile.am b/saml/Makefile.am index d371504..4cfdd44 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -83,6 +83,7 @@ saml2bindinclude_HEADERS = \ saml2mdinclude_HEADERS = \ saml2/metadata/AbstractMetadataProvider.h \ saml2/metadata/ChainingMetadataProvider.h \ + saml2/metadata/DynamicMetadataProvider.h \ saml2/metadata/EndpointManager.h \ saml2/metadata/Metadata.h \ saml2/metadata/MetadataCredentialContext.h \ @@ -135,6 +136,7 @@ libsaml_la_SOURCES = \ saml2/metadata/impl/AbstractMetadataProvider.cpp \ saml2/metadata/impl/BlacklistMetadataFilter.cpp \ saml2/metadata/impl/ChainingMetadataProvider.cpp \ + saml2/metadata/impl/DynamicMetadataProvider.cpp \ saml2/metadata/impl/MetadataImpl.cpp \ saml2/metadata/impl/MetadataProvider.cpp \ saml2/metadata/impl/MetadataSchemaValidators.cpp \ diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 1314ae8..76dd93f 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -347,6 +347,10 @@ > + + @@ -750,6 +754,10 @@ > + + diff --git a/saml/saml2/metadata/AbstractMetadataProvider.h b/saml/saml2/metadata/AbstractMetadataProvider.h index 3506cac..806347f 100644 --- a/saml/saml2/metadata/AbstractMetadataProvider.h +++ b/saml/saml2/metadata/AbstractMetadataProvider.h @@ -82,29 +82,32 @@ namespace opensaml { * processing known reverse lookup strategies for artifacts. * * @param site entity definition - * @param validUntil expiration time of the entity definition + * @param validUntil maximum expiration time of the entity definition + * @param replace true iff existing entries for the same entity should be cleared/replaced */ - virtual void index(EntityDescriptor* site, time_t validUntil); + virtual void index(EntityDescriptor* site, time_t validUntil, bool replace=false) const; /** * Loads a group of entities into the cache for faster lookup. * * @param group group definition - * @param validUntil expiration time of the group definition + * @param validUntil maximum expiration time of the group definition */ - virtual void index(EntitiesDescriptor* group, time_t validUntil); + virtual void index(EntitiesDescriptor* group, time_t validUntil) const; /** * Clear the cache of known entities and groups. + * + * @param freeSites true iff the objects cached in the site map should be freed. */ - virtual void clearDescriptorIndex(); + virtual void clearDescriptorIndex(bool freeSites=false); private: typedef std::multimap sitemap_t; typedef std::multimap groupmap_t; - sitemap_t m_sites; - sitemap_t m_sources; - groupmap_t m_groups; + mutable sitemap_t m_sites; + mutable sitemap_t m_sources; + mutable groupmap_t m_groups; mutable xmltooling::Mutex* m_credentialLock; typedef std::map< const RoleDescriptor*, std::vector > credmap_t; diff --git a/saml/saml2/metadata/DynamicMetadataProvider.h b/saml/saml2/metadata/DynamicMetadataProvider.h new file mode 100644 index 0000000..44b622b --- /dev/null +++ b/saml/saml2/metadata/DynamicMetadataProvider.h @@ -0,0 +1,83 @@ +/* + * Copyright 2001-2007 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/DynamicMetadataProvider.h + * + * Simple implementation of a dynamic caching MetadataProvider. + */ + +#ifndef __saml2_dynmetadataprov_h__ +#define __saml2_dynmetadataprov_h__ + +#include + +namespace opensaml { + namespace saml2md { + + /** + * Simple implementation of a dynamic, caching MetadataProvider. + */ + class SAML_API DynamicMetadataProvider : public AbstractMetadataProvider + { + public: + /** + * Constructor. + * + * @param e DOM to supply configuration for provider + */ + DynamicMetadataProvider(const xercesc::DOMElement* e=NULL); + + virtual ~DynamicMetadataProvider(); + + xmltooling::Lockable* lock() { + m_lock->rdlock(); + return this; + } + + void unlock() { + m_lock->unlock(); + } + + void init() { + } + + const xmltooling::XMLObject* getMetadata() const { + throw MetadataException("getMetadata operation not implemented on this provider."); + } + + const EntityDescriptor* getEntityDescriptor(const char* id, bool requireValidMetadata=true) const; + + protected: + /** Controls XML schema validation. */ + bool m_validate; + + /** + * Resolves an entityID into a metadata instance for that entity. + * + * @param entityID entity ID to resolve + * @return a valid metadata instance + */ + virtual EntityDescriptor* resolve(const char* entityID) const; + + private: + mutable xmltooling::RWLock* m_lock; + }; + + }; +}; + +#endif /* __saml2_dynmetadataprov_h__ */ diff --git a/saml/saml2/metadata/MetadataProvider.h b/saml/saml2/metadata/MetadataProvider.h index 6e2c6d4..2e9b4b9 100644 --- a/saml/saml2/metadata/MetadataProvider.h +++ b/saml/saml2/metadata/MetadataProvider.h @@ -211,6 +211,9 @@ namespace opensaml { /** MetadataProvider based on local or remote XML file */ #define XML_METADATA_PROVIDER "XML" + /** MetadataProvider based on dynamic resolution */ + #define DYNAMIC_METADATA_PROVIDER "Dynamic" + /** MetadataProvider that wraps a sequence of metadata providers. */ #define CHAINING_METADATA_PROVIDER "Chaining" diff --git a/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp b/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp index 196962b..6508e58 100644 --- a/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/AbstractMetadataProvider.cpp @@ -69,13 +69,26 @@ void AbstractMetadataProvider::emitChangeEvent() const ObservableMetadataProvider::emitChangeEvent(); } -void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil) +void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil, bool replace) const { if (validUntil < site->getValidUntilEpoch()) site->setValidUntil(validUntil); auto_ptr_char id(site->getEntityID()); if (id.get()) { + if (replace) { + m_sites.erase(id.get()); + for (sitemap_t::iterator s = m_sources.begin(); s != m_sources.end();) { + if (s->second == site) { + sitemap_t::iterator temp = s; + ++s; + m_sources.erase(temp); + } + else { + ++s; + } + } + } m_sites.insert(sitemap_t::value_type(id.get(),site)); } @@ -120,7 +133,7 @@ void AbstractMetadataProvider::index(EntityDescriptor* site, time_t validUntil) } } -void AbstractMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) +void AbstractMetadataProvider::index(EntitiesDescriptor* group, time_t validUntil) const { if (validUntil < group->getValidUntilEpoch()) group->setValidUntil(validUntil); @@ -139,11 +152,13 @@ void AbstractMetadataProvider::index(EntitiesDescriptor* group, time_t validUnti index(*j,group->getValidUntilEpoch()); } -void AbstractMetadataProvider::clearDescriptorIndex() +void AbstractMetadataProvider::clearDescriptorIndex(bool freeSites) { - m_sources.clear(); + if (freeSites) + for_each(m_sites.begin(), m_sites.end(), cleanup_const_pair()); m_sites.clear(); m_groups.clear(); + m_sources.clear(); } const EntitiesDescriptor* AbstractMetadataProvider::getEntitiesDescriptor(const char* name, bool strict) const diff --git a/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp b/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp index 25bc3db..1bcd092 100644 --- a/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp @@ -115,7 +115,7 @@ void ChainingMetadataProvider::unlock() const XMLObject* ChainingMetadataProvider::getMetadata() const { - throw XMLToolingException("getMetadata operation not implemented on this provider."); + throw MetadataException("getMetadata operation not implemented on this provider."); } const EntitiesDescriptor* ChainingMetadataProvider::getEntitiesDescriptor(const char* name, bool requireValidMetadata) const diff --git a/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp b/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp new file mode 100644 index 0000000..a23308f --- /dev/null +++ b/saml/saml2/metadata/impl/DynamicMetadataProvider.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2001-2007 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. + */ + +/** + * DynamicMetadataProvider.cpp + * + * Base class for caching metadata providers. + */ + +#include "internal.h" +#include "saml2/metadata/Metadata.h" +#include "saml2/metadata/DynamicMetadataProvider.h" + +#include +#include +#include +#include +#include + +using namespace opensaml::saml2md; +using namespace xmltooling::logging; +using namespace xmltooling; +using namespace std; + +static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e); + +namespace opensaml { + namespace saml2md { + MetadataProvider* SAML_DLLLOCAL DynamicMetadataProviderFactory(const DOMElement* const & e) + { + return new DynamicMetadataProvider(e); + } + }; +}; + +DynamicMetadataProvider::DynamicMetadataProvider(const DOMElement* e) + : AbstractMetadataProvider(e), m_lock(RWLock::create()) +{ + const XMLCh* flag=e ? e->getAttributeNS(NULL,validate) : NULL; + m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)); +} + +DynamicMetadataProvider::~DynamicMetadataProvider() +{ + // Each entity in the map is unique (no multimap semantics), so this is safe. + clearDescriptorIndex(true); + delete m_lock; +} + +const EntityDescriptor* DynamicMetadataProvider::getEntityDescriptor(const char* name, bool strict) const +{ + // Check cache while holding the read lock. + const EntityDescriptor* entity = AbstractMetadataProvider::getEntityDescriptor(name, strict); + if (entity) + return entity; + + Category& log = Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic"); + log.info("resolving metadata for (%s)", name); + + // Try resolving it. + auto_ptr entity2(resolve(name)); + + // Filter it, which may throw. + doFilters(*entity2.get()); + + log.info("caching resolved metadata for (%s)", name); + + // Translate cacheDuration into validUntil. + if (entity2->getCacheDuration()) + entity2->setValidUntil(time(NULL) + entity2->getCacheDurationEpoch()); + + // Upgrade our lock so we can cache the new metadata. + m_lock->unlock(); + m_lock->wrlock(); + + // Notify observers. + emitChangeEvent(); + + // Make sure we clear out any existing copies, including stale metadata or if somebody snuck in. + index(entity2.release(), SAMLTIME_MAX, true); + + // Downgrade back to a read lock. + m_lock->unlock(); + m_lock->rdlock(); + + // Rinse and repeat. + return getEntityDescriptor(name, strict); +} + +EntityDescriptor* DynamicMetadataProvider::resolve(const char* entityID) const +{ + try { + DOMDocument* doc=NULL; + auto_ptr_XMLCh widenit(entityID); + URLInputSource src(widenit.get()); + Wrapper4InputSource dsrc(&src,false); + if (m_validate) + doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); + else + doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); + + // Wrap the document for now. + XercesJanitor docjanitor(doc); + + // Unmarshall objects, binding the document. + auto_ptr xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true)); + docjanitor.release(); + + // Make sure it's metadata. + EntityDescriptor* entity = dynamic_cast(xmlObject.get()); + if (!entity) { + throw MetadataException( + "Root of metadata instance not recognized: $1", params(1,xmlObject->getElementQName().toString().c_str()) + ); + } + xmlObject.release(); + return entity; + } + catch (XMLException& e) { + auto_ptr_char msg(e.getMessage()); + Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( + "Xerces error while resolving entityID (%s): %s", entityID, msg.get() + ); + throw MetadataException(msg.get()); + } + catch (exception& e) { + Category::getInstance(SAML_LOGCAT".MetadataProvider.Dynamic").error( + "error while resolving entityID (%s): %s", entityID, e.what() + ); + throw; + } +} diff --git a/saml/saml2/metadata/impl/MetadataProvider.cpp b/saml/saml2/metadata/impl/MetadataProvider.cpp index 152f994..62665a0 100644 --- a/saml/saml2/metadata/impl/MetadataProvider.cpp +++ b/saml/saml2/metadata/impl/MetadataProvider.cpp @@ -38,6 +38,7 @@ using namespace std; namespace opensaml { namespace saml2md { SAML_DLLLOCAL PluginManager::Factory XMLMetadataProviderFactory; + SAML_DLLLOCAL PluginManager::Factory DynamicMetadataProviderFactory; SAML_DLLLOCAL PluginManager::Factory ChainingMetadataProviderFactory; SAML_DLLLOCAL PluginManager::Factory BlacklistMetadataFilterFactory; SAML_DLLLOCAL PluginManager::Factory WhitelistMetadataFilterFactory; @@ -49,9 +50,8 @@ void SAML_API opensaml::saml2md::registerMetadataProviders() { SAMLConfig& conf=SAMLConfig::getConfig(); conf.MetadataProviderManager.registerFactory(XML_METADATA_PROVIDER, XMLMetadataProviderFactory); + conf.MetadataProviderManager.registerFactory(DYNAMIC_METADATA_PROVIDER, DynamicMetadataProviderFactory); conf.MetadataProviderManager.registerFactory(CHAINING_METADATA_PROVIDER, ChainingMetadataProviderFactory); - conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.metadata.provider.XMLMetadata", XMLMetadataProviderFactory); - conf.MetadataProviderManager.registerFactory("edu.internet2.middleware.shibboleth.common.provider.XMLMetadata", XMLMetadataProviderFactory); } void SAML_API opensaml::saml2md::registerMetadataFilters() -- 2.1.4