SSPCPP-616 - fix tuple namespaces and string literal errors
[shibboleth/cpp-sp.git] / shibsp / attribute / resolver / impl / XMLAttributeExtractor.cpp
index fdb3025..beb726c 100644 (file)
@@ -38,7 +38,6 @@
 #include "util/SPConstants.h"
 
 #include <boost/bind.hpp>
-#include <boost/scoped_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/iterator/indirect_iterator.hpp>
@@ -103,55 +102,14 @@ namespace shibsp {
             d.clear();
         }
 
+        void extractAttributes(const Application&, const char*, const char*, const NameIdentifier&, ptr_vector<Attribute>&) const;
+        void extractAttributes(const Application&, const char*, const char*, const NameID&, ptr_vector<Attribute>&) const;
+        void extractAttributes(const Application&, const GenericRequest*, const char*, const char*, const saml1::Attribute&, ptr_vector<Attribute>&) const;
+        void extractAttributes(const Application&, const GenericRequest*, const char*, const char*, const saml2::Attribute&, ptr_vector<Attribute>&) const;
+        void extractAttributes(const Application&, const GenericRequest*, const char*, const char*, const saml1::AttributeStatement&, ptr_vector<Attribute>&) const;
+        void extractAttributes(const Application&, const GenericRequest*, const char*, const char*, const saml2::AttributeStatement&, ptr_vector<Attribute>&) const;
         void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const NameIdentifier& nameid,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const NameID& nameid,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const saml1::Attribute& attr,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const saml2::Attribute& attr,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const saml1::AttributeStatement& statement,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const char* assertingParty,
-            const char* relyingParty,
-            const saml2::AttributeStatement& statement,
-            ptr_vector<Attribute>& attributes
-            ) const;
-        void extractAttributes(
-            const Application& application,
-            const ObservableMetadataProvider* observable,
-            const XMLCh* entityID,
-            const char* relyingParty,
-            const Extensions& ext,
-            ptr_vector<Attribute>& attributes
+            const Application&, const GenericRequest*, const ObservableMetadataProvider*, const XMLCh*, const char*, const Extensions&, ptr_vector<Attribute>&
             ) const;
 
         void getAttributeIds(vector<string>& attributes) const {
@@ -166,14 +124,14 @@ namespace shibsp {
         typedef map< pair<xstring,xstring>,pair< boost::shared_ptr<AttributeDecoder>,vector<string> > > attrmap_t;
         attrmap_t m_attrMap;
         vector<string> m_attributeIds;
-        vector< tuple<xstring,xstring,bool> > m_requestedAttrs;
+        vector< boost::tuple<xstring,xstring,bool> > m_requestedAttrs;
 
         // settings for embedded assertions in metadata
         string m_policyId;
         scoped_ptr<AttributeFilter> m_filter;
         scoped_ptr<MetadataProvider> m_metadata;
         scoped_ptr<TrustEngine> m_trust;
-        bool m_entityAssertions;
+        bool m_entityAssertions,m_metaAttrCaching;
 
         // manages caching of decoded Attributes
         scoped_ptr<RWLock> m_attrLock;
@@ -184,16 +142,23 @@ namespace shibsp {
     class XMLExtractor : public AttributeExtractor, public ReloadableXMLFile
     {
     public:
-        XMLExtractor(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeExtractor.XML")) {
+        XMLExtractor(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT ".AttributeExtractor.XML")) {
+            if (m_local && m_lock)
+                m_log.warn("attribute mappings are reloadable; be sure to restart web server when adding new attribute IDs");
             background_load();
         }
         ~XMLExtractor() {
             shutdown();
         }
 
+        // deprecated method
         void extractAttributes(
             const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector<Attribute*>& attributes
-            ) const;
+            ) const {
+            extractAttributes(application, nullptr, issuer, xmlObject, attributes);
+        }
+
+        void extractAttributes(const Application&, const GenericRequest*, const RoleDescriptor*, const XMLObject&, vector<Attribute*>&) const;
 
         void getAttributeIds(std::vector<std::string>& attributes) const {
             if (m_impl)
@@ -211,9 +176,7 @@ namespace shibsp {
     private:
         scoped_ptr<XMLExtractorImpl> m_impl;
 
-        void extractAttributes(
-            const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, ptr_vector<Attribute>& attributes
-            ) const;
+        void extractAttributes(const Application&, const GenericRequest*, const RoleDescriptor*, const XMLObject&, ptr_vector<Attribute>&) const;
     };
 
 #if defined (_MSC_VER)
@@ -225,25 +188,27 @@ namespace shibsp {
         return new XMLExtractor(e);
     }
 
-    static const XMLCh _aliases[] =             UNICODE_LITERAL_7(a,l,i,a,s,e,s);
-    static const XMLCh _AttributeDecoder[] =    UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
-    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);
-    static const XMLCh metadataPolicyId[] =     UNICODE_LITERAL_16(m,e,t,a,d,a,t,a,P,o,l,i,c,y,I,d);
-    static const XMLCh _TrustEngine[] =         UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
-    static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
+    static const XMLCh _aliases[] =                 UNICODE_LITERAL_7(a,l,i,a,s,e,s);
+    static const XMLCh _AttributeDecoder[] =        UNICODE_LITERAL_16(A,t,t,r,i,b,u,t,e,D,e,c,o,d,e,r);
+    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 metadataAttributeCaching[] = UNICODE_LITERAL_24(m,e,t,a,d,a,t,a,A,t,t,r,i,b,u,t,e,C,a,c,h,i,n,g);
+    static const XMLCh metadataPolicyId[] =         UNICODE_LITERAL_16(m,e,t,a,d,a,t,a,P,o,l,i,c,y,I,d);
+    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);
+    static const XMLCh _TrustEngine[] =             UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e);
+    static const XMLCh _type[] =                    UNICODE_LITERAL_4(t,y,p,e);
 };
 
 XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
     : m_log(log),
         m_document(nullptr),
         m_policyId(XMLHelper::getAttrString(e, nullptr, metadataPolicyId)),
-        m_entityAssertions(true)
+        m_entityAssertions(true),
+        m_metaAttrCaching(XMLHelper::getAttrBool(e, true, metadataAttributeCaching))
 {
 #ifdef _DEBUG
     xmltooling::NDC ndc("XMLExtractorImpl");
@@ -376,12 +341,14 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
         bool requested = XMLHelper::getAttrBool(child, false, isRequested);
         bool required = XMLHelper::getAttrBool(child, false, RequestedAttribute::ISREQUIRED_ATTRIB_NAME);
         if (required || requested)
-            m_requestedAttrs.push_back(tuple<xstring,xstring,bool>(name,format,required));
+            m_requestedAttrs.push_back(boost::tuple<xstring,xstring,bool>(name,format,required));
 
         name = child->getAttributeNS(nullptr, _aliases);
         if (name && *name) {
+            m_log.warn("attribute mapping rule (%s) uses deprecated aliases feature, consider revising", id.get());
             auto_ptr_char aliases(name);
             string dup(aliases.get());
+            trim(dup);
             set<string> new_aliases;
             split(new_aliases, dup, is_space(), algorithm::token_compress_on);
             set<string>::iterator ru = new_aliases.find("REMOTE_USER");
@@ -389,13 +356,15 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
                 m_log.warn("skipping alias, REMOTE_USER is a reserved name");
                 new_aliases.erase(ru);
             }
+            decl.second.insert(decl.second.end(), new_aliases.begin(), new_aliases.end());
             m_attributeIds.insert(m_attributeIds.end(), new_aliases.begin(), new_aliases.end());
         }
 
         child = XMLHelper::getNextSiblingElement(child, shibspconstants::SHIB2ATTRIBUTEMAP_NS, saml1::Attribute::LOCAL_NAME);
     }
 
-    m_attrLock.reset(RWLock::create());
+    if (m_metaAttrCaching)
+        m_attrLock.reset(RWLock::create());
 }
 
 void XMLExtractorImpl::generateMetadata(SPSSODescriptor& role) const
@@ -418,7 +387,7 @@ void XMLExtractorImpl::generateMetadata(SPSSODescriptor& role) const
     static const XMLCh english[] = UNICODE_LITERAL_2(e,n);
     sn->setLang(english);
 
-    for (vector< tuple<xstring,xstring,bool> >::const_iterator i = m_requestedAttrs.begin(); i != m_requestedAttrs.end(); ++i) {
+    for (vector< boost::tuple<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->get<0>().c_str());
@@ -444,9 +413,10 @@ void XMLExtractorImpl::extractAttributes(
         format = NameIdentifier::UNSPECIFIED;
     attrmap_t::const_iterator rule;
     if ((rule = m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
-        auto_ptr<Attribute> a(rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty));
+        auto_ptr<Attribute> a(rule->second.first->decode(nullptr, rule->second.second, &nameid, assertingParty, relyingParty));
         if (a.get()) {
-            attributes.push_back(a);
+            attributes.push_back(a.get());
+            a.release();
         }
     }
     else if (m_log.isDebugEnabled()) {
@@ -468,9 +438,10 @@ void XMLExtractorImpl::extractAttributes(
         format = NameID::UNSPECIFIED;
     attrmap_t::const_iterator rule;
     if ((rule = m_attrMap.find(pair<xstring,xstring>(format,xstring()))) != m_attrMap.end()) {
-        auto_ptr<Attribute> a(rule->second.first->decode(rule->second.second, &nameid, assertingParty, relyingParty));
+        auto_ptr<Attribute> a(rule->second.first->decode(nullptr, rule->second.second, &nameid, assertingParty, relyingParty));
         if (a.get()) {
-            attributes.push_back(a);
+            attributes.push_back(a.get());
+            a.release();
         }
     }
     else if (m_log.isDebugEnabled()) {
@@ -481,6 +452,7 @@ void XMLExtractorImpl::extractAttributes(
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const GenericRequest* request,
     const char* assertingParty,
     const char* relyingParty,
     const saml1::Attribute& attr,
@@ -495,9 +467,10 @@ void XMLExtractorImpl::extractAttributes(
         format = &chNull;
     attrmap_t::const_iterator rule;
     if ((rule = m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
-        auto_ptr<Attribute> a(rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty));
+        auto_ptr<Attribute> a(rule->second.first->decode(request, rule->second.second, &attr, assertingParty, relyingParty));
         if (a.get()) {
-            attributes.push_back(a);
+            attributes.push_back(a.get());
+            a.release();
         }
     }
     else if (m_log.isInfoEnabled()) {
@@ -509,6 +482,7 @@ void XMLExtractorImpl::extractAttributes(
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const GenericRequest* request,
     const char* assertingParty,
     const char* relyingParty,
     const saml2::Attribute& attr,
@@ -525,16 +499,17 @@ void XMLExtractorImpl::extractAttributes(
         format = &chNull;
     attrmap_t::const_iterator rule;
     if ((rule = m_attrMap.find(pair<xstring,xstring>(name,format))) != m_attrMap.end()) {
-        auto_ptr<Attribute> a(rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty));
+        auto_ptr<Attribute> a(rule->second.first->decode(request, rule->second.second, &attr, assertingParty, relyingParty));
         if (a.get()) {
-            attributes.push_back(a);
+            attributes.push_back(a.get());
+            a.release();
             return;
         }
     }
     else if (XMLString::equals(format, saml2::Attribute::UNSPECIFIED)) {
         // As a fallback, if the format is "unspecified", null out the value and re-map.
         if ((rule = m_attrMap.find(pair<xstring,xstring>(name,xstring()))) != m_attrMap.end()) {
-            auto_ptr<Attribute> a(rule->second.first->decode(rule->second.second, &attr, assertingParty, relyingParty));
+            auto_ptr<Attribute> a(rule->second.first->decode(request, rule->second.second, &attr, assertingParty, relyingParty));
             if (a.get()) {
                 attributes.push_back(a.get());
                 a.release();
@@ -552,6 +527,7 @@ void XMLExtractorImpl::extractAttributes(
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const GenericRequest* request,
     const char* assertingParty,
     const char* relyingParty,
     const saml1::AttributeStatement& statement,
@@ -559,16 +535,17 @@ void XMLExtractorImpl::extractAttributes(
     ) const
 {
     static void (XMLExtractorImpl::* extract)(
-        const Application&, const char*, const char*, const saml1::Attribute&, ptr_vector<Attribute>&
+        const Application&, const GenericRequest*, const char*, const char*, const saml1::Attribute&, ptr_vector<Attribute>&
         ) const = &XMLExtractorImpl::extractAttributes;
     for_each(
         make_indirect_iterator(statement.getAttributes().begin()), make_indirect_iterator(statement.getAttributes().end()),
-        boost::bind(extract, this, boost::ref(application), assertingParty, relyingParty, _1, boost::ref(attributes))
+        boost::bind(extract, this, boost::cref(application), request, assertingParty, relyingParty, _1, boost::ref(attributes))
         );
 }
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const GenericRequest* request,
     const char* assertingParty,
     const char* relyingParty,
     const saml2::AttributeStatement& statement,
@@ -576,16 +553,17 @@ void XMLExtractorImpl::extractAttributes(
     ) const
 {
     static void (XMLExtractorImpl::* extract)(
-        const Application&, const char*, const char*, const saml2::Attribute&, ptr_vector<Attribute>&
+        const Application&, const GenericRequest*, const char*, const char*, const saml2::Attribute&, ptr_vector<Attribute>&
         ) const = &XMLExtractorImpl::extractAttributes;
     for_each(
         make_indirect_iterator(statement.getAttributes().begin()), make_indirect_iterator(statement.getAttributes().end()),
-        boost::bind(extract, this, boost::ref(application), assertingParty, relyingParty, _1, boost::ref(attributes))
+        boost::bind(extract, this, boost::cref(application), request, assertingParty, relyingParty, _1, boost::ref(attributes))
         );
 }
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const GenericRequest* request,
     const ObservableMetadataProvider* observable,
     const XMLCh* entityID,
     const char* relyingParty,
@@ -603,7 +581,7 @@ void XMLExtractorImpl::extractAttributes(
         map<const ObservableMetadataProvider*,decoded_t>::iterator cacheEntry;
 
         // Check for cached result.
-        if (observable) {
+        if (observable && m_metaAttrCaching) {
             m_attrLock->rdlock();
             cacheEntry = m_decodedMap.find(observable);
             if (cacheEntry == m_decodedMap.end()) {
@@ -638,7 +616,8 @@ void XMLExtractorImpl::extractAttributes(
                 for (vector<DDF>::iterator obj = d->second.begin(); obj != d->second.end(); ++obj) {
                     auto_ptr<Attribute> wrapper(Attribute::unmarshall(*obj));
                     m_log.debug("recovered cached metadata attribute (%s)", wrapper->getId());
-                    attributes.push_back(wrapper);
+                    attributes.push_back(wrapper.get());
+                    wrapper.release();
                 }
                 break;
             }
@@ -652,11 +631,11 @@ void XMLExtractorImpl::extractAttributes(
 
         // Extract attributes into holding area with no asserting party set.
         static void (XMLExtractorImpl::* extractV2Attr)(
-            const Application&, const char*, const char*, const saml2::Attribute&, ptr_vector<Attribute>&
+            const Application&, const GenericRequest*, const char*, const char*, const saml2::Attribute&, ptr_vector<Attribute>&
             ) const = &XMLExtractorImpl::extractAttributes;
         for_each(
             make_indirect_iterator(container->getAttributes().begin()), make_indirect_iterator(container->getAttributes().end()),
-            boost::bind(extractV2Attr, this, boost::ref(application), (const char*)nullptr, relyingParty, _1, boost::ref(holding))
+            boost::bind(extractV2Attr, this, boost::ref(application), request, (const char*)nullptr, relyingParty, _1, boost::ref(holding))
             );
 
         if (entityID && m_entityAssertions) {
@@ -761,7 +740,7 @@ void XMLExtractorImpl::extractAttributes(
                         const_cast<const saml2::AttributeStatement*>(tokencopy->getAttributeStatements().front())->getAttributes();
                     for_each(
                         make_indirect_iterator(attrs2.begin()), make_indirect_iterator(attrs2.end()),
-                        boost::bind(extractV2Attr, this, boost::ref(application), inlineAssertingParty.get(), relyingParty, _1, boost::ref(holding2))
+                        boost::bind(extractV2Attr, this, boost::ref(application), request, inlineAssertingParty.get(), relyingParty, _1, boost::ref(holding2))
                         );
 
                     // Now we locally filter the attributes so that the actual issuer can be properly set.
@@ -787,7 +766,8 @@ void XMLExtractorImpl::extractAttributes(
                             while (!unsafe_holding2.empty()) {
                                 auto_ptr<Attribute> ptr(unsafe_holding2.back());
                                 unsafe_holding2.pop_back();
-                                holding2.push_back(ptr);
+                                holding2.push_back(ptr.get());
+                                ptr.release();
                             }
                         }
                         catch (std::exception& ex) {
@@ -847,14 +827,14 @@ void XMLExtractorImpl::extractAttributes(
 }
 
 void XMLExtractor::extractAttributes(
-    const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector<Attribute*>& attributes
+    const Application& application, const GenericRequest* request, const RoleDescriptor* issuer, const XMLObject& xmlObject, vector<Attribute*>& attributes
     ) const
 {
     if (!m_impl)
         return;
 
     ptr_vector<Attribute> holding;
-    extractAttributes(application, issuer, xmlObject, holding);
+    extractAttributes(application, request, issuer, xmlObject, holding);
 
     // Transfer ownership from the ptr_vector to the unsafe vector for API compatibility.
     // Any throws should leave each container in a consistent state. The holding container
@@ -867,14 +847,14 @@ void XMLExtractor::extractAttributes(
 }
 
 void XMLExtractor::extractAttributes(
-    const Application& application, const RoleDescriptor* issuer, const XMLObject& xmlObject, ptr_vector<Attribute>& attributes
+    const Application& application, const GenericRequest* request, const RoleDescriptor* issuer, const XMLObject& xmlObject, ptr_vector<Attribute>& attributes
     ) const
 {
     static void (XMLExtractor::* extractEncrypted)(
-        const Application&, const RoleDescriptor*, const XMLObject&, ptr_vector<Attribute>&
+        const Application&, const GenericRequest*, const RoleDescriptor*, const XMLObject&, ptr_vector<Attribute>&
         ) const = &XMLExtractor::extractAttributes;
     static void (XMLExtractorImpl::* extractV1Statement)(
-        const Application&, const char*, const char*, const saml1::AttributeStatement&, ptr_vector<Attribute>&
+        const Application&, const GenericRequest*, const char*, const char*, const saml1::AttributeStatement&, ptr_vector<Attribute>&
         ) const = &XMLExtractorImpl::extractAttributes;
 
     const EntityDescriptor* entity = issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent()) : nullptr;
@@ -885,12 +865,12 @@ void XMLExtractor::extractAttributes(
         const saml2::AttributeStatement* statement2 = dynamic_cast<const saml2::AttributeStatement*>(&xmlObject);
         if (statement2) {
             auto_ptr_char assertingParty(entity ? entity->getEntityID() : nullptr);
-            m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement2, attributes);
+            m_impl->extractAttributes(application, request, assertingParty.get(), relyingParty, *statement2, attributes);
             // Handle EncryptedAttributes inline so we have access to the role descriptor.
             const vector<saml2::EncryptedAttribute*>& encattrs = statement2->getEncryptedAttributes();
             for_each(
                 make_indirect_iterator(encattrs.begin()), make_indirect_iterator(encattrs.end()),
-                boost::bind(extractEncrypted, this, boost::ref(application), issuer, _1, boost::ref(attributes))
+                boost::bind(extractEncrypted, this, boost::ref(application), request, issuer, _1, boost::ref(attributes))
                 );
             return;
         }
@@ -898,7 +878,7 @@ void XMLExtractor::extractAttributes(
         const saml1::AttributeStatement* statement1 = dynamic_cast<const saml1::AttributeStatement*>(&xmlObject);
         if (statement1) {
             auto_ptr_char assertingParty(entity ? entity->getEntityID() : nullptr);
-            m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement1, attributes);
+            m_impl->extractAttributes(application, request, assertingParty.get(), relyingParty, *statement1, attributes);
             return;
         }
 
@@ -913,12 +893,12 @@ void XMLExtractor::extractAttributes(
             const vector<saml2::AttributeStatement*>& statements = token2->getAttributeStatements();
             for (indirect_iterator<vector<saml2::AttributeStatement*>::const_iterator> s = make_indirect_iterator(statements.begin());
                     s != make_indirect_iterator(statements.end()); ++s) {
-                m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *s, attributes);
+                m_impl->extractAttributes(application, request, assertingParty.get(), relyingParty, *s, attributes);
                 // Handle EncryptedAttributes inline so we have access to the role descriptor.
                 const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement&>(*s).getEncryptedAttributes();
                 for_each(
                     make_indirect_iterator(encattrs.begin()), make_indirect_iterator(encattrs.end()),
-                    boost::bind(extractEncrypted, this, boost::ref(application), issuer, _1, boost::ref(attributes))
+                    boost::bind(extractEncrypted, this, boost::ref(application), request, issuer, _1, boost::ref(attributes))
                     );
             }
             return;
@@ -929,7 +909,7 @@ void XMLExtractor::extractAttributes(
             auto_ptr_char assertingParty(entity ? entity->getEntityID() : nullptr);
             const vector<saml1::AttributeStatement*>& statements = token1->getAttributeStatements();
             for_each(make_indirect_iterator(statements.begin()), make_indirect_iterator(statements.end()),
-                boost::bind(extractV1Statement, m_impl.get(), boost::ref(application), assertingParty.get(), relyingParty, _1, boost::ref(attributes))
+                boost::bind(extractV1Statement, m_impl.get(), boost::ref(application), request, assertingParty.get(), relyingParty, _1, boost::ref(attributes))
                 );
             return;
         }
@@ -947,6 +927,7 @@ void XMLExtractor::extractAttributes(
         if (ext) {
             m_impl->extractAttributes(
                 application,
+                request,
                 dynamic_cast<const ObservableMetadataProvider*>(application.getMetadataProvider(false)),
                 entityToExtract->getEntityID(),
                 relyingParty,
@@ -960,6 +941,7 @@ void XMLExtractor::extractAttributes(
             if (ext) {
                 m_impl->extractAttributes(
                     application,
+                    request,
                     dynamic_cast<const ObservableMetadataProvider*>(application.getMetadataProvider(false)),
                     nullptr,   // not an entity, so inline assertions won't be processed
                     relyingParty,
@@ -977,11 +959,11 @@ void XMLExtractor::extractAttributes(
         auto_ptr_char assertingParty(entity ? entity->getEntityID() : nullptr);
         const saml2::Attribute* attr2 = dynamic_cast<const saml2::Attribute*>(&xmlObject);
         if (attr2)
-            return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *attr2, attributes);
+            return m_impl->extractAttributes(application, request, assertingParty.get(), relyingParty, *attr2, attributes);
 
         const saml1::Attribute* attr1 = dynamic_cast<const saml1::Attribute*>(&xmlObject);
         if (attr1)
-            return m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *attr1, attributes);
+            return m_impl->extractAttributes(application, request, assertingParty.get(), relyingParty, *attr1, attributes);
 
         throw AttributeExtractionException("Unable to extract attributes, unknown object type.");
     }
@@ -1003,17 +985,17 @@ void XMLExtractor::extractAttributes(
                     scoped_ptr<XMLObject> decrypted(encattr->decrypt(*cr, recipient, &mcc));
                     if (m_log.isDebugEnabled())
                         m_log.debugStream() << "decrypted Attribute: " << *decrypted << logging::eol;
-                    return extractAttributes(application, issuer, *decrypted, attributes);
+                    return extractAttributes(application, request, issuer, *decrypted, attributes);
                 }
                 else {
                     scoped_ptr<XMLObject> decrypted(encattr->decrypt(*cr, recipient));
                     if (m_log.isDebugEnabled())
                         m_log.debugStream() << "decrypted Attribute: " << *decrypted << logging::eol;
-                    return extractAttributes(application, issuer, *decrypted, attributes);
+                    return extractAttributes(application, request, issuer, *decrypted, attributes);
                 }
             }
             catch (std::exception& ex) {
-                m_log.error("caught exception decrypting Attribute: %s", ex.what());
+                m_log.error("failed to decrypt Attribute: %s", ex.what());
                 return;
             }
         }