diff --git a/composer.json b/composer.json index 09d58e7..8dfd333 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "simplesamlphp/simplesamlphp": "^2.3", "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 d0e5ded..ceb92e4 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 f37c621..5639503 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -10,6 +10,12 @@ 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\WSSecurity\XML\wsa_200508\{Action, EndpointReference, MessageID, To}; +use SimpleSAML\WSSecurity\XML\wsp\AppliesTo; +use SimpleSAML\WSSecurity\XML\wsse\Security; +use SimpleSAML\WSSecurity\XML\wst_200502\RequestSecurityToken; +use SimpleSAML\XML\DOMDocumentFactory; use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse}; /** @@ -205,4 +211,68 @@ 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); + + $header = $soapEnvelope->getHeader(); + $body = $soapEnvelope->getBody(); + + $to = $action = $messageid = $security = null; + foreach ($header->getElements() as $elt) { + if ($elt instanceof To) { + $to = $elt; + } elseif ($elt instanceof Action) { + $action = $elt; + } elseif ($elt instanceof MessageID) { + $messageid = $elt; + } elseif ($elt instanceof Security) { + $security = $elt; + } + } + + $requestSecurityToken = null; + foreach ($body->getElements() as $elt) { + if ($elt instanceof RequestSecurityToken) { + $requestSecurityToken = $elt; + } + } + + $appliesTo = null; + foreach ($requestSecurityToken->getElements() as $elt) { + if ($elt instanceof AppliesTo) { + $appliesTo = $elt; + } + } + + $endpointReference = null; + foreach ($appliesTo->getElements() as $elt) { + if ($elt instanceof EndpointReference) { + $endpointReference = $elt; + } + } + + // Make sure the message was addressed to us. + if ($to === null || $request->server->get('SCRIPT_URI') !== $to->getContent()) { + throw new SspError\BadRequest('This server is not the audience for the message received.'); + } + +\SimpleSAML\Logger::debug(var_export($endpointReference->getAddress()->getContent(), true)); + } }