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