Add ability to generate additional metadata content based on config.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 11 Oct 2010 21:05:50 +0000 (21:05 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 11 Oct 2010 21:05:50 +0000 (21:05 +0000)
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
shibsp/attribute/resolver/AttributeExtractor.h
shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp
shibsp/attribute/resolver/impl/XMLAttributeExtractor.cpp
shibsp/handler/impl/MetadataGenerator.cpp

index 17b90cc..bf95425 100644 (file)
                 <documentation>The SAML 1 Namespace or SAML 2 NameFormat of the attribute.</documentation>
             </annotation>
         </attribute>
+        <attribute name="isRequested" type="boolean">
+          <annotation>
+            <documentation>Marks an attribute as requested by the service.</documentation>
+          </annotation>
+        </attribute>
+        <attribute name="isRequired" type="boolean">
+          <annotation>
+            <documentation>Marks an attribute as required by the service.</documentation>
+          </annotation>
+        </attribute>
     </complexType>
     
     <complexType name="AttributeDecoderType" abstract="true">
index 9e4f217..b756c5d 100644 (file)
@@ -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<std::string>& attributes) const=0;
+
+        /**
+         * Generates and/or modifies metadata reflecting the extractor,
+         * typically attribute-related requirements.
+         *
+         * <p>The default implementation does nothing.
+         *
+         * @param role          metadata role to decorate
+         */
+        virtual void generateMetadata(opensaml::saml2md::SPSSODescriptor& role) const;
     };
 
     /**
index c0f5983..d10c9a1 100644 (file)
@@ -64,6 +64,13 @@ namespace shibsp {
             }
         }
 
+        void generateMetadata(SPSSODescriptor& role) const {
+            for (vector<AttributeExtractor*>::const_iterator i=m_extractors.begin(); i!=m_extractors.end(); ++i) {
+                Locker locker(*i);
+                (*i)->generateMetadata(role);
+            }
+        }
+
     private:
         vector<AttributeExtractor*> m_extractors;
     };
@@ -96,6 +103,10 @@ AttributeExtractor::~AttributeExtractor()
 {
 }
 
+void AttributeExtractor::generateMetadata(SPSSODescriptor& role) const
+{
+}
+
 ChainingAttributeExtractor::ChainingAttributeExtractor(const DOMElement* e)
 {
     SPConfig& conf = SPConfig::getConfig();
index fd8ebd7..5cc1954 100644 (file)
@@ -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<xstring,xstring>,pair< AttributeDecoder*,vector<string> > > attrmap_t;
-#else
-        typedef map< pair<string,string>,pair< AttributeDecoder*,vector<string> > > attrmap_t;
-#endif
         attrmap_t m_attrMap;
         vector<string> m_attributeIds;
+        vector< pair< pair<xstring,xstring>,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<bool,DOMElement*> 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<string> >& decl = m_attrMap[pair<xstring,xstring>(name,format)];
-#else
-        auto_ptr_char n(name);
-        auto_ptr_char f(format);
-        pair< AttributeDecoder*,vector<string> >& decl = m_attrMap[pair<string,string>(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<AttributeConsumingService*>& svcs = const_cast<const SPSSODescriptor*>(&role)->getAttributeConsumingServices();
+    for (vector<AttributeConsumingService*>::const_iterator s =svcs.begin(); s != svcs.end(); ++s) {
+        pair<bool,int> 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<EntityDescriptor*>(role.getParent())->getEntityID());
+    static const XMLCh english[] = UNICODE_LITERAL_2(e,n);
+    sn->setLang(english);
+
+    for (vector< pair< pair<xstring,xstring>,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<Attribute*>& attributes
     ) const
 {
-#ifdef HAVE_GOOD_STL
     map< pair<xstring,xstring>,pair< AttributeDecoder*,vector<string> > >::const_iterator rule;
-#else
-    map< pair<string,string>,pair< AttributeDecoder*,vector<string> > >::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<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
-#else
-    auto_ptr_char temp(format);
-    if ((rule=m_attrMap.find(pair<string,string>(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<Attribute*>& attributes
     ) const
 {
-#ifdef HAVE_GOOD_STL
     map< pair<xstring,xstring>,pair< AttributeDecoder*,vector<string> > >::const_iterator rule;
-#else
-    map< pair<string,string>,pair< AttributeDecoder*,vector<string> > >::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<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
-#else
-    auto_ptr_char temp(format);
-    if ((rule=m_attrMap.find(pair<string,string>(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<Attribute*>& attributes
     ) const
 {
-#ifdef HAVE_GOOD_STL
     map< pair<xstring,xstring>,pair< AttributeDecoder*,vector<string> > >::const_iterator rule;
-#else
-    map< pair<string,string>,pair< AttributeDecoder*,vector<string> > >::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<xstring,xstring>(name,format))) != m_attrMap.end()) {
-#else
-    auto_ptr_char temp1(name);
-    auto_ptr_char temp2(format);
-    if ((rule=m_attrMap.find(pair<string,string>(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<Attribute*>& attributes
     ) const
 {
-#ifdef HAVE_GOOD_STL
     map< pair<xstring,xstring>,pair< AttributeDecoder*,vector<string> > >::const_iterator rule;
-#else
-    map< pair<string,string>,pair< AttributeDecoder*,vector<string> > >::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<xstring,xstring>(name,format))) != m_attrMap.end()) {
-#else
-    auto_ptr_char temp1(name);
-    auto_ptr_char temp2(format);
-    if ((rule=m_attrMap.find(pair<string,string>(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<xstring,xstring>(name,xstring()))) != m_attrMap.end()) {
-#else
-        if ((rule=m_attrMap.find(pair<string,string>(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());
     }
 }
index b3f9720..7ae1c9d 100644 (file)
@@ -29,6 +29,7 @@
 #include "handler/RemotedHandler.h"
 
 #ifndef SHIBSP_LITE
+# include "attribute/resolver/AttributeExtractor.h"
 # include "metadata/MetadataProviderCriteria.h"
 # include <saml/exceptions.h>
 # include <saml/SAMLConfig.h>
@@ -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<ContactPerson>());
+            for_each(m_reqAttrs.begin(), m_reqAttrs.end(), xmltooling::cleanup<RequestedAttribute>());
+            for_each(m_attrConsumers.begin(), m_attrConsumers.end(), xmltooling::cleanup<AttributeConsumingService>());
+#endif
+        }
 
         pair<bool,long> 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<string> m_bases;
+        UIInfo* m_uiinfo;
+        Organization* m_org;
+        EntityAttributes* m_entityAttrs;
+        vector<ContactPerson*> m_contacts;
+        vector<RequestedAttribute*> m_reqAttrs;
+        vector<AttributeConsumingService*> 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<XMLObject> child(XMLObjectBuilder::buildOneFromElement(const_cast<DOMElement*>(e)));
+            ContactPerson* cp = dynamic_cast<ContactPerson*>(child.get());
+            if (cp) {
+                child.release();
+                m_contacts.push_back(cp);
+            }
+            else {
+                RequestedAttribute* req = dynamic_cast<RequestedAttribute*>(child.get());
+                if (req) {
+                    child.release();
+                    m_reqAttrs.push_back(req);
+                }
+                else {
+                    AttributeConsumingService* acs = dynamic_cast<AttributeConsumingService*>(child.get());
+                    if (acs) {
+                        child.release();
+                        m_attrConsumers.push_back(acs);
+                    }
+                    else {
+                        UIInfo* info = dynamic_cast<UIInfo*>(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<Organization*>(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<EntityAttributes*>(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<bool,long> 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<ContactPerson*>::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<bool,long> 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<AttributeConsumingService*>::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<AttributeConsumingService*>& svcs = const_cast<const SPSSODescriptor*>(role)->getAttributeConsumingServices();
+        for (vector<AttributeConsumingService*>::const_iterator s =svcs.begin(); s != svcs.end(); ++s) {
+            pair<bool,int> 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<RequestedAttribute*>::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<bool,long> MetadataGenerator::processMessage(
         }
     }
 
+    AttributeExtractor* extractor = application.getAttributeExtractor();
+    if (extractor) {
+        Locker extlocker(extractor);
+        extractor->generateMetadata(*role);
+    }
+
     CredentialResolver* credResolver=application.getCredentialResolver();
     if (credResolver) {
         Locker credLocker(credResolver);