Skip to content

Commit fa8fb91

Browse files
tvdijenmonkeyiq
andcommitted
Feature/metadata exchange (#25)
* Migrate module to use saml11 + ws-security libraries * allow hosted metadata again. This lets me see it in the admin ui (#18) * Fix constant * Back to SSP release-branch * Fix constants * Fixes * Feature/standalone metadata (#21) * Do not rely on SimpleSAMLphp for metadata-building * Fix * Introduce a clokc * Generate ID * Cleanup * Update ws-security lib * WIP: Add mex-endpoint * Use updated ws-security lib * Refactor * Refactor * Fix IncludeToken * Add certificate policies * Add IssuedToken policies * Refactor * Add IssuedToken policies * Add WS-trust 1.3 policies * Fix namespaces * Add IssuedToken policies * Add wsdl:types and wsdl:message elements * Add wsdl:portTtype elements * Add bindings * Import interoperability-test from ws-security lib * Fix coding style * Add last binding and service * Add missing namespaces * Add yet another namespace decl * Fix constant * Fix constant * Fix typo * Use correct version of ws-addressing * Disable anything WS-trust 1.3 related - May remove later * Add usernamemixed endpoint * Fix * Fix rebase: remove duplicate use-statements * Fix namespace * Fix missing parameter * Fix return type * Fix parameter type * Fix constant * Fix namespace for callback * Fix namespace * Fix missing use-statement * Passive response * Bump dependencies * Bump dependencies * Re-add code that got lost earlier * Consider wauth-parameter when processing request * Log the active response under debug-logging * Add debug logging --------- Co-authored-by: monkeyiq <monkeyiq@gmail.com>
1 parent 2c52408 commit fa8fb91

File tree

12 files changed

+2642
-24
lines changed

12 files changed

+2642
-24
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
3+
name: Interoperability
4+
5+
on: # yamllint disable-line rule:truthy
6+
push:
7+
branches: ['**']
8+
paths-ignore:
9+
- '**.md'
10+
- '**.yml'
11+
pull_request:
12+
branches: [master, release-*]
13+
paths-ignore:
14+
- '**.md'
15+
- '**.yml'
16+
workflow_dispatch:
17+
18+
jobs:
19+
edugain:
20+
name: "Interoperability tests, PHP ${{ matrix.php-versions }}, ${{ matrix.operating-system }}"
21+
runs-on: ${{ matrix.operating-system }}
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
operating-system: [ubuntu-latest]
26+
php-versions: ['8.2']
27+
28+
steps:
29+
- name: Setup PHP, with composer and extensions
30+
# https://github.com/shivammathur/setup-php
31+
uses: shivammathur/setup-php@v2
32+
with:
33+
php-version: ${{ matrix.php-versions }}
34+
extensions: ctype, date, dom, hash, mbstring, openssl, pcre, spl, xml
35+
tools: composer:v2
36+
ini-values: error_reporting=E_ALL, memory_limit=-1
37+
coverage: none
38+
39+
- name: Setup problem matchers for PHP
40+
run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
41+
42+
- name: Setup problem matchers for PHPUnit
43+
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
44+
45+
- name: Set git to use LF
46+
run: |
47+
git config --global core.autocrlf false
48+
git config --global core.eol lf
49+
50+
- uses: actions/checkout@v4
51+
52+
- name: Cache composer dependencies
53+
uses: actions/cache@v4
54+
with:
55+
path: $(composer config cache-files-dir)
56+
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
57+
restore-keys: ${{ runner.os }}-composer-
58+
59+
- name: Validate composer.json and composer.lock
60+
run: composer validate
61+
62+
- name: Install Composer dependencies
63+
run: composer install --no-progress --prefer-dist --optimize-autoloader
64+
65+
- name: Get current date
66+
id: date
67+
run: |
68+
echo "{date}={$(date +'%Y-%m-%d')}" >> "$GITHUB_STATE"
69+
70+
- name: Cache metadata
71+
id: cache-metadata
72+
uses: actions/cache@v4
73+
with:
74+
path: /tmp/metadata
75+
key: ${{ runner.os }}-metadata-${{ env.date }}
76+
restore-keys: ${{ runner.os }}-metadata-
77+
78+
- name: Run unit tests
79+
run: |
80+
./vendor/bin/phpunit -c phpunit-interoperability.xml

composer.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
},
3131
"autoload-dev": {
3232
"psr-4": {
33+
"SimpleSAML\\Test\\Module\\adfs\\": "vendor/simplesamlphp/simplesamlphp/tests",
3334
"SimpleSAML\\Test\\Utils\\": "vendor/simplesamlphp/simplesamlphp/tests/Utils"
3435
}
3536
},
@@ -39,18 +40,19 @@
3940

4041
"beste/clock": "^3.0",
4142
"psr/clock": "^1.0",
42-
"simplesamlphp/assert": "^1.1",
43-
"simplesamlphp/saml11": "^1.0",
43+
"simplesamlphp/assert": "~1.8.0",
44+
"simplesamlphp/saml11": "~1.2.0",
4445
"simplesamlphp/saml2": "^5@dev",
4546
"simplesamlphp/simplesamlphp": "^2.4",
46-
"simplesamlphp/ws-security": "^1.6",
47-
"simplesamlphp/xml-common": "^1.16",
48-
"simplesamlphp/xml-security": "^1.9",
47+
"simplesamlphp/xml-common": "~1.24.0",
48+
"simplesamlphp/xml-security": "~1.13.0",
49+
"simplesamlphp/xml-soap": "~1.7.0",
50+
"simplesamlphp/xml-wsdl": "~1.2.0",
51+
"simplesamlphp/ws-security": "~1.9.0",
4952
"symfony/http-foundation": "^6.4"
5053
},
5154
"require-dev": {
52-
"simplesamlphp/simplesamlphp-test-framework": "^1.6",
53-
"simplesamlphp/xml-security": "^1.7"
55+
"simplesamlphp/simplesamlphp-test-framework": "~1.8.0"
5456
},
5557
"support": {
5658
"issues": "https://github.com/simplesamlphp/simplesamlphp-module-adfs/issues",

phpunit-interoperability.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" colors="true" processIsolation="false" stopOnFailure="false" bootstrap="./tests/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.0/phpunit.xsd" cacheDirectory=".phpunit.cache" backupStaticProperties="false">
3+
<testsuites>
4+
<testsuite name="Test Suite - Interoperability">
5+
<directory>./tests/InterOperability</directory>
6+
</testsuite>
7+
</testsuites>
8+
</phpunit>

routing/routes/routes.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,17 @@ adfs-prp-legacy:
2727
_controller: 'SimpleSAML\Module\adfs\Controller\Adfs::prp'
2828
}
2929
methods: [GET, POST]
30+
31+
adfs-wstrust-mex:
32+
path: /ws-trust/mex
33+
defaults: {
34+
_controller: 'SimpleSAML\Module\adfs\Controller\Adfs::mex'
35+
}
36+
methods: [GET]
37+
38+
adfs-wstrust-usernamemixed:
39+
path: /ws-trust/2005/services/usernamemixed
40+
defaults: {
41+
_controller: 'SimpleSAML\Module\adfs\Controller\Adfs::usernamemixed'
42+
}
43+
methods: [POST]

src/Controller/Adfs.php

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
use SimpleSAML\Error as SspError;
1010
use SimpleSAML\Module\adfs\IdP\ADFS as ADFS_IDP;
1111
use SimpleSAML\Module\adfs\IdP\MetadataBuilder;
12+
use SimpleSAML\Module\adfs\IdP\PassiveIdP;
13+
use SimpleSAML\Module\adfs\MetadataExchange;
14+
use SimpleSAML\SOAP\XML\env_200305\Envelope;
15+
use SimpleSAML\XML\DOMDocumentFactory;
1216
use Symfony\Component\HttpFoundation\{Request, Response, StreamedResponse};
1317

1418
/**
@@ -77,7 +81,6 @@ public function metadata(Request $request): Response
7781
// Some products like DirX are known to break on pretty-printed XML
7882
$document->ownerDocument->formatOutput = false;
7983
$document->ownerDocument->encoding = 'UTF-8';
80-
8184
$metaxml = $document->ownerDocument->saveXML();
8285

8386
$response = new Response();
@@ -137,4 +140,96 @@ function () use ($idp, /** @scrutinizer ignore-type */ $assocId, $relayState, $l
137140
}
138141
throw new SspError\BadRequest("Missing parameter 'wa' or 'assocId' in request.");
139142
}
143+
144+
145+
/**
146+
* @param \Symfony\Component\HttpFoundation\Request $request
147+
* @return \Symfony\Component\HttpFoundation\Response
148+
*/
149+
public function mex(Request $request): Response
150+
{
151+
if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) {
152+
throw new SspError\Error('NOACCESS');
153+
}
154+
155+
// check if valid local session exists
156+
$authUtils = new Utils\Auth();
157+
if ($this->config->getOptionalBoolean('admin.protectmetadata', false) && !$authUtils->isAdmin()) {
158+
return new StreamedResponse([$authUtils, 'requireAdmin']);
159+
}
160+
161+
$mexBuilder = new MetadataExchange();
162+
$document = $mexBuilder->buildDocument()->toXML();
163+
// Some products like DirX are known to break on pretty-printed XML
164+
$document->ownerDocument->formatOutput = false;
165+
$document->ownerDocument->encoding = 'UTF-8';
166+
167+
$document->setAttributeNS(
168+
'http://www.w3.org/2000/xmlns/',
169+
'xmlns:tns',
170+
'http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice',
171+
);
172+
173+
$document->setAttributeNS(
174+
'http://www.w3.org/2000/xmlns/',
175+
'xmlns:soapenc',
176+
'http://schemas.xmlsoap.org/soap/encoding/',
177+
);
178+
179+
$document->setAttributeNS(
180+
'http://www.w3.org/2000/xmlns/',
181+
'xmlns:msc',
182+
'http://schemas.microsoft.com/ws/2005/12/wsdl/contract',
183+
);
184+
185+
$document->setAttributeNS(
186+
'http://www.w3.org/2000/xmlns/',
187+
'xmlns:wsam',
188+
'http://www.w3.org/2007/05/addressing/metadata',
189+
);
190+
191+
$document->setAttributeNS(
192+
'http://www.w3.org/2000/xmlns/',
193+
'xmlns:wsap',
194+
'http://schemas.xmlsoap.org/ws/2004/08/addressing/policy',
195+
);
196+
197+
$metaxml = $document->ownerDocument->saveXML();
198+
199+
$response = new Response();
200+
$response->setEtag(hash('sha256', $metaxml));
201+
$response->setPublic();
202+
if ($response->isNotModified($request)) {
203+
return $response;
204+
}
205+
$response->headers->set('Content-Type', 'text/xml');
206+
$response->setContent($metaxml);
207+
208+
return $response;
209+
}
210+
211+
212+
/**
213+
* @param \Symfony\Component\HttpFoundation\Request $request
214+
* @return \Symfony\Component\HttpFoundation\Response
215+
*/
216+
public function usernamemixed(Request $request): Response
217+
{
218+
if (!$this->config->getOptionalBoolean('enable.adfs-idp', false)) {
219+
throw new SspError\Error('NOACCESS');
220+
}
221+
222+
$soapMessage = $request->getContent();
223+
if ($soapMessage === false) {
224+
throw new SspError\BadRequest('Missing SOAP-content.');
225+
}
226+
227+
$domDocument = DOMDocumentFactory::fromString($soapMessage);
228+
$soapEnvelope = Envelope::fromXML($domDocument->documentElement);
229+
230+
$idpEntityId = $this->metadata->getMetaDataCurrentEntityID('adfs-idp-hosted');
231+
$idp = PassiveIdP::getById($this->config, 'adfs:' . $idpEntityId);
232+
233+
return ADFS_IDP::receivePassiveAuthnRequest($request, $soapEnvelope, $idp);
234+
}
140235
}

0 commit comments

Comments
 (0)