https://issues.shibboleth.net/jira/browse/SSPCPP-339
authorScott Cantor <cantor.2@osu.edu>
Fri, 27 Jan 2012 02:44:20 +0000 (02:44 +0000)
committerScott Cantor <cantor.2@osu.edu>
Fri, 27 Jan 2012 02:44:20 +0000 (02:44 +0000)
13 files changed:
adfs/adfs.cpp
shibsp/Makefile.am
shibsp/attribute/DOMAttributeDecoder.cpp
shibsp/attribute/resolver/AttributeExtractor.h
shibsp/attribute/resolver/impl/ChainingAttributeExtractor.cpp
shibsp/attribute/resolver/impl/MetadataAttributeExtractor.cpp
shibsp/handler/AssertionConsumerService.h
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/SAML1Consumer.cpp
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp.vcxproj
shibsp/shibsp.vcxproj.filters

index e84729e..7a6172f 100644 (file)
@@ -763,6 +763,7 @@ void ADFSConsumer::implementProtocol(
     scoped_ptr<ResolutionContext> ctx(
         resolveAttributes(
             application,
+            &httpRequest,
             policy.getIssuerMetadata(),
             m_protocol.get(),
             saml1name,
index 0170384..d2b2358 100644 (file)
@@ -211,6 +211,7 @@ libshibsp_la_SOURCES = \
        attribute/resolver/impl/ChainingAttributeExtractor.cpp \
        attribute/resolver/impl/DelegationAttributeExtractor.cpp \
        attribute/resolver/impl/KeyDescriptorAttributeExtractor.cpp \
+    attribute/resolver/impl/MetadataAttributeExtractor.cpp \
        attribute/resolver/impl/XMLAttributeExtractor.cpp \
        binding/impl/ArtifactResolver.cpp \
        binding/impl/SOAPClient.cpp \
index 5238164..8b2b7e1 100644 (file)
@@ -96,13 +96,12 @@ Attribute* DOMAttributeDecoder::decode(
 {
     Category& log = Category::getInstance(SHIBSP_LOGCAT".AttributeDecoder.DOM");
 
-    if (!xmlObject || !XMLString::equals(saml1::Attribute::LOCAL_NAME, xmlObject->getElementQName().getLocalPart())) {
-        log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");
+    if (!xmlObject)
         return nullptr;
-    }
 
     auto_ptr<ExtensibleAttribute> attr(new ExtensibleAttribute(ids, m_formatter.c_str()));
     DDF dest = attr->getValues();
+    vector<XMLObject*> genericObjectWrapper;    // used to support stand-alone object decoding
     vector<XMLObject*>::const_iterator v,stop;
 
     const saml2::Attribute* saml2attr = dynamic_cast<const saml2::Attribute*>(xmlObject);
@@ -133,8 +132,10 @@ Attribute* DOMAttributeDecoder::decode(
             }
         }
         else {
-            log.warn("XMLObject type not recognized by DOMAttributeDecoder, no values returned");
-            return nullptr;
+            log.debug("decoding arbitrary XMLObject type (%s)", xmlObject->getElementQName().toString().c_str());
+            genericObjectWrapper.push_back(const_cast<XMLObject*>(xmlObject));
+            v = genericObjectWrapper.begin();
+            stop = genericObjectWrapper.end();
         }
     }
 
@@ -146,7 +147,7 @@ Attribute* DOMAttributeDecoder::decode(
                 dest.add(converted);
         }
         else
-            log.warn("skipping AttributeValue without a backing DOM");
+            log.warn("skipping XMLObject without a backing DOM");
     }
 
     return dest.integer() ? _decode(attr.release()) : nullptr;
@@ -194,7 +195,7 @@ DDF DOMAttributeDecoder::convert(DOMElement* e, bool nameit) const
     DOMElement* child = XMLHelper::getFirstChildElement(e);
     if (!child && e->hasChildNodes() && e->getFirstChild()->getNodeType() == DOMNode::TEXT_NODE) {
         // Attach a _text member if a text node is present.
-        obj.addmember("_string").string(toUTF8(e->getFirstChild()->getNodeValue(), true), false);
+        obj.addmember("_string").string(toUTF8(e->getFirstChild()->getTextContent(), true), false);
     }
     else {
         while (child) {
index 7c40d04..08889b8 100644 (file)
@@ -61,6 +61,7 @@ namespace shibsp {
         virtual ~AttributeExtractor();
 
         /**
+         * @deprecated
          * Extracts the attributes found in an XMLObject.
          *
          * @param application   Application performing the extraction
@@ -75,7 +76,26 @@ namespace shibsp {
             const opensaml::saml2md::RoleDescriptor* issuer,
             const xmltooling::XMLObject& xmlObject,
             std::vector<Attribute*>& attributes
-            ) const=0;
+            ) const;
+
+        /**
+         * Extracts the attributes found in an XMLObject.
+         *
+         * @param application   Application performing the extraction
+         * @param request       request triggering the extraction, if any
+         * @param issuer        source of object, if known
+         * @param xmlObject     object to extract
+         * @param attributes    an array to populate with the extracted attributes
+         *
+         * @throws AttributeExtractionException thrown if there is a problem extracting attributes
+         */
+        virtual void extractAttributes(
+            const Application& application,
+            const xmltooling::GenericRequest* request,
+            const opensaml::saml2md::RoleDescriptor* issuer,
+            const xmltooling::XMLObject& xmlObject,
+            std::vector<Attribute*>& attributes
+            ) const;
 
         /**
          * Populates an array with the set of Attribute IDs that might be generated.
@@ -106,6 +126,9 @@ namespace shibsp {
     /** AttributeExtractor for SAML assertion information. */
     #define ASSERTION_ATTRIBUTE_EXTRACTOR "Assertion"
 
+    /** AttributeExtractor for SAML metadata information. */
+    #define METADATA_ATTRIBUTE_EXTRACTOR "Metadata"
+
     /** AttributeExtractor for DelegationRestriction information. */
     #define DELEGATION_ATTRIBUTE_EXTRACTOR "Delegation"
 
index 7411668..850e753 100644 (file)
@@ -60,9 +60,20 @@ namespace shibsp {
             const XMLObject& xmlObject,
             vector<Attribute*>& attributes
             ) const {
+            // Make sure new version gets run.
+            extractAttributes(application, nullptr, issuer, xmlObject, attributes);
+        }
+
+        void extractAttributes(
+            const Application& application,
+            const GenericRequest* request,
+            const RoleDescriptor* issuer,
+            const XMLObject& xmlObject,
+            vector<Attribute*>& attributes
+            ) const {
             for (ptr_vector<AttributeExtractor>::iterator i = m_extractors.begin(); i != m_extractors.end(); ++i) {
                 Locker locker(&(*i));
-                i->extractAttributes(application, issuer, xmlObject, attributes);
+                i->extractAttributes(application, request, issuer, xmlObject, attributes);
             }
         }
 
@@ -88,6 +99,7 @@ namespace shibsp {
     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
 
     SHIBSP_DLLLOCAL PluginManager<AttributeExtractor,string,const DOMElement*>::Factory AssertionAttributeExtractorFactory;
+    SHIBSP_DLLLOCAL PluginManager<AttributeExtractor,string,const DOMElement*>::Factory MetadataAttributeExtractorFactory;
     SHIBSP_DLLLOCAL PluginManager<AttributeExtractor,string,const DOMElement*>::Factory DelegationAttributeExtractorFactory;
     SHIBSP_DLLLOCAL PluginManager<AttributeExtractor,string,const DOMElement*>::Factory KeyDescriptorAttributeExtractorFactory;
     SHIBSP_DLLLOCAL PluginManager<AttributeExtractor,string,const DOMElement*>::Factory XMLAttributeExtractorFactory;
@@ -100,6 +112,7 @@ namespace shibsp {
 void SHIBSP_API shibsp::registerAttributeExtractors()
 {
     SPConfig::getConfig().AttributeExtractorManager.registerFactory(ASSERTION_ATTRIBUTE_EXTRACTOR, AssertionAttributeExtractorFactory);
+    SPConfig::getConfig().AttributeExtractorManager.registerFactory(METADATA_ATTRIBUTE_EXTRACTOR, MetadataAttributeExtractorFactory);
     SPConfig::getConfig().AttributeExtractorManager.registerFactory(DELEGATION_ATTRIBUTE_EXTRACTOR, DelegationAttributeExtractorFactory);
     SPConfig::getConfig().AttributeExtractorManager.registerFactory(KEYDESCRIPTOR_ATTRIBUTE_EXTRACTOR, KeyDescriptorAttributeExtractorFactory);
     SPConfig::getConfig().AttributeExtractorManager.registerFactory(XML_ATTRIBUTE_EXTRACTOR, XMLAttributeExtractorFactory);
@@ -118,6 +131,28 @@ void AttributeExtractor::generateMetadata(SPSSODescriptor& role) const
 {
 }
 
+void AttributeExtractor::extractAttributes(
+    const Application& application,
+    const GenericRequest* request,
+    const RoleDescriptor* issuer,
+    const XMLObject& xmlObject,
+    vector<Attribute*>& attributes
+    ) const
+{
+    // Default call into deprecated method.
+    extractAttributes(application, issuer, xmlObject, attributes);
+}
+
+void AttributeExtractor::extractAttributes(
+    const Application& application,
+    const RoleDescriptor* issuer,
+    const XMLObject& xmlObject,
+    vector<Attribute*>& attributes
+    ) const
+{
+    // Empty default for deprecated method.
+}
+
 ChainingAttributeExtractor::ChainingAttributeExtractor(const DOMElement* e)
 {
     SPConfig& conf = SPConfig::getConfig();
index 2060f22..4f4a8d2 100644 (file)
 #include "Application.h"
 #include "ServiceProvider.h"
 #include "attribute/SimpleAttribute.h"
+#include "attribute/AttributeDecoder.h"
 #include "attribute/resolver/AttributeExtractor.h"
 
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/iterator/indirect_iterator.hpp>
+#include <boost/tuple/tuple.hpp>
 #include <saml/saml2/metadata/Metadata.h>
 #include <xmltooling/util/XMLHelper.h>
+#include <xercesc/util/XMLStringTokenizer.hpp>
 #include <xercesc/util/XMLUniDefs.hpp>
 
 using namespace shibsp;
 using namespace opensaml::saml2md;
 using namespace opensaml;
 using namespace xmltooling;
+using namespace boost;
 using namespace std;
 
 namespace shibsp {
@@ -60,16 +67,26 @@ namespace shibsp {
         void unlock() {
         }
 
+        // deprecated
         void extractAttributes(
             const Application& application,
             const RoleDescriptor* issuer,
             const XMLObject& xmlObject,
             vector<shibsp::Attribute*>& attributes
+            ) const {
+            extractAttributes(application, nullptr, issuer, xmlObject, attributes);
+        }
+
+        void extractAttributes(
+            const Application& application,
+            const GenericRequest* request,
+            const RoleDescriptor* issuer,
+            const XMLObject& xmlObject,
+            vector<shibsp::Attribute*>& attributes
             ) const;
         void getAttributeIds(vector<string>& attributes) const;
 
     private:
-        bool m_langFromClient;
         string m_attributeProfiles,
             m_errorURL,
             m_displayName,
@@ -78,27 +95,12 @@ namespace shibsp {
             m_privacyURL,
             m_orgName,
             m_orgDisplayName,
-            m_orgURL,
-            m_techContactName,
-            m_techContactCompany,
-            m_techContactEmail,
-            m_techContactPhone,
-            m_supportContactName,
-            m_supportContactCompany,
-            m_supportContactEmail,
-            m_supportContactPhone,
-            m_adminContactName,
-            m_adminContactCompany,
-            m_adminContactEmail,
-            m_adminContactPhone,
-            m_billContactName,
-            m_billContactCompany,
-            m_billContactEmail,
-            m_billContactPhone,
-            m_otherContactName,
-            m_otherContactCompany,
-            m_otherContactEmail,
-            m_otherContactPhone;
+            m_orgURL;
+        typedef tuple< string,xstring,boost::shared_ptr<AttributeDecoder> > contact_tuple_t;
+        vector<contact_tuple_t> m_contacts;  // tuple is attributeID, contact type, decoder
+
+        template <class T> void doLangSensitive(const GenericRequest*, const vector<T*>&, const string&, vector<shibsp::Attribute*>&) const;
+        void doContactPerson(const RoleDescriptor*, const contact_tuple_t&, vector<shibsp::Attribute*>&) const;
     };
 
 #if defined (_MSC_VER)
@@ -110,240 +112,190 @@ namespace shibsp {
         return new MetadataExtractor(e);
     }
 
+    static const XMLCh _id[] = UNICODE_LITERAL_2(i,d);
+    static const XMLCh _formatter[] = UNICODE_LITERAL_9(f,o,r,m,a,t,t,e,r);
 };
 
 MetadataExtractor::MetadataExtractor(const DOMElement* e)
-    : m_authnAuthority(XMLHelper::getAttrString(e, nullptr, AuthenticatingAuthority::LOCAL_NAME)),
-        m_authnClass(XMLHelper::getAttrString(e, nullptr, AuthnContextClassRef::LOCAL_NAME)),
-        m_authnDecl(XMLHelper::getAttrString(e, nullptr, AuthnContextDeclRef::LOCAL_NAME)),
-        m_authnInstant(XMLHelper::getAttrString(e, nullptr, AuthnStatement::AUTHNINSTANT_ATTRIB_NAME)),
-        m_issuer(XMLHelper::getAttrString(e, nullptr, Issuer::LOCAL_NAME)),
-        m_notOnOrAfter(XMLHelper::getAttrString(e, nullptr, saml2::Conditions::NOTONORAFTER_ATTRIB_NAME)),
-        m_sessionIndex(XMLHelper::getAttrString(e, nullptr, AuthnStatement::SESSIONINDEX_ATTRIB_NAME)),
-        m_sessionNotOnOrAfter(XMLHelper::getAttrString(e, nullptr, AuthnStatement::SESSIONNOTONORAFTER_ATTRIB_NAME)),
-        m_subjectAddress(XMLHelper::getAttrString(e, nullptr, saml2::SubjectLocality::ADDRESS_ATTRIB_NAME)),
-        m_subjectDNS(XMLHelper::getAttrString(e, nullptr, saml2::SubjectLocality::DNSNAME_ATTRIB_NAME))
+    : m_attributeProfiles(XMLHelper::getAttrString(e, nullptr, AttributeProfile::LOCAL_NAME)),
+        m_errorURL(XMLHelper::getAttrString(e, nullptr, RoleDescriptor::ERRORURL_ATTRIB_NAME)),
+        m_displayName(XMLHelper::getAttrString(e, nullptr, DisplayName::LOCAL_NAME)),
+        m_description(XMLHelper::getAttrString(e, nullptr, Description::LOCAL_NAME)),
+        m_informationURL(XMLHelper::getAttrString(e, nullptr, InformationURL::LOCAL_NAME)),
+        m_privacyURL(XMLHelper::getAttrString(e, nullptr, PrivacyStatementURL::LOCAL_NAME)),
+        m_orgName(XMLHelper::getAttrString(e, nullptr, OrganizationName::LOCAL_NAME)),
+        m_orgDisplayName(XMLHelper::getAttrString(e, nullptr, OrganizationDisplayName::LOCAL_NAME)),
+        m_orgURL(XMLHelper::getAttrString(e, nullptr, OrganizationURL::LOCAL_NAME))
 {
+    e = e ? XMLHelper::getFirstChildElement(e, ContactPerson::LOCAL_NAME) : nullptr;
+    while (e) {
+        string id(XMLHelper::getAttrString(e, nullptr, _id));
+        const XMLCh* type = e->getAttributeNS(nullptr, ContactPerson::CONTACTTYPE_ATTRIB_NAME);
+        if (!id.empty() && type && *type) {
+            boost::shared_ptr<AttributeDecoder> decoder(SPConfig::getConfig().AttributeDecoderManager.newPlugin(DOMAttributeDecoderType, e));
+            m_contacts.push_back(contact_tuple_t(id, type, decoder));
+        }
+        e = XMLHelper::getNextSiblingElement(e, ContactPerson::LOCAL_NAME);
+    }
+}
+
+void MetadataExtractor::getAttributeIds(vector<string>& attributes) const
+{
+    if (!m_attributeProfiles.empty())
+        attributes.push_back(m_attributeProfiles);
+    if (!m_errorURL.empty())
+        attributes.push_back(m_errorURL);
+    if (!m_displayName.empty())
+        attributes.push_back(m_displayName);
+    if (!m_description.empty())
+        attributes.push_back(m_description);
+    if (!m_informationURL.empty())
+        attributes.push_back(m_informationURL);
+    if (!m_privacyURL.empty())
+        attributes.push_back(m_privacyURL);
+    if (!m_orgName.empty())
+        attributes.push_back(m_orgName);
+    if (!m_orgDisplayName.empty())
+        attributes.push_back(m_orgDisplayName);
+    if (!m_orgURL.empty())
+        attributes.push_back(m_orgURL);
+    static void (vector<string>::* push_back)(const string&) = &vector<string>::push_back;
+    static const string& (contact_tuple_t::* tget)() const = &contact_tuple_t::get<0>;
+    for_each(m_contacts.begin(), m_contacts.end(), boost::bind(push_back, boost::ref(attributes), boost::bind(tget, _1)));
 }
 
 void MetadataExtractor::extractAttributes(
-    const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector<shibsp::Attribute*>& attributes
+    const Application& application,
+    const GenericRequest* request,
+    const RoleDescriptor* issuer,
+    const XMLObject& xmlObject,
+    vector<shibsp::Attribute*>& attributes
     ) const
 {
-    const saml2::Assertion* saml2assertion = dynamic_cast<const saml2::Assertion*>(&xmlObject);
-    if (saml2assertion) {
-        // Issuer
-        if (!m_issuer.empty()) {
-            const Issuer* i = saml2assertion->getIssuer();
-            if (i && (!i->getFormat() || !*(i->getFormat()) || XMLString::equals(i->getFormat(), NameIDType::ENTITY))) {
-                auto_ptr<SimpleAttribute> issuer(new SimpleAttribute(vector<string>(1, m_issuer)));
-                auto_ptr_char temp(i->getName());
-                if (temp.get()) {
-                    issuer->getValues().push_back(temp.get());
-                    attributes.push_back(issuer.release());
-                }
-            }
-        }
+    const RoleDescriptor* roleToExtract = dynamic_cast<const RoleDescriptor*>(&xmlObject);
+    if (!roleToExtract)
+        return;
 
-        // NotOnOrAfter
-        if (!m_notOnOrAfter.empty() && saml2assertion->getConditions() && saml2assertion->getConditions()->getNotOnOrAfter()) {
-            auto_ptr<SimpleAttribute> notonorafter(new SimpleAttribute(vector<string>(1, m_notOnOrAfter)));
-            auto_ptr_char temp(saml2assertion->getConditions()->getNotOnOrAfter()->getRawData());
-            if (temp.get()) {
-                notonorafter->getValues().push_back(temp.get());
-                attributes.push_back(notonorafter.release());
-            }
+    if (!m_attributeProfiles.empty()) {
+        const vector<AttributeProfile*>* profiles = nullptr;
+        const IDPSSODescriptor* idpRole = dynamic_cast<const IDPSSODescriptor*>(roleToExtract);
+        if (idpRole) {
+            profiles = &(idpRole->getAttributeProfiles());
         }
-    }
-    else {
-        const AuthnStatement* saml2statement = dynamic_cast<const AuthnStatement*>(&xmlObject);
-        if (saml2statement) {
-            // AuthnInstant
-            if (!m_authnInstant.empty() && saml2statement->getAuthnInstant()) {
-                auto_ptr<SimpleAttribute> authninstant(new SimpleAttribute(vector<string>(1, m_authnInstant)));
-                auto_ptr_char temp(saml2statement->getAuthnInstant()->getRawData());
-                if (temp.get()) {
-                    authninstant->getValues().push_back(temp.get());
-                    attributes.push_back(authninstant.release());
-                }
+        else {
+            const AttributeAuthorityDescriptor* aaRole = dynamic_cast<const AttributeAuthorityDescriptor*>(roleToExtract);
+            if (aaRole) {
+                profiles = &(aaRole->getAttributeProfiles());
             }
-
-            // SessionIndex
-            if (!m_sessionIndex.empty() && saml2statement->getSessionIndex() && *(saml2statement->getSessionIndex())) {
-                auto_ptr<SimpleAttribute> sessionindex(new SimpleAttribute(vector<string>(1, m_sessionIndex)));
-                auto_ptr_char temp(saml2statement->getSessionIndex());
-                if (temp.get()) {
-                    sessionindex->getValues().push_back(temp.get());
-                    attributes.push_back(sessionindex.release());
-                }
+        }
+        if (profiles && !profiles->empty()) {
+            auto_ptr<SimpleAttribute> attr(new SimpleAttribute(vector<string>(1, m_attributeProfiles)));
+            for (indirect_iterator<vector<AttributeProfile*>::const_iterator> i = make_indirect_iterator(profiles->begin());
+                    i != make_indirect_iterator(profiles->end()); ++i) {
+                auto_ptr_char temp(i->getProfileURI());
+                if (temp.get())
+                    attr->getValues().push_back(temp.get());
             }
-
-            // SessionNotOnOrAfter
-            if (!m_sessionNotOnOrAfter.empty() && saml2statement->getSessionNotOnOrAfter()) {
-                auto_ptr<SimpleAttribute> sessionnotonorafter(new SimpleAttribute(vector<string>(1, m_sessionNotOnOrAfter)));
-                auto_ptr_char temp(saml2statement->getSessionNotOnOrAfter()->getRawData());
-                if (temp.get()) {
-                    sessionnotonorafter->getValues().push_back(temp.get());
-                    attributes.push_back(sessionnotonorafter.release());
-                }
+            if (attr->valueCount() > 0) {
+                attributes.push_back(attr.get());
+                attr.release();
             }
+        }
+    }
 
-            if (saml2statement->getSubjectLocality()) {
-                const saml2::SubjectLocality* locality = saml2statement->getSubjectLocality();
-                // Address
-                if (!m_subjectAddress.empty() && locality->getAddress() && *(locality->getAddress())) {
-                    auto_ptr<SimpleAttribute> address(new SimpleAttribute(vector<string>(1, m_subjectAddress)));
-                    auto_ptr_char temp(locality->getAddress());
-                    if (temp.get()) {
-                        address->getValues().push_back(temp.get());
-                        attributes.push_back(address.release());
-                    }
-                }
-
-                // DNSName
-                if (!m_subjectDNS.empty() && locality->getDNSName() && *(locality->getDNSName())) {
-                    auto_ptr<SimpleAttribute> dns(new SimpleAttribute(vector<string>(1, m_subjectDNS)));
-                    auto_ptr_char temp(locality->getDNSName());
-                    if (temp.get()) {
-                        dns->getValues().push_back(temp.get());
-                        attributes.push_back(dns.release());
-                    }
-                }
-            }
-
-            if (saml2statement->getAuthnContext()) {
-                const AuthnContext* ac = saml2statement->getAuthnContext();
-                // AuthnContextClassRef
-                if (!m_authnClass.empty() && ac->getAuthnContextClassRef() && ac->getAuthnContextClassRef()->getReference()) {
-                    auto_ptr<SimpleAttribute> classref(new SimpleAttribute(vector<string>(1, m_authnClass)));
-                    auto_ptr_char temp(ac->getAuthnContextClassRef()->getReference());
-                    if (temp.get()) {
-                        classref->getValues().push_back(temp.get());
-                        attributes.push_back(classref.release());
-                    }
-                }
-
-                // AuthnContextDeclRef
-                if (!m_authnDecl.empty() && ac->getAuthnContextDeclRef() && ac->getAuthnContextDeclRef()->getReference()) {
-                    auto_ptr<SimpleAttribute> declref(new SimpleAttribute(vector<string>(1, m_authnDecl)));
-                    auto_ptr_char temp(ac->getAuthnContextDeclRef()->getReference());
-                    if (temp.get()) {
-                        declref->getValues().push_back(temp.get());
-                        attributes.push_back(declref.release());
-                    }
-                }
+    if (!m_errorURL.empty() && roleToExtract->getErrorURL()) {
+        auto_ptr_char temp(roleToExtract->getErrorURL());
+        if (temp.get() && *temp.get()) {
+            auto_ptr<SimpleAttribute> attr(new SimpleAttribute(vector<string>(1, m_errorURL)));
+            attr->getValues().push_back(temp.get());
+            attributes.push_back(attr.get());
+            attr.release();
+        }
+    }
 
-                // AuthenticatingAuthority
-                if (!m_authnAuthority.empty() && !ac->getAuthenticatingAuthoritys().empty()) {
-                    auto_ptr<SimpleAttribute> attr(new SimpleAttribute(vector<string>(1, m_authnAuthority)));
-                    const vector<AuthenticatingAuthority*>& authorities = ac->getAuthenticatingAuthoritys();
-                    for (vector<AuthenticatingAuthority*>::const_iterator a = authorities.begin(); a != authorities.end(); ++a) {
-                        auto_ptr_char temp((*a)->getID());
-                        if (temp.get())
-                            attr->getValues().push_back(temp.get());
-                    }
-                    if (attr->valueCount() > 0) {
-                        attributes.push_back(attr.release());
-                    }
+    if (!m_displayName.empty() || !m_description.empty() || !m_informationURL.empty() || !m_privacyURL.empty()) {
+        const Extensions* exts = roleToExtract->getExtensions();
+        if (exts) {
+            const UIInfo* ui;
+            for (vector<XMLObject*>::const_iterator ext = exts->getUnknownXMLObjects().begin(); ext != exts->getUnknownXMLObjects().end(); ++ext) {
+                ui = dynamic_cast<const UIInfo*>(*ext);
+                if (ui) {
+                    doLangSensitive(request, ui->getDisplayNames(), m_displayName, attributes);
+                    doLangSensitive(request, ui->getDescriptions(), m_description, attributes);
+                    doLangSensitive(request, ui->getInformationURLs(), m_informationURL, attributes);
+                    doLangSensitive(request, ui->getPrivacyStatementURLs(), m_privacyURL, attributes);
+                    break;
                 }
             }
         }
-        else {
-            const saml1::Assertion* saml1assertion = dynamic_cast<const saml1::Assertion*>(&xmlObject);
-            if (saml1assertion) {
-                // Issuer
-                if (!m_issuer.empty()) {
-                    if (saml1assertion->getIssuer() && *(saml1assertion->getIssuer())) {
-                        auto_ptr<SimpleAttribute> issuer(new SimpleAttribute(vector<string>(1, m_issuer)));
-                        auto_ptr_char temp(saml1assertion->getIssuer());
-                        if (temp.get()) {
-                            issuer->getValues().push_back(temp.get());
-                            attributes.push_back(issuer.release());
-                        }
-                    }
-                }
+    }
 
-                // NotOnOrAfter
-                if (!m_notOnOrAfter.empty() && saml1assertion->getConditions() && saml1assertion->getConditions()->getNotOnOrAfter()) {
-                    auto_ptr<SimpleAttribute> notonorafter(new SimpleAttribute(vector<string>(1, m_notOnOrAfter)));
-                    auto_ptr_char temp(saml1assertion->getConditions()->getNotOnOrAfter()->getRawData());
-                    if (temp.get()) {
-                        notonorafter->getValues().push_back(temp.get());
-                        attributes.push_back(notonorafter.release());
-                    }
-                }
-            }
-            else {
-                const AuthenticationStatement* saml1statement = dynamic_cast<const AuthenticationStatement*>(&xmlObject);
-                if (saml1statement) {
-                    // AuthnInstant
-                    if (!m_authnInstant.empty() && saml1statement->getAuthenticationInstant()) {
-                        auto_ptr<SimpleAttribute> authninstant(new SimpleAttribute(vector<string>(1, m_authnInstant)));
-                        auto_ptr_char temp(saml1statement->getAuthenticationInstant()->getRawData());
-                        if (temp.get()) {
-                            authninstant->getValues().push_back(temp.get());
-                            attributes.push_back(authninstant.release());
-                        }
-                    }
+    if (!m_orgName.empty() || !m_orgDisplayName.empty() || !m_orgURL.empty()) {
+        const Organization* org = roleToExtract->getOrganization();
+        if (!org)
+            org = dynamic_cast<EntityDescriptor*>(roleToExtract->getParent())->getOrganization();
+        if (org) {
+            doLangSensitive(request, org->getOrganizationNames(), m_orgName, attributes);
+            doLangSensitive(request, org->getOrganizationDisplayNames(), m_orgDisplayName, attributes);
+            doLangSensitive(request, org->getOrganizationURLs(), m_orgURL, attributes);
+        }
+    }
 
-                    // AuthenticationMethod
-                    if (!m_authnClass.empty() && saml1statement->getAuthenticationMethod() && *(saml1statement->getAuthenticationMethod())) {
-                        auto_ptr<SimpleAttribute> authnmethod(new SimpleAttribute(vector<string>(1, m_authnClass)));
-                        auto_ptr_char temp(saml1statement->getAuthenticationMethod());
-                        if (temp.get()) {
-                            authnmethod->getValues().push_back(temp.get());
-                            attributes.push_back(authnmethod.release());
-                        }
-                    }
+    for_each(
+        m_contacts.begin(), m_contacts.end(),
+        boost::bind(&MetadataExtractor::doContactPerson, this, roleToExtract, _1, boost::ref(attributes))
+        );
+}
 
-                    if (saml1statement->getSubjectLocality()) {
-                        const saml1::SubjectLocality* locality = saml1statement->getSubjectLocality();
-                        // IPAddress
-                        if (!m_subjectAddress.empty() && locality->getIPAddress() && *(locality->getIPAddress())) {
-                            auto_ptr<SimpleAttribute> address(new SimpleAttribute(vector<string>(1, m_subjectAddress)));
-                            auto_ptr_char temp(locality->getIPAddress());
-                            if (temp.get()) {
-                                address->getValues().push_back(temp.get());
-                                attributes.push_back(address.release());
-                            }
-                        }
+template <class T> void MetadataExtractor::doLangSensitive(
+    const GenericRequest* request, const vector<T*>& objects, const string& id, vector<shibsp::Attribute*>& attributes
+    ) const
+{
+    if (objects.empty() || id.empty())
+        return;
 
-                        // DNSAddress
-                        if (!m_subjectDNS.empty() && locality->getDNSAddress() && *(locality->getDNSAddress())) {
-                            auto_ptr<SimpleAttribute> dns(new SimpleAttribute(vector<string>(1, m_subjectDNS)));
-                            auto_ptr_char temp(locality->getDNSAddress());
-                            if (temp.get()) {
-                                dns->getValues().push_back(temp.get());
-                                attributes.push_back(dns.release());
-                            }
-                        }
-                    }
-                }
+    T* match = nullptr;
+    if (request && request->startLangMatching()) {
+        do {
+            for (vector<T*>::const_iterator i = objects.begin(); !match && i != objects.end(); ++i) {
+                if (request->matchLang((*i)->getLang()))
+                    match = *i;
             }
-        }
+        } while (!match && request->continueLangMatching());
+    }
+    if (!match)
+        match = objects.front();
+
+    auto_ptr_char temp(match->getTextContent());
+    if (temp.get() && *temp.get()) {
+        auto_ptr<SimpleAttribute> attr(new SimpleAttribute(vector<string>(1, id)));
+        attr->getValues().push_back(temp.get());
+        attributes.push_back(attr.get());
+        attr.release();
     }
 }
 
-void MetadataExtractor::getAttributeIds(vector<string>& attributes) const
+void MetadataExtractor::doContactPerson(
+    const RoleDescriptor* role, const contact_tuple_t& params, vector<shibsp::Attribute*>& attributes
+    ) const
 {
-    if (!m_authnAuthority.empty())
-        attributes.push_back(m_authnAuthority);
-    if (!m_authnClass.empty())
-        attributes.push_back(m_authnClass);
-    if (!m_authnDecl.empty())
-        attributes.push_back(m_authnDecl);
-    if (!m_authnInstant.empty())
-        attributes.push_back(m_authnInstant);
-    if (!m_issuer.empty())
-        attributes.push_back(m_issuer);
-    if (!m_notOnOrAfter.empty())
-        attributes.push_back(m_notOnOrAfter);
-    if (!m_sessionIndex.empty())
-        attributes.push_back(m_sessionIndex);
-    if (!m_sessionNotOnOrAfter.empty())
-        attributes.push_back(m_sessionNotOnOrAfter);
-    if (!m_subjectAddress.empty())
-        attributes.push_back(m_subjectAddress);
-    if (!m_subjectDNS.empty())
-        attributes.push_back(m_subjectDNS);
+    const XMLCh* ctype = params.get<1>().c_str();
+    static bool (*eq)(const XMLCh*, const XMLCh*) = &XMLString::equals;
+    const ContactPerson* cp = find_if(role->getContactPersons(),boost::bind(eq, ctype, boost::bind(&ContactPerson::getContactType, _1)));
+    if (!cp) {
+        cp = find_if(dynamic_cast<EntityDescriptor*>(role->getParent())->getContactPersons(),
+                boost::bind(eq, ctype, boost::bind(&ContactPerson::getContactType, _1)));
+    }
+
+    if (cp) {
+        if (!cp->getDOM()) {
+            cp->marshall();
+        }
+        vector<string> ids(1, params.get<0>());
+        auto_ptr<Attribute> attr(params.get<2>()->decode(ids, cp));
+        if (attr.get()) {
+            attributes.push_back(attr.get());
+            attr.release();
+        }
+    }
 }
index d00a6f7..991346a 100644 (file)
@@ -190,6 +190,7 @@ namespace shibsp {
          * <p>The caller must free the returned context handle.
          * 
          * @param application           reference to application receiving message
+         * @param request               request delivering message, if any
          * @param issuer                source of SSO tokens
          * @param protocol              SSO protocol used
          * @param v1nameid              identifier of principal in SAML 1.x form, if any
@@ -202,6 +203,7 @@ namespace shibsp {
          */
         ResolutionContext* resolveAttributes(
             const Application& application,
+            const xmltooling::GenericRequest* request=nullptr,
             const opensaml::saml2md::RoleDescriptor* issuer=nullptr,
             const XMLCh* protocol=nullptr,
             const opensaml::saml1::NameIdentifier* v1nameid=nullptr,
index 9438b04..d8f0f36 100644 (file)
@@ -106,6 +106,7 @@ pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler
         // When not out of process, we remote all the message processing.
         vector<string> headers(1, "Cookie");
         headers.push_back("User-Agent");
+        headers.push_back("Accept-Language");
         DDF out,in = wrap(request, &headers);
         DDFJanitor jin(in), jout(out);
         out=request.getServiceProvider().getListenerService()->send(in);
@@ -345,6 +346,7 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
 {
     return resolveAttributes(
         application,
+        nullptr,
         issuer,
         protocol,
         v1nameid,
@@ -359,6 +361,7 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
 
 ResolutionContext* AssertionConsumerService::resolveAttributes(
     const Application& application,
+    const GenericRequest* request,
     const saml2md::RoleDescriptor* issuer,
     const XMLCh* protocol,
     const saml1::NameIdentifier* v1nameid,
@@ -381,7 +384,7 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
                 m_log.debug("extracting metadata-derived attributes...");
                 try {
                     // We pass nullptr for "issuer" because the IdP isn't the one asserting metadata-based attributes.
-                    extractor->extractAttributes(application, nullptr, *issuer, resolvedAttributes);
+                    extractor->extractAttributes(application, request, nullptr, *issuer, resolvedAttributes);
                     for (indirect_iterator<vector<Attribute*>::iterator> a = make_indirect_iterator(resolvedAttributes.begin());
                             a != make_indirect_iterator(resolvedAttributes.end()); ++a) {
                         vector<string>& ids = a->getAliases();
@@ -400,9 +403,9 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
         if (v1nameid || nameid) {
             try {
                 if (v1nameid)
-                    extractor->extractAttributes(application, issuer, *v1nameid, resolvedAttributes);
+                    extractor->extractAttributes(application, request, issuer, *v1nameid, resolvedAttributes);
                 else
-                    extractor->extractAttributes(application, issuer, *nameid, resolvedAttributes);
+                    extractor->extractAttributes(application, request, issuer, *nameid, resolvedAttributes);
             }
             catch (std::exception& ex) {
                 m_log.error("caught exception extracting attributes: %s", ex.what());
@@ -412,9 +415,9 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
         if (v1statement || statement) {
             try {
                 if (v1statement)
-                    extractor->extractAttributes(application, issuer, *v1statement, resolvedAttributes);
+                    extractor->extractAttributes(application, request, issuer, *v1statement, resolvedAttributes);
                 else
-                    extractor->extractAttributes(application, issuer, *statement, resolvedAttributes);
+                    extractor->extractAttributes(application, request, issuer, *statement, resolvedAttributes);
             }
             catch (std::exception& ex) {
                 m_log.error("caught exception extracting attributes: %s", ex.what());
@@ -425,7 +428,7 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
             for (indirect_iterator<vector<const Assertion*>::const_iterator> t = make_indirect_iterator(tokens->begin());
                     t != make_indirect_iterator(tokens->end()); ++t) {
                 try {
-                    extractor->extractAttributes(application, issuer, *t, resolvedAttributes);
+                    extractor->extractAttributes(application, request, issuer, *t, resolvedAttributes);
                 }
                 catch (std::exception& ex) {
                     m_log.error("caught exception extracting attributes: %s", ex.what());
index 545dda1..385c906 100644 (file)
@@ -296,6 +296,7 @@ void SAML1Consumer::implementProtocol(
     scoped_ptr<ResolutionContext> ctx(
         resolveAttributes(
             application,
+            &httpRequest,
             policy.getIssuerMetadata(),
             (!response->getMinorVersion().first || response->getMinorVersion().second==1) ?
                 samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM,
index 98f9522..7fe023d 100644 (file)
@@ -414,6 +414,7 @@ void SAML2Consumer::implementProtocol(
     scoped_ptr<ResolutionContext> ctx(
         resolveAttributes(
             application,
+            &httpRequest,
             policy.getIssuerMetadata(),
             samlconstants::SAML20P_NS,
             nullptr,
index 9a6a033..2004ab5 100644 (file)
@@ -1883,6 +1883,11 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, XMLConfig* outer,
             split(HTTPResponse::getAllowedSchemes(), schemes, is_space(), algorithm::token_compress_on);
         }
 
+        // Default language handling.
+        pair<bool,bool> langFromClient = getBool("langFromClient");
+        pair<bool,const XMLCh*> langPriority = getXMLString("langPriority");
+        GenericRequest::setLangDefaults(!langFromClient.first || langFromClient.second, langPriority.second);
+
         // Extensions
         doExtensions(e, "global", log);
         if (conf.isEnabled(SPConfig::OutOfProcess))
index 20fe70e..23b79eb 100644 (file)
     <ClCompile Include="attribute\filtering\impl\DummyAttributeFilter.cpp" />\r
     <ClCompile Include="attribute\filtering\impl\NameIDQualifierStringFunctor.cpp" />\r
     <ClCompile Include="attribute\resolver\impl\AssertionAttributeExtractor.cpp" />\r
+    <ClCompile Include="attribute\resolver\impl\MetadataAttributeExtractor.cpp" />\r
     <ClCompile Include="binding\impl\XMLProtocolProvider.cpp" />\r
     <ClCompile Include="handler\impl\DiscoveryFeed.cpp" />\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp" />\r
index 8ffb26b..d0bd18c 100644 (file)
     <ClCompile Include="attribute\resolver\impl\AssertionAttributeExtractor.cpp">\r
       <Filter>Source Files\attribute\resolver\impl</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="attribute\resolver\impl\MetadataAttributeExtractor.cpp">\r
+      <Filter>Source Files\attribute\resolver\impl</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="remoting\impl\SocketListener.h">\r