Merged issuer/protocol extraction back into rules.
authorScott Cantor <cantor.2@osu.edu>
Sat, 11 Nov 2006 23:11:30 +0000 (23:11 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sat, 11 Nov 2006 23:11:30 +0000 (23:11 +0000)
12 files changed:
saml/binding/ClientCertAuthRule.h
saml/binding/MessageFlowRule.h
saml/binding/SecurityPolicy.h
saml/binding/SecurityPolicyRule.h
saml/binding/SimpleSigningRule.h
saml/binding/XMLSigningRule.h
saml/binding/impl/ClientCertAuthRule.cpp
saml/binding/impl/MessageDecoder.cpp
saml/binding/impl/MessageFlowRule.cpp
saml/binding/impl/SecurityPolicy.cpp
saml/binding/impl/SimpleSigningRule.cpp
saml/binding/impl/XMLSigningRule.cpp

index 287f0e4..154d27b 100644 (file)
@@ -38,9 +38,21 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine,
-            const MessageExtractor& extractor
+            const TrustEngine* trustEngine
             ) 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 d9fdeed..632f305 100644 (file)
@@ -41,8 +41,7 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine,
-            const MessageExtractor& extractor
+            const TrustEngine* trustEngine
             ) const;
 
         /**
index fcf3151..2074e79 100644 (file)
@@ -62,8 +62,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_extractor(NULL),
-                m_metadata(metadataProvider), m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
+            ) : m_issuer(NULL), m_issuerRole(NULL), m_matchingPolicy(NULL), m_metadata(metadataProvider),
+                m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
         }
 
         /**
@@ -80,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_extractor(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_rules(rules), m_metadata(metadataProvider),
+                m_role(role ? *role : xmltooling::QName()), m_trust(trustEngine) {
         }
 
         virtual ~SecurityPolicy();
@@ -238,42 +238,15 @@ namespace opensaml {
             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 14e0352..427f5cc 100644 (file)
@@ -52,27 +52,6 @@ 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.
@@ -92,8 +71,7 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine,
-            const MessageExtractor& extractor
+            const TrustEngine* trustEngine
             ) const=0;
     };
 
index e0488c0..5a2659c 100644 (file)
@@ -27,6 +27,9 @@ namespace opensaml {
     /**
      * Blob-oriented signature checking SecurityPolicyRule for
      * bindings that support non-XML signature techniques.
+     * 
+     * Subclasses can provide support for additional message types
+     * by overriding the issuer derivation method.
      */
     class SAML_API SimpleSigningRule : public SecurityPolicyRule
     {
@@ -39,9 +42,22 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine,
-            const MessageExtractor& extractor
+            const TrustEngine* trustEngine
             ) 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 2b01d39..f4ff85e 100644 (file)
@@ -26,6 +26,9 @@
 namespace opensaml {
     /**
      * XML Signature checking SecurityPolicyRule
+     * 
+     * Subclasses can provide support for additional message types
+     * by overriding the issuer derivation method.
      */
     class SAML_API XMLSigningRule : public SecurityPolicyRule
     {
@@ -38,9 +41,22 @@ namespace opensaml {
             const xmltooling::XMLObject& message,
             const saml2md::MetadataProvider* metadataProvider,
             const xmltooling::QName* role,
-            const TrustEngine* trustEngine,
-            const MessageExtractor& extractor
+            const TrustEngine* trustEngine
             ) 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 63a6bb1..fc5be31 100644 (file)
@@ -49,8 +49,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
     const XMLObject& message,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine,
-    const MessageExtractor& extractor
+    const opensaml::TrustEngine* trustEngine
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.ClientCertAuth");
@@ -72,7 +71,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
     
     try {
         log.debug("extracting issuer from message");
-        pair<saml2::Issuer*,const XMLCh*> issuerInfo = extractor.getIssuerAndProtocol(message);
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
         
         auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
         if (!issuerInfo.first || !issuerInfo.second ||
@@ -115,3 +114,21 @@ pair<saml2::Issuer*,const RoleDescriptor*> ClientCertAuthRule::evaluate(
     }
     return ret;
 }
+
+pair<saml2::Issuer*,const XMLCh*> ClientCertAuthRule::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)) {
+            // 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);
+        }
+    }
+    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
+}
index 90819f0..83bd7b1 100644 (file)
@@ -31,6 +31,7 @@ namespace opensaml {
     namespace saml1p {
         SAML_DLLLOCAL PluginManager<MessageDecoder,const DOMElement*>::Factory SAML1ArtifactDecoderFactory;
         SAML_DLLLOCAL PluginManager<MessageDecoder,const DOMElement*>::Factory SAML1POSTDecoderFactory;
+        SAML_DLLLOCAL PluginManager<MessageDecoder,const DOMElement*>::Factory SAML1SOAPDecoderFactory;
     }; 
 
     namespace saml2p {
@@ -45,6 +46,7 @@ void SAML_API opensaml::registerMessageDecoders()
     SAMLConfig& conf=SAMLConfig::getConfig();
     conf.MessageDecoderManager.registerFactory(samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT, saml1p::SAML1ArtifactDecoderFactory);
     conf.MessageDecoderManager.registerFactory(samlconstants::SAML1_PROFILE_BROWSER_POST, saml1p::SAML1POSTDecoderFactory);
+    conf.MessageDecoderManager.registerFactory(samlconstants::SAML1_BINDING_SOAP, saml1p::SAML1SOAPDecoderFactory);
     conf.MessageDecoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_ARTIFACT, saml2p::SAML2ArtifactDecoderFactory);
     conf.MessageDecoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_POST, saml2p::SAML2POSTDecoderFactory);
     conf.MessageDecoderManager.registerFactory(samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN, saml2p::SAML2POSTDecoderFactory);
index 5c30951..476ef69 100644 (file)
@@ -24,6 +24,7 @@
 #include "exceptions.h"
 #include "RootObject.h"
 #include "binding/MessageFlowRule.h"
+#include "util/SAMLConstants.h"
 
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/ReplayCache.h>
@@ -62,16 +63,24 @@ pair<saml2::Issuer*,const saml2md::RoleDescriptor*> MessageFlowRule::evaluate(
     const XMLObject& message,
     const saml2md::MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine,
-    const MessageExtractor& extractor
+    const opensaml::TrustEngine* trustEngine
     ) const
 {
+    Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.MessageFlow");
+    log.debug("evaluating message flow policy");
+
     try {
-        const RootObject& obj = dynamic_cast<const RootObject&>(message);
-        check(obj.getID(), obj.getIssueInstantEpoch());
+        const XMLCh* ns = message.getElementQName().getNamespaceURI();
+        if (ns && (XMLString::equals(ns, samlconstants::SAML20P_NS) || XMLString::equals(ns, samlconstants::SAML1P_NS))) {
+            const RootObject& obj = dynamic_cast<const RootObject&>(message);
+            check(obj.getID(), obj.getIssueInstantEpoch());
+        }
+        else {
+            log.debug("ignoring unrecognized message type");
+        }
     }
     catch (bad_cast&) {
-        throw BindingException("Message was not of a recognized type.");
+        log.warn("caught a bad_cast while extracting issuer");
     }
     return pair<saml2::Issuer*,const saml2md::RoleDescriptor*>(NULL,NULL);
 }
index 89aad2d..b2157ce 100644 (file)
@@ -52,11 +52,8 @@ 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;
 }
@@ -66,8 +63,7 @@ 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,getMessageExtractor());
+        pair<Issuer*,const RoleDescriptor*> ident = (*i)->evaluate(request,message,m_metadata,&m_role,m_trust);
 
         // Make sure returned issuer doesn't conflict.
          
@@ -134,62 +130,3 @@ 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 6ba58f8..91e53fa 100644 (file)
@@ -24,6 +24,7 @@
 #include "exceptions.h"
 #include "binding/HTTPRequest.h"
 #include "binding/SimpleSigningRule.h"
+#include "saml2/core/Protocols.h"
 #include "saml2/metadata/Metadata.h"
 #include "saml2/metadata/MetadataProvider.h"
 #include "security/TrustEngine.h"
@@ -72,8 +73,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
     const XMLObject& message,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine,
-    const MessageExtractor& extractor
+    const opensaml::TrustEngine* trustEngine
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.SimpleSigning");
@@ -100,7 +100,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> SimpleSigningRule::evaluate(
 
     try {
         log.debug("extracting issuer from message");
-        pair<saml2::Issuer*,const XMLCh*> issuerInfo = extractor.getIssuerAndProtocol(message);
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
         
         auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
         if (!issuerInfo.first || !issuerInfo.second ||
@@ -193,3 +193,29 @@ pair<saml2::Issuer*,const 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)) {
+            // 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 552cef8..d667cfd 100644 (file)
@@ -23,7 +23,9 @@
 #include "internal.h"
 #include "exceptions.h"
 #include "binding/XMLSigningRule.h"
-#include "saml2/core/Assertions.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"
 #include "security/TrustEngine.h"
@@ -50,8 +52,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> XMLSigningRule::evaluate(
     const XMLObject& message,
     const MetadataProvider* metadataProvider,
     const QName* role,
-    const opensaml::TrustEngine* trustEngine,
-    const MessageExtractor& extractor
+    const opensaml::TrustEngine* trustEngine
     ) const
 {
     Category& log=Category::getInstance(SAML_LOGCAT".SecurityPolicyRule.XMLSigning");
@@ -65,14 +66,14 @@ pair<saml2::Issuer*,const RoleDescriptor*> XMLSigningRule::evaluate(
     }
     
     try {
-        const RootObject& root = dynamic_cast<const RootObject&>(message);
-        if (!root.getSignature()) {
-            log.debug("ignoring unsigned message");
+        const SignableObject* signable = dynamic_cast<const SignableObject*>(&message);
+        if (!signable || !signable->getSignature()) {
+            log.debug("ignoring unsigned or unrecognized message");
             return ret;
         }
         
         log.debug("extracting issuer from message");
-        pair<saml2::Issuer*,const XMLCh*> issuerInfo = extractor.getIssuerAndProtocol(message);
+        pair<saml2::Issuer*,const XMLCh*> issuerInfo = getIssuerAndProtocol(message);
         
         auto_ptr<saml2::Issuer> issuer(issuerInfo.first);
         if (!issuerInfo.first || !issuerInfo.second ||
@@ -96,7 +97,7 @@ pair<saml2::Issuer*,const RoleDescriptor*> XMLSigningRule::evaluate(
             return ret;
         }
 
-        if (!trustEngine->validate(*(root.getSignature()), *roledesc, metadataProvider->getKeyResolver())) {
+        if (!trustEngine->validate(*(signable->getSignature()), *roledesc, metadataProvider->getKeyResolver())) {
             log.error("unable to verify signature on message with supplied trust engine");
             return ret;
         }
@@ -115,3 +116,48 @@ pair<saml2::Issuer*,const 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
+                        );
+                }
+            }
+        }
+    }
+    return pair<saml2::Issuer*,const XMLCh*>(NULL,NULL);
+}