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 6ffdc3d..bf02cf7 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" } }, @@ -39,18 +40,19 @@ "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-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", 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/routing/routes/routes.yml b/routing/routes/routes.yml index c03345d..d9bed6b 100644 --- a/routing/routes/routes.yml +++ b/routing/routes/routes.yml @@ -27,3 +27,17 @@ 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] + +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 df999e6..c17e849 100644 --- a/src/Controller/Adfs.php +++ b/src/Controller/Adfs.php @@ -9,6 +9,10 @@ use SimpleSAML\Error as SspError; 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}; /** @@ -77,7 +81,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(); @@ -137,4 +140,96 @@ 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()->toXML(); + // Some products like DirX are known to break on pretty-printed XML + $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', + ); + + $document->setAttributeNS( + 'http://www.w3.org/2000/xmlns/', + 'xmlns:wsap', + 'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy', + ); + + $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; + } + + + /** + * @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 = PassiveIdP::getById($this->config, 'adfs:' . $idpEntityId); + + return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp); + } } diff --git a/src/IdP/ADFS.php b/src/IdP/ADFS.php index 4125c7d..a10ebe4 100644 --- a/src/IdP/ADFS.php +++ b/src/IdP/ADFS.php @@ -8,12 +8,14 @@ use DateTimeImmutable; use DateTimeZone; use Exception; +use SimpleSAML\Assert\Assert; use SimpleSAML\Configuration; use SimpleSAML\Error; use SimpleSAML\IdP; 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; @@ -24,30 +26,128 @@ 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\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\Address; -use SimpleSAML\WSSecurity\XML\wsa_200508\EndpointReference; +use SimpleSAML\WSSecurity\XML\wsa_200508\{Action, Address, EndpointReference, MessageID, RelatesTo, 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\{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; use SimpleSAML\XMLSecurity\XML\ds\KeyInfo; use SimpleSAML\XMLSecurity\XML\ds\X509Certificate; use SimpleSAML\XMLSecurity\XML\ds\X509Data; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\{Request, StreamedResponse}; +use function array_pop; use function base64_encode; use function chunk_split; +use function str_replace; use function trim; class ADFS { /** + * @param \Symfony\Component\HttpFoundation\Request $request + * @param \SimpleSAML\SOAP\XML\env_200305\Envelope $soapEnvelope + * @param \SimpleSAML\Module\adfs\IdP\PassiveIdP $idp + * @throws \SimpleSAML\Error\MetadataNotFound + */ + public static function receivePassiveAuthnRequest( + Request $request, + Envelope $soapEnvelope, + PassiveIdP $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(); + + $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(); + } + + $requestSecurityTokenStr = $requestSecurityToken->toXML()->ownerDocument->saveXML(); + $requestSecurityTokenStr = str_replace($password->getContent(), '*****', $requestSecurityTokenStr); + Logger::debug($requestSecurityTokenStr); + + $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, + ]; + + return new StreamedResponse( + function () use ($idp, &$state) { + $idp->handleAuthenticationRequest($state); + }, + ); + } + + + /** + * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\IdP $idp * @throws \SimpleSAML\Error\MetadataNotFound */ @@ -68,6 +168,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(), @@ -81,6 +186,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']); @@ -100,14 +209,16 @@ 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 generateAssertion( + private static function generateActiveAssertion( string $issuer, string $target, string $nameid, array $attributes, int $assertionLifetime, + string $method, ): Assertion { $httpUtils = new Utils\HTTP(); $randomUtils = new Utils\Random(); @@ -121,12 +232,6 @@ private static function generateAssertion( $nameid = htmlspecialchars($nameid); $now = new DateTimeImmutable('now', new DateTimeZone('Z')); - if ($httpUtils->isHTTPS()) { - $method = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'; - } else { - $method = C::AC_PASSWORD; - } - $audience = new Audience($target); $audienceRestrictionCondition = new AudienceRestrictionCondition([$audience]); $conditions = new Conditions( @@ -178,6 +283,77 @@ 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(); + $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(new SubjectConfirmation([new ConfirmationMethod(C::CM_BEARER)]), $nameIdentifier); + + $authenticationStatement = new AuthenticationStatement($subject, $method, $now); + + $attrs = []; + $attrs[] = new Attribute( + 'UPN', + 'http://schemas.xmlsoap.org/claims', + [new AttributeValue($attributes['http://schemas.xmlsoap.org/claims/UPN'][0])], + ); + $attrs[] = new Attribute( + 'ImmutableID', + 'http://schemas.microsoft.com/LiveID/Federation/2008/05', + [new AttributeValue($attributes['http://schemas.microsoft.com/LiveID/Federation/2008/05/ImmutableID'][0])], + ); + + $attributeStatement = new AttributeStatement($subject, $attrs); + + return new Assertion( + $assertionID, + $issuer, + $now, + $conditions, + null, // Advice + [$attributeStatement, $authenticationStatement], + ); + } + + /** * @param \SimpleSAML\SAML11\XML\saml\Assertion $assertion * @param string $key @@ -232,13 +408,123 @@ private static function postResponse(string $wreply, string $wresult, ?string $w } + /** + * @param array $state + * @throws \Exception + */ + public static function sendPassiveResponse(array $state): void + { + $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); + } + + $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 = $state['saml:NameID'][SAML2_C::NAMEID_UNSPECIFIED]; + + $assertion = ADFS::generatePassiveAssertion($idpEntityId, $spEntityId, $nameid->getValue(), $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()); + } + + $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(); + } + + /** * @param array $state * @throws \Exception */ public static function sendResponse(array $state): void { - $spMetadata = $state["SPMetadata"]; + $spMetadata = $state['SPMetadata']; $spEntityId = $spMetadata['entityid']; $spMetadata = Configuration::loadFromArray( $spMetadata, @@ -273,7 +559,15 @@ public static function sendResponse(array $state): void $assertionLifetime = $idpMetadata->getOptionalInteger('assertion.lifetime', 300); } - $assertion = ADFS::generateAssertion($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); @@ -299,6 +593,8 @@ 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); diff --git a/src/IdP/PassiveIdP.php b/src/IdP/PassiveIdP.php new file mode 100644 index 0000000..4ee5ed1 --- /dev/null +++ b/src/IdP/PassiveIdP.php @@ -0,0 +1,324 @@ +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\Module\adfs\IdP\PassiveIdP The IdP. + */ + public static function getById(Configuration $config, string $id): PassiveIdP + { + 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\Module\adfs\IdP\PassiveIdP The IdP. + */ + public static function getByState(Configuration $config, array &$state): PassiveIdP + { + 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): void + { + 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, + ); + } + + call_user_func($state['Responder'], $state); + Assert::true(false); + } + + + /** + * 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 = PassiveIdP::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\adfs\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 new file mode 100644 index 0000000..6136fda --- /dev/null +++ b/src/MetadataExchange.php @@ -0,0 +1,678 @@ +getTypes(), + message: $this->getMessages(), + portType: $this->getPortTypes(), + binding: $this->getBindings(), + service: $this->getServices(), + elements: $this->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(), + ); + } + + + /** + * 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(), + ), + )], + ), +*/ + ]; + } + + + /** + * 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/RSTR/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', + ), + ], + ), + ), + ]), +*/ + ]; + } + + + /** + * 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'), + ], + ), + 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/ws-trust/2005/services/'); + + return [ + new Service( + name: 'SecurityTokenService', + ports: [ + new Port( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . 'certificatemixed'), + new EndpointReference( + new Address($defaultEndpoint . 'certificatemixed'), + ), + ], + ), + new Port( + name: 'CertificateWSTrustBinding_IWSTrustFeb2005Async1', + binding: 'tns:CertificateWSTrustBinding_IWSTrustFeb2005Async1', + elements: [ + new Soap12Address($defaultEndpoint . 'certificatetransport'), + new EndpointReference( + new Address($defaultEndpoint . 'certificatetransport'), + ), + ], + ), + new Port( + name: 'UserNameWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:UserNameWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . 'usernamemixed'), + new EndpointReference( + new Address($defaultEndpoint . 'usernamemixed'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async', + elements: [ + new Soap12Address($defaultEndpoint . 'issuedtokenmixedasymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . 'issuedtokenmixedasymmetricbasic256'), + ), + ], + ), + new Port( + name: 'IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', + binding: 'tns:IssuedTokenWSTrustBinding_IWSTrustFeb2005Async1', + elements: [ + new Soap12Address($defaultEndpoint . 'issuedtokenmixedsymmetricbasic256'), + new EndpointReference( + new Address($defaultEndpoint . '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'), + ), + ], + ), + */ + ], + ), + ]; + } +} diff --git a/src/Trust/Policy13.php b/src/Trust/Policy13.php new file mode 100644 index 0000000..9473b8a --- /dev/null +++ b/src/Trust/Policy13.php @@ -0,0 +1,512 @@ +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()], + )], + ), + 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_12, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new KeyValueToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_12, '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 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', 'CertificateWSTrustBinding_IWSTrust13Async_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()], + )], + ), + new AlgorithmSuite( + elements: [new Policy( + children: [new Basic256()], + )], + ), + new Layout( + elements: [new Policy( + children: [new Strict()], + )], + ), + new IncludeTimestamp(), + ], + )], + ); + + $signedEncryptedSupportingTokens = new SignedEncryptedSupportingTokens( + elements: [new Policy( + children: [new UsernameToken( + elts: [new Policy( + children: [new WssUsernameToken10()], + )], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_12, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + )], + )], + ); + + $endorsingSupportingTokens = new EndorsingSupportingTokens( + elements: [new Policy( + children: [ + new KeyValueToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_12, '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()], + ); + + $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', 'UserNameWSTrustBinding_IWSTrustFeb2005Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $signedEncryptedSupportingTokens, + $endorsingSupportingTokens, + $wss11, + $trust13, + $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()], + )], + ), + 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( + 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 Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_12, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new KeyValueToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_12, '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( + )], + ); + + $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_IWSTrust13Async_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust13, + $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()], + )], + ), + 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( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + 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), + ], + ), + elts: [ + new Policy( + children: [ + new RequireInternalReference(), + ], + ), + ], + namespacedAttributes: [ + new XMLAttribute( + C::NS_SEC_POLICY_12, + 'sp', + 'IncludeToken', + IncludeToken::AlwaysToRecipient->value, + ), + ], + ), + new KeyValueToken( + namespacedAttributes: [ + new XMLAttribute(C::NS_SEC_POLICY_12, '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( + )], + ); + + $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_IWSTrust13Async1_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust13, + $usingAddressing, + ], + )], + )], + ); + } +} diff --git a/src/Trust/Policy2005.php b/src/Trust/Policy2005.php new file mode 100644 index 0000000..4c98ff8 --- /dev/null +++ b/src/Trust/Policy2005.php @@ -0,0 +1,555 @@ +getCertificateWSTrustBinding(), + $this->getCertificateWSTrustBinding1(), + $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 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, + ], + )], + )], + ); + } + + + /** + * 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( + 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 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( + requestSecurityTokenTemplate: new RequestSecurityTokenTemplate( + elts: [ + new KeyType([KeyTypeEnum::SymmetricKey]), + new KeySize('256'), + 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), + ], + ), + elts: [ + 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_IWSTrustFeb2005Async1_policy'), + operatorContent: [new ExactlyOne( + operatorContent: [new All( + children: [ + $transportBinding, + $endorsingSupportingTokens, + $wss11, + $trust10, + $usingAddressing, + ], + )], + )], + ); + } +} 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