X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-opensaml.git;a=blobdiff_plain;f=saml%2Fsaml2%2Fmetadata%2Fimpl%2FDiscoverableMetadataProvider.cpp;h=2b2661839ae95a4ce79e2e640ceef54b315fc82e;hp=5274446e1aecfc96d6cf520ff2160b35433dad12;hb=a6268a810d7892f1752de92ac8b5fbe929c27c4c;hpb=e5535875498112e903f636f464a494e7a74f2ea2 diff --git a/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp b/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp index 5274446..2b26618 100644 --- a/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp +++ b/saml/saml2/metadata/impl/DiscoverableMetadataProvider.cpp @@ -1,17 +1,21 @@ -/* - * Copyright 2010 Internet2 +/** + * 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. * - * 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 + * 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 + * 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. + * 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. */ /** @@ -22,185 +26,412 @@ #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 +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 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() +{ +} + +void DiscoverableMetadataProvider::generateFeed() +{ + m_feed.erase(); + bool first = true; + const XMLObject* object = getMetadata(); + discoGroup(m_feed, dynamic_cast(object), first); + discoEntity(m_feed, dynamic_cast(object), first); + + SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4); + m_feedTag = SAMLArtifact::toHex(m_feedTag); +} + +string DiscoverableMetadataProvider::getCacheTag() const +{ + return m_feedTag; +} + +void DiscoverableMetadataProvider::outputFeed(ostream& os, bool& first, bool wrapArray) const +{ + if (wrapArray) + os << '['; + if (!m_feed.empty()) { + if (first) + first = false; + else + os << ",\n"; + os << m_feed; + } + if (wrapArray) + os << "\n]"; +} + namespace { - void disco(string& s, const EntityDescriptor* entity, bool first) { - if (entity) { - const vector& idps = entity->getIDPSSODescriptors(); - if (!idps.empty()) { - auto_ptr_char entityid(entity->getEntityID()); - // Open a struct and output id: entityID. - if (first) - first = false; - else - s += ','; - s += "\n{\n \"entityID\": \""; - s += entityid.get(); - s += '\"'; - for (vector::const_iterator idp = idps.begin(); idp != idps.end(); ++idp) { - if ((*idp)->getExtensions()) { - const vector& exts = const_cast((*idp)->getExtensions())->getUnknownXMLObjects(); - for (vector::const_iterator ext = exts.begin(); ext != exts.end(); ++ext) { - const UIInfo* info = dynamic_cast(*ext); - if (info) { - const vector& dispnames = info->getDisplayNames(); - if (!dispnames.empty()) { - s += ",\n \"DisplayNames\": ["; - for (vector::const_iterator dispname = dispnames.begin(); dispname != dispnames.end(); ++dispname) { - if (dispname != dispnames.begin()) - s += ','; - auto_ptr_char val((*dispname)->getName()); - auto_ptr_char lang((*dispname)->getLang()); - s += "\n {\n \"value\": \""; - s += val.get(); - s += "\",\n \"lang\": \""; - s += lang.get(); - s += "\"\n }"; - } - s += "\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; + } + } + 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()); + // Open a struct and output id: entityID. + if (first) + first = false; + else + s += ','; + s += "\n{\n \"entityID\": \""; + json_safe(s, entityid.get()); + s += '\"'; + bool extFound = false; + bool displayNameFound = false; + for (indirect_iterator::const_iterator> idp = make_indirect_iterator(idps.begin()); + !extFound && idp != make_indirect_iterator(idps.end()); ++idp) { + if (idp->isValid(now) && idp->getExtensions()) { + const vector& exts = const_cast(idp->getExtensions())->getUnknownXMLObjects(); + for (vector::const_iterator ext = exts.begin(); !extFound && ext != exts.end(); ++ext) { + const UIInfo* info = dynamic_cast(*ext); + if (info) { + extFound = true; + const vector& dispnames = info->getDisplayNames(); + if (!dispnames.empty()) { + displayNameFound = true; + s += ",\n \"DisplayNames\": ["; + for (indirect_iterator::const_iterator> dispname = make_indirect_iterator(dispnames.begin()); + dispname != make_indirect_iterator(dispnames.end()); ++dispname) { + if (dispname.base() != dispnames.begin()) + s += ','; + auto_arrayptr val(toUTF8(dispname->getName())); + auto_ptr_char lang(dispname->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; + } + s += "\n ]"; + } + + const vector& descs = info->getDescriptions(); + if (!descs.empty()) { + s += ",\n \"Descriptions\": ["; + for (indirect_iterator::const_iterator> desc = make_indirect_iterator(descs.begin()); + desc != make_indirect_iterator(descs.end()); ++desc) { + if (desc.base() != descs.begin()) + s += ','; + auto_arrayptr val(toUTF8(desc->getDescription())); + auto_ptr_char lang(desc->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; } + s += "\n ]"; + } - const vector& descs = info->getDescriptions(); - if (!descs.empty()) { - s += ",\n \"Descriptions\": ["; - for (vector::const_iterator desc = descs.begin(); desc != descs.end(); ++desc) { - if (desc != descs.begin()) - s += ','; - auto_ptr_char val((*desc)->getDescription()); - auto_ptr_char lang((*desc)->getLang()); - s += "\n {\n \"value\": \""; - s += val.get(); - s += "\",\n \"lang\": \""; - s += lang.get(); - s += "\"\n }"; - } - s += "\n ]"; + const vector& keywords = info->getKeywordss(); + if (!keywords.empty()) { + s += ",\n \"Keywords\": ["; + for (indirect_iterator::const_iterator> words = make_indirect_iterator(keywords.begin()); + words != make_indirect_iterator(keywords.end()); ++words) { + if (words.base() != keywords.begin()) + s += ','; + auto_arrayptr val(toUTF8(words->getValues())); + auto_ptr_char lang(words->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; } + s += "\n ]"; + } - const vector& infurls = info->getInformationURLs(); - if (!infurls.empty()) { - s += ",\n \"InformationURLs\": ["; - for (vector::const_iterator infurl = infurls.begin(); infurl != infurls.end(); ++infurl) { - if (infurl != infurls.begin()) - s += ','; - auto_ptr_char val((*infurl)->getURL()); - auto_ptr_char lang((*infurl)->getLang()); - s += "\n {\n \"value\": \""; - s += val.get(); - s += "\",\n \"lang\": \""; - s += lang.get(); - s += "\"\n }"; - } - s += "\n ]"; + const vector& infurls = info->getInformationURLs(); + if (!infurls.empty()) { + s += ",\n \"InformationURLs\": ["; + for (indirect_iterator::const_iterator> infurl = make_indirect_iterator(infurls.begin()); + infurl != make_indirect_iterator(infurls.end()); ++infurl) { + if (infurl.base() != infurls.begin()) + s += ','; + auto_ptr_char val(infurl->getURL()); + auto_ptr_char lang(infurl->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; } + s += "\n ]"; + } - const vector& privs = info->getPrivacyStatementURLs(); - if (!privs.empty()) { - s += ",\n \"PrivacyStatementURLs\": ["; - for (vector::const_iterator priv = privs.begin(); priv != privs.end(); ++priv) { - if (priv != privs.begin()) - s += ','; - auto_ptr_char val((*priv)->getURL()); - auto_ptr_char lang((*priv)->getLang()); - s += "\n {\n \"value\": \""; - s += val.get(); - s += "\",\n \"lang\": \""; - s += lang.get(); - s += "\"\n }"; - } - s += "\n ]"; + const vector& privs = info->getPrivacyStatementURLs(); + if (!privs.empty()) { + s += ",\n \"PrivacyStatementURLs\": ["; + for (indirect_iterator::const_iterator> priv = make_indirect_iterator(privs.begin()); + priv != make_indirect_iterator(privs.end()); ++priv) { + if (priv.base() != privs.begin()) + s += ','; + auto_ptr_char val(priv->getURL()); + auto_ptr_char lang(priv->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; } + s += "\n ]"; + } - const vector& logos = info->getLogos(); - if (!logos.empty()) { - s += ",\n \"Logos\": ["; - for (vector::const_iterator logo = logos.begin(); logo != logos.end(); ++logo) { - if (logo != logos.begin()) - s += ','; - s += "\n {\n"; - auto_ptr_char val((*logo)->getURL()); - s += " \"value\": \""; - s += val.get(); - ostringstream ht; - ht << (*logo)->getHeight().second; - s += "\",\n \"height\": \""; - s += ht.str(); - ht.clear(); - ht << (*logo)->getWidth().second; - s += "\",\n \"width\": \""; - s += ht.str(); + const vector& logos = info->getLogos(); + if (!logos.empty()) { + s += ",\n \"Logos\": ["; + for (indirect_iterator::const_iterator> logo = make_indirect_iterator(logos.begin()); + logo != make_indirect_iterator(logos.end()); ++logo) { + if (logo.base() != logos.begin()) + s += ','; + s += "\n {\n"; + auto_ptr_char val(logo->getURL()); + s += " \"value\": \""; + json_safe(s, val.get()); + ostringstream ht; + ht << logo->getHeight().second; + s += "\",\n \"height\": \""; + s += ht.str(); + ostringstream wt; + wt << logo->getWidth().second; + s += "\",\n \"width\": \""; + s += wt.str(); + s += '\"'; + if (logo->getLang()) { + auto_ptr_char lang(logo->getLang()); + s += ",\n \"lang\": \""; + s += lang.get(); s += '\"'; - if ((*logo)->getLang()) { - auto_ptr_char lang((*logo)->getLang()); - s += ",\n \"lang\": \""; - s += lang.get(); - s += '\"'; - } - s += "\n }"; } - s += "\n ]"; + s += "\n }"; } + s += "\n ]"; } } } } - // Close the struct; - s += "\n}"; } - } - } - void disco(string& s, const EntitiesDescriptor* group, bool first) { - if (group) { - const vector& groups = group->getEntitiesDescriptors(); - for (vector::const_iterator i = groups.begin(); i != groups.end(); ++i) - disco(s, *i, first); + if (m_legacyOrgNames && !displayNameFound) { + const Organization* org = nullptr; + for (indirect_iterator::const_iterator> idp = make_indirect_iterator(idps.begin()); + !org && idp != make_indirect_iterator(idps.end()); ++idp) { + if (idp->isValid(now)) + org = idp->getOrganization(); + } + if (!org) + org = entity->getOrganization(); + if (org) { + const vector& odns = org->getOrganizationDisplayNames(); + if (!odns.empty()) { + s += ",\n \"DisplayNames\": ["; + for (indirect_iterator::const_iterator> dispname = make_indirect_iterator(odns.begin()); + dispname != make_indirect_iterator(odns.end()); ++dispname) { + if (dispname.base() != odns.begin()) + s += ','; + auto_arrayptr val(toUTF8(dispname->getName())); + auto_ptr_char lang(dispname->getLang()); + s += "\n {\n \"value\": \""; + json_safe(s, val.get()); + s += "\",\n \"lang\": \""; + s += lang.get(); + s += "\"\n }"; + } + s += "\n ]"; + } + } + } + + 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 ]"; + } - const vector& sites = group->getEntityDescriptors(); - for (vector::const_iterator j = sites.begin(); j != sites.end(); ++j) - disco(s, *j, first); + // Close the struct; + s += "\n}"; } } } -DiscoverableMetadataProvider::DiscoverableMetadataProvider() +void DiscoverableMetadataProvider::discoGroup(string& s, const EntitiesDescriptor* group, bool& first) const { + if (group) { + for_each( + group->getEntitiesDescriptors().begin(), group->getEntitiesDescriptors().end(), + lambda::bind(&DiscoverableMetadataProvider::discoGroup, this, boost::ref(s), _1, boost::ref(first)) + ); + for_each( + group->getEntityDescriptors().begin(), group->getEntityDescriptors().end(), + lambda::bind(&DiscoverableMetadataProvider::discoEntity, this, boost::ref(s), _1, boost::ref(first)) + ); + } } -DiscoverableMetadataProvider::~DiscoverableMetadataProvider() +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::generateFeed() +void DiscoverableMetadataProvider::discoAttributes(std::string& s, const vector& attrs, bool& first) const { - bool first = true; - m_feed = "["; - const XMLObject* object = getMetadata(); - disco(m_feed, dynamic_cast(object), first); - disco(m_feed, dynamic_cast(object), first); - m_feed += "\n]\n"; + for (indirect_iterator::const_iterator> a = make_indirect_iterator(attrs.begin()); + a != make_indirect_iterator(attrs.end()); ++a) { - SAMLConfig::getConfig().generateRandomBytes(m_feedTag, 4); - m_feedTag = SAMLArtifact::toHex(m_feedTag); -} - -string DiscoverableMetadataProvider::getCacheTag() const -{ - return m_feedTag; -} + if (first) { + s += ",\n \"EntityAttributes\": ["; + first = false; + } + else { + s += ','; + } -ostream& DiscoverableMetadataProvider::outputFeed(ostream& os) const -{ - return os << m_feed; + 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 }"; + } }