diff --git a/README-DEV.md b/README-DEV.md index 42249a911..9c9acd7ca 100644 --- a/README-DEV.md +++ b/README-DEV.md @@ -11,8 +11,8 @@ Test with the PHPCS configuration in tools/phpcs/ruleset.xml Testing ------- Use PHPUnit for Unit Testing. -Test with the 2 known users: (SimpleSAMLphp)[http://www.simplesaml.org] and -(OpenConext-engineblock)[http://www.openconext.org] . +Test with the 2 known users: (SimpleSAMLphp)[https://www.simplesamlphp.org] and +(OpenConext-engineblock)[https://www.openconext.org] . ### Using Tests in Development diff --git a/src/SAML2/SOAPClient.php b/src/SAML2/SOAPClient.php index a08753684..31622d099 100644 --- a/src/SAML2/SOAPClient.php +++ b/src/SAML2/SOAPClient.php @@ -34,6 +34,7 @@ public function send(Message $msg, SimpleSAML_Configuration $srcMetadata, Simple $ctxOpts = array( 'ssl' => array( 'capture_peer_cert' => true, + 'allow_self_signed' => true ), ); diff --git a/src/SAML2/Utilities/ArrayCollection.php b/src/SAML2/Utilities/ArrayCollection.php index d1ce1fabd..ea7223989 100644 --- a/src/SAML2/Utilities/ArrayCollection.php +++ b/src/SAML2/Utilities/ArrayCollection.php @@ -38,7 +38,7 @@ public function filter(\Closure $f) public function set($key, $value) { - $this->elements[$value] = $key; + $this->elements[$key] = $value; } public function remove($element) diff --git a/tests/SAML2/Certificate/FingerprintCollectionTest.php b/tests/SAML2/Certificate/FingerprintCollectionTest.php new file mode 100644 index 000000000..57a21e5a9 --- /dev/null +++ b/tests/SAML2/Certificate/FingerprintCollectionTest.php @@ -0,0 +1,49 @@ +<?php + +namespace SAML2\Certificate; + +class FingerprintCollectionTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @group certificate + * @test + */ + public function add_fingerprint() + { + $fpr0 = new Fingerprint('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'); + $fpr1 = new Fingerprint('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01'); + + $fpc = new FingerprintCollection(); + // collection has none of the fingerprints + $this->assertFalse($fpc->contains($fpr0)); + $this->assertFalse($fpc->contains($fpr1)); + + $fpc->add($fpr0); + // fingerprint 0 is present, 1 remains absent + $this->assertTrue($fpc->contains($fpr0)); + $this->assertFalse($fpc->contains($fpr1)); + + $fpc->add($fpr0); + // adding an existing fingerprint is idempotent + $this->assertTrue($fpc->contains($fpr0)); + $this->assertFalse($fpc->contains($fpr1)); + + $fpc->add($fpr1); + // both fingerprints are now present + $this->assertTrue($fpc->contains($fpr0)); + $this->assertTrue($fpc->contains($fpr1)); + } + + /** + * @group certificate + * @test + * + * @expectedException \SAML2\Exception\InvalidArgumentException + */ + public function fails_on_invalid_fingerprint_data() + { + $fpc = new FingerprintCollection(); + $fpc->add('string'); + } +} diff --git a/tests/SAML2/Certificate/KeyCollectionTest.php b/tests/SAML2/Certificate/KeyCollectionTest.php new file mode 100644 index 000000000..ec3df278c --- /dev/null +++ b/tests/SAML2/Certificate/KeyCollectionTest.php @@ -0,0 +1,18 @@ +<?php + +namespace SAML2\Certificate; + +class KeyCollectionTest extends \PHPUnit_Framework_TestCase +{ + /** + * @group certificate + * + * @test + * @expectedException \SAML2\Exception\InvalidArgumentException + */ + public function testKeyCollectionAddWrongType() + { + $kc = new KeyCollection(); + $kc->add("not a key, just a string"); + } +} diff --git a/tests/SAML2/Certificate/KeyTest.php b/tests/SAML2/Certificate/KeyTest.php index 9f8afc76b..27023968c 100644 --- a/tests/SAML2/Certificate/KeyTest.php +++ b/tests/SAML2/Certificate/KeyTest.php @@ -17,6 +17,19 @@ public function invalid_key_usage_should_throw_an_exception() $key->canBeUsedFor('foo'); } + /** + * @group certificate + * + * @test + * @expectedException \SAML2\Exception\InvalidArgumentException + */ + public function invalid_offset_type_should_throw_an_exception() + { + $key = new Key(array(Key::USAGE_SIGNING => true)); + $key->offsetGet(0); + } + + /** * @group certificate * @@ -32,4 +45,33 @@ public function assert_that_key_usage_check_works_correctly() $key[Key::USAGE_ENCRYPTION] = false; $this->assertFalse($key->canBeUsedFor(Key::USAGE_ENCRYPTION)); } + + /** + * @group certificate + * + * @test + */ + public function assert_that_offsetget_works_correctly() + { + $key = new Key(array(Key::USAGE_SIGNING => true)); + $this->assertTrue($key->offsetGet(Key::USAGE_SIGNING)); + } + + /** + * @group certificate + * + * @test + */ + public function assert_that_offsetunset_unsets_offset() + { + $key = new Key(array(Key::USAGE_SIGNING => true, Key::USAGE_ENCRYPTION => true)); + $this->assertTrue($key->offsetExists(Key::USAGE_SIGNING)); + $this->assertTrue($key->offsetExists(Key::USAGE_ENCRYPTION)); + $key->offsetUnset(Key::USAGE_SIGNING); + $this->assertFalse($key->offsetExists(Key::USAGE_SIGNING)); + $this->assertTrue($key->offsetExists(Key::USAGE_ENCRYPTION)); + $key->offsetUnset(Key::USAGE_ENCRYPTION); + $this->assertFalse($key->offsetExists(Key::USAGE_SIGNING)); + $this->assertFalse($key->offsetExists(Key::USAGE_ENCRYPTION)); + } } diff --git a/tests/SAML2/Certificate/PrivateKeyTest.php b/tests/SAML2/Certificate/PrivateKeyTest.php new file mode 100644 index 000000000..0c859ee25 --- /dev/null +++ b/tests/SAML2/Certificate/PrivateKeyTest.php @@ -0,0 +1,46 @@ +<?php + +namespace SAML2\Certificate; + +class PrivateKeyTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @group certificate + * @test + */ + public function test_create_from_key() + { + $key = \SAML2\CertificatesMock::getPlainPrivateKey(); + + $pk_nopass = PrivateKey::create($key); + $this->assertEquals($key, $pk_nopass->getKeyAsString()); + + $pk_withpass = PrivateKey::create($key, "s3cr1t"); + $this->assertEquals($key, $pk_withpass->getKeyAsString()); + $this->assertEquals("s3cr1t", $pk_withpass->getPassphrase()); + } + + /** + * @group certificate + * + * @test + * @expectedException \SAML2\Exception\InvalidArgumentException + */ + public function test_create_from_nonstring_throws_exception() + { + PrivateKey::create(0); + } + + /** + * @group certificate + * + * @test + * @expectedException \SAML2\Exception\InvalidArgumentException + */ + public function test_create_with_nonstring_password_throws_exception() + { + $key = \SAML2\CertificatesMock::getPlainPrivateKey(); + PrivateKey::create($key, 1); + } +} diff --git a/tests/SAML2/CertificatesMock.php b/tests/SAML2/CertificatesMock.php index 0e5730004..276568037 100644 --- a/tests/SAML2/CertificatesMock.php +++ b/tests/SAML2/CertificatesMock.php @@ -26,6 +26,8 @@ class CertificatesMock Q4/67OZfHd7R+POBXhophSMv1ZOo -----END CERTIFICATE-----'; + const PUBLIC_KEY_PEM_CONTENTS = 'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo'; + const PRIVATE_KEY_PEM = '-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9 IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+ @@ -42,6 +44,33 @@ class CertificatesMock PQnYaNUN/Fy2SYtETXTb0CQ9X1rt8ffkFP7ya+5TC83aMg== -----END RSA PRIVATE KEY-----'; + const PUBLIC_KEY_2_PEM = '-----BEGIN CERTIFICATE----- +MIIEdjCCA16gAwIBAgIRALcDQnHscLnQAL4GULGaHsIwDQYJKoZIhvcNAQEFBQAw +NjELMAkGA1UEBhMCTkwxDzANBgNVBAoTBlRFUkVOQTEWMBQGA1UEAxMNVEVSRU5B +IFNTTCBDQTAeFw0wOTEwMjYwMDAwMDBaFw0xMjEwMjUyMzU5NTlaMFExCzAJBgNV +BAYTAk5MMRUwEwYDVQQKEwxTVVJGbmV0IEIuVi4xETAPBgNVBAsTCFNlcnZpY2Vz +MRgwFgYDVQQDEw9iZXRhLnN1cmZuZXQubmwwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC7D6V4dl41AF70veMMQqL7kpk+p06qU9EW2YUuJAnwhRO1Sfbi +qPweitLE7ReXfK/vSPvdFG194gZjJ4JS7M7pP00eolfEK6ljLhMoUrzbBuEc5LoQ +B6HsOogod7YrJiVRXLzM4bV2LzbyjAwBgForoM4l576CjbN/NDAPO3YvbktodR5D +H/nPiw/5L7w2KS3x55xUd8clY7Nji9W7XbviMUAugSkkC2ethl0AMpdmk6BI5pjy +r6KCUrGz9bKl101yUGhrkHy1NpHagOoxakkgVDgi2Erf8aDnkCe8A419m2mBD1WA +9uQWF5eMpiY8fDMwjwSFDFoSUlJPhiKN8vpnAgMBAAGjggFiMIIBXjAfBgNVHSME +GDAWgBQMvZNoDPPeq6NJays3V0fqkOO57TAdBgNVHQ4EFgQUrCpeMIzTW5M13G4i +b1B+yq7Fm60wDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYw +FAYIKwYBBQUHAwEGCCsGAQUFBwMCMBgGA1UdIAQRMA8wDQYLKwYBBAGyMQECAh0w +OgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC50Y3MudGVyZW5hLm9yZy9URVJF +TkFTU0xDQS5jcmwwbQYIKwYBBQUHAQEEYTBfMDUGCCsGAQUFBzAChilodHRwOi8v +Y3J0LnRjcy50ZXJlbmEub3JnL1RFUkVOQVNTTENBLmNydDAmBggrBgEFBQcwAYYa +aHR0cDovL29jc3AudGNzLnRlcmVuYS5vcmcwGgYDVR0RBBMwEYIPYmV0YS5zdXJm +bmV0Lm5sMA0GCSqGSIb3DQEBBQUAA4IBAQC8iCICDbP4/8JTDeLPfo/n6roOvpMs +teQt7X5oN2Ka1xgKflpBGqJO5o3PcnfP437kcLRTnp6XDyTfS4eyZdxCqCECR2Pb +8nbULVFv9hyF6asIWUfbJ67CFcRIpcuaD5habSrg8+rT86DjKdtYQKwbKL+rNbOs +g6/ROR7vJgbSqrBLraXvl8HDUq5+lSF/II4LHVzNM8TpQlMY4ynRP6GEjcNUTH3I +FKPQk+NwBYQqJ83Uil/36kbXsHQ81o/Vp6it7tlvLBOP1EN9jNGUXZuAqvFphNkw +EJpABx1x4ukY8bZVl6QzQ79P48oGxOaIy27/g1FVkGqRtA4UPABcn0sJ +-----END CERTIFICATE-----'; + /** * @return XMLSecurityKey */ @@ -61,4 +90,50 @@ public static function getPrivateKey() $privateKey->loadKey(self::PRIVATE_KEY_PEM); return $privateKey; } + + /** + * @return XMLSecurityKey + */ + public static function getPublicKeySha1() + { + $publicKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public')); + $publicKey->loadKey(self::PUBLIC_KEY_PEM); + return $publicKey; + } + + /** + * @return XMLSecurityKey + */ + public static function getPublicKey2Sha1() + { + $publicKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public')); + $publicKey->loadKey(self::PUBLIC_KEY_2_PEM); + return $publicKey; + } + + public static function getPlainPublicKey() + { + return self::PUBLIC_KEY_PEM; + } + + public static function getPlainPrivateKey() + { + return self::PRIVATE_KEY_PEM; + } + + /** + * Returns just the certificate contents without the begin and end markings + */ + public static function getPlainPublicKeyContents() + { + return self::PUBLIC_KEY_PEM_CONTENTS; + } + + /** + * Returns malformed public key by truncating it. + */ + public static function getPlainInvalidPublicKey() + { + return substr(self::PUBLIC_KEY_PEM,200); + } } diff --git a/tests/SAML2/DOMDocumentFactoryTest.php b/tests/SAML2/DOMDocumentFactoryTest.php index b97a6dba6..794375249 100644 --- a/tests/SAML2/DOMDocumentFactoryTest.php +++ b/tests/SAML2/DOMDocumentFactoryTest.php @@ -46,7 +46,7 @@ public function testXmlStringIsCorrectlyLoaded() */ public function testOnlyAStringIsAcceptedByFromFile($argument) { - DOMDocumentFactory::fromString($argument); + DOMDocumentFactory::fromFile($argument); } /** diff --git a/tests/SAML2/HTTPPostTest.php b/tests/SAML2/HTTPPostTest.php new file mode 100644 index 000000000..1c91ec57f --- /dev/null +++ b/tests/SAML2/HTTPPostTest.php @@ -0,0 +1,84 @@ +<?php + +namespace SAML2; + +use PHPUnit_Framework_Error_Warning; +use PHPUnit_Framework_TestCase; + +class HTTPPostTest extends PHPUnit_Framework_TestCase +{ + /** + * test parsing of basic query string with authnrequest and + * verify that the correct issuer is found. + */ + public function testRequestParsing() + { + $_POST = array(); + $_POST['SAMLRequest'] = 'PHNhbWxwOkF1dGhuUmVxdWVzdCB4bWxuczpzYW1scD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQ09SVE9lZjI3YWE5NWM2ZjAzZTMyMjhhZDQyNDU0MWEzMWY1ZWUxMDY5ZmM0IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0xMi0zMVQxMTo1OToyMVoiIERlc3RpbmF0aW9uPSJodHRwczovL3Roa2ktc2lkLnB0LTQ4LnV0ci5zdXJmY2xvdWQubmwvc3NwL3NhbWwyL2lkcC9TU09TZXJ2aWNlLnBocCIgQXNzZXJ0aW9uQ29uc3VtZXJTZXJ2aWNlVVJMPSJodHRwczovL2VuZ2luZS50ZXN0LnN1cmZjb25leHQubmwvYXV0aGVudGljYXRpb24vc3AvY29uc3VtZS1hc3NlcnRpb24iIFByb3RvY29sQmluZGluZz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmJpbmRpbmdzOkhUVFAtUE9TVCI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9zcC9tZXRhZGF0YTwvc2FtbDpJc3N1ZXI+PHNhbWxwOk5hbWVJRFBvbGljeSBBbGxvd0NyZWF0ZT0idHJ1ZSIvPjxzYW1scDpTY29waW5nIFByb3h5Q291bnQ9IjEwIj48c2FtbHA6UmVxdWVzdGVySUQ+aHR0cHM6Ly90aGtpLXNpZC5wdC00OC51dHIuc3VyZmNsb3VkLm5sL21lbGxvbi9tZXRhZGF0YTwvc2FtbHA6UmVxdWVzdGVySUQ+PC9zYW1scDpTY29waW5nPjwvc2FtbHA6QXV0aG5SZXF1ZXN0Pg=='; + + $hp = new HTTPPost(); + $request = $hp->receive(); + $this->assertInstanceOf('SAML2\AuthnRequest', $request); + $issuer = $request->getIssuer(); + $this->assertEquals('https://engine.test.surfconext.nl/authentication/sp/metadata', $issuer); + } + + /** + * test parsing of SAMLResponse in post. + * verify that the correct issuer is found. + * verify that relaystate is found. + */ + public function testResponseParsing() + { + $_POST = array(); + $_POST['SAMLResponse'] = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJDT1JUT2JjODA4OGRkNjA2YmQ4ZGQ1NDMwMzQ1ZGIxZjc2YWY1Njk1YzkzMzIiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ4OjEyWiIgRGVzdGluYXRpb249Imh0dHBzOi8vdGhraS1zaWQucHQtNDgudXRyLnN1cmZjbG91ZC5ubC9tZWxsb24vcG9zdFJlc3BvbnNlIiBJblJlc3BvbnNlVG89Il83Mjc1NUI3MkRBQTJDQjY3M0ExQ0YxNURCMkQ1OTA5QiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9pZHAvbWV0YWRhdGE8L3NhbWw6SXNzdWVyPjxzYW1scDpTdGF0dXM+PHNhbWxwOlN0YXR1c0NvZGUgVmFsdWU9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpzdGF0dXM6U3VjY2VzcyIvPjwvc2FtbHA6U3RhdHVzPjxzYW1sOkFzc2VydGlvbiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIElEPSJDT1JUTzNmZjNjNzEzZmM0NGVkMWI5ZWM4MzkzMWEwZWVhNjlmNGRmOWY1MGQiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ4OjEyWiI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vZW5naW5lLnRlc3Quc3VyZmNvbmV4dC5ubC9hdXRoZW50aWNhdGlvbi9pZHAvbWV0YWRhdGE8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3JzYS1zaGExIi8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNDT1JUTzNmZjNjNzEzZmM0NGVkMWI5ZWM4MzkzMWEwZWVhNjlmNGRmOWY1MGQiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPkNHekdBNXBKQlN6VkRZOHpTbGplcEhRdEp5ND08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+R2VKVVQ2WjllRENpSTVLVVYzU2FvMHhHL2hnZUI0bk5JcHJvMzYzSElPNm1aYkFMVVBuaVQrS212OFMwOUFDc0ppS0k5MVhidjkvbkVJMGxzVy82aG5teEl6WFFKamU5eTlxcnBhSGtZS203S01oanNRYWhjTWFxMDBVNlNtYnY1RDhNd3ZDak1xWVU3eEVzUmkzejdnTmthT1E1V0Q5dCtUUEMxZGVoc2JnPTwvZHM6U2lnbmF0dXJlVmFsdWU+CjxkczpLZXlJbmZvPjxkczpYNTA5RGF0YT48ZHM6WDUwOUNlcnRpZmljYXRlPk1JSUMrekNDQW1TZ0F3SUJBZ0lKQVBKdkxqUXNSUjRpTUEwR0NTcUdTSWIzRFFFQkJRVUFNRjB4Q3pBSkJnTlZCQVlUQWs1TU1SQXdEZ1lEVlFRSUV3ZFZkSEpsWTJoME1SQXdEZ1lEVlFRSEV3ZFZkSEpsWTJoME1SQXdEZ1lEVlFRS0V3ZFRWVkpHYm1WME1SZ3dGZ1lEVlFRREV3OTBaWE4wTWlCellXMXNJR05sY25Rd0hoY05NVFV3TXpJME1UUXdNekUzV2hjTk1qVXdNekl4TVRRd016RTNXakJkTVFzd0NRWURWUVFHRXdKT1RERVFNQTRHQTFVRUNCTUhWWFJ5WldOb2RERVFNQTRHQTFVRUJ4TUhWWFJ5WldOb2RERVFNQTRHQTFVRUNoTUhVMVZTUm01bGRERVlNQllHQTFVRUF4TVBkR1Z6ZERJZ2MyRnRiQ0JqWlhKME1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRQ3hLWXJuVzhOd3FkUndSd2FIdWx1ZUw2OWdRRlRKYmRmb0FTTGhZaWUyYk9pb0p5SncxZitjQXZwanNsNFNWWXB2RXNlSWV0WEg4TGdwazdLNzNQa0RKTDhkRmc0c0VqRkY2amdtanJPRVMzb3gvZ3RUZDlkL1Z3UEhJL3ZQSWNHeTFzYlZKNHBFTUZsNWQ4R09Bem9Ka05XZFBqOXdWNHJ2NHV2MzVNYXk4d0lEQVFBQm80SENNSUcvTUIwR0ExVWREZ1FXQkJUVElhUHdwS3BKbFk4ZUlNU3pOUlpValN0YmlqQ0Jqd1lEVlIwakJJR0hNSUdFZ0JUVElhUHdwS3BKbFk4ZUlNU3pOUlpValN0YmlxRmhwRjh3WFRFTE1Ba0dBMVVFQmhNQ1Rrd3hFREFPQmdOVkJBZ1RCMVYwY21WamFIUXhFREFPQmdOVkJBY1RCMVYwY21WamFIUXhFREFPQmdOVkJBb1RCMU5WVWtadVpYUXhHREFXQmdOVkJBTVREM1JsYzNReUlITmhiV3dnWTJWeWRJSUpBUEp2TGpRc1JSNGlNQXdHQTFVZEV3UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUs4RXZUVTBMZ0hKc1N1Z29yT2VtZ1JscHBNZkpBZU9tdXVaTmhTTVkyUWh1bUZPWnBhQWI4TkZJd1VLVVZ5eUpuU283azZrdEhDS0k5NHNRczk3NjI0MmhURERZRXdXSkQ5SGhBc0FxT28yMVVhOGdaVDM4L3dtNjJlM0tncktYdm5sakFiS1BYRFhKTTRha3o3eTZINnd2dklHVDZmMGYwaUpXSHEzNGp3dz08L2RzOlg1MDlDZXJ0aWZpY2F0ZT48L2RzOlg1MDlEYXRhPjwvZHM6S2V5SW5mbz48L2RzOlNpZ25hdHVyZT48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpuYW1laWQtZm9ybWF0OnRyYW5zaWVudCI+ZjhjMWRmMDc0Y2IxMTVhOTllYWNlOGI1OWNlZjVjMzk5YjRhNWFhNDwvc2FtbDpOYW1lSUQ+PHNhbWw6U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb25EYXRhIE5vdE9uT3JBZnRlcj0iMjAxNS0xMi0zMVQxMTo1MzoxMloiIFJlY2lwaWVudD0iaHR0cHM6Ly90aGtpLXNpZC5wdC00OC51dHIuc3VyZmNsb3VkLm5sL21lbGxvbi9wb3N0UmVzcG9uc2UiIEluUmVzcG9uc2VUbz0iXzcyNzU1QjcyREFBMkNCNjczQTFDRjE1REIyRDU5MDlCIi8+PC9zYW1sOlN1YmplY3RDb25maXJtYXRpb24+PC9zYW1sOlN1YmplY3Q+PHNhbWw6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMTUtMTItMzFUMTE6NDg6MTFaIiBOb3RPbk9yQWZ0ZXI9IjIwMTUtMTItMzFUMTE6NTM6MTJaIj48c2FtbDpBdWRpZW5jZVJlc3RyaWN0aW9uPjxzYW1sOkF1ZGllbmNlPmh0dHBzOi8vdGhraS1zaWQucHQtNDgudXRyLnN1cmZjbG91ZC5ubC9tZWxsb24vbWV0YWRhdGE8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE1LTEyLTMxVDExOjQ3OjUwWiI+PHNhbWw6QXV0aG5Db250ZXh0PjxzYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPnVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphYzpjbGFzc2VzOlBhc3N3b3JkPC9zYW1sOkF1dGhuQ29udGV4dENsYXNzUmVmPjxzYW1sOkF1dGhlbnRpY2F0aW5nQXV0aG9yaXR5Pmh0dHA6Ly9tb2NrLWlkcDwvc2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT48c2FtbDpBdXRoZW50aWNhdGluZ0F1dGhvcml0eT5odHRwOi8vbW9jay1pZHA8L3NhbWw6QXV0aGVudGljYXRpbmdBdXRob3JpdHk+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg=='; + $_POST['RelayState'] = 'relaystate001'; + + $hp = new HTTPPost(); + $response = $hp->receive(); + $this->assertInstanceOf('SAML2\Response', $response); + $issuer = $response->getIssuer(); + $this->assertEquals('https://engine.test.surfconext.nl/authentication/idp/metadata', $issuer); + $relay = $response->getRelayState(); + $this->assertEquals('relaystate001', $relay); + } + + + /** + * test parsing a request that contains no SAMLRequest or Response + * must generate an exception. + */ + public function testNoRequestParsing() + { + $_POST = array(); + $_POST = array('non' => 'sense'); + $hp = new HTTPPost(); + $this->setExpectedException('Exception', 'Missing SAMLRequest or SAMLResponse parameter'); + $msg = $hp->receive(); + } + + /** + * Construct an authnrequest and send it. + */ + public function testSendAuthnrequest() + { + $request = new AuthnRequest(); + $hp = new HTTPPost(); + $hp->send($request); + } + + /** + * Construct an authnresponse and send it. + * Also test setting a relaystate and destination for the response. + */ + public function testSendAuthnResponse() + { + $response = new Response(); + $response->setIssuer('testIssuer'); + $response->setRelayState('http://example.org'); + $response->setDestination('http://example.org/login?success=yes'); + $response->setSignatureKey(CertificatesMock::getPrivateKey()); + $hr = new HTTPPost(); + $hr->send($response); + } +} diff --git a/tests/SAML2/HTTPRedirectTest.php b/tests/SAML2/HTTPRedirectTest.php index 3145be9c7..c1ecc0b1c 100644 --- a/tests/SAML2/HTTPRedirectTest.php +++ b/tests/SAML2/HTTPRedirectTest.php @@ -70,6 +70,44 @@ public function testSignedRequestParsing() $this->assertEquals('https://beta.surfnet.nl/simplesaml/module.php/core/authenticate.php?as=Braindrops', $relaystate); } + /** + * Test validating a signed authentication request. + */ + public function testSignedRequestValidation() + { + $qs = 'SAMLRequest=nVLBauMwEP0Vo7sjW7FpKpJA2rBsoNuGOruHXhZFHm8EsuRqxtv27yvbWWgvYelFgjfvzbx5zBJVazu56enkHuG5B6TktbUO5VhYsT446RUalE61gJK0rDY%2F7qSYZbILnrz2ln2QXFYoRAhkvGPJbrtiv7VoygJEoTJ9LOusXDSFuJ4vdH6cxwoIEGUjsrqoFUt%2BQcCoXLHYKMoRe9g5JOUoQlleprlI8%2FyQz6W4ksXiiSXbuI1xikbViahDyfkRSM2wD40DmjnL0bSdhcE6Hx7BTd3xqnqoIPw1GmbdqWPJNx80jCGtGIUeWLL5t8mtd9i3EM78n493%2FzWr9XVvx%2B58mj39IlUaR%2FQmKOPq4Dtkyf4c9E1EjPtzOePjREL5%2FXDYp%2FuH6sDWy6G3HDML66%2B5ayO7VlHx2dySf2y9nM7pPprabffeGv02ZNcquux5QEydNiNVUlAODTiKMVvrX24DKIJz8nw9jfx8tOt3&RelayState=https%3A%2F%2Fbeta.surfnet.nl%2Fsimplesaml%2Fmodule.php%2Fcore%2Fauthenticate.php%3Fas%3DBraindrops&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=b%2Bqe%2FXGgICOrEL1v9dwuoy0RJtJ%2FGNAr7gJGYSJzLG0riPKwo7v5CH8GPC2P9IRikaeaNeQrnhBAaf8FCWrO0cLFw4qR6msK9bxRBGk%2BhIaTUYCh54ETrVCyGlmBneMgC5%2FiCRvtEW3ESPXCCqt8Ncu98yZmv9LIVyHSl67Se%2BfbB9sDw3%2FfzwYIHRMqK2aS8jnsnqlgnBGGOXqIqN3%2Bd%2F2dwtCfz14s%2F9odoYzSUv32qfNPiPez6PSNqwhwH7dWE3TlO%2FjZmz0DnOeQ2ft6qdZEi5ZN5KCV6VmNKpkrLMq6DDPnuwPm%2F8oCAoT88R2jG7uf9QZB%2BArWJKMEhDLsCA%3D%3D'; + $_SERVER['QUERY_STRING'] = $qs; + + $hr = new HTTPRedirect(); + $request = $hr->receive(); + + // validate with the correct certificate, should verify + $result = $request->validate(CertificatesMock::getPublicKey2Sha1()); + $this->assertTrue($result); + + // validate with another cert, should fail + $this->setExpectedException('Exception', 'Unable to validate signature'); + $result = $request->validate(CertificatesMock::getPublicKeySha1()); + } + + /** + * Test validating a signed authentication request. + */ + public function testSignedRequestValidationWrongKeytype() + { + $qs = 'SAMLRequest=nVLBauMwEP0Vo7sjW7FpKpJA2rBsoNuGOruHXhZFHm8EsuRqxtv27yvbWWgvYelFgjfvzbx5zBJVazu56enkHuG5B6TktbUO5VhYsT446RUalE61gJK0rDY%2F7qSYZbILnrz2ln2QXFYoRAhkvGPJbrtiv7VoygJEoTJ9LOusXDSFuJ4vdH6cxwoIEGUjsrqoFUt%2BQcCoXLHYKMoRe9g5JOUoQlleprlI8%2FyQz6W4ksXiiSXbuI1xikbViahDyfkRSM2wD40DmjnL0bSdhcE6Hx7BTd3xqnqoIPw1GmbdqWPJNx80jCGtGIUeWLL5t8mtd9i3EM78n493%2FzWr9XVvx%2B58mj39IlUaR%2FQmKOPq4Dtkyf4c9E1EjPtzOePjREL5%2FXDYp%2FuH6sDWy6G3HDML66%2B5ayO7VlHx2dySf2y9nM7pPprabffeGv02ZNcquux5QEydNiNVUlAODTiKMVvrX24DKIJz8nw9jfx8tOt3&RelayState=https%3A%2F%2Fbeta.surfnet.nl%2Fsimplesaml%2Fmodule.php%2Fcore%2Fauthenticate.php%3Fas%3DBraindrops&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=b%2Bqe%2FXGgICOrEL1v9dwuoy0RJtJ%2FGNAr7gJGYSJzLG0riPKwo7v5CH8GPC2P9IRikaeaNeQrnhBAaf8FCWrO0cLFw4qR6msK9bxRBGk%2BhIaTUYCh54ETrVCyGlmBneMgC5%2FiCRvtEW3ESPXCCqt8Ncu98yZmv9LIVyHSl67Se%2BfbB9sDw3%2FfzwYIHRMqK2aS8jnsnqlgnBGGOXqIqN3%2Bd%2F2dwtCfz14s%2F9odoYzSUv32qfNPiPez6PSNqwhwH7dWE3TlO%2FjZmz0DnOeQ2ft6qdZEi5ZN5KCV6VmNKpkrLMq6DDPnuwPm%2F8oCAoT88R2jG7uf9QZB%2BArWJKMEhDLsCA%3D%3D'; + $_SERVER['QUERY_STRING'] = $qs; + + $hr = new HTTPRedirect(); + $request = $hr->receive(); + + // validate with wrong type of cert + $this->setExpectedException('Exception', 'Invalid key type for validating signature'); + $result = $request->validate(CertificatesMock::getPublicKey()); + } + + + /** * test that a request with unsupported encoding specified fails */ @@ -142,6 +180,7 @@ public function testSendAuthnResponse() $response->setIssuer('testIssuer'); $response->setRelayState('http://example.org'); $response->setDestination('http://example.org/login?success=yes'); + $response->setSignatureKey(CertificatesMock::getPrivateKey()); $hr = new HTTPRedirect(); $hr->send($response); } diff --git a/tests/SAML2/StatusResponseTest.php b/tests/SAML2/StatusResponseTest.php index 3bffa0a42..43761828f 100644 --- a/tests/SAML2/StatusResponseTest.php +++ b/tests/SAML2/StatusResponseTest.php @@ -33,4 +33,159 @@ public function testMarshalling() $this->assertCount(1, $statusMessageElements); $this->assertEquals('OurMessageText', $statusMessageElements[0]->textContent); } + + public function testUnmarshalling() + { + $xml = <<<XML +<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b" + InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984" + Version="2.0" + IssueInstant="2007-12-10T11:39:48Z" + Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php"> + <saml:Issuer>max.example.org</saml:Issuer> + <samlp:Status> + <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder" /> + <samlp:StatusMessage>Something is wrong...</samlp:StatusMessage> + </samlp:Status> +</samlp:Response> +XML; + + $fixtureResponseDom = DOMDocumentFactory::fromString($xml); + $response = new Response($fixtureResponseDom->firstChild); + + $this->assertFalse($response->isSuccess()); + + $status = $response->getStatus(); + $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Responder", $status['Code']); + $this->assertNull($status['SubCode']); + $this->assertEquals("Something is wrong...", $status['Message']); + + $this->assertEquals("_bec424fa5103428909a30ff1e31168327f79474984", $response->getInResponseTo()); + } + + /** + * A status reponse that is not an error + */ + public function testStatusSuccess() + { + $xml = <<<XML +<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b" + InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984" + Version="2.0" + IssueInstant="2007-12-10T11:39:48Z" + Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php"> + <saml:Issuer>max.example.org</saml:Issuer> + <samlp:Status> + <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /> + </samlp:Status> +</samlp:Response> +XML; + + $fixtureResponseDom = DOMDocumentFactory::fromString($xml); + $response = new Response($fixtureResponseDom->firstChild); + + $this->assertTrue($response->isSuccess()); + + $status = $response->getStatus(); + $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Success", $status['Code']); + $this->assertNull($status['SubCode']); + $this->assertNull($status['Message']); + } + + /** + * See if we can parse a StatusResponse with a subcode + */ + public function testStatusSubcode() + { + $xml = <<<XML +<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="id-HW1q4BUkjB3GuVvTlqYtwr1cuuI-" + Version="2.0" + IssueInstant="2016-01-20T20:28:02Z" + Destination="https://engine.example.edu/authentication/sp/consume-assertion" + InResponseTo="CORTO8a275030e97351e68e7cc0f89d5b46393d9ee3d9"> + <saml:Issuer>https://example.org/</saml:Issuer> + <samlp:Status> + <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Requester"> + <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:RequestDenied"/> + </samlp:StatusCode> + <samlp:StatusMessage>The AuthnRequest could not be validated</samlp:StatusMessage> + </samlp:Status> +</samlp:Response> +XML; + + $fixtureResponseDom = DOMDocumentFactory::fromString($xml); + $response = new Response($fixtureResponseDom->firstChild); + + $this->assertFalse($response->isSuccess()); + + $status = $response->getStatus(); + $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:Requester", $status['Code']); + $this->assertEquals("urn:oasis:names:tc:SAML:2.0:status:RequestDenied", $status['SubCode']); + $this->assertEquals("The AuthnRequest could not be validated", $status['Message']); + } + + /** + * Test adding in-response-to to a status message. + */ + public function testResponseTo() + { + $response = new Response(); + $response->setIssueInstant(1453323439); + $response->setStatus(array( + 'Code' => 'OurStatusCode' + )); + $response->setInResponseTo('aabb12234'); + + $responseElement = $response->toUnsignedXML(); + + $expectedStructureDocument = new \DOMDocument(); + $expectedStructureDocument->loadXML(<<<STATUSXML +<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="123" + Version="2.0" + IssueInstant="2016-01-20T20:57:19Z" + InResponseTo="aabb12234"> + <samlp:Status> + <samlp:StatusCode Value="OurStatusCode"/> + </samlp:Status> +</samlp:Response> +STATUSXML + ); + $expectedStructure = $expectedStructureDocument->documentElement; + $this->assertEqualXMLStructure($expectedStructure, $responseElement); + } + + /** + * StatusCode is required in a StatusResponse. + */ + public function testNoStatusCodeThrowsException() + { + $this->setExpectedException('Exception', 'Missing status code'); + + $xml = <<<XML +<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + ID="s2a0da3504aff978b0f8c80f6a62c713c4a2f64c5b" + InResponseTo="_bec424fa5103428909a30ff1e31168327f79474984" + Version="2.0" + IssueInstant="2007-12-10T11:39:48Z" + Destination="http://somewhere.example.org/simplesaml/saml2/sp/AssertionConsumerService.php"> + <saml:Issuer>max.example.org</saml:Issuer> + <samlp:Status> + <samlp:StatusMessage>Something is wrong...</samlp:StatusMessage> + </samlp:Status> +</samlp:Response> +XML; + + $fixtureResponseDom = DOMDocumentFactory::fromString($xml); + $response = new Response($fixtureResponseDom->firstChild); + } + } diff --git a/tests/SAML2/Utilities/ArrayCollectionTest.php b/tests/SAML2/Utilities/ArrayCollectionTest.php new file mode 100644 index 000000000..3263787c0 --- /dev/null +++ b/tests/SAML2/Utilities/ArrayCollectionTest.php @@ -0,0 +1,134 @@ +<?php + +namespace SAML2\Utilities; + +class ArrayCollectionTest extends \PHPUnit_Framework_TestCase +{ + + public function test_construct_get_add_set() + { + $arc = new ArrayCollection(array('aap', 'aap', 'noot')); + + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), 'noot'); + $this->assertNull($arc->get(3)); + + $arc->add('mies'); + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), 'noot'); + $this->assertEquals($arc->get(3), 'mies'); + + $arc->set(1, 'mies'); + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'mies'); + $this->assertEquals($arc->get(2), 'noot'); + $this->assertEquals($arc->get(3), 'mies'); + } + + public function test_remove() + { + $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies')); + + $removed = $arc->remove('noot'); + $this->assertEquals('noot', $removed); + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), null); + $this->assertEquals($arc->get(3), 'mies'); + + $removed = $arc->remove('wim'); + $this->assertFalse($removed); + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), null); + $this->assertEquals($arc->get(3), 'mies'); + + $removed = $arc->remove('aap'); + $this->assertEquals('aap', $removed); + $this->assertEquals($arc->get(0), null); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), null); + $this->assertEquals($arc->get(3), 'mies'); + + $removed = $arc->remove('aap'); + $this->assertEquals('aap', $removed); + $this->assertEquals($arc->get(0), null); + $this->assertEquals($arc->get(1), null); + $this->assertEquals($arc->get(2), null); + $this->assertEquals($arc->get(3), 'mies'); + } + + public function test_first_last_count() + { + $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies')); + + $this->assertEquals($arc->first(), 'aap'); + $this->assertEquals($arc->last(), 'mies'); + $this->assertEquals($arc->count(), 4); + } + + public function test_offset() + { + $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies')); + + $this->assertTrue($arc->offsetExists(0)); + $this->assertTrue($arc->offsetExists(1)); + $this->assertFalse($arc->offsetExists(4)); + + $this->assertEquals($arc->offsetGet(0), 'aap'); + $this->assertEquals($arc->offsetGet(2), 'noot'); + + $arc->offsetSet(2, 'zus'); + $this->assertEquals($arc->offsetGet(2), 'zus'); + + $arc->offsetUnset(0); + $this->assertFalse($arc->offsetExists(0)); + $this->assertTrue($arc->offsetExists(1)); + } + + public function test_onlyelement() + { + $arc = new ArrayCollection(array('aap')); + $this->assertEquals($arc->getOnlyElement(), 'aap'); + } + + public function test_onlyelement_fail() + { + $arc = new ArrayCollection(array('aap', 'noot')); + $this->setExpectedException('SAML2\Exception\RuntimeException', 'SAML2\Utilities\ArrayCollection::SAML2\Utilities\ArrayCollection::getOnlyElement requires that the collection has exactly one element, "2" elements found'); + $arc->getOnlyElement(); + } + + public function test_getiterator() + { + $arc = new ArrayCollection(array('aap', 'noot')); + $this->assertInstanceOf('\ArrayIterator', $arc->getIterator()); + } + + public function test_filter_map() + { + $arc = new ArrayCollection(array('aap', 'aap', 'noot', 'mies')); + + $filtered = $arc->filter(function ($i) { return $i != 'aap'; }); + $this->assertInstanceOf('\SAML2\Utilities\ArrayCollection', $filtered); + $this->assertEquals($filtered->get(0), null); + $this->assertEquals($filtered->get(1), null); + $this->assertEquals($filtered->get(2), 'noot'); + $this->assertEquals($filtered->get(3), 'mies'); + + $mapped = $arc->map(function ($i) { return ucfirst($i); }); + $this->assertInstanceOf('\SAML2\Utilities\ArrayCollection', $mapped); + $this->assertEquals($mapped->get(0), 'Aap'); + $this->assertEquals($mapped->get(1), 'Aap'); + $this->assertEquals($mapped->get(2), 'Noot'); + $this->assertEquals($mapped->get(3), 'Mies'); + + // ensure original is not changed + $this->assertEquals($arc->get(0), 'aap'); + $this->assertEquals($arc->get(1), 'aap'); + $this->assertEquals($arc->get(2), 'noot'); + $this->assertEquals($arc->get(3), 'mies'); + } +} diff --git a/tests/SAML2/Utilities/CertificateTest.php b/tests/SAML2/Utilities/CertificateTest.php new file mode 100644 index 000000000..16c6af067 --- /dev/null +++ b/tests/SAML2/Utilities/CertificateTest.php @@ -0,0 +1,29 @@ +<?php + +namespace SAML2\Utilities; + +class CertificateTest extends \PHPUnit_Framework_TestCase +{ + /** + * @group utilities + * @test + */ + public function testValidStructure() + { + $result = Certificate::hasValidStructure(\SAML2\CertificatesMock::getPlainPublicKey()); + $this->assertTrue($result); + $result = Certificate::hasValidStructure(\SAML2\CertificatesMock::getPlainInvalidPublicKey()); + $this->assertFalse($result); + } + + /** + * @group utilities + * @test + */ + public function testConvertToCertificate() + { + $result = Certificate::convertToCertificate(\SAML2\CertificatesMock::getPlainPublicKeyContents()); + // the formatted public key in CertificatesMock is stored with unix newlines + $this->assertEquals(\SAML2\CertificatesMock::getPlainPublicKey() . "\n", str_replace("\r", "", $result)); + } +} diff --git a/tests/SAML2/XML/saml/AttributeValueTest.php b/tests/SAML2/XML/saml/AttributeValueTest.php new file mode 100644 index 000000000..de01bc2e0 --- /dev/null +++ b/tests/SAML2/XML/saml/AttributeValueTest.php @@ -0,0 +1,112 @@ +<?php + +namespace SAML2\XML\md; + +use SAML2\Constants; +use SAML2\DOMDocumentFactory; +use SAML2\Utils; +use SAML2\XML\saml\Attribute; +use SAML2\XML\saml\AttributeValue; + +/** + * Class \SAML2\XML\md\AttributeTest + */ +class AttributeValueTest extends \PHPUnit_Framework_TestCase +{ + + /** + * Verifies that supplying an empty string as attribute value will + * generate a tag with no content (instead of e.g. an empty tag). + */ + public function testEmptyStringAttribute() + { + $attribute = new Attribute(); + $attribute->Name = 'TheName'; + $attribute->NameFormat = 'TheNameFormat'; + $attribute->FriendlyName = 'TheFriendlyName'; + $attribute->AttributeValue = array( + new AttributeValue(""), + ); + + $document = DOMDocumentFactory::fromString('<root />'); + $returnedStructure = $attribute->toXML($document->firstChild); + + $expectedStructureDocument = new \DOMDocument(); + $expectedStructureDocument->loadXML(<<<ATTRIBUTEVALUE +<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Name="TheName" + NameFormat="TheNameFormat" FriendlyName="TheFriendlyName"> + <saml:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string"></saml:AttributeValue> +</saml:Attribute> +ATTRIBUTEVALUE + ); + $expectedStructure = $expectedStructureDocument->documentElement; + + $this->assertEqualXMLStructure($expectedStructure, $returnedStructure); + $this->assertEquals("", $attribute->AttributeValue[0]->getString()); + } + + /** + * Verifies that we can create an AttributeValue from a DOMElement. + */ + public function testCreateAttributeFromDOMElement() + { + $attribute = new Attribute(); + $attribute->Name = 'TheName'; + $attribute->NameFormat = 'TheNameFormat'; + $attribute->FriendlyName = 'TheFriendlyName'; + + $element = new \DOMDocument(); + $element->loadXML(<<<ATTRIBUTEVALUE +<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID> +ATTRIBUTEVALUE + ); + + $attribute->AttributeValue = array( + new AttributeValue($element->documentElement), + ); + + $document = DOMDocumentFactory::fromString('<root />'); + $returnedStructure = $attribute->toXML($document->firstChild); + + $expectedStructureDocument = new \DOMDocument(); + $expectedStructureDocument->loadXML(<<<ATTRIBUTEXML +<saml:Attribute xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" + Name="TheName" NameFormat="TheNameFormat" FriendlyName="TheFriendlyName"> + <saml:AttributeValue> + <NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID> + </saml:AttributeValue> +</saml:Attribute> +ATTRIBUTEXML + ); + $expectedStructure = $expectedStructureDocument->documentElement; + + $this->assertEqualXMLStructure($expectedStructure, $returnedStructure); + $this->assertEquals("urn:collab:person:surftest.nl:example", $attribute->AttributeValue[0]->getString()); + } + + /** + * Serialize an AttributeValue and Unserialize that again. + */ + public function testSerialize() + { + $av1 = new AttributeValue("Aap:noot:mies"); + $ser = $av1->serialize(); + $av2 = new AttributeValue("Wim"); + $av2->unserialize($ser); + + $this->assertEquals("Aap:noot:mies", $av2->getString()); + + $element = new \DOMDocument(); + $element->loadXML(<<<ATTRIBUTEVALUE +<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">urn:collab:person:surftest.nl:example</NameID> +ATTRIBUTEVALUE + ); + + $av3 = new AttributeValue($element->documentElement); + $ser = $av3->serialize(); + $av4 = new AttributeValue("Wim"); + $av4->unserialize($ser); + $this->assertEquals("urn:collab:person:surftest.nl:example", $av4->getString()); + } +}