Factor out issuer/protocol extraction.
authorScott Cantor <cantor.2@osu.edu>
Thu, 9 Nov 2006 05:15:55 +0000 (05:15 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 9 Nov 2006 05:15:55 +0000 (05:15 +0000)
saml/binding/MessageFlowRule.h
saml/binding/SecurityPolicy.h
saml/binding/SecurityPolicyRule.h
saml/binding/SimpleSigningRule.h
saml/binding/XMLSigningRule.h
saml/binding/impl/MessageFlowRule.cpp
saml/binding/impl/SecurityPolicy.cpp
saml/binding/impl/SimpleSigningRule.cpp
saml/binding/impl/XMLSigningRule.cpp

index 632f305..d9fdeed 100644 (file)
@@ -41,7 +41,8 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine
+            const TrustEngine* trustEngine,
+            const MessageExtractor& extractor
             ) const;
 
         /**
index 81f990e..fcf3151 100644 (file)
@@ -62,12 +62,13 @@ namespace opensaml {
             const saml2md::MetadataProvider* metadataProvider=NULL,
             const xmltooling::QName* role=NULL,
             const TrustEngine* trustEngine=NULL
-            ) : m_issuer(NULL), m_issuerRole(NULL), m_matchingPolicy(NULL), m_metadata(metadataProvider),
-                m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
+            ) : m_issuer(NULL), m_issuerRole(NULL), m_matchingPolicy(NULL), m_extractor(NULL),
+                m_metadata(metadataProvider), m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
         }
 
         /**
-         * Constructor for policy using existing rules.
+         * Constructor for policy using existing rules. The lifetime of the policy rules
+         * must be at least as long as the policy object.
          *
          * @param rules             reference to array of policy rules to use 
          * @param metadataProvider  locked MetadataProvider instance
@@ -79,8 +80,8 @@ namespace opensaml {
             const saml2md::MetadataProvider* metadataProvider=NULL,
             const xmltooling::QName* role=NULL,
             const TrustEngine* trustEngine=NULL
-            ) : m_issuer(NULL), m_issuerRole(NULL), m_matchingPolicy(NULL), m_rules(rules), m_metadata(metadataProvider),
-                m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
+            ) : m_issuer(NULL), m_issuerRole(NULL), m_matchingPolicy(NULL), m_extractor(NULL), m_rules(rules),
+                m_metadata(metadataProvider), m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
         }
 
         virtual ~SecurityPolicy();
@@ -113,6 +114,16 @@ namespace opensaml {
         }
 
         /**
+         * Adds a SecurityPolicyRule to the policy. The lifetime of the policy rule
+         * must be at least as long as the policy object.
+         * 
+         * @param rule  SecurityPolicyRule to add
+         */
+        void addRule(const SecurityPolicyRule* rule) {
+            m_rules.push_back(rule);
+        }
+
+        /**
          * Sets a locked MetadataProvider for the policy.
          * 
          * @param metadata a locked MetadataProvider or NULL
@@ -210,8 +221,8 @@ namespace opensaml {
          * 
          * @return the effective IssuerMatchingPolicy
          */
-        const IssuerMatchingPolicy* getIssuerMatchingPolicy() const {
-            return m_matchingPolicy ? m_matchingPolicy : &m_defaultMatching;
+        const IssuerMatchingPolicy& getIssuerMatchingPolicy() const {
+            return m_matchingPolicy ? *m_matchingPolicy : m_defaultMatching;
         }
 
         /**
@@ -222,20 +233,47 @@ namespace opensaml {
          * 
          * @param matchingPolicy the IssuerMatchingPolicy to use
          */
-        void getIssuerMatchingPolicy(IssuerMatchingPolicy* matchingPolicy) {
+        void setIssuerMatchingPolicy(IssuerMatchingPolicy* matchingPolicy) {
             delete m_matchingPolicy;
             m_matchingPolicy = matchingPolicy;
         }
 
+        /**
+         * Returns the MessageExtractor in effect.
+         * 
+         * @return the effective MessageExtractor
+         */
+        const SecurityPolicyRule::MessageExtractor& getMessageExtractor() const {
+            return m_extractor ? *m_extractor : m_defaultExtractor;
+        }
+
+        /**
+         * Sets the MessageExtractor in effect. Setting no extractor will
+         * cause the default extractor to be used.
+         * 
+         * <p>The extractor will be freed by the SecurityPolicy.
+         * 
+         * @param extractor the MessageExtractor to use
+         */
+        void setMessageExtractor(SecurityPolicyRule::MessageExtractor* extractor) {
+            delete m_extractor;
+            m_extractor = extractor;
+        }
+
     protected:
         /** A shared matching object that just supports the default matching rules. */
         static IssuerMatchingPolicy m_defaultMatching;
 
+        /** A shared extractor object that just supports the default SAML message types. */
+        static SecurityPolicyRule::MessageExtractor m_defaultExtractor;
+
     private:
         saml2::Issuer* m_issuer;
         const saml2md::RoleDescriptor* m_issuerRole;
         
         IssuerMatchingPolicy* m_matchingPolicy;
+        SecurityPolicyRule::MessageExtractor* m_extractor;
+
         std::vector<const SecurityPolicyRule*> m_rules;
         const saml2md::MetadataProvider* m_metadata;
         xmltooling::QName m_role;
index 0222017..9a1b49f 100644 (file)
@@ -52,6 +52,27 @@ namespace opensaml {
     public:
         virtual ~SecurityPolicyRule() {}
 
+        /** Allows override of code for extracting saml2:Issuer and protocol information. */
+        class SAML_API MessageExtractor {
+            MAKE_NONCOPYABLE(MessageExtractor);
+        public:
+            MessageExtractor() {}
+            virtual ~MessageExtractor() {}
+            
+            /**
+             * Examines the message and/or its contents and extracts the issuer's claimed
+             * identity along with a protocol identifier. Conventions may be needed
+             * to properly encode non-SAML2 issuer information into a compatible form. 
+             * 
+             * <p>The caller is responsible for freeing the Issuer object.
+             * 
+             * @param message       message to examine
+             * @return  a pair consisting of a SAML 2.0 Issuer object and a protocol constant.
+             * @throws std::bad_cast thrown if the message is not of an expected type
+             */
+            virtual std::pair<saml2::Issuer*,const XMLCh*> getIssuerAndProtocol(const xmltooling::XMLObject& message) const;
+        };
+
         /**
          * Evaluates the rule against the given request and message. If an Issuer is
          * returned, the caller is responsible for freeing the Issuer object.
@@ -61,6 +82,7 @@ namespace opensaml {
          * @param metadataProvider  locked MetadataProvider instance to authenticate the message
          * @param role              identifies the role (generally IdP or SP) of the peer who issued the message 
          * @param trustEngine       TrustEngine to authenticate the message
+         * @param extractor         MessageExtractor to use in examining message
          * @return the identity of the message issuer, in two forms, or NULL
          * 
          * @throws BindingException thrown if the request/message do not meet the requirements of this rule
@@ -70,7 +92,8 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine
+            const TrustEngine* trustEngine,
+            const MessageExtractor& extractor
             ) const=0;
     };
 
index 5a2659c..a4d79e2 100644 (file)
@@ -42,22 +42,9 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine
+            const TrustEngine* trustEngine,
+            const MessageExtractor& extractor
             ) const;
-    
-    protected:
-        /**
-         * Examines the message and/or its contents and extracts the issuer's claimed
-         * identity along with a protocol identifier. The two together can be used to
-         * locate metadata to use in validating the signature. Conventions may be needed
-         * to properly encode non-SAML2 issuer information into a compatible form. 
-         * 
-         * <p>The caller is responsible for freeing the Issuer object.
-         * 
-         * @param message       message to examine
-         * @return  a pair consisting of a SAML 2.0 Issuer object and a protocol constant.
-         */
-        virtual std::pair<saml2::Issuer*,const XMLCh*> getIssuerAndProtocol(const xmltooling::XMLObject& message) const;
     };
     
 };
index f4ff85e..29816b7 100644 (file)
@@ -41,22 +41,9 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine
+            const TrustEngine* trustEngine,
+            const MessageExtractor& extractor
             ) const;
-    
-    protected:
-        /**
-         * Examines the message and/or its contents and extracts the issuer's claimed
-         * identity along with a protocol identifier. The two together can be used to
-         * locate metadata to use in validating the signature. Conventions may be needed
-         * to properly encode non-SAML2 issuer information into a compatible form. 
-         * 
-         * <p>The caller is responsible for freeing the Issuer object.
-         * 
-         * @param message       message to examine
-         * @return  a pair consisting of a SAML 2.0 Issuer object and a protocol constant.
-         */
-        virtual std::pair<saml2::Issuer*,const XMLCh*> getIssuerAndProtocol(const xmltooling::XMLObject& message) const;
     };
     
 };
index 001f84e..5c30951 100644 (file)
@@ -62,7 +62,8 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> MessageFlowRule::evaluate(
     const XMLObject& message,
     const saml2md::MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine
+    const opensaml::TrustEngine* trustEngine,
+    const MessageExtractor& extractor
     ) const
 {
     try {
index 437241a..2edad23 100644 (file)
 #include "internal.h"
 #include "exceptions.h"
 #include "binding/SecurityPolicy.h"
+#include "saml1/core/Assertions.h"
+#include "saml1/core/Protocols.h"
 #include "saml2/core/Assertions.h"
+#include "saml2/core/Protocols.h"
 
 using namespace opensaml::saml2md;
 using namespace opensaml::saml2;
@@ -47,8 +50,11 @@ void SAML_API opensaml::registerSecurityPolicyRules()
 
 SecurityPolicy::IssuerMatchingPolicy SecurityPolicy::m_defaultMatching;
 
+SecurityPolicyRule::MessageExtractor SecurityPolicy::m_defaultExtractor;
+
 SecurityPolicy::~SecurityPolicy()
 {
+    delete m_extractor;
     delete m_matchingPolicy;
     delete m_issuer;
 }
@@ -58,12 +64,13 @@ void SecurityPolicy::evaluate(const GenericRequest& request, const XMLObject& me
     for (vector<const SecurityPolicyRule*>::const_iterator i=m_rules.begin(); i!=m_rules.end(); ++i) {
 
         // Run the rule...
-        pair<Issuer*,const RoleDescriptor*> ident = (*i)->evaluate(request,message,m_metadata,&m_role,m_trust);
+        pair<Issuer*,const RoleDescriptor*> ident =
+            (*i)->evaluate(request,message,m_metadata,&m_role,m_trust,getMessageExtractor());
 
         // Make sure returned issuer doesn't conflict.
          
         if (ident.first) {
-            if (!(m_matchingPolicy ? m_matchingPolicy : &m_defaultMatching)->issuerMatches(ident.first, m_issuer)) {
+            if (!getIssuerMatchingPolicy().issuerMatches(ident.first, m_issuer)) {
                 delete ident.first;
                 throw BindingException("Policy rules returned differing Issuers.");
             }
@@ -81,7 +88,7 @@ void SecurityPolicy::evaluate(const GenericRequest& request, const XMLObject& me
 
 void SecurityPolicy::setIssuer(saml2::Issuer* issuer)
 {
-    if (!((m_matchingPolicy ? m_matchingPolicy : &m_defaultMatching))->issuerMatches(issuer, m_issuer)) {
+    if (!getIssuerMatchingPolicy().issuerMatches(issuer, m_issuer)) {
         delete issuer;
         throw BindingException("Externally provided Issuer conflicts with policy results.");
     }
@@ -125,3 +132,62 @@ bool SecurityPolicy::IssuerMatchingPolicy::issuerMatches(const Issuer* issuer1,
     
     return true;
 }
+
+
+pair<saml2::Issuer*,const XMLCh*> SecurityPolicyRule::MessageExtractor::getIssuerAndProtocol(const XMLObject& message) const
+{
+    // We just let any bad casts throw here.
+    
+    saml2::Issuer* issuer;
+
+    // Shortcuts some of the casting.
+    const XMLCh* ns = message.getElementQName().getNamespaceURI();
+    if (ns) {
+        if (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML20_NS)) {
+            // 2.0 namespace should be castable to a specialized 2.0 root.
+            const saml2::RootObject& root = dynamic_cast<const saml2::RootObject&>(message);
+            issuer = root.getIssuer();
+            if (issuer && issuer->getName()) {
+                return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+            }
+            
+            // No issuer in the message, so we have to try the Response approach. 
+            const vector<saml2::Assertion*>& assertions = dynamic_cast<const saml2p::Response&>(message).getAssertions();
+            if (!assertions.empty()) {
+                issuer = assertions.front()->getIssuer();
+                if (issuer && issuer->getName())
+                    return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
+            }
+        }
+        else if (XMLString::equals(ns, samlconstants::SAML1P_NS)) {
+            // Should be a samlp:Response, at least in OpenSAML.
+            const vector<saml1::Assertion*>& assertions = dynamic_cast<const saml1p::Response&>(message).getAssertions();
+            if (!assertions.empty()) {
+                const saml1::Assertion* a = assertions.front();
+                if (a->getIssuer()) {
+                    issuer = saml2::IssuerBuilder::buildIssuer();
+                    issuer->setName(a->getIssuer());
+                    pair<bool,int> minor = a->getMinorVersion();
+                    return make_pair(
+                        issuer,
+                        (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+                        );
+                }
+            }
+        }
+        else if (XMLString::equals(ns, samlconstants::SAML1_NS)) {
+            // Should be a saml:Assertion.
+            const saml1::Assertion& a = dynamic_cast<const saml1::Assertion&>(message);
+            if (a.getIssuer()) {
+                issuer = saml2::IssuerBuilder::buildIssuer();
+                issuer->setName(a.getIssuer());
+                pair<bool,int> minor = a.getMinorVersion();
+                return make_pair(
+                    issuer,
+                    (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
+                    );
+            }
+        }
+    }
+    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
+}
index 481223d..44c6e9c 100644 (file)
@@ -74,7 +74,8 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
     const XMLObject& message,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine
+    const opensaml::TrustEngine* trustEngine,
+    const MessageExtractor& extractor
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
@@ -101,7 +102,7 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
 
     try {
         log.debug("extracting issuer from message");
-        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = extractor.getIssuerAndProtocol(message);
         
         auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
         if (!issuerInfo.first || !issuerInfo.second ||
@@ -194,30 +195,3 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> SimpleSigningRule::evaluate(
     }
     return ret;
 }
-
-pair<saml2::Issuer*,const XMLCh*> SimpleSigningRule::getIssuerAndProtocol(const XMLObject& message) const
-{
-    // We just let any bad casts throw here.
-
-    // Shortcuts some of the casting.
-    const XMLCh* ns = message.getElementQName().getNamespaceURI();
-    if (ns) {
-        if (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML20_NS)) {
-            // 2.0 namespace should be castable to a specialized 2.0 root.
-            const saml2::RootObject& root = dynamic_cast<const saml2::RootObject&>(message);
-            saml2::Issuer* issuer = root.getIssuer();
-            if (issuer && issuer->getName()) {
-                return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
-            }
-            
-            // No issuer in the message, so we have to try the Response approach. 
-            const vector<saml2::Assertion*>& assertions = dynamic_cast<const saml2p::Response&>(message).getAssertions();
-            if (!assertions.empty()) {
-                issuer = assertions.front()->getIssuer();
-                if (issuer && issuer->getName())
-                    return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
-            }
-        }
-    }
-    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
-}
index cad0e6d..a63c18c 100644 (file)
@@ -24,8 +24,6 @@
 #include "exceptions.h"
 #include "RootObject.h"
 #include "binding/XMLSigningRule.h"
-#include "saml1/core/Assertions.h"
-#include "saml1/core/Protocols.h"
 #include "saml2/core/Protocols.h"
 #include "saml2/metadata/Metadata.h"
 #include "saml2/metadata/MetadataProvider.h"
@@ -53,7 +51,8 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> XMLSigningRule::evaluate(
     const XMLObject& message,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine
+    const opensaml::TrustEngine* trustEngine,
+    const MessageExtractor& extractor
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.XMLSigning");
@@ -74,7 +73,7 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> XMLSigningRule::evaluate(
         }
         
         log.debug("extracting issuer from message");
-        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = extractor.getIssuerAndProtocol(message);
         
         auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
         if (!issuerInfo.first || !issuerInfo.second ||
@@ -117,61 +116,3 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> XMLSigningRule::evaluate(
     }
     return ret;
 }
-
-pair<saml2::Issuer*,const XMLCh*> XMLSigningRule::getIssuerAndProtocol(const XMLObject& message) const
-{
-    // We just let any bad casts throw here.
-    
-    saml2::Issuer* issuer;
-
-    // Shortcuts some of the casting.
-    const XMLCh* ns = message.getElementQName().getNamespaceURI();
-    if (ns) {
-        if (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML20_NS)) {
-            // 2.0 namespace should be castable to a specialized 2.0 root.
-            const saml2::RootObject& root = dynamic_cast<const saml2::RootObject&>(message);
-            issuer = root.getIssuer();
-            if (issuer && issuer->getName()) {
-                return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
-            }
-            
-            // No issuer in the message, so we have to try the Response approach. 
-            const vector<saml2::Assertion*>& assertions = dynamic_cast<const saml2p::Response&>(message).getAssertions();
-            if (!assertions.empty()) {
-                issuer = assertions.front()->getIssuer();
-                if (issuer && issuer->getName())
-                    return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS);
-            }
-        }
-        else if (XMLString::equals(ns, samlconstants::SAML1P_NS)) {
-            // Should be a samlp:Response, at least in OpenSAML.
-            const vector<saml1::Assertion*>& assertions = dynamic_cast<const saml1p::Response&>(message).getAssertions();
-            if (!assertions.empty()) {
-                const saml1::Assertion* a = assertions.front();
-                if (a->getIssuer()) {
-                    issuer = saml2::IssuerBuilder::buildIssuer();
-                    issuer->setName(a->getIssuer());
-                    pair<bool,int> minor = a->getMinorVersion();
-                    return make_pair(
-                        issuer,
-                        (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
-                        );
-                }
-            }
-        }
-        else if (XMLString::equals(ns, samlconstants::SAML1_NS)) {
-            // Should be a saml:Assertion.
-            const saml1::Assertion& a = dynamic_cast<const saml1::Assertion&>(message);
-            if (a.getIssuer()) {
-                issuer = saml2::IssuerBuilder::buildIssuer();
-                issuer->setName(a.getIssuer());
-                pair<bool,int> minor = a.getMinorVersion();
-                return make_pair(
-                    issuer,
-                    (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM
-                    );
-            }
-        }
-    }
-    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
-}