From f753e2293ab6a40575bc9b294490e134eac5db9e Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sat, 11 Nov 2006 23:11:30 +0000 Subject: [PATCH] Merged issuer/protocol extraction back into rules. --- saml/binding/ClientCertAuthRule.h | 18 +++++++-- saml/binding/MessageFlowRule.h | 3 +- saml/binding/SecurityPolicy.h | 35 ++--------------- saml/binding/SecurityPolicyRule.h | 24 +----------- saml/binding/SimpleSigningRule.h | 20 +++++++++- saml/binding/XMLSigningRule.h | 20 +++++++++- saml/binding/impl/ClientCertAuthRule.cpp | 23 +++++++++-- saml/binding/impl/MessageDecoder.cpp | 2 + saml/binding/impl/MessageFlowRule.cpp | 19 +++++++--- saml/binding/impl/SecurityPolicy.cpp | 65 +------------------------------- saml/binding/impl/SimpleSigningRule.cpp | 32 ++++++++++++++-- saml/binding/impl/XMLSigningRule.cpp | 62 ++++++++++++++++++++++++++---- 12 files changed, 177 insertions(+), 146 deletions(-) diff --git a/saml/binding/ClientCertAuthRule.h b/saml/binding/ClientCertAuthRule.h index 287f0e4..154d27b 100644 --- a/saml/binding/ClientCertAuthRule.h +++ b/saml/binding/ClientCertAuthRule.h @@ -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. + * + *

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 getIssuerAndProtocol(const xmltooling::XMLObject& message) const; }; - }; diff --git a/saml/binding/MessageFlowRule.h b/saml/binding/MessageFlowRule.h index d9fdeed..632f305 100644 --- a/saml/binding/MessageFlowRule.h +++ b/saml/binding/MessageFlowRule.h @@ -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; /** diff --git a/saml/binding/SecurityPolicy.h b/saml/binding/SecurityPolicy.h index fcf3151..2074e79 100644 --- a/saml/binding/SecurityPolicy.h +++ b/saml/binding/SecurityPolicy.h @@ -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. - * - *

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 m_rules; const saml2md::MetadataProvider* m_metadata; xmltooling::QName m_role; diff --git a/saml/binding/SecurityPolicyRule.h b/saml/binding/SecurityPolicyRule.h index 14e0352..427f5cc 100644 --- a/saml/binding/SecurityPolicyRule.h +++ b/saml/binding/SecurityPolicyRule.h @@ -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. - * - *

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 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; }; diff --git a/saml/binding/SimpleSigningRule.h b/saml/binding/SimpleSigningRule.h index e0488c0..5a2659c 100644 --- a/saml/binding/SimpleSigningRule.h +++ b/saml/binding/SimpleSigningRule.h @@ -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. + * + *

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 getIssuerAndProtocol(const xmltooling::XMLObject& message) const; }; }; diff --git a/saml/binding/XMLSigningRule.h b/saml/binding/XMLSigningRule.h index 2b01d39..f4ff85e 100644 --- a/saml/binding/XMLSigningRule.h +++ b/saml/binding/XMLSigningRule.h @@ -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. + * + *

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 getIssuerAndProtocol(const xmltooling::XMLObject& message) const; }; }; diff --git a/saml/binding/impl/ClientCertAuthRule.cpp b/saml/binding/impl/ClientCertAuthRule.cpp index 63a6bb1..fc5be31 100644 --- a/saml/binding/impl/ClientCertAuthRule.cpp +++ b/saml/binding/impl/ClientCertAuthRule.cpp @@ -49,8 +49,7 @@ pair 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 ClientCertAuthRule::evaluate( try { log.debug("extracting issuer from message"); - pair issuerInfo = extractor.getIssuerAndProtocol(message); + pair issuerInfo = getIssuerAndProtocol(message); auto_ptr issuer(issuerInfo.first); if (!issuerInfo.first || !issuerInfo.second || @@ -115,3 +114,21 @@ pair ClientCertAuthRule::evaluate( } return ret; } + +pair 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(message); + saml2::Issuer* issuer = root.getIssuer(); + if (issuer && issuer->getName()) + return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS); + } + } + return pair(NULL,NULL); +} diff --git a/saml/binding/impl/MessageDecoder.cpp b/saml/binding/impl/MessageDecoder.cpp index 90819f0..83bd7b1 100644 --- a/saml/binding/impl/MessageDecoder.cpp +++ b/saml/binding/impl/MessageDecoder.cpp @@ -31,6 +31,7 @@ namespace opensaml { namespace saml1p { SAML_DLLLOCAL PluginManager::Factory SAML1ArtifactDecoderFactory; SAML_DLLLOCAL PluginManager::Factory SAML1POSTDecoderFactory; + SAML_DLLLOCAL PluginManager::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); diff --git a/saml/binding/impl/MessageFlowRule.cpp b/saml/binding/impl/MessageFlowRule.cpp index 5c30951..476ef69 100644 --- a/saml/binding/impl/MessageFlowRule.cpp +++ b/saml/binding/impl/MessageFlowRule.cpp @@ -24,6 +24,7 @@ #include "exceptions.h" #include "RootObject.h" #include "binding/MessageFlowRule.h" +#include "util/SAMLConstants.h" #include #include @@ -62,16 +63,24 @@ pair 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(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(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(NULL,NULL); } diff --git a/saml/binding/impl/SecurityPolicy.cpp b/saml/binding/impl/SecurityPolicy.cpp index 89aad2d..b2157ce 100644 --- a/saml/binding/impl/SecurityPolicy.cpp +++ b/saml/binding/impl/SecurityPolicy.cpp @@ -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_iterator i=m_rules.begin(); i!=m_rules.end(); ++i) { // Run the rule... - pair ident = - (*i)->evaluate(request,message,m_metadata,&m_role,m_trust,getMessageExtractor()); + pair 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 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(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& assertions = dynamic_cast(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& assertions = dynamic_cast(message).getAssertions(); - if (!assertions.empty()) { - const saml1::Assertion* a = assertions.front(); - if (a->getIssuer()) { - issuer = saml2::IssuerBuilder::buildIssuer(); - issuer->setName(a->getIssuer()); - pair 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(message); - if (a.getIssuer()) { - issuer = saml2::IssuerBuilder::buildIssuer(); - issuer->setName(a.getIssuer()); - pair minor = a.getMinorVersion(); - return make_pair( - issuer, - (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM - ); - } - } - } - return pair(NULL,NULL); -} diff --git a/saml/binding/impl/SimpleSigningRule.cpp b/saml/binding/impl/SimpleSigningRule.cpp index 6ba58f8..91e53fa 100644 --- a/saml/binding/impl/SimpleSigningRule.cpp +++ b/saml/binding/impl/SimpleSigningRule.cpp @@ -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 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 SimpleSigningRule::evaluate( try { log.debug("extracting issuer from message"); - pair issuerInfo = extractor.getIssuerAndProtocol(message); + pair issuerInfo = getIssuerAndProtocol(message); auto_ptr issuer(issuerInfo.first); if (!issuerInfo.first || !issuerInfo.second || @@ -193,3 +193,29 @@ pair SimpleSigningRule::evaluate( } return ret; } + +pair 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(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& assertions = dynamic_cast(message).getAssertions(); + if (!assertions.empty()) { + issuer = assertions.front()->getIssuer(); + if (issuer && issuer->getName()) + return make_pair(issuer->cloneIssuer(), samlconstants::SAML20P_NS); + } + } + } + return pair(NULL,NULL); +} diff --git a/saml/binding/impl/XMLSigningRule.cpp b/saml/binding/impl/XMLSigningRule.cpp index 552cef8..d667cfd 100644 --- a/saml/binding/impl/XMLSigningRule.cpp +++ b/saml/binding/impl/XMLSigningRule.cpp @@ -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 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 XMLSigningRule::evaluate( } try { - const RootObject& root = dynamic_cast(message); - if (!root.getSignature()) { - log.debug("ignoring unsigned message"); + const SignableObject* signable = dynamic_cast(&message); + if (!signable || !signable->getSignature()) { + log.debug("ignoring unsigned or unrecognized message"); return ret; } log.debug("extracting issuer from message"); - pair issuerInfo = extractor.getIssuerAndProtocol(message); + pair issuerInfo = getIssuerAndProtocol(message); auto_ptr issuer(issuerInfo.first); if (!issuerInfo.first || !issuerInfo.second || @@ -96,7 +97,7 @@ pair 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 XMLSigningRule::evaluate( } return ret; } + +pair 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(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& assertions = dynamic_cast(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& assertions = dynamic_cast(message).getAssertions(); + if (!assertions.empty()) { + const saml1::Assertion* a = assertions.front(); + if (a->getIssuer()) { + issuer = saml2::IssuerBuilder::buildIssuer(); + issuer->setName(a->getIssuer()); + pair minor = a->getMinorVersion(); + return make_pair( + issuer, + (minor.first && minor.second==0) ? samlconstants::SAML10_PROTOCOL_ENUM : samlconstants::SAML11_PROTOCOL_ENUM + ); + } + } + } + } + return pair(NULL,NULL); +} -- 2.1.4