Unit test for 1.x POST binding, plus fixes.
authorScott Cantor <cantor.2@osu.edu>
Thu, 5 Oct 2006 19:30:39 +0000 (19:30 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 5 Oct 2006 19:30:39 +0000 (19:30 +0000)
.cdtproject
saml/saml1/binding/impl/SAML1POSTDecoder.cpp
samltest/Makefile.am
samltest/binding.h [new file with mode: 0644]
samltest/data/binding/ExampleMetadataProvider.xml [new file with mode: 0644]
samltest/data/binding/example-metadata.xml [new file with mode: 0644]
samltest/data/saml1/binding/SAML1Response.xml [new file with mode: 0644]
samltest/internal.h
samltest/saml1/binding/SAML1POSTTest.h [new file with mode: 0644]
samltest/samltest.h
samltest/samltest.vcproj

index 7827afa..0361b54 100644 (file)
 <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/" kind="src" path="samltest"/>\r
+<pathentry excluding="saml1/core/impl/|signature/|saml2/core/impl/|saml2/metadata/|security/|saml1/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
 </item>\r
 </data>\r
 </cdtproject>\r
index b99ab68..181d6f9 100644 (file)
@@ -105,54 +105,84 @@ Response* SAML1POSTDecoder::decode(
         // Check recipient URL.
         auto_ptr_char recipient(response->getRecipient());
         const char* recipient2 = httpRequest.getRequestURL();
-        if (!recipient2 || !*recipient2 || strcmp(recipient.get(),recipient2)) {
+        if (!recipient.get() || !*(recipient.get())) {
+            log.error("response missing Recipient attribute");
+            throw BindingException("SAML response did not contain Recipient attribute identifying intended destination.");
+        }
+        else if (!recipient2 || !*recipient2 || strcmp(recipient.get(),recipient2)) {
             log.error("POST targeted at (%s), but delivered to (%s)", recipient.get(), recipient2 ? recipient2 : "none");
             throw BindingException("SAML message delivered with POST to incorrect server URL.");
         }
         
+        // Check freshness.
         time_t now = time(NULL);
         if (response->getIssueInstant()->getEpoch() < now-(2*XMLToolingConfig::getConfig().clock_skew_secs))
             throw BindingException("Detected expired POST profile response.");
-            
+        
+        // Check replay.
         ReplayCache* replayCache = SAMLConfig::getConfig().getReplayCache();
         if (replayCache) {
             auto_ptr_char id(response->getResponseID());
-            if (!replayCache->check("SAML1POST", id.get(), response->getIssueInstant()->getEpoch() + (2*XMLToolingConfig::getConfig().clock_skew_secs)))
+            if (!replayCache->check("SAML1POST", 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!");
         
+        /* For SAML 1, the issuer can only be established from any assertions in the message.
+         * Generally, errors aren't delivered like this, so there should be one.
+         * The Issuer attribute is matched against metadata, and then trust checking can be
+         * applied.
+         */
         issuer = NULL;
         issuerTrusted = false;
         log.debug("attempting to establish issuer and integrity of message...");
         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;
             if (provider) {
+                log.debug("matched assertion issuer against metadata, searching for applicable role...");
                 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 && trustEngine && response->getSignature()) {
-                    issuerTrusted = static_cast<const TrustEngine*>(trustEngine)->validate(
-                        *(response->getSignature()), *issuer, metadataProvider->getKeyResolver()
+                if (issuer) {
+                    if (trustEngine && response->getSignature()) {
+                        issuerTrusted = static_cast<const TrustEngine*>(trustEngine)->validate(
+                            *(response->getSignature()), *issuer, metadataProvider->getKeyResolver()
+                            );
+                        if (!issuerTrusted)
+                            log.error("unable to verify signature on message with supplied trust engine");
+                    }
+                    else {
+                        log.warn("unable to verify integrity of the message, leaving untrusted");
+                    }
+                }
+                else {
+                    log.warn(
+                        "unable to find compatible SAML 1.%d role (%s) in metadata",
+                        (minor.first && minor.second==0) ? 0 : 1,
+                        role->toString().c_str()
                         );
-                    if (!issuerTrusted)
-                        log.error("signature on message could not be verified by supplied trust engine");
                 }
                 if (log.isDebugEnabled()) {
                     auto_ptr_char iname(assertions.front()->getIssuer());
                     log.debug("message from (%s), integrity %sverified", iname.get(), issuerTrusted ? "" : "NOT ");
                 }
             }
-            else
-                log.warn("no metadata provider supplied, can't establish identity of issuer");
+            else {
+                auto_ptr_char temp(assertions.front()->getIssuer());
+                log.warn("no metadata found, can't establish identity of issuer (%s)", temp.get());
+            }
         }
-        else
+        else {
             log.warn("no assertions found, can't establish identity of issuer");
+        }
     }
     catch (XMLToolingException& ex) {
         // Check for an Issuer.
@@ -169,10 +199,11 @@ Response* SAML1POSTDecoder::decode(
             const EntityDescriptor* provider=metadataProvider->getEntityDescriptor(assertions.front()->getIssuer(),false);
             if (provider) {
                 pair<bool,int> minor = response->getMinorVersion();
-                const IDPSSODescriptor* role=provider->getIDPSSODescriptor(
+                issuer=provider->getRoleDescriptor(
+                    *role,
                     (minor.first && minor.second==0) ? SAMLConstants::SAML10_PROTOCOL_ENUM : SAMLConstants::SAML11_PROTOCOL_ENUM
                     );
-                if (role) annotateException(&ex,role); // throws it
+                if (issuer) annotateException(&ex,issuer); // throws it
                 annotateException(&ex,provider);  // throws it
             }
         }
index 50e65a2..f4f7be6 100644 (file)
@@ -20,6 +20,7 @@ samltest_h = \
     signature/SAML2AssertionTest.h \
     security/AbstractPKIXTrustEngineTest.h \
     security/ExplicitKeyTrustEngineTest.h \
+    saml1/binding/impl/SAML1POSTTest.h \
     saml1/core/impl/ActionTest.h \
     saml1/core/impl/AdviceTest.h \
     saml1/core/impl/AssertionIDReferenceTest.h \
@@ -90,6 +91,7 @@ samltest_h = \
     saml2/metadata/FilesystemMetadataProviderTest.h
 
 noinst_HEADERS = \
+       binding.h \
     internal.h \
     signature/SAMLSignatureTestBase.h
 
diff --git a/samltest/binding.h b/samltest/binding.h
new file mode 100644 (file)
index 0000000..474e882
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ *  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.
+ */
+
+#include "internal.h"
+
+#include <saml/SAMLConfig.h>
+#include <saml/binding/MessageDecoder.h>
+#include <saml/binding/MessageEncoder.h>
+#include <saml/saml2/metadata/MetadataProvider.h>
+#include <saml/security/X509TrustEngine.h>
+
+using namespace saml2md;
+using namespace xmlsignature;
+
+class SAMLBindingBaseTestCase : public MessageDecoder::HTTPRequest
+{
+protected:
+    CredentialResolver* m_creds; 
+    MetadataProvider* m_metadata;
+    opensaml::X509TrustEngine* m_trust;
+    map<string,string> m_fields;
+
+public:
+    void setUp() {
+        m_creds=NULL;
+        m_metadata=NULL;
+        m_trust=NULL;
+        m_fields.clear();
+
+        try {
+            string config = data_path + "binding/ExampleMetadataProvider.xml";
+            ifstream in(config.c_str());
+            DOMDocument* doc=XMLToolingConfig::getConfig().getParser().parse(in);
+            XercesJanitor<DOMDocument> janitor(doc);
+    
+            auto_ptr_XMLCh path("path");
+            string s = data_path + "binding/example-metadata.xml";
+            auto_ptr_XMLCh file(s.c_str());
+            doc->getDocumentElement()->setAttributeNS(NULL,path.get(),file.get());
+    
+            m_metadata = SAMLConfig::getConfig().MetadataProviderManager.newPlugin(
+                FILESYSTEM_METADATA_PROVIDER,doc->getDocumentElement()
+                );
+            m_metadata->init();
+
+            config = data_path + "FilesystemCredentialResolver.xml";
+            ifstream in2(config.c_str());
+            DOMDocument* doc2=XMLToolingConfig::getConfig().getParser().parse(in2);
+            XercesJanitor<DOMDocument> janitor2(doc2);
+            m_creds = XMLToolingConfig::getConfig().CredentialResolverManager.newPlugin(
+                FILESYSTEM_CREDENTIAL_RESOLVER,doc2->getDocumentElement()
+                );
+                
+            m_trust = dynamic_cast<X509TrustEngine*>(
+                SAMLConfig::getConfig().TrustEngineManager.newPlugin(EXPLICIT_KEY_SAMLTRUSTENGINE, NULL)
+                );
+        }
+        catch (XMLToolingException& ex) {
+            TS_TRACE(ex.what());
+            tearDown();
+            throw;
+        }
+
+    }
+    
+    void tearDown() {
+        delete m_creds;
+        delete m_metadata;
+        delete m_trust;
+        m_creds=NULL;
+        m_metadata=NULL;
+        m_trust=NULL;
+        m_fields.clear();
+    }
+
+    const char* getParameter(const char* name) const {
+        map<string,string>::const_iterator i=m_fields.find(name);
+        return i==m_fields.end() ? NULL : i->second.c_str();
+    }
+
+    vector<const char*>::size_type getParameters(const char* name, vector<const char*>& values) const {
+        values.clear();
+        map<string,string>::const_iterator i=m_fields.find(name);
+        if (i!=m_fields.end())
+            values.push_back(i->second.c_str());
+        return values.size();
+    }
+};
diff --git a/samltest/data/binding/ExampleMetadataProvider.xml b/samltest/data/binding/ExampleMetadataProvider.xml
new file mode 100644 (file)
index 0000000..c296c1b
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<FilesystemMetadataProvider path="../samltest/data/binding/example-metadata.xml" validate="0"/>
diff --git a/samltest/data/binding/example-metadata.xml b/samltest/data/binding/example-metadata.xml
new file mode 100644 (file)
index 0000000..c67283b
--- /dev/null
@@ -0,0 +1,92 @@
+<EntitiesDescriptor\r
+    xmlns="urn:oasis:names:tc:SAML:2.0:metadata"\r
+    xmlns:ds="http://www.w3.org/2000/09/xmldsig#"\r
+    validUntil="2010-01-01T00:00:00Z">\r
+\r
+       <EntityDescriptor entityID="https://idp.example.org/">\r
+               \r
+               <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">\r
+                       <KeyDescriptor use="signing">\r
+                           <ds:KeyInfo>\r
+                               <ds:X509Data>\r
+                                       <ds:X509Certificate>\r
+                                               MIICjzCCAfigAwIBAgIJAKk8t1hYcMkhMA0GCSqGSIb3DQEBBAUAMDoxCzAJBgNV
+                                               BAYTAlVTMRIwEAYDVQQKEwlJbnRlcm5ldDIxFzAVBgNVBAMTDnNwLmV4YW1wbGUu
+                                               b3JnMB4XDTA1MDYyMDE1NDgzNFoXDTMyMTEwNTE1NDgzNFowOjELMAkGA1UEBhMC
+                                               VVMxEjAQBgNVBAoTCUludGVybmV0MjEXMBUGA1UEAxMOc3AuZXhhbXBsZS5vcmcw
+                                               gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANlZ1L1mKzYbUVKiMQLhZlfGDyYa
+                                               /jjCiaXP0WhLNgvJpOTeajvsrApYNnFX5MLNzuC3NeQIjXUNLN2Yo2MCSthBIOL5
+                                               qE5dka4z9W9zytoflW1LmJ8vXpx8Ay/meG4z//J5iCpYVEquA0xl28HUIlownZUF
+                                               7w7bx0cF/02qrR23AgMBAAGjgZwwgZkwHQYDVR0OBBYEFJZiO1qsyAyc3HwMlL9p
+                                               JpN6fbGwMGoGA1UdIwRjMGGAFJZiO1qsyAyc3HwMlL9pJpN6fbGwoT6kPDA6MQsw
+                                               CQYDVQQGEwJVUzESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5zcC5leGFt
+                                               cGxlLm9yZ4IJAKk8t1hYcMkhMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQAD
+                                               gYEAMFq/UeSQyngE0GpZueyD2UW0M358uhseYOgGEIfm+qXIFQF6MYwNoX7WFzhC
+                                               LJZ2E6mEvZZFHCHUtl7mGDvsRwgZ85YCtRbvleEpqfgNQToto9pLYe+X6vvH9Z6p
+                                               gmYsTmak+kxO93JprrOd9xp8aZPMEprL7VCdrhbZEfyYER0=
+                                       </ds:X509Certificate>\r
+                               </ds:X509Data>\r
+                           </ds:KeyInfo>\r
+                       </KeyDescriptor>\r
+\r
+                       <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:protocol"\r
+                           Location="https://idp.example.org/SSO"/>\r
+               </IDPSSODescriptor>\r
+               \r
+               <Organization>\r
+                   <OrganizationName xml:lang="en">Example Identity Provider</OrganizationName>\r
+                   <OrganizationDisplayName xml:lang="en">Identities 'R' Us</OrganizationDisplayName>\r
+                   <OrganizationURL xml:lang="en">http://idp.example.org/</OrganizationURL>\r
+               </Organization>\r
+               <ContactPerson contactType="technical">\r
+                   <SurName>Technical Support</SurName>\r
+                   <EmailAddress>support@idp.example.org</EmailAddress>\r
+               </ContactPerson>\r
+\r
+       </EntityDescriptor>\r
+\r
+       <EntityDescriptor entityID="https://sp.example.org/">\r
+       \r
+               <SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:1.1:protocol urn:oasis:names:tc:SAML:2.0:protocol">\r
+                       <KeyDescriptor use="signing">\r
+                           <ds:KeyInfo>\r
+                               <ds:X509Data>\r
+                                       <ds:X509Certificate>\r
+                                               MIICjzCCAfigAwIBAgIJAKk8t1hYcMkhMA0GCSqGSIb3DQEBBAUAMDoxCzAJBgNV
+                                               BAYTAlVTMRIwEAYDVQQKEwlJbnRlcm5ldDIxFzAVBgNVBAMTDnNwLmV4YW1wbGUu
+                                               b3JnMB4XDTA1MDYyMDE1NDgzNFoXDTMyMTEwNTE1NDgzNFowOjELMAkGA1UEBhMC
+                                               VVMxEjAQBgNVBAoTCUludGVybmV0MjEXMBUGA1UEAxMOc3AuZXhhbXBsZS5vcmcw
+                                               gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANlZ1L1mKzYbUVKiMQLhZlfGDyYa
+                                               /jjCiaXP0WhLNgvJpOTeajvsrApYNnFX5MLNzuC3NeQIjXUNLN2Yo2MCSthBIOL5
+                                               qE5dka4z9W9zytoflW1LmJ8vXpx8Ay/meG4z//J5iCpYVEquA0xl28HUIlownZUF
+                                               7w7bx0cF/02qrR23AgMBAAGjgZwwgZkwHQYDVR0OBBYEFJZiO1qsyAyc3HwMlL9p
+                                               JpN6fbGwMGoGA1UdIwRjMGGAFJZiO1qsyAyc3HwMlL9pJpN6fbGwoT6kPDA6MQsw
+                                               CQYDVQQGEwJVUzESMBAGA1UEChMJSW50ZXJuZXQyMRcwFQYDVQQDEw5zcC5leGFt
+                                               cGxlLm9yZ4IJAKk8t1hYcMkhMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQAD
+                                               gYEAMFq/UeSQyngE0GpZueyD2UW0M358uhseYOgGEIfm+qXIFQF6MYwNoX7WFzhC
+                                               LJZ2E6mEvZZFHCHUtl7mGDvsRwgZ85YCtRbvleEpqfgNQToto9pLYe+X6vvH9Z6p
+                                               gmYsTmak+kxO93JprrOd9xp8aZPMEprL7VCdrhbZEfyYER0=
+                                       </ds:X509Certificate>\r
+                               </ds:X509Data>\r
+                           </ds:KeyInfo>\r
+                       </KeyDescriptor>\r
+                       \r
+                       <AssertionConsumerService index="1" isDefault="true"\r
+                               Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"\r
+                               Location="https://sp.example.org/SAML/POST"/>\r
+\r
+               </SPSSODescriptor>\r
+\r
+               <Organization>\r
+                       <OrganizationName xml:lang="en">Example Service Provider</OrganizationName>\r
+                       <OrganizationDisplayName xml:lang="en">Services 'R' Us</OrganizationDisplayName>\r
+                       <OrganizationURL xml:lang="en">http://sp.example.org/</OrganizationURL>\r
+               </Organization>\r
+               <ContactPerson contactType="technical">\r
+                       <SurName>Technical Support</SurName>\r
+                       <EmailAddress>support@sp.example.org</EmailAddress>\r
+               </ContactPerson>\r
+               \r
+       </EntityDescriptor>\r
+\r
+</EntitiesDescriptor>\r
diff --git a/samltest/data/saml1/binding/SAML1Response.xml b/samltest/data/saml1/binding/SAML1Response.xml
new file mode 100644 (file)
index 0000000..5671c67
--- /dev/null
@@ -0,0 +1,10 @@
+<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" IssueInstant="1970-01-02T01:01:02.100Z"
+MajorVersion="1" MinorVersion="1" ResponseID="rident" Recipient="https://sp.example.org/SAML/POST">
+    <samlp:Status><samlp:StatusCode Value="samlp:Success"/></samlp:Status>
+    <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" AssertionID="aident"
+    IssueInstant="1970-01-02T01:01:02.100Z" Issuer="https://idp.example.org/" MajorVersion="1" MinorVersion="1">
+        <saml:AuthenticationStatement AuthenticationInstant="1970-01-02T01:01:02.100Z" AuthenticationMethod="method">
+            <saml:Subject><saml:NameIdentifier>John Doe</saml:NameIdentifier></saml:Subject>
+        </saml:AuthenticationStatement>
+    </saml:Assertion>
+</samlp:Response>
index 15b4afb..1cfe28f 100644 (file)
 #include <fstream>
 #include <saml/exceptions.h>
 #include <saml/util/SAMLConstants.h>
-#include <xmltooling/exceptions.h>
-#include <xmltooling/validation/Validator.h>
 #include <xmltooling/XMLObject.h>
 #include <xmltooling/XMLObjectBuilder.h>
 #include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/validation/Validator.h>
 
 using namespace opensaml;
 using namespace xmltooling;
diff --git a/samltest/saml1/binding/SAML1POSTTest.h b/samltest/saml1/binding/SAML1POSTTest.h
new file mode 100644 (file)
index 0000000..2d6da7a
--- /dev/null
@@ -0,0 +1,145 @@
+/*\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/saml1/core/Protocols.h>\r
+\r
+using namespace opensaml::saml1p;\r
+using namespace opensaml::saml1;\r
+\r
+class SAML1POSTTest : 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 testSAML1POSTTrusted() {\r
+        try {\r
+            // Read message to use from file.\r
+            string path = data_path + "saml1/binding/SAML1Response.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(SAML1_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(SAML1_POST_DECODER, NULL));\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("TARGET 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 testSAML1POSTUntrusted() {\r
+        try {\r
+            // Read message to use from file.\r
+            string path = data_path + "saml1/binding/SAML1Response.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->setResponseID(NULL);\r
+    \r
+            // Encode message.\r
+            auto_ptr<MessageEncoder> encoder(SAMLConfig::getConfig().MessageEncoderManager.newPlugin(SAML1_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(SAML1_POST_DECODER, NULL));\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("TARGET 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
index 92509d8..938f1e8 100644 (file)
@@ -15,7 +15,8 @@
  */\r
 \r
 #include "internal.h"\r
-#include <saml/SAMLConfig.h>
+#include <saml/SAMLConfig.h>\r
+#include <saml/binding/ReplayCache.h>\r
 \r
 #include <fstream>\r
 #include <cxxtest/GlobalFixture.h>\r
@@ -31,6 +32,8 @@ public:
         XMLToolingConfig::getConfig().log_config();\r
         if (!SAMLConfig::getConfig().init())\r
             return false;\r
+        SAMLConfig::getConfig().setReplayCache(new ReplayCache());\r
+\r
         if (getenv("SAMLTEST_DATA"))\r
             data_path=std::string(getenv("SAMLTEST_DATA")) + "/";\r
         //std::string catpath=data_path + "catalog.xml";\r
index eadeff8..98d301e 100644 (file)
                                                </File>\r
                                        </Filter>\r
                                </Filter>\r
+                               <Filter\r
+                                       Name="binding"\r
+                                       >\r
+                                       <File\r
+                                               RelativePath=".\saml1\binding\SAML1POSTTest.cpp"\r
+                                               >\r
+                                       </File>\r
+                               </Filter>\r
                        </Filter>\r
                        <Filter\r
                                Name="signature"\r
                                                >\r
                                        </File>\r
                                </Filter>\r
+                               <Filter\r
+                                       Name="binding"\r
+                                       >\r
+                               </Filter>\r
                        </Filter>\r
                        <Filter\r
                                Name="security"\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 &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputDir)$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\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 &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputDir)$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                        </File>\r
                        <File\r
+                               RelativePath=".\binding.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\internal.h"\r
                                >\r
                        </File>\r
                                                </File>\r
                                        </Filter>\r
                                </Filter>\r
+                               <Filter\r
+                                       Name="bnding"\r
+                                       >\r
+                                       <File\r
+                                               RelativePath=".\saml1\binding\SAML1POSTTest.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 &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                                               Outputs="&quot;$(InputDir)$(InputName)&quot;.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 &quot;$(InputDir)$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                                               Outputs="&quot;$(InputDir)$(InputName)&quot;.cpp"\r
+                                                       />\r
+                                               </FileConfiguration>\r
+                                       </File>\r
+                               </Filter>\r
                        </Filter>\r
                        <Filter\r
                                Name="signature"\r
                                                </FileConfiguration>\r
                                        </File>\r
                                </Filter>\r
+                               <Filter\r
+                                       Name="binding"\r
+                                       >\r
+                               </Filter>\r
                        </Filter>\r
                        <Filter\r
                                Name="security"\r