<pathentry kind="src" path="saml/signature"/>\r
<pathentry kind="src" path="saml/util"/>\r
<pathentry kind="src" path="samltest/signature"/>\r
-<pathentry excluding="saml1/core/impl/|signature/|saml2/core/impl/|saml2/metadata/|security/|saml1/binding/" kind="src" path="samltest"/>\r
+<pathentry excluding="saml1/core/impl/|signature/|saml2/core/impl/|saml2/metadata/|security/|saml1/binding/|saml2/binding/" kind="src" path="samltest"/>\r
<pathentry kind="src" path="samltest/saml1/core/impl"/>\r
<pathentry kind="src" path="samltest/saml2/core/impl"/>\r
<pathentry kind="src" path="samltest/saml2/metadata"/>\r
<pathentry kind="src" path="samltest/security"/>\r
<pathentry kind="src" path="samltest/saml1/binding"/>\r
+<pathentry kind="src" path="samltest/saml2/binding"/>\r
</item>\r
</data>\r
</cdtproject>\r
saml1/binding/SAMLArtifactType0001.h \
saml1/binding/SAMLArtifactType0002.h \
saml1/binding/SAML1ArtifactEncoder.h \
+ saml1/binding/SAML1POSTDecoder.h \
saml1/binding/SAML1POSTEncoder.h
saml2coreinclude_HEADERS = \
saml2bindinclude_HEADERS = \
saml2/binding/SAML2Artifact.h \
saml2/binding/SAML2ArtifactType0004.h \
- saml1/binding/SAML2ArtifactEncoder.h \
- saml1/binding/SAML2POSTEncoder.h
+ saml2/binding/SAML2ArtifactEncoder.h \
+ saml2/binding/SAML2POSTDecoder.h \
+ saml2/binding/SAML2POSTEncoder.h
saml2mdinclude_HEADERS = \
saml2/metadata/AbstractMetadataProvider.h \
saml2/binding/impl/SAML2Artifact.cpp \
saml2/binding/impl/SAML2ArtifactType0004.cpp \
saml2/binding/impl/SAML2ArtifactEncoder.cpp \
+ saml2/binding/impl/SAML2POSTDecoder.cpp \
saml2/binding/impl/SAML2POSTEncoder.cpp \
encryption/EncryptedKeyResolver.cpp \
security/impl/TrustEngine.cpp \
//conf.MessageDecoderManager.registerFactory(SAML1_ARTIFACT_DECODER, saml1p::SAML1ArtifactDecoderFactory);
conf.MessageDecoderManager.registerFactory(SAML1_POST_DECODER, saml1p::SAML1POSTDecoderFactory);
//conf.MessageDecoderManager.registerFactory(SAML2_ARTIFACT_DECODER, saml2p::SAML2ArtifactDecoderFactory);
- //conf.MessageDecoderManager.registerFactory(SAML2_POST_DECODER, saml2p::SAML2POSTDecoderFactory);
+ conf.MessageDecoderManager.registerFactory(SAML2_POST_DECODER, saml2p::SAML2POSTDecoderFactory);
}
</FileConfiguration>\r
</File>\r
<File\r
+ RelativePath=".\saml2\binding\impl\SAML2POSTDecoder.cpp"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\saml2\binding\impl\SAML2POSTEncoder.cpp"\r
>\r
</File>\r
>\r
</File>\r
<File\r
+ RelativePath=".\saml2\binding\SAML2POSTDecoder.h"\r
+ >\r
+ </File>\r
+ <File\r
RelativePath=".\saml2\binding\SAML2POSTEncoder.h"\r
>\r
</File>\r
if (!response)
throw BindingException("Decoded message was not a SAML 1.x Response.");
+ const EntityDescriptor* provider=NULL;
try {
if (!m_validate)
SchemaValidators.validate(xmlObject.get());
const vector<Assertion*>& assertions=const_cast<const Response*>(response)->getAssertions();
if (!assertions.empty()) {
log.debug("searching metadata for assertion issuer...");
- const EntityDescriptor* provider=
- metadataProvider ? metadataProvider->getEntityDescriptor(assertions.front()->getIssuer()) : NULL;
+ provider=metadataProvider ? metadataProvider->getEntityDescriptor(assertions.front()->getIssuer()) : NULL;
if (provider) {
log.debug("matched assertion issuer against metadata, searching for applicable role...");
pair<bool,int> minor = response->getMinorVersion();
issuerTrusted = static_cast<const TrustEngine*>(trustEngine)->validate(
*(response->getSignature()), *issuer, metadataProvider->getKeyResolver()
);
- if (!issuerTrusted)
+ if (!issuerTrusted) {
log.error("unable to verify signature on message with supplied trust engine");
+ throw BindingException("Message signature failed verification.");
+ }
}
else {
log.warn("unable to verify integrity of the message, leaving untrusted");
}
catch (XMLToolingException& ex) {
// Check for an Issuer.
- const vector<Assertion*>& assertions=const_cast<const Response*>(response)->getAssertions();
- if (!assertions.empty()) {
- if (!metadataProvider) {
+ if (!provider) {
+ const vector<Assertion*>& assertions=const_cast<const Response*>(response)->getAssertions();
+ if (!assertions.empty() || !metadataProvider ||
+ !(provider=metadataProvider->getEntityDescriptor(assertions.front()->getIssuer(), false))) {
// Just record it.
- auto_ptr_char issuer(assertions.front()->getIssuer());
- if (issuer.get())
- ex.addProperty("entityID", issuer.get());
- throw;
- }
- // Try and locate metadata for error handling.
- const EntityDescriptor* provider=metadataProvider->getEntityDescriptor(assertions.front()->getIssuer(),false);
- if (provider) {
- pair<bool,int> minor = response->getMinorVersion();
- issuer=provider->getRoleDescriptor(
- *role,
- (minor.first && minor.second==0) ? SAMLConstants::SAML10_PROTOCOL_ENUM : SAMLConstants::SAML11_PROTOCOL_ENUM
- );
- if (issuer) annotateException(&ex,issuer); // throws it
- annotateException(&ex,provider); // throws it
+ auto_ptr_char iname(assertions.front()->getIssuer());
+ if (iname.get())
+ ex.addProperty("entityID", iname.get());
+ throw;
}
}
+ if (!issuer) {
+ pair<bool,int> minor = response->getMinorVersion();
+ issuer=provider->getRoleDescriptor(
+ *role,
+ (minor.first && minor.second==0) ? SAMLConstants::SAML10_PROTOCOL_ENUM : SAMLConstants::SAML11_PROTOCOL_ENUM
+ );
+ }
+ if (issuer) annotateException(&ex,issuer); // throws it
+ annotateException(&ex,provider); // throws it
}
xmlObject.release();
--- /dev/null
+/*
+ * Copyright 2001-2006 Internet2
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file saml/saml2/binding/SAML2POSTDecoder.h
+ *
+ * SAML 2.0 HTTP POST binding message decoder
+ */
+
+#include <saml/binding/MessageDecoder.h>
+
+namespace opensaml {
+ namespace saml2p {
+
+ /**
+ * SAML 2.0 HTTP POST binding message decoder
+ */
+ class SAML_API SAML2POSTDecoder : public MessageDecoder
+ {
+ public:
+ SAML2POSTDecoder(const DOMElement* e);
+ virtual ~SAML2POSTDecoder();
+
+ xmltooling::XMLObject* decode(
+ std::string& relayState,
+ const saml2md::RoleDescriptor*& issuer,
+ bool& issuerTrusted,
+ const HTTPRequest& httpRequest,
+ const saml2md::MetadataProvider* metadataProvider=NULL,
+ const xmltooling::QName* role=NULL,
+ const X509TrustEngine* trustEngine=NULL
+ ) const;
+ };
+
+ };
+};
--- /dev/null
+/*
+ * Copyright 2001-2006 Internet2
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * SAML2POSTDecoder.cpp
+ *
+ * SAML 2.0 HTTP POST binding message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "saml/binding/ReplayCache.h"
+#include "saml2/binding/SAML2POSTDecoder.h"
+#include "saml2/core/Protocols.h"
+#include "saml2/metadata/Metadata.h"
+#include "saml2/metadata/MetadataProvider.h"
+#include "security/X509TrustEngine.h"
+
+#include <log4cpp/Category.hh>
+#include <xercesc/util/Base64.hpp>
+#include <xmltooling/util/NDC.h>
+
+using namespace opensaml::saml2md;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2;
+using namespace opensaml;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+ namespace saml2p {
+ MessageDecoder* SAML_DLLLOCAL SAML2POSTDecoderFactory(const DOMElement* const & e)
+ {
+ return new SAML2POSTDecoder(e);
+ }
+ };
+};
+
+SAML2POSTDecoder::SAML2POSTDecoder(const DOMElement* e) {}
+
+SAML2POSTDecoder::~SAML2POSTDecoder() {}
+
+XMLObject* SAML2POSTDecoder::decode(
+ string& relayState,
+ const RoleDescriptor*& issuer,
+ bool& issuerTrusted,
+ const HTTPRequest& httpRequest,
+ const MetadataProvider* metadataProvider,
+ const QName* role,
+ const X509TrustEngine* trustEngine
+ ) const
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("decode");
+#endif
+ Category& log = Category::getInstance(SAML_LOGCAT".MessageDecoder.SAML2POST");
+
+ log.debug("validating input");
+ if (strcmp(httpRequest.getMethod(),"POST"))
+ return NULL;
+ const char* msg = httpRequest.getParameter("SAMLResponse");
+ if (!msg)
+ msg = httpRequest.getParameter("SAMLRequest");
+ if (!msg)
+ return NULL;
+ const char* state = httpRequest.getParameter("RelayState");
+ if (state)
+ relayState = state;
+ else
+ relayState.erase();
+
+ // Decode the base64 into SAML.
+ unsigned int x;
+ XMLByte* decoded=Base64::decode(reinterpret_cast<const XMLByte*>(msg),&x);
+ if (!decoded)
+ throw BindingException("Unable to decode base64 in POST binding message.");
+ log.debug("decoded SAML message:\n%s", decoded);
+ istringstream is(reinterpret_cast<char*>(decoded));
+ XMLString::release(&decoded);
+
+ // Parse and bind the document into an XMLObject.
+ DOMDocument* doc = (m_validate ? XMLToolingConfig::getConfig().getValidatingParser()
+ : XMLToolingConfig::getConfig().getParser()).parse(is);
+ XercesJanitor<DOMDocument> janitor(doc);
+ auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));
+ janitor.release();
+
+ StatusResponseType* response = NULL;
+ RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject.get());
+ if (!request) {
+ response = dynamic_cast<StatusResponseType*>(xmlObject.get());
+ if (!response)
+ throw BindingException("XML content for SAML 2.0 HTTP-POST Decoder must be a SAML 2.0 protocol message.");
+ }
+
+ /* For SAML 2, the issuer can be established either from the message, or in some profiles
+ * it's possible to omit it and defer to assertions in a Response.
+ * The Issuer is later matched against metadata, and then trust checking can be applied.
+ */
+ const Issuer* claimedIssuer = request ? request->getIssuer() : response->getIssuer();
+ if (!claimedIssuer) {
+ // Check assertion option. I cannot resist the variable name, for the sake of google.
+ const Response* assbag = dynamic_cast<const Response*>(response);
+ if (assbag) {
+ const vector<Assertion*>& assertions=assbag->getAssertions();
+ if (!assertions.empty())
+ claimedIssuer = assertions.front()->getIssuer();
+ }
+ }
+
+ const EntityDescriptor* provider=NULL;
+ try {
+ if (!m_validate)
+ SchemaValidators.validate(xmlObject.get());
+
+ Signature* signature = request ? request->getSignature() : response->getSignature();
+
+ // Check destination URL.
+ auto_ptr_char dest(request ? request->getDestination() : response->getDestination());
+ const char* dest2 = httpRequest.getRequestURL();
+ if (signature && !dest.get() || !*(dest.get())) {
+ log.error("signed SAML message missing Destination attribute");
+ throw BindingException("Signed SAML message missing Destination attribute identifying intended destination.");
+ }
+ else if (dest.get() && (!dest2 || !*dest2 || strcmp(dest.get(),dest2))) {
+ log.error("POST targeted at (%s), but delivered to (%s)", dest.get(), dest2 ? dest2 : "none");
+ throw BindingException("SAML message delivered with POST to incorrect server URL.");
+ }
+
+ // Check freshness.
+ time_t now = time(NULL);
+ if ((request ? request->getIssueInstant()->getEpoch() : response->getIssueInstant()->getEpoch())
+ < now-(2*XMLToolingConfig::getConfig().clock_skew_secs))
+ throw BindingException("Detected expired POST binding message.");
+
+ // Check replay.
+ ReplayCache* replayCache = SAMLConfig::getConfig().getReplayCache();
+ if (replayCache) {
+ auto_ptr_char id(xmlObject->getXMLID());
+ if (!replayCache->check("SAML2POST", id.get(), response->getIssueInstant()->getEpoch() + (2*XMLToolingConfig::getConfig().clock_skew_secs))) {
+ log.error("replay detected of response ID (%s)", id.get());
+ throw BindingException("Rejecting replayed response ID ($1).", params(1,id.get()));
+ }
+ }
+ else
+ log.warn("replay cache was not provided, this is a serious security risk!");
+
+ issuer = NULL;
+ issuerTrusted = false;
+ log.debug("attempting to establish issuer and integrity of message...");
+
+ // If we can't identify the issuer, we're done, since we can't lookup or verify anything.
+ if (!claimedIssuer || !claimedIssuer->getName()) {
+ log.warn("unable to establish identity of message issuer");
+ return xmlObject.release();
+ }
+ else if (claimedIssuer->getFormat() && !XMLString::equals(claimedIssuer->getFormat(), NameIDType::ENTITY)) {
+ auto_ptr_char iformat(claimedIssuer->getFormat());
+ log.warn("message issuer was in an unsupported format (%s)", iformat.get());
+ return xmlObject.release();
+ }
+
+ log.debug("searching metadata for assertion issuer...");
+ provider=metadataProvider ? metadataProvider->getEntityDescriptor(claimedIssuer->getName()) : NULL;
+ if (provider) {
+ log.debug("matched assertion issuer against metadata, searching for applicable role...");
+ issuer=provider->getRoleDescriptor(*role, SAMLConstants::SAML20P_NS);
+ if (issuer) {
+ if (trustEngine && signature) {
+ issuerTrusted = static_cast<const TrustEngine*>(trustEngine)->validate(
+ *signature, *issuer, metadataProvider->getKeyResolver()
+ );
+ if (!issuerTrusted) {
+ log.error("unable to verify signature on message with supplied trust engine");
+ throw BindingException("Message signature failed verification.");
+ }
+ }
+ else {
+ log.warn("unable to verify integrity of the message, leaving untrusted");
+ }
+ }
+ else {
+ log.warn("unable to find compatible SAML 2.0 role (%s) in metadata", role->toString().c_str());
+ }
+ if (log.isDebugEnabled()) {
+ auto_ptr_char iname(provider->getEntityID());
+ log.debug("message from (%s), integrity %sverified", iname.get(), issuerTrusted ? "" : "NOT ");
+ }
+ }
+ else {
+ auto_ptr_char temp(claimedIssuer->getName());
+ log.warn("no metadata found, can't establish identity of issuer (%s)", temp.get());
+ }
+ }
+ catch (XMLToolingException& ex) {
+ if (!provider) {
+ if (!claimedIssuer || !claimedIssuer->getName())
+ throw;
+ if (!metadataProvider || !(provider=metadataProvider->getEntityDescriptor(claimedIssuer->getName(), false))) {
+ // Just record it.
+ auto_ptr_char iname(claimedIssuer->getName());
+ if (iname.get())
+ ex.addProperty("entityID", iname.get());
+ throw;
+ }
+ }
+ if (!issuer)
+ issuer=provider->getRoleDescriptor(*role, SAMLConstants::SAML20P_NS);
+ if (issuer) annotateException(&ex,issuer); // throws it
+ annotateException(&ex,provider); // throws it
+ }
+
+ return xmlObject.release();
+}
saml1/core/impl/AudienceRestrictionConditionTest.h \
saml1/core/impl/AudienceTest.h \
saml1/core/impl/AuthenticationStatementTest.h \
+ saml2/binding/impl/SAML2POSTTest.h \
saml2/core/impl/Action20Test.h \
saml2/core/impl/Advice20Test.h \
saml2/core/impl/Artifact20Test.h \
--- /dev/null
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+IssueInstant="1970-01-02T01:01:02.100Z" Version="2.0" ID="rident" Destination="https://sp.example.org/SAML/POST">
+ <saml:Issuer>https://idp.example.org/</saml:Issuer>
+ <samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status>
+ <saml:Assertion ID="aident" IssueInstant="1970-01-02T01:01:02.100Z" Version="2.0">
+ <saml:Issuer>https://idp.example.org/</saml:Issuer>
+ <saml:Subject><saml:NameID>John Doe</saml:NameID></saml:Subject>
+ <saml:AuthnStatement AuthnInstant="1970-01-02T01:01:02.100Z">
+ <saml:AuthnContext>
+ <saml:AuthnContextClassRef>foo</saml:AuthnContextClassRef>
+ </saml:AuthnContext>
+ </saml:AuthnStatement>
+ </saml:Assertion>
+</samlp:Response>
bool trusted=false;\r
QName idprole(SAMLConstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);\r
auto_ptr<MessageDecoder> decoder(SAMLConfig::getConfig().MessageDecoderManager.newPlugin(SAML1_POST_DECODER, NULL));\r
+ Locker locker(m_metadata);\r
auto_ptr<Response> response(\r
dynamic_cast<Response*>(\r
decoder->decode(relayState,issuer,trusted,*this,m_metadata,&idprole,m_trust)\r
bool trusted=false;\r
QName idprole(SAMLConstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);\r
auto_ptr<MessageDecoder> decoder(SAMLConfig::getConfig().MessageDecoderManager.newPlugin(SAML1_POST_DECODER, NULL));\r
+ Locker locker(m_metadata);\r
auto_ptr<Response> response(\r
dynamic_cast<Response*>(\r
decoder->decode(relayState,issuer,trusted,*this,m_metadata,&idprole)\r
--- /dev/null
+/*\r
+ * Copyright 2001-2005 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#include "binding.h"\r
+\r
+#include <saml/saml2/core/Protocols.h>\r
+\r
+using namespace opensaml::saml2p;\r
+using namespace opensaml::saml2;\r
+\r
+class SAML2POSTTest : public CxxTest::TestSuite, public SAMLBindingBaseTestCase {\r
+public:\r
+ void setUp() {\r
+ m_fields.clear();\r
+ SAMLBindingBaseTestCase::setUp();\r
+ }\r
+\r
+ void tearDown() {\r
+ m_fields.clear();\r
+ SAMLBindingBaseTestCase::tearDown();\r
+ }\r
+\r
+ void testSAML2POSTTrusted() {\r
+ try {\r
+ // Read message to use from file.\r
+ string path = data_path + "saml2/binding/SAML2Response.xml";\r
+ ifstream in(path.c_str());\r
+ DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);\r
+ XercesJanitor<DOMDocument> janitor(doc);\r
+ auto_ptr<Response> toSend(\r
+ dynamic_cast<Response*>(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(),true))\r
+ );\r
+ janitor.release();\r
+\r
+ // Freshen timestamp.\r
+ toSend->setIssueInstant(time(NULL));\r
+ \r
+ // Encode message.\r
+ auto_ptr<MessageEncoder> encoder(SAMLConfig::getConfig().MessageEncoderManager.newPlugin(SAML2_POST_ENCODER, NULL));\r
+ encoder->encode(m_fields,toSend.get(),"https://sp.example.org/","state",m_creds);\r
+ toSend.release();\r
+ \r
+ // Decode message.\r
+ string relayState;\r
+ const RoleDescriptor* issuer=NULL;\r
+ bool trusted=false;\r
+ QName idprole(SAMLConstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);\r
+ auto_ptr<MessageDecoder> decoder(SAMLConfig::getConfig().MessageDecoderManager.newPlugin(SAML2_POST_DECODER, NULL));\r
+ Locker locker(m_metadata);\r
+ auto_ptr<Response> response(\r
+ dynamic_cast<Response*>(\r
+ decoder->decode(relayState,issuer,trusted,*this,m_metadata,&idprole,m_trust)\r
+ )\r
+ );\r
+ \r
+ // Test the results.\r
+ TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");\r
+ TSM_ASSERT("SAML Response not decoded successfully.", response.get());\r
+ TSM_ASSERT("Message was not verified.", issuer && trusted);\r
+ auto_ptr_char entityID(dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID());\r
+ TSM_ASSERT("Issuer was not expected.", !strcmp(entityID.get(),"https://idp.example.org/"));\r
+ TSM_ASSERT_EQUALS("Assertion count was not correct.", response->getAssertions().size(), 1);\r
+ }\r
+ catch (XMLToolingException& ex) {\r
+ TS_TRACE(ex.what());\r
+ throw;\r
+ }\r
+ }\r
+\r
+ void testSAML2POSTUntrusted() {\r
+ try {\r
+ // Read message to use from file.\r
+ string path = data_path + "saml2/binding/SAML2Response.xml";\r
+ ifstream in(path.c_str());\r
+ DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);\r
+ XercesJanitor<DOMDocument> janitor(doc);\r
+ auto_ptr<Response> toSend(\r
+ dynamic_cast<Response*>(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(),true))\r
+ );\r
+ janitor.release();\r
+\r
+ // Freshen timestamp and clear ID.\r
+ toSend->setIssueInstant(time(NULL));\r
+ toSend->setID(NULL);\r
+ \r
+ // Encode message.\r
+ auto_ptr<MessageEncoder> encoder(SAMLConfig::getConfig().MessageEncoderManager.newPlugin(SAML2_POST_ENCODER, NULL));\r
+ encoder->encode(m_fields,toSend.get(),"https://sp.example.org/","state");\r
+ toSend.release();\r
+ \r
+ // Decode message.\r
+ string relayState;\r
+ const RoleDescriptor* issuer=NULL;\r
+ bool trusted=false;\r
+ QName idprole(SAMLConstants::SAML20MD_NS, IDPSSODescriptor::LOCAL_NAME);\r
+ auto_ptr<MessageDecoder> decoder(SAMLConfig::getConfig().MessageDecoderManager.newPlugin(SAML2_POST_DECODER, NULL));\r
+ Locker locker(m_metadata);\r
+ auto_ptr<Response> response(\r
+ dynamic_cast<Response*>(\r
+ decoder->decode(relayState,issuer,trusted,*this,m_metadata,&idprole)\r
+ )\r
+ );\r
+ \r
+ // Test the results.\r
+ TSM_ASSERT_EQUALS("RelayState was not the expected result.", relayState, "state");\r
+ TSM_ASSERT("SAML Response not decoded successfully.", response.get());\r
+ TSM_ASSERT("Message was verified.", issuer && !trusted);\r
+ auto_ptr_char entityID(dynamic_cast<const EntityDescriptor*>(issuer->getParent())->getEntityID());\r
+ TSM_ASSERT("Issuer was not expected.", !strcmp(entityID.get(),"https://idp.example.org/"));\r
+ TSM_ASSERT_EQUALS("Assertion count was not correct.", response->getAssertions().size(), 1);\r
+\r
+ // Trigger a replay.\r
+ TSM_ASSERT_THROWS("Did not catch the replay.", \r
+ decoder->decode(relayState,issuer,trusted,*this,m_metadata,&idprole,m_trust),\r
+ BindingException);\r
+ }\r
+ catch (XMLToolingException& ex) {\r
+ TS_TRACE(ex.what());\r
+ throw;\r
+ }\r
+ }\r
+\r
+ const char* getMethod() const {\r
+ return "POST";\r
+ } \r
+\r
+ const char* getRequestURL() const {\r
+ return "https://sp.example.org/SAML/POST";\r
+ }\r
+ \r
+ const char* getQueryString() const {\r
+ return NULL;\r
+ }\r
+};\r
<Filter\r
Name="binding"\r
>\r
+ <File\r
+ RelativePath=".\saml2\binding\SAML2POSTTest.cpp"\r
+ >\r
+ </File>\r
</Filter>\r
</Filter>\r
<Filter\r
<Filter\r
Name="binding"\r
>\r
+ <File\r
+ RelativePath=".\saml2\binding\SAML2POSTTest.h"\r
+ >\r
+ <FileConfiguration\r
+ Name="Debug|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o "$(InputDir)$(InputName)".cpp "$(InputPath)""\r
+ Outputs=""$(InputDir)$(InputName)".cpp"\r
+ />\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Release|Win32"\r
+ >\r
+ <Tool\r
+ Name="VCCustomBuildTool"\r
+ CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o "$(InputDir)$(InputName)".cpp "$(InputPath)""\r
+ Outputs=""$(InputDir)$(InputName)".cpp"\r
+ />\r
+ </FileConfiguration>\r
+ </File>\r
</Filter>\r
</Filter>\r
<Filter\r