Migrate to updated reloadable-file APIs.
[shibboleth/sp.git] / shibsp / attribute / resolver / impl / XMLAttributeExtractor.cpp
index 8c3920f..a61e67b 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.
  */
 
 #include "internal.h"
+#include "exceptions.h"
 #include "Application.h"
 #include "ServiceProvider.h"
+#include "attribute/Attribute.h"
 #include "attribute/AttributeDecoder.h"
 #include "attribute/filtering/AttributeFilter.h"
 #include "attribute/filtering/BasicFilteringContext.h"
 #include "attribute/resolver/AttributeExtractor.h"
+#include "remoting/ddf.h"
 #include "security/SecurityPolicy.h"
 #include "util/SPConstants.h"
 
 #include <saml/SAMLConfig.h>
 #include <saml/saml1/core/Assertions.h>
 #include <saml/saml2/core/Assertions.h>
+#include <saml/saml2/metadata/Metadata.h>
 #include <saml/saml2/metadata/MetadataCredentialCriteria.h>
 #include <saml/saml2/metadata/ObservableMetadataProvider.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/security/TrustEngine.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ReloadableXMLFile.h>
+#include <xmltooling/util/Threads.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
@@ -120,6 +127,20 @@ namespace shibsp {
             ) const;
         void extractAttributes(
             const Application& application,
+            const char* assertingParty,
+            const char* relyingParty,
+            const saml1::AttributeStatement& statement,
+            vector<Attribute*>& attributes
+            ) const;
+        void extractAttributes(
+            const Application& application,
+            const char* assertingParty,
+            const char* relyingParty,
+            const saml2::AttributeStatement& statement,
+            vector<Attribute*>& attributes
+            ) const;
+        void extractAttributes(
+            const Application& application,
             const ObservableMetadataProvider* observable,
             const XMLCh* entityID,
             const char* relyingParty,
@@ -159,7 +180,7 @@ namespace shibsp {
     {
     public:
         XMLExtractor(const DOMElement* e) : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".AttributeExtractor.XML")), m_impl(NULL) {
-            load();
+            background_load();
         }
         ~XMLExtractor() {
             delete m_impl;
@@ -175,7 +196,7 @@ namespace shibsp {
         }
 
     protected:
-        pair<bool,DOMElement*> load();
+        pair<bool,DOMElement*> background_load();
 
     private:
         XMLExtractorImpl* m_impl;
@@ -210,7 +231,7 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
         m_metadata(NULL),
         m_trust(NULL),
         m_filter(NULL),
-        m_entityAssertions(false),
+        m_entityAssertions(true),
         m_attrLock(NULL)
 {
 #ifdef _DEBUG
@@ -230,18 +251,13 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
             auto_ptr<MetadataProvider> mp(SAMLConfig::getConfig().MetadataProviderManager.newPlugin(type.get(), child));
             mp->init();
             m_metadata = mp.release();
-            m_entityAssertions = true;
         }
         catch (exception& ex) {
-            m_log.crit(
-                "disabling support for Assertions in EntityAttributes extension, error building/initializing MetadataProvider: %s",
-                ex.what()
-                );
+            m_entityAssertions = false;
+            m_log.crit("error building/initializing dedicated MetadataProvider: %s", ex.what());
+            m_log.crit("disabling support for Assertions in EntityAttributes extension");
         }
     }
-    else {
-        m_log.info("no dedicated MetadataProvider supplied, disabling support for Assertions in EntityAttributes extension");
-    }
 
     if (m_entityAssertions) {
         child = XMLHelper::getFirstChildElement(e, shibspconstants::SHIB2ATTRIBUTEMAP_NS, _TrustEngine);
@@ -254,10 +270,9 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
                 m_trust = XMLToolingConfig::getConfig().TrustEngineManager.newPlugin(type.get(), child);
             }
             catch (exception& ex) {
-                m_log.crit(
-                    "disabling support for Assertions in EntityAttributes extension, error building TrustEngine: %s", ex.what()
-                    );
                 m_entityAssertions = false;
+                m_log.crit("error building/initializing dedicated TrustEngine: %s", ex.what());
+                m_log.crit("disabling support for Assertions in EntityAttributes extension");
             }
         }
     }
@@ -273,10 +288,9 @@ XMLExtractorImpl::XMLExtractorImpl(const DOMElement* e, Category& log)
                 m_filter = SPConfig::getConfig().AttributeFilterManager.newPlugin(type.get(), child);
             }
             catch (exception& ex) {
-                m_log.crit(
-                    "disabling support for Assertions in EntityAttributes extension, error building AttributeFilter: %s", ex.what()
-                    );
                 m_entityAssertions = false;
+                m_log.crit("error building/initializing dedicated AttributeFilter: %s", ex.what());
+                m_log.crit("disabling support for Assertions in EntityAttributes extension");
             }
         }
     }
@@ -540,6 +554,32 @@ void XMLExtractorImpl::extractAttributes(
 
 void XMLExtractorImpl::extractAttributes(
     const Application& application,
+    const char* assertingParty,
+    const char* relyingParty,
+    const saml1::AttributeStatement& statement,
+    vector<Attribute*>& attributes
+    ) const
+{
+    const vector<saml1::Attribute*>& attrs = statement.getAttributes();
+    for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
+        extractAttributes(application, assertingParty, relyingParty, *(*a), attributes);
+}
+
+void XMLExtractorImpl::extractAttributes(
+    const Application& application,
+    const char* assertingParty,
+    const char* relyingParty,
+    const saml2::AttributeStatement& statement,
+    vector<Attribute*>& attributes
+    ) const
+{
+    const vector<saml2::Attribute*>& attrs = statement.getAttributes();
+    for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
+        extractAttributes(application, assertingParty, relyingParty, *(*a), attributes);
+}
+
+void XMLExtractorImpl::extractAttributes(
+    const Application& application,
     const ObservableMetadataProvider* observable,
     const XMLCh* entityID,
     const char* relyingParty,
@@ -650,7 +690,8 @@ void XMLExtractorImpl::extractAttributes(
                     // Set up and evaluate a policy for an AA asserting attributes to us.
                     shibsp::SecurityPolicy policy(application, &AttributeAuthorityDescriptor::ELEMENT_QNAME, false, m_policyId.get());
                     Locker locker(m_metadata);
-                    policy.setMetadataProvider(m_metadata);
+                    if (m_metadata)
+                        policy.setMetadataProvider(m_metadata);
                     if (m_trust)
                         policy.setTrustEngine(m_trust);
                     // Populate recipient as audience.
@@ -688,19 +729,18 @@ void XMLExtractorImpl::extractAttributes(
                         }
                     }
 
-                    // Authenticate the assertion. We have to marshall them to establish the signature for verification.
-                    (*assert)->marshall();
-                    policy.evaluate(*(*assert));
+                    // Authenticate the assertion. We have to clone and marshall it to establish the signature for verification.
+                    auto_ptr<saml2::Assertion> tokencopy((*assert)->cloneAssertion());
+                    tokencopy->marshall();
+                    policy.evaluate(*tokencopy);
                     if (!policy.isAuthenticated()) {
                         if (m_log.isDebugEnabled()) {
-                            auto_ptr_char tempid((*assert)->getID());
+                            auto_ptr_char tempid(tokencopy->getID());
                             auto_ptr_char eid(entityID);
                             m_log.debug(
                                 "failed to authenticate assertion (%s) in metadata extension for entity (%s)", tempid.get(), eid.get()
                                 );
                         }
-                        (*assert)->releaseThisAndChildrenDOM();
-                        (*assert)->setDocument(NULL);
                         continue;
                     }
 
@@ -710,11 +750,9 @@ void XMLExtractorImpl::extractAttributes(
                     auto_ptr_char inlineAssertingParty(inlineEntity ? inlineEntity->getEntityID() : NULL);
                     relyingParty = application.getRelyingParty(inlineEntity)->getString("entityID").second;
                     const vector<saml2::Attribute*>& attrs2 =
-                        const_cast<const saml2::AttributeStatement*>((*assert)->getAttributeStatements().front())->getAttributes();
+                        const_cast<const saml2::AttributeStatement*>(tokencopy->getAttributeStatements().front())->getAttributes();
                     for (vector<saml2::Attribute*>::const_iterator a = attrs2.begin(); a!=attrs2.end(); ++a)
                         extractAttributes(application, inlineAssertingParty.get(), relyingParty, *(*a), holding2);
-                    (*assert)->releaseThisAndChildrenDOM();
-                    (*assert)->setDocument(NULL);
 
                     // Now we locally filter the attributes so that the actual issuer can be properly set.
                     // If we relied on outside filtering, the attributes couldn't be distinguished from the
@@ -750,8 +788,6 @@ void XMLExtractorImpl::extractAttributes(
                             ex.what()
                             );
                     }
-                    (*assert)->releaseThisAndChildrenDOM();
-                    (*assert)->setDocument(NULL);
                     for_each(holding2.begin(), holding2.end(), xmltooling::cleanup<Attribute>());
                     continue;
                 }
@@ -759,8 +795,6 @@ void XMLExtractorImpl::extractAttributes(
                     // Unknown exceptions are fatal.
                     if (useCache)
                         m_attrLock->unlock();
-                    (*assert)->releaseThisAndChildrenDOM();
-                    (*assert)->setDocument(NULL);
                     for_each(holding.begin(), holding.end(), xmltooling::cleanup<Attribute>());
                     for_each(holding2.begin(), holding2.end(), xmltooling::cleanup<Attribute>());
                     throw;
@@ -798,6 +832,29 @@ void XMLExtractor::extractAttributes(
     const EntityDescriptor* entity = issuer ? dynamic_cast<const EntityDescriptor*>(issuer->getParent()) : NULL;
     const char* relyingParty = application.getRelyingParty(entity)->getString("entityID").second;
 
+    // Check for statements.
+    if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::AttributeStatement::LOCAL_NAME)) {
+        const saml2::AttributeStatement* statement2 = dynamic_cast<const saml2::AttributeStatement*>(&xmlObject);
+        if (statement2) {
+            auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
+            m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement2, attributes);
+            // Handle EncryptedAttributes inline so we have access to the role descriptor.
+            const vector<saml2::EncryptedAttribute*>& encattrs = statement2->getEncryptedAttributes();
+            for (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea)
+                extractAttributes(application, issuer, *(*ea), attributes);
+            return;
+        }
+
+        const saml1::AttributeStatement* statement1 = dynamic_cast<const saml1::AttributeStatement*>(&xmlObject);
+        if (statement1) {
+            auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
+            m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *statement1, attributes);
+            return;
+        }
+
+        throw AttributeExtractionException("Unable to extract attributes, unknown object type.");
+    }
+
     // Check for assertions.
     if (XMLString::equals(xmlObject.getElementQName().getLocalPart(), saml1::Assertion::LOCAL_NAME)) {
         const saml2::Assertion* token2 = dynamic_cast<const saml2::Assertion*>(&xmlObject);
@@ -805,10 +862,8 @@ void XMLExtractor::extractAttributes(
             auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
             const vector<saml2::AttributeStatement*>& statements = token2->getAttributeStatements();
             for (vector<saml2::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
-                const vector<saml2::Attribute*>& attrs = const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
-                for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
-                    m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes);
-
+                m_impl->extractAttributes(application, 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 (vector<saml2::EncryptedAttribute*>::const_iterator ea = encattrs.begin(); ea!=encattrs.end(); ++ea)
                     extractAttributes(application, issuer, *(*ea), attributes);
@@ -820,11 +875,8 @@ void XMLExtractor::extractAttributes(
         if (token1) {
             auto_ptr_char assertingParty(entity ? entity->getEntityID() : NULL);
             const vector<saml1::AttributeStatement*>& statements = token1->getAttributeStatements();
-            for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s) {
-                const vector<saml1::Attribute*>& attrs = const_cast<const saml1::AttributeStatement*>(*s)->getAttributes();
-                for (vector<saml1::Attribute*>::const_iterator a = attrs.begin(); a!=attrs.end(); ++a)
-                    m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*a), attributes);
-            }
+            for (vector<saml1::AttributeStatement*>::const_iterator s = statements.begin(); s!=statements.end(); ++s)
+                m_impl->extractAttributes(application, assertingParty.get(), relyingParty, *(*s), attributes);
             return;
         }
 
@@ -929,7 +981,7 @@ void XMLExtractor::extractAttributes(
     throw AttributeExtractionException("Unable to extract attributes, unknown object type.");
 }
 
-pair<bool,DOMElement*> XMLExtractor::load()
+pair<bool,DOMElement*> XMLExtractor::background_load()
 {
     // Load from source using base class.
     pair<bool,DOMElement*> raw = ReloadableXMLFile::load();
@@ -942,6 +994,10 @@ pair<bool,DOMElement*> XMLExtractor::load()
     // If we held the document, transfer it to the impl. If we didn't, it's a no-op.
     impl->setDocument(docjanitor.release());
 
+    // Perform the swap inside a lock.
+    if (m_lock)
+        m_lock->wrlock();
+    SharedLock locker(m_lock, false);
     delete m_impl;
     m_impl = impl;