Skip to content

Commit 6edc115

Browse files
committed
Enable testing trust chain resolution in admin UI
1 parent 20c6cbf commit 6edc115

File tree

22 files changed

+711
-18
lines changed

22 files changed

+711
-18
lines changed

config-templates/module_oidc.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,33 @@
368368
// 'eyJ...GHg',
369369
],
370370

371+
// (optional) Federation participation limit by Trust Marks. This is an array with the following format:
372+
// [
373+
// 'trust-anchor-id' => [
374+
// 'limit-id' => [
375+
// 'trust-mark-id',
376+
// 'trust-mark-id-2',
377+
// ],
378+
// ],
379+
// ],
380+
// Check example below on how this can be used. If federation participation limit is configured for particular
381+
// Trust Anchor ID, at least one combination of "limit ID" => "trust mark list" should be defined.
382+
ModuleConfig::OPTION_FEDERATION_PARTICIPATION_LIMIT_BY_TRUST_MARKS => [
383+
// We are limiting federation participation using Trust Marks for 'https://ta.example.org/'.
384+
'https://ta.example.org/' => [
385+
// Entities must have (at least) one Trust Mark from the list below.
386+
\SimpleSAML\Module\oidc\Codebooks\LimitsEnum::OneOf->value => [
387+
'trust-mark-id',
388+
'trust-mark-id-2',
389+
],
390+
// Entities must have all Trust Marks from the list below.
391+
\SimpleSAML\Module\oidc\Codebooks\LimitsEnum::AllOf->value => [
392+
'trust-mark-id-3',
393+
'trust-mark-id-4',
394+
],
395+
],
396+
],
397+
371398
// (optional) Dedicated federation cache adapter, used to cache federation artifacts like trust chains, entity
372399
// statements, etc. It will also be used for token reuse check in federation context. Setting this option is
373400
// recommended in production environments. If set to null, no caching will be used. Can be set to any

routing/routes/routes.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use SimpleSAML\Module\oidc\Controllers\AccessTokenController;
1111
use SimpleSAML\Module\oidc\Controllers\Admin\ClientController;
1212
use SimpleSAML\Module\oidc\Controllers\Admin\ConfigController;
13+
use SimpleSAML\Module\oidc\Controllers\Admin\TestController;
1314
use SimpleSAML\Module\oidc\Controllers\AuthorizationController;
1415
use SimpleSAML\Module\oidc\Controllers\ConfigurationDiscoveryController;
1516
use SimpleSAML\Module\oidc\Controllers\EndSessionController;
@@ -57,6 +58,12 @@
5758
->controller([ClientController::class, 'delete'])
5859
->methods([HttpMethodsEnum::POST->value]);
5960

61+
// Testing
62+
63+
$routes->add(RoutesEnum::AdminTestTrustChainResolution->name, RoutesEnum::AdminTestTrustChainResolution->value)
64+
->controller([TestController::class, 'trustChainResolution'])
65+
->methods([HttpMethodsEnum::GET->value, HttpMethodsEnum::POST->value]);
66+
6067
/*****************************************************************************************************************
6168
* OpenID Connect
6269
****************************************************************************************************************/

routing/services/services.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ services:
9999
factory: ['@SimpleSAML\Module\oidc\Factories\ResourceServerFactory', 'build']
100100

101101
# Utils
102+
SimpleSAML\Module\oidc\Utils\Debug\ArrayLogger: ~
103+
SimpleSAML\Module\oidc\Utils\FederationParticipationValidator: ~
102104
SimpleSAML\Module\oidc\Utils\Routes: ~
103105
SimpleSAML\Module\oidc\Utils\RequestParamsResolver: ~
104106
SimpleSAML\Module\oidc\Utils\ClassInstanceBuilder: ~

src/Codebooks/LimitsEnum.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Module\oidc\Codebooks;
6+
7+
enum LimitsEnum: string
8+
{
9+
case OneOf = 'one_of';
10+
case AllOf = 'all_of';
11+
}

src/Codebooks/RoutesEnum.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ enum RoutesEnum: string
2424
case AdminClientsResetSecret = 'admin/clients/reset-secret';
2525
case AdminClientsDelete = 'admin/clients/delete';
2626

27+
// Testing
28+
case AdminTestTrustChainResolution = 'admin/test/trust-chain-resolution';
29+
30+
2731
/*****************************************************************************************************************
2832
* OpenID Connect
2933
****************************************************************************************************************/
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Module\oidc\Controllers\Admin;
6+
7+
use SimpleSAML\Module\oidc\Admin\Authorization;
8+
use SimpleSAML\Module\oidc\Codebooks\RoutesEnum;
9+
use SimpleSAML\Module\oidc\Exceptions\OidcException;
10+
use SimpleSAML\Module\oidc\Factories\TemplateFactory;
11+
use SimpleSAML\Module\oidc\Helpers;
12+
use SimpleSAML\Module\oidc\ModuleConfig;
13+
use SimpleSAML\Module\oidc\Utils\Debug\ArrayLogger;
14+
use SimpleSAML\OpenID\Codebooks\EntityTypesEnum;
15+
use SimpleSAML\OpenID\Exceptions\TrustChainException;
16+
use SimpleSAML\OpenID\Federation;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpFoundation\Response;
19+
20+
class TestController
21+
{
22+
public function __construct(
23+
protected readonly ModuleConfig $moduleConfig,
24+
protected readonly TemplateFactory $templateFactory,
25+
protected readonly Authorization $authorization,
26+
protected readonly Federation $federation,
27+
protected readonly Helpers $helpers,
28+
protected readonly ArrayLogger $arrayLogger,
29+
) {
30+
$this->authorization->requireAdmin(true);
31+
}
32+
33+
/**
34+
* @throws \SimpleSAML\Error\ConfigurationError
35+
* @throws \SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException
36+
* @throws \SimpleSAML\Module\oidc\Exceptions\OidcException
37+
*/
38+
public function trustChainResolution(Request $request): Response
39+
{
40+
$this->arrayLogger->setWeight(ArrayLogger::WEIGHT_WARNING);
41+
// Let's create new Federation instance so we can inject our debug logger and go without cache.
42+
$federation = new Federation(
43+
supportedAlgorithms: $this->federation->supportedAlgorithms(),
44+
cache: null,
45+
logger: $this->arrayLogger,
46+
);
47+
48+
$leafEntityId = $this->moduleConfig->getIssuer();
49+
$trustChainBag = null;
50+
$resolvedMetadata = [];
51+
$isFormSubmitted = false;
52+
53+
try {
54+
$trustAnchorIds = $this->moduleConfig->getFederationTrustAnchorIds();
55+
} catch (\Throwable $exception) {
56+
$this->arrayLogger->error('Module config error: ' . $exception->getMessage());
57+
$trustAnchorIds = [];
58+
}
59+
60+
if ($request->isMethod(Request::METHOD_POST)) {
61+
$isFormSubmitted = true;
62+
63+
!empty($leafEntityId = $request->request->getString('leafEntityId')) ||
64+
throw new OidcException('Empty leaf entity ID.');
65+
!empty($rawTrustAnchorIds = $request->request->getString('trustAnchorIds')) ||
66+
throw new OidcException('Empty Trust Anchor IDs.');
67+
68+
/** @var non-empty-array<non-empty-string> $trustAnchorIds */
69+
$trustAnchorIds = $this->helpers->str()->convertTextToArray($rawTrustAnchorIds);
70+
71+
try {
72+
$trustChainBag = $federation->trustChainResolver()->for($leafEntityId, $trustAnchorIds);
73+
74+
foreach ($trustChainBag->getAll() as $index => $trustChain) {
75+
$metadataEntries = [];
76+
foreach (EntityTypesEnum::cases() as $entityTypeEnum) {
77+
try {
78+
$metadataEntries[$entityTypeEnum->value] =
79+
$trustChain->getResolvedMetadata($entityTypeEnum);
80+
} catch (\Throwable $exception) {
81+
$this->arrayLogger->error(
82+
'Metadata resolving error: ' . $exception->getMessage(),
83+
compact('index', 'entityTypeEnum'),
84+
);
85+
continue;
86+
}
87+
}
88+
$resolvedMetadata[$index] = array_filter($metadataEntries);
89+
}
90+
} catch (TrustChainException $exception) {
91+
$this->arrayLogger->error('Trust chain error: ' . $exception->getMessage());
92+
}
93+
}
94+
95+
$trustAnchorIds = implode("\n", $trustAnchorIds);
96+
$logMessages = $this->arrayLogger->getEntries();
97+
//dd($this->arrayLogger->getEntries());
98+
return $this->templateFactory->build(
99+
'oidc:tests/trust-chain-resolution.twig',
100+
compact(
101+
'leafEntityId',
102+
'trustAnchorIds',
103+
'trustChainBag',
104+
'resolvedMetadata',
105+
'logMessages',
106+
'isFormSubmitted',
107+
),
108+
RoutesEnum::AdminTestTrustChainResolution->value,
109+
);
110+
}
111+
}

src/Controllers/Federation/Test.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,25 @@ public function __invoke(): Response
6565
// $requestObject = $requestObjectFactory->fromToken($unprotectedJws);
6666

6767
// dd($requestObject, $requestObject->getPayload(), $requestObject->getHeader());
68-
// $cache->clear();
68+
$this->federationCache?->cache->clear();
6969

7070
$trustChain = $this->federation
7171
->trustChainResolver()
7272
->for(
73-
'https://08-dap.localhost.markoivancic.from.hr/openid/entities/ALeaf/',
73+
// 'https://08-dap.localhost.markoivancic.from.hr/openid/entities/ALeaf/',
7474
// 'https://trust-anchor.testbed.oidcfed.incubator.geant.org/oidc/rp/',
7575
// 'https://relying-party-php.testbed.oidcfed.incubator.geant.org/',
76-
// 'https://gorp.testbed.oidcfed.incubator.geant.org',
76+
'https://gorp.testbed.oidcfed.incubator.geant.org',
7777
// 'https://maiv1.incubator.geant.org',
7878
[
79-
// 'https://trust-anchor.testbed.oidcfed.incubator.geant.org/',
79+
'https://trust-anchor.testbed.oidcfed.incubator.geant.org/',
8080
'https://08-dap.localhost.markoivancic.from.hr/openid/entities/ABTrustAnchor/',
81-
// 'https://08-dap.localhost.markoivancic.from.hr/openid/entities/CTrustAnchor/',
81+
'https://08-dap.localhost.markoivancic.from.hr/openid/entities/CTrustAnchor/',
8282
],
83-
);
84-
83+
)->getAll();
84+
dd($trustChain);
8585
$leaf = $trustChain->getResolvedLeaf();
86-
// dd($leaf);
86+
dd($leaf->getPayload());
8787
$leafFederationJwks = $leaf->getJwks();
8888
// dd($leafFederationJwks);
8989
// /** @psalm-suppress PossiblyNullArgument */

src/Factories/RequestRulesManagerFactory.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use SimpleSAML\Module\oidc\Services\LoggerService;
3737
use SimpleSAML\Module\oidc\Utils\ClaimTranslatorExtractor;
3838
use SimpleSAML\Module\oidc\Utils\FederationCache;
39+
use SimpleSAML\Module\oidc\Utils\FederationParticipationValidator;
3940
use SimpleSAML\Module\oidc\Utils\JwksResolver;
4041
use SimpleSAML\Module\oidc\Utils\ProtocolCache;
4142
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
@@ -58,6 +59,7 @@ public function __construct(
5859
private readonly Federation $federation,
5960
private readonly Helpers $helpers,
6061
private readonly JwksResolver $jwksResolver,
62+
private readonly FederationParticipationValidator $federationParticipationValidator,
6163
private readonly ?FederationCache $federationCache = null,
6264
private readonly ?ProtocolCache $protocolCache = null,
6365
) {
@@ -88,6 +90,7 @@ private function getDefaultRules(): array
8890
$this->federation,
8991
$this->helpers,
9092
$this->jwksResolver,
93+
$this->federationParticipationValidator,
9194
$this->federationCache,
9295
),
9396
new RedirectUriRule($this->requestParamsResolver),

src/Factories/TemplateFactory.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ protected function includeDefaultMenuItems(): void
107107
),
108108
);
109109

110+
$this->oidcMenu->addItem(
111+
$this->oidcMenu->buildItem(
112+
$this->moduleConfig->getModuleUrl(RoutesEnum::AdminClients->value),
113+
Translate::noop('Client Registry'),
114+
),
115+
);
116+
110117
$this->oidcMenu->addItem(
111118
$this->oidcMenu->buildItem(
112119
$this->moduleConfig->getModuleUrl(RoutesEnum::AdminConfigProtocol->value),
@@ -123,8 +130,8 @@ protected function includeDefaultMenuItems(): void
123130

124131
$this->oidcMenu->addItem(
125132
$this->oidcMenu->buildItem(
126-
$this->moduleConfig->getModuleUrl(RoutesEnum::AdminClients->value),
127-
Translate::noop('Client Registry'),
133+
$this->moduleConfig->getModuleUrl(RoutesEnum::AdminTestTrustChainResolution->value),
134+
Translate::noop('Test Trust Chain Resolution'),
128135
),
129136
);
130137
}

src/Forms/ClientForm.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ protected function getScopes(): array
414414
}
415415

416416
/**
417+
* TODO mivanci Move to Str helper.
417418
* @return string[]
418419
*/
419420
protected function convertTextToArrayWithLinesAsValues(string $text): array

0 commit comments

Comments
 (0)