From 3f107084066988f951c36f1671d5ef5b19e498a1 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Wed, 9 May 2012 20:34:09 +0000 Subject: [PATCH] https://issues.shibboleth.net/jira/browse/CPPOST-76 --- saml/saml2/metadata/DiscoverableMetadataProvider.h | 23 ++- .../metadata/impl/ChainingMetadataProvider.cpp | 4 +- .../metadata/impl/DiscoverableMetadataProvider.cpp | 185 +++++++++++++++++---- saml/saml2/metadata/impl/XMLMetadataProvider.cpp | 8 +- 4 files changed, 181 insertions(+), 39 deletions(-) diff --git a/saml/saml2/metadata/DiscoverableMetadataProvider.h b/saml/saml2/metadata/DiscoverableMetadataProvider.h index 2086705..095d03c 100644 --- a/saml/saml2/metadata/DiscoverableMetadataProvider.h +++ b/saml/saml2/metadata/DiscoverableMetadataProvider.h @@ -29,9 +29,18 @@ #include +#include + namespace opensaml { + namespace saml2 { + class SAML_API Attribute; + }; + namespace saml2md { + + class SAML_API EntityAttributes; + class SAML_API EntityMatcher; #if defined (_MSC_VER) #pragma warning( push ) @@ -52,6 +61,15 @@ namespace opensaml { *
legacyOrgNames
*
true iff IdPs without a UIInfo extension should * be identified using <md:OrganizationDisplayName>
+ *
entityAttributes
+ *
true iff tags found in <mdattr:EntityAttributes> + * extensions should be included in the feed
+ *
<DiscoveryFilter type="..." matcher="..." >
+ *
Zero or more filters of type "Whitelist" or "Blacklist" that + * affect which entities get exposed by the feed. The actual matching + * is driven by an EntityMatcher plugin identified by the matcher + * attribute. Other element content will be present to configure + * that plugin.
* * * @param e DOM to supply configuration for provider @@ -96,8 +114,11 @@ namespace opensaml { private: void discoEntity(std::string& s, const EntityDescriptor* entity, bool& first) const; void discoGroup(std::string& s, const EntitiesDescriptor* group, bool& first) const; + void discoEntityAttributes(std::string& s, const EntityAttributes& ea, bool& first) const; + void discoAttributes(std::string& s, const std::vector& attrs, bool& first) const; - bool m_legacyOrgNames; + bool m_legacyOrgNames, m_entityAttributes; + std::vector< std::pair< bool, boost::shared_ptr > > m_discoFilters; }; #if defined (_MSC_VER) diff --git a/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp b/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp index f5fb8c5..e8dcf15 100644 --- a/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/ChainingMetadataProvider.cpp @@ -195,7 +195,7 @@ ChainingMetadataProvider::ChainingMetadataProvider(const DOMElement* e) m_providers.push_back(provider.get()); provider.release(); } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("error building MetadataProvider: %s", ex.what()); } } @@ -214,7 +214,7 @@ void ChainingMetadataProvider::init() try { i->init(); } - catch (exception& ex) { + catch (std::exception& ex) { m_log.crit("failure initializing MetadataProvider: %s", ex.what()); } } diff --git a/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp b/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp index a2d89bb..7316903 100644 --- a/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp @@ -26,25 +26,65 @@ #include "internal.h" #include "binding/SAMLArtifact.h" +#include "saml2/metadata/EntityMatcher.h" #include "saml2/metadata/Metadata.h" #include "saml2/metadata/DiscoverableMetadataProvider.h" #include #include -#include +#include +#include +#include #include #include #include +using namespace opensaml::saml2; using namespace opensaml::saml2md; +using namespace xmltooling::logging; using namespace xmltooling; +using namespace boost::lambda; using namespace boost; using namespace std; DiscoverableMetadataProvider::DiscoverableMetadataProvider(const DOMElement* e) : MetadataProvider(e), m_legacyOrgNames(false) { - static const XMLCh legacyOrgNames[] = UNICODE_LITERAL_14(l,e,g,a,c,y,O,r,g,N,a,m,e,s); + static const XMLCh legacyOrgNames[] = UNICODE_LITERAL_14(l,e,g,a,c,y,O,r,g,N,a,m,e,s); + static const XMLCh matcher[] = UNICODE_LITERAL_7(m,a,t,c,h,e,r); + static const XMLCh tagsInFeed[] = UNICODE_LITERAL_10(t,a,g,s,I,n,F,e,e,d); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + static const XMLCh DiscoveryFilter[] = UNICODE_LITERAL_15(D,i,s,c,o,v,e,r,y,F,i,l,t,e,r); + m_legacyOrgNames = XMLHelper::getAttrBool(e, false, legacyOrgNames); + m_entityAttributes = XMLHelper::getAttrBool(e, false, tagsInFeed); + + e = e ? XMLHelper::getFirstChildElement(e, DiscoveryFilter) : nullptr; + while (e) { + string t(XMLHelper::getAttrString(e, nullptr, _type)); + if (t == "Whitelist" || t == "Blacklist") { + string m(XMLHelper::getAttrString(e, nullptr, matcher)); + if (!m.empty()) { + try { + boost::shared_ptr temp(SAMLConfig::getConfig().EntityMatcherManager.newPlugin(m, e)); + m_discoFilters.push_back(make_pair(t == "Whitelist", temp)); + } + catch (std::exception& ex) { + Category::getInstance(SAML_LOGCAT".MetadataProvider.Discoverable").error( + "exception creating EntityMatcher: %s", ex.what() + ); + } + } + else { + Category::getInstance(SAML_LOGCAT".MetadataProvider.Discoverable").error(" requires matcher attribute"); + } + } + else { + Category::getInstance(SAML_LOGCAT".MetadataProvider.Discoverable").error( + "unknown type (%s)", t.empty() ? "none" : t.c_str() + ); + } + e = XMLHelper::getNextSiblingElement(e, DiscoveryFilter); + } } DiscoverableMetadataProvider::~DiscoverableMetadataProvider() @@ -83,41 +123,52 @@ void DiscoverableMetadataProvider::outputFeed(ostream& os, bool& first, bool wra os << "\n]"; } -static string& json_safe(string& s, const char* buf) -{ - for (; *buf; ++buf) { - switch (*buf) { - case '\\': - case '"': - s += '\\'; - s += *buf; - break; - case '\b': - s += "\\b"; - break; - case '\t': - s += "\\t"; - break; - case '\n': - s += "\\n"; - break; - case '\f': - s += "\\f"; - break; - case '\r': - s += "\\r"; - break; - default: - s += *buf; +namespace { + static string& json_safe(string& s, const char* buf) + { + for (; *buf; ++buf) { + switch (*buf) { + case '\\': + case '"': + s += '\\'; + s += *buf; + break; + case '\b': + s += "\\b"; + break; + case '\t': + s += "\\t"; + break; + case '\n': + s += "\\n"; + break; + case '\f': + s += "\\f"; + break; + case '\r': + s += "\\r"; + break; + default: + s += *buf; + } } + return s; } - return s; -} +}; void DiscoverableMetadataProvider::discoEntity(string& s, const EntityDescriptor* entity, bool& first) const { time_t now = time(nullptr); if (entity && entity->isValid(now)) { + + // Check filter(s). + for (vector< pair < bool, boost::shared_ptr > >::const_iterator f = m_discoFilters.begin(); f != m_discoFilters.end(); ++f) { + // The flag is true for a whitelist and false for a blacklist, + // so we omit the entity if the match outcome is the inverse. + if (f->first != f->second->matches(*entity)) + return; + } + const vector& idps = entity->getIDPSSODescriptors(); if (!idps.empty()) { auto_ptr_char entityid(entity->getEntityID()); @@ -293,6 +344,32 @@ void DiscoverableMetadataProvider::discoEntity(string& s, const EntityDescriptor } } + if (m_entityAttributes) { + bool tagfirst = true; + // Check for an EntityAttributes extension in 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) + discoEntityAttributes(s, *dynamic_cast(xo), tagfirst); + } + + 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) + discoEntityAttributes(s, *dynamic_cast(xo), tagfirst); + } + group = dynamic_cast(group->getParent()); + } + if (!tagfirst) + s += "\n ]"; + } + // Close the struct; s += "\n}"; } @@ -304,11 +381,55 @@ void DiscoverableMetadataProvider::discoGroup(string& s, const EntitiesDescripto if (group) { for_each( group->getEntitiesDescriptors().begin(), group->getEntitiesDescriptors().end(), - boost::bind(&DiscoverableMetadataProvider::discoGroup, this, boost::ref(s), _1, boost::ref(first)) + lambda::bind(&DiscoverableMetadataProvider::discoGroup, this, boost::ref(s), _1, boost::ref(first)) ); for_each( group->getEntityDescriptors().begin(), group->getEntityDescriptors().end(), - boost::bind(&DiscoverableMetadataProvider::discoEntity, this, boost::ref(s), _1, boost::ref(first)) + lambda::bind(&DiscoverableMetadataProvider::discoEntity, this, boost::ref(s), _1, boost::ref(first)) ); } } + +void DiscoverableMetadataProvider::discoEntityAttributes(std::string& s, const EntityAttributes& ea, bool& first) const +{ + discoAttributes(s, ea.getAttributes(), first); + const vector& tokens = ea.getAssertions(); + for (vector::const_iterator t = tokens.begin(); t != tokens.end(); ++t) { + const vector statements = const_cast(*t)->getAttributeStatements(); + for (vector::const_iterator st = statements.begin(); st != statements.end(); ++st) { + discoAttributes(s, const_cast(*st)->getAttributes(), first); + } + } +} + +void DiscoverableMetadataProvider::discoAttributes(std::string& s, const vector& attrs, bool& first) const +{ + for (indirect_iterator::const_iterator> a = make_indirect_iterator(attrs.begin()); + a != make_indirect_iterator(attrs.end()); ++a) { + + if (first) { + s += ",\n \"EntityAttributes\": ["; + first = false; + } + else { + s += ','; + } + + auto_ptr_char n(a->getName()); + s += "\n {\n \"name\": \""; + json_safe(s, n.get()); + s += "\",\n \"values\": ["; + const vector& vals = const_cast(*a).getAttributeValues(); + for (indirect_iterator::const_iterator> v = make_indirect_iterator(vals.begin()); + v != make_indirect_iterator(vals.end()); ++v) { + if (v.base() != vals.begin()) + s += ','; + auto_arrayptr val(toUTF8(v->getTextContent())); + s += "\n \""; + if (val.get()) + json_safe(s, val.get()); + s += '\"'; + } + s += "\n ]\n }"; + } +} diff --git a/saml/saml2/metadata/impl/XMLMetadataProvider.cpp b/saml/saml2/metadata/impl/XMLMetadataProvider.cpp index c01c496..86c9601 100644 --- a/saml/saml2/metadata/impl/XMLMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/XMLMetadataProvider.cpp @@ -212,7 +212,7 @@ pair XMLMetadataProvider::load(bool backup) try { SchemaValidators.validate(xmlObject.get()); } - catch (exception& ex) { + catch (std::exception& ex) { m_log.error("metadata intance failed manual validation checking: %s", ex.what()); throw MetadataException("Metadata instance failed manual validation checking."); } @@ -234,7 +234,7 @@ pair XMLMetadataProvider::load(bool backup) ofstream backer(backupKey.c_str()); backer << *(raw.second->getOwnerDocument()); } - catch (exception& ex) { + catch (std::exception& ex) { m_log.crit("exception while backing up metadata: %s", ex.what()); backupKey.erase(); } @@ -243,7 +243,7 @@ pair XMLMetadataProvider::load(bool backup) try { doFilters(*xmlObject); } - catch (exception&) { + catch (std::exception&) { if (!backupKey.empty()) remove(backupKey.c_str()); throw; @@ -313,7 +313,7 @@ pair XMLMetadataProvider::background_load() return load(true); throw; } - catch (exception&) { + catch (std::exception&) { if (!m_local) { m_reloadInterval = m_minRefreshDelay * m_backoffFactor++; if (m_reloadInterval > m_maxRefreshDelay) -- 2.1.4