├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG-1.0.md ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── autoload.php ├── bin ├── lightsaml ├── lightsaml.bat └── lightsaml.php ├── composer.json ├── composer.lock ├── doc ├── command_sp_meta_build.md ├── console.md ├── index.md └── signing_and_certificates.md ├── phpunit.xml ├── resources └── sample │ ├── Certificate │ ├── saml.crt │ └── saml.pem │ ├── EntitiesDescriptor │ └── testshib-providers.xml │ ├── EntityDescriptor │ ├── ed01-formatted-certificate.xml │ ├── idp-ed.xml │ ├── idp2-ed.xml │ ├── sp-ed.xml │ └── sp-ed2.xml │ ├── Request │ ├── logoutrequest01.xml │ └── request01.xml │ ├── Response │ ├── invalid.xml │ ├── invalid02.xml │ ├── response01-formatted.xml │ ├── response01.xml │ ├── response02.xml │ ├── response03-formatted.xml │ └── response03.xml │ ├── foo-formatted.xml │ └── foo.xml └── src └── AerialShip └── LightSaml ├── Binding ├── AbstractBinding.php ├── BindingDetector.php ├── HttpPost.php ├── HttpPostTemplate.php ├── HttpRedirect.php ├── PostResponse.php ├── RedirectResponse.php ├── Request.php └── Response.php ├── Bindings.php ├── ClaimTypes.php ├── Command └── BuildSPMetadataCommand.php ├── Error ├── BindingException.php ├── BuildRequestException.php ├── InvalidAssertionException.php ├── InvalidAuthnStatementException.php ├── InvalidBindingException.php ├── InvalidCertificateException.php ├── InvalidMessageException.php ├── InvalidNameIDException.php ├── InvalidRequestException.php ├── InvalidResponseException.php ├── InvalidSubjectException.php ├── InvalidXmlException.php ├── InvalidXmlNamespaceException.php ├── LightSamlBaseException.php └── SecurityException.php ├── Helper.php ├── Meta ├── AbstractRequestBuilder.php ├── AuthnRequestBuilder.php ├── GetSignedXmlInterface.php ├── GetXmlInterface.php ├── LoadFromXmlInterface.php ├── LogoutRequestBuilder.php ├── SerializationContext.php ├── SpMeta.php ├── XmlChildrenLoaderTrait.php └── XmlRequiredAttributesTrait.php ├── Model ├── Assertion │ ├── Assertion.php │ ├── Attribute.php │ ├── AuthnStatement.php │ ├── NameID.php │ ├── Subject.php │ ├── SubjectConfirmation.php │ └── SubjectConfirmationData.php ├── Metadata │ ├── EntitiesDescriptor.php │ ├── EntityDescriptor.php │ ├── IdpSsoDescriptor.php │ ├── KeyDescriptor.php │ ├── NameIDFormat.php │ ├── SSODescriptor.php │ ├── Service │ │ ├── AbstractService.php │ │ ├── AssertionConsumerService.php │ │ ├── SingleLogoutService.php │ │ └── SingleSignOnService.php │ └── SpSsoDescriptor.php ├── Protocol │ ├── AbstractRequest.php │ ├── AuthnRequest.php │ ├── LogoutRequest.php │ ├── LogoutResponse.php │ ├── Message.php │ ├── Response.php │ ├── Status.php │ ├── StatusCode.php │ └── StatusResponse.php └── XmlDSig │ ├── Signature.php │ ├── SignatureCreator.php │ ├── SignatureStringValidator.php │ ├── SignatureValidatorInterface.php │ └── SignatureXmlValidator.php ├── NameIDPolicy.php ├── Protocol.php ├── Security ├── KeyHelper.php └── X509Certificate.php └── Tests ├── Binding ├── Base.php ├── HttpPostTest.php └── HttpRedirectTest.php ├── CommonHelper.php ├── Meta └── AuthnRequestBuilderTest.php ├── Model ├── Metadata │ ├── EntitiesDescriptor │ │ ├── EntitiesDescriptorFunctional01Test.php │ │ └── EntitiesDescriptorTest.php │ ├── EntityDescriptor │ │ ├── EntityDescriptorSample01Test.php │ │ ├── EntityDescriptorSample02Test.php │ │ └── EntityDescriptorXmlTest.php │ └── SSODescriptor │ │ └── SSODescriptorTest.php ├── Protocol │ ├── AuthnRequest │ │ └── AuthnRequestSample01Test.php │ ├── LogoutRequest │ │ └── LogoutRequestSample01Test.php │ └── Response │ │ └── ResponseSample01Test.php └── XmlDSig │ └── Signature │ ├── SignatureCreatorValidatorFunctionalTest.php │ └── SignatureSample01Test.php └── Security └── X509CertificateTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Bootstrap 2 | app/bootstrap* 3 | 4 | # Symfony directories 5 | vendor/* 6 | */logs/* 7 | */cache/* 8 | web/uploads/* 9 | web/bundles/* 10 | 11 | # Configuration files 12 | app/config/parameters.ini 13 | app/config/parameters.yml 14 | 15 | /.idea 16 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - php 3 | 4 | tools: 5 | external_code_coverage: 6 | timeout: 600 # Timeout in seconds. 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - hhvm 8 | 9 | matrix: 10 | allow_failures: 11 | - php: hhvm 12 | 13 | before_script: 14 | - COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev install 15 | 16 | script: 17 | - phpunit --coverage-clover=coverage.clover 18 | 19 | after_script: 20 | - wget https://scrutinizer-ci.com/ocular.phar 21 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 22 | -------------------------------------------------------------------------------- /CHANGELOG-1.0.md: -------------------------------------------------------------------------------- 1 | CHANGELOG for 1.0.x 2 | =================== 3 | 4 | 1.0.2 (2014-07-09) 5 | * [Bug] Logout Request Builder #5 6 | * [CodeStyle] Improve code metric #7 7 | * [Bug] Support for formatted certificate in message XML #10 8 | * [Bug] "KeyDescriptor" elment "use" attribute should be optional #12 9 | * [NewFeature] Support for EntitiesDescriptor element #11 10 | * [Bug] InResponseTo attribute optional for StatusResponse #14 #15 11 | * [Bug] InvalidArgumentException at LogoutRequest ->setNotOnOrAfter() #16 12 | * [NewFeature] New method in Signature Validator for array of keys #18 13 | * [NewFeature] New method EntitiesDescriptor::getByEntityId #19 14 | * [Bug] Fix AuthnRequest send and Response receive bidnings and url #20 15 | * [NewFeature] Logging of sent/received messages in Binding #23 16 | * [Bug] NameIDPolicy made optional in AuthnRequest? #21 17 | * [Bug] SignatureMethod element made optional #24 18 | * [Bug] StatusCode missing from status #26 19 | * [NewFeature] Optional constructor arguments 037a595fc 20 | * [NewFeature] Support for IdpSsoDescriptor Attributes & NameIdFormat e37b037d1 21 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | CONTRIBUTORS 2 | ============ 3 | 4 | LightSaml so far is a result of work of 5 | 6 | * Milos Tomic (tmilos) 7 | * Boris Vujicic (i3or1s) 8 | 9 | 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 aerialship 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SAML 2.0 PHP Component 2 | ====================== 3 | 4 | NEW VERSION 5 | ----------- 6 | 7 | **New version of this library is in own organization: [lightsaml/lightsaml](https://github.com/lightsaml/lightsaml)**. 8 | Details can be found at [LightSAML website](http://www.lightsaml.com/LightSAML-Core/). 9 | 10 | 11 | [![Stories in Ready](https://badge.waffle.io/aerialship/lightsaml.png?label=ready&title=Ready)](https://waffle.io/aerialship/lightsaml) 12 | [![Build Status](https://travis-ci.org/aerialship/lightsaml.svg)](https://travis-ci.org/aerialship/lightsaml) 13 | [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/aerialship/lightsaml/badges/quality-score.png?s=bc6dc951b7ec67e23699ff28a35ccd98bf97415b)](https://scrutinizer-ci.com/g/aerialship/lightsaml/) 14 | [![Sensio Insight](https://insight.sensiolabs.com/projects/a5537ce0-a513-4d3d-ba7a-21e9115f4c89/small.png)](https://insight.sensiolabs.com/projects/a5537ce0-a513-4d3d-ba7a-21e9115f4c89) 15 | [![Code Coverage](https://scrutinizer-ci.com/g/aerialship/lightsaml/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/aerialship/lightsaml/?branch=master) 16 | [![HHVM Status](http://hhvm.h4cc.de/badge/aerialship/lightsaml.svg)](http://hhvm.h4cc.de/package/aerialship/lightsaml) 17 | 18 | 19 | 20 | LightSaml Implements basic SAML 2.0 data model classes, serialization/deserialization to/from xml with XML security and 21 | certificates support, and message encapsulations to bindings. Covered with unit tests. 22 | 23 | * [PHP SAML Symfony2 Bundle](https://github.com/aerialship/SamlSPBundle) 24 | 25 | 26 | DOCUMENTATION 27 | ============= 28 | 29 | * [Usage](doc/index.md) 30 | * [Console](doc/console.md) 31 | * [Signing and Certificates](doc/signing_and_certificates.md) 32 | 33 | 34 | CONTRIBUTING 35 | ============ 36 | 37 | LightSaml is an open source project and is open for contributions. 38 | 39 | 40 | CREDIT 41 | ====== 42 | 43 | Thanks to all contributors and specially to SimpleSamlPHP open source which though hardly reusable helped us in 44 | start to understand better how SAML works and speed up the implementation. 45 | -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | add(new \AerialShip\LightSaml\Command\BuildSPMetadataCommand()); 10 | $app->run($input); 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aerialship/lightsaml", 3 | "license": "MIT", 4 | "description": "Light SAML2 php library", 5 | "autoload": { 6 | "psr-0": { "AerialShip\\LightSaml\\": "src/" } 7 | }, 8 | "require": { 9 | "php": ">=5.4.1", 10 | "rnijveld/xmlseclibs": "1.3.1", 11 | "symfony/console": "~2.2" 12 | }, 13 | "require-dev": { 14 | "phpunit/phpunit": "~4.1" 15 | }, 16 | "bin": ["bin/lightsaml"], 17 | "prefer-stable": true, 18 | "minimum-stability": "stable" 19 | } 20 | -------------------------------------------------------------------------------- /doc/command_sp_meta_build.md: -------------------------------------------------------------------------------- 1 | 2 | SP Metadata build command 3 | ========================= 4 | 5 | ID: aerialship:lightsaml:sp:meta:build 6 | Class: AerialShip\LightSaml\Command\BuildSPMetadataCommand 7 | 8 | Used to build SP metadata xml. Just run it and follow dialog questions. As a result a metadata xml file is created. 9 | 10 | -------------------------------------------------------------------------------- /doc/console.md: -------------------------------------------------------------------------------- 1 | LightSaml console 2 | ================= 3 | 4 | Executes a lightsaml command. 5 | 6 | Location: bin/lightsaml 7 | 8 | Commands: 9 | 10 | * [aerialship:lightsaml:sp:meta:build](command_sp_meta_build.md) 11 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | METADATA 2 | ======== 3 | 4 | Metadata building 5 | ----------------- 6 | 7 | Use [aerialship:lightsaml:sp:meta:build](command_sp_meta_build.md) command for simple cases 8 | 9 | ```php 10 | $ed = new EntityDescriptor(); 11 | $sp = new SpSsoDescriptor(); 12 | $ed->addItem($sp); 13 | 14 | // KeyDescriptor 15 | $certificate = new X509Certificate(); 16 | $certificate->loadFromFile($certificatePath); 17 | $keyDescriptor = new KeyDescriptor('signing', $certificate); 18 | $ed->addItem($keyDescriptor); 19 | 20 | // SingleLogoutService 21 | $s = new SingleLogoutService(); 22 | $s->setLocation($url); 23 | $s->setBinding($this->resolveBinding($binding)); 24 | $sp->addService($s); 25 | 26 | // AssertionConsumerService 27 | $s = new AssertionConsumerService($binding1, $url, 0); 28 | $sp->addService($s); 29 | $s = new AssertionConsumerService($binding2, $url, 1); 30 | $sp->addService($s); 31 | ``` 32 | 33 | Saving metadata to xml 34 | ---------------------- 35 | 36 | ```php 37 | $ed = new Model\Metadata\EntityDescriptor(); 38 | // ... 39 | $context = new Meta\SerializationContext(); 40 | $ed->getXml($context->getDocument(), $context); 41 | file_put_contents($context->getDocument()->saveXML(), $xml); 42 | ``` 43 | 44 | Loading metadata from xml 45 | ------------------------- 46 | 47 | ```php 48 | $doc = new \DOMDocument(); 49 | $doc->load('sp.xml'); 50 | $ed = new EntityDescriptor(); 51 | $ed->loadFromXml($doc->firstChild); 52 | ``` 53 | 54 | 55 | AUTHN REQUEST 56 | ============= 57 | 58 | ```php 59 | $spMeta = new Meta\SpMeta(); 60 | $spMeta->setNameIdFormat(NameIDPolicy::PERSISTENT); 61 | $spED = getEntityDescriptorSP(); 62 | $idpED = getEntityDescriptorIDP(); 63 | 64 | $builder = new Meta\AuthnRequestBuilder($spED, $idpED, $spMeta); 65 | $request = $builder->build(); 66 | ``` 67 | 68 | 69 | SENDING MESSAGE 70 | =============== 71 | 72 | HTTP Redirect 73 | ------------- 74 | 75 | ```php 76 | $binding = new Binding\HttpRedirect(); 77 | $bindingResponse = $binding->send($message); 78 | $bindingResponse->render(); 79 | ``` 80 | 81 | HTTP POST 82 | --------- 83 | 84 | ```php 85 | $binding = new Binding\HttpPost(); 86 | $bindingResponse = $binding->send($message); 87 | $bindingResponse->render(); 88 | ``` 89 | 90 | 91 | RECEIVING MESSAGE 92 | ================= 93 | 94 | ```php 95 | $request = new Binding\Request(); 96 | $request->setQueryString($_SERVER['QUERY_STRING']); 97 | $request->setGet($_GET); 98 | $request->getPost($_POST); 99 | $request->setRequestMethod($_SERVER['REQUEST_METHOD']); 100 | 101 | $detector = new BindingDetector(); 102 | $binding = $detector->instantiate($this->bindingDetector->getBinding($bindingRequest)); 103 | 104 | $message = $binding->receive($bindingRequest); 105 | ``` 106 | -------------------------------------------------------------------------------- /doc/signing_and_certificates.md: -------------------------------------------------------------------------------- 1 | SIGNING AND CERTIFICATES 2 | ======================== 3 | 4 | The LightSaml relies on the [xmlseclibs](https://code.google.com/p/xmlseclibs/) regarding signing and certificates 5 | functionality. LightSaml has helper methods and was tested using xmlseclibs with PEM format only. 6 | 7 | 8 | Creating self signed certificates 9 | --------------------------------- 10 | 11 | Run the following command to generate pem certificate and key 12 | 13 | ``` bash 14 | $ openssl req -new -x509 -days 3652 -nodes -out saml.crt -keyout saml.pem 15 | ``` 16 | 17 | Loading certificate from file and creating public key 18 | ----------------------------------------------------- 19 | 20 | ``` php 21 | $certificate = new X509Certificate(); 22 | $certificate->loadFromFile($crtFilename); 23 | 24 | $publicKey = KeyHelper::createPublicKey($certificate); 25 | ``` 26 | 27 | 28 | Creating private key 29 | -------------------- 30 | 31 | ``` php 32 | $privateKey = KeyHelper::createPrivateKey($filename, $pass, true); 33 | ``` 34 | 35 | Signing SAML messages 36 | --------------------- 37 | You would require private key for signing SAML messages. 38 | 39 | ``` php 40 | $message->sign($certificate, $privateKey); 41 | ``` 42 | 43 | Exposing certificate in metadata 44 | -------------------------------- 45 | 46 | If your SP supports signing you would need to expose your certificate in your SP's metadata entity descriptor 47 | 48 | ``` php 49 | $ed = new EntityDescriptor(); 50 | $sp = new SpSsoDescriptor(); 51 | $sp->addKeyDescriptor(new KeyDescriptor('signing', $certificate)); 52 | $ed->->addItem($sp); 53 | ``` 54 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | src/AerialShip/LightSaml/Tests 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /resources/sample/Certificate/saml.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDrDCCApSgAwIBAgIJAIxzbGLou3BjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNV 3 | BAYTAlJTMQ8wDQYDVQQIEwZTZXJiaWExDDAKBgNVBAoTA0JPUzEUMBIGA1UEAxML 4 | bXQuZXZvLnRlYW0wHhcNMTMxMDA4MTg1OTMyWhcNMjMxMDA4MTg1OTMyWjBCMQsw 5 | CQYDVQQGEwJSUzEPMA0GA1UECBMGU2VyYmlhMQwwCgYDVQQKEwNCT1MxFDASBgNV 6 | BAMTC210LmV2by50ZWFtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA 7 | ws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs1MeeTOMXjnA35bre 8 | Ga9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20jTqO6JQbDli3pYNP0 9 | fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf4QNMIb1iI1oNErt3 10 | iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6GUMTF1x0eQMUzw4d 11 | kxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj/jHUlX+POFWx2JRv 12 | +428ghrXpNvqUNqv7ozfFwIDAQABo4GkMIGhMB0GA1UdDgQWBBRomf3Xyc5ck3ce 13 | IXq0n45pxUkgwjByBgNVHSMEazBpgBRomf3Xyc5ck3ceIXq0n45pxUkgwqFGpEQw 14 | QjELMAkGA1UEBhMCUlMxDzANBgNVBAgTBlNlcmJpYTEMMAoGA1UEChMDQk9TMRQw 15 | EgYDVQQDEwttdC5ldm8udGVhbYIJAIxzbGLou3BjMAwGA1UdEwQFMAMBAf8wDQYJ 16 | KoZIhvcNAQEFBQADggEBAGAXc8pe6+6owl9z2iqybE6pbjXTKqjSclMGrdeooItU 17 | 1xGqBhYu/b2q6hEvYZCzlqYe5euf3r8C7GAAKEYyuwu3xuLDYV4n6l6eWTIl1dou 18 | g+r0Bl8Z3157A4BcgmUT64QkekI2VDHO8WAdDOWQg1UTEoqCryTOtmRaC391iGAq 19 | bz1wtZtV95boGdur8SChK9LKcPrbCDxpo64BMgtPk2HkRgE7h5YWkLHxmxwZrYi3 20 | EAfS6IucblY3wwY4GEix8DQh1lYgpv5TOD8IMVf+oUWdp81Un/IqHqLhnSupwk6r 21 | BYbUFhN/ClK5UcoDqWHcj27tGKD6aNlxTdSwcYBl3Ts= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /resources/sample/Certificate/saml.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs 3 | 1MeeTOMXjnA35breGa9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20j 4 | TqO6JQbDli3pYNP0fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf 5 | 4QNMIb1iI1oNErt3iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6 6 | GUMTF1x0eQMUzw4dkxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj 7 | /jHUlX+POFWx2JRv+428ghrXpNvqUNqv7ozfFwIDAQABAoIBAAuwFflqsG/8XFfD 8 | QT+A5Ov6tPTmLw0wR6KguPKllT0SnkKRI/NwNpMuXM9y9CeYWpL37bUEdIl8HMlp 9 | z+cfu0K+sYVIMbp0a2H07qJRiuEHaGkFl0S0lgp6qWltP79LeDOpc2wjcwPQBjup 10 | LEE4WtsKhTHePUaBP1nH+rt9lOiVddR+09qJf+v149XGaMvU9CngnzB1QUHEZGu/ 11 | g9gbr37uv0zaJ08Nr+eZvJ8ByVCbseEaM4bPE3pj/5GCI/b4Y24cEYyVxNWKxK4x 12 | DtVyMA9WkVg1J+C9cyQtWyFAByjfzLXCeRV5Yanh5erfBGxKeLf2iYj+67vZnoIV 13 | 45py66ECgYEA/Ejrir3dD1ehUf8QQrn9SzPtHKIfvKxjY9099Jr7YaFJMnsD3D3j 14 | EYV0Jf9MOVakc+cQyNuqLGXhgpcHuBIHFKxOQ2ROleUxBlix93wkKXYJTAbg3dlz 15 | HM5JOi5H1Ms/cHiZtmi7jP9qPxgvVx5PjnHT74gme6z2cVtSwmwQdzMCgYEAxa1J 16 | ubQehmU00SPhtmiD1nkv/5N1IzcerwAbMSfEe3VTbTer+c42F50GW0RPLhSTyS7I 17 | vKI2kjgnvbL+ZDftGJ/8Sy/xuR4oqI7HS8R/b0TDzs00Ih6jRoKvCvWc4Rz/LnAK 18 | 3U+By2kO0LoAYMMyEIROJY1ToM0ZJNw+YJi36I0CgYBYooHmd7gfuMjblwIuf0un 19 | TTID+k2uKt6sakFnG8Av18twqdl8Wl3ZfQqGgwBTsMYMVmUGPG64NH8bM1ap6+OC 20 | 9tp0QNx/O0uTEDxOV9YYnvi7NjtwlgIkX7FIeJQSBepe6HAQrxEdg2rfie6v691F 21 | jWZJZIsormbTAqO73N2YEwKBgE5vV9WLlqEU/0QMzL6n8cfkBhuO0ufKMy6VCDUU 22 | 5m1mPFgVwvM301bDQ4ZrME/Ecm9dVOEkLCsQhRRMNYxmdHlU4bKjr3IYeWGIZaO2 23 | sCniSx9/ZLQ1G+waZoIeusTH0KkIZAc0SPgBE5PWedfrG65aEPftf8VKHDP3//gs 24 | IKzxAoGBAKaDo/sPvb6Z/RLFDt+QH+VBLSFOrdAn4+tKmSoMHWRrx4mxt48NF4hh 25 | X+1z7SA9OHZ1iLBRwGhjWhyELkV0MvwCZDb3TC0wp+BJMGQnOMwB8KPisvHj/Qxh 26 | OgAVD1IafyfRczuodKG3I2qu2VRcLMbmUZKmhOQ/ylj93vKC5B/y 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /resources/sample/EntityDescriptor/ed01-formatted-certificate.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | 9 | 10 | 11 | MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV 12 | MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD 13 | VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 14 | MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI 15 | EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl 16 | c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B 17 | AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C 18 | yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 19 | 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT 20 | NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 21 | kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH 22 | gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G 23 | A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 24 | 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl 25 | bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo 26 | aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN 27 | BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL 28 | I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 29 | 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 30 | /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj 31 | Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 32 | 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== 33 | 34 | 35 | 36 | 37 | 39 | 41 | 43 | 45 | 46 | -------------------------------------------------------------------------------- /resources/sample/EntityDescriptor/sp-ed.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 11 | 15 | 19 | 23 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /resources/sample/EntityDescriptor/sp-ed2.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | MIIDrDCCApSgAwIBAgIJAIxzbGLou3BjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlJTMQ8wDQYDVQQIEwZTZXJiaWExDDAKBgNVBAoTA0JPUzEUMBIGA1UEAxMLbXQuZXZvLnRlYW0wHhcNMTMxMDA4MTg1OTMyWhcNMjMxMDA4MTg1OTMyWjBCMQswCQYDVQQGEwJSUzEPMA0GA1UECBMGU2VyYmlhMQwwCgYDVQQKEwNCT1MxFDASBgNVBAMTC210LmV2by50ZWFtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs1MeeTOMXjnA35breGa9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20jTqO6JQbDli3pYNP0fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf4QNMIb1iI1oNErt3iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6GUMTF1x0eQMUzw4dkxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj/jHUlX+POFWx2JRv+428ghrXpNvqUNqv7ozfFwIDAQABo4GkMIGhMB0GA1UdDgQWBBRomf3Xyc5ck3ceIXq0n45pxUkgwjByBgNVHSMEazBpgBRomf3Xyc5ck3ceIXq0n45pxUkgwqFGpEQwQjELMAkGA1UEBhMCUlMxDzANBgNVBAgTBlNlcmJpYTEMMAoGA1UEChMDQk9TMRQwEgYDVQQDEwttdC5ldm8udGVhbYIJAIxzbGLou3BjMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGAXc8pe6+6owl9z2iqybE6pbjXTKqjSclMGrdeooItU1xGqBhYu/b2q6hEvYZCzlqYe5euf3r8C7GAAKEYyuwu3xuLDYV4n6l6eWTIl1doug+r0Bl8Z3157A4BcgmUT64QkekI2VDHO8WAdDOWQg1UTEoqCryTOtmRaC391iGAqbz1wtZtV95boGdur8SChK9LKcPrbCDxpo64BMgtPk2HkRgE7h5YWkLHxmxwZrYi3EAfS6IucblY3wwY4GEix8DQh1lYgpv5TOD8IMVf+oUWdp81Un/IqHqLhnSupwk6rBYbUFhN/ClK5UcoDqWHcj27tGKD6aNlxTdSwcYBl3Ts= 11 | 12 | 13 | 14 | 15 | 16 | 17 | MIIDrDCCApSgAwIBAgIJAIxzbGLou3BjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlJTMQ8wDQYDVQQIEwZTZXJiaWExDDAKBgNVBAoTA0JPUzEUMBIGA1UEAxMLbXQuZXZvLnRlYW0wHhcNMTMxMDA4MTg1OTMyWhcNMjMxMDA4MTg1OTMyWjBCMQswCQYDVQQGEwJSUzEPMA0GA1UECBMGU2VyYmlhMQwwCgYDVQQKEwNCT1MxFDASBgNVBAMTC210LmV2by50ZWFtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs1MeeTOMXjnA35breGa9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20jTqO6JQbDli3pYNP0fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf4QNMIb1iI1oNErt3iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6GUMTF1x0eQMUzw4dkxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj/jHUlX+POFWx2JRv+428ghrXpNvqUNqv7ozfFwIDAQABo4GkMIGhMB0GA1UdDgQWBBRomf3Xyc5ck3ceIXq0n45pxUkgwjByBgNVHSMEazBpgBRomf3Xyc5ck3ceIXq0n45pxUkgwqFGpEQwQjELMAkGA1UEBhMCUlMxDzANBgNVBAgTBlNlcmJpYTEMMAoGA1UEChMDQk9TMRQwEgYDVQQDEwttdC5ldm8udGVhbYIJAIxzbGLou3BjMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGAXc8pe6+6owl9z2iqybE6pbjXTKqjSclMGrdeooItU1xGqBhYu/b2q6hEvYZCzlqYe5euf3r8C7GAAKEYyuwu3xuLDYV4n6l6eWTIl1doug+r0Bl8Z3157A4BcgmUT64QkekI2VDHO8WAdDOWQg1UTEoqCryTOtmRaC391iGAqbz1wtZtV95boGdur8SChK9LKcPrbCDxpo64BMgtPk2HkRgE7h5YWkLHxmxwZrYi3EAfS6IucblY3wwY4GEix8DQh1lYgpv5TOD8IMVf+oUWdp81Un/IqHqLhnSupwk6rBYbUFhN/ClK5UcoDqWHcj27tGKD6aNlxTdSwcYBl3Ts= 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /resources/sample/Request/logoutrequest01.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | https://mt.evo.team/simplesaml/module.php/saml/sp/metadata.php/default-sp 11 | user 12 | _677952a2-7fb3-4e7a-b439-326366e677db 13 | 14 | -------------------------------------------------------------------------------- /resources/sample/Request/request01.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | https://mt.evo.team/simplesaml/module.php/saml/sp/metadata.php/default-sp 11 | 12 | -------------------------------------------------------------------------------- /resources/sample/Response/invalid.xml: -------------------------------------------------------------------------------- 1 | https://B1.bead.loc/adfs/services/trust -------------------------------------------------------------------------------- /resources/sample/Response/invalid02.xml: -------------------------------------------------------------------------------- 1 | 8 | https://sts.windows.net/554fadfe-f04f-4975-90cb-ddc8b147aaa2/ 9 | 10 | 11 | 12 | 13 | 14 | ACS75006: An error occurred while processing a SAML2 Authentication request. ACS75003: SAML protocol response cannot be sent via bindings other than HTTP POST. Requested binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect 15 | Trace ID: d75d5305-d3fc-40b0-9087-d59032682dd9 16 | Correlation ID: ca26b4bd-23d4-4233-9c28-96bc0a336c39 17 | Timestamp: 2013-11-17 12:35:10Z 18 | 19 | 20 | -------------------------------------------------------------------------------- /resources/sample/Response/response01-formatted.xml: -------------------------------------------------------------------------------- 1 | 6 | https://B1.bead.loc/adfs/services/trust 7 | 8 | 9 | 10 | 12 | https://B1.bead.loc/adfs/services/trust 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | V8Ls9BSGOMwyyCmMtz2AFEGZdic= 24 | 25 | 26 | 27 | LHTt9aRHMBkgNukWiCEGTqrRavdHjz+i0duaUe6t1yVWVqRHsbdjQDHv4nxAPe18qMEO3j1PGoyjjFTZNR6v+yvuFu7zmARhxTjDuqzNGP5npiP5D8DACiTFyL1WFH8C7jZmJA5j9wRGDHq4xvMExQr2pTgQEHe1InmLvix8xjbPoevR7Wf1rG3nGIhfHcyQRpMqAeNQN2qI7hSQQ1k9rEGS7daz1j29YCeoWqvQ+hZNkhtY/HSCVeQzfXEG79AFZHpYQyXzWK+v8f48ntegIJx2hi9FV/HE4n4RBbNMdqbT9y71bw8EM/kEZoJehpgMgQYZP7ryGfdNMGWC9LfbjA== 28 | 29 | 30 | 31 | 32 | MIIC0jCCAbqgAwIBAgIQGFT6omLmWbhAD65bM40rGzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDExpBREZTIFNpZ25pbmcgLSBCMS5iZWFkLmxvYzAeFw0xMzEwMDkxNDUyMDVaFw0xNDEwMDkxNDUyMDVaMCUxIzAhBgNVBAMTGkFERlMgU2lnbmluZyAtIEIxLmJlYWQubG9jMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlGKV64+63lpqdPmCTZ0kt/yKr8xukR1Y071SlmRVV5sSFhTe8cjylPqqxdyEBrfPhpL6vwFQyKfDhuM8T9E+BW5fUdoXO4WmIHrLOxV/BzKv2rDGidlCFzDSQPDxPH2RdQkMBksiauIMSHIYXB92rO4fkcsTgQ6cc+PZp4M3Z/jR1mcxQzz9RQk3I9w2OtI9xcv+uDC5mQU0ZWVHc99VSFQt+zshduwIqxQdHvMdTRslso+oCLEQom42pGCD8TksQTGw4sB7Ctb0mgXdfy0PDIznfi2oDBGtPY2Hkms6/n9xoyCynQea0YYXcpEe7lAvs+t6Lq+ZaKp2kUaa2x8d+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfwlmaN1iPg0gNiqdVphJjWnzpV4h6/Mz3L0xYzNQeglWCDKCKuajQfmo/AQBErtOWZJsP8avzK79gNRqFHXF6CirjGnL6WO+S6Ug1hvy3xouOxOkIYgZsbmcNL2XO1hIxP4z/QWPthotp3FSUTae2hFBHuy4Gtb+9d9a60GDtgrHnfgVeCTE7CSiaI/D/51JNbtpg2tCpcEzMQgPkQqb8E+V79xc0dnEcI5cBaS6eYgkJgS5gKIMbwaJ/VxzCVGIKwFjFnJedJ5N7zH7OVwor56Q7nuKD7X4yFY9XR3isjGnwXveh9E4d9wD4CMl52AHJpsYsToXsi3eRvApDV/PE 33 | 34 | 35 | 36 | 37 | 38 | bos@bead.loc 39 | 40 | 43 | 44 | 45 | 46 | 47 | https://mt.evo.team/simplesaml/module.php/saml/sp/metadata.php/b1 48 | 49 | 50 | 51 | 52 | bos@bead.loc 53 | 54 | 55 | 56 | 57 | urn:federation:authentication:windows 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /resources/sample/Response/response01.xml: -------------------------------------------------------------------------------- 1 | https://B1.bead.loc/adfs/services/trusthttps://B1.bead.loc/adfs/services/trustV8Ls9BSGOMwyyCmMtz2AFEGZdic=LHTt9aRHMBkgNukWiCEGTqrRavdHjz+i0duaUe6t1yVWVqRHsbdjQDHv4nxAPe18qMEO3j1PGoyjjFTZNR6v+yvuFu7zmARhxTjDuqzNGP5npiP5D8DACiTFyL1WFH8C7jZmJA5j9wRGDHq4xvMExQr2pTgQEHe1InmLvix8xjbPoevR7Wf1rG3nGIhfHcyQRpMqAeNQN2qI7hSQQ1k9rEGS7daz1j29YCeoWqvQ+hZNkhtY/HSCVeQzfXEG79AFZHpYQyXzWK+v8f48ntegIJx2hi9FV/HE4n4RBbNMdqbT9y71bw8EM/kEZoJehpgMgQYZP7ryGfdNMGWC9LfbjA==MIIC0jCCAbqgAwIBAgIQGFT6omLmWbhAD65bM40rGzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDExpBREZTIFNpZ25pbmcgLSBCMS5iZWFkLmxvYzAeFw0xMzEwMDkxNDUyMDVaFw0xNDEwMDkxNDUyMDVaMCUxIzAhBgNVBAMTGkFERlMgU2lnbmluZyAtIEIxLmJlYWQubG9jMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlGKV64+63lpqdPmCTZ0kt/yKr8xukR1Y071SlmRVV5sSFhTe8cjylPqqxdyEBrfPhpL6vwFQyKfDhuM8T9E+BW5fUdoXO4WmIHrLOxV/BzKv2rDGidlCFzDSQPDxPH2RdQkMBksiauIMSHIYXB92rO4fkcsTgQ6cc+PZp4M3Z/jR1mcxQzz9RQk3I9w2OtI9xcv+uDC5mQU0ZWVHc99VSFQt+zshduwIqxQdHvMdTRslso+oCLEQom42pGCD8TksQTGw4sB7Ctb0mgXdfy0PDIznfi2oDBGtPY2Hkms6/n9xoyCynQea0YYXcpEe7lAvs+t6Lq+ZaKp2kUaa2x8d+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfwlmaN1iPg0gNiqdVphJjWnzpV4h6/Mz3L0xYzNQeglWCDKCKuajQfmo/AQBErtOWZJsP8avzK79gNRqFHXF6CirjGnL6WO+S6Ug1hvy3xouOxOkIYgZsbmcNL2XO1hIxP4z/QWPthotp3FSUTae2hFBHuy4Gtb+9d9a60GDtgrHnfgVeCTE7CSiaI/D/51JNbtpg2tCpcEzMQgPkQqb8E+V79xc0dnEcI5cBaS6eYgkJgS5gKIMbwaJ/VxzCVGIKwFjFnJedJ5N7zH7OVwor56Q7nuKD7X4yFY9XR3isjGnwXveh9E4d9wD4CMl52AHJpsYsToXsi3eRvApDV/PEbos@bead.lochttps://mt.evo.team/simplesaml/module.php/saml/sp/metadata.php/b1bos@bead.locurn:federation:authentication:windows -------------------------------------------------------------------------------- /resources/sample/Response/response02.xml: -------------------------------------------------------------------------------- 1 | https://B1.bead.loc/adfs/services/trusthttps://B1.bead.loc/adfs/services/trustV8Ls9BSGOMwyyCmMtz2AFEGZdic=LHTt9aRHMBkgNukWiCEGTqrRavdHjz+i0duaUe6t1yVWVqRHsbdjQDHv4nxAPe18qMEO3j1PGoyjjFTZNR6v+yvuFu7zmARhxTjDuqzNGP5npiP5D8DACiTFyL1WFH8C7jZmJA5j9wRGDHq4xvMExQr2pTgQEHe1InmLvix8xjbPoevR7Wf1rG3nGIhfHcyQRpMqAeNQN2qI7hSQQ1k9rEGS7daz1j29YCeoWqvQ+hZNkhtY/HSCVeQzfXEG79AFZHpYQyXzWK+v8f48ntegIJx2hi9FV/HE4n4RBbNMdqbT9y71bw8EM/kEZoJehpgMgQYZP7ryGfdNMGWC9LfbjA==MIIC0jCCAbqgAwIBAgIQGFT6omLmWbhAD65bM40rGzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDExpBREZTIFNpZ25pbmcgLSBCMS5iZWFkLmxvYzAeFw0xMzEwMDkxNDUyMDVaFw0xNDEwMDkxNDUyMDVaMCUxIzAhBgNVBAMTGkFERlMgU2lnbmluZyAtIEIxLmJlYWQubG9jMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlGKV64+63lpqdPmCTZ0kt/yKr8xukR1Y071SlmRVV5sSFhTe8cjylPqqxdyEBrfPhpL6vwFQyKfDhuM8T9E+BW5fUdoXO4WmIHrLOxV/BzKv2rDGidlCFzDSQPDxPH2RdQkMBksiauIMSHIYXB92rO4fkcsTgQ6cc+PZp4M3Z/jR1mcxQzz9RQk3I9w2OtI9xcv+uDC5mQU0ZWVHc99VSFQt+zshduwIqxQdHvMdTRslso+oCLEQom42pGCD8TksQTGw4sB7Ctb0mgXdfy0PDIznfi2oDBGtPY2Hkms6/n9xoyCynQea0YYXcpEe7lAvs+t6Lq+ZaKp2kUaa2x8d+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfwlmaN1iPg0gNiqdVphJjWnzpV4h6/Mz3L0xYzNQeglWCDKCKuajQfmo/AQBErtOWZJsP8avzK79gNRqFHXF6CirjGnL6WO+S6Ug1hvy3xouOxOkIYgZsbmcNL2XO1hIxP4z/QWPthotp3FSUTae2hFBHuy4Gtb+9d9a60GDtgrHnfgVeCTE7CSiaI/D/51JNbtpg2tCpcEzMQgPkQqb8E+V79xc0dnEcI5cBaS6eYgkJgS5gKIMbwaJ/VxzCVGIKwFjFnJedJ5N7zH7OVwor56Q7nuKD7X4yFY9XR3isjGnwXveh9E4d9wD4CMl52AHJpsYsToXsi3eRvApDV/PEbos@bead.lochttps://mt.evo.team/simplesaml/module.php/saml/sp/metadata.php/b1bos@bead.locurn:federation:authentication:windows -------------------------------------------------------------------------------- /resources/sample/Response/response03-formatted.xml: -------------------------------------------------------------------------------- 1 | 6 | https://B1.bead.loc/adfs/services/trust 7 | 8 | 9 | 10 | 12 | https://B1.bead.loc/adfs/services/trust 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | geMEaAJqRuNYPx3ccSqFwEIcGnk= 24 | 25 | 26 | 27 | Wt5E/asZPwmuPyk9He3h7jaGC5W5z4VVKRlBFhws+x821hUrjGjL0YsQo6ILQplwvrfJJvfZ/T+n2mAkxtm/VRt8Tj2MuvppRNdT6Ou+pZ76+uivXfZon/sayBQ9CxTd6ph/SMDG/oQ7/wOOupaPiZujFIZ5iVuIoQdwZxhoxlkh8D7ZDNwCH0iKHAtih6fazJ7Dtb6cK0+GyAam63sfNuPWK1gXon4Qe66rNxhgyxHMxGgyfp9tpHMGdYDbJ7cZqIAyBV0P5nmkFtPohBnpZ4EJHZfAw3AysvGiGZxaXrGasVyfONra/kbkYsN/1+FWhLByXrgTuZIEh28jUVReTg== 28 | 29 | 30 | 31 | 32 | MIIC0jCCAbqgAwIBAgIQGFT6omLmWbhAD65bM40rGzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDExpBREZTIFNpZ25pbmcgLSBCMS5iZWFkLmxvYzAeFw0xMzEwMDkxNDUyMDVaFw0xNDEwMDkxNDUyMDVaMCUxIzAhBgNVBAMTGkFERlMgU2lnbmluZyAtIEIxLmJlYWQubG9jMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlGKV64+63lpqdPmCTZ0kt/yKr8xukR1Y071SlmRVV5sSFhTe8cjylPqqxdyEBrfPhpL6vwFQyKfDhuM8T9E+BW5fUdoXO4WmIHrLOxV/BzKv2rDGidlCFzDSQPDxPH2RdQkMBksiauIMSHIYXB92rO4fkcsTgQ6cc+PZp4M3Z/jR1mcxQzz9RQk3I9w2OtI9xcv+uDC5mQU0ZWVHc99VSFQt+zshduwIqxQdHvMdTRslso+oCLEQom42pGCD8TksQTGw4sB7Ctb0mgXdfy0PDIznfi2oDBGtPY2Hkms6/n9xoyCynQea0YYXcpEe7lAvs+t6Lq+ZaKp2kUaa2x8d+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfwlmaN1iPg0gNiqdVphJjWnzpV4h6/Mz3L0xYzNQeglWCDKCKuajQfmo/AQBErtOWZJsP8avzK79gNRqFHXF6CirjGnL6WO+S6Ug1hvy3xouOxOkIYgZsbmcNL2XO1hIxP4z/QWPthotp3FSUTae2hFBHuy4Gtb+9d9a60GDtgrHnfgVeCTE7CSiaI/D/51JNbtpg2tCpcEzMQgPkQqb8E+V79xc0dnEcI5cBaS6eYgkJgS5gKIMbwaJ/VxzCVGIKwFjFnJedJ5N7zH7OVwor56Q7nuKD7X4yFY9XR3isjGnwXveh9E4d9wD4CMl52AHJpsYsToXsi3eRvApDV/PE 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | https://bevo.evo.loc/saml 47 | 48 | 49 | 50 | 51 | bos@bead.loc 52 | 53 | 54 | bos@bos.com 55 | 56 | 57 | BosFirstName 58 | 59 | 60 | LastName 61 | 62 | 63 | 64 | 65 | urn:federation:authentication:windows 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /resources/sample/Response/response03.xml: -------------------------------------------------------------------------------- 1 | https://B1.bead.loc/adfs/services/trusthttps://B1.bead.loc/adfs/services/trustgeMEaAJqRuNYPx3ccSqFwEIcGnk=Wt5E/asZPwmuPyk9He3h7jaGC5W5z4VVKRlBFhws+x821hUrjGjL0YsQo6ILQplwvrfJJvfZ/T+n2mAkxtm/VRt8Tj2MuvppRNdT6Ou+pZ76+uivXfZon/sayBQ9CxTd6ph/SMDG/oQ7/wOOupaPiZujFIZ5iVuIoQdwZxhoxlkh8D7ZDNwCH0iKHAtih6fazJ7Dtb6cK0+GyAam63sfNuPWK1gXon4Qe66rNxhgyxHMxGgyfp9tpHMGdYDbJ7cZqIAyBV0P5nmkFtPohBnpZ4EJHZfAw3AysvGiGZxaXrGasVyfONra/kbkYsN/1+FWhLByXrgTuZIEh28jUVReTg==MIIC0jCCAbqgAwIBAgIQGFT6omLmWbhAD65bM40rGzANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDExpBREZTIFNpZ25pbmcgLSBCMS5iZWFkLmxvYzAeFw0xMzEwMDkxNDUyMDVaFw0xNDEwMDkxNDUyMDVaMCUxIzAhBgNVBAMTGkFERlMgU2lnbmluZyAtIEIxLmJlYWQubG9jMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlGKV64+63lpqdPmCTZ0kt/yKr8xukR1Y071SlmRVV5sSFhTe8cjylPqqxdyEBrfPhpL6vwFQyKfDhuM8T9E+BW5fUdoXO4WmIHrLOxV/BzKv2rDGidlCFzDSQPDxPH2RdQkMBksiauIMSHIYXB92rO4fkcsTgQ6cc+PZp4M3Z/jR1mcxQzz9RQk3I9w2OtI9xcv+uDC5mQU0ZWVHc99VSFQt+zshduwIqxQdHvMdTRslso+oCLEQom42pGCD8TksQTGw4sB7Ctb0mgXdfy0PDIznfi2oDBGtPY2Hkms6/n9xoyCynQea0YYXcpEe7lAvs+t6Lq+ZaKp2kUaa2x8d+QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfwlmaN1iPg0gNiqdVphJjWnzpV4h6/Mz3L0xYzNQeglWCDKCKuajQfmo/AQBErtOWZJsP8avzK79gNRqFHXF6CirjGnL6WO+S6Ug1hvy3xouOxOkIYgZsbmcNL2XO1hIxP4z/QWPthotp3FSUTae2hFBHuy4Gtb+9d9a60GDtgrHnfgVeCTE7CSiaI/D/51JNbtpg2tCpcEzMQgPkQqb8E+V79xc0dnEcI5cBaS6eYgkJgS5gKIMbwaJ/VxzCVGIKwFjFnJedJ5N7zH7OVwor56Q7nuKD7X4yFY9XR3isjGnwXveh9E4d9wD4CMl52AHJpsYsToXsi3eRvApDV/PEhttps://bevo.evo.loc/samlbos@bead.locbos@bos.comBosFirstNameLastNameurn:federation:authentication:windows -------------------------------------------------------------------------------- /resources/sample/foo-formatted.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | BXhK3C4G4X4jcKMiyiiagHLqyEw= 14 | 15 | 16 | 17 | WveIJuhS9u1JoEoQXtJudRflIsAURDQBDiFMavyFk0jkomRsh2mhh0OZ8bycnBzxzmr0ddH37F0yHlKJ4fjIlNTMvbT9XF7a/UncNRu3kC9mnr0/dLF9tu4MNq3tcRb5NreMFWtD6Ikzfh11mQJten3FhPeZ3wAiz8fzOORAcnsH+oKugcDB4tmP425pPKmQ+J0WMMM9kiwW4Wew1+fNEw0axS0HrKNLvLqDixlELbwNrdTh1Qu7+5vu2PUKRRAFbif/EBzay7B1QaY+cyRj46Lb2phuHysBRh6K5hA2L+/KT6eUnypqwgAV9RPTsXSEHALXwHTsR6aKsa6Es2acIg== 18 | 19 | 20 | 21 | 22 | MIIDrDCCApSgAwIBAgIJAIxzbGLou3BjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlJTMQ8wDQYDVQQIEwZTZXJiaWExDDAKBgNVBAoTA0JPUzEUMBIGA1UEAxMLbXQuZXZvLnRlYW0wHhcNMTMxMDA4MTg1OTMyWhcNMjMxMDA4MTg1OTMyWjBCMQswCQYDVQQGEwJSUzEPMA0GA1UECBMGU2VyYmlhMQwwCgYDVQQKEwNCT1MxFDASBgNVBAMTC210LmV2by50ZWFtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs1MeeTOMXjnA35breGa9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20jTqO6JQbDli3pYNP0fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf4QNMIb1iI1oNErt3iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6GUMTF1x0eQMUzw4dkxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj/jHUlX+POFWx2JRv+428ghrXpNvqUNqv7ozfFwIDAQABo4GkMIGhMB0GA1UdDgQWBBRomf3Xyc5ck3ceIXq0n45pxUkgwjByBgNVHSMEazBpgBRomf3Xyc5ck3ceIXq0n45pxUkgwqFGpEQwQjELMAkGA1UEBhMCUlMxDzANBgNVBAgTBlNlcmJpYTEMMAoGA1UEChMDQk9TMRQwEgYDVQQDEwttdC5ldm8udGVhbYIJAIxzbGLou3BjMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGAXc8pe6+6owl9z2iqybE6pbjXTKqjSclMGrdeooItU1xGqBhYu/b2q6hEvYZCzlqYe5euf3r8C7GAAKEYyuwu3xuLDYV4n6l6eWTIl1doug+r0Bl8Z3157A4BcgmUT64QkekI2VDHO8WAdDOWQg1UTEoqCryTOtmRaC391iGAqbz1wtZtV95boGdur8SChK9LKcPrbCDxpo64BMgtPk2HkRgE7h5YWkLHxmxwZrYi3EAfS6IucblY3wwY4GEix8DQh1lYgpv5TOD8IMVf+oUWdp81Un/IqHqLhnSupwk6rBYbUFhN/ClK5UcoDqWHcj27tGKD6aNlxTdSwcYBl3Ts= 23 | 24 | 25 | 26 | 27 | 28 | something 29 | 30 | 31 | -------------------------------------------------------------------------------- /resources/sample/foo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BXhK3C4G4X4jcKMiyiiagHLqyEw=WveIJuhS9u1JoEoQXtJudRflIsAURDQBDiFMavyFk0jkomRsh2mhh0OZ8bycnBzxzmr0ddH37F0yHlKJ4fjIlNTMvbT9XF7a/UncNRu3kC9mnr0/dLF9tu4MNq3tcRb5NreMFWtD6Ikzfh11mQJten3FhPeZ3wAiz8fzOORAcnsH+oKugcDB4tmP425pPKmQ+J0WMMM9kiwW4Wew1+fNEw0axS0HrKNLvLqDixlELbwNrdTh1Qu7+5vu2PUKRRAFbif/EBzay7B1QaY+cyRj46Lb2phuHysBRh6K5hA2L+/KT6eUnypqwgAV9RPTsXSEHALXwHTsR6aKsa6Es2acIg== 6 | MIIDrDCCApSgAwIBAgIJAIxzbGLou3BjMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlJTMQ8wDQYDVQQIEwZTZXJiaWExDDAKBgNVBAoTA0JPUzEUMBIGA1UEAxMLbXQuZXZvLnRlYW0wHhcNMTMxMDA4MTg1OTMyWhcNMjMxMDA4MTg1OTMyWjBCMQswCQYDVQQGEwJSUzEPMA0GA1UECBMGU2VyYmlhMQwwCgYDVQQKEwNCT1MxFDASBgNVBAMTC210LmV2by50ZWFtMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAws7jML47jTQbWleRwihk15wOjuspoKPcxW1aERexAMWe8BMs1MeeTOMXjnA35breGa9PwJi2KjtDz3gkhVCglZzLZGBLLO7uchZvagFhTomZa20jTqO6JQbDli3pYNP0fBIrmEbH9cfhgm91Fm+6bTVnJ4xQhT4aPWrPAVKU2FDTBFBf4QNMIb1iI1oNErt3iocsbRTbIyjjvIe8yLVrtmZXA0DnkxB/riym0GT+4gpOEKV6GUMTF1x0eQMUzw4dkxhFs7fv6YrJymtEMmHOeiA5vVPEtxEr84JAXJyZUaZfufkj/jHUlX+POFWx2JRv+428ghrXpNvqUNqv7ozfFwIDAQABo4GkMIGhMB0GA1UdDgQWBBRomf3Xyc5ck3ceIXq0n45pxUkgwjByBgNVHSMEazBpgBRomf3Xyc5ck3ceIXq0n45pxUkgwqFGpEQwQjELMAkGA1UEBhMCUlMxDzANBgNVBAgTBlNlcmJpYTEMMAoGA1UEChMDQk9TMRQwEgYDVQQDEwttdC5ldm8udGVhbYIJAIxzbGLou3BjMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGAXc8pe6+6owl9z2iqybE6pbjXTKqjSclMGrdeooItU1xGqBhYu/b2q6hEvYZCzlqYe5euf3r8C7GAAKEYyuwu3xuLDYV4n6l6eWTIl1doug+r0Bl8Z3157A4BcgmUT64QkekI2VDHO8WAdDOWQg1UTEoqCryTOtmRaC391iGAqbz1wtZtV95boGdur8SChK9LKcPrbCDxpo64BMgtPk2HkRgE7h5YWkLHxmxwZrYi3EAfS6IucblY3wwY4GEix8DQh1lYgpv5TOD8IMVf+oUWdp81Un/IqHqLhnSupwk6rBYbUFhN/ClK5UcoDqWHcj27tGKD6aNlxTdSwcYBl3Ts=something 7 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/AbstractBinding.php: -------------------------------------------------------------------------------- 1 | destination = $destination; 25 | } 26 | 27 | /** 28 | * @return string 29 | */ 30 | public function getDestination() { 31 | return $this->destination; 32 | } 33 | 34 | 35 | /** 36 | * @param $callable 37 | */ 38 | public function addReceiveListener($callable) 39 | { 40 | $this->receiveListeners[] = $callable; 41 | } 42 | 43 | /** 44 | * @param $callable 45 | */ 46 | public function addSendListener($callable) 47 | { 48 | $this->sendListeners[] = $callable; 49 | } 50 | 51 | 52 | /** 53 | * @param string $messageString 54 | */ 55 | protected function dispatchReceive($messageString) 56 | { 57 | foreach ($this->receiveListeners as $callable) { 58 | call_user_func($callable, $messageString); 59 | } 60 | } 61 | 62 | /** 63 | * @param string $messageString 64 | */ 65 | protected function dispatchSend($messageString) 66 | { 67 | foreach ($this->sendListeners as $callable) { 68 | call_user_func($callable, $messageString); 69 | } 70 | } 71 | 72 | /** 73 | * @param Message $message 74 | * @return Response 75 | */ 76 | abstract function send(Message $message); 77 | 78 | 79 | /** 80 | * @param Request $request 81 | * @return Message 82 | */ 83 | abstract function receive(Request $request); 84 | 85 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/BindingDetector.php: -------------------------------------------------------------------------------- 1 | getRequestMethod())); 41 | if ($requestMethod == 'GET') { 42 | return $this->processGET($request); 43 | } else if ($requestMethod == 'POST') { 44 | return $this->processPOST($request); 45 | } 46 | return null; 47 | } 48 | 49 | 50 | /** 51 | * @param Request $request 52 | * @return null|string 53 | */ 54 | private function processGET(Request $request) 55 | { 56 | $get = $request->getGet(); 57 | if (array_key_exists('SAMLRequest', $get) || array_key_exists('SAMLResponse', $get)) { 58 | return Bindings::SAML2_HTTP_REDIRECT; 59 | } elseif (array_key_exists('SAMLart', $get) ){ 60 | return Bindings::SAML2_HTTP_ARTIFACT; 61 | } 62 | return null; 63 | } 64 | 65 | 66 | /** 67 | * @param Request $request 68 | * @return null|string 69 | */ 70 | private function processPOST(Request $request) 71 | { 72 | $post = $request->getPost(); 73 | if (array_key_exists('SAMLRequest', $post) || array_key_exists('SAMLResponse', $post)) { 74 | return Bindings::SAML2_HTTP_POST; 75 | } elseif (array_key_exists('SAMLart', $post) ){ 76 | return Bindings::SAML2_HTTP_ARTIFACT; 77 | } else { 78 | if ($request->getContentType()) { 79 | $contentType = explode(';', $request->getContentType()); 80 | $contentType = $contentType[0]; /* Remove charset. */ 81 | if ($contentType === 'text/xml') { 82 | return Bindings::SAML2_SOAP; 83 | } 84 | } 85 | } 86 | return null; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/HttpPost.php: -------------------------------------------------------------------------------- 1 | getDestination() ?: $this->getDestination(); 20 | 21 | $context = new SerializationContext(); 22 | $message->getSignedXml($context->getDocument(), $context); 23 | $msgStr = $context->getDocument()->saveXML(); 24 | 25 | $this->dispatchSend($msgStr); 26 | 27 | $msgStr = base64_encode($msgStr); 28 | 29 | $type = $message instanceof AbstractRequest ? 'SAMLRequest' : 'SAMLResponse'; 30 | 31 | $data = array($type => $msgStr); 32 | if ($message->getRelayState()) { 33 | $data['RelayState'] = $message->getRelayState(); 34 | } 35 | 36 | $result = new PostResponse($destination, $data); 37 | return $result; 38 | } 39 | 40 | 41 | /** 42 | * @param Request $request 43 | * @return Message 44 | * @throws \AerialShip\LightSaml\Error\BindingException 45 | */ 46 | function receive(Request $request) { 47 | $post = $request->getPost(); 48 | if (array_key_exists('SAMLRequest', $post)) { 49 | $msg = $post['SAMLRequest']; 50 | } elseif (array_key_exists('SAMLResponse', $post)) { 51 | $msg = $post['SAMLResponse']; 52 | } else { 53 | throw new BindingException('Missing SAMLRequest or SAMLResponse parameter'); 54 | } 55 | 56 | $msg = base64_decode($msg); 57 | 58 | $this->dispatchReceive($msg); 59 | 60 | $doc = new \DOMDocument(); 61 | $doc->loadXML($msg); 62 | $result = Message::fromXML($doc->firstChild); 63 | 64 | if (array_key_exists('RelayState', $post)) { 65 | $result->setRelayState($post['RelayState']); 66 | } 67 | 68 | return $result; 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/HttpPostTemplate.php: -------------------------------------------------------------------------------- 1 | destination = $destination; 17 | $this->data = $data; 18 | } 19 | 20 | 21 | function render() { 22 | ?> 23 | 25 | 26 | 27 | 28 | POST data 29 | 30 | 31 | 32 | 35 | 36 |
37 | 38 | 39 | data as $name=>$value) { ?> 40 | 41 | 42 | 43 | 46 | 47 |
48 | 49 | data = $data; 20 | } 21 | 22 | 23 | /** 24 | * @param array $data 25 | */ 26 | public function setData($data) 27 | { 28 | $this->data = $data; 29 | } 30 | 31 | /** 32 | * @return array 33 | */ 34 | public function getData() 35 | { 36 | return $this->data; 37 | } 38 | 39 | 40 | 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function render() 46 | { 47 | $template = new HttpPostTemplate($this->getDestination(), $this->getData()); 48 | ob_start(); 49 | $template->render(); 50 | $result = ob_get_clean(); 51 | return $result; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/RedirectResponse.php: -------------------------------------------------------------------------------- 1 | getDestination(), true, 302); 21 | header('Pragma: no-cache'); 22 | header('Cache-Control: no-cache, must-revalidate'); 23 | } 24 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/Request.php: -------------------------------------------------------------------------------- 1 | setContentType($_SERVER['CONTENT_TYPE']); 30 | $result->setGet($_GET); 31 | $result->setPost($_POST); 32 | $result->setQueryString($_SERVER['QUERY_STRING']); 33 | $result->setRequestMethod($_SERVER['REQUEST_METHOD']); 34 | 35 | return $result; 36 | } 37 | 38 | /** 39 | * @param string $contentType 40 | */ 41 | public function setContentType($contentType) 42 | { 43 | $this->contentType = $contentType; 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getContentType() 50 | { 51 | return $this->contentType; 52 | } 53 | 54 | /** 55 | * @param array $get 56 | */ 57 | public function setGet($get) 58 | { 59 | $this->get = $get; 60 | } 61 | 62 | /** 63 | * @return array 64 | */ 65 | public function getGet() 66 | { 67 | return $this->get; 68 | } 69 | 70 | /** 71 | * @param array $post 72 | */ 73 | public function setPost($post) 74 | { 75 | $this->post = $post; 76 | } 77 | 78 | /** 79 | * @return array 80 | */ 81 | public function getPost() 82 | { 83 | return $this->post; 84 | } 85 | 86 | /** 87 | * @param string $queryString 88 | */ 89 | public function setQueryString($queryString) 90 | { 91 | $this->queryString = $queryString; 92 | } 93 | 94 | /** 95 | * @return string 96 | */ 97 | public function getQueryString() 98 | { 99 | return $this->queryString; 100 | } 101 | 102 | /** 103 | * @param string $requestMethod 104 | */ 105 | public function setRequestMethod($requestMethod) 106 | { 107 | $this->requestMethod = $requestMethod; 108 | } 109 | 110 | /** 111 | * @return string 112 | */ 113 | public function getRequestMethod() 114 | { 115 | return $this->requestMethod; 116 | } 117 | 118 | 119 | 120 | 121 | 122 | public function parseQueryString($queryString = null, $urlDecodeValues = false) 123 | { 124 | if ($queryString) { 125 | $this->queryString = $queryString; 126 | } 127 | $result = array(); 128 | foreach (explode('&', $this->queryString) as $e) { 129 | $tmp = explode('=', $e, 2); 130 | $name = $tmp[0]; 131 | $value = count($tmp) === 2 ? $value = $tmp[1] : ''; 132 | $name = urldecode($name); 133 | $result[$name] = $urlDecodeValues ? urldecode($value) : $value; 134 | } 135 | return $result; 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Binding/Response.php: -------------------------------------------------------------------------------- 1 | destination = $destination; 16 | } 17 | 18 | 19 | 20 | /** 21 | * @param string $destination 22 | */ 23 | public function setDestination($destination) 24 | { 25 | $this->destination = $destination; 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | public function getDestination() 32 | { 33 | return $this->destination; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Bindings.php: -------------------------------------------------------------------------------- 1 | Protocol::SAML2, 24 | self::SAML2_HTTP_POST => Protocol::SAML2, 25 | self::SAML2_HTTP_POST_SIMPLE_SIGN => Protocol::SAML2, 26 | self::SAML2_HTTP_ARTIFACT => Protocol::SAML2, 27 | self::SAML1_BROWSER_POST => Protocol::SAML1, 28 | self::SAML1_ARTIFACT1 => Protocol::SAML1, 29 | self::SHIB1_AUTHN_REQUEST => Protocol::SHIB1 30 | ); 31 | 32 | 33 | private static $_constants = null; 34 | 35 | private static function getConstants() { 36 | if (self::$_constants === null) { 37 | $ref = new \ReflectionClass('\AerialShip\LightSaml\Bindings'); 38 | self::$_constants = $ref->getConstants(); 39 | } 40 | return self::$_constants; 41 | } 42 | 43 | /** 44 | * @param string $binding 45 | * @return bool 46 | */ 47 | static function isValid($binding) { 48 | $result = in_array($binding, self::getConstants()); 49 | return $result; 50 | } 51 | 52 | 53 | static function validate($binding) { 54 | if (!self::isValid($binding)) { 55 | throw new InvalidBindingException($binding); 56 | } 57 | } 58 | 59 | 60 | /** 61 | * @param string $binding one of \AerialShip\LightSaml\Bindings 62 | * @return string one of \AerialShip\LightSaml\Protocol::* constants 63 | */ 64 | static function getBindingProtocol($binding) { 65 | $result = @self::$_binding2protocol[$binding]; 66 | return $result; 67 | } 68 | 69 | 70 | private function __construct() { } 71 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/ClaimTypes.php: -------------------------------------------------------------------------------- 1 | edSP = $edSP; 38 | $this->edIDP = $edIDP; 39 | $this->spMeta = $spMeta; 40 | $this->signature = $signature; 41 | } 42 | 43 | 44 | /** 45 | * @param Message $message 46 | * @return \AerialShip\LightSaml\Binding\Response 47 | */ 48 | abstract public function send(Message $message); 49 | 50 | 51 | /** 52 | * @param EntityDescriptor $edIDP 53 | */ 54 | public function setEdIDP($edIDP) { 55 | $this->edIDP = $edIDP; 56 | } 57 | 58 | /** 59 | * @return EntityDescriptor 60 | */ 61 | public function getEdIDP() { 62 | return $this->edIDP; 63 | } 64 | 65 | /** 66 | * @param EntityDescriptor $edSP 67 | */ 68 | public function setEdSP($edSP) { 69 | $this->edSP = $edSP; 70 | } 71 | 72 | /** 73 | * @return EntityDescriptor 74 | */ 75 | public function getEdSP() { 76 | return $this->edSP; 77 | } 78 | 79 | /** 80 | * @param Signature $signature 81 | */ 82 | public function setSigningProvider($signature) 83 | { 84 | $this->signature = $signature; 85 | } 86 | 87 | /** 88 | * @return Signature 89 | */ 90 | public function getSigningProvider() 91 | { 92 | return $this->signature; 93 | } 94 | 95 | 96 | /** 97 | * @return SpSsoDescriptor 98 | * @throws BuildRequestException 99 | */ 100 | protected function getSpSsoDescriptor() 101 | { 102 | $ed = $this->getEdSP(); 103 | if (!$ed) { 104 | throw new BuildRequestException('No SP EntityDescriptor set'); 105 | } 106 | $arr = $ed->getAllSpSsoDescriptors(); 107 | if (empty($arr)) { 108 | throw new BuildRequestException('SP EntityDescriptor has no SPSSODescriptor'); 109 | } 110 | if (count($arr)>1) { 111 | throw new BuildRequestException('SP EntityDescriptor has more then one SPSSODescriptor'); 112 | } 113 | $result = $arr[0]; 114 | return $result; 115 | } 116 | 117 | 118 | /** 119 | * @return IdpSsoDescriptor 120 | * @throws BuildRequestException 121 | */ 122 | protected function getIdpSsoDescriptor() 123 | { 124 | $ed = $this->getEdIDP(); 125 | if (!$ed) { 126 | throw new BuildRequestException('No IDP EntityDescriptor set'); 127 | } 128 | $arr = $ed->getAllIdpSsoDescriptors(); 129 | if (empty($arr)) { 130 | throw new BuildRequestException('IDP EntityDescriptor has no IDPSSODescriptor'); 131 | } 132 | if (count($arr)>1) { 133 | throw new BuildRequestException('IDP EntityDescriptor has more then one IDPSSODescriptor'); 134 | } 135 | $result = $arr[0]; 136 | return $result; 137 | } 138 | 139 | 140 | /** 141 | * @param \AerialShip\LightSaml\Model\Metadata\Service\AbstractService[] $services 142 | * @param string|null $binding 143 | * @return \AerialShip\LightSaml\Model\Metadata\Service\AbstractService|null 144 | */ 145 | protected function findServiceByBinding(array $services, $binding) 146 | { 147 | $result = null; 148 | if (!$binding) { 149 | $result = array_shift($services); 150 | } else { 151 | foreach ($services as $service) { 152 | if ($binding == $service->getBinding()) { 153 | $result = $service; 154 | break; 155 | } 156 | } 157 | } 158 | 159 | return $result; 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/AuthnRequestBuilder.php: -------------------------------------------------------------------------------- 1 | getEdSP(); 23 | 24 | $result->setID(Helper::generateID()); 25 | $result->setDestination($this->getDestination()); 26 | $result->setIssueInstant(time()); 27 | 28 | $asc = $this->getAssertionConsumerService(); 29 | $result->setAssertionConsumerServiceURL($asc->getLocation()); 30 | $result->setProtocolBinding($asc->getBinding()); 31 | 32 | $result->setIssuer($edSP->getEntityID()); 33 | 34 | if ($this->spMeta->getNameIdFormat()) { 35 | $result->setNameIdPolicyFormat($this->spMeta->getNameIdFormat()); 36 | } 37 | 38 | $result->setSignature($this->signature); 39 | 40 | return $result; 41 | } 42 | 43 | 44 | /** 45 | * @param Message $message 46 | * @return \AerialShip\LightSaml\Binding\Response 47 | */ 48 | public function send(Message $message) 49 | { 50 | $bindingType = $this->spMeta->getAuthnRequestBinding(); 51 | if ($bindingType) { 52 | $detector = new BindingDetector(); 53 | $binding = $detector->instantiate($bindingType); 54 | } else { 55 | $binding = new HttpRedirect(); 56 | } 57 | $result = $binding->send($message); 58 | return $result; 59 | } 60 | 61 | 62 | /** 63 | * @return AssertionConsumerService 64 | * @throws BuildRequestException 65 | */ 66 | protected function getAssertionConsumerService() 67 | { 68 | $sp = $this->getSpSsoDescriptor(); 69 | $arr = $sp->findAssertionConsumerServices(); 70 | if (empty($arr)) { 71 | throw new BuildRequestException('SPSSODescriptor does not have any AssertionConsumerService'); 72 | } 73 | $result = $this->findServiceByBinding($arr, $this->spMeta->getResponseBinding()); 74 | if (!$result) { 75 | throw new BuildRequestException('SPSSODescriptor does not have AssertionConsumerService with binding '.$this->spMeta->getResponseBinding()); 76 | } 77 | return $result; 78 | } 79 | 80 | /** 81 | * @return string 82 | * @throws \AerialShip\LightSaml\Error\BuildRequestException 83 | */ 84 | protected function getDestination() 85 | { 86 | $idp = $this->getIdpSsoDescriptor(); 87 | $arr = $idp->findSingleSignOnServices(); 88 | if (empty($arr)) { 89 | throw new BuildRequestException('IDPSSODescriptor does not have any SingleSignOnService'); 90 | } 91 | $result = $this->findServiceByBinding($arr, $this->spMeta->getAuthnRequestBinding()); 92 | if (!$result) { 93 | throw new BuildRequestException('IDPSSODescriptor does not have SingleSignOnService with binding '.$this->spMeta->getAuthnRequestBinding()); 94 | } 95 | 96 | return $result->getLocation(); 97 | } 98 | 99 | 100 | /** 101 | * @return string 102 | * @throws \AerialShip\LightSaml\Error\BuildRequestException 103 | */ 104 | protected function getProtocolBinding() 105 | { 106 | $result = $this->spMeta->getAuthnRequestBinding(); 107 | if (!$result) { 108 | $asc = $this->getAssertionConsumerService(); 109 | if ($asc) { 110 | $result = $asc->getBinding(); 111 | } 112 | } 113 | if (!$result) { 114 | throw new BuildRequestException('Unable to determine protocol binding'); 115 | } 116 | return $result; 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/GetSignedXmlInterface.php: -------------------------------------------------------------------------------- 1 | getIdpSsoDescriptor(); 19 | $result = null; 20 | if ($this->spMeta->getLogoutRequestBinding()) { 21 | $arr = $idp->findSingleLogoutServices($this->spMeta->getLogoutRequestBinding()); 22 | if ($arr) { 23 | $sso = array_shift($arr); 24 | $result = $sso->getLocation(); 25 | } 26 | } 27 | if (!$result) { 28 | $arr = $idp->findSingleLogoutServices(); 29 | /** @var SingleLogoutService $sso */ 30 | $sso = array_shift($arr); 31 | $result = $sso->getLocation(); 32 | } 33 | if (!$result) { 34 | throw new \LogicException('Unable to find IDP destination'); 35 | } 36 | return $result; 37 | } 38 | 39 | 40 | /** 41 | * @param string $nameIDValue 42 | * @param string|null $nameIDFormat 43 | * @param string|null $sessionIndex 44 | * @param string|null $reason 45 | * @return LogoutRequest 46 | */ 47 | public function build($nameIDValue, $nameIDFormat = null, $sessionIndex = null, $reason = null) 48 | { 49 | $result = new LogoutRequest(); 50 | $edSP = $this->getEdSP(); 51 | 52 | $result->setID(Helper::generateID()); 53 | $result->setDestination($this->getDestination()); 54 | $result->setIssueInstant(time()); 55 | if ($reason) { 56 | $result->setReason($reason); 57 | } 58 | if ($sessionIndex) { 59 | $result->setSessionIndex($sessionIndex); 60 | } 61 | 62 | $nameID = new NameID(); 63 | $nameID->setValue($nameIDValue); 64 | if ($nameIDFormat) { 65 | $nameID->setFormat($nameIDFormat); 66 | } 67 | $result->setNameID($nameID); 68 | 69 | $result->setIssuer($edSP->getEntityID()); 70 | return $result; 71 | } 72 | 73 | 74 | /** 75 | * @param Message $message 76 | * @return \AerialShip\LightSaml\Binding\RedirectResponse|\AerialShip\LightSaml\Binding\Response 77 | */ 78 | public function send(Message $message) 79 | { 80 | $bindingType = $this->spMeta->getLogoutRequestBinding(); 81 | if ($bindingType) { 82 | $detector = new BindingDetector(); 83 | $binding = $detector->instantiate($bindingType); 84 | } else { 85 | $binding = new HttpRedirect(); 86 | } 87 | $result = $binding->send($message); 88 | return $result; 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/SerializationContext.php: -------------------------------------------------------------------------------- 1 | document = $document ? $document : new \DOMDocument(); 14 | } 15 | 16 | 17 | /** 18 | * @param \DOMDocument $document 19 | */ 20 | public function setDocument($document) { 21 | $this->document = $document; 22 | } 23 | 24 | /** 25 | * @return \DOMDocument 26 | */ 27 | public function getDocument() { 28 | return $this->document; 29 | } 30 | 31 | 32 | 33 | 34 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/SpMeta.php: -------------------------------------------------------------------------------- 1 | nameIdFormat = $nameIdFormat; 33 | } 34 | 35 | 36 | /** 37 | * @return string 38 | */ 39 | public function getNameIdFormat() 40 | { 41 | return $this->nameIdFormat; 42 | } 43 | 44 | /** 45 | * @param string $authnRequestBinding 46 | */ 47 | public function setAuthnRequestBinding($authnRequestBinding) 48 | { 49 | $this->authnRequestBinding = $authnRequestBinding; 50 | } 51 | 52 | /** 53 | * @return string 54 | */ 55 | public function getAuthnRequestBinding() 56 | { 57 | return $this->authnRequestBinding; 58 | } 59 | 60 | /** 61 | * @param string $responseBinding 62 | */ 63 | public function setResponseBinding($responseBinding) 64 | { 65 | $this->responseBinding = $responseBinding; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function getResponseBinding() 72 | { 73 | return $this->responseBinding; 74 | } 75 | 76 | /** 77 | * @param string $logoutRequestBinding 78 | */ 79 | public function setLogoutRequestBinding($logoutRequestBinding) 80 | { 81 | $this->logoutRequestBinding = $logoutRequestBinding; 82 | } 83 | 84 | /** 85 | * @return string 86 | */ 87 | public function getLogoutRequestBinding() 88 | { 89 | return $this->logoutRequestBinding; 90 | } 91 | 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/XmlChildrenLoaderTrait.php: -------------------------------------------------------------------------------- 1 | firstChild; $node !== NULL; $node = $node->nextSibling) { 10 | if ($node instanceof \DOMElement) { 11 | $elementCallback($node); 12 | } 13 | } 14 | } 15 | 16 | protected function loadXmlChildren(\DOMElement $xml, array $node2ClassMap, \Closure $itemCallback) { 17 | $result = array(); 18 | $this->iterateChildrenElements($xml, function(\DOMElement $node) use (&$result, $node2ClassMap, $itemCallback) { 19 | $recognized = $this->doMapping($node, $node2ClassMap, $itemCallback); 20 | if (!$recognized) { 21 | $result[] = $node; 22 | } 23 | }); 24 | return $result; 25 | } 26 | 27 | 28 | /** 29 | * @param \DOMElement $node 30 | * @param array $node2ClassMap 31 | * @param callable $itemCallback 32 | * @return \DOMElement|null 33 | */ 34 | private function doMapping(\DOMElement $node, array $node2ClassMap, \Closure $itemCallback) { 35 | $recognized = false; 36 | foreach ($node2ClassMap as $meta) { 37 | if (!$meta) continue; 38 | $this->getNodeNameAndNamespaceFromMeta($meta, $nodeName, $nodeNS); 39 | if ($nodeName == $node->localName 40 | && (!$nodeNS || $nodeNS == $node->namespaceURI) 41 | ) { 42 | $obj = $this->getObjectFromMetaClass($meta, $node); 43 | $itemCallback($obj); 44 | $recognized = true; 45 | break; 46 | } 47 | } // foreach $node2ClassMap 48 | return $recognized; 49 | } 50 | 51 | 52 | private function getNodeNameAndNamespaceFromMeta($meta, &$nodeName, &$nodeNS) { 53 | if (!is_array($meta)) { 54 | throw new \InvalidArgumentException('Meta must be array'); 55 | } 56 | if (!isset($meta['node'])) { 57 | throw new \InvalidArgumentException('Missing node meta'); 58 | } 59 | $nodeName = null; 60 | $nodeNS = null; 61 | if (is_string($meta['node'])) { 62 | $nodeName = $meta['node']; 63 | } else if (is_array($meta['node'])) { 64 | $nodeName = @$meta['node']['name']; 65 | $nodeNS = @$meta['node']['ns']; 66 | } 67 | if (!$nodeName) { 68 | throw new \InvalidArgumentException('Missing node name meta'); 69 | } 70 | } 71 | 72 | /** 73 | * @param $meta 74 | * @param \DOMElement $node 75 | * @throws \InvalidArgumentException 76 | * @return LoadFromXmlInterface 77 | */ 78 | private function getObjectFromMetaClass($meta, \DOMElement $node) { 79 | $class = @$meta['class']; 80 | if (!$class) { 81 | throw new \InvalidArgumentException('Missing class meta'); 82 | } 83 | $obj = new $class(); 84 | if ($obj instanceof LoadFromXmlInterface) { 85 | $obj->loadFromXml($node); 86 | } else { 87 | throw new \InvalidArgumentException("Class $class must implement LoadFromXmlInterface"); 88 | } 89 | return $obj; 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Meta/XmlRequiredAttributesTrait.php: -------------------------------------------------------------------------------- 1 | hasAttribute($name)) { 13 | throw new InvalidXmlException('XML Element '.$element->localName.' missing required attribute '.$name); 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/Attribute.php: -------------------------------------------------------------------------------- 1 | name = $name; 35 | $this->values = $values; 36 | $this->friendlyName = $friendlyName; 37 | } 38 | 39 | 40 | /** 41 | * @param string $name 42 | */ 43 | public function setName($name) 44 | { 45 | $this->name = $name; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getName() 52 | { 53 | return $this->name; 54 | } 55 | 56 | /** 57 | * @param null|string $nameFormat 58 | * @return $this|Attribute 59 | */ 60 | public function setNameFormat($nameFormat) 61 | { 62 | $this->nameFormat = $nameFormat; 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return null|string 69 | */ 70 | public function getNameFormat() 71 | { 72 | return $this->nameFormat; 73 | } 74 | 75 | /** 76 | * @param string $friendlyName 77 | */ 78 | public function setFriendlyName($friendlyName) 79 | { 80 | $this->friendlyName = $friendlyName; 81 | } 82 | 83 | /** 84 | * @return string 85 | */ 86 | public function getFriendlyName() 87 | { 88 | return $this->friendlyName; 89 | } 90 | 91 | /** 92 | * @param string[] $values 93 | */ 94 | public function setValues(array $values) 95 | { 96 | $this->values = $values; 97 | } 98 | 99 | /** 100 | * @return string[] 101 | */ 102 | public function getValues() 103 | { 104 | return $this->values; 105 | } 106 | 107 | 108 | /** 109 | * @param string $value 110 | */ 111 | public function addValue($value) 112 | { 113 | $this->values[] = $value; 114 | } 115 | 116 | 117 | public function getFirstValue() 118 | { 119 | return $this->values[0]; 120 | } 121 | 122 | 123 | /** 124 | * @param \DOMNode $parent 125 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 126 | * @return \DOMElement 127 | */ 128 | public function getXml(\DOMNode $parent, SerializationContext $context) 129 | { 130 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:Attribute'); 131 | $parent->appendChild($result); 132 | 133 | $result->setAttribute('Name', $this->getName()); 134 | if ($this->getNameFormat()) { 135 | $result->setAttribute('NameFormat', $this->getNameFormat()); 136 | } 137 | if ($this->getFriendlyName()) { 138 | $result->setAttribute('FriendlyName', $this->getFriendlyName()); 139 | } 140 | 141 | foreach ($this->getValues() as $v) { 142 | $valueNode = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:AttributeValue', $v); 143 | $result->appendChild($valueNode); 144 | } 145 | 146 | return $result; 147 | } 148 | 149 | /** 150 | * @param \DOMElement $xml 151 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 152 | */ 153 | public function loadFromXml(\DOMElement $xml) 154 | { 155 | if ($xml->localName != 'Attribute' || $xml->namespaceURI != Protocol::NS_ASSERTION) { 156 | throw new InvalidXmlException('Expected Attribute element but got '.$xml->localName); 157 | } 158 | 159 | if (!$xml->hasAttribute('Name')) { 160 | throw new InvalidXmlException('Missing Attribute Name'); 161 | } 162 | $this->setName($xml->getAttribute('Name')); 163 | 164 | if ($xml->hasAttribute('NameFormat')) { 165 | $this->setNameFormat($xml->getAttribute('NameFormat')); 166 | } 167 | if ($xml->hasAttribute('FriendlyName')) { 168 | $this->setFriendlyName($xml->getAttribute('FriendlyName')); 169 | } 170 | 171 | for ($node = $xml->firstChild; $node !== NULL; $node = $node->nextSibling) { 172 | if ($node->localName != 'AttributeValue') { 173 | throw new InvalidXmlException('Expected AttributeValue but got '.$node->localName); 174 | } 175 | $this->addValue($node->textContent); 176 | } 177 | } 178 | 179 | 180 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/AuthnStatement.php: -------------------------------------------------------------------------------- 1 | authnContext = trim($authnContext); 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getAuthnContext() { 42 | return $this->authnContext; 43 | } 44 | 45 | /** 46 | * @param int|string $authnInstant 47 | * @throws \InvalidArgumentException 48 | */ 49 | public function setAuthnInstant($authnInstant) { 50 | if (is_string($authnInstant)) { 51 | $authnInstant = Helper::parseSAMLTime($authnInstant); 52 | } else if (!is_int($authnInstant) || $authnInstant < 1) { 53 | throw new \InvalidArgumentException('Invalid AuthnInstant'); 54 | } 55 | $this->authnInstant = $authnInstant; 56 | } 57 | 58 | /** 59 | * @return int 60 | */ 61 | public function getAuthnInstant() { 62 | return $this->authnInstant; 63 | } 64 | 65 | /** 66 | * @param string $sessionIndex 67 | */ 68 | public function setSessionIndex($sessionIndex) { 69 | $this->sessionIndex = $sessionIndex; 70 | } 71 | 72 | /** 73 | * @return string 74 | */ 75 | public function getSessionIndex() { 76 | return $this->sessionIndex; 77 | } 78 | 79 | 80 | 81 | protected function prepareForXml() { 82 | if (!$this->getAuthnInstant()) { 83 | $this->setAuthnInstant(time()); 84 | } 85 | } 86 | 87 | 88 | /** 89 | * @param \DOMNode $parent 90 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 91 | * @return \DOMElement 92 | */ 93 | function getXml(\DOMNode $parent, SerializationContext $context) { 94 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:AuthnStatement'); 95 | $parent->appendChild($result); 96 | 97 | $result->setAttribute('AuthnInstant', Helper::time2string($this->getAuthnInstant())); 98 | if ($this->getSessionIndex()) { 99 | $result->setAttribute('SessionIndex', $this->getSessionIndex()); 100 | } 101 | 102 | $authnContextNode = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:AuthnContext'); 103 | $result->appendChild($authnContextNode); 104 | $refNode = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:AuthnContextClassRef', $this->getAuthnContext()); 105 | $authnContextNode->appendChild($refNode); 106 | 107 | return $result; 108 | } 109 | 110 | /** 111 | * @param \DOMElement $xml 112 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 113 | */ 114 | function loadFromXml(\DOMElement $xml) { 115 | if ($xml->localName != 'AuthnStatement' || $xml->namespaceURI != Protocol::NS_ASSERTION) { 116 | throw new InvalidXmlException('Expected AuthnStatement element but got '.$xml->localName); 117 | } 118 | 119 | $this->checkRequiredAttributes($xml, array('AuthnInstant')); 120 | $this->setAuthnInstant($xml->getAttribute('AuthnInstant')); 121 | 122 | if ($xml->hasAttribute('SessionIndex')) { 123 | $this->setSessionIndex($xml->getAttribute('SessionIndex')); 124 | } 125 | 126 | $xpath = new \DOMXPath($xml->ownerDocument); 127 | $xpath->registerNamespace('saml', Protocol::NS_ASSERTION); 128 | $xpath->registerNamespace('samlp', Protocol::SAML2); 129 | 130 | $list = $xpath->query('./saml:AuthnContext/saml:AuthnContextClassRef', $xml); 131 | if ($list->length) { 132 | $this->setAuthnContext($list->item(0)->textContent); 133 | } 134 | } 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/NameID.php: -------------------------------------------------------------------------------- 1 | value = $value; 38 | $this->format = $format; 39 | } 40 | 41 | 42 | /** 43 | * @param string $value 44 | */ 45 | public function setValue($value) { 46 | $this->value = trim($value); 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getValue() { 53 | return $this->value; 54 | } 55 | 56 | /** 57 | * @param string $nameQualifier 58 | * @return $this 59 | */ 60 | public function setNameQualifier($nameQualifier){ 61 | $this->nameQualifier = $nameQualifier; 62 | return $this; 63 | } 64 | 65 | /** 66 | * @return string 67 | */ 68 | public function getNameQualifier(){ 69 | return $this->nameQualifier; 70 | } 71 | 72 | /** 73 | * @param string $sPNameQualifier 74 | * @return $this 75 | */ 76 | public function setSPNameQualifier($sPNameQualifier){ 77 | $this->spNameQualifier = $sPNameQualifier; 78 | return $this; 79 | } 80 | 81 | /** 82 | * @return string 83 | */ 84 | public function getSPNameQualifier(){ 85 | return $this->spNameQualifier; 86 | } 87 | 88 | /** 89 | * @param string $format 90 | * @return $this 91 | */ 92 | public function setFormat($format){ 93 | $this->format = trim($format); 94 | return $this; 95 | } 96 | 97 | /** 98 | * @return string 99 | */ 100 | public function getFormat(){ 101 | return $this->format; 102 | } 103 | 104 | /** 105 | * @param string $sPProvidedID 106 | * @return $this 107 | */ 108 | public function setSPProvidedID($sPProvidedID){ 109 | $this->spProvidedID = $sPProvidedID; 110 | return $this; 111 | } 112 | 113 | /** 114 | * @return string 115 | */ 116 | public function getSPProvidedID(){ 117 | return $this->spProvidedID; 118 | } 119 | 120 | 121 | 122 | 123 | /** 124 | * @param \DOMNode $parent 125 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 126 | * @return \DOMElement 127 | */ 128 | public function getXml(\DOMNode $parent, SerializationContext $context) 129 | { 130 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:NameID', $this->getValue()); 131 | 132 | $parent->appendChild($result); 133 | 134 | if($this->getSPNameQualifier()) { 135 | $result->setAttribute('SPNameQualifier', $this->getSPNameQualifier()); 136 | } 137 | if($this->getNameQualifier()) { 138 | $result->setAttribute('NameQualifier', $this->getNameQualifier()); 139 | } 140 | if($this->getSPProvidedID()) { 141 | $result->setAttribute('SPProvidedID', $this->getSPProvidedID()); 142 | } 143 | if($this->getFormat()) { 144 | $result->setAttribute('Format', $this->getFormat()); 145 | } 146 | 147 | return $result; 148 | } 149 | 150 | 151 | /** 152 | * @param \DOMElement $xml 153 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 154 | */ 155 | function loadFromXml(\DOMElement $xml) { 156 | if ($xml->localName != 'NameID') { 157 | throw new InvalidXmlException('Expected NameID element got '.$xml->localName); 158 | } 159 | 160 | if ($xml->hasAttribute('SPNameQualifier')) { 161 | $this->setSPNameQualifier($xml->getAttribute('SPNameQualifier')); 162 | } 163 | if ($xml->hasAttribute('NameQualifier')) { 164 | $this->setNameQualifier($xml->getAttribute('NameQualifier')); 165 | } 166 | if ($xml->hasAttribute('SPProvidedID')) { 167 | $this->setSPProvidedID($xml->getAttribute('SPProvidedID')); 168 | } 169 | if ($xml->hasAttribute('Format')) { 170 | $this->setFormat($xml->getAttribute('Format')); 171 | } 172 | $this->setValue(trim($xml->textContent)); 173 | } 174 | 175 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/Subject.php: -------------------------------------------------------------------------------- 1 | nameID = $nameID; 34 | } 35 | 36 | /** 37 | * @return NameID 38 | */ 39 | public function getNameID() 40 | { 41 | return $this->nameID; 42 | } 43 | 44 | /** 45 | * @param SubjectConfirmation $subjectConfirmation 46 | */ 47 | public function addSubjectConfirmation($subjectConfirmation) 48 | { 49 | $this->subjectConfirmations[] = $subjectConfirmation; 50 | } 51 | 52 | /** 53 | * @return SubjectConfirmation[] 54 | */ 55 | public function getSubjectConfirmations() 56 | { 57 | return $this->subjectConfirmations; 58 | } 59 | 60 | 61 | 62 | 63 | 64 | /** 65 | * @param \DOMNode $parent 66 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 67 | * @return \DOMElement 68 | */ 69 | public function getXml(\DOMNode $parent, SerializationContext $context) 70 | { 71 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:Subject'); 72 | $parent->appendChild($result); 73 | 74 | if ($this->getNameID()) { 75 | $this->getNameID()->getXml($result, $context); 76 | } 77 | 78 | foreach ($this->getSubjectConfirmations() as $sc) { 79 | $sc->getXml($result, $context); 80 | } 81 | 82 | return $result; 83 | } 84 | 85 | /** 86 | * @param \DOMElement $xml 87 | * @throws \LogicException 88 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 89 | */ 90 | public function loadFromXml(\DOMElement $xml) 91 | { 92 | if ($xml->localName != 'Subject' || $xml->namespaceURI != Protocol::NS_ASSERTION) { 93 | throw new InvalidXmlException('Expected Subject element but got '.$xml->localName); 94 | } 95 | 96 | $this->nameID = null; 97 | $this->subjectConfirmations = array(); 98 | 99 | $this->loadXmlChildren( 100 | $xml, 101 | array( 102 | array( 103 | 'node' => array('name'=>'NameID', 'ns'=>Protocol::NS_ASSERTION), 104 | 'class' => '\AerialShip\LightSaml\Model\Assertion\NameID' 105 | ), 106 | array( 107 | 'node' => array('name'=>'SubjectConfirmation', 'ns'=>Protocol::NS_ASSERTION), 108 | 'class' => '\AerialShip\LightSaml\Model\Assertion\SubjectConfirmation' 109 | ) 110 | ), 111 | function ($object) { 112 | $this->loadXmlCallback($object); 113 | } 114 | ); 115 | if (!$this->getSubjectConfirmations()) { 116 | throw new InvalidXmlException('Missing SubjectConfirmation element in Subject'); 117 | } 118 | } 119 | 120 | 121 | protected function loadXmlCallback($object) 122 | { 123 | if ($object instanceof NameID) { 124 | if ($this->getNameID()) { 125 | throw new InvalidXmlException('More than one NameID in Subject'); 126 | } 127 | $this->setNameID($object); 128 | } else if ($object instanceof SubjectConfirmation) { 129 | $this->addSubjectConfirmation($object); 130 | } else { 131 | throw new \LogicException('Unexpected type '.get_class($object)); 132 | } 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/SubjectConfirmation.php: -------------------------------------------------------------------------------- 1 | method = $method; 35 | } 36 | 37 | /** 38 | * @return string 39 | */ 40 | public function getMethod() { 41 | return $this->method; 42 | } 43 | 44 | /** 45 | * @param NameID $nameID 46 | */ 47 | public function setNameID($nameID) { 48 | $this->nameID = $nameID; 49 | } 50 | 51 | /** 52 | * @return NameID 53 | */ 54 | public function getNameID() { 55 | return $this->nameID; 56 | } 57 | 58 | /** 59 | * @param SubjectConfirmationData $data 60 | */ 61 | public function setData($data) { 62 | $this->data = $data; 63 | } 64 | 65 | /** 66 | * @return SubjectConfirmationData 67 | */ 68 | public function getData() { 69 | return $this->data; 70 | } 71 | 72 | 73 | 74 | 75 | 76 | 77 | protected function prepareForXml() { 78 | if (!$this->getMethod()) { 79 | throw new InvalidSubjectException('No SubjectConfirmation Method set'); 80 | } 81 | if (!$this->getData()) { 82 | throw new InvalidSubjectException('No SubjectConfirmationData set'); 83 | } 84 | } 85 | 86 | 87 | /** 88 | * @param \DOMNode $parent 89 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 90 | * @return \DOMElement 91 | */ 92 | function getXml(\DOMNode $parent, SerializationContext $context) { 93 | $this->prepareForXml(); 94 | 95 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:SubjectConfirmation'); 96 | $parent->appendChild($result); 97 | 98 | $result->setAttribute('Method', $this->getMethod()); 99 | 100 | if ($this->getNameID()) { 101 | $this->getNameID()->getXml($result, $context); 102 | } 103 | 104 | $this->getData()->getXml($result, $context); 105 | 106 | return $result; 107 | } 108 | 109 | /** 110 | * @param \DOMElement $xml 111 | * @throws \LogicException 112 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 113 | */ 114 | function loadFromXml(\DOMElement $xml) { 115 | if ($xml->localName != 'SubjectConfirmation' || $xml->namespaceURI != Protocol::NS_ASSERTION) { 116 | throw new InvalidXmlException('Expected Subject element but got '.$xml->localName); 117 | } 118 | 119 | if (!$xml->hasAttribute('Method')) { 120 | throw new InvalidXmlException('Missing Method attribute in SubjectConfirmation'); 121 | } 122 | $this->setMethod($xml->getAttribute('Method')); 123 | 124 | 125 | $this->nameID = null; 126 | $this->loadXmlChildren( 127 | $xml, 128 | array( 129 | array( 130 | 'node' => array('name'=>'NameID', 'ns'=>Protocol::NS_ASSERTION), 131 | 'class' => '\AerialShip\LightSaml\Model\Assertion\NameID' 132 | ), 133 | array( 134 | 'node' => array('name'=>'SubjectConfirmationData', 'ns'=>Protocol::NS_ASSERTION), 135 | 'class' => '\AerialShip\LightSaml\Model\Assertion\SubjectConfirmationData' 136 | ) 137 | ), 138 | function ($obj) { 139 | if ($obj instanceof NameID) { 140 | if ($this->getNameID()) { 141 | throw new InvalidXmlException('More than one NameID in SubjectConfirmation'); 142 | } 143 | $this->setNameID($obj); 144 | } else if ($obj instanceof SubjectConfirmationData) { 145 | $this->setData($obj); 146 | } else { 147 | throw new \LogicException('Unexpected type '.get_class($obj)); 148 | } 149 | } 150 | ); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Assertion/SubjectConfirmationData.php: -------------------------------------------------------------------------------- 1 | address = $address; 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function getAddress() { 45 | return $this->address; 46 | } 47 | 48 | /** 49 | * @param string $inResponseTo 50 | */ 51 | public function setInResponseTo($inResponseTo) { 52 | $this->inResponseTo = $inResponseTo; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getInResponseTo() { 59 | return $this->inResponseTo; 60 | } 61 | 62 | /** 63 | * @param int|string $notBefore 64 | * @throws \InvalidArgumentException 65 | */ 66 | public function setNotBefore($notBefore) { 67 | if (is_string($notBefore)) { 68 | $notBefore = Helper::parseSAMLTime($notBefore); 69 | } 70 | if (!is_int($notBefore) || $notBefore < 1) { 71 | throw new \InvalidArgumentException(); 72 | } 73 | $this->notBefore = $notBefore; 74 | } 75 | 76 | /** 77 | * @return int 78 | */ 79 | public function getNotBefore() { 80 | return $this->notBefore; 81 | } 82 | 83 | /** 84 | * @param int|string $notOnOrAfter 85 | * @throws \InvalidArgumentException 86 | */ 87 | public function setNotOnOrAfter($notOnOrAfter) { 88 | if (is_string($notOnOrAfter)) { 89 | $notOnOrAfter = Helper::parseSAMLTime($notOnOrAfter); 90 | } 91 | if (!is_int($notOnOrAfter) || $notOnOrAfter < 1) { 92 | throw new \InvalidArgumentException(); 93 | } 94 | $this->notOnOrAfter = $notOnOrAfter; 95 | } 96 | 97 | /** 98 | * @return int 99 | */ 100 | public function getNotOnOrAfter() { 101 | return $this->notOnOrAfter; 102 | } 103 | 104 | /** 105 | * @param string $recipient 106 | */ 107 | public function setRecipient($recipient) { 108 | $this->recipient = $recipient; 109 | } 110 | 111 | /** 112 | * @return string 113 | */ 114 | public function getRecipient() { 115 | return $this->recipient; 116 | } 117 | 118 | 119 | /** 120 | * @param \DOMNode $parent 121 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 122 | * @return \DOMElement 123 | */ 124 | function getXml(\DOMNode $parent, SerializationContext $context) { 125 | $result = $context->getDocument()->createElementNS(Protocol::NS_ASSERTION, 'saml:SubjectConfirmationData'); 126 | $parent->appendChild($result); 127 | 128 | if ($this->getNotBefore()) { 129 | $result->setAttribute('NotBefore', Helper::time2string($this->getNotBefore())); 130 | } 131 | if ($this->getNotOnOrAfter()) { 132 | $result->setAttribute('NotOnOrAfter', Helper::time2string($this->getNotOnOrAfter())); 133 | } 134 | 135 | foreach (array('Recipient', 'InResponseTo', 'Address') as $name) { 136 | $method = "get{$name}"; 137 | if ($this->$method()) { 138 | $result->setAttribute($name, $this->$method()); 139 | } 140 | } 141 | 142 | return $result; 143 | } 144 | 145 | /** 146 | * @param \DOMElement $xml 147 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 148 | */ 149 | function loadFromXml(\DOMElement $xml) { 150 | if ($xml->localName != 'SubjectConfirmationData' || $xml->namespaceURI != Protocol::NS_ASSERTION) { 151 | throw new InvalidXmlException('Expected SubjectConfirmationData element but got '.$xml->localName); 152 | } 153 | 154 | foreach (array('NotBefore', 'NotOnOrAfter', 'Recipient', 'InResponseTo', 'Address') as $name) { 155 | if ($xml->hasAttribute($name)) { 156 | $method = "set{$name}"; 157 | $this->$method($xml->getAttribute($name)); 158 | } 159 | } 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/IdpSsoDescriptor.php: -------------------------------------------------------------------------------- 1 | attributes = $attributes; 26 | return $this; 27 | } 28 | 29 | /** 30 | * @return \AerialShip\LightSaml\Model\Assertion\Attribute[] 31 | */ 32 | public function getAttributes() 33 | { 34 | return $this->attributes; 35 | } 36 | 37 | /** 38 | * @param Attribute $attribute 39 | */ 40 | public function addAttribute(Attribute $attribute) 41 | { 42 | $this->attributes[] = $attribute; 43 | } 44 | 45 | 46 | /** 47 | * @param AbstractService $service 48 | * @return SpSsoDescriptor 49 | * @throws \InvalidArgumentException 50 | */ 51 | public function addService(AbstractService $service) 52 | { 53 | $class = Helper::getClassNameOnly($service); 54 | if ($class != 'SingleLogoutService' && 55 | $class != 'SingleSignOnService' 56 | ) { 57 | throw new \InvalidArgumentException("Invalid service type $class for IDPSSODescriptor"); 58 | } 59 | return parent::addService($service); 60 | } 61 | 62 | 63 | /** 64 | * @return string 65 | */ 66 | public function getXmlNodeName() 67 | { 68 | return 'IDPSSODescriptor'; 69 | } 70 | 71 | 72 | /** 73 | * @param \DOMNode $parent 74 | * @param SerializationContext $context 75 | * @return \DOMElement 76 | */ 77 | public function getXml(\DOMNode $parent, SerializationContext $context) 78 | { 79 | $result = parent::getXml($parent, $context); 80 | 81 | if ($this->getAttributes()) { 82 | foreach ($this->getAttributes() as $attribute) { 83 | $attribute->getXml($result, $context); 84 | } 85 | } 86 | 87 | return $result; 88 | } 89 | 90 | 91 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/KeyDescriptor.php: -------------------------------------------------------------------------------- 1 | use = $use; 33 | $this->certificate = $certificate; 34 | } 35 | 36 | 37 | /** 38 | * @param string $use 39 | * @throws \InvalidArgumentException 40 | */ 41 | public function setUse($use) { 42 | $use = trim($use); 43 | if ($use != '' && $use != self::USE_ENCRYPTION && $use != self::USE_SIGNING) { 44 | throw new \InvalidArgumentException("Invalid use value: $use"); 45 | } 46 | $this->use = $use; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getUse() { 53 | return $this->use; 54 | } 55 | 56 | 57 | /** 58 | * @param X509Certificate $certificate 59 | */ 60 | public function setCertificate(X509Certificate $certificate) { 61 | $this->certificate = $certificate; 62 | } 63 | 64 | /** 65 | * @return X509Certificate 66 | */ 67 | public function getCertificate() { 68 | return $this->certificate; 69 | } 70 | 71 | 72 | /** 73 | * @param \DOMNode $parent 74 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 75 | * @return \DOMNode 76 | */ 77 | function getXml(\DOMNode $parent, SerializationContext $context) 78 | { 79 | $result = $context->getDocument()->createElementNS(Protocol::NS_METADATA, 'md:KeyDescriptor'); 80 | $parent->appendChild($result); 81 | if ($this->getUse()) { 82 | $result->setAttribute('use', $this->getUse()); 83 | } 84 | $keyInfo = $parent->ownerDocument->createElementNS(Protocol::NS_XMLDSIG, 'ds:KeyInfo'); 85 | $result->appendChild($keyInfo); 86 | $xData = $parent->ownerDocument->createElementNS(Protocol::NS_XMLDSIG, 'ds:X509Data'); 87 | $keyInfo->appendChild($xData); 88 | $xCert = $parent->ownerDocument->createElementNS(Protocol::NS_XMLDSIG, 'ds:X509Certificate'); 89 | $xData->appendChild($xCert); 90 | $xCert->nodeValue = $this->getCertificate()->getData(); 91 | return $result; 92 | } 93 | 94 | /** 95 | * @param \DOMElement $xml 96 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 97 | */ 98 | public function loadFromXml(\DOMElement $xml) 99 | { 100 | if ($xml->localName != 'KeyDescriptor' || $xml->namespaceURI != Protocol::NS_METADATA) { 101 | throw new InvalidXmlException('Expected KeyDescriptor element and '.Protocol::NS_METADATA.' namespace but got '.$xml->localName); 102 | } 103 | 104 | $this->setUse($xml->getAttribute('use')); 105 | 106 | $xpath = new \DOMXPath($xml instanceof \DOMDocument ? $xml : $xml->ownerDocument); 107 | $xpath->registerNamespace('ds', \XMLSecurityDSig::XMLDSIGNS); 108 | 109 | $list = $xpath->query('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', $xml); 110 | if ($list->length != 1) { 111 | throw new InvalidXmlException("Missing X509Certificate node"); 112 | } 113 | 114 | /** @var $x509CertificateNode \DOMElement */ 115 | $x509CertificateNode = $list->item(0); 116 | $certificateData = trim($x509CertificateNode->nodeValue); 117 | if (!$certificateData) { 118 | throw new InvalidXmlException("Missing certificate data"); 119 | } 120 | 121 | $this->certificate = new X509Certificate(); 122 | $this->certificate->setData($certificateData); 123 | } 124 | 125 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/NameIDFormat.php: -------------------------------------------------------------------------------- 1 | value = $value; 24 | } 25 | 26 | /** 27 | * @param string $value 28 | * @throws \InvalidArgumentException 29 | * @return $this|NameIDFormat 30 | */ 31 | public function setValue($value) 32 | { 33 | $value = trim($value); 34 | if ($value && false == NameIDPolicy::isValid($value)) { 35 | throw new \InvalidArgumentException(sprintf("Invalid NameIDFormat '%s'", $value)); 36 | } 37 | $this->value = $value; 38 | 39 | return $this; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getValue() 46 | { 47 | return $this->value; 48 | } 49 | 50 | 51 | 52 | /** 53 | * @param \DOMNode $parent 54 | * @param SerializationContext $context 55 | * @return \DOMElement 56 | */ 57 | public function getXml(\DOMNode $parent, SerializationContext $context) 58 | { 59 | $result = $context->getDocument()->createElementNS(Protocol::NS_METADATA, 'md:NameIDFormat'); 60 | $parent->appendChild($result); 61 | 62 | $result->nodeValue = $this->value; 63 | 64 | return $result; 65 | } 66 | 67 | /** 68 | * @param \DOMElement $xml 69 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 70 | * @return void 71 | */ 72 | public function loadFromXml(\DOMElement $xml) 73 | { 74 | if ($xml->localName != 'NameIDFormat' || $xml->namespaceURI != Protocol::NS_METADATA) { 75 | throw new InvalidXmlException('Expected NameIDFormat element and '.Protocol::NS_METADATA.' namespace but got '.$xml->localName); 76 | } 77 | 78 | $this->setValue($xml->nodeValue); 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/Service/AbstractService.php: -------------------------------------------------------------------------------- 1 | setBinding($binding); 27 | } 28 | if ($location !== null) { 29 | $this->setLocation($location); 30 | } 31 | } 32 | 33 | 34 | 35 | /** 36 | * @param string $binding 37 | */ 38 | public function setBinding($binding) { 39 | Bindings::validate($binding); 40 | $this->binding = $binding; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getBinding() { 47 | return $this->binding; 48 | } 49 | 50 | /** 51 | * @param string $location 52 | */ 53 | public function setLocation($location) { 54 | $this->location = $location; 55 | } 56 | 57 | /** 58 | * @return string 59 | */ 60 | public function getLocation() { 61 | return $this->location; 62 | } 63 | 64 | 65 | 66 | abstract protected function getXmlNodeName(); 67 | 68 | 69 | /** 70 | * @param \DOMNode $parent 71 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 72 | * @return \DOMElement 73 | */ 74 | function getXml(\DOMNode $parent, SerializationContext $context) { 75 | $result = $context->getDocument()->createElementNS(Protocol::NS_METADATA, 'md:'.$this->getXmlNodeName()); 76 | $parent->appendChild($result); 77 | $result->setAttribute('Binding', $this->getBinding()); 78 | $result->setAttribute('Location', $this->getLocation()); 79 | return $result; 80 | } 81 | 82 | 83 | /** 84 | * @param \DOMElement $xml 85 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 86 | */ 87 | function loadFromXml(\DOMElement $xml) { 88 | $name = $this->getXmlNodeName(); 89 | if ($xml->localName != $name || $xml->namespaceURI != Protocol::NS_METADATA) { 90 | throw new InvalidXmlException("Expected $name element and ".Protocol::NS_METADATA.' namespace but got '.$xml->localName); 91 | } 92 | if (!$xml->hasAttribute('Binding')) { 93 | throw new InvalidXmlException("Missing Binding attribute"); 94 | } 95 | if (!$xml->hasAttribute('Location')) { 96 | throw new InvalidXmlException("Missing Location attribute"); 97 | } 98 | $this->setBinding($xml->getAttribute('Binding')); 99 | $this->setLocation($xml->getAttribute('Location')); 100 | } 101 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/Service/AssertionConsumerService.php: -------------------------------------------------------------------------------- 1 | setIndex($index); 19 | } 20 | } 21 | 22 | 23 | /** 24 | * @param int $index 25 | * @throws \InvalidArgumentException 26 | */ 27 | public function setIndex($index) { 28 | $v = intval($index); 29 | if ($v != $index) { 30 | throw new \InvalidArgumentException("Expected int got $index"); 31 | } 32 | $this->index = $index; 33 | } 34 | 35 | /** 36 | * @return int 37 | */ 38 | public function getIndex() { 39 | return $this->index; 40 | } 41 | 42 | 43 | protected function getXmlNodeName() { 44 | return 'AssertionConsumerService'; 45 | } 46 | 47 | /** 48 | * @param \DOMNode $parent 49 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 50 | * @return \DOMElement 51 | */ 52 | function getXml(\DOMNode $parent, SerializationContext $context) { 53 | $result = $result = parent::getXml($parent, $context); 54 | $result->setAttribute('index', $this->getIndex()); 55 | return $result; 56 | } 57 | 58 | /** 59 | * @param \DOMElement $xml 60 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 61 | */ 62 | function loadFromXml(\DOMElement $xml) { 63 | parent::loadFromXml($xml); 64 | if (!$xml->hasAttribute('index')) { 65 | throw new InvalidXmlException("Missing index attribute"); 66 | } 67 | $this->setIndex($xml->getAttribute('index')); 68 | } 69 | 70 | 71 | 72 | 73 | 74 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Metadata/Service/SingleLogoutService.php: -------------------------------------------------------------------------------- 1 | wantAssertionsSigned = (bool)$wantAssertionsSigned; 21 | } 22 | 23 | /** 24 | * @return boolean 25 | */ 26 | public function getWantAssertionsSigned() { 27 | return $this->wantAssertionsSigned; 28 | } 29 | 30 | 31 | 32 | 33 | 34 | 35 | public function addService(AbstractService $service) { 36 | $class = Helper::getClassNameOnly($service); 37 | if ($class != 'SingleLogoutService' && 38 | $class != 'AssertionConsumerService' 39 | ) { 40 | throw new \InvalidArgumentException("Invalid service type $class for SPSSODescriptor"); 41 | } 42 | return parent::addService($service); 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getXmlNodeName() { 49 | return 'SPSSODescriptor'; 50 | } 51 | 52 | 53 | /** 54 | * @param \DOMNode $parent 55 | * @param SerializationContext $context 56 | * @return \DOMElement 57 | */ 58 | function getXml(\DOMNode $parent, SerializationContext $context) { 59 | $result = parent::getXml($parent, $context); 60 | $result->setAttribute('WantAssertionsSigned', $this->getWantAssertionsSigned() ? 'true' : 'false'); 61 | return $result; 62 | } 63 | 64 | 65 | /** 66 | * @param \DOMElement $xml 67 | */ 68 | function loadFromXml(\DOMElement $xml) { 69 | parent::loadFromXml($xml); 70 | if ($xml->hasAttribute('WantAssertionsSigned')) { 71 | $this->setWantAssertionsSigned($xml->getAttribute('WantAssertionsSigned')); 72 | } 73 | } 74 | 75 | 76 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Protocol/AbstractRequest.php: -------------------------------------------------------------------------------- 1 | notOnOrAfter = $notOnOrAfter; 56 | return $this; 57 | } 58 | 59 | /** 60 | * @return int 61 | */ 62 | public function getNotOnOrAfter(){ 63 | return $this->notOnOrAfter; 64 | } 65 | 66 | /** 67 | * An indication of the reason for the logout 68 | * @param string $reason 69 | * @return LogoutRequest 70 | */ 71 | public function setReason($reason){ 72 | $this->reason = trim($reason); 73 | return $this; 74 | } 75 | 76 | public function getReason(){ 77 | return $this->reason; 78 | } 79 | 80 | /** 81 | * @param NameID $nameId 82 | * @return LogoutRequest 83 | */ 84 | public function setNameID(NameID $nameId){ 85 | $this->nameID = $nameId; 86 | return $this; 87 | } 88 | 89 | /** 90 | * @return null|NameID 91 | */ 92 | public function getNameID(){ 93 | return $this->nameID; 94 | } 95 | 96 | /** 97 | * @param string $sessionIndex 98 | * @return LogoutRequest 99 | */ 100 | public function setSessionIndex($sessionIndex){ 101 | $this->sessionIndex = $sessionIndex; 102 | return $this; 103 | } 104 | 105 | /** 106 | * @return null|string 107 | */ 108 | public function getSessionIndex(){ 109 | return $this->sessionIndex; 110 | } 111 | 112 | 113 | 114 | function getXml(\DOMNode $parent, SerializationContext $context) 115 | { 116 | $result = parent::getXml($parent, $context); 117 | 118 | if ($this->getNotOnOrAfter()) { 119 | $result->setAttribute('NotOnOrAfter', Helper::time2string($this->getNotOnOrAfter())); 120 | } 121 | if ($this->getReason()) { 122 | $result->setAttribute('Reason', $this->getReason()); 123 | } 124 | if ($this->getNameID()) { 125 | $result->appendChild($this->getNameID()->getXml($parent, $context)); 126 | } 127 | if ($this->getSessionIndex()) { 128 | $sessionIndex = $context->getDocument()->createElementNS(Protocol::SAML2, 'samlp:SessionIndex', $this->getSessionIndex()); 129 | $result->appendChild($sessionIndex); 130 | } 131 | 132 | return $result; 133 | } 134 | 135 | /** 136 | * @param \DOMElement $xml 137 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 138 | */ 139 | function loadFromXml(\DOMElement $xml) 140 | { 141 | parent::loadFromXml($xml); 142 | 143 | if ($xml->hasAttribute('Reason')) { 144 | $this->setReason($xml->getAttribute('Reason')); 145 | } 146 | if ($xml->hasAttribute('NotOnOrAfter')) { 147 | $this->setNotOnOrAfter($xml->getAttribute('NotOnOrAfter')); 148 | } 149 | 150 | $signatureNode = null; 151 | $this->iterateChildrenElements($xml, function(\DOMElement $node) use (&$signatureNode) { 152 | if ($node->localName == 'NameID') { 153 | $nameID = new NameID(); 154 | $nameID->loadFromXml($node); 155 | $this->setNameID($nameID); 156 | } 157 | if ($node->localName == 'SessionIndex') { 158 | $this->setSessionIndex($node->textContent); 159 | } 160 | 161 | if ($node->localName == 'Signature' && $node->namespaceURI == Protocol::NS_XMLDSIG) { 162 | $signatureNode = $node; 163 | } 164 | }); 165 | 166 | if (null !== $signatureNode) { 167 | $signature = new SignatureXmlValidator; 168 | $signature->loadFromXml($signatureNode); 169 | $this->setSignature($signature); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Protocol/LogoutResponse.php: -------------------------------------------------------------------------------- 1 | assertions; 39 | } 40 | 41 | public function addAssertion(Assertion $assertion) { 42 | $this->assertions[] = $assertion; 43 | } 44 | 45 | 46 | 47 | 48 | protected function prepareForXml() { 49 | parent::prepareForXml(); 50 | if (!$this->getAllAssertions()) { 51 | throw new InvalidResponseException('Missing Assertions'); 52 | } 53 | } 54 | 55 | 56 | /** 57 | * @param \DOMNode $parent 58 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 59 | * @return \DOMElement 60 | */ 61 | function getXml(\DOMNode $parent, SerializationContext $context) { 62 | $result = parent::getXml($parent, $context); 63 | foreach ($this->getAllAssertions() as $assertion) { 64 | $assertion->getXml($result, $context); 65 | } 66 | return $result; 67 | } 68 | 69 | 70 | /** 71 | * @param \DOMElement $xml 72 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 73 | */ 74 | function loadFromXml(\DOMElement $xml) { 75 | parent::loadFromXml($xml); 76 | $this->iterateChildrenElements($xml, function(\DOMElement $node) { 77 | if ($node->localName == 'Assertion' && $node->namespaceURI == Protocol::NS_ASSERTION) { 78 | $assertion = new Assertion(); 79 | $assertion->loadFromXml($node); 80 | $this->addAssertion($assertion); 81 | } 82 | }); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Protocol/Status.php: -------------------------------------------------------------------------------- 1 | statusCode = $statusCode; 31 | $this->message = $message; 32 | } 33 | 34 | /** 35 | * @param \AerialShip\LightSaml\Model\Protocol\StatusCode $statusCode 36 | */ 37 | public function setStatusCode($statusCode) { 38 | $this->statusCode = $statusCode; 39 | } 40 | 41 | /** 42 | * @return \AerialShip\LightSaml\Model\Protocol\StatusCode 43 | */ 44 | public function getStatusCode() { 45 | return $this->statusCode; 46 | } 47 | 48 | /** 49 | * @param string $message 50 | */ 51 | public function setMessage($message) { 52 | $this->message = $message; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getMessage() { 59 | return $this->message; 60 | } 61 | 62 | 63 | 64 | public function isSuccess() { 65 | $result = $this->getStatusCode() && $this->getStatusCode()->getValue() == Protocol::STATUS_SUCCESS; 66 | return $result; 67 | } 68 | 69 | 70 | public function setSuccess() { 71 | $this->setStatusCode(new StatusCode()); 72 | $this->getStatusCode()->setValue(Protocol::STATUS_SUCCESS); 73 | } 74 | 75 | 76 | 77 | protected function prepareForXml() { 78 | if (!$this->getStatusCode()) { 79 | throw new InvalidXmlException('StatusCode not set'); 80 | } 81 | } 82 | 83 | 84 | /** 85 | * @param \DOMNode $parent 86 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 87 | * @return \DOMElement 88 | */ 89 | function getXml(\DOMNode $parent, SerializationContext $context) { 90 | $this->prepareForXml(); 91 | 92 | $result = $context->getDocument()->createElementNS(Protocol::SAML2, 'samlp:Status'); 93 | $parent->appendChild($result); 94 | 95 | $result->appendChild($this->getStatusCode()->getXml($result, $context)); 96 | 97 | if ($this->getMessage()) { 98 | $statusMessageNode = $context->getDocument()->createElementNS(Protocol::SAML2, 'samlp:StatusMessage', $this->getMessage()); 99 | $result->appendChild($statusMessageNode); 100 | } 101 | 102 | return $result; 103 | } 104 | 105 | 106 | /** 107 | * @param \DOMElement $xml 108 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 109 | */ 110 | function loadFromXml(\DOMElement $xml) { 111 | if ($xml->localName != 'Status' || $xml->namespaceURI != Protocol::SAML2) { 112 | throw new InvalidXmlException('Expected Status element but got '.$xml->localName); 113 | } 114 | 115 | $this->iterateChildrenElements($xml, function(\DOMElement $node) { 116 | if ($node->localName == 'StatusCode' && $node->namespaceURI == Protocol::SAML2) { 117 | $statusCode = new StatusCode(); 118 | $statusCode->loadFromXml($node); 119 | $this->setStatusCode($statusCode); 120 | } else if ($node->localName == 'StatusMessage' && $node->namespaceURI == Protocol::SAML2) { 121 | $this->setMessage($node->textContent); 122 | } 123 | }); 124 | 125 | if (!$this->getStatusCode()) { 126 | throw new InvalidXmlException('Missing StatusCode node'); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Protocol/StatusCode.php: -------------------------------------------------------------------------------- 1 | value = $value; 29 | } 30 | 31 | /** 32 | * @param string $value 33 | */ 34 | public function setValue($value) { 35 | $this->value = $value; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getValue() { 42 | return $this->value; 43 | } 44 | 45 | /** 46 | * @param \AerialShip\LightSaml\Model\Protocol\StatusCode|null $child 47 | */ 48 | public function setChild($child) { 49 | $this->child = $child; 50 | } 51 | 52 | /** 53 | * @return \AerialShip\LightSaml\Model\Protocol\StatusCode|null 54 | */ 55 | public function getChild() { 56 | return $this->child; 57 | } 58 | 59 | 60 | 61 | protected function prepareForXml() { 62 | if (!$this->getValue()) { 63 | throw new InvalidXmlException('StatusCode value not set'); 64 | } 65 | } 66 | 67 | 68 | /** 69 | * @param \DOMNode $parent 70 | * @param SerializationContext $context 71 | * @return \DOMElement 72 | */ 73 | function getXml(\DOMNode $parent, SerializationContext $context) { 74 | $this->prepareForXml(); 75 | 76 | $result = $context->getDocument()->createElementNS(Protocol::SAML2, 'samlp:StatusCode'); 77 | $result->setAttribute('Value', $this->getValue()); 78 | 79 | if ($this->getChild()) { 80 | $this->getChild()->getXml($result, $context); 81 | } 82 | 83 | return $result; 84 | } 85 | 86 | /** 87 | * @param \DOMElement $xml 88 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 89 | * @return void 90 | */ 91 | function loadFromXml(\DOMElement $xml) { 92 | if ($xml->localName != 'StatusCode' || $xml->namespaceURI != Protocol::SAML2) { 93 | throw new InvalidXmlException('Expected StatusCode element but got '.$xml->localName); 94 | } 95 | 96 | if (!$xml->hasAttribute('Value')) { 97 | throw new InvalidXmlException('Required attribute StatusCode Value missing'); 98 | } 99 | $this->setValue($xml->getAttribute('Value')); 100 | 101 | $this->iterateChildrenElements($xml, function(\DOMElement $node) { 102 | if ($node->localName == 'StatusCode' && $node->namespaceURI == Protocol::SAML2) { 103 | $this->setChild(new StatusCode()); 104 | $this->getChild()->loadFromXml($node); 105 | } else { 106 | throw new InvalidXmlException('Unknown element '.$node->localName); 107 | } 108 | }); 109 | } 110 | 111 | 112 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/Protocol/StatusResponse.php: -------------------------------------------------------------------------------- 1 | inResponseTo = $inResponseTo; 26 | } 27 | 28 | /** 29 | * @return string 30 | */ 31 | public function getInResponseTo() { 32 | return $this->inResponseTo; 33 | } 34 | 35 | 36 | /** 37 | * @param \AerialShip\LightSaml\Model\Protocol\Status $status 38 | */ 39 | public function setStatus($status) { 40 | $this->status = $status; 41 | } 42 | 43 | /** 44 | * @return \AerialShip\LightSaml\Model\Protocol\Status 45 | */ 46 | public function getStatus() { 47 | return $this->status; 48 | } 49 | 50 | 51 | 52 | 53 | 54 | protected function prepareForXml() { 55 | parent::prepareForXml(); 56 | if (!$this->getStatus()) { 57 | throw new InvalidResponseException('Missing Status'); 58 | } 59 | } 60 | 61 | 62 | /** 63 | * @param \DOMNode $parent 64 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 65 | * @return \DOMElement 66 | */ 67 | function getXml(\DOMNode $parent, SerializationContext $context) { 68 | $result = parent::getXml($parent, $context); 69 | 70 | if ($this->getInResponseTo()) { 71 | $result->setAttribute('InResponseTo', $this->getInResponseTo()); 72 | } 73 | $this->getStatus()->getXml($result, $context); 74 | return $result; 75 | } 76 | 77 | /** 78 | * @param \DOMElement $xml 79 | * @throws \AerialShip\LightSaml\Error\InvalidXmlException 80 | */ 81 | function loadFromXml(\DOMElement $xml) { 82 | parent::loadFromXml($xml); 83 | 84 | if ($xml->hasAttribute('InResponseTo')) { 85 | $this->setInResponseTo($xml->getAttribute('InResponseTo')); 86 | } 87 | $this->iterateChildrenElements($xml, function(\DOMElement $node) { 88 | if ($node->localName == 'Status' && $node->namespaceURI == Protocol::SAML2) { 89 | $this->setStatus(new Status()); 90 | $this->getStatus()->loadFromXml($node); 91 | } 92 | }); 93 | if (!$this->getStatus()) { 94 | throw new InvalidXmlException('Missing Status element'); 95 | } 96 | } 97 | 98 | 99 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/XmlDSig/Signature.php: -------------------------------------------------------------------------------- 1 | canonicalMethod; 28 | } 29 | 30 | /** 31 | * @param string $canonicalMethod 32 | */ 33 | public function setCanonicalMethod($canonicalMethod) { 34 | $this->canonicalMethod = $canonicalMethod; 35 | } 36 | 37 | /** 38 | * @param \XMLSecurityKey $key 39 | */ 40 | public function setXmlSecurityKey(\XMLSecurityKey $key) { 41 | $this->xmlSecurityKey = $key; 42 | } 43 | 44 | /** 45 | * @return \XMLSecurityKey 46 | */ 47 | public function getXmlSecurityKey() { 48 | return $this->xmlSecurityKey; 49 | } 50 | 51 | /** 52 | * @param \AerialShip\LightSaml\Security\X509Certificate $certificate 53 | */ 54 | public function setCertificate(X509Certificate $certificate) { 55 | $this->certificate = $certificate; 56 | } 57 | 58 | /** 59 | * @return \AerialShip\LightSaml\Security\X509Certificate 60 | */ 61 | public function getCertificate() { 62 | return $this->certificate; 63 | } 64 | 65 | 66 | /** 67 | * @param \DOMNode $parent 68 | * @param \AerialShip\LightSaml\Meta\SerializationContext $context 69 | * @return \DOMNode 70 | */ 71 | function getXml(\DOMNode $parent, SerializationContext $context) { 72 | $objXMLSecDSig = new \XMLSecurityDSig(); 73 | $objXMLSecDSig->setCanonicalMethod($this->getCanonicalMethod()); 74 | $key = $this->getXmlSecurityKey(); 75 | switch ($key->type) { 76 | case \XMLSecurityKey::RSA_SHA256: 77 | $type = \XMLSecurityDSig::SHA256; 78 | break; 79 | case \XMLSecurityKey::RSA_SHA384: 80 | $type = \XMLSecurityDSig::SHA384; 81 | break; 82 | case \XMLSecurityKey::RSA_SHA512: 83 | $type = \XMLSecurityDSig::SHA512; 84 | break; 85 | default: 86 | $type = \XMLSecurityDSig::SHA1; 87 | } 88 | 89 | $objXMLSecDSig->addReferenceList( 90 | array($parent), 91 | $type, 92 | array(Protocol::XMLSEC_TRANSFORM_ALGORITHM_ENVELOPED_SIGNATURE, \XMLSecurityDSig::EXC_C14N), 93 | array('id_name' => $this->getIDName(), 'overwrite' => FALSE) 94 | ); 95 | 96 | $objXMLSecDSig->sign($key); 97 | $objXMLSecDSig->add509Cert($this->getCertificate()->getData(), false, false); 98 | $firstChild = $parent->hasChildNodes() ? $parent->firstChild : null; 99 | if ($firstChild && $firstChild->localName == 'Issuer') { 100 | // The signature node should come after the issuer node 101 | $firstChild = $firstChild->nextSibling; 102 | } 103 | $objXMLSecDSig->insertSignature($parent, $firstChild); 104 | } 105 | 106 | 107 | 108 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/XmlDSig/SignatureStringValidator.php: -------------------------------------------------------------------------------- 1 | signature = $signature; 25 | $this->algorithm = $algorithm; 26 | $this->data = $data; 27 | } 28 | 29 | 30 | 31 | /** 32 | * @param string $algorithm 33 | */ 34 | public function setAlgorithm($algorithm) 35 | { 36 | $this->algorithm = $algorithm; 37 | } 38 | 39 | /** 40 | * @return string 41 | */ 42 | public function getAlgorithm() 43 | { 44 | return $this->algorithm; 45 | } 46 | 47 | /** 48 | * @param string $data 49 | */ 50 | public function setData($data) 51 | { 52 | $this->data = $data; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getData() 59 | { 60 | return $this->data; 61 | } 62 | 63 | /** 64 | * @param string $signature 65 | */ 66 | public function setSignature($signature) 67 | { 68 | $this->signature = $signature; 69 | } 70 | 71 | /** 72 | * @return string 73 | */ 74 | public function getSignature() 75 | { 76 | return $this->signature; 77 | } 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | /** 86 | * @param \XMLSecurityKey $key 87 | * @return bool True if validated, False if validation was not performed 88 | * @throws \AerialShip\LightSaml\Error\SecurityException If validation fails 89 | */ 90 | public function validate(\XMLSecurityKey $key) 91 | { 92 | if ($this->getSignature() == null) { 93 | return false; 94 | } 95 | 96 | if ($key->type !== \XMLSecurityKey::RSA_SHA1) { 97 | throw new SecurityException('Invalid key type for validating signature on query string'); 98 | } 99 | if ($key->type !== $this->getAlgorithm()) { 100 | $key = KeyHelper::castKey($key, $this->getAlgorithm()); 101 | } 102 | 103 | $signature = base64_decode($this->getSignature()); 104 | if (!$key->verifySignature($this->getData(), $signature)) { 105 | throw new SecurityException('Unable to validate signature on query string'); 106 | } 107 | 108 | return true; 109 | } 110 | 111 | 112 | 113 | /** 114 | * @param \XMLSecurityKey[] $keys 115 | * @throws \LogicException 116 | * @throws \InvalidArgumentException If some element of $keys array is not \XMLSecurityKey 117 | * @throws \AerialShip\LightSaml\Error\SecurityException If validation fails 118 | * @throws \Exception 119 | * @throws null 120 | * @return bool True if validated, False if validation was not performed 121 | */ 122 | public function validateMulti(array $keys) 123 | { 124 | $lastException = null; 125 | 126 | foreach ($keys as $key) { 127 | if (!$key instanceof \XMLSecurityKey) { 128 | throw new \InvalidArgumentException('Expected XMLSecurityKey but got '.get_class($key)); 129 | } 130 | 131 | try { 132 | $result = $this->validate($key); 133 | 134 | if ($result === false) { 135 | return false; 136 | } 137 | 138 | return true; 139 | 140 | } catch (SecurityException $ex) { 141 | $lastException = $ex; 142 | } 143 | } 144 | 145 | if ($lastException) { 146 | throw $lastException; 147 | } else { 148 | throw new \LogicException('Should not get here???'); 149 | } 150 | } 151 | 152 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Model/XmlDSig/SignatureValidatorInterface.php: -------------------------------------------------------------------------------- 1 | getConstants(); 25 | } 26 | return self::$_constants; 27 | } 28 | 29 | 30 | static function isValid($value) { 31 | $result = in_array($value, self::getConstants()); 32 | return $result; 33 | } 34 | 35 | 36 | private function __construct() { } 37 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Protocol.php: -------------------------------------------------------------------------------- 1 | 'private')); 20 | $result->passphrase = $passphrase; 21 | $result->loadKey($key, $isFile, $isCert); 22 | 23 | return $result; 24 | } 25 | 26 | /** 27 | * @param X509Certificate $certificate 28 | * @return \XMLSecurityKey 29 | */ 30 | static function createPublicKey(X509Certificate $certificate) 31 | { 32 | $key = new \XMLSecurityKey(\XMLSecurityKey::RSA_SHA1, array('type'=>'public')); 33 | $key->loadKey($certificate->toPem(), false, true); 34 | 35 | return $key; 36 | } 37 | 38 | 39 | /** 40 | * @param \XMLSecurityKey $key 41 | * @param string $algorithm 42 | * @throws \AerialShip\LightSaml\Error\SecurityException 43 | * @throws \InvalidArgumentException 44 | * @return \XMLSecurityKey 45 | */ 46 | static function castKey(\XMLSecurityKey $key, $algorithm) 47 | { 48 | if (!is_string($algorithm)) { 49 | throw new \InvalidArgumentException('Algorithm must be string'); 50 | } 51 | 52 | // do nothing if algorithm is already the type of the key 53 | if ($key->type === $algorithm) { 54 | return $key; 55 | } 56 | 57 | $keyInfo = openssl_pkey_get_details($key->key); 58 | if ($keyInfo === FALSE) { 59 | throw new SecurityException('Unable to get key details from XMLSecurityKey.'); 60 | } 61 | if (!isset($keyInfo['key'])) { 62 | throw new SecurityException('Missing key in public key details.'); 63 | } 64 | 65 | $newKey = new \XMLSecurityKey($algorithm, array('type'=>'public')); 66 | $newKey->loadKey($keyInfo['key']); 67 | 68 | return $newKey; 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Security/X509Certificate.php: -------------------------------------------------------------------------------- 1 | data = preg_replace('/\s+/', '', $data); 19 | } 20 | 21 | /** 22 | * @return string 23 | */ 24 | public function getData() { 25 | return $this->data; 26 | } 27 | 28 | 29 | 30 | 31 | function loadPem($data) { 32 | $pattern = '/^-----BEGIN CERTIFICATE-----([^-]*)^-----END CERTIFICATE-----/m'; 33 | if (!preg_match($pattern, $data, $matches)) { 34 | throw new InvalidCertificateException('Invalid PEM encoded certificate'); 35 | } 36 | $this->data = preg_replace('/\s+/', '', $matches[1]); 37 | } 38 | 39 | function loadFromFile($filename) { 40 | if (!is_file($filename)) { 41 | throw new InvalidCertificateException("File not found $filename"); 42 | } 43 | $content = file_get_contents($filename); 44 | $this->loadPem($content); 45 | } 46 | 47 | function toPem() { 48 | $result = "-----BEGIN CERTIFICATE-----\n".chunk_split($this->getData(), 64, "\n")."-----END CERTIFICATE-----\n"; 49 | return $result; 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Binding/Base.php: -------------------------------------------------------------------------------- 1 | loadFromFile(__DIR__.'/../../../../../resources/sample/Certificate/saml.crt'); 38 | 39 | $key = new \XMLSecurityKey(\XMLSecurityKey::RSA_SHA1, array('type'=>'private')); 40 | $key->loadKey(__DIR__.'/../../../../../resources/sample/Certificate/saml.pem', true, false); 41 | 42 | $signature = new SignatureCreator(); 43 | $signature->setCertificate($certificate); 44 | $signature->setXmlSecurityKey($key); 45 | $request->setSignature($signature); 46 | 47 | $request->setRelayState($this->relayState); 48 | 49 | return $request; 50 | } 51 | 52 | 53 | protected function checkRequest(AuthnRequest $request, $id, $time) { 54 | $this->assertEquals($id, $request->getID()); 55 | $this->assertEquals('2.0', $request->getVersion()); 56 | $this->assertEquals($this->destination, $request->getDestination()); 57 | $this->assertEquals($this->ascURL, $request->getAssertionConsumerServiceURL()); 58 | $this->assertEquals($this->protocolBinding, $request->getProtocolBinding()); 59 | $this->assertEquals($time, $request->getIssueInstant()); 60 | 61 | $this->assertEquals($this->issuer, $request->getIssuer()); 62 | $this->assertEquals($this->nameIDPolicyFormat, $request->getNameIdPolicyFormat()); 63 | $this->assertTrue($request->getNameIdPolicyAllowCreate()); 64 | 65 | 66 | /** @var SignatureValidatorInterface $signature */ 67 | $signature = $request->getSignature(); 68 | $this->assertNotNull($signature); 69 | $this->assertTrue($signature instanceof SignatureValidatorInterface); 70 | 71 | $certificate = new X509Certificate(); 72 | $certificate->loadFromFile(__DIR__.'/../../../../../resources/sample/Certificate/saml.crt'); 73 | $key = KeyHelper::createPublicKey($certificate); 74 | $signature->validate($key); 75 | } 76 | 77 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Binding/HttpPostTest.php: -------------------------------------------------------------------------------- 1 | getRequest(); 15 | $id = $authnRequest->getID(); 16 | $time = $authnRequest->getIssueInstant(); 17 | 18 | $binding = new HttpPost(); 19 | 20 | /** @var PostResponse $response */ 21 | $response = $binding->send($authnRequest); 22 | $this->assertNotNull($response); 23 | $this->assertTrue($response instanceof PostResponse); 24 | $this->assertEquals($this->destination, $response->getDestination()); 25 | 26 | /** @var $authnRequest AuthnRequest */ 27 | $bindingRequest = new Request(); 28 | $bindingRequest->setPost($response->getData()); 29 | $authnRequest = $binding->receive($bindingRequest); 30 | $this->assertTrue($authnRequest instanceof AuthnRequest); 31 | $this->checkRequest($authnRequest, $id, $time); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Binding/HttpRedirectTest.php: -------------------------------------------------------------------------------- 1 | getRequest(); 16 | $id = $authnRequest->getID(); 17 | $time = $authnRequest->getIssueInstant(); 18 | 19 | $binding = new HttpRedirect(); 20 | 21 | /** @var RedirectResponse $response */ 22 | $response = $binding->send($authnRequest); 23 | $this->assertNotNull($response); 24 | $this->assertTrue($response instanceof RedirectResponse); 25 | $pos = strpos($response->getDestination(), '?'); 26 | $destination = substr($response->getDestination(), 0, $pos); 27 | $queryString = substr($response->getDestination(), $pos+1); 28 | 29 | $this->assertEquals($this->destination, $destination); 30 | 31 | $bindingRequest = new Request(); 32 | $data = $bindingRequest->parseQueryString($queryString, true); 33 | $this->checkData($data); 34 | 35 | 36 | /** @var AuthnRequest $authnRequest */ 37 | $authnRequest = $binding->receive($bindingRequest); 38 | $this->assertTrue($authnRequest instanceof AuthnRequest); 39 | $this->checkRequest($authnRequest, $id, $time); 40 | } 41 | 42 | 43 | 44 | 45 | 46 | private function checkData(array $data) { 47 | $this->assertTrue(array_key_exists('SAMLRequest', $data)); 48 | $this->assertTrue(array_key_exists('RelayState', $data)); 49 | $this->assertTrue(array_key_exists('SigAlg', $data)); 50 | $this->assertTrue(array_key_exists('Signature', $data)); 51 | 52 | $this->assertEquals($this->relayState, $data['RelayState']); 53 | $this->assertEquals($this->sigAlg, $data['SigAlg']); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/CommonHelper.php: -------------------------------------------------------------------------------- 1 | load($file); 28 | $result = new EntityDescriptor(); 29 | $result->loadFromXml($doc->firstChild); 30 | return $result; 31 | } 32 | 33 | 34 | /** 35 | * @param string $sp 36 | * @param string $idp 37 | * @param SpMeta $spMeta 38 | * @return AuthnRequest 39 | * @throws \InvalidArgumentException 40 | */ 41 | public static function buildAuthnRequestFromEntityDescriptors($sp, $idp, SpMeta $spMeta = null) 42 | { 43 | if (is_string($sp)) { 44 | $sp = self::getEntityDescriptorFromXmlFile($sp); 45 | } else if (!$sp instanceof EntityDescriptor) { 46 | throw new \InvalidArgumentException('SP parameter must be instance of EntityDescriptor or string'); 47 | } 48 | 49 | if (is_string($idp)) { 50 | $idp = self::getEntityDescriptorFromXmlFile($idp); 51 | } else if (!$idp instanceof EntityDescriptor) { 52 | throw new \InvalidArgumentException('IDP parameter must be instance of EntityDescriptor or string'); 53 | } 54 | 55 | if (!$spMeta) { 56 | $spMeta = new SpMeta(); 57 | $spMeta->setNameIdFormat(NameIDPolicy::PERSISTENT); 58 | } 59 | 60 | $builder = new AuthnRequestBuilder($sp, $idp, $spMeta); 61 | $result = $builder->build(); 62 | return $result; 63 | } 64 | 65 | /** 66 | * @param string $sp 67 | * @param string $idp 68 | * @param SpMeta $spMeta 69 | * @return LogoutRequest 70 | * @throws \InvalidArgumentException 71 | */ 72 | public static function buildLogoutRequestFromEntityDescriptors($sp, $idp, SpMeta $spMeta = null) 73 | { 74 | if (is_string($sp)) { 75 | $sp = self::getEntityDescriptorFromXmlFile($sp); 76 | } else if (!$sp instanceof EntityDescriptor) { 77 | throw new \InvalidArgumentException('SP parameter must be instance of EntityDescriptor or string'); 78 | } 79 | 80 | if (is_string($idp)) { 81 | $idp = self::getEntityDescriptorFromXmlFile($idp); 82 | } else if (!$idp instanceof EntityDescriptor) { 83 | throw new \InvalidArgumentException('IDP parameter must be instance of EntityDescriptor or string'); 84 | } 85 | 86 | if (!$spMeta) { 87 | $spMeta = new SpMeta(); 88 | $spMeta->setNameIdFormat(NameIDPolicy::PERSISTENT); 89 | } 90 | 91 | $builder = new LogoutRequestBuilder($sp, $idp, $spMeta); 92 | $result = $builder->build('urn:oasis:names:tc:SAML:2.0:nameid-format:transient', 'user', '_677952a2-7fb3-4e7a-b439-326366e677db'); 93 | return $result; 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/Metadata/EntitiesDescriptor/EntitiesDescriptorFunctional01Test.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/../../../../../../../resources/sample/EntitiesDescriptor/testshib-providers.xml'); 18 | 19 | $ed = new EntitiesDescriptor(); 20 | $ed->loadFromXml($doc->firstChild); 21 | 22 | $arr = $ed->getAllEntityDescriptors(); 23 | $this->assertCount(2, $arr); 24 | 25 | $this->assertEquals('https://idp.testshib.org/idp/shibboleth', $arr[0]->getEntityID()); 26 | $this->assertCount(1, $arr[0]->getAllIdpSsoDescriptors()); 27 | $this->assertCount(0, $arr[0]->getAllSpSsoDescriptors()); 28 | 29 | $this->assertEquals('https://sp.testshib.org/shibboleth-sp', $arr[1]->getEntityID()); 30 | $this->assertCount(0, $arr[1]->getAllIdpSsoDescriptors()); 31 | $this->assertCount(1, $arr[1]->getAllSpSsoDescriptors()); 32 | } 33 | 34 | 35 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/Metadata/EntityDescriptor/EntityDescriptorSample02Test.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/../../../../../../../resources/sample/EntityDescriptor/ed01-formatted-certificate.xml'); 24 | 25 | $ed = new EntityDescriptor(); 26 | $ed->loadFromXml($doc->firstChild); 27 | 28 | $this->checkSP($ed); 29 | $this->checkIDP($ed); 30 | } 31 | 32 | 33 | private function checkIDP(EntityDescriptor $ed) { 34 | $arr = $ed->getAllIdpSsoDescriptors(); 35 | $this->assertEquals(1, count($arr)); 36 | $idp = $arr[0]; 37 | 38 | $this->assertEquals(1, count($idp->getKeyDescriptors())); 39 | 40 | $arr = $idp->getKeyDescriptors(); 41 | $this->assertEquals(1, count($arr)); 42 | $this->assertEquals('', $arr[0]->getUse()); 43 | $cert = $arr[0]->getCertificate(); 44 | $this->assertNotNull($cert); 45 | $this->assertGreaterThan(100, strlen($cert->getData())); 46 | 47 | $this->assertEquals(0, count($idp->findSingleLogoutServices())); 48 | 49 | $this->assertEquals(4, count($idp->findSingleSignOnServices())); 50 | 51 | $arr = $idp->findSingleSignOnServices(Bindings::SAML2_HTTP_POST); 52 | $this->assertEquals(1, count($arr)); 53 | $this->assertEquals(Bindings::SAML2_HTTP_POST, $arr[0]->getBinding()); 54 | $this->assertEquals('https://idp.testshib.org/idp/profile/SAML2/POST/SSO', $arr[0]->getLocation()); 55 | 56 | $arr = $idp->findSingleSignOnServices(Bindings::SAML2_HTTP_REDIRECT); 57 | $this->assertEquals(1, count($arr)); 58 | $this->assertEquals(Bindings::SAML2_HTTP_REDIRECT, $arr[0]->getBinding()); 59 | $this->assertEquals('https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO', $arr[0]->getLocation()); 60 | } 61 | 62 | 63 | private function checkSP(EntityDescriptor $ed) { 64 | $arr = $ed->getAllSpSsoDescriptors(); 65 | $this->assertEquals(0, count($arr)); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/Metadata/SSODescriptor/SSODescriptorTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($rc->isAbstract()); 16 | } 17 | 18 | /** 19 | * @test 20 | */ 21 | public function shouldImplementGetXmlInterface() 22 | { 23 | $rc = new \ReflectionClass('AerialShip\LightSaml\Model\Metadata\SSODescriptor'); 24 | $this->assertTrue($rc->implementsInterface('AerialShip\LightSaml\Meta\GetXmlInterface')); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function shouldImplementLoadFromXmlInterface() 31 | { 32 | $rc = new \ReflectionClass('AerialShip\LightSaml\Model\Metadata\SSODescriptor'); 33 | $this->assertTrue($rc->implementsInterface('AerialShip\LightSaml\Meta\LoadFromXmlInterface')); 34 | } 35 | 36 | /** 37 | * @test 38 | */ 39 | public function shouldReturnAllKeyDescriptorsWhenFindKeyDescriptorsCalledWithNullArgument() 40 | { 41 | $mock = $this->getSSODescriptorMock(); 42 | $mock->addKeyDescriptor($kd1 = new KeyDescriptor()); 43 | $mock->addKeyDescriptor($kd2 = new KeyDescriptor(KeyDescriptor::USE_SIGNING)); 44 | $mock->addKeyDescriptor($kd3 = new KeyDescriptor(KeyDescriptor::USE_ENCRYPTION)); 45 | 46 | $arr = $mock->findKeyDescriptors(null); 47 | $this->assertCount(3, $arr); 48 | $this->assertContainsOnlyInstancesOf('AerialShip\LightSaml\Model\Metadata\KeyDescriptor', $arr); 49 | $this->assertSame($kd1, $arr[0]); 50 | $this->assertSame($kd2, $arr[1]); 51 | $this->assertSame($kd3, $arr[2]); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function shouldReturnSignAndNullUseKeyDescriptorsWhenFindKeyDescriptorsCalledWithSignArgument() 58 | { 59 | $mock = $this->getSSODescriptorMock(); 60 | $mock->addKeyDescriptor($kd1 = new KeyDescriptor()); 61 | $mock->addKeyDescriptor($kd2 = new KeyDescriptor(KeyDescriptor::USE_SIGNING)); 62 | $mock->addKeyDescriptor($kd3 = new KeyDescriptor(KeyDescriptor::USE_ENCRYPTION)); 63 | 64 | $arr = $mock->findKeyDescriptors(KeyDescriptor::USE_SIGNING); 65 | $this->assertCount(2, $arr); 66 | $this->assertContainsOnlyInstancesOf('AerialShip\LightSaml\Model\Metadata\KeyDescriptor', $arr); 67 | $this->assertSame($kd1, $arr[0]); 68 | $this->assertSame($kd2, $arr[1]); 69 | } 70 | 71 | /** 72 | * @test 73 | */ 74 | public function shouldReturnEncryptionAndNullUseKeyDescriptorsWhenFindKeyDescriptorsCalledWithEncryptionArgument() 75 | { 76 | $mock = $this->getSSODescriptorMock(); 77 | $mock->addKeyDescriptor($kd1 = new KeyDescriptor()); 78 | $mock->addKeyDescriptor($kd2 = new KeyDescriptor(KeyDescriptor::USE_SIGNING)); 79 | $mock->addKeyDescriptor($kd3 = new KeyDescriptor(KeyDescriptor::USE_ENCRYPTION)); 80 | 81 | $arr = $mock->findKeyDescriptors(KeyDescriptor::USE_ENCRYPTION); 82 | $this->assertCount(2, $arr); 83 | $this->assertContainsOnlyInstancesOf('AerialShip\LightSaml\Model\Metadata\KeyDescriptor', $arr); 84 | $this->assertSame($kd1, $arr[0]); 85 | $this->assertSame($kd3, $arr[1]); 86 | } 87 | 88 | 89 | 90 | /** 91 | * @return \PHPUnit_Framework_MockObject_MockObject|\AerialShip\LightSaml\Model\Metadata\SSODescriptor 92 | */ 93 | private function getSSODescriptorMock() 94 | { 95 | return $this->getMock('AerialShip\LightSaml\Model\Metadata\SSODescriptor', array('getXmlNodeName')); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/Protocol/AuthnRequest/AuthnRequestSample01Test.php: -------------------------------------------------------------------------------- 1 | setNameIdFormat(NameIDPolicy::PERSISTENT); 26 | $request = CommonHelper::buildAuthnRequestFromEntityDescriptors( 27 | __DIR__.'/../../../../../../../resources/sample/EntityDescriptor/sp-ed2.xml', 28 | __DIR__.'/../../../../../../../resources/sample/EntityDescriptor/idp2-ed.xml', 29 | $spMeta 30 | ); 31 | 32 | $id = $request->getID(); 33 | $this->assertNotEmpty($id); 34 | $this->assertEquals(43, strlen($id)); 35 | 36 | $time = $request->getIssueInstant(); 37 | $this->assertNotEmpty($time); 38 | $this->assertLessThan(2, abs(time()-$time)); 39 | 40 | $this->checkRequestObject($request, $id, $time); 41 | 42 | // serialize to XML Document and check xml 43 | $context = new SerializationContext(); 44 | $request->getXml($context->getDocument(), $context); 45 | $this->checkRequestXml($context->getDocument(), $id); 46 | 47 | // Deserialize new request out of xml 48 | $request = new AuthnRequest(); 49 | $request->loadFromXml($context->getDocument()->firstChild); 50 | $this->checkRequestObject($request, $id, $time); 51 | 52 | // serialize again to xml and check xml 53 | $context = new SerializationContext(); 54 | $request->getXml($context->getDocument(), $context); 55 | $this->checkRequestXml($context->getDocument(), $id); 56 | } 57 | 58 | private function checkRequestObject(AuthnRequest $request, $id, $time) { 59 | $this->assertEquals($id, $request->getID()); 60 | $this->assertEquals('2.0', $request->getVersion()); 61 | $this->assertEquals($this->destination, $request->getDestination()); 62 | $this->assertEquals($this->ascURL, $request->getAssertionConsumerServiceURL()); 63 | $this->assertEquals($this->protocolBinding, $request->getProtocolBinding()); 64 | $this->assertEquals($time, $request->getIssueInstant()); 65 | 66 | $this->assertEquals($this->issuer, $request->getIssuer()); 67 | $this->assertEquals($this->nameIDPolicyFormat, $request->getNameIdPolicyFormat()); 68 | $this->assertTrue($request->getNameIdPolicyAllowCreate()); 69 | } 70 | 71 | 72 | private function checkRequestXml(\DOMDocument $doc, $id) { 73 | //$xml = $doc->saveXML(); 74 | //print "\n\n$xml\n\n"; 75 | 76 | $xpath = new \DOMXPath($doc); 77 | $xpath->registerNamespace('samlp', Protocol::SAML2); 78 | $xpath->registerNamespace('saml', Protocol::NS_ASSERTION); 79 | 80 | $list = $xpath->query('/samlp:AuthnRequest'); 81 | $this->assertEquals(1, $list->length); 82 | 83 | /** @var $node \DOMElement */ 84 | $node = $list->item(0); 85 | 86 | $this->assertEquals($id, $node->getAttribute('ID')); 87 | $this->assertEquals('2.0', $node->getAttribute('Version')); 88 | $this->assertEquals($this->destination, $node->getAttribute('Destination')); 89 | $this->assertEquals($this->ascURL, $node->getAttribute('AssertionConsumerServiceURL')); 90 | $this->assertEquals($this->protocolBinding, $node->getAttribute('ProtocolBinding')); 91 | 92 | $list = $xpath->query('/samlp:AuthnRequest/saml:Issuer'); 93 | $this->assertEquals(1, $list->length); 94 | /** @var $node \DOMElement */ 95 | $node = $list->item(0); 96 | $this->assertEquals($this->issuer, $node->textContent); 97 | 98 | $list = $xpath->query('/samlp:AuthnRequest/samlp:NameIDPolicy'); 99 | $this->assertEquals(1, $list->length); 100 | /** @var $node \DOMElement */ 101 | $node = $list->item(0); 102 | $this->assertEquals($this->nameIDPolicyFormat, $node->getAttribute('Format')); 103 | $this->assertEquals('true', $node->getAttribute('AllowCreate')); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/Protocol/LogoutRequest/LogoutRequestSample01Test.php: -------------------------------------------------------------------------------- 1 | setNameIdFormat(NameIDPolicy::PERSISTENT); 23 | 24 | $request = CommonHelper::buildLogoutRequestFromEntityDescriptors( 25 | __DIR__.'/../../../../../../../resources/sample/EntityDescriptor/sp-ed2.xml', 26 | __DIR__.'/../../../../../../../resources/sample/EntityDescriptor/idp2-ed.xml', 27 | $spMeta 28 | ); 29 | 30 | $id = $request->getID(); 31 | $this->assertNotEmpty($id); 32 | $this->assertEquals(43, strlen($id)); 33 | 34 | $time = $request->getIssueInstant(); 35 | $this->assertNotEmpty($time); 36 | $this->assertLessThan(2, abs(time()-$time)); 37 | 38 | $this->checkRequestObject($request, $id, $time); 39 | 40 | $context = new SerializationContext(); 41 | $request->getXml($context->getDocument(), $context); 42 | $this->checkRequestXml($context->getDocument(), $request); 43 | 44 | $request = new LogoutRequest(); 45 | $request->loadFromXml($context->getDocument()->firstChild); 46 | $this->checkRequestObject($request, $id, $time); 47 | } 48 | 49 | private function checkRequestObject(LogoutRequest $request, $id, $time) { 50 | $this->assertEquals($id, $request->getID()); 51 | $this->assertEquals('2.0', $request->getVersion()); 52 | $this->assertEquals($this->destination, $request->getDestination()); 53 | $this->assertEquals($time, $request->getIssueInstant()); 54 | $this->assertEquals($this->issuer, $request->getIssuer()); 55 | 56 | $reason = $request->getReason(); 57 | if($reason != null){ 58 | $this->assertStringMatchesFormat('%s', $reason); 59 | } 60 | $NameId = $request->getNameID(); 61 | $this->assertInstanceOf('AerialShip\LightSaml\Model\Assertion\NameID', $NameId); 62 | $this->assertNotEmpty($NameId->getFormat()); 63 | } 64 | 65 | private function checkRequestXml(\DOMDocument $doc, LogoutRequest $request) 66 | { 67 | $xpath = new \DOMXPath($doc); 68 | $xpath->registerNamespace('samlp', Protocol::SAML2); 69 | $xpath->registerNamespace('saml', Protocol::NS_ASSERTION); 70 | 71 | $list = $xpath->query('/samlp:LogoutRequest'); 72 | $this->assertEquals(1, $list->length); 73 | 74 | /** @var $node \DOMElement */ 75 | $node = $list->item(0); 76 | 77 | $this->assertEquals($request->getReason(), $node->getAttribute('Reason')); 78 | $this->assertEquals($request->getID(), $node->getAttribute('ID')); 79 | $this->assertEquals('2.0', $node->getAttribute('Version')); 80 | $this->assertEquals($this->destination, $node->getAttribute('Destination')); 81 | 82 | $list = $xpath->query('/samlp:LogoutRequest/saml:Issuer'); 83 | $this->assertEquals(1, $list->length); 84 | $node = $list->item(0); 85 | $this->assertEquals($this->issuer, $node->textContent); 86 | 87 | $list = $xpath->query('/samlp:LogoutRequest/saml:NameID'); 88 | $this->assertEquals(1, $list->length); 89 | $node = $list->item(0); 90 | $this->assertEquals($request->getNameID()->getFormat(), $node->getAttribute('Format')); 91 | $this->assertEquals($request->getNameID()->getValue(), $node->textContent); 92 | } 93 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/XmlDSig/Signature/SignatureCreatorValidatorFunctionalTest.php: -------------------------------------------------------------------------------- 1 | getSignedXml(); 17 | $this->verifySignature($xml); 18 | } 19 | 20 | 21 | private function verifySignature($xml) { 22 | $doc = new \DOMDocument(); 23 | $doc->loadXML($xml); 24 | 25 | $xpath = new \DOMXPath($doc); 26 | $xpath->registerNamespace('ds', Protocol::NS_XMLDSIG); 27 | 28 | $list = $xpath->query('/root/ds:Signature'); 29 | $this->assertEquals(1, $list->length); 30 | 31 | /** @var $signatureNode \DOMElement */ 32 | $signatureNode = $list->item(0); 33 | 34 | $signatureValidator = new SignatureXmlValidator(); 35 | $signatureValidator->loadFromXml($signatureNode); 36 | 37 | 38 | $certificate = new X509Certificate(); 39 | $certificate->loadFromFile(__DIR__.'/../../../../../../../resources/sample/Certificate/saml.crt'); 40 | 41 | $key = KeyHelper::createPublicKey($certificate); 42 | 43 | $ok = $signatureValidator->validate($key); 44 | 45 | $this->assertTrue($ok); 46 | } 47 | 48 | 49 | private function getSignedXml() { 50 | $doc = new \DOMDocument(); 51 | $doc->appendChild($doc->createElement('root')); 52 | /** @var $root \DOMElement */ 53 | $root = $doc->firstChild; 54 | $root->setAttribute('foo', 'bar'); 55 | 56 | $other = $doc->createElement('other'); 57 | $root->appendChild($other); 58 | $child = $doc->createElement('child', 'something'); 59 | $other->appendChild($child); 60 | 61 | $certificate = new X509Certificate(); 62 | $certificate->loadFromFile(__DIR__.'/../../../../../../../resources/sample/Certificate/saml.crt'); 63 | 64 | $key = new \XMLSecurityKey(\XMLSecurityKey::RSA_SHA1, array('type'=>'private')); 65 | $key->loadKey(__DIR__.'/../../../../../../../resources/sample/Certificate/saml.pem', true); 66 | 67 | $signatureCreator = new SignatureCreator(); 68 | $signatureCreator->setCertificate($certificate); 69 | $signatureCreator->setXmlSecurityKey($key); 70 | 71 | $context = new SerializationContext($doc); 72 | $signatureCreator->getXml($root, $context); 73 | 74 | $xml = $doc->saveXML(); 75 | 76 | return $xml; 77 | } 78 | 79 | 80 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Model/XmlDSig/Signature/SignatureSample01Test.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/../../../../../../../resources/sample/Response/response01.xml'); 17 | 18 | $xpath = new \DOMXPath($doc); 19 | $xpath->registerNamespace('samlp', Protocol::SAML2); 20 | $xpath->registerNamespace('ds', Protocol::NS_XMLDSIG); 21 | $xpath->registerNamespace('a', Protocol::NS_ASSERTION); 22 | 23 | $list = $xpath->query('/samlp:Response/a:Assertion/ds:Signature'); 24 | $this->assertEquals(1, $list->length); 25 | /** @var $signatureNode \DOMElement */ 26 | $signatureNode = $list->item(0); 27 | 28 | $signatureValidator = new SignatureXmlValidator(); 29 | $signatureValidator->loadFromXml($signatureNode); 30 | 31 | $list = $xpath->query('./ds:KeyInfo/ds:X509Data/ds:X509Certificate', $signatureNode); 32 | $this->assertEquals(1, $list->length); 33 | /** @var $signatureNode \DOMElement */ 34 | $certificateDataNode = $list->item(0); 35 | 36 | $certData = $certificateDataNode->textContent; 37 | $certificate = new X509Certificate(); 38 | $certificate->setData($certData); 39 | $key = KeyHelper::createPublicKey($certificate); 40 | 41 | $ok = $signatureValidator->validate($key); 42 | $this->assertTrue($ok); 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/AerialShip/LightSaml/Tests/Security/X509CertificateTest.php: -------------------------------------------------------------------------------- 1 | setData($this->getData()); 18 | KeyHelper::createPublicKey($cert); 19 | } 20 | 21 | 22 | /** 23 | * @test 24 | */ 25 | public function shouldCreatePublicKeyWhenLoadedFromPem() 26 | { 27 | $cert = new X509Certificate(); 28 | $cert->loadPem($this->getPEM()); 29 | KeyHelper::createPublicKey($cert); 30 | } 31 | 32 | 33 | /** 34 | * @return string 35 | */ 36 | protected function getPEM() 37 | { 38 | return "-----BEGIN CERTIFICATE-----\n".trim($this->getData())."\n-----END CERTIFICATE-----\n"; 39 | } 40 | 41 | 42 | /** 43 | * @return string 44 | */ 45 | protected function getData() 46 | { 47 | return " 48 | MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV 49 | MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD 50 | VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4 51 | MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI 52 | EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl 53 | c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B 54 | AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C 55 | yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe 56 | 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT 57 | NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614 58 | kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH 59 | gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G 60 | A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86 61 | 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl 62 | bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo 63 | aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN 64 | BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL 65 | I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo 66 | 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4 67 | /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj 68 | Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr 69 | 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA== 70 | "; 71 | } 72 | } --------------------------------------------------------------------------------