From 0c0b5c09b4ffd3f83657f93b457e7932ae937dda Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 26 Feb 2012 01:24:38 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/SSPCPP-342 --- saml/Makefile.am | 3 + saml/SAMLConfig.cpp | 3 + saml/SAMLConfig.h | 4 + saml/saml.vcxproj | 3 + saml/saml.vcxproj.filters | 9 ++ saml/saml2/metadata/EntityMatcher.h | 70 +++++++++ .../metadata/impl/BlacklistMetadataFilter.cpp | 112 +++++++------ .../impl/EntityAttributesEntityMatcher.cpp | 174 +++++++++++++++++++++ saml/saml2/metadata/impl/NameEntityMatcher.cpp | 88 +++++++++++ .../metadata/impl/WhitelistMetadataFilter.cpp | 131 +++------------- 10 files changed, 437 insertions(+), 160 deletions(-) create mode 100644 saml/saml2/metadata/EntityMatcher.h create mode 100644 saml/saml2/metadata/impl/EntityAttributesEntityMatcher.cpp create mode 100644 saml/saml2/metadata/impl/NameEntityMatcher.cpp diff --git a/saml/Makefile.am b/saml/Makefile.am index 4c34286..d138a3e 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -87,6 +87,7 @@ saml2mdinclude_HEADERS = \ saml2/metadata/DiscoverableMetadataProvider.h \ saml2/metadata/DynamicMetadataProvider.h \ saml2/metadata/EndpointManager.h \ + saml2/metadata/EntityMatcher.h \ saml2/metadata/Metadata.h \ saml2/metadata/MetadataCredentialContext.h \ saml2/metadata/MetadataCredentialCriteria.h \ @@ -146,6 +147,7 @@ libsaml_la_SOURCES = \ saml2/metadata/impl/ChainingMetadataProvider.cpp \ saml2/metadata/impl/DiscoverableMetadataProvider.cpp \ saml2/metadata/impl/DynamicMetadataProvider.cpp \ + saml2/metadata/impl/EntityAttributesEntityMatcher.cpp \ saml2/metadata/impl/EntityAttributesMetadataFilter.cpp \ saml2/metadata/impl/EntityRoleMetadataFilter.cpp \ saml2/metadata/impl/FolderMetadataProvider.cpp \ @@ -154,6 +156,7 @@ libsaml_la_SOURCES = \ saml2/metadata/impl/MetadataImpl.cpp \ saml2/metadata/impl/MetadataProvider.cpp \ saml2/metadata/impl/MetadataSchemaValidators.cpp \ + saml2/metadata/impl/NameEntityMatcher.cpp \ saml2/metadata/impl/NullMetadataProvider.cpp \ saml2/metadata/impl/ObservableMetadataProvider.cpp \ saml2/metadata/impl/SignatureMetadataFilter.cpp \ diff --git a/saml/SAMLConfig.cpp b/saml/SAMLConfig.cpp index e0ff16c..3470e06 100644 --- a/saml/SAMLConfig.cpp +++ b/saml/SAMLConfig.cpp @@ -48,6 +48,7 @@ #include "saml1/core/Assertions.h" #include "saml1/core/Protocols.h" #include "saml2/core/Protocols.h" +#include "saml2/metadata/EntityMatcher.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataFilter.h" #include "saml2/metadata/MetadataProvider.h" @@ -186,6 +187,7 @@ bool SAMLInternalConfig::init(bool initXMLTooling) saml2md::registerMetadataClasses(); saml2md::registerMetadataProviders(); saml2md::registerMetadataFilters(); + saml2md::registerEntityMatchers(); registerSAMLArtifacts(); registerMessageEncoders(); registerMessageDecoders(); @@ -218,6 +220,7 @@ void SAMLInternalConfig::term(bool termXMLTooling) MessageEncoderManager.deregisterFactories(); SecurityPolicyRuleManager.deregisterFactories(); SAMLArtifactManager.deregisterFactories(); + EntityMatcherManager.deregisterFactories(); MetadataFilterManager.deregisterFactories(); MetadataProviderManager.deregisterFactories(); diff --git a/saml/SAMLConfig.h b/saml/SAMLConfig.h index 4944abb..b1b3a43 100644 --- a/saml/SAMLConfig.h +++ b/saml/SAMLConfig.h @@ -48,6 +48,7 @@ namespace opensaml { namespace saml2md { class SAML_API ContactPerson; class SAML_API EntityDescriptor; + class SAML_API EntityMatcher; class SAML_API MetadataProvider; class SAML_API MetadataFilter; class SAML_API RoleDescriptor; @@ -191,6 +192,9 @@ namespace opensaml { /** Manages factories for MetadataFilter plugins. */ xmltooling::PluginManager MetadataFilterManager; + /** Manages factories for EntityMatcher plugins. */ + xmltooling::PluginManager EntityMatcherManager; + protected: SAMLConfig(); diff --git a/saml/saml.vcxproj b/saml/saml.vcxproj index becd37b..f5db90f 100644 --- a/saml/saml.vcxproj +++ b/saml/saml.vcxproj @@ -177,8 +177,10 @@ + + @@ -284,6 +286,7 @@ + diff --git a/saml/saml.vcxproj.filters b/saml/saml.vcxproj.filters index 531be08..93a3dfd 100644 --- a/saml/saml.vcxproj.filters +++ b/saml/saml.vcxproj.filters @@ -369,6 +369,12 @@ Source Files\saml2\metadata\impl + + Source Files\saml2\metadata\impl + + + Source Files\saml2\metadata\impl + @@ -515,6 +521,9 @@ Header Files\saml2\metadata + + Header Files\saml2\metadata + diff --git a/saml/saml2/metadata/EntityMatcher.h b/saml/saml2/metadata/EntityMatcher.h new file mode 100644 index 0000000..d15c2a2 --- /dev/null +++ b/saml/saml2/metadata/EntityMatcher.h @@ -0,0 +1,70 @@ +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you 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/EntityMatcher.h + * + * Applies a set of matching rules to an entity. + */ + +#include + +#ifndef __saml2_entitymatcher_h__ +#define __saml2_entitymatcher_h__ + +namespace opensaml { + namespace saml2md { + + class SAML_API EntityDescriptor; + + /** + * An entity matcher is a predicate that evaluates an entity against a set of matching rules. + */ + class SAML_API EntityMatcher + { + MAKE_NONCOPYABLE(EntityMatcher); + protected: + EntityMatcher(); + public: + virtual ~EntityMatcher(); + + /** + * Applies the instance's matching rule(s) against an entity. + * + * @param entity the entity to evaluate + * @return true iff the entity is matched + */ + virtual bool matches(const EntityDescriptor& entity) const=0; + }; + + /** + * Registers EntityMatcher classes into the runtime. + */ + void SAML_API registerEntityMatchers(); + + /** EntityMatcher that matches based on name. */ + #define NAME_ENTITY_MATCHER "Name" + + /** EntityMatcher that applies a set of input attributes. */ + #define ENTITYATTR_ENTITY_MATCHER "EntityAttributes" + }; +}; + +#endif /* __saml2_entitymatcher_h__ */ diff --git a/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp b/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp index d3175b2..62142e7 100644 --- a/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/BlacklistMetadataFilter.cpp @@ -25,20 +25,22 @@ */ #include "internal.h" +#include "saml2/metadata/EntityMatcher.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataFilter.h" +#include #include -#include using namespace opensaml::saml2md; +using namespace opensaml::saml2; using namespace xmltooling::logging; using namespace xmltooling; +using namespace boost; using namespace std; namespace opensaml { namespace saml2md { - class SAML_DLLLOCAL BlacklistMetadataFilter : public MetadataFilter { public: @@ -49,15 +51,11 @@ namespace opensaml { void doFilter(XMLObject& xmlObject) const; private: - void doFilter(EntitiesDescriptor& entities) const; - - bool found(const XMLCh* id) const { - if (!id) - return false; - return m_set.count(id)==1; - } + void filterGroup(EntitiesDescriptor*) const; + bool included(const EntityDescriptor&) const; - set m_set; + set m_entities; + scoped_ptr m_matcher; }; MetadataFilter* SAML_DLLLOCAL BlacklistMetadataFilterFactory(const DOMElement* const & e) @@ -65,78 +63,88 @@ namespace opensaml { return new BlacklistMetadataFilter(e); } + static const XMLCh Exclude[] = UNICODE_LITERAL_7(E,x,c,l,u,d,e); + static const XMLCh matcher[] = UNICODE_LITERAL_7(m,a,t,c,h,e,r); }; }; -static const XMLCh Exclude[] = UNICODE_LITERAL_7(E,x,c,l,u,d,e); BlacklistMetadataFilter::BlacklistMetadataFilter(const DOMElement* e) { - e = XMLHelper::getFirstChildElement(e); + string matcher(XMLHelper::getAttrString(e, nullptr, matcher)); + if (!matcher.empty()) + m_matcher.reset(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(matcher.c_str(), e)); + + e = XMLHelper::getFirstChildElement(e, Exclude); while (e) { - if (XMLString::equals(e->getLocalName(), Exclude) && e->hasChildNodes()) { - m_set.insert(e->getFirstChild()->getTextContent()); + if (e->hasChildNodes()) { + const XMLCh* excl = e->getTextContent(); + if (excl && *excl) + m_entities.insert(excl); } - e = XMLHelper::getNextSiblingElement(e); + e = XMLHelper::getNextSiblingElement(e, Exclude); } } void BlacklistMetadataFilter::doFilter(XMLObject& xmlObject) const { -#ifdef _DEBUG - NDC ndc("doFilter"); -#endif - - try { - EntitiesDescriptor& entities = dynamic_cast(xmlObject); - if (found(entities.getName())) - throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter instructed to filter the root/only group in the metadata."); - doFilter(entities); - return; - } - catch (bad_cast&) { - } - - try { - EntityDescriptor& entity = dynamic_cast(xmlObject); - if (found(entity.getEntityID())) - throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter instructed to filter the root/only entity in the metadata."); - return; + EntitiesDescriptor* group = dynamic_cast(&xmlObject); + if (group) { + if (group->getName() && !m_entities.empty() && m_entities.count(group->getName()) > 0) + throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter instructed to filter the root group in the metadata."); + filterGroup(group); } - catch (bad_cast&) { + else { + EntityDescriptor* entity = dynamic_cast(&xmlObject); + if (entity) { + if (included(*entity)) + throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter instructed to filter the root/only entity in the metadata."); + } + else { + throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter was given an improper metadata instance to filter."); + } } - - throw MetadataFilterException(BLACKLIST_METADATA_FILTER" MetadataFilter was given an improper metadata instance to filter."); } -void BlacklistMetadataFilter::doFilter(EntitiesDescriptor& entities) const +void BlacklistMetadataFilter::filterGroup(EntitiesDescriptor* entities) const { - Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter."BLACKLIST_METADATA_FILTER); - - VectorOf(EntityDescriptor) v=entities.getEntityDescriptors(); - for (VectorOf(EntityDescriptor)::size_type i=0; igetEntityID(); - if (found(id)) { - auto_ptr_char id2(id); - log.info("filtering out blacklisted entity (%s)", id2.get()); + Category& log = Category::getInstance(SAML_LOGCAT".MetadataFilter."WHITELIST_METADATA_FILTER); + + VectorOf(EntityDescriptor) v = entities->getEntityDescriptors(); + for (VectorOf(EntityDescriptor)::size_type i = 0; i < v.size(); ) { + if (included(*v[i])) { + auto_ptr_char id(v[i]->getEntityID()); + log.info("filtering out blacklisted entity (%s)", id.get()); v.erase(v.begin() + i); } else { i++; } } - - VectorOf(EntitiesDescriptor) w=entities.getEntitiesDescriptors(); - for (VectorOf(EntitiesDescriptor)::size_type j=0; jgetName(); - if (found(name)) { + + VectorOf(EntitiesDescriptor) w = entities->getEntitiesDescriptors(); + for (VectorOf(EntitiesDescriptor)::size_type j = 0; j < w.size(); ) { + const XMLCh* name = w[j]->getName(); + if (name && !m_entities.empty() && m_entities.count(name) > 0) { auto_ptr_char name2(name); log.info("filtering out blacklisted group (%s)", name2.get()); w.erase(w.begin() + j); } else { - doFilter(*(w[j])); + filterGroup(w[j]); j++; } } } + +bool BlacklistMetadataFilter::included(const EntityDescriptor& entity) const +{ + // Check for entityID. + if (entity.getEntityID() && !m_entities.empty() && m_entities.count(entity.getEntityID()) > 0) + return true; + + if (m_matcher && m_matcher->matches(entity)) + return true; + + return false; +} diff --git a/saml/saml2/metadata/impl/EntityAttributesEntityMatcher.cpp b/saml/saml2/metadata/impl/EntityAttributesEntityMatcher.cpp new file mode 100644 index 0000000..47ecbe6 --- /dev/null +++ b/saml/saml2/metadata/impl/EntityAttributesEntityMatcher.cpp @@ -0,0 +1,174 @@ +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you 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. + */ + +/** + * EntityAttributesEntityMatcher.cpp + * + * EntityMatcher that applies a set of input attributes. + */ + +#include "internal.h" +#include "saml2/metadata/EntityMatcher.h" +#include "saml2/metadata/Metadata.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace opensaml::saml2md; +using namespace opensaml::saml2; +using namespace opensaml; +using namespace xmltooling; +using namespace boost::lambda; +using namespace boost; +using namespace std; + +namespace opensaml { + namespace saml2md { + class SAML_DLLLOCAL EntityAttributesEntityMatcher : public EntityMatcher + { + public: + EntityAttributesEntityMatcher(const DOMElement* e); + ~EntityAttributesEntityMatcher() {} + + bool matches(const EntityDescriptor& entity) const; + + private: + bool _matches(const EntityAttributes*, const Attribute*) const; + + bool m_trimTags; + vector< boost::shared_ptr > m_tags; + }; + + EntityMatcher* SAML_DLLLOCAL EntityAttributesEntityMatcherFactory(const DOMElement* const & e) + { + return new EntityAttributesEntityMatcher(e); + } + + static const XMLCh trimTags[] = UNICODE_LITERAL_8(t,r,i,m,T,a,g,s); + }; +}; + + +EntityAttributesEntityMatcher::EntityAttributesEntityMatcher(const DOMElement* e) + : m_trimTags(XMLHelper::getAttrBool(e, false, trimTags)) +{ + DOMElement* child = XMLHelper::getFirstChildElement(e, samlconstants::SAML20_NS, Attribute::LOCAL_NAME); + while (child) { + boost::shared_ptr obj(AttributeBuilder::buildOneFromElement(child)); + m_tags.push_back(boost::shared_dynamic_cast(obj)); + child = XMLHelper::getNextSiblingElement(child, samlconstants::SAML20_NS, Attribute::LOCAL_NAME); + } + + if (m_tags.empty()) + throw XMLToolingException("EntityAttributes EntityMatcher requires at least one saml2:Attribute to match."); +} + +bool EntityAttributesEntityMatcher::matches(const EntityDescriptor& entity) const +{ + // Check for a tag match in the EntityAttributes extension of the entity and its parent(s). + const Extensions* exts = entity.getExtensions(); + if (exts) { + const vector& children = exts->getUnknownXMLObjects(); + const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); + if (xo) { + // If we find a matching tag, we win. Each tag is treated in OR fashion. + if (find_if(m_tags.begin(), m_tags.end(), + lambda::bind(&EntityAttributesEntityMatcher::_matches, this, dynamic_cast(xo), + lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { + return true; + } + } + } + + const EntitiesDescriptor* group = dynamic_cast(entity.getParent()); + while (group) { + exts = group->getExtensions(); + if (exts) { + const vector& children = exts->getUnknownXMLObjects(); + const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); + if (xo) { + // If we find a matching tag, we win. Each tag is treated in OR fashion. + if (find_if(m_tags.begin(), m_tags.end(), + lambda::bind(&EntityAttributesEntityMatcher::_matches, this, dynamic_cast(xo), + lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { + return true; + } + } + } + group = dynamic_cast(group->getParent()); + } + + return false; +} + +bool EntityAttributesEntityMatcher::_matches(const EntityAttributes* ea, const Attribute* tag) const +{ + const vector& attrs = ea->getAttributes(); + const vector& tagvals = tag->getAttributeValues(); + if (!attrs.empty() && !tagvals.empty()) { + // Track whether we've found every tag value. + vector flags(tagvals.size()); + + // Check each attribute/tag in the candidate. + for (indirect_iterator::const_iterator> a = make_indirect_iterator(attrs.begin()); + a != make_indirect_iterator(attrs.end()); ++a) { + // Compare Name and NameFormat for a matching tag. + if (XMLString::equals(a->getName(), tag->getName()) && + (!tag->getNameFormat() || XMLString::equals(tag->getNameFormat(), Attribute::UNSPECIFIED) || + XMLString::equals(tag->getNameFormat(), a->getNameFormat()))) { + // Check each tag value's simple content for a match. + for (vector::size_type tagindex = 0; tagindex < tagvals.size(); ++tagindex) { + const XMLObject* tagval = tagvals[tagindex]; + const XMLCh* tagvalstr = (tagval->getDOM()) ? tagval->getDOM()->getTextContent() : tagval->getTextContent(); + const vector& cvals = const_cast(*a).getAttributeValues(); + for (indirect_iterator::const_iterator> cval = make_indirect_iterator(cvals.begin()); + cval != make_indirect_iterator(cvals.end()); ++cval) { + const XMLCh* cvalstr = cval->getDOM() ? cval->getDOM()->getTextContent() : cval->getTextContent(); + if (tagvalstr && cvalstr) { + if (XMLString::equals(tagvalstr, cvalstr)) { + flags[tagindex] = true; + break; + } + else if (m_trimTags) { + XMLCh* dup = XMLString::replicate(cvalstr); + XMLString::trim(dup); + if (XMLString::equals(tagvalstr, dup)) { + XMLString::release(&dup); + flags[tagindex] = true; + break; + } + XMLString::release(&dup); + } + } + } + } + } + } + + if (find(flags.begin(), flags.end(), false) == flags.end()) + return true; + } + return false; +} diff --git a/saml/saml2/metadata/impl/NameEntityMatcher.cpp b/saml/saml2/metadata/impl/NameEntityMatcher.cpp new file mode 100644 index 0000000..7eb65b0 --- /dev/null +++ b/saml/saml2/metadata/impl/NameEntityMatcher.cpp @@ -0,0 +1,88 @@ +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * UCAID licenses this file to you 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. + */ + +/** + * NameEntityMatcher.cpp + * + * EntityMatcher that matches based on name. + */ + +#include "internal.h" +#include "saml2/metadata/EntityMatcher.h" +#include "saml2/metadata/Metadata.h" + +using namespace opensaml::saml2md; +using namespace opensaml; +using namespace xmltooling; +using namespace std; + +namespace opensaml { + namespace saml2md { + class SAML_DLLLOCAL NameEntityMatcher : public EntityMatcher + { + public: + NameEntityMatcher(const DOMElement* e) + : m_name(e ? e->getAttributeNS(nullptr, EntitiesDescriptor::NAME_ATTRIB_NAME) : nullptr) { + if (!m_name || !*m_name) + throw XMLToolingException("Name EntityMatcher missing required Name attribute."); + } + ~NameEntityMatcher() {} + + bool matches(const EntityDescriptor& entity) const; + + private: + const XMLCh* m_name; + }; + + EntityMatcher* SAML_DLLLOCAL NameEntityMatcherFactory(const DOMElement* const & e) + { + return new NameEntityMatcher(e); + } + + SAML_DLLLOCAL PluginManager::Factory EntityAttributesEntityMatcherFactory; + }; +}; + +void SAML_API opensaml::saml2md::registerEntityMatchers() +{ + SAMLConfig::getConfig().EntityMatcherManager.registerFactory(NAME_ENTITY_MATCHER, NameEntityMatcherFactory); + SAMLConfig::getConfig().EntityMatcherManager.registerFactory(ENTITYATTR_ENTITY_MATCHER, EntityAttributesEntityMatcherFactory); +} + +EntityMatcher::EntityMatcher() +{ +} + +EntityMatcher::~EntityMatcher() +{ +} + +bool NameEntityMatcher::matches(const EntityDescriptor& entity) const +{ + if (XMLString::equals(m_name, entity.getEntityID())) + return true; + const EntitiesDescriptor* group = dynamic_cast(entity.getParent()); + while (group) { + if (XMLString::equals(m_name, group->getName())) + return true; + group = dynamic_cast(group->getParent()); + } + return false; +} diff --git a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp index 3b086ba..842fcef 100644 --- a/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp +++ b/saml/saml2/metadata/impl/WhitelistMetadataFilter.cpp @@ -25,21 +25,18 @@ */ #include "internal.h" +#include "saml2/metadata/EntityMatcher.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/MetadataFilter.h" -#include -#include -#include -#include -#include +#include +#include #include -using namespace opensaml::saml2; using namespace opensaml::saml2md; +using namespace opensaml::saml2; using namespace xmltooling::logging; using namespace xmltooling; -using namespace boost::lambda; using namespace boost; using namespace std; @@ -57,11 +54,9 @@ namespace opensaml { private: void filterGroup(EntitiesDescriptor*) const; bool included(const EntityDescriptor&) const; - bool matches(const EntityAttributes*, const Attribute*) const; set m_entities; - bool m_trimTags; - vector< boost::shared_ptr > m_tags; + scoped_ptr m_matcher; }; MetadataFilter* SAML_DLLLOCAL WhitelistMetadataFilterFactory(const DOMElement* const & e) @@ -69,25 +64,26 @@ namespace opensaml { return new WhitelistMetadataFilter(e); } - static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); - static const XMLCh trimTags[] = UNICODE_LITERAL_8(t,r,i,m,T,a,g,s); + static const XMLCh Include[] = UNICODE_LITERAL_7(I,n,c,l,u,d,e); + static const XMLCh matcher[] = UNICODE_LITERAL_7(m,a,t,c,h,e,r); }; }; WhitelistMetadataFilter::WhitelistMetadataFilter(const DOMElement* e) - : m_trimTags(XMLHelper::getAttrBool(e, false, trimTags)) { - DOMElement* child = XMLHelper::getFirstChildElement(e); - while (child) { - if (XMLString::equals(child->getLocalName(), Include) && child->hasChildNodes()) { - m_entities.insert(child->getFirstChild()->getTextContent()); + string matcher(XMLHelper::getAttrString(e, nullptr, matcher)); + if (!matcher.empty()) + m_matcher.reset(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(matcher.c_str(), e)); + + e = XMLHelper::getFirstChildElement(e, Include); + while (e) { + if (e->hasChildNodes()) { + const XMLCh* incl = e->getTextContent(); + if (incl && *incl) + m_entities.insert(incl); } - else if (XMLHelper::isNodeNamed(child, samlconstants::SAML20_NS, Attribute::LOCAL_NAME)) { - boost::shared_ptr obj(AttributeBuilder::buildOneFromElement(child)); - m_tags.push_back(boost::shared_dynamic_cast(obj)); - } - child = XMLHelper::getNextSiblingElement(child); + e = XMLHelper::getNextSiblingElement(e, Include); } } @@ -111,7 +107,7 @@ void WhitelistMetadataFilter::doFilter(XMLObject& xmlObject) const void WhitelistMetadataFilter::filterGroup(EntitiesDescriptor* entities) const { - Category& log=Category::getInstance(SAML_LOGCAT".MetadataFilter."WHITELIST_METADATA_FILTER); + Category& log = Category::getInstance(SAML_LOGCAT".MetadataFilter."WHITELIST_METADATA_FILTER); VectorOf(EntityDescriptor) v = entities->getEntityDescriptors(); for (VectorOf(EntityDescriptor)::size_type i = 0; i < v.size(); ) { @@ -126,98 +122,17 @@ void WhitelistMetadataFilter::filterGroup(EntitiesDescriptor* entities) const } const vector& groups = const_cast(entities)->getEntitiesDescriptors(); - for_each(groups.begin(), groups.end(), lambda::bind(&WhitelistMetadataFilter::filterGroup, this, _1)); + for_each(groups.begin(), groups.end(), boost::bind(&WhitelistMetadataFilter::filterGroup, this, _1)); } bool WhitelistMetadataFilter::included(const EntityDescriptor& entity) const { // Check for entityID. - if (entity.getEntityID() && !m_entities.empty() && m_entities.count(entity.getEntityID()) == 1) + if (entity.getEntityID() && !m_entities.empty() && m_entities.count(entity.getEntityID()) > 0) return true; - // Check for a tag match in the EntityAttributes extension of the entity and its parent(s). - if (!m_tags.empty()) { - const Extensions* exts = entity.getExtensions(); - if (exts) { - const vector& children = exts->getUnknownXMLObjects(); - const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); - if (xo) { - // If we find a matching tag, we win. Each tag is treated in OR fashion. - if (find_if(m_tags.begin(), m_tags.end(), - lambda::bind(&WhitelistMetadataFilter::matches, this, dynamic_cast(xo), - lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { - return true; - } - } - } - - const EntitiesDescriptor* group = dynamic_cast(entity.getParent()); - while (group) { - exts = group->getExtensions(); - if (exts) { - const vector& children = exts->getUnknownXMLObjects(); - const XMLObject* xo = find_if(children, ll_dynamic_cast(_1) != ((EntityAttributes*)nullptr)); - if (xo) { - // If we find a matching tag, we win. Each tag is treated in OR fashion. - if (find_if(m_tags.begin(), m_tags.end(), - lambda::bind(&WhitelistMetadataFilter::matches, this, dynamic_cast(xo), - lambda::bind(&boost::shared_ptr::get, _1))) != m_tags.end()) { - return true; - } - } - } - group = dynamic_cast(group->getParent()); - } - } - return false; -} - -bool WhitelistMetadataFilter::matches(const EntityAttributes* ea, const Attribute* tag) const -{ - const vector& attrs = ea->getAttributes(); - const vector& tagvals = tag->getAttributeValues(); - if (!attrs.empty() && !tagvals.empty()) { - // Track whether we've found every tag value. - vector flags(tagvals.size()); - - // Check each attribute/tag in the candidate. - for (indirect_iterator::const_iterator> a = make_indirect_iterator(attrs.begin()); - a != make_indirect_iterator(attrs.end()); ++a) { - // Compare Name and NameFormat for a matching tag. - if (XMLString::equals(a->getName(), tag->getName()) && - (!tag->getNameFormat() || XMLString::equals(tag->getNameFormat(), Attribute::UNSPECIFIED) || - XMLString::equals(tag->getNameFormat(), a->getNameFormat()))) { - // Check each tag value's simple content for a match. - for (vector::size_type tagindex = 0; tagindex < tagvals.size(); ++tagindex) { - const XMLObject* tagval = tagvals[tagindex]; - const XMLCh* tagvalstr = (tagval->getDOM()) ? tagval->getDOM()->getTextContent() : tagval->getTextContent(); - const vector& cvals = const_cast(*a).getAttributeValues(); - for (indirect_iterator::const_iterator> cval = make_indirect_iterator(cvals.begin()); - cval != make_indirect_iterator(cvals.end()); ++cval) { - const XMLCh* cvalstr = cval->getDOM() ? cval->getDOM()->getTextContent() : cval->getTextContent(); - if (tagvalstr && cvalstr) { - if (XMLString::equals(tagvalstr, cvalstr)) { - flags[tagindex] = true; - break; - } - else if (m_trimTags) { - XMLCh* dup = XMLString::replicate(cvalstr); - XMLString::trim(dup); - if (XMLString::equals(tagvalstr, dup)) { - XMLString::release(&dup); - flags[tagindex] = true; - break; - } - XMLString::release(&dup); - } - } - } - } - } - } + if (m_matcher && m_matcher->matches(entity)) + return true; - if (find(flags.begin(), flags.end(), false) == flags.end()) - return true; - } return false; } -- 2.1.4