From 654496922b328f632c41889e1e80f3e301145899 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sun, 24 Mar 2024 23:32:12 +0100 Subject: [PATCH 01/51] Migrate module to use saml11 + ws-security libraries --- src/IdP/ADFS.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 790205d..6d9c0cf 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -8,6 +8,7 @@ use DateTimeImmutable; use DateTimeZone; use Exception; +use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\IdP; @@ -31,13 +32,13 @@ use SimpleSAML\WSSecurity\XML\wsp\AppliesTo; use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityToken; use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityTokenResponse; -use SimpleSAML\XHTML\Template; use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory; use SimpleSAML\XMLSecurity\Key\PrivateKey; use SimpleSAML\XMLSecurity\Key\X509Certificate as PublicKey; use SimpleSAML\XMLSecurity\XML\ds\KeyInfo; use SimpleSAML\XMLSecurity\XML\ds\X509Certificate; use SimpleSAML\XMLSecurity\XML\ds\X509Data; +use SimpleSAML\XHTML\Template; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -432,10 +433,11 @@ public static function sendResponse(array $state): void 'adfs:entityID' => $spEntityId, ]); - $assertionLifetime = $spMetadata->getOptionalInteger('assertion.lifetime', null); + $assertionLifetime = $spMetadata->getOptionalString('assertion.lifetime', null); if ($assertionLifetime === null) { - $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); + $assertionLifetime = $idpMetadata->getOptionalString('assertion.lifetime', 'PT300S'); } + Assert::nullOrValidDuration($assertionLifetime); $assertion = ADFS::generateAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); From dea9ca9c239475bc94f88b382bf5924bf69de392 Mon Sep 17 00:00:00 2001 From: monkeyiq Date: Mon, 29 Apr 2024 17:25:16 +1000 Subject: [PATCH 02/51] allow hosted metadata again. This lets me see it in the admin ui (#18) --- src/IdP/ADFS.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 6d9c0cf..c17afae 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -39,8 +39,7 @@ use SimpleSAML\XMLSecurity\XML\ds\X509Certificate; use SimpleSAML\XMLSecurity\XML\ds\X509Data; use SimpleSAML\XHTML\Template; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\{Request, StreamedResponse}; use function base64_encode; use function chunk_split; From 0664cf8b2bf600080978232b83c13b2bf9afff18 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 31 Aug 2024 20:41:37 +0200 Subject: [PATCH 03/51] Fix constant --- src/IdP/ADFS.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index c17afae..04e0af4 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -15,6 +15,7 @@ use SimpleSAML\Logger; use SimpleSAML\Metadata\MetaDataStorageHandler; use SimpleSAML\Module; +use SimpleSAML\SAML2\Constants as SAML2_C; use SimpleSAML\SAML11\Constants as C; use SimpleSAML\SAML11\XML\saml\Assertion; use SimpleSAML\SAML11\XML\saml\Attribute; @@ -122,7 +123,7 @@ private static function generateAssertion( $now = new DateTimeImmutable('now', new DateTimeZone('Z')); if ($httpUtils->isHTTPS()) { - $method = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; + $method = SAML2_C::AC_PASSWORD_PROTECTED_TRANSPORT; } else { $method = C::AC_PASSWORD; } From c46448d7412e483980fb7b51eac1b49734953e47 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 31 Aug 2024 21:07:08 +0200 Subject: [PATCH 04/51] Back to SSP release-branch --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d7e47e8..6ffdc3d 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "simplesamlphp/assert": "^1.1", "simplesamlphp/saml11": "^1.0", "simplesamlphp/saml2": "^5@dev", - "simplesamlphp/simplesamlphp": "dev-feature/adfs-upgrade", + "simplesamlphp/simplesamlphp": "^2.4", "simplesamlphp/ws-security": "^1.6", "simplesamlphp/xml-common": "^1.16", "simplesamlphp/xml-security": "^1.9", From 12c7a978325db291736252050294bddebd83bb83 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sun, 1 Sep 2024 00:57:24 +0200 Subject: [PATCH 05/51] Fix constants --- src/Controller/Adfs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index df999e6..d07fd6e 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -9,6 +9,7 @@ use SimpleSAML\Error as SspError; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; +use SimpleSAML\SAML2\Constants as C; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; /** @@ -77,7 +78,6 @@ public function metadata(Request $request): Response // Some products like DirX are known to break on pretty-printed XML $document->ownerDocument->formatOutput = false; $document->ownerDocument->encoding = 'UTF-8'; - $metaxml = $document->ownerDocument->saveXML(); $response = new Response(); From 5ebd2eed2aef5f01973af4686c71dd7e2f9a76dc Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sun, 1 Sep 2024 13:32:24 +0200 Subject: [PATCH 06/51] Fixes --- src/IdP/ADFS.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 04e0af4..be73b17 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -46,6 +46,10 @@ use function chunk_split; use function trim; +use function base64_encode; +use function chunk_split; +use function trim; + class ADFS { /** @@ -433,11 +437,10 @@ public static function sendResponse(array $state): void 'adfs:entityID' => $spEntityId, ]); - $assertionLifetime = $spMetadata->getOptionalString('assertion.lifetime', null); + $assertionLifetime = $spMetadata->getOptionalInteger('assertion.lifetime', null); if ($assertionLifetime === null) { - $assertionLifetime = $idpMetadata->getOptionalString('assertion.lifetime', 'PT300S'); + $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); } - Assert::nullOrValidDuration($assertionLifetime); $assertion = ADFS::generateAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); From 28a7aa27c7a285c91e12d2c5c9ec42da45c67c1f Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sun, 1 Sep 2024 18:27:07 +0200 Subject: [PATCH 07/51] Feature/standalone metadata (#21) * Do not rely on SimpleSAMLphp for metadata-building * Fix * Introduce a clokc * Generate ID * Cleanup --- src/Controller/Adfs.php | 1 - src/IdP/ADFS.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index d07fd6e..c07a82e 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -9,7 +9,6 @@ use SimpleSAML\Error as SspError; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; -use SimpleSAML\SAML2\Constants as C; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; /** diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index be73b17..1b4142d 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -8,7 +8,6 @@ use DateTimeImmutable; use DateTimeZone; use Exception; -use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\IdP; From b761d2673f13a34c887c3566894a4274a4d21ceb Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 2 Oct 2024 23:19:53 +0200 Subject: [PATCH 08/51] Update ws-security lib --- src/IdP/ADFS.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 1b4142d..eb1b0af 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -32,13 +32,13 @@ use SimpleSAML\WSSecurity\XML\wsp\AppliesTo; use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityToken; use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityTokenResponse; +use SimpleSAML\XHTML\Template; use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory; use SimpleSAML\XMLSecurity\Key\PrivateKey; use SimpleSAML\XMLSecurity\Key\X509Certificate as PublicKey; use SimpleSAML\XMLSecurity\XML\ds\KeyInfo; use SimpleSAML\XMLSecurity\XML\ds\X509Certificate; use SimpleSAML\XMLSecurity\XML\ds\X509Data; -use SimpleSAML\XHTML\Template; use Symfony\Component\HttpFoundation\{Request, StreamedResponse}; use function base64_encode; From ac846f353464dab99ce331d8c0c230a94dbb40fe Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 3 Sep 2024 11:31:31 +0200 Subject: [PATCH 09/51] WIP: Add mex-endpoint --- routing/routes/routes.yml | 7 ++ src/Controller/Adfs.php | 40 ++++++++ src/Trust/MetadataExchange.php | 164 +++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 src/Trust/MetadataExchange.php diff --git a/routing/routes/routes.yml b/routing/routes/routes.yml index c03345d..60264d4 100644 --- a/routing/routes/routes.yml +++ b/routing/routes/routes.yml @@ -27,3 +27,10 @@ adfs-prp-legacy: _controller: 'SimpleSAML\Module\adfs\Controller\Adfs::prp' } methods: [GET, POST] + +adfs-wstrust-mex: + path: /ws-trust/mex + defaults: { + _controller: 'SimpleSAML\Module\adfs\Controller\Adfs::mex' + } + methods: [GET] diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index c07a82e..f532ff6 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -9,6 +9,7 @@ use SimpleSAML\Error as SspError; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; +use SimpleSAML\Module\adfs\Trust\MetadataExchange; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; /** @@ -136,4 +137,43 @@ function () use ($idp, /** @scrutinizer ignore-type */ $assocId, $relayState, $l } throw new SspError\BadRequest("Missing parameter 'wa' or 'assocId' in request."); } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response + */ + public function mex(Request $request): Response + { + if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) { + throw new SspError\Error('NOACCESS'); + } + + // check if valid local session exists + $authUtils = new Utils\Auth(); + if ($this->config->getOptionalBoolean('admin.protectmetadata', false) && !$authUtils->isAdmin()) { + return new StreamedResponse([$authUtils, 'requireAdmin']); + } + + $mexBuilder = new MetadataExchange(); + $document = $mexBuilder->buildDocument(); + + $document = $builder->buildDocument()->toXML(); + // Some products like DirX are known to break on pretty-printed XML + $document->ownerDocument->formatOutput = false; + $document->ownerDocument->encoding = 'UTF-8'; + + $metaxml = $document->ownerDocument->saveXML(); + + $response = new Response(); + $response->setEtag(hash('sha256', $metaxml)); + $response->setPublic(); + if ($response->isNotModified($request)) { + return $response; + } + $response->headers->set('Content-Type', 'text/xml'); + $response->setContent($metaxml); + + return $response; + } } diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php new file mode 100644 index 0000000..eb1a597 --- /dev/null +++ b/src/Trust/MetadataExchange.php @@ -0,0 +1,164 @@ +getPolicies(), + ); + } + + + /** + * This method builds the wsp:Policy elements + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy[] + */ + private function getPolicies(): array + { + return [$this->getUserNameWSTrustBindingPolicy()]; + } + + + /** + * This method builds the UserNameWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getUserNameWSTrustBindingPolicy(): Policy + { + return new Policy( + Id: 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy', + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken( + namespacedAttributes: [ + new XMLAttribute(null, null, 'RequireClientCertificate', 'false'), + ], + )], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ), + new SignedSupportingTokens( + elements: [new Policy( + children: [new UsernameToken( + includeToken: IncludeToken::AlwaysToRecipient, + elts: [new Policy( + children: [new WssUsernameToken10()], + )], + )], + )], + ), + new EndorsingSupportingTokens( + elements: [new Policy( + children: [new RsaToken( + includeToken: IncludeToken::Never, + elts: [new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR, + name: 'To', + ), + ], + )], + namespacedAttributes: [ + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + )], + )], + ), + new Wss11( + elements: [new Policy()], + ), + new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ), + new UsingAddressing(), + ], + )], + )], + ); + } +} From c47ece5e3d9d8121ce9d28a385ef26d08d6a31c7 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 10 Sep 2024 12:38:15 +0200 Subject: [PATCH 10/51] Use updated ws-security lib --- src/Trust/MetadataExchange.php | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index eb1a597..a4e688e 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -6,26 +6,26 @@ use SimpleSAML\WSSecurity\Constants as C; use SimpleSAML\WSSecurity\XML\mssp\RsaToken; -use SimpleSAML\WSSecurity\XML\sp\AlgorithmSuite; -use SimpleSAML\WSSecurity\XML\sp\Basic256; -use SimpleSAML\WSSecurity\XML\sp\EndorsingSupportingTokens; -use SimpleSAML\WSSecurity\XML\sp\Header; -use SimpleSAML\WSSecurity\XML\sp\HttpsToken; -use SimpleSAML\WSSecurity\XML\sp\IncludeTimestamp; -use SimpleSAML\WSSecurity\XML\sp\IncludeToken; -use SimpleSAML\WSSecurity\XML\sp\Layout; -use SimpleSAML\WSSecurity\XML\sp\MustSupportIssuedTokens; -use SimpleSAML\WSSecurity\XML\sp\RequireClientEntropy; -use SimpleSAML\WSSecurity\XML\sp\RequireServerEntropy; -use SimpleSAML\WSSecurity\XML\sp\SignedParts; -use SimpleSAML\WSSecurity\XML\sp\SignedSupportingTokens; -use SimpleSAML\WSSecurity\XML\sp\Strict; -use SimpleSAML\WSSecurity\XML\sp\TransportBinding; -use SimpleSAML\WSSecurity\XML\sp\TransportToken; -use SimpleSAML\WSSecurity\XML\sp\Trust10; -use SimpleSAML\WSSecurity\XML\sp\UsernameToken; -use SimpleSAML\WSSecurity\XML\sp\Wss11; -use SimpleSAML\WSSecurity\XML\sp\WssUsernameToken10; +use SimpleSAML\WSSecurity\XML\sp_200507\AlgorithmSuite; +use SimpleSAML\WSSecurity\XML\sp_200507\Basic256; +use SimpleSAML\WSSecurity\XML\sp_200507\EndorsingSupportingTokens; +use SimpleSAML\WSSecurity\XML\sp_200507\Header; +use SimpleSAML\WSSecurity\XML\sp_200507\HttpsToken; +use SimpleSAML\WSSecurity\XML\sp_200507\IncludeTimestamp; +use SimpleSAML\WSSecurity\XML\sp_200507\IncludeToken; +use SimpleSAML\WSSecurity\XML\sp_200507\Layout; +use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportIssuedTokens; +use SimpleSAML\WSSecurity\XML\sp_200507\RequireClientEntropy; +use SimpleSAML\WSSecurity\XML\sp_200507\RequireServerEntropy; +use SimpleSAML\WSSecurity\XML\sp_200507\SignedParts; +use SimpleSAML\WSSecurity\XML\sp_200507\SignedSupportingTokens; +use SimpleSAML\WSSecurity\XML\sp_200507\Strict; +use SimpleSAML\WSSecurity\XML\sp_200507\TransportBinding; +use SimpleSAML\WSSecurity\XML\sp_200507\TransportToken; +use SimpleSAML\WSSecurity\XML\sp_200507\Trust10; +use SimpleSAML\WSSecurity\XML\sp_200507\UsernameToken; +use SimpleSAML\WSSecurity\XML\sp_200507\Wss11; +use SimpleSAML\WSSecurity\XML\sp_200507\WssUsernameToken10; use SimpleSAML\WSSecurity\XML\wsdl\Definitions; use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; From bb9cfc354d59f93769f95449e20e7ac596292a97 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 17:17:01 +0200 Subject: [PATCH 11/51] Refactor --- src/Trust/MetadataExchange.php | 142 +++++++++++++++++---------------- 1 file changed, 75 insertions(+), 67 deletions(-) diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index a4e688e..631e80d 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -84,78 +84,86 @@ private function getPolicies(): array */ private function getUserNameWSTrustBindingPolicy(): Policy { + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(true)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $signedSupportingTokens = new SignedSupportingTokens( + elements: [new Policy( + children: [new UsernameToken( + includeToken: IncludeToken::AlwaysToRecipient, + elts: [new Policy( + children: [new WssUsernameToken10()], + )], + )], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [new RsaToken( + includeToken: IncludeToken::Never, + elts: [new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + )], + namespacedAttributes: [ + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + )], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy()], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + return new Policy( Id: 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy', operatorContent: [new ExactlyOne( operatorContent: [new All( children: [ - new TransportBinding( - elements: [new Policy( - children: [ - new TransportToken( - elements: [new Policy( - children: [new HttpsToken( - namespacedAttributes: [ - new XMLAttribute(null, null, 'RequireClientCertificate', 'false'), - ], - )], - )], - ), - new AlgorithmSuite( - elements: [new Policy( - children: [new Basic256()], - )], - ), - new Layout( - elements: [new Policy( - children: [new Strict()], - )], - ), - new IncludeTimestamp(), - ], - )], - ), - new SignedSupportingTokens( - elements: [new Policy( - children: [new UsernameToken( - includeToken: IncludeToken::AlwaysToRecipient, - elts: [new Policy( - children: [new WssUsernameToken10()], - )], - )], - )], - ), - new EndorsingSupportingTokens( - elements: [new Policy( - children: [new RsaToken( - includeToken: IncludeToken::Never, - elts: [new SignedParts( - header: [ - new Header( - namespace: C::NS_ADDR, - name: 'To', - ), - ], - )], - namespacedAttributes: [ - new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), - ], - )], - )], - ), - new Wss11( - elements: [new Policy()], - ), - new Trust10( - elements: [new Policy( - children: [ - new MustSupportIssuedTokens(), - new RequireClientEntropy(), - new RequireServerEntropy(), - ], - )], - ), - new UsingAddressing(), + $transportBinding, + $signedSupportingTokens, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, ], )], )], From 70dbde69fb07ba1bc14113e13bec4a49840799fb Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 18:00:35 +0200 Subject: [PATCH 12/51] Refactor --- src/Controller/Adfs.php | 4 +--- src/Trust/MetadataExchange.php | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index f532ff6..a4d1d99 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -156,9 +156,7 @@ public function mex(Request $request): Response } $mexBuilder = new MetadataExchange(); - $document = $mexBuilder->buildDocument(); - - $document = $builder->buildDocument()->toXML(); + $document = $mexBuilder->buildDocument()->toXML(); // Some products like DirX are known to break on pretty-printed XML $document->ownerDocument->formatOutput = false; $document->ownerDocument->encoding = 'UTF-8'; diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index 631e80d..63fb1b0 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -89,7 +89,7 @@ private function getUserNameWSTrustBindingPolicy(): Policy children: [ new TransportToken( elements: [new Policy( - children: [new HttpsToken(true)], + children: [new HttpsToken(false)], )], ), new AlgorithmSuite( @@ -120,20 +120,22 @@ private function getUserNameWSTrustBindingPolicy(): Policy $endorsingSupportingTokens = new EndorsingSupportingTokens( elements: [new Policy( - children: [new RsaToken( - includeToken: IncludeToken::Never, - elts: [new SignedParts( + children: [ + new RsaToken( + includeToken: IncludeToken::Never, + namespacedAttributes: [ + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( header: [ new Header( namespace: C::NS_ADDR_200508, name: 'To', ), ], - )], - namespacedAttributes: [ - new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), - ], - )], + ), + ], )], ); @@ -154,7 +156,7 @@ private function getUserNameWSTrustBindingPolicy(): Policy $usingAddressing = new UsingAddressing(); return new Policy( - Id: 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy', + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( children: [ From 073db4ce2cebf0aca47c3121aee8fa93cb1a50c2 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 18:12:41 +0200 Subject: [PATCH 13/51] Fix IncludeToken --- src/Trust/MetadataExchange.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index 63fb1b0..5f7838a 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -110,10 +110,17 @@ private function getUserNameWSTrustBindingPolicy(): Policy $signedSupportingTokens = new SignedSupportingTokens( elements: [new Policy( children: [new UsernameToken( - includeToken: IncludeToken::AlwaysToRecipient, elts: [new Policy( children: [new WssUsernameToken10()], )], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], )], )], ); @@ -122,8 +129,8 @@ private function getUserNameWSTrustBindingPolicy(): Policy elements: [new Policy( children: [ new RsaToken( - includeToken: IncludeToken::Never, namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), ], ), From ffd02994729b258be847232a4eee25daee17205d Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 21:27:02 +0200 Subject: [PATCH 14/51] Add certificate policies --- src/Trust/MetadataExchange.php | 159 ++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index 5f7838a..b4b4c0f 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -15,8 +15,10 @@ use SimpleSAML\WSSecurity\XML\sp_200507\IncludeToken; use SimpleSAML\WSSecurity\XML\sp_200507\Layout; use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportIssuedTokens; +use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportRefThumbprint; use SimpleSAML\WSSecurity\XML\sp_200507\RequireClientEntropy; use SimpleSAML\WSSecurity\XML\sp_200507\RequireServerEntropy; +use SimpleSAML\WSSecurity\XML\sp_200507\RequireThumbprintReference; use SimpleSAML\WSSecurity\XML\sp_200507\SignedParts; use SimpleSAML\WSSecurity\XML\sp_200507\SignedSupportingTokens; use SimpleSAML\WSSecurity\XML\sp_200507\Strict; @@ -26,6 +28,8 @@ use SimpleSAML\WSSecurity\XML\sp_200507\UsernameToken; use SimpleSAML\WSSecurity\XML\sp_200507\Wss11; use SimpleSAML\WSSecurity\XML\sp_200507\WssUsernameToken10; +use SimpleSAML\WSSecurity\XML\sp_200507\WssX509V3Token10; +use SimpleSAML\WSSecurity\XML\sp_200507\X509Token; use SimpleSAML\WSSecurity\XML\wsdl\Definitions; use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; @@ -73,7 +77,160 @@ public function buildDocument(): Definitions */ private function getPolicies(): array { - return [$this->getUserNameWSTrustBindingPolicy()]; + return [ + $this->getCertificateWSTrustBinding(), + $this->getCertificateWSTrustBinding1(), + $this->getUserNameWSTrustBindingPolicy(), + ]; + } + + + /** + * This method builds the CertificateWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getCertificateWSTrustBinding(): Policy + { + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new X509Token( + elts: [new Policy( + children: [ + new RequireThumbprintReference(), + new WssX509V3Token10(), + ], + )], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy( + children: [ + new MustSupportRefThumbprint(), + ], + )], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); + } + + + /** + * This method builds the CertificateWSTrustBinding1 policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getCertificateWSTrustBinding1(): Policy + { + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(true)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async1_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $usingAddressing, + ], + )], + )], + ); } From 67c4fed621525dff818f6efd49ae359a769445db Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 22:18:07 +0200 Subject: [PATCH 15/51] Add IssuedToken policies --- src/Trust/MetadataExchange.php | 219 ++++++++++++++++++++++++++++++++- 1 file changed, 218 insertions(+), 1 deletion(-) diff --git a/src/Trust/MetadataExchange.php b/src/Trust/MetadataExchange.php index b4b4c0f..054fd13 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/MetadataExchange.php @@ -13,10 +13,13 @@ use SimpleSAML\WSSecurity\XML\sp_200507\HttpsToken; use SimpleSAML\WSSecurity\XML\sp_200507\IncludeTimestamp; use SimpleSAML\WSSecurity\XML\sp_200507\IncludeToken; +use SimpleSAML\WSSecurity\XML\sp_200507\IssuedToken; use SimpleSAML\WSSecurity\XML\sp_200507\Layout; use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportIssuedTokens; use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportRefThumbprint; +use SimpleSAML\WSSecurity\XML\sp_200507\RequestSecurityTokenTemplate; use SimpleSAML\WSSecurity\XML\sp_200507\RequireClientEntropy; +use SimpleSAML\WSSecurity\XML\sp_200507\RequireInternalReference; use SimpleSAML\WSSecurity\XML\sp_200507\RequireServerEntropy; use SimpleSAML\WSSecurity\XML\sp_200507\RequireThumbprintReference; use SimpleSAML\WSSecurity\XML\sp_200507\SignedParts; @@ -30,11 +33,17 @@ use SimpleSAML\WSSecurity\XML\sp_200507\WssUsernameToken10; use SimpleSAML\WSSecurity\XML\sp_200507\WssX509V3Token10; use SimpleSAML\WSSecurity\XML\sp_200507\X509Token; +use SimpleSAML\WSSecurity\XML\wsaw\UsingAddressing; use SimpleSAML\WSSecurity\XML\wsdl\Definitions; use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; use SimpleSAML\WSSecurity\XML\wsp\Policy; -use SimpleSAML\WSSecurity\XML\wsaw\UsingAddressing; +use SimpleSAML\WSSecurity\XML\wst\CanonicalizationAlgorithm; +use SimpleSAML\WSSecurity\XML\wst\EncryptionAlgorithm; +use SimpleSAML\WSSecurity\XML\wst\EncryptWith; +use SimpleSAML\WSSecurity\XML\wst\KeyType; +use SimpleSAML\WSSecurity\XML\wst\KeyTypeEnum; +use SimpleSAML\WSSecurity\XML\wst\SignatureAlgorithm; use SimpleSAML\XML\Attribute as XMLAttribute; /** @@ -81,6 +90,8 @@ private function getPolicies(): array $this->getCertificateWSTrustBinding(), $this->getCertificateWSTrustBinding1(), $this->getUserNameWSTrustBindingPolicy(), + $this->getIssuedTokenWSTrustBinding(), + $this->getIssuedTokenWSTrustBinding1(), ]; } @@ -335,4 +346,210 @@ private function getUserNameWSTrustBindingPolicy(): Policy )], ); } + + + /** + * This method builds the IssuedTokenWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getIssuedTokenWSTrustBinding(): Policy + { + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new IssuedToken( + elts: [ + new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::PublicKey]), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_RSA_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), + new Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy( + )], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); + } + + + /** + * This method builds the IssuedTokenWSTrustBinding1 policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getIssuedTokenWSTrustBinding1(): Policy + { + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new IssuedToken( + elts: [ + new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_HMAC_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), + new Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); + } } From 409a9468de044466dc247e6b74411e4a4e3514d5 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 22:26:21 +0200 Subject: [PATCH 16/51] Refactor --- src/Controller/Adfs.php | 2 +- src/MetadataExchange.php | 58 ++ src/Trust/Policy13.php | 502 ++++++++++++++++++ .../{MetadataExchange.php => Policy2005.php} | 21 +- 4 files changed, 563 insertions(+), 20 deletions(-) create mode 100644 src/MetadataExchange.php create mode 100644 src/Trust/Policy13.php rename src/Trust/{MetadataExchange.php => Policy2005.php} (97%) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index a4d1d99..d3e2cf2 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -9,7 +9,7 @@ use SimpleSAML\Error as SspError; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; -use SimpleSAML\Module\adfs\Trust\MetadataExchange; +use SimpleSAML\Module\adfs\MetadataExchange; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; /** diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php new file mode 100644 index 0000000..0909895 --- /dev/null +++ b/src/MetadataExchange.php @@ -0,0 +1,58 @@ +getPolicies(), + ); + } + + + /** + * This method builds the wsp:Policy elements + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy[] + */ + private function getPolicies(): array + { + $policy2005 = new Trust\Policy2005(); + $policy13 = new Trust\Policy13(); + + return array_merge( + $policy2005->getPolicies(), + $policy13->getPolicies(), + ); + } +} diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php new file mode 100644 index 0000000..8115246 --- /dev/null +++ b/src/Trust/Policy13.php @@ -0,0 +1,502 @@ +getCertificateWSTrustBinding(), +// $this->getUserNameWSTrustBindingPolicy(), +// $this->getIssuedTokenWSTrustBinding(), +// $this->getIssuedTokenWSTrustBinding1(), + ]; + } + + + /** + * This method builds the CertificateWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getCertificateWSTrustBinding(): Policy + { +/* + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new X509Token( + elts: [new Policy( + children: [ + new RequireThumbprintReference(), + new WssX509V3Token10(), + ], + )], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy( + children: [ + new MustSupportRefThumbprint(), + ], + )], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); +*/ + } + + + /** + * This method builds the UserNameWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getUserNameWSTrustBindingPolicy(): Policy + { +/* + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $signedSupportingTokens = new SignedSupportingTokens( + elements: [new Policy( + children: [new UsernameToken( + elts: [new Policy( + children: [new WssUsernameToken10()], + )], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + )], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy()], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $signedSupportingTokens, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); +*/ + } + + + /** + * This method builds the IssuedTokenWSTrustBinding policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getIssuedTokenWSTrustBinding(): Policy + { +/* + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new IssuedToken( + elts: [ + new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::PublicKey]), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_RSA_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), + new Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + $wss11 = new Wss11( + elements: [new Policy( + )], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); +*/ + } + + + /** + * This method builds the IssuedTokenWSTrustBinding1 policy. + * + * @param \SimpleSAML\WSSecurity\XML\wsp\Policy + */ + private function getIssuedTokenWSTrustBinding1(): Policy + { +/* + $transportBinding = new TransportBinding( + elements: [new Policy( + children: [ + new TransportToken( + elements: [new Policy( + children: [new HttpsToken(false)], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new IssuedToken( + elts: [ + new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_HMAC_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), + new Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_11, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new RsaToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), + ], + ), + new SignedParts( + header: [ + new Header( + namespace: C::NS_ADDR_200508, + name: 'To', + ), + ], + ), + ], + )], + ); + + return new Policy( + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); +*/ + } +} diff --git a/src/Trust/MetadataExchange.php b/src/Trust/Policy2005.php similarity index 97% rename from src/Trust/MetadataExchange.php rename to src/Trust/Policy2005.php index 054fd13..da6c308 100644 --- a/src/Trust/MetadataExchange.php +++ b/src/Trust/Policy2005.php @@ -47,11 +47,9 @@ use SimpleSAML\XML\Attribute as XMLAttribute; /** - * Common code for building MetaExchange (mex) documents based on the available configuration. - * * @package simplesamlphp/simplesamlphp-module-adfs */ -class MetadataExchange +class Policy2005 { /** * Constructor. @@ -64,27 +62,12 @@ public function __construct( } - /** - * Build a mex document - * - * @return \SimpleSAML\WSSecurity\XML\wsdl\Definitions - */ - public function buildDocument(): Definitions - { - return new Definitions( - targetNamespace: 'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice', - name: 'SecurityTokenService', - elements: $this->getPolicies(), - ); - } - - /** * This method builds the wsp:Policy elements * * @param \SimpleSAML\WSSecurity\XML\wsp\Policy[] */ - private function getPolicies(): array + public function getPolicies(): array { return [ $this->getCertificateWSTrustBinding(), From 0fb4515f10eb0594b60c747ada7faa137422583c Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 22:42:37 +0200 Subject: [PATCH 17/51] Add IssuedToken policies --- src/Trust/Policy2005.php | 56 ++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php index da6c308..9c01ead 100644 --- a/src/Trust/Policy2005.php +++ b/src/Trust/Policy2005.php @@ -41,6 +41,7 @@ use SimpleSAML\WSSecurity\XML\wst\CanonicalizationAlgorithm; use SimpleSAML\WSSecurity\XML\wst\EncryptionAlgorithm; use SimpleSAML\WSSecurity\XML\wst\EncryptWith; +use SimpleSAML\WSSecurity\XML\wst\KeySize; use SimpleSAML\WSSecurity\XML\wst\KeyType; use SimpleSAML\WSSecurity\XML\wst\KeyTypeEnum; use SimpleSAML\WSSecurity\XML\wst\SignatureAlgorithm; @@ -365,16 +366,16 @@ private function getIssuedTokenWSTrustBinding(): Policy elements: [new Policy( children: [ new IssuedToken( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::PublicKey]), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_RSA_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), elts: [ - new RequestSecurityTokenTemplate( - elts: [ - new KeyType([KeyTypeEnum::PublicKey]), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), - new SignatureAlgorithm(C::SIG_RSA_SHA1), - new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), - new EncryptionAlgorithm(C::BLOCK_ENC_AES256), - ], - ), new Policy( children: [ new RequireInternalReference(), @@ -476,17 +477,17 @@ private function getIssuedTokenWSTrustBinding1(): Policy elements: [new Policy( children: [ new IssuedToken( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_HMAC_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), elts: [ - new RequestSecurityTokenTemplate( - elts: [ - new KeyType([KeyTypeEnum::SymmetricKey]), - new KeySize('256'), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), - new SignatureAlgorithm(C::SIG_HMAC_SHA1), - new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), - new EncryptionAlgorithm(C::BLOCK_ENC_AES256), - ], - ), new Policy( children: [ new RequireInternalReference(), @@ -520,6 +521,23 @@ private function getIssuedTokenWSTrustBinding1(): Policy )], ); + $wss11 = new Wss11( + elements: [new Policy( + )], + ); + + $trust10 = new Trust10( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + return new Policy( Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy'), operatorContent: [new ExactlyOne( From a81c07d43d8c7766d2feff7ff9a8daa5b2b63a04 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 23 Sep 2024 23:32:40 +0200 Subject: [PATCH 18/51] Add WS-trust 1.3 policies --- src/Trust/Policy13.php | 142 ++++++++++++++++++++------------------- src/Trust/Policy2005.php | 1 - 2 files changed, 74 insertions(+), 69 deletions(-) diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php index 8115246..9ea4e79 100644 --- a/src/Trust/Policy13.php +++ b/src/Trust/Policy13.php @@ -5,37 +5,33 @@ namespace SimpleSAML\Module\adfs\Trust; use SimpleSAML\WSSecurity\Constants as C; -/* -use SimpleSAML\WSSecurity\XML\mssp\RsaToken; -use SimpleSAML\WSSecurity\XML\sp_200507\AlgorithmSuite; -use SimpleSAML\WSSecurity\XML\sp_200507\Basic256; -use SimpleSAML\WSSecurity\XML\sp_200507\EndorsingSupportingTokens; -use SimpleSAML\WSSecurity\XML\sp_200507\Header; -use SimpleSAML\WSSecurity\XML\sp_200507\HttpsToken; -use SimpleSAML\WSSecurity\XML\sp_200507\IncludeTimestamp; -use SimpleSAML\WSSecurity\XML\sp_200507\IncludeToken; -use SimpleSAML\WSSecurity\XML\sp_200507\IssuedToken; -use SimpleSAML\WSSecurity\XML\sp_200507\Layout; -use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportIssuedTokens; -use SimpleSAML\WSSecurity\XML\sp_200507\MustSupportRefThumbprint; -use SimpleSAML\WSSecurity\XML\sp_200507\RequestSecurityTokenTemplate; -use SimpleSAML\WSSecurity\XML\sp_200507\RequireClientEntropy; -use SimpleSAML\WSSecurity\XML\sp_200507\RequireInternalReference; -use SimpleSAML\WSSecurity\XML\sp_200507\RequireServerEntropy; -use SimpleSAML\WSSecurity\XML\sp_200507\RequireThumbprintReference; -use SimpleSAML\WSSecurity\XML\sp_200507\SignedParts; -use SimpleSAML\WSSecurity\XML\sp_200507\SignedSupportingTokens; -use SimpleSAML\WSSecurity\XML\sp_200507\Strict; -use SimpleSAML\WSSecurity\XML\sp_200507\TransportBinding; -use SimpleSAML\WSSecurity\XML\sp_200507\TransportToken; -use SimpleSAML\WSSecurity\XML\sp_200507\Trust10; -use SimpleSAML\WSSecurity\XML\sp_200507\UsernameToken; -use SimpleSAML\WSSecurity\XML\sp_200507\Wss11; -use SimpleSAML\WSSecurity\XML\sp_200507\WssUsernameToken10; -use SimpleSAML\WSSecurity\XML\sp_200507\WssX509V3Token10; -use SimpleSAML\WSSecurity\XML\sp_200507\X509Token; +use SimpleSAML\WSSecurity\XML\sp_200702\AlgorithmSuite; +use SimpleSAML\WSSecurity\XML\sp_200702\Basic256; +use SimpleSAML\WSSecurity\XML\sp_200702\EndorsingSupportingTokens; +use SimpleSAML\WSSecurity\XML\sp_200702\Header; +use SimpleSAML\WSSecurity\XML\sp_200702\HttpsToken; +use SimpleSAML\WSSecurity\XML\sp_200702\IncludeTimestamp; +use SimpleSAML\WSSecurity\XML\sp_200702\IncludeToken; +use SimpleSAML\WSSecurity\XML\sp_200702\IssuedToken; +use SimpleSAML\WSSecurity\XML\sp_200702\KeyValueToken; +use SimpleSAML\WSSecurity\XML\sp_200702\Layout; +use SimpleSAML\WSSecurity\XML\sp_200702\MustSupportIssuedTokens; +use SimpleSAML\WSSecurity\XML\sp_200702\MustSupportRefThumbprint; +use SimpleSAML\WSSecurity\XML\sp_200702\RequireClientEntropy; +use SimpleSAML\WSSecurity\XML\sp_200702\RequireServerEntropy; +use SimpleSAML\WSSecurity\XML\sp_200702\RequireThumbprintReference; +use SimpleSAML\WSSecurity\XML\sp_200702\SignedParts; +use SimpleSAML\WSSecurity\XML\sp_200702\SignedEncryptedSupportingTokens; +use SimpleSAML\WSSecurity\XML\sp_200702\Strict; +use SimpleSAML\WSSecurity\XML\sp_200702\TransportBinding; +use SimpleSAML\WSSecurity\XML\sp_200702\TransportToken; +use SimpleSAML\WSSecurity\XML\sp_200702\Trust13; +use SimpleSAML\WSSecurity\XML\sp_200702\UsernameToken; +use SimpleSAML\WSSecurity\XML\sp_200702\Wss11; +use SimpleSAML\WSSecurity\XML\sp_200702\WssUsernameToken10; +use SimpleSAML\WSSecurity\XML\sp_200702\WssX509V3Token10; +use SimpleSAML\WSSecurity\XML\sp_200702\X509Token; use SimpleSAML\WSSecurity\XML\wsaw\UsingAddressing; -use SimpleSAML\WSSecurity\XML\wsdl\Definitions; use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; use SimpleSAML\WSSecurity\XML\wsp\Policy; @@ -44,8 +40,8 @@ use SimpleSAML\WSSecurity\XML\wst\EncryptWith; use SimpleSAML\WSSecurity\XML\wst\KeyType; use SimpleSAML\WSSecurity\XML\wst\KeyTypeEnum; +use SimpleSAML\WSSecurity\XML\wst\KeyWrapAlgorithm; use SimpleSAML\WSSecurity\XML\wst\SignatureAlgorithm; -*/ use SimpleSAML\XML\Attribute as XMLAttribute; /** @@ -72,8 +68,8 @@ public function __construct( public function getPolicies(): array { return [ -// $this->getCertificateWSTrustBinding(), -// $this->getUserNameWSTrustBindingPolicy(), + $this->getCertificateWSTrustBinding(), + $this->getUserNameWSTrustBindingPolicy(), // $this->getIssuedTokenWSTrustBinding(), // $this->getIssuedTokenWSTrustBinding1(), ]; @@ -87,13 +83,12 @@ public function getPolicies(): array */ private function getCertificateWSTrustBinding(): Policy { -/* $transportBinding = new TransportBinding( elements: [new Policy( children: [ new TransportToken( elements: [new Policy( - children: [new HttpsToken(false)], + children: [new HttpsToken()], )], ), new AlgorithmSuite( @@ -123,16 +118,16 @@ private function getCertificateWSTrustBinding(): Policy )], namespacedAttributes: [ new XMLAttribute( - C::NS_SEC_POLICY_11, + C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::AlwaysToRecipient->value, ), ], ), - new RsaToken( + new KeyValueToken( namespacedAttributes: [ - new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::Never->value), new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), ], ), @@ -156,7 +151,7 @@ private function getCertificateWSTrustBinding(): Policy )], ); - $trust10 = new Trust10( + $trust10 = new Trust13( elements: [new Policy( children: [ new MustSupportIssuedTokens(), @@ -169,7 +164,7 @@ private function getCertificateWSTrustBinding(): Policy $usingAddressing = new UsingAddressing(); return new Policy( - Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async_policy'), + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrust13Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( children: [ @@ -182,7 +177,6 @@ private function getCertificateWSTrustBinding(): Policy )], )], ); -*/ } @@ -193,13 +187,12 @@ private function getCertificateWSTrustBinding(): Policy */ private function getUserNameWSTrustBindingPolicy(): Policy { -/* $transportBinding = new TransportBinding( elements: [new Policy( children: [ new TransportToken( elements: [new Policy( - children: [new HttpsToken(false)], + children: [new HttpsToken()], )], ), new AlgorithmSuite( @@ -217,7 +210,7 @@ private function getUserNameWSTrustBindingPolicy(): Policy )], ); - $signedSupportingTokens = new SignedSupportingTokens( + $signedEncryptedSupportingTokens = new SignedEncryptedSupportingTokens( elements: [new Policy( children: [new UsernameToken( elts: [new Policy( @@ -225,7 +218,7 @@ private function getUserNameWSTrustBindingPolicy(): Policy )], namespacedAttributes: [ new XMLAttribute( - C::NS_SEC_POLICY_11, + C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::AlwaysToRecipient->value, @@ -238,9 +231,9 @@ private function getUserNameWSTrustBindingPolicy(): Policy $endorsingSupportingTokens = new EndorsingSupportingTokens( elements: [new Policy( children: [ - new RsaToken( + new KeyValueToken( namespacedAttributes: [ - new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::Never->value), new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), ], ), @@ -260,7 +253,7 @@ private function getUserNameWSTrustBindingPolicy(): Policy elements: [new Policy()], ); - $trust10 = new Trust10( + $trust13 = new Trust13( elements: [new Policy( children: [ new MustSupportIssuedTokens(), @@ -278,16 +271,15 @@ private function getUserNameWSTrustBindingPolicy(): Policy operatorContent: [new All( children: [ $transportBinding, - $signedSupportingTokens, + $signedEncryptedSupportingTokens, $endorsingSupportingTokens, $wss11, - $trust10, + $trust13, $usingAddressing, ], )], )], ); -*/ } @@ -298,13 +290,12 @@ private function getUserNameWSTrustBindingPolicy(): Policy */ private function getIssuedTokenWSTrustBinding(): Policy { -/* $transportBinding = new TransportBinding( elements: [new Policy( children: [ new TransportToken( elements: [new Policy( - children: [new HttpsToken(false)], + children: [new HttpsToken()], )], ), new AlgorithmSuite( @@ -330,6 +321,7 @@ private function getIssuedTokenWSTrustBinding(): Policy new RequestSecurityTokenTemplate( elts: [ new KeyType([KeyTypeEnum::PublicKey]), + new KeyWrapAlgorithm(C::KEY_TRANSPORT_OAEP_MGF1P), new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), new SignatureAlgorithm(C::SIG_RSA_SHA1), new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), @@ -344,16 +336,16 @@ private function getIssuedTokenWSTrustBinding(): Policy ], namespacedAttributes: [ new XMLAttribute( - C::NS_SEC_POLICY_11, + C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::AlwaysToRecipient->value, ), ], ), - new RsaToken( + new KeyValueToken( namespacedAttributes: [ - new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::Never->value), new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), ], ), @@ -374,7 +366,7 @@ private function getIssuedTokenWSTrustBinding(): Policy )], ); - $trust10 = new Trust10( + $trust13 = new Trust13( elements: [new Policy( children: [ new MustSupportIssuedTokens(), @@ -387,20 +379,19 @@ private function getIssuedTokenWSTrustBinding(): Policy $usingAddressing = new UsingAddressing(); return new Policy( - Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async_policy'), + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrust13Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( children: [ $transportBinding, $endorsingSupportingTokens, $wss11, - $trust10, + $trust13, $usingAddressing, ], )], )], ); -*/ } @@ -411,13 +402,12 @@ private function getIssuedTokenWSTrustBinding(): Policy */ private function getIssuedTokenWSTrustBinding1(): Policy { -/* $transportBinding = new TransportBinding( elements: [new Policy( children: [ new TransportToken( elements: [new Policy( - children: [new HttpsToken(false)], + children: [new HttpsToken()], )], ), new AlgorithmSuite( @@ -458,16 +448,16 @@ private function getIssuedTokenWSTrustBinding1(): Policy ], namespacedAttributes: [ new XMLAttribute( - C::NS_SEC_POLICY_11, + C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::AlwaysToRecipient->value, ), ], ), - new RsaToken( + new KeyValueToken( namespacedAttributes: [ - new XMLAttribute(C::NS_SEC_POLICY_11, 'sp', 'IncludeToken', IncludeToken::Never->value), + new XMLAttribute(C::NS_SEC_POLICY_12, 'sp', 'IncludeToken', IncludeToken::Never->value), new XMLAttribute(C::NS_POLICY, 'wsp', 'Optional', 'true'), ], ), @@ -483,20 +473,36 @@ private function getIssuedTokenWSTrustBinding1(): Policy )], ); + $wss11 = new Wss11( + elements: [new Policy( + )], + ); + + $trust13 = new Trust13( + elements: [new Policy( + children: [ + new MustSupportIssuedTokens(), + new RequireClientEntropy(), + new RequireServerEntropy(), + ], + )], + ); + + $usingAddressing = new UsingAddressing(); + return new Policy( - Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy'), + Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrust13Async1_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( children: [ $transportBinding, $endorsingSupportingTokens, $wss11, - $trust10, + $trust13, $usingAddressing, ], )], )], ); -*/ } } diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php index 9c01ead..ee1f2a4 100644 --- a/src/Trust/Policy2005.php +++ b/src/Trust/Policy2005.php @@ -34,7 +34,6 @@ use SimpleSAML\WSSecurity\XML\sp_200507\WssX509V3Token10; use SimpleSAML\WSSecurity\XML\sp_200507\X509Token; use SimpleSAML\WSSecurity\XML\wsaw\UsingAddressing; -use SimpleSAML\WSSecurity\XML\wsdl\Definitions; use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; use SimpleSAML\WSSecurity\XML\wsp\Policy; From 00bb6bc1c843a8fec8f85937ba55c5ab54c8ce4e Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 2 Oct 2024 23:38:31 +0200 Subject: [PATCH 19/51] Fix namespaces --- src/Trust/Policy13.php | 14 +++++++------- src/Trust/Policy2005.php | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php index 9ea4e79..c12aaff 100644 --- a/src/Trust/Policy13.php +++ b/src/Trust/Policy13.php @@ -35,13 +35,13 @@ use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; use SimpleSAML\WSSecurity\XML\wsp\Policy; -use SimpleSAML\WSSecurity\XML\wst\CanonicalizationAlgorithm; -use SimpleSAML\WSSecurity\XML\wst\EncryptionAlgorithm; -use SimpleSAML\WSSecurity\XML\wst\EncryptWith; -use SimpleSAML\WSSecurity\XML\wst\KeyType; -use SimpleSAML\WSSecurity\XML\wst\KeyTypeEnum; -use SimpleSAML\WSSecurity\XML\wst\KeyWrapAlgorithm; -use SimpleSAML\WSSecurity\XML\wst\SignatureAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200512\CanonicalizationAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200512\EncryptionAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200512\EncryptWith; +use SimpleSAML\WSSecurity\XML\wst_200512\KeyType; +use SimpleSAML\WSSecurity\XML\wst_200512\KeyTypeEnum; +use SimpleSAML\WSSecurity\XML\wst_200512\KeyWrapAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200512\SignatureAlgorithm; use SimpleSAML\XML\Attribute as XMLAttribute; /** diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php index ee1f2a4..eb538cd 100644 --- a/src/Trust/Policy2005.php +++ b/src/Trust/Policy2005.php @@ -37,13 +37,13 @@ use SimpleSAML\WSSecurity\XML\wsp\All; use SimpleSAML\WSSecurity\XML\wsp\ExactlyOne; use SimpleSAML\WSSecurity\XML\wsp\Policy; -use SimpleSAML\WSSecurity\XML\wst\CanonicalizationAlgorithm; -use SimpleSAML\WSSecurity\XML\wst\EncryptionAlgorithm; -use SimpleSAML\WSSecurity\XML\wst\EncryptWith; -use SimpleSAML\WSSecurity\XML\wst\KeySize; -use SimpleSAML\WSSecurity\XML\wst\KeyType; -use SimpleSAML\WSSecurity\XML\wst\KeyTypeEnum; -use SimpleSAML\WSSecurity\XML\wst\SignatureAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200502\CanonicalizationAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200502\EncryptionAlgorithm; +use SimpleSAML\WSSecurity\XML\wst_200502\EncryptWith; +use SimpleSAML\WSSecurity\XML\wst_200502\KeySize; +use SimpleSAML\WSSecurity\XML\wst_200502\KeyType; +use SimpleSAML\WSSecurity\XML\wst_200502\KeyTypeEnum; +use SimpleSAML\WSSecurity\XML\wst_200502\SignatureAlgorithm; use SimpleSAML\XML\Attribute as XMLAttribute; /** From 1e2d4ab5b0113a67afbe41ca111060d7ea8b145a Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 3 Oct 2024 09:33:49 +0200 Subject: [PATCH 20/51] Add IssuedToken policies --- src/Trust/Policy13.php | 47 ++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php index c12aaff..8093f70 100644 --- a/src/Trust/Policy13.php +++ b/src/Trust/Policy13.php @@ -17,7 +17,9 @@ use SimpleSAML\WSSecurity\XML\sp_200702\Layout; use SimpleSAML\WSSecurity\XML\sp_200702\MustSupportIssuedTokens; use SimpleSAML\WSSecurity\XML\sp_200702\MustSupportRefThumbprint; +use SimpleSAML\WSSecurity\XML\sp_200702\RequestSecurityTokenTemplate; use SimpleSAML\WSSecurity\XML\sp_200702\RequireClientEntropy; +use SimpleSAML\WSSecurity\XML\sp_200702\RequireInternalReference; use SimpleSAML\WSSecurity\XML\sp_200702\RequireServerEntropy; use SimpleSAML\WSSecurity\XML\sp_200702\RequireThumbprintReference; use SimpleSAML\WSSecurity\XML\sp_200702\SignedParts; @@ -38,6 +40,7 @@ use SimpleSAML\WSSecurity\XML\wst_200512\CanonicalizationAlgorithm; use SimpleSAML\WSSecurity\XML\wst_200512\EncryptionAlgorithm; use SimpleSAML\WSSecurity\XML\wst_200512\EncryptWith; +use SimpleSAML\WSSecurity\XML\wst_200512\KeySize; use SimpleSAML\WSSecurity\XML\wst_200512\KeyType; use SimpleSAML\WSSecurity\XML\wst_200512\KeyTypeEnum; use SimpleSAML\WSSecurity\XML\wst_200512\KeyWrapAlgorithm; @@ -70,8 +73,8 @@ public function getPolicies(): array return [ $this->getCertificateWSTrustBinding(), $this->getUserNameWSTrustBindingPolicy(), -// $this->getIssuedTokenWSTrustBinding(), -// $this->getIssuedTokenWSTrustBinding1(), + $this->getIssuedTokenWSTrustBinding(), + $this->getIssuedTokenWSTrustBinding1(), ]; } @@ -317,17 +320,17 @@ private function getIssuedTokenWSTrustBinding(): Policy elements: [new Policy( children: [ new IssuedToken( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::PublicKey]), + new KeyWrapAlgorithm(C::KEY_TRANSPORT_OAEP_MGF1P), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_RSA_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), elts: [ - new RequestSecurityTokenTemplate( - elts: [ - new KeyType([KeyTypeEnum::PublicKey]), - new KeyWrapAlgorithm(C::KEY_TRANSPORT_OAEP_MGF1P), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), - new SignatureAlgorithm(C::SIG_RSA_SHA1), - new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), - new EncryptionAlgorithm(C::BLOCK_ENC_AES256), - ], - ), new Policy( children: [ new RequireInternalReference(), @@ -429,17 +432,17 @@ private function getIssuedTokenWSTrustBinding1(): Policy elements: [new Policy( children: [ new IssuedToken( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new SignatureAlgorithm(C::SIG_HMAC_SHA1), + new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), + new EncryptionAlgorithm(C::BLOCK_ENC_AES256), + ], + ), elts: [ - new RequestSecurityTokenTemplate( - elts: [ - new KeyType([KeyTypeEnum::SymmetricKey]), - new KeySize('256'), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), - new SignatureAlgorithm(C::SIG_HMAC_SHA1), - new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), - new EncryptionAlgorithm(C::BLOCK_ENC_AES256), - ], - ), new Policy( children: [ new RequireInternalReference(), From adae2db6ed6df07bb1d1cc5101480aad5692c4a7 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 3 Oct 2024 10:41:00 +0200 Subject: [PATCH 21/51] Add wsdl:types and wsdl:message elements --- src/MetadataExchange.php | 106 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 0909895..7aaad8e 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -4,8 +4,22 @@ namespace SimpleSAML\Module\adfs; +use SimpleSAML\Module; use SimpleSAML\Module\adfs\Trust; -use SimpleSAML\WSSecurity\XML\wsdl\Definitions; +use SimpleSAML\WSSecurity\XML\wsdl\{Definitions, Message, Part, Types}; +use SimpleSAML\WSSecurity\XML\wst_200502\{ + RequestSecurityToken as RequestSecurityToken2005, + RequestSecurityTokenResponse as RequestSecurityTokenResponse2005, +}; +use SimpleSAML\WSSecurity\XML\wst_200512\{ + RequestSecurityToken as RequestSecurityToken13, + RequestSecurityTokenResponseCollection as RequestSecurityTokenResponseCollection13, +}; +use SimpleSAML\XML\Chunk; +use SimpleSAML\XML\DOMDocumentFactory; + +use function array_merge; +use function sprintf; /** * Common code for building MetaExchange (mex) documents based on the available configuration. @@ -20,8 +34,8 @@ class MetadataExchange * @param \SimpleSAML\Configuration $config The general configuration * @param \SimpleSAML\Configuration $metadata The metadata configuration */ - public function __construct( - ) { + public function __construct() + { } @@ -35,6 +49,12 @@ public function buildDocument(): Definitions return new Definitions( targetNamespace: 'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice', name: 'SecurityTokenService', + import: [], + types: $this->getTypes(), + message: $this->getMessages(), + portType: [], + binding: [], + service: [], elements: $this->getPolicies(), ); } @@ -55,4 +75,84 @@ private function getPolicies(): array $policy13->getPolicies(), ); } + + + /** + * This method builds the wsdl:types elements + * + * @param \SimpleSAML\WSSecurity\XML\wsdl\Types[] + */ + private function getTypes(): array + { + $defaultEndpoint = Module::getModuleURL('adfs/services/trust/mex'); + $xml = << + + + + +IMPORT; + + return [ + new Types([ + new Chunk(DOMDocumentFactory::fromString($xml)->documentElement), + ]), + ]; + } + + + /** + * This method builds the wsdl:message elements + * + * @param \SimpleSAML\WSSecurity\XML\wsdl\Message[] + */ + private function getMessages(): array + { + return [ + new Message( + 'IWSTrustFeb2005Async_TrustFeb2005IssueAsync_InputMessage', + [new Part( + 'request', + sprintf( + "%s:%s", + RequestSecurityToken2005::getNamespacePrefix(), + RequestSecurityToken2005::getLocalName(), + ), + )], + ), + new Message( + 'IWSTrustFeb2005Async_TrustFeb2005IssueAsync_OutputMessage', + [new Part( + 'TrustFeb2005IssueAsyncResult', + sprintf( + "%s:%s", + RequestSecurityTokenResponse2005::getNamespacePrefix(), + RequestSecurityTokenResponse2005::getLocalName(), + ), + )], + ), + new Message( + 'IWSTrust13Async_Trust13IssueAsync_InputMessage', + [new Part( + 'request', + sprintf( + "%s:%s", + RequestSecurityToken13::getNamespacePrefix(), + RequestSecurityToken13::getLocalName(), + ), + )], + ), + new Message( + 'IWSTrust13Async_Trust13IssueAsync_OutputMessage', + [new Part( + 'Trust13IssueAsyncResult', + sprintf( + "%s:%s", + RequestSecurityTokenResponseCollection13::getNamespacePrefix(), + RequestSecurityTokenResponseCollection13::getLocalName(), + ), + )], + ), + ]; + } } From 11e6f1ecaaef308f3df15dfb42a8350fb3d0ea75 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 3 Oct 2024 12:17:10 +0200 Subject: [PATCH 22/51] Add wsdl:portTtype elements --- src/MetadataExchange.php | 85 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 7aaad8e..3e31b2f 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -6,7 +6,17 @@ use SimpleSAML\Module; use SimpleSAML\Module\adfs\Trust; -use SimpleSAML\WSSecurity\XML\wsdl\{Definitions, Message, Part, Types}; +use SimpleSAML\WSSecurity\XML\wsdl\{ + Definitions, + Input, + Message, + Output, + Part, + PortType, + PortTypeOperation, + Types, +}; +use SimpleSAML\WSSecurity\Constants as C; use SimpleSAML\WSSecurity\XML\wst_200502\{ RequestSecurityToken as RequestSecurityToken2005, RequestSecurityTokenResponse as RequestSecurityTokenResponse2005, @@ -15,6 +25,7 @@ RequestSecurityToken as RequestSecurityToken13, RequestSecurityTokenResponseCollection as RequestSecurityTokenResponseCollection13, }; +use SimpleSAML\XML\Attribute as XMLAttribute; use SimpleSAML\XML\Chunk; use SimpleSAML\XML\DOMDocumentFactory; @@ -52,7 +63,7 @@ public function buildDocument(): Definitions import: [], types: $this->getTypes(), message: $this->getMessages(), - portType: [], + portType: $this->getPortTypes(), binding: [], service: [], elements: $this->getPolicies(), @@ -86,7 +97,9 @@ private function getTypes(): array { $defaultEndpoint = Module::getModuleURL('adfs/services/trust/mex'); $xml = << + @@ -155,4 +168,70 @@ private function getMessages(): array ), ]; } + + + /** + * This method builds the wsdl:portType elements + * + * @param \SimpleSAML\WSSecurity\XML\wsdl\PortType[] + */ + private function getPortTypes(): array + { + return [ + new PortType('IWSTrustFeb2005Async', [ + new PortTypeOperation( + name: 'TrustFeb2005IssueAsync', + input: new Input( + message: 'tns:IWSTrustFeb2005Async_TrustFeb2005IssueAsync_InputMessage', + attributes: [ + new XMLAttribute( + C::NS_WSDL_ADDR, + 'wsaw', + 'Action', + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + ), + ], + ), + output: new Output( + message: 'tns:IWSTrustFeb2005Async_TrustFeb2005IssueAsync_OutputMessage', + attributes: [ + new XMLAttribute( + C::NS_WSDL_ADDR, + 'wsaw', + 'Action', + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + ), + ], + ), + ), + ]), + new PortType('IWSTrust13Async', [ + new PortTypeOperation( + name: 'Trust13IssueAsync', + input: new Input( + message: 'tns:IWSTrust13Async_Trust13IssueAsync_InputMessage', + attributes: [ + new XMLAttribute( + C::NS_WSDL_ADDR, + 'wsaw', + 'Action', + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue', + ), + ], + ), + output: new Output( + message: 'tns:IWSTrust13Async_Trust13IssueAsync_OutputMessage', + attributes: [ + new XMLAttribute( + C::NS_WSDL_ADDR, + 'wsaw', + 'Action', + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal', + ), + ], + ), + ), + ]), + ]; + } } From 8e0093194e06f0f27150e041a6f981a3c026f7ee Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 8 Oct 2024 15:51:03 +0200 Subject: [PATCH 23/51] Add bindings --- composer.json | 2 ++ src/MetadataExchange.php | 59 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 6ffdc3d..c6f18e8 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,8 @@ "simplesamlphp/ws-security": "^1.6", "simplesamlphp/xml-common": "^1.16", "simplesamlphp/xml-security": "^1.9", + "simplesamlphp/xml-wsdl": "^1.1", + "simplesamlphp/ws-security": "^1.7", "symfony/http-foundation": "^6.4" }, "require-dev": { diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 3e31b2f..e5ea069 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -6,7 +6,16 @@ use SimpleSAML\Module; use SimpleSAML\Module\adfs\Trust; -use SimpleSAML\WSSecurity\XML\wsdl\{ +use SimpleSAML\WSDL\XML\soap12\{ + Binding as Soap12Binding, + Body as Soap12Body, + Operation as Soap12Operation, +}; +use SimpleSAML\WSDL\XML\wsdl\{ + Binding, + BindingOperation, + BindingOperationInput, + BindingOperationOutput, Definitions, Input, Message, @@ -17,6 +26,7 @@ Types, }; use SimpleSAML\WSSecurity\Constants as C; +use SimpleSAML\WSSecurity\XML\wsp\PolicyReference; use SimpleSAML\WSSecurity\XML\wst_200502\{ RequestSecurityToken as RequestSecurityToken2005, RequestSecurityTokenResponse as RequestSecurityTokenResponse2005, @@ -64,7 +74,7 @@ public function buildDocument(): Definitions types: $this->getTypes(), message: $this->getMessages(), portType: $this->getPortTypes(), - binding: [], + binding: $this->getBindings(), service: [], elements: $this->getPolicies(), ); @@ -234,4 +244,49 @@ private function getPortTypes(): array ]), ]; } + + + /** + * This method builds the wsdl:binding elements + * + * @param \SimpleSAML\WSSecurity\XML\wsdl\Binding[] + */ + private function getBindings(): array + { + return [ + new Binding( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async', + type: 'tns:IWSTrustFeb2005Async', + operation: [ + new BindingOperation( + name: 'TrustFeb2005IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#CertificateWSTrustBinding_IWSTrustFeb2005Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + ]; + } } From 76672ea98be6d38ab19b5d45f87aea42cc8d2def Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 8 Oct 2024 16:18:04 +0200 Subject: [PATCH 24/51] Import interoperability-test from ws-security lib --- .github/workflows/interoperability.yml | 80 +++++++++++++++++++ composer.json | 1 + phpunit-interoperability.xml | 8 ++ tests/InterOperability/DefinitionsTest.php | 53 ++++++++++++ tests/resources/interoperability/adfs_mex.xml | 1 + 5 files changed, 143 insertions(+) create mode 100644 .github/workflows/interoperability.yml create mode 100644 phpunit-interoperability.xml create mode 100644 tests/InterOperability/DefinitionsTest.php create mode 100644 tests/resources/interoperability/adfs_mex.xml diff --git a/.github/workflows/interoperability.yml b/.github/workflows/interoperability.yml new file mode 100644 index 0000000..c0d376e --- /dev/null +++ b/.github/workflows/interoperability.yml @@ -0,0 +1,80 @@ +--- + +name: Interoperability + +on: # yamllint disable-line rule:truthy + push: + branches: ['**'] + paths-ignore: + - '**.md' + - '**.yml' + pull_request: + branches: [master, release-*] + paths-ignore: + - '**.md' + - '**.yml' + workflow_dispatch: + +jobs: + edugain: + name: "Interoperability tests, PHP ${{ matrix.php-versions }}, ${{ matrix.operating-system }}" + runs-on: ${{ matrix.operating-system }} + strategy: + fail-fast: false + matrix: + operating-system: [ubuntu-latest] + php-versions: ['8.2'] + + steps: + - name: Setup PHP, with composer and extensions + # https://github.com/shivammathur/setup-php + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: ctype, date, dom, hash, mbstring, openssl, pcre, spl, xml + tools: composer:v2 + ini-values: error_reporting=E_ALL, memory_limit=-1 + coverage: none + + - name: Setup problem matchers for PHP + run: echo "::add-matcher::${{ runner.tool_cache }}/php.json" + + - name: Setup problem matchers for PHPUnit + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Set git to use LF + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v4 + + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: $(composer config cache-files-dir) + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Install Composer dependencies + run: composer install --no-progress --prefer-dist --optimize-autoloader + + - name: Get current date + id: date + run: | + echo "{date}={$(date +'%Y-%m-%d')}" >> "$GITHUB_STATE" + + - name: Cache metadata + id: cache-metadata + uses: actions/cache@v4 + with: + path: /tmp/metadata + key: ${{ runner.os }}-metadata-${{ env.date }} + restore-keys: ${{ runner.os }}-metadata- + + - name: Run unit tests + run: | + ./vendor/bin/phpunit -c phpunit-interoperability.xml diff --git a/composer.json b/composer.json index c6f18e8..0b40935 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ }, "autoload-dev": { "psr-4": { + "SimpleSAML\\Test\\Module\\adfs\\": "vendor/simplesamlphp/simplesamlphp/tests", "SimpleSAML\\Test\\Utils\\": "vendor/simplesamlphp/simplesamlphp/tests/Utils" } }, diff --git a/phpunit-interoperability.xml b/phpunit-interoperability.xml new file mode 100644 index 0000000..ebfec76 --- /dev/null +++ b/phpunit-interoperability.xml @@ -0,0 +1,8 @@ + + + + + ./tests/InterOperability + + + diff --git a/tests/InterOperability/DefinitionsTest.php b/tests/InterOperability/DefinitionsTest.php new file mode 100644 index 0000000..6a54c6b --- /dev/null +++ b/tests/InterOperability/DefinitionsTest.php @@ -0,0 +1,53 @@ +assertTrue($shouldPass); + } catch (Exception $e) { + fwrite(STDERR, $e->getFile() . '(' . strval($e->getLine()) . '):' . $e->getMessage()); + fwrite(STDERR, $e->getTraceAsString()); + $this->assertFalse($shouldPass); + } + } + + + /** + * @return array + */ + public static function provideMex(): array + { + return [ + 'MicrosoftAdfs' => [ + true, + DOMDocumentFactory::fromFile( + dirname(__FILE__, 2) . '/resources/interoperability/adfs_mex.xml', + )->documentElement, + ], + ]; + } +} diff --git a/tests/resources/interoperability/adfs_mex.xml b/tests/resources/interoperability/adfs_mex.xml new file mode 100644 index 0000000..425e90f --- /dev/null +++ b/tests/resources/interoperability/adfs_mex.xml @@ -0,0 +1 @@ +http://schemas.xmlsoap.org/ws/2005/02/trust/PublicKeyhttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2000/09/xmldsig#rsa-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey256http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://www.w3.org/2000/09/xmldsig#hmac-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKeyhttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2000/09/xmldsig#rsa-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttp://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey256http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1phttp://www.w3.org/2001/04/xmlenc#aes256-cbchttp://www.w3.org/2000/09/xmldsig#hmac-sha1http://www.w3.org/2001/10/xml-exc-c14n#http://www.w3.org/2001/04/xmlenc#aes256-cbchttps://adfs.example.org/adfs/services/trust/2005/certificatemixedhttps://certauth.adfs.example.org/adfs/services/trust/2005/certificatetransporthttps://adfs.example.org/adfs/services/trust/2005/usernamemixedhttps://adfs.example.org/adfs/services/trust/2005/issuedtokenmixedasymmetricbasic256https://adfs.example.org/adfs/services/trust/2005/issuedtokenmixedsymmetricbasic256https://adfs.example.org/adfs/services/trust/13/certificatemixedhttps://adfs.example.org/adfs/services/trust/13/usernamemixedhttps://adfs.example.org/adfs/services/trust/13/issuedtokenmixedasymmetricbasic256https://adfs.example.org/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256 From 7a757d8a5ea89741f986de6b00337cfa85aca9a7 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 8 Oct 2024 16:23:53 +0200 Subject: [PATCH 25/51] Fix coding style --- src/Trust/Policy13.php | 64 ++++++++++++++++++------------------ src/Trust/Policy2005.php | 70 ++++++++++++++++++++-------------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php index 8093f70..0b29965 100644 --- a/src/Trust/Policy13.php +++ b/src/Trust/Policy13.php @@ -22,8 +22,8 @@ use SimpleSAML\WSSecurity\XML\sp_200702\RequireInternalReference; use SimpleSAML\WSSecurity\XML\sp_200702\RequireServerEntropy; use SimpleSAML\WSSecurity\XML\sp_200702\RequireThumbprintReference; -use SimpleSAML\WSSecurity\XML\sp_200702\SignedParts; use SimpleSAML\WSSecurity\XML\sp_200702\SignedEncryptedSupportingTokens; +use SimpleSAML\WSSecurity\XML\sp_200702\SignedParts; use SimpleSAML\WSSecurity\XML\sp_200702\Strict; use SimpleSAML\WSSecurity\XML\sp_200702\TransportBinding; use SimpleSAML\WSSecurity\XML\sp_200702\TransportToken; @@ -58,8 +58,8 @@ class Policy13 * @param \SimpleSAML\Configuration $config The general configuration * @param \SimpleSAML\Configuration $metadata The metadata configuration */ - public function __construct( - ) { + public function __construct() + { } @@ -170,13 +170,13 @@ private function getCertificateWSTrustBinding(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrust13Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust10, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], )], )], ); @@ -272,14 +272,14 @@ private function getUserNameWSTrustBindingPolicy(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $signedEncryptedSupportingTokens, - $endorsingSupportingTokens, - $wss11, - $trust13, - $usingAddressing, - ], + children: [ + $transportBinding, + $signedEncryptedSupportingTokens, + $endorsingSupportingTokens, + $wss11, + $trust13, + $usingAddressing, + ], )], )], ); @@ -385,13 +385,13 @@ private function getIssuedTokenWSTrustBinding(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrust13Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust13, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust13, + $usingAddressing, + ], )], )], ); @@ -497,13 +497,13 @@ private function getIssuedTokenWSTrustBinding1(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrust13Async1_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust13, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust13, + $usingAddressing, + ], )], )], ); diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php index eb538cd..020083e 100644 --- a/src/Trust/Policy2005.php +++ b/src/Trust/Policy2005.php @@ -57,8 +57,8 @@ class Policy2005 * @param \SimpleSAML\Configuration $config The general configuration * @param \SimpleSAML\Configuration $metadata The metadata configuration */ - public function __construct( - ) { + public function __construct() + { } @@ -170,13 +170,13 @@ private function getCertificateWSTrustBinding(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust10, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], )], )], ); @@ -218,10 +218,10 @@ private function getCertificateWSTrustBinding1(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'CertificateWSTrustBinding_IWSTrustFeb2005Async1_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $usingAddressing, - ], + children: [ + $transportBinding, + $usingAddressing, + ], )], )], ); @@ -317,14 +317,14 @@ private function getUserNameWSTrustBindingPolicy(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $signedSupportingTokens, - $endorsingSupportingTokens, - $wss11, - $trust10, - $usingAddressing, - ], + children: [ + $transportBinding, + $signedSupportingTokens, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], )], )], ); @@ -429,13 +429,13 @@ private function getIssuedTokenWSTrustBinding(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust10, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], )], )], ); @@ -541,13 +541,13 @@ private function getIssuedTokenWSTrustBinding1(): Policy Id: new XMLAttribute(C::NS_SEC_UTIL, 'wsu', 'Id', 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy'), operatorContent: [new ExactlyOne( operatorContent: [new All( - children: [ - $transportBinding, - $endorsingSupportingTokens, - $wss11, - $trust10, - $usingAddressing, - ], + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], )], )], ); From 9236250f2f8ad943c8217d4a4cdf9ebed16f7ffc Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 8 Oct 2024 20:18:09 +0200 Subject: [PATCH 26/51] Add last binding and service --- src/MetadataExchange.php | 379 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 378 insertions(+), 1 deletion(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index e5ea069..0fd6d1c 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -7,6 +7,7 @@ use SimpleSAML\Module; use SimpleSAML\Module\adfs\Trust; use SimpleSAML\WSDL\XML\soap12\{ + Address as Soap12Address, Binding as Soap12Binding, Body as Soap12Body, Operation as Soap12Operation, @@ -21,11 +22,14 @@ Message, Output, Part, + Port, PortType, PortTypeOperation, + Service, Types, }; use SimpleSAML\WSSecurity\Constants as C; +use SimpleSAML\WSSecurity\XML\wsa_200408\{Address, EndpointReference}; use SimpleSAML\WSSecurity\XML\wsp\PolicyReference; use SimpleSAML\WSSecurity\XML\wst_200502\{ RequestSecurityToken as RequestSecurityToken2005, @@ -75,7 +79,7 @@ public function buildDocument(): Definitions message: $this->getMessages(), portType: $this->getPortTypes(), binding: $this->getBindings(), - service: [], + service: $this->getServices(), elements: $this->getPolicies(), ); } @@ -287,6 +291,379 @@ private function getBindings(): array new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), ], ), + new Binding( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async1', + type: 'tns:IWSTrustFeb2005Async', + operation: [ + new BindingOperation( + name: 'TrustFeb2005IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#CertificateWSTrustBinding_IWSTrustFeb2005Async1_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'UserNameWSTrustBinding_IWSTrustFeb2005Async', + type: 'tns:IWSTrustFeb2005Async', + operation: [ + new BindingOperation( + name: 'TrustFeb2005IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#UserNameWSTrustBinding_IWSTrustFeb2005Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', + type: 'tns:IWSTrustFeb2005Async', + operation: [ + new BindingOperation( + name: 'TrustFeb2005IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#IssuedTokenWSTrustBinding_IWSTrustFeb2005Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', + type: 'tns:IWSTrustFeb2005Async', + operation: [ + new BindingOperation( + name: 'TrustFeb2005IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'CertificateWSTrustBinding_IWSTrust13Async', + type: 'tns:IWSTrust13Async', + operation: [ + new BindingOperation( + name: 'Trust13IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#CertificateWSTrustBinding_IWSTrust13Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'UserNameWSTrustBinding_IWSTrust13Async', + type: 'tns:IWSTrust13Async', + operation: [ + new BindingOperation( + name: 'Trust13IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#UserNameWSTrustBinding_IWSTrust13Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'IssuedTokenWSTrustBinding_IWSTrust13Async', + type: 'tns:IWSTrust13Async', + operation: [ + new BindingOperation( + name: 'Trust13IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#IssuedTokenWSTrustBinding_IWSTrust13Async_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + new Binding( + name: 'IssuedTokenWSTrustBinding_IWSTrust13Async1', + type: 'tns:IWSTrust13Async', + operation: [ + new BindingOperation( + name: 'Trust13IssueAsync', + input: new BindingOperationInput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + output: new BindingOperationOutput( + elements: [ + new Soap12Body(null, null, 'literal'), + ], + ), + elements: [ + new Soap12Operation( + 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue', + null, + 'document', + ), + ], + ), + ], + elements: [ + new PolicyReference( + URI: '#IssuedTokenWSTrustBinding_IWSTrust13Async1_policy', + DigestAlgorithm: null, + ), + new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), + ], + ), + ]; + } + + + /** + * This method builds the wsdl:service elements + * + * @param \SimpleSAML\WSSecurity\XML\wsdl\Service[] + */ + private function getServices(): array + { + $defaultEndpoint = Module::getModuleURL('adfs/services/trust/'); + + return [ + new Service( + name: 'SecurityTokenService', + ports: [ + new Port( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . '2005/certificatemixed'), + new EndpointReference( + new Address($defaultEndpoint . '2005/certificatemixed'), + ), + ], + ), + new Port( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async1', + binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async1', + elements: [ + new Soap12Address($defaultEndpoint . '2005/certificatetransport'), + new EndpointReference( + new Address($defaultEndpoint . '2005/certificatetransport'), + ), + ], + ), + new Port( + name: 'UserNameWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:UserNameWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . '2005/usernamemixed'), + new EndpointReference( + new Address($defaultEndpoint . '2005/usernamemixed'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . '2005/issuedtokenmixedasymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . '2005/issuedtokenmixedasymmetricbasic256'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', + elements: [ + new Soap12Address($defaultEndpoint . '2005/issuedtokenmixedsymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . '2005/issuedtokenmixedsymmetricbasic256'), + ), + ], + ), + new Port( + name: 'CertificateWSTrustBinding_IWSTrust13Async', + binding: 'tns:CertificateWSTrustBinding_IWSTrust13Async', + elements: [ + new Soap12Address($defaultEndpoint . '13/certificatemixed'), + new EndpointReference( + new Address($defaultEndpoint . '13/certificatemixed'), + ), + ], + ), + new Port( + name: 'UserNameWSTrustBinding_IWSTrust13Async', + binding: 'tns:UserNameWSTrustBinding_IWSTrust13Async', + elements: [ + new Soap12Address($defaultEndpoint . '13/usernamemixed'), + new EndpointReference( + new Address($defaultEndpoint . '13/usernamemixed'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrust13Async', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrust13Async', + elements: [ + new Soap12Address($defaultEndpoint . '13/issuedtokenmixedasymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . '13/issuedtokenmixedasymmetricbasic256'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrust13Async1', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrust13Async1', + elements: [ + new Soap12Address($defaultEndpoint . '13/issuedtokenmixedsymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . '13/issuedtokenmixedsymmetricbasic256'), + ), + ], + ), + ], + ), ]; } } From e4dbf55c3113cfded16c455b92722f31751ddb21 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 14 Oct 2024 14:11:53 +0200 Subject: [PATCH 27/51] Add missing namespaces --- src/Controller/Adfs.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index d3e2cf2..a14378d 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -161,6 +161,31 @@ public function mex(Request $request): Response $document->ownerDocument->formatOutput = false; $document->ownerDocument->encoding = 'UTF-8'; + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:tns', + 'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice', + ); + + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:soapenc', + 'http://schemas.xmlsoap.org/soap/encoding/' + ); + + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:msc', + 'http://schemas.microsoft.com/ws/2005/12/wsdl/contract' + ); + + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:wsam', + 'http://www.w3.org/2007/05/addressing/metadata' + ); + + $metaxml = $document->ownerDocument->saveXML(); $response = new Response(); From 51cb9bc8bcb69fdec1de4d7f1dfe40db540fe05e Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 16 Oct 2024 15:37:17 +0200 Subject: [PATCH 28/51] Add yet another namespace decl --- src/Controller/Adfs.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index a14378d..5afbed1 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -185,6 +185,11 @@ public function mex(Request $request): Response 'http://www.w3.org/2007/05/addressing/metadata' ); + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:wsap', + 'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy' + ); $metaxml = $document->ownerDocument->saveXML(); From 0a0ed1443ee909a14431b321a368d47ef93c5553 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 16 Oct 2024 17:57:44 +0200 Subject: [PATCH 29/51] Fix constant --- src/Trust/Policy2005.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php index 020083e..4c98ff8 100644 --- a/src/Trust/Policy2005.php +++ b/src/Trust/Policy2005.php @@ -480,7 +480,7 @@ private function getIssuedTokenWSTrustBinding1(): Policy elts: [ new KeyType([KeyTypeEnum::SymmetricKey]), new KeySize('256'), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new EncryptWith(C::BLOCK_ENC_AES256), new SignatureAlgorithm(C::SIG_HMAC_SHA1), new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), new EncryptionAlgorithm(C::BLOCK_ENC_AES256), From 6db319bf6f834a7bacbb58402ac3c3cbf43efd92 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 16 Oct 2024 18:04:36 +0200 Subject: [PATCH 30/51] Fix constant --- src/Trust/Policy13.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php index 0b29965..9473b8a 100644 --- a/src/Trust/Policy13.php +++ b/src/Trust/Policy13.php @@ -436,7 +436,8 @@ private function getIssuedTokenWSTrustBinding1(): Policy elts: [ new KeyType([KeyTypeEnum::SymmetricKey]), new KeySize('256'), - new EncryptWith(C::KEY_TRANSPORT_OAEP_MGF1P), + new KeyWrapAlgorithm(C::KEY_TRANSPORT_OAEP_MGF1P), + new EncryptWith(C::BLOCK_ENC_AES256), new SignatureAlgorithm(C::SIG_HMAC_SHA1), new CanonicalizationAlgorithm(C::C14N_EXCLUSIVE_WITHOUT_COMMENTS), new EncryptionAlgorithm(C::BLOCK_ENC_AES256), From c7ccc08669082871d70e27140f8b5cc3b4e0f1eb Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 16 Oct 2024 18:07:56 +0200 Subject: [PATCH 31/51] Fix typo --- src/MetadataExchange.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 0fd6d1c..316f006 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -213,7 +213,7 @@ private function getPortTypes(): array C::NS_WSDL_ADDR, 'wsaw', 'Action', - 'http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue', + 'http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue', ), ], ), From da3aedf5c4ed9bad5df3be8faaf0819c91cd042e Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 16 Oct 2024 18:08:51 +0200 Subject: [PATCH 32/51] Use correct version of ws-addressing --- src/MetadataExchange.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 316f006..302ed88 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -29,7 +29,7 @@ Types, }; use SimpleSAML\WSSecurity\Constants as C; -use SimpleSAML\WSSecurity\XML\wsa_200408\{Address, EndpointReference}; +use SimpleSAML\WSSecurity\XML\wsa_200508\{Address, EndpointReference}; use SimpleSAML\WSSecurity\XML\wsp\PolicyReference; use SimpleSAML\WSSecurity\XML\wst_200502\{ RequestSecurityToken as RequestSecurityToken2005, From 660c9c053fabb7716049b9727c28771e6db34f46 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 17 Oct 2024 10:47:47 +0200 Subject: [PATCH 33/51] Disable anything WS-trust 1.3 related - May remove later --- src/MetadataExchange.php | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 302ed88..3af19a8 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -74,8 +74,8 @@ public function buildDocument(): Definitions return new Definitions( targetNamespace: 'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice', name: 'SecurityTokenService', - import: [], - types: $this->getTypes(), +// import: [], +// types: $this->getTypes(), message: $this->getMessages(), portType: $this->getPortTypes(), binding: $this->getBindings(), @@ -97,7 +97,7 @@ private function getPolicies(): array return array_merge( $policy2005->getPolicies(), - $policy13->getPolicies(), +// $policy13->getPolicies(), ); } @@ -158,6 +158,7 @@ private function getMessages(): array ), )], ), +/* new Message( 'IWSTrust13Async_Trust13IssueAsync_InputMessage', [new Part( @@ -180,6 +181,7 @@ private function getMessages(): array ), )], ), +*/ ]; } @@ -219,6 +221,7 @@ private function getPortTypes(): array ), ), ]), +/* new PortType('IWSTrust13Async', [ new PortTypeOperation( name: 'Trust13IssueAsync', @@ -246,6 +249,7 @@ private function getPortTypes(): array ), ), ]), +*/ ]; } @@ -423,6 +427,7 @@ private function getBindings(): array new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), ], ), +/* new Binding( name: 'CertificateWSTrustBinding_IWSTrust13Async', type: 'tns:IWSTrust13Async', @@ -555,6 +560,7 @@ private function getBindings(): array new Soap12Binding('http://schemas.xmlsoap.org/soap/http'), ], ), +*/ ]; } @@ -566,7 +572,7 @@ private function getBindings(): array */ private function getServices(): array { - $defaultEndpoint = Module::getModuleURL('adfs/services/trust/'); + $defaultEndpoint = Module::getModuleURL('adfs/ws-trust/2005/services/'); return [ new Service( @@ -576,9 +582,9 @@ private function getServices(): array name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async', binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async', elements: [ - new Soap12Address($defaultEndpoint . '2005/certificatemixed'), + new Soap12Address($defaultEndpoint . 'certificatemixed'), new EndpointReference( - new Address($defaultEndpoint . '2005/certificatemixed'), + new Address($defaultEndpoint . 'certificatemixed'), ), ], ), @@ -586,9 +592,9 @@ private function getServices(): array name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async1', binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async1', elements: [ - new Soap12Address($defaultEndpoint . '2005/certificatetransport'), + new Soap12Address($defaultEndpoint . 'certificatetransport'), new EndpointReference( - new Address($defaultEndpoint . '2005/certificatetransport'), + new Address($defaultEndpoint . 'certificatetransport'), ), ], ), @@ -596,9 +602,9 @@ private function getServices(): array name: 'UserNameWSTrustBinding_IWSTrustFeb2005Async', binding: 'tns:UserNameWSTrustBinding_IWSTrustFeb2005Async', elements: [ - new Soap12Address($defaultEndpoint . '2005/usernamemixed'), + new Soap12Address($defaultEndpoint . 'usernamemixed'), new EndpointReference( - new Address($defaultEndpoint . '2005/usernamemixed'), + new Address($defaultEndpoint . 'usernamemixed'), ), ], ), @@ -606,9 +612,9 @@ private function getServices(): array name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', elements: [ - new Soap12Address($defaultEndpoint . '2005/issuedtokenmixedasymmetricbasic256'), + new Soap12Address($defaultEndpoint . 'issuedtokenmixedasymmetricbasic256'), new EndpointReference( - new Address($defaultEndpoint . '2005/issuedtokenmixedasymmetricbasic256'), + new Address($defaultEndpoint . 'issuedtokenmixedasymmetricbasic256'), ), ], ), @@ -616,12 +622,13 @@ private function getServices(): array name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', elements: [ - new Soap12Address($defaultEndpoint . '2005/issuedtokenmixedsymmetricbasic256'), + new Soap12Address($defaultEndpoint . 'issuedtokenmixedsymmetricbasic256'), new EndpointReference( - new Address($defaultEndpoint . '2005/issuedtokenmixedsymmetricbasic256'), + new Address($defaultEndpoint . 'issuedtokenmixedsymmetricbasic256'), ), ], ), +/* new Port( name: 'CertificateWSTrustBinding_IWSTrust13Async', binding: 'tns:CertificateWSTrustBinding_IWSTrust13Async', @@ -662,6 +669,7 @@ private function getServices(): array ), ], ), +*/ ], ), ]; From 7ff5c0a4c33abfbde95848216bb68beb24cc988a Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 17 Oct 2024 10:48:48 +0200 Subject: [PATCH 34/51] Add usernamemixed endpoint --- composer.json | 1 + routing/routes/routes.yml | 7 +++ src/Controller/Adfs.php | 30 +++++++++++ src/IdP/ADFS.php | 108 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 142 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 0b40935..9786a3b 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "simplesamlphp/ws-security": "^1.6", "simplesamlphp/xml-common": "^1.16", "simplesamlphp/xml-security": "^1.9", + "simplesamlphp/xml-soap": "^1.5", "simplesamlphp/xml-wsdl": "^1.1", "simplesamlphp/ws-security": "^1.7", "symfony/http-foundation": "^6.4" diff --git a/routing/routes/routes.yml b/routing/routes/routes.yml index 60264d4..d9bed6b 100644 --- a/routing/routes/routes.yml +++ b/routing/routes/routes.yml @@ -34,3 +34,10 @@ adfs-wstrust-mex: _controller: 'SimpleSAML\Module\adfs\Controller\Adfs::mex' } methods: [GET] + +adfs-wstrust-usernamemixed: + path: /ws-trust/2005/services/usernamemixed + defaults: { + _controller: 'SimpleSAML\Module\adfs\Controller\Adfs::usernamemixed' + } + methods: [POST] diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index 5afbed1..2cf5765 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -7,11 +7,16 @@ use Exception; use SimpleSAML\{Configuration, IdP, Logger, Metadata, Module, Session, Utils}; use SimpleSAML\Error as SspError; +use SimpleSAML\Metadata\MetaDataStorageHandler; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; use SimpleSAML\Module\adfs\MetadataExchange; +use SimpleSAML\SOAP\XML\env_200305\Envelope; +use SimpleSAML\XML\DOMDocumentFactory; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; +use function array_pop; + /** * Controller class for the adfs module. * @@ -204,4 +209,29 @@ public function mex(Request $request): Response return $response; } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response + */ + public function usernamemixed(Request $request): Response + { + if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) { + throw new SspError\Error('NOACCESS'); + } + + $soapMessage = $request->getContent(); + if ($soapMessage === false) { + throw new SspError\BadRequest('Missing SOAP-content.'); + } + + $domDocument = DOMDocumentFactory::fromString($soapMessage); + $soapEnvelope = Envelope::fromXML($domDocument->documentElement); + + $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); + $idp = IdP::getById('adfs:' . $idpEntityId); + + return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp); + } } diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index eb1b0af..6e0e96c 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -8,6 +8,7 @@ use DateTimeImmutable; use DateTimeZone; use Exception; +use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\IdP; @@ -26,12 +27,12 @@ use SimpleSAML\SAML11\XML\saml\Conditions; use SimpleSAML\SAML11\XML\saml\NameIdentifier; use SimpleSAML\SAML11\XML\saml\Subject; +use SimpleSAML\SOAP\XML\env_200305\Envelope; use SimpleSAML\Utils; -use SimpleSAML\WSSecurity\XML\wsa_200508\Address; -use SimpleSAML\WSSecurity\XML\wsa_200508\EndpointReference; +use SimpleSAML\WSSecurity\XML\wsa_200508\{Action, Address, EndpointReference, MessageID, To}; use SimpleSAML\WSSecurity\XML\wsp\AppliesTo; -use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityToken; -use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityTokenResponse; +use SimpleSAML\WSSecurity\XML\wsse\{Password, Security, Username, UsernameToken}; +use SimpleSAML\WSSecurity\XML\wst_200502\{RequestSecurityToken, RequestSecurityTokenResponse}; use SimpleSAML\XHTML\Template; use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory; use SimpleSAML\XMLSecurity\Key\PrivateKey; @@ -45,6 +46,7 @@ use function chunk_split; use function trim; +use function array_pop; use function base64_encode; use function chunk_split; use function trim; @@ -52,6 +54,94 @@ class ADFS { /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \SimpleSAML\SOAP\XML\env_200305\Envelope $soapEnvelope + * @param \SimpleSAML\IdP $idp + * @throws \SimpleSAML\Error\MetadataNotFound + */ + public static function receivePassiveAuthnRequest( + Request $request, + Envelope $soapEnvelope, + IdP $idp, + ): StreamedResponse { + // Parse the SOAP-header + $header = $soapEnvelope->getHeader(); + + $to = To::getChildrenOfClass($header->toXML()); + Assert::count($to, 1, 'Missing To in SOAP Header.'); + $to = array_pop($to); + + $action = Action::getChildrenOfClass($header->toXML()); + Assert::count($action, 1, 'Missing Action in SOAP Header.'); + $action = array_pop($action); + + $messageid = MessageID::getChildrenOfClass($header->toXML()); + Assert::count($messageid, 1, 'Missing MessageID in SOAP Header.'); + $messageid = array_pop($messageid); + + $security = Security::getChildrenOfClass($header->toXML()); + Assert::count($security, 1, 'Missing Security in SOAP Header.'); + $security = array_pop($security); + + // Parse the SOAP-body + $body = $soapEnvelope->getBody(); + + $requestSecurityToken = RequestSecurityToken::getChildrenOfClass($body->toXML()); + Assert::count($requestSecurityToken, 1, 'Missing RequestSecurityToken in SOAP Body.'); + $requestSecurityToken = array_pop($requestSecurityToken); + + $appliesTo = AppliesTo::getChildrenOfClass($requestSecurityToken->toXML()); + Assert::count($appliesTo, 1, 'Missing AppliesTo in RequestSecurityToken.'); + $appliesTo = array_pop($appliesTo); + + $endpointReference = EndpointReference::getChildrenOfClass($appliesTo->toXML()); + Assert::count($endpointReference, 1, 'Missing EndpointReference in AppliesTo.'); + $endpointReference = array_pop($endpointReference); + + // Make sure the message was addressed to us. + if ($to === null || $request->server->get('SCRIPT_URI') !== $to->getContent()) { + throw new Error\BadRequest('This server is not the audience for the message received.'); + } + + // Ensure we know the issuer + $issuer = $endpointReference->getAddress()->getContent(); + //$idp = IdP::getById($this->config, 'adfs:' . $issuer); + + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); + $spMetadata = $metadata->getMetaDataConfig($issuer, 'adfs-sp-remote'); + + $usernameToken = UsernameToken::getChildrenOfClass($security->toXML()); + Assert::count($usernameToken, 1, 'Missing UsernameToken in Security.'); + $usernameToken = array_pop($usernameToken); + + $username = $usernameToken->getUsername(); + $password = Password::getChildrenOfClass($usernameToken->toXML()); + $password = array_pop($password); + + if ($username === null || $password === null) { + throw new Error\BadRequest('Missing username or password in SOAP header.'); + } else { + $_SERVER['PHP_AUTH_USER'] = $username->getContent(); + $_SERVER['PHP_AUTH_PW'] = $password->getContent(); + } + + $state = [ + 'Responder' => [ADFS::class, 'sendPassiveResponse'], + 'SPMetadata' => $spMetadata->toArray(), + // Dirty hack to leverage the SAML ECP logics + 'saml:Binding' => C::BINDING_PAOS, + ]; + + return new StreamedResponse( + function () use ($idp, &$state) { + $idp->handleAuthenticationRequest($state); + }, + ); + } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\IdP $idp * @throws \SimpleSAML\Error\MetadataNotFound */ @@ -400,6 +490,16 @@ public static function getHostedMetadata(string $entityid, MetaDataStorageHandle } + /** + * @param array $state + * @throws \Exception + */ + public static function sendPassiveResponse(array $state): void + { + \SimpleSAML\Logger::debug(var_export($state, true)); + } + + /** * @param array $state * @throws \Exception From fd1c8e2f190097e3b2209cb66a6b495cfa68bdc1 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 22 Oct 2024 23:40:36 +0200 Subject: [PATCH 35/51] Fix --- src/Controller/Adfs.php | 14 +- src/IdP/PassiveIdP.php | 325 +++++++++++++++++++++++++++++++++++++++ src/MetadataExchange.php | 17 +- 3 files changed, 340 insertions(+), 16 deletions(-) create mode 100644 src/IdP/PassiveIdP.php diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index 2cf5765..7fed11a 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -7,16 +7,14 @@ use Exception; use SimpleSAML\{Configuration, IdP, Logger, Metadata, Module, Session, Utils}; use SimpleSAML\Error as SspError; -use SimpleSAML\Metadata\MetaDataStorageHandler; use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP; use SimpleSAML\Module\adfs\IdP\MetadataBuilder; +use SimpleSAML\Module\adfs\IdP\PassiveIdP; use SimpleSAML\Module\adfs\MetadataExchange; use SimpleSAML\SOAP\XML\env_200305\Envelope; use SimpleSAML\XML\DOMDocumentFactory; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; -use function array_pop; - /** * Controller class for the adfs module. * @@ -175,25 +173,25 @@ public function mex(Request $request): Response $document->setAttributeNS( 'http://www.w3.org/2000/xmlns/', 'xmlns:soapenc', - 'http://schemas.xmlsoap.org/soap/encoding/' + 'http://schemas.xmlsoap.org/soap/encoding/', ); $document->setAttributeNS( 'http://www.w3.org/2000/xmlns/', 'xmlns:msc', - 'http://schemas.microsoft.com/ws/2005/12/wsdl/contract' + 'http://schemas.microsoft.com/ws/2005/12/wsdl/contract', ); $document->setAttributeNS( 'http://www.w3.org/2000/xmlns/', 'xmlns:wsam', - 'http://www.w3.org/2007/05/addressing/metadata' + 'http://www.w3.org/2007/05/addressing/metadata', ); $document->setAttributeNS( 'http://www.w3.org/2000/xmlns/', 'xmlns:wsap', - 'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy' + 'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy', ); $metaxml = $document->ownerDocument->saveXML(); @@ -230,7 +228,7 @@ public function usernamemixed(Request $request): Response $soapEnvelope = Envelope::fromXML($domDocument->documentElement); $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); - $idp = IdP::getById('adfs:' . $idpEntityId); + $idp = PassiveIdP::getById('adfs:' . $idpEntityId); return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp); } diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php new file mode 100644 index 0000000..4ddb924 --- /dev/null +++ b/src/IdP/PassiveIdP.php @@ -0,0 +1,325 @@ +id = $id; + + $this->globalConfig = $config; + $metadata = MetaDataStorageHandler::getMetadataHandler($this->globalConfig); + + if (substr($id, 0, 5) === 'adfs:') { + if (!$this->globalConfig->getOptionalBoolean('enable.adfs-idp', false)) { + throw new Error\Exception('enable.adfs-idp disabled in config.php.'); + } + $this->config = $metadata->getMetaDataConfig(substr($id, 5), 'adfs-idp-hosted'); + } else { + throw new Exception("Protocol not implemented."); + } + + $auth = $this->config->getString('passiveAuth'); + if (Auth\Source::getById($auth) !== null) { + $this->authSource = new Auth\Simple($auth); + } else { + throw new Error\Exception('No such "' . $auth . '" auth source found.'); + } + } + + + /** + * Retrieve the ID of this IdP. + * + * @return string The ID of this IdP. + */ + public function getId(): string + { + return $this->id; + } + + + /** + * Retrieve an IdP by ID. + * + * @param \SimpleSAML\Configuration $config The Configuration + * @param string $id The identifier of the IdP. + * + * @return \SimpleSAML\IdP The IdP. + */ + public static function getById(Configuration $config, string $id): IdP + { + if (isset(self::$idpCache[$id])) { + return self::$idpCache[$id]; + } + + $idp = new self($config, $id); + self::$idpCache[$id] = $idp; + return $idp; + } + + + /** + * Retrieve the IdP "owning" the state. + * + * @param \SimpleSAML\Configuration $config The Configuration. + * @param array &$state The state array. + * + * @return \SimpleSAML\IdP The IdP. + */ + public static function getByState(Configuration $config, array &$state): IdP + { + Assert::notNull($state['core:IdP']); + + return self::getById($config, $state['core:IdP']); + } + + + /** + * Retrieve the configuration for this IdP. + * + * @return Configuration The configuration object. + */ + public function getConfig(): Configuration + { + return $this->config; + } + + + /** + * Is the current user authenticated? + * + * @return boolean True if the user is authenticated, false otherwise. + */ + public function isAuthenticated(): bool + { + return $this->authSource->isAuthenticated(); + } + + + /** + * Called after authproc has run. + * + * @param array $state The authentication request state array. + */ + public static function postAuthProc(array $state): Response + { + Assert::isCallable($state['Responder']); + + if (isset($state['core:SP'])) { + $session = Session::getSessionFromRequest(); + $session->setData( + 'core:idp-ssotime', + $state['core:IdP'] . ';' . $state['core:SP'], + time(), + Session::DATA_TIMEOUT_SESSION_END, + ); + } + + $response = call_user_func($state['Responder'], $state); + Assert::isInstanceOf($response, Response::class); + return $response; + } + + + /** + * The user is authenticated. + * + * @param array $state The authentication request state array. + * + * @throws \SimpleSAML\Error\Exception If we are not authenticated. + */ + public static function postAuth(array $state): Response + { + $idp = IdP::getByState(Configuration::getInstance(), $state); + + if (!$idp->isAuthenticated()) { + throw new Error\Exception('Not authenticated.'); + } + + $state['Attributes'] = $idp->authSource->getAttributes(); + + if (isset($state['SPMetadata'])) { + $spMetadata = $state['SPMetadata']; + } else { + $spMetadata = []; + } + + if (isset($state['core:SP'])) { + $session = Session::getSessionFromRequest(); + $previousSSOTime = $session->getData('core:idp-ssotime', $state['core:IdP'] . ';' . $state['core:SP']); + if ($previousSSOTime !== null) { + $state['PreviousSSOTimestamp'] = $previousSSOTime; + } + } + + $idpMetadata = $idp->getConfig()->toArray(); + + $pc = new Auth\ProcessingChain($idpMetadata, $spMetadata, 'idp'); + + $state['ReturnCall'] = ['\SimpleSAML\Module\adfs\IdP\PassiveIdP', 'postAuthProc']; + $state['Destination'] = $spMetadata; + $state['Source'] = $idpMetadata; + + $pc->processState($state); + + return self::postAuthProc($state); + } + + + /** + * Authenticate the user. + * + * This function authenticates the user. + * + * @param array &$state The authentication request state. + */ + private function authenticate(array &$state): Response + { + return $this->authSource->login($state); + } + + + /** + * Process authentication requests. + * + * @param array &$state The authentication request state. + */ + public function handleAuthenticationRequest(array &$state): Response + { + Assert::notNull($state['Responder']); + + $state['core:IdP'] = $this->id; + + if (isset($state['SPMetadata']['entityid'])) { + $spEntityId = $state['SPMetadata']['entityid']; + } elseif (isset($state['SPMetadata']['entityID'])) { + $spEntityId = $state['SPMetadata']['entityID']; + } else { + $spEntityId = null; + } + + $state['core:SP'] = $spEntityId; + $state['IdPMetadata'] = $this->getConfig()->toArray(); + $state['ReturnCallback'] = ['\SimpleSAML\Module\saml\IdP\PassiveIdP', 'postAuth']; + + try { + return $this->authenticate($state); + } catch (Error\Exception $e) { + Auth\State::throwException($state, $e); + } catch (Exception $e) { + $e = new Error\UnserializableException($e); + Auth\State::throwException($state, $e); + } + + throw new Exception('Should never happen.'); + } + + + /** + * Find the logout handler of this IdP. + * + * @return \SimpleSAML\IdP\LogoutHandlerInterface The logout handler class. + * + * @throws \Exception If we cannot find a logout handler. + */ + public function getLogoutHandler(): LogoutHandlerInterface + { + // find the logout handler + $logouttype = $this->getConfig()->getOptionalString('logouttype', 'traditional'); + switch ($logouttype) { + case 'traditional': + $handler = TraditionalLogoutHandler::class; + break; + case 'iframe': + $handler = IFrameLogoutHandler::class; + break; + default: + throw new Error\Exception('Unknown logout handler: ' . var_export($logouttype, true)); + } + + /** @var \SimpleSAML\IdP\LogoutHandlerInterface */ + return new $handler($this); + } + + + /** + * Finish the logout operation. + * + * This function will never return. + * + * @param array &$state The logout request state. + */ + public function finishLogout(array &$state): Response + { + Assert::notNull($state['Responder']); + return call_user_func($state['Responder'], $state); + } +} diff --git a/src/MetadataExchange.php b/src/MetadataExchange.php index 3af19a8..6136fda 100644 --- a/src/MetadataExchange.php +++ b/src/MetadataExchange.php @@ -40,8 +40,9 @@ RequestSecurityTokenResponseCollection as RequestSecurityTokenResponseCollection13, }; use SimpleSAML\XML\Attribute as XMLAttribute; -use SimpleSAML\XML\Chunk; -use SimpleSAML\XML\DOMDocumentFactory; + +//use SimpleSAML\XML\Chunk; +//use SimpleSAML\XML\DOMDocumentFactory; use function array_merge; use function sprintf; @@ -74,8 +75,8 @@ public function buildDocument(): Definitions return new Definitions( targetNamespace: 'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice', name: 'SecurityTokenService', -// import: [], -// types: $this->getTypes(), + //import: [], + //types: $this->getTypes(), message: $this->getMessages(), portType: $this->getPortTypes(), binding: $this->getBindings(), @@ -97,7 +98,7 @@ private function getPolicies(): array return array_merge( $policy2005->getPolicies(), -// $policy13->getPolicies(), + //$policy13->getPolicies(), ); } @@ -106,7 +107,6 @@ private function getPolicies(): array * This method builds the wsdl:types elements * * @param \SimpleSAML\WSSecurity\XML\wsdl\Types[] - */ private function getTypes(): array { $defaultEndpoint = Module::getModuleURL('adfs/services/trust/mex'); @@ -126,6 +126,7 @@ private function getTypes(): array ]), ]; } + */ /** @@ -628,7 +629,7 @@ private function getServices(): array ), ], ), -/* + /* new Port( name: 'CertificateWSTrustBinding_IWSTrust13Async', binding: 'tns:CertificateWSTrustBinding_IWSTrust13Async', @@ -669,7 +670,7 @@ private function getServices(): array ), ], ), -*/ + */ ], ), ]; From 8bcad6f7936cb3c3d6c07ab5539788a95b5dab1e Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 15:58:04 +0200 Subject: [PATCH 36/51] Fix rebase: remove duplicate use-statements --- src/IdP/ADFS.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 6e0e96c..e6588a5 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -42,10 +42,6 @@ use SimpleSAML\XMLSecurity\XML\ds\X509Data; use Symfony\Component\HttpFoundation\{Request, StreamedResponse}; -use function base64_encode; -use function chunk_split; -use function trim; - use function array_pop; use function base64_encode; use function chunk_split; From 7a8d3d0d3c9520ddad8fe4d8b24486bcce6d7e87 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:20:55 +0200 Subject: [PATCH 37/51] Fix namespace --- src/IdP/PassiveIdP.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index 4ddb924..94e8382 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace SimpleSAML\Module\adfs; +namespace SimpleSAML\Module\adfs\IdP; use Exception; use SimpleSAML\{Auth, Configuration, Error, Utils}; From 23ef3ab00a16313124376d520302833e489d7545 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:24:52 +0200 Subject: [PATCH 38/51] Fix missing parameter --- src/Controller/Adfs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Adfs.php b/src/Controller/Adfs.php index 7fed11a..c17e849 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -228,7 +228,7 @@ public function usernamemixed(Request $request): Response $soapEnvelope = Envelope::fromXML($domDocument->documentElement); $idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted'); - $idp = PassiveIdP::getById('adfs:' . $idpEntityId); + $idp = PassiveIdP::getById($this->config, 'adfs:' . $idpEntityId); return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp); } From 6e41286edd0f6ac04f3086f84791922498fb5fd3 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:25:54 +0200 Subject: [PATCH 39/51] Fix return type --- src/IdP/PassiveIdP.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index 94e8382..b015b07 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -112,9 +112,9 @@ public function getId(): string * @param \SimpleSAML\Configuration $config The Configuration * @param string $id The identifier of the IdP. * - * @return \SimpleSAML\IdP The IdP. + * @return \SimpleSAML\Module\adfs\IdP\PassiveIdP The IdP. */ - public static function getById(Configuration $config, string $id): IdP + public static function getById(Configuration $config, string $id): PassiveIdP { if (isset(self::$idpCache[$id])) { return self::$idpCache[$id]; @@ -132,9 +132,9 @@ public static function getById(Configuration $config, string $id): IdP * @param \SimpleSAML\Configuration $config The Configuration. * @param array &$state The state array. * - * @return \SimpleSAML\IdP The IdP. + * @return \SimpleSAML\Module\adfs\IdP\PassiveIdP The IdP. */ - public static function getByState(Configuration $config, array &$state): IdP + public static function getByState(Configuration $config, array &$state): PassiveIdP { Assert::notNull($state['core:IdP']); From 48340e7b30212446a800c54ff83b0da92032fb5b Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:28:30 +0200 Subject: [PATCH 40/51] Fix parameter type --- src/IdP/ADFS.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index e6588a5..71da313 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -52,13 +52,13 @@ class ADFS /** * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\SOAP\XML\env_200305\Envelope $soapEnvelope - * @param \SimpleSAML\IdP $idp + * @param \SimpleSAML\Module\adfs\IdP\PassiveIdP $idp * @throws \SimpleSAML\Error\MetadataNotFound */ public static function receivePassiveAuthnRequest( Request $request, Envelope $soapEnvelope, - IdP $idp, + PassiveIdP $idp, ): StreamedResponse { // Parse the SOAP-header $header = $soapEnvelope->getHeader(); From 116056e04fa77e4d9c699354b769fc3acff6a6a0 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:29:09 +0200 Subject: [PATCH 41/51] Fix constant --- src/IdP/ADFS.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 71da313..5e8eb47 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -125,7 +125,7 @@ public static function receivePassiveAuthnRequest( 'Responder' => [ADFS::class, 'sendPassiveResponse'], 'SPMetadata' => $spMetadata->toArray(), // Dirty hack to leverage the SAML ECP logics - 'saml:Binding' => C::BINDING_PAOS, + 'saml:Binding' => SAML2_C::BINDING_PAOS, ]; return new StreamedResponse( From 2634e056d71a8ec84e33fc6c31d0b4dae0c1cc09 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:31:37 +0200 Subject: [PATCH 42/51] Fix namespace for callback --- src/IdP/PassiveIdP.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index b015b07..ab88508 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -268,7 +268,7 @@ public function handleAuthenticationRequest(array &$state): Response $state['core:SP'] = $spEntityId; $state['IdPMetadata'] = $this->getConfig()->toArray(); - $state['ReturnCallback'] = ['\SimpleSAML\Module\saml\IdP\PassiveIdP', 'postAuth']; + $state['ReturnCallback'] = ['\SimpleSAML\Module\adfs\IdP\PassiveIdP', 'postAuth']; try { return $this->authenticate($state); From 40581d90a73d303e10a9092871c730202b635f76 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:33:49 +0200 Subject: [PATCH 43/51] Fix namespace --- src/IdP/PassiveIdP.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index ab88508..1fd47f4 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -198,7 +198,7 @@ public static function postAuthProc(array $state): Response */ public static function postAuth(array $state): Response { - $idp = IdP::getByState(Configuration::getInstance(), $state); + $idp = PassiveIdP::getByState(Configuration::getInstance(), $state); if (!$idp->isAuthenticated()) { throw new Error\Exception('Not authenticated.'); From 9634e9aa0cc1ee40cb02b7d973d914e28af6129c Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 23 Oct 2024 16:35:06 +0200 Subject: [PATCH 44/51] Fix missing use-statement --- src/IdP/PassiveIdP.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index 1fd47f4..2ac720c 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -5,7 +5,7 @@ namespace SimpleSAML\Module\adfs\IdP; use Exception; -use SimpleSAML\{Auth, Configuration, Error, Utils}; +use SimpleSAML\{Auth, Configuration, Error, Session, Utils}; use SimpleSAML\Assert\Assert; use SimpleSAML\IdP\{IFrameLogoutHandler, LogoutHandlerInterface, TraditionalLogoutHandler}; use SimpleSAML\Metadata\MetaDataStorageHandler; From f1d23f10ab28158164387f2fa1d81e1727aba0ee Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 14 Nov 2024 20:23:20 +0100 Subject: [PATCH 45/51] Passive response --- src/IdP/ADFS.php | 119 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 5e8eb47..6fbf748 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -192,7 +192,7 @@ function () use ($idp, &$state) { * @param int $assertionLifetime * @return \SimpleSAML\SAML11\XML\saml\Assertion */ - private static function generateAssertion( + private static function generateActiveAssertion( string $issuer, string $target, string $nameid, @@ -268,6 +268,79 @@ private static function generateAssertion( } + /** + * @param string $issuer + * @param string $target + * @param string $nameid + * @param array $attributes + * @param int $assertionLifetime + * @return \SimpleSAML\SAML11\XML\saml\Assertion + */ + private static function generatePassiveAssertion( + string $issuer, + string $target, + string $nameid, + array $attributes, + int $assertionLifetime, + ): Assertion { + $httpUtils = new Utils\HTTP(); + $randomUtils = new Utils\Random(); + $timeUtils = new Utils\Time(); + + $issueInstant = $timeUtils->generateTimestamp(); + $notBefore = DateInterval::createFromDateString('30 seconds'); + $notOnOrAfter = DateInterval::createFromDateString(sprintf('%d seconds', $assertionLifetime)); + $assertionID = $randomUtils->generateID(); +// $nameidFormat = 'http://schemas.xmlsoap.org/claims/UPN'; +// $nameid = htmlspecialchars($nameid); + $now = new DateTimeImmutable('now', new DateTimeZone('Z')); + + if ($httpUtils->isHTTPS()) { + $method = SAML2_C::AC_PASSWORD_PROTECTED_TRANSPORT; + } else { + $method = C::AC_PASSWORD; + } + + $audience = new Audience($target); + $audienceRestrictionCondition = new AudienceRestrictionCondition([$audience]); + $conditions = new Conditions( + [$audienceRestrictionCondition], + [], + [], + $now->sub($notBefore), + $now->add($notOnOrAfter), + ); + + $nameIdentifier = new NameIdentifier($nameid, null, C::NAMEID_UNSPECIFIED); + $subject = new Subject(null, $nameIdentifier); + + $authenticationStatement = new AuthenticationStatement($subject, $method, $now); + + $attrs = []; + $attrs[] = new Attribute( + 'UPN', + 'http://schemas.xmlsoap.org/claims', + $attributes['userPrincipalName'][0], + ); + $attrs[] = new Attribute( + 'ImmutableID', + 'http://schemas.microsoft.com/LiveID/Federation/2008/05', + $attributes['ms-DS-ConsistencyGuid'][0], + ); + + $attributeStatement = new AttributeStatement($subject, $attrs); + + return new Assertion( + $assertionID, + $issuer, + $now, + $conditions, + null, // Advice + [$authenticationStatement, $attributeStatement], + ); + } + + /** * @param \SimpleSAML\SAML11\XML\saml\Assertion $assertion * @param string $key @@ -492,7 +565,45 @@ public static function getHostedMetadata(string $entityid, MetaDataStorageHandle */ public static function sendPassiveResponse(array $state): void { - \SimpleSAML\Logger::debug(var_export($state, true)); + $idp = IdP::getByState($state); + $idpMetadata = $idp->getConfig(); + $idpEntityId = $idpMetadata->getString('entityid'); + + $spMetadata = $state['SPMetadata']; + $spEntityId = $spMetadata['entityid']; + $spMetadata = Configuration::loadFromArray( + $spMetadata, + '$metadata[' . var_export($spEntityId, true) . ']', + ); + + $assertionLifetime = $spMetadata->getOptionalInteger('assertion.lifetime', null); + if ($assertionLifetime === null) { + $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); + } + + $attributes = $state['Attributes']; + $nameid = $attributes['ms-DS-ConsistencyGuid'][0]; + + $assertion = ADFS::generateActiveAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); + + $privateKeyCfg = $idpMetadata->getOptionalString('privatekey', null); + $certificateCfg = $idpMetadata->getOptionalString('certificate', null); + + if ($privateKeyCfg !== null && $certificateCfg !== null) { + $configUtils = new Utils\Config(); + $privateKeyFile = $configUtils->getCertPath($privateKeyCfg); + $certificateFile = $configUtils->getCertPath($certificateCfg); + $passphrase = $idpMetadata->getOptionalString('privatekey_pass', null); + + $algo = $spMetadata->getOptionalString('signature.algorithm', null); + if ($algo === null) { + $algo = $idpMetadata->getOptionalString('signature.algorithm', C::SIG_RSA_SHA256); + } + + $assertion = ADFS::signAssertion($assertion, $privateKeyFile, $certificateFile, $algo, $passphrase); + $assertion = Assertion::fromXML($assertion->toXML()); + } + \SimpleSAML\Logger::debug($assertion->toXML()->ownerDocument->saveXML()); } @@ -502,7 +613,7 @@ public static function sendPassiveResponse(array $state): void */ public static function sendResponse(array $state): void { - $spMetadata = $state["SPMetadata"]; + $spMetadata = $state['SPMetadata']; $spEntityId = $spMetadata['entityid']; $spMetadata = Configuration::loadFromArray( $spMetadata, @@ -537,7 +648,7 @@ public static function sendResponse(array $state): void $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); } - $assertion = ADFS::generateAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); + $assertion = ADFS::generateActiveAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); $privateKeyCfg = $idpMetadata->getOptionalString('privatekey', null); $certificateCfg = $idpMetadata->getOptionalString('certificate', null); From 4a1287c80672ede3a87d939b40ccef7105bdc4c0 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 9 Jan 2025 23:41:10 +0100 Subject: [PATCH 46/51] Bump dependencies --- composer.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 9786a3b..c70a8d6 100644 --- a/composer.json +++ b/composer.json @@ -40,21 +40,20 @@ "beste/clock": "^3.0", "psr/clock": "^1.0", - "simplesamlphp/assert": "^1.1", - "simplesamlphp/saml11": "^1.0", + "simplesamlphp/assert": "~1.8.0", + "simplesamlphp/saml11": "~1.2.0", "simplesamlphp/saml2": "^5@dev", - "simplesamlphp/simplesamlphp": "^2.4", - "simplesamlphp/ws-security": "^1.6", - "simplesamlphp/xml-common": "^1.16", - "simplesamlphp/xml-security": "^1.9", - "simplesamlphp/xml-soap": "^1.5", - "simplesamlphp/xml-wsdl": "^1.1", - "simplesamlphp/ws-security": "^1.7", + "simplesamlphp/simplesamlphp": "~2.4.0", + "simplesamlphp/ws-security": "~1.8.0", + "simplesamlphp/xml-common": "~1.24.0", + "simplesamlphp/xml-security": "~1.13.0", + "simplesamlphp/xml-soap": "~1.7.0", + "simplesamlphp/xml-wsdl": "~1.2.0", + "simplesamlphp/ws-security": "~1.9.0", "symfony/http-foundation": "^6.4" }, "require-dev": { - "simplesamlphp/simplesamlphp-test-framework": "^1.6", - "simplesamlphp/xml-security": "^1.7" + "simplesamlphp/simplesamlphp-test-framework": "~1.8.0" }, "support": { "issues": "https://github.com/simplesamlphp/simplesamlphp-module-adfs/issues", @@ -64,5 +63,6 @@ "branch-alias": { "dev-master": "3.0.x-dev" } - } + }, + "version": "2.4.0" } From c7662d367753127618823c1db5d825e6fbd48863 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Fri, 10 Jan 2025 15:29:49 +0100 Subject: [PATCH 47/51] Bump dependencies --- composer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index c70a8d6..6ab82fc 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ "simplesamlphp/assert": "~1.8.0", "simplesamlphp/saml11": "~1.2.0", "simplesamlphp/saml2": "^5@dev", - "simplesamlphp/simplesamlphp": "~2.4.0", + "simplesamlphp/simplesamlphp": "^2.4", "simplesamlphp/ws-security": "~1.8.0", "simplesamlphp/xml-common": "~1.24.0", "simplesamlphp/xml-security": "~1.13.0", @@ -63,6 +63,5 @@ "branch-alias": { "dev-master": "3.0.x-dev" } - }, - "version": "2.4.0" + } } From a19d91e741b72e018c70038a51aebdad0d81c1c7 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Wed, 15 Jan 2025 12:47:12 +0100 Subject: [PATCH 48/51] Re-add code that got lost earlier --- composer.json | 1 - src/IdP/ADFS.php | 94 +++++++++++++++++++++++++++++++++++------- src/IdP/PassiveIdP.php | 7 ++-- 3 files changed, 83 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 6ab82fc..bf02cf7 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,6 @@ "simplesamlphp/saml11": "~1.2.0", "simplesamlphp/saml2": "^5@dev", "simplesamlphp/simplesamlphp": "^2.4", - "simplesamlphp/ws-security": "~1.8.0", "simplesamlphp/xml-common": "~1.24.0", "simplesamlphp/xml-security": "~1.13.0", "simplesamlphp/xml-soap": "~1.7.0", diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 6fbf748..14e095b 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -26,14 +26,19 @@ use SimpleSAML\SAML11\XML\saml\AuthenticationStatement; use SimpleSAML\SAML11\XML\saml\Conditions; use SimpleSAML\SAML11\XML\saml\NameIdentifier; -use SimpleSAML\SAML11\XML\saml\Subject; -use SimpleSAML\SOAP\XML\env_200305\Envelope; +use SimpleSAML\SAML11\XML\saml\{ConfirmationMethod, Subject, SubjectConfirmation}; +use SimpleSAML\SOAP\Constants as SOAP_C; +use SimpleSAML\SOAP\XML\env_200305\{Body, Envelope, Header}; use SimpleSAML\Utils; -use SimpleSAML\WSSecurity\XML\wsa_200508\{Action, Address, EndpointReference, MessageID, To}; +use SimpleSAML\WSSecurity\XML\wsa_200508\{Action, Address, EndpointReference, MessageID, RelatesTo, To}; use SimpleSAML\WSSecurity\XML\wsp\AppliesTo; -use SimpleSAML\WSSecurity\XML\wsse\{Password, Security, Username, UsernameToken}; -use SimpleSAML\WSSecurity\XML\wst_200502\{RequestSecurityToken, RequestSecurityTokenResponse}; +use SimpleSAML\WSSecurity\XML\wsse\{KeyIdentifier, Password, Security, SecurityTokenReference, Username, UsernameToken}; +use SimpleSAML\WSSecurity\XML\wst_200502\{KeyType, KeyTypeEnum, Lifetime, RequestType, RequestTypeEnum, TokenType}; +use SimpleSAML\WSSecurity\XML\wst_200502\{RequestedSecurityToken, RequestSecurityToken, RequestSecurityTokenResponse}; +use SimpleSAML\WSSecurity\XML\wst_200502\{RequestedAttachedReference, RequestedUnattachedReference}; +use SimpleSAML\WSSecurity\XML\wsu\{Created, Expires, Timestamp}; use SimpleSAML\XHTML\Template; +use SimpleSAML\XML\Attribute as XMLAttribute; use SimpleSAML\XMLSecurity\Alg\Signature\SignatureAlgorithmFactory; use SimpleSAML\XMLSecurity\Key\PrivateKey; use SimpleSAML\XMLSecurity\Key\X509Certificate as PublicKey; @@ -124,6 +129,7 @@ public static function receivePassiveAuthnRequest( $state = [ 'Responder' => [ADFS::class, 'sendPassiveResponse'], 'SPMetadata' => $spMetadata->toArray(), + 'MessageID' => $messageid->getContent(), // Dirty hack to leverage the SAML ECP logics 'saml:Binding' => SAML2_C::BINDING_PAOS, ]; @@ -291,8 +297,6 @@ private static function generatePassiveAssertion( $notBefore = DateInterval::createFromDateString('30 seconds'); $notOnOrAfter = DateInterval::createFromDateString(sprintf('%d seconds', $assertionLifetime)); $assertionID = $randomUtils->generateID(); -// $nameidFormat = 'http://schemas.xmlsoap.org/claims/UPN'; -// $nameid = htmlspecialchars($nameid); $now = new DateTimeImmutable('now', new DateTimeZone('Z')); if ($httpUtils->isHTTPS()) { @@ -312,7 +316,7 @@ private static function generatePassiveAssertion( ); $nameIdentifier = new NameIdentifier($nameid, null, C::NAMEID_UNSPECIFIED); - $subject = new Subject(null, $nameIdentifier); + $subject = new Subject(new SubjectConfirmation([new ConfirmationMethod(C::CM_BEARER)]), $nameIdentifier); $authenticationStatement = new AuthenticationStatement($subject, $method, $now); @@ -320,12 +324,12 @@ private static function generatePassiveAssertion( $attrs[] = new Attribute( 'UPN', 'http://schemas.xmlsoap.org/claims', - $attributes['userPrincipalName'][0], + [new AttributeValue($attributes['http://schemas.xmlsoap.org/claims/UPN'][0])], ); $attrs[] = new Attribute( 'ImmutableID', 'http://schemas.microsoft.com/LiveID/Federation/2008/05', - $attributes['ms-DS-ConsistencyGuid'][0], + [new AttributeValue($attributes['http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID'][0])], ); $attributeStatement = new AttributeStatement($subject, $attrs); @@ -336,7 +340,7 @@ private static function generatePassiveAssertion( $now, $conditions, null, // Advice - [$authenticationStatement, $attributeStatement], + [$attributeStatement, $authenticationStatement], ); } @@ -581,10 +585,14 @@ public static function sendPassiveResponse(array $state): void $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); } + $now = new DateTimeImmutable('now', new DateTimeZone('Z')); + $created = $now->sub(DateInterval::createFromDateString(sprintf('30 seconds'))); + $expires = $now->add(DateInterval::createFromDateString(sprintf('%d seconds', $assertionLifetime))); + $attributes = $state['Attributes']; - $nameid = $attributes['ms-DS-ConsistencyGuid'][0]; + $nameid = $state['saml:NameID'][SAML2_C::NAMEID_UNSPECIFIED]; - $assertion = ADFS::generateActiveAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); + $assertion = ADFS::generatePassiveAssertion($idpEntityId, $spEntityId, $nameid->getValue(), $attributes, $assertionLifetime); $privateKeyCfg = $idpMetadata->getOptionalString('privatekey', null); $certificateCfg = $idpMetadata->getOptionalString('certificate', null); @@ -603,7 +611,65 @@ public static function sendPassiveResponse(array $state): void $assertion = ADFS::signAssertion($assertion, $privateKeyFile, $certificateFile, $algo, $passphrase); $assertion = Assertion::fromXML($assertion->toXML()); } - \SimpleSAML\Logger::debug($assertion->toXML()->ownerDocument->saveXML()); + + $requestedSecurityToken = new RequestedSecurityToken($assertion); + $lifetime = new LifeTime(new Created($created), new Expires($expires)); + $appliesTo = new AppliesTo([new EndpointReference(new Address($spEntityId))]); + + $requestedAttachedReference = new RequestedAttachedReference( + new SecurityTokenReference(null, null, [ + new KeyIdentifier( + $assertion->getId(), + 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID', + ), + ]), + ); + $requestedUnattachedReference = new RequestedUnattachedReference( + new SecurityTokenReference(null, null, [ + new KeyIdentifier( + $assertion->getId(), + 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID', + ), + ]), + ); + $tokenType = new TokenType(C::NS_SAML); + $requestType = new RequestType([RequestTypeEnum::Issue]); + $keyType = new KeyType(['http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey']); + + $requestSecurityTokenResponse = new RequestSecurityTokenResponse(null, [ + $lifetime, + $appliesTo, + $requestedSecurityToken, + $requestedAttachedReference, + $requestedUnattachedReference, + $tokenType, + $requestType, + $keyType, + ]); + + // Build envelope + $mustUnderstand = new XMLAttribute(SOAP_C::NS_SOAP_ENV_12, 'env', 'mustUnderstand', '1'); + $header = new Header([ + new Action('http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue', [$mustUnderstand]), + new RelatesTo($state['MessageID'], null), + new Security( + [ + new Timestamp( + new Created($created), + new Expires($expires), + ), + ], + [$mustUnderstand], + ), + ]); + $body = new Body(null, [$requestSecurityTokenResponse]); + $envelope = new Envelope($body, $header); + + $xmlResponse = $envelope->toXML(); + \SimpleSAML\Logger::debug($xmlResponse->ownerDocument->saveXML($xmlResponse)); + + echo $xmlResponse->ownerDocument->saveXML($xmlResponse); + exit(); } diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php index 2ac720c..4ee5ed1 100644 --- a/src/IdP/PassiveIdP.php +++ b/src/IdP/PassiveIdP.php @@ -169,7 +169,7 @@ public function isAuthenticated(): bool * * @param array $state The authentication request state array. */ - public static function postAuthProc(array $state): Response + public static function postAuthProc(array $state): void { Assert::isCallable($state['Responder']); @@ -183,9 +183,8 @@ public static function postAuthProc(array $state): Response ); } - $response = call_user_func($state['Responder'], $state); - Assert::isInstanceOf($response, Response::class); - return $response; + call_user_func($state['Responder'], $state); + Assert::true(false); } From 9af556233d185af7ee99b35e8134bfd20aec1b00 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 16 Jan 2025 11:08:34 +0100 Subject: [PATCH 49/51] Consider wauth-parameter when processing request --- src/IdP/ADFS.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 14e095b..17e2d66 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -164,6 +164,11 @@ public static function receiveAuthnRequest(Request $request, IdP $idp): Streamed $username = (string) $request->query->get('username'); } + $wauth = null; + if ($request->query->has('wauth')) { + $wauth = (string) $request->query->get('wauth'); + } + $state = [ 'Responder' => [ADFS::class, 'sendResponse'], 'SPMetadata' => $spMetadata->toArray(), @@ -177,6 +182,10 @@ public static function receiveAuthnRequest(Request $request, IdP $idp): Streamed $state['core:username'] = $username; } + if ($wauth !== null) { + $state['saml:RequestedAuthnContext'] = ['AuthnContextClassRef' => [$wauth]]; + } + if (isset($query['wreply']) && !empty($query['wreply'])) { $httpUtils = new Utils\HTTP(); $state['adfs:wreply'] = $httpUtils->checkURLAllowed($query['wreply']); @@ -196,6 +205,7 @@ function () use ($idp, &$state) { * @param string $nameid * @param array $attributes * @param int $assertionLifetime + * @param string $method * @return \SimpleSAML\SAML11\XML\saml\Assertion */ private static function generateActiveAssertion( @@ -204,6 +214,7 @@ private static function generateActiveAssertion( string $nameid, array $attributes, int $assertionLifetime, + string $method, ): Assertion { $httpUtils = new Utils\HTTP(); $randomUtils = new Utils\Random(); @@ -217,12 +228,6 @@ private static function generateActiveAssertion( $nameid = htmlspecialchars($nameid); $now = new DateTimeImmutable('now', new DateTimeZone('Z')); - if ($httpUtils->isHTTPS()) { - $method = SAML2_C::AC_PASSWORD_PROTECTED_TRANSPORT; - } else { - $method = C::AC_PASSWORD; - } - $audience = new Audience($target); $audienceRestrictionCondition = new AudienceRestrictionCondition([$audience]); $conditions = new Conditions( @@ -714,7 +719,15 @@ public static function sendResponse(array $state): void $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); } - $assertion = ADFS::generateActiveAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime); + if (isset($state['saml:AuthnContextClassRef'])) { + $method = $state['saml:AuthnContextClassRef']; + } elseif ((new Utils\HTTP())->isHTTPS()) { + $method = SAML2_C::AC_PASSWORD_PROTECTED_TRANSPORT; + } else { + $method = C::AC_PASSWORD; + } + + $assertion = ADFS::generateActiveAssertion($idpEntityId, $spEntityId, $nameid, $attributes, $assertionLifetime, $method); $privateKeyCfg = $idpMetadata->getOptionalString('privatekey', null); $certificateCfg = $idpMetadata->getOptionalString('certificate', null); From 5916072cb93f71de20121705254fd8fa97590321 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 16 Jan 2025 12:23:35 +0100 Subject: [PATCH 50/51] Log the active response under debug-logging --- src/IdP/ADFS.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 17e2d66..9fc8b2b 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -753,6 +753,7 @@ public static function sendResponse(array $state): void $xmlResponse = $requestSecurityTokenResponse->toXML(); $wresult = $xmlResponse->ownerDocument->saveXML($xmlResponse); + Logger::debug($wresult); $wctx = $state['adfs:wctx']; $wreply = $state['adfs:wreply'] ? : $spMetadata->getValue('prp'); ADFS::postResponse($wreply, $wresult, $wctx); From 080befd13e32e80d746380e9e97008dd2bcfd6a5 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Mon, 17 Feb 2025 20:41:41 +0100 Subject: [PATCH 51/51] Add debug logging --- src/IdP/ADFS.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 9fc8b2b..1891ed1 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -50,6 +50,7 @@ use function array_pop; use function base64_encode; use function chunk_split; +use function str_replace; use function trim; class ADFS @@ -106,7 +107,6 @@ public static function receivePassiveAuthnRequest( // Ensure we know the issuer $issuer = $endpointReference->getAddress()->getContent(); - //$idp = IdP::getById($this->config, 'adfs:' . $issuer); $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); $spMetadata = $metadata->getMetaDataConfig($issuer, 'adfs-sp-remote'); @@ -126,6 +126,10 @@ public static function receivePassiveAuthnRequest( $_SERVER['PHP_AUTH_PW'] = $password->getContent(); } + $requestSecurityTokenStr = $requestSecurityToken->toXML()->ownerDocument->saveXML(); + $requestSecurityTokenStr = str_replace($password->getContent(), '*****', $requestSecurityTokenStr); + Logger::debug($requestSecurityTokenStr); + $state = [ 'Responder' => [ADFS::class, 'sendPassiveResponse'], 'SPMetadata' => $spMetadata->toArray(), @@ -754,6 +758,7 @@ public static function sendResponse(array $state): void $xmlResponse = $requestSecurityTokenResponse->toXML(); $wresult = $xmlResponse->ownerDocument->saveXML($xmlResponse); Logger::debug($wresult); + $wctx = $state['adfs:wctx']; $wreply = $state['adfs:wreply'] ? : $spMetadata->getValue('prp'); ADFS::postResponse($wreply, $wresult, $wctx);