From 0134a743f9c47b8e7ac74076cc708f11f90603f9 Mon Sep 17 00:00:00 2001 From: cantor Date: Mon, 11 Oct 2010 21:05:50 +0000 Subject: [PATCH] Add ability to generate additional metadata content based on config. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/branches/REL_2@3336 cb58f699-b61c-0410-a6fe-9272a202ed29 --- schemas/shibboleth-2.0-attribute-map.xsd | 10 ++ shibsp/attribute/resolver/AttributeExtractor.h | 13 +- .../resolver/impl/ChainingAttributeExtractor.cpp | 11 ++ .../resolver/impl/XMLAttributeExtractor.cpp | 110 ++++++++--------- shibsp/handler/impl/MetadataGenerator.cpp | 132 ++++++++++++++++++++- 5 files changed, 208 insertions(+), 68 deletions(-) diff --git a/schemas/shibboleth-2.0-attribute-map.xsd b/schemas/shibboleth-2.0-attribute-map.xsd index 17b90cc..bf95425 100644 --- a/schemas/shibboleth-2.0-attribute-map.xsd +++ b/schemas/shibboleth-2.0-attribute-map.xsd @@ -81,6 +81,16 @@ The SAML 1 Namespace or SAML 2 NameFormat of the attribute. + + + Marks an attribute as requested by the service. + + + + + Marks an attribute as required by the service. + + diff --git a/shibsp/attribute/resolver/AttributeExtractor.h b/shibsp/attribute/resolver/AttributeExtractor.h index 9e4f217..b756c5d 100644 --- a/shibsp/attribute/resolver/AttributeExtractor.h +++ b/shibsp/attribute/resolver/AttributeExtractor.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009 Internet2 + * Copyright 2001-2010 Internet2 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ namespace xmltooling { namespace opensaml { namespace saml2md { class SAML_API RoleDescriptor; + class SAML_API SPSSODescriptor; }; }; @@ -78,6 +79,16 @@ namespace shibsp { * @param attributes array to populate */ virtual void getAttributeIds(std::vector& attributes) const=0; + + /** + * Generates and/or modifies metadata reflecting the extractor, + * typically attribute-related requirements. + * + *

The default implementation does nothing. + * + * @param role metadata role to decorate + */ + virtual void generateMetadata(opensaml::saml2md::SPSSODescriptor& role) const; }; /** diff --git a/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp index c0f5983..d10c9a1 100644 --- a/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp +++ b/shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp @@ -64,6 +64,13 @@ namespace shibsp { } } + void generateMetadata(SPSSODescriptor& role) const { + for (vector::const_iterator i=m_extractors.begin(); i!=m_extractors.end(); ++i) { + Locker locker(*i); + (*i)->generateMetadata(role); + } + } + private: vector m_extractors; }; @@ -96,6 +103,10 @@ AttributeExtractor::~AttributeExtractor() { } +void AttributeExtractor::generateMetadata(SPSSODescriptor& role) const +{ +} + ChainingAttributeExtractor::ChainingAttributeExtractor(const DOMElement* e) { SPConfig& conf = SPConfig::getConfig(); diff --git a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp index fd8ebd7..5cc1954 100644 --- a/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp +++ b/shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp @@ -152,16 +152,15 @@ namespace shibsp { attributes.insert(attributes.end(), m_attributeIds.begin(), m_attributeIds.end()); } + void generateMetadata(SPSSODescriptor& role) const; + private: Category& m_log; DOMDocument* m_document; -#ifdef HAVE_GOOD_STL typedef map< pair,pair< AttributeDecoder*,vector > > attrmap_t; -#else - typedef map< pair,pair< AttributeDecoder*,vector > > attrmap_t; -#endif attrmap_t m_attrMap; vector m_attributeIds; + vector< pair< pair,bool > > m_requestedAttrs; // settings for embedded assertions in metadata string m_policyId; @@ -196,6 +195,11 @@ namespace shibsp { m_impl->getAttributeIds(attributes); } + void generateMetadata(SPSSODescriptor& role) const { + if (m_impl) + m_impl->generateMetadata(role); + } + protected: pair background_load(); @@ -217,6 +221,7 @@ namespace shibsp { static const XMLCh _AttributeFilter[] = UNICODE_LITERAL_15(A,t,t,r,i,b,u,t,e,F,i,l,t,e,r); static const XMLCh Attributes[] = UNICODE_LITERAL_10(A,t,t,r,i,b,u,t,e,s); static const XMLCh _id[] = UNICODE_LITERAL_2(i,d); + static const XMLCh isRequested[] = UNICODE_LITERAL_11(i,s,R,e,q,u,e,s,t,e,d); static const XMLCh _MetadataProvider[] = UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r); static const XMLCh _name[] = UNICODE_LITERAL_4(n,a,m,e); static const XMLCh nameFormat[] = UNICODE_LITERAL_10(n,a,m,e,F,o,r,m,a,t); @@ -345,13 +350,7 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) format = &chNull; // ignore default Format/Namespace values // Fetch/create the map entry and see if it's a duplicate rule. -#ifdef HAVE_GOOD_STL pair< AttributeDecoder*,vector >& decl = m_attrMap[pair(name,format)]; -#else - auto_ptr_char n(name); - auto_ptr_char f(format); - pair< AttributeDecoder*,vector >& decl = m_attrMap[pair(n.get(),f.get())]; -#endif if (decl.first) { m_log.warn("skipping duplicate Attribute mapping (same name and nameFormat)"); delete decoder; @@ -360,10 +359,8 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) } if (m_log.isInfoEnabled()) { -#ifdef HAVE_GOOD_STL auto_ptr_char n(name); auto_ptr_char f(format); -#endif m_log.info("creating mapping for Attribute %s%s%s", n.get(), *f.get() ? ", Format/Namespace:" : "", f.get()); } @@ -371,6 +368,12 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) decl.second.push_back(id.get()); m_attributeIds.push_back(id.get()); + // Check for isRequired/isRequested. + bool requested = XMLHelper::getAttrBool(child, false, isRequested); + bool required = XMLHelper::getAttrBool(child, false, RequestedAttribute::ISREQUIRED_ATTRIB_NAME); + if (required || requested) + m_requestedAttrs.push_back(make_pair(make_pair(name,format), required)); + name = child->getAttributeNS(nullptr, _aliases); if (name && *name) { auto_ptr_char aliases(name); @@ -401,6 +404,39 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log) m_attrLock = RWLock::create(); } +void XMLExtractorImpl::generateMetadata(SPSSODescriptor& role) const +{ + if (m_requestedAttrs.empty()) + return; + int index = 1; + const vector& svcs = const_cast(&role)->getAttributeConsumingServices(); + for (vector::const_iterator s =svcs.begin(); s != svcs.end(); ++s) { + pair i = (*s)->getIndex(); + if (i.first && index == i.second) + index = i.second + 1; + } + AttributeConsumingService* svc = AttributeConsumingServiceBuilder::buildAttributeConsumingService(); + role.getAttributeConsumingServices().push_back(svc); + svc->setIndex(index); + ServiceName* sn = ServiceNameBuilder::buildServiceName(); + svc->getServiceNames().push_back(sn); + sn->setName(dynamic_cast(role.getParent())->getEntityID()); + static const XMLCh english[] = UNICODE_LITERAL_2(e,n); + sn->setLang(english); + + for (vector< pair< pair,bool > >::const_iterator i = m_requestedAttrs.begin(); i != m_requestedAttrs.end(); ++i) { + RequestedAttribute* req = RequestedAttributeBuilder::buildRequestedAttribute(); + svc->getRequestedAttributes().push_back(req); + req->setName(i->first.first.c_str()); + if (i->first.second.empty()) + req->setNameFormat(saml2::Attribute::URI_REFERENCE); + else + req->setNameFormat(i->first.second.c_str()); + if (i->second) + req->isRequired(true); + } +} + void XMLExtractorImpl::extractAttributes( const Application& application, const char* assertingParty, @@ -409,29 +445,18 @@ void XMLExtractorImpl::extractAttributes( vector& attributes ) const { -#ifdef HAVE_GOOD_STL map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#else - map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#endif const XMLCh* format = nameid.getFormat(); if (!format || !*format) format = NameIdentifier::UNSPECIFIED; -#ifdef HAVE_GOOD_STL if ((rule=m_attrMap.find(pair(format,xstring()))) != m_attrMap.end()) { -#else - auto_ptr_char temp(format); - if ((rule=m_attrMap.find(pair(temp.get(),string()))) != m_attrMap.end()) { -#endif Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty); if (a) attributes.push_back(a); } else if (m_log.isDebugEnabled()) { -#ifdef HAVE_GOOD_STL auto_ptr_char temp(format); -#endif m_log.debug("skipping unmapped NameIdentifier with format (%s)", temp.get()); } } @@ -444,29 +469,18 @@ void XMLExtractorImpl::extractAttributes( vector& attributes ) const { -#ifdef HAVE_GOOD_STL map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#else - map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#endif const XMLCh* format = nameid.getFormat(); if (!format || !*format) format = NameID::UNSPECIFIED; -#ifdef HAVE_GOOD_STL if ((rule=m_attrMap.find(pair(format,xstring()))) != m_attrMap.end()) { -#else - auto_ptr_char temp(format); - if ((rule=m_attrMap.find(pair(temp.get(),string()))) != m_attrMap.end()) { -#endif Attribute* a = rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty); if (a) attributes.push_back(a); } else if (m_log.isDebugEnabled()) { -#ifdef HAVE_GOOD_STL auto_ptr_char temp(format); -#endif m_log.debug("skipping unmapped NameID with format (%s)", temp.get()); } } @@ -479,11 +493,7 @@ void XMLExtractorImpl::extractAttributes( vector& attributes ) const { -#ifdef HAVE_GOOD_STL map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#else - map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#endif const XMLCh* name = attr.getAttributeName(); const XMLCh* format = attr.getAttributeNamespace(); @@ -491,22 +501,14 @@ void XMLExtractorImpl::extractAttributes( return; if (!format || XMLString::equals(format, shibspconstants::SHIB1_ATTRIBUTE_NAMESPACE_URI)) format = &chNull; -#ifdef HAVE_GOOD_STL if ((rule=m_attrMap.find(pair(name,format))) != m_attrMap.end()) { -#else - auto_ptr_char temp1(name); - auto_ptr_char temp2(format); - if ((rule=m_attrMap.find(pair(temp1.get(),temp2.get()))) != m_attrMap.end()) { -#endif Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty); if (a) attributes.push_back(a); } else if (m_log.isInfoEnabled()) { -#ifdef HAVE_GOOD_STL auto_ptr_char temp1(name); auto_ptr_char temp2(format); -#endif m_log.info("skipping unmapped SAML 1.x Attribute with Name: %s%s%s", temp1.get(), *temp2.get() ? ", Namespace:" : "", temp2.get()); } } @@ -519,11 +521,7 @@ void XMLExtractorImpl::extractAttributes( vector& attributes ) const { -#ifdef HAVE_GOOD_STL map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#else - map< pair,pair< AttributeDecoder*,vector > >::const_iterator rule; -#endif const XMLCh* name = attr.getName(); const XMLCh* format = attr.getNameFormat(); @@ -534,13 +532,7 @@ void XMLExtractorImpl::extractAttributes( else if (XMLString::equals(format, saml2::Attribute::URI_REFERENCE)) format = &chNull; -#ifdef HAVE_GOOD_STL if ((rule=m_attrMap.find(pair(name,format))) != m_attrMap.end()) { -#else - auto_ptr_char temp1(name); - auto_ptr_char temp2(format); - if ((rule=m_attrMap.find(pair(temp1.get(),temp2.get()))) != m_attrMap.end()) { -#endif Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty); if (a) { attributes.push_back(a); @@ -549,11 +541,7 @@ void XMLExtractorImpl::extractAttributes( } else if (XMLString::equals(format, saml2::Attribute::UNSPECIFIED)) { // As a fallback, if the format is "unspecified", null out the value and re-map. -#ifdef HAVE_GOOD_STL if ((rule=m_attrMap.find(pair(name,xstring()))) != m_attrMap.end()) { -#else - if ((rule=m_attrMap.find(pair(temp1.get(),string()))) != m_attrMap.end()) { -#endif Attribute* a = rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty); if (a) { attributes.push_back(a); @@ -563,10 +551,8 @@ void XMLExtractorImpl::extractAttributes( } if (m_log.isInfoEnabled()) { -#ifdef HAVE_GOOD_STL auto_ptr_char temp1(name); auto_ptr_char temp2(format); -#endif m_log.info("skipping unmapped SAML 2.0 Attribute with Name: %s%s%s", temp1.get(), *temp2.get() ? ", Format:" : "", temp2.get()); } } diff --git a/shibsp/handler/impl/MetadataGenerator.cpp b/shibsp/handler/impl/MetadataGenerator.cpp index b3f9720..7ae1c9d 100644 --- a/shibsp/handler/impl/MetadataGenerator.cpp +++ b/shibsp/handler/impl/MetadataGenerator.cpp @@ -29,6 +29,7 @@ #include "handler/RemotedHandler.h" #ifndef SHIBSP_LITE +# include "attribute/resolver/AttributeExtractor.h" # include "metadata/MetadataProviderCriteria.h" # include # include @@ -82,7 +83,16 @@ namespace shibsp { { public: MetadataGenerator(const DOMElement* e, const char* appId); - virtual ~MetadataGenerator() {} + virtual ~MetadataGenerator() { +#ifndef SHIBSP_LITE + delete m_uiinfo; + delete m_org; + delete m_entityAttrs; + for_each(m_contacts.begin(), m_contacts.end(), xmltooling::cleanup()); + for_each(m_reqAttrs.begin(), m_reqAttrs.end(), xmltooling::cleanup()); + for_each(m_attrConsumers.begin(), m_attrConsumers.end(), xmltooling::cleanup()); +#endif + } pair run(SPRequest& request, bool isHandler=true) const; void receive(DDF& in, ostream& out); @@ -100,6 +110,12 @@ namespace shibsp { string m_salt; short m_http,m_https; vector m_bases; + UIInfo* m_uiinfo; + Organization* m_org; + EntityAttributes* m_entityAttrs; + vector m_contacts; + vector m_reqAttrs; + vector m_attrConsumers; #endif }; @@ -117,7 +133,7 @@ namespace shibsp { MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".MetadataGenerator"), &g_Blocker) #ifndef SHIBSP_LITE - ,m_http(0), m_https(0) + ,m_http(0), m_https(0), m_uiinfo(nullptr), m_org(nullptr), m_entityAttrs(nullptr) #endif { string address(appId); @@ -152,14 +168,73 @@ MetadataGenerator::MetadataGenerator(const DOMElement* e, const char* appId) if (flag.first) m_https = flag.second ? 1 : -1; - e = XMLHelper::getFirstChildElement(e, EndpointBase); + e = XMLHelper::getFirstChildElement(e); while (e) { - if (e->hasChildNodes()) { + if (XMLString::equals(e->getLocalName(), EndpointBase) && e->hasChildNodes()) { auto_ptr_char base(e->getFirstChild()->getNodeValue()); if (base.get() && *base.get()) m_bases.push_back(base.get()); } - e = XMLHelper::getNextSiblingElement(e, EndpointBase); + else { + // Try and parse the object. + auto_ptr child(XMLObjectBuilder::buildOneFromElement(const_cast(e))); + ContactPerson* cp = dynamic_cast(child.get()); + if (cp) { + child.release(); + m_contacts.push_back(cp); + } + else { + RequestedAttribute* req = dynamic_cast(child.get()); + if (req) { + child.release(); + m_reqAttrs.push_back(req); + } + else { + AttributeConsumingService* acs = dynamic_cast(child.get()); + if (acs) { + child.release(); + m_attrConsumers.push_back(acs); + } + else { + UIInfo* info = dynamic_cast(child.get()); + if (info) { + if (!m_uiinfo) { + child.release(); + m_uiinfo = info; + } + else { + m_log.warn("skipping duplicate UIInfo element"); + } + } + else { + Organization* org = dynamic_cast(child.get()); + if (org) { + if (!m_org) { + child.release(); + m_org = org; + } + else { + m_log.warn("skipping duplicate Organization element"); + } + } + else { + EntityAttributes* ea = dynamic_cast(child.get()); + if (ea) { + if (!m_entityAttrs) { + child.release(); + m_entityAttrs = ea; + } + else { + m_log.warn("skipping duplicate EntityAttributes element"); + } + } + } + } + } + } + } + } + e = XMLHelper::getNextSiblingElement(e); } #endif } @@ -284,6 +359,18 @@ pair MetadataGenerator::processMessage( entity->setValidUntil(time(nullptr) + cache.second); entity->setEntityID(relyingParty->getXMLString("entityID").second); + if (m_org && !entity->getOrganization()) + entity->setOrganization(m_org->cloneOrganization()); + + for (vector::const_iterator cp = m_contacts.begin(); cp != m_contacts.end(); ++cp) + entity->getContactPersons().push_back((*cp)->cloneContactPerson()); + + if (m_entityAttrs) { + if (!entity->getExtensions()) + entity->setExtensions(ExtensionsBuilder::buildExtensions()); + entity->getExtensions()->getUnknownXMLObjects().push_back(m_entityAttrs->cloneEntityAttributes()); + } + SPSSODescriptor* role; if (entity->getSPSSODescriptors().empty()) { role = SPSSODescriptorBuilder::buildSPSSODescriptor(); @@ -293,6 +380,35 @@ pair MetadataGenerator::processMessage( role = entity->getSPSSODescriptors().front(); } + if (m_uiinfo) { + if (!role->getExtensions()) + role->setExtensions(ExtensionsBuilder::buildExtensions()); + role->getExtensions()->getUnknownXMLObjects().push_back(m_uiinfo->cloneUIInfo()); + } + + for (vector::const_iterator acs = m_attrConsumers.begin(); acs != m_attrConsumers.end(); ++acs) + role->getAttributeConsumingServices().push_back((*acs)->cloneAttributeConsumingService()); + + if (!m_reqAttrs.empty()) { + int index = 1; + const vector& svcs = const_cast(role)->getAttributeConsumingServices(); + for (vector::const_iterator s =svcs.begin(); s != svcs.end(); ++s) { + pair i = (*s)->getIndex(); + if (i.first && index == i.second) + index = i.second + 1; + } + AttributeConsumingService* svc = AttributeConsumingServiceBuilder::buildAttributeConsumingService(); + role->getAttributeConsumingServices().push_back(svc); + svc->setIndex(index); + ServiceName* sn = ServiceNameBuilder::buildServiceName(); + svc->getServiceNames().push_back(sn); + sn->setName(entity->getEntityID()); + static const XMLCh english[] = UNICODE_LITERAL_2(e,n); + sn->setLang(english); + for (vector::const_iterator req = m_reqAttrs.begin(); req != m_reqAttrs.end(); ++req) + svc->getRequestedAttributes().push_back((*req)->cloneRequestedAttribute()); + } + // Policy flags. prop = relyingParty->getString("signing"); if (prop.first && (!strcmp(prop.second,"true") || !strcmp(prop.second,"front"))) @@ -331,6 +447,12 @@ pair MetadataGenerator::processMessage( } } + AttributeExtractor* extractor = application.getAttributeExtractor(); + if (extractor) { + Locker extlocker(extractor); + extractor->generateMetadata(*role); + } + CredentialResolver* credResolver=application.getCredentialResolver(); if (credResolver) { Locker credLocker(credResolver); -- 2.1.4