Skip to content

Commit a8115aa

Browse files
committed
Extract WAYF rendering logic from Corto into Symfony WayfRenderer service
Prior to this change, much of the wayf rendering happened in Corto. This change moves that logic to the Symfony workspaces. Benefits: - Business logic is now in a testable Symfony service with proper DI - Production (SingleSignOn) and the functional test (WayfController) share the exact same rendering path, eliminating logic duplication - DiContainerRuntime bridge is thinner
1 parent 1a36de3 commit a8115aa

18 files changed

Lines changed: 348 additions & 95 deletions

File tree

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ Changes:
3232
* A new parameter `wayf.preferred_idp_entity_ids` must be added to `parameters.yml`. To display a set of IdPs prominent at the top of the WAYF, add the entityId's of those IdPs to this parameter.
3333
* To keep the old behaviour, set the value to `[]`
3434

35-
**Default behaviour (no change):** when the parameter is absent or empty, the WAYF behaves exactly as before.
3635
* Stabilized consent checks
3736
* In order to make the consent hashes more robust, a more consistent way of hashing the user attributes has been introduced
3837
* This feature automatically migrates from the old hashes to the new hashes upon login.

config/services/ci/controllers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ services:
1919
engineblock.functional_test.controller.wayf:
2020
class: OpenConext\EngineBlockFunctionalTestingBundle\Controllers\WayfController
2121
arguments:
22-
- '@twig'
22+
- '@OpenConext\EngineBlockBundle\Service\WayfRenderer'
2323

2424
engineblock.functional_test.controller.feedback:
2525
class: OpenConext\EngineBlockFunctionalTestingBundle\Controllers\FeedbackController

config/services/services.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ services:
88
arguments:
99
$wayfExtension: '@OpenConext\EngineBlockBundle\Twig\Extensions\Extension\Wayf'
1010

11+
OpenConext\EngineBlockBundle\Service\WayfRenderer:
12+
autowire: true
13+
1114
OpenConext\EngineBlockBundle\Bridge\EngineBlockBootstrapper:
1215
autowire: true
1316
autoconfigure: true

library/EngineBlock/Corto/Module/Service/SingleSignOn.php

Lines changed: 7 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -475,40 +475,21 @@ protected function _showWayf(EngineBlock_Saml2_AuthnRequestAnnotationDecorator $
475475
$container->getDefaultIdPEntityId()
476476
);
477477

478-
$defaultIdPInIdPList = $this->isDefaultIdPPresent($idpList);
479-
480478
$diContainerRuntime = $application->getDiContainerRuntime();
481-
$preferredIdpEntityIds = $diContainerRuntime->getPreferredIdpEntityIds();
482-
$split = $diContainerRuntime->idpSplitter->split($idpList, $preferredIdpEntityIds);
483-
$showPreferredIdps = !empty($split['preferred']);
484-
$isDefaultIdpPreferred = in_array($container->getDefaultIdPEntityId(), $preferredIdpEntityIds, true);
485-
$showDefaultIdpBanner = (bool) $container->shouldDisplayDefaultIdpBannerOnWayf()
486-
&& $defaultIdPInIdPList
487-
&& (!$showPreferredIdps || !$isDefaultIdpPreferred);
488-
489-
$rememberChoiceFeature = $container->getRememberChoice();
490479

491-
$viewModel = $diContainerRuntime->wayfViewModelFactory->create(
480+
$output = $diContainerRuntime->wayfRenderer->render(
492481
idpList: $idpList,
493-
regularIdpList: $split['regular'],
494-
preferredIdpList: $split['preferred'],
495-
showPreferredIdps: $showPreferredIdps,
482+
preferredIdpEntityIds: $diContainerRuntime->getPreferredIdpEntityIds(),
496483
action: $action,
497-
greenHeader: $serviceProvider->getDisplayName($currentLocale),
498-
helpLink: '/authentication/idp/help-discover?lang=' . $currentLocale,
484+
currentLocale: $currentLocale,
485+
defaultIdpEntityId: $container->getDefaultIdPEntityId(),
486+
shouldDisplayBanner: (bool) $container->shouldDisplayDefaultIdpBannerOnWayf(),
499487
backLink: $container->isUiOptionReturnToSpActive(),
500-
cutoffPointForShowingUnfilteredIdps: $container->getCutoffPointForShowingUnfilteredIdps(),
501-
showIdPBanner: $showDefaultIdpBanner,
502-
rememberChoiceFeature: $rememberChoiceFeature,
488+
cutoffPoint: $container->getCutoffPointForShowingUnfilteredIdps(),
489+
rememberChoice: $container->getRememberChoice(),
503490
showRequestAccess: $serviceProvider->getCoins()->displayUnconnectedIdpsWayf(),
504491
requestId: $request->getId(),
505492
serviceProvider: $serviceProvider,
506-
showRequestAccessContainer: true,
507-
);
508-
509-
$output = $this->twig->render(
510-
'@theme/Authentication/View/Proxy/wayf.html.twig',
511-
$viewModel->toArray()
512493
);
513494
$this->_server->sendOutput($output);
514495
}
@@ -685,14 +666,4 @@ protected function getEngineSpRole(EngineBlock_Corto_ProxyServer $proxyServer)
685666
$serviceProvider = $this->_serviceProviderFactory->createEngineBlockEntityFrom($keyId);
686667
return ServiceProvider::fromServiceProviderEntity($serviceProvider);
687668
}
688-
689-
private function isDefaultIdPPresent(array $idpList): bool
690-
{
691-
foreach ($idpList as $idp) {
692-
if ($idp[self::IS_DEFAULT_IDP_KEY] === true) {
693-
return true;
694-
}
695-
}
696-
return false;
697-
}
698669
}

src/OpenConext/EngineBlock/Service/Wayf/IdpSplitter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
/**
4-
* Copyright 2025 SURFnet B.V.
4+
* Copyright 2026 SURFnet B.V.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

src/OpenConext/EngineBlockBundle/Bridge/DiContainerRuntime.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818

1919
namespace OpenConext\EngineBlockBundle\Bridge;
2020

21-
use OpenConext\EngineBlock\Service\Wayf\IdpSplitter;
22-
use OpenConext\EngineBlockBundle\Service\WayfViewModelFactory;
21+
use OpenConext\EngineBlockBundle\Service\WayfRenderer;
2322
use Twig\Environment;
2423

2524
/**
@@ -33,8 +32,7 @@
3332

3433
public function __construct(
3534
public Environment $twig,
36-
public IdpSplitter $idpSplitter,
37-
public WayfViewModelFactory $wayfViewModelFactory,
35+
public WayfRenderer $wayfRenderer,
3836
private array $preferredIdpEntityIds = [],
3937
) {
4038
}

src/OpenConext/EngineBlockBundle/Bridge/EngineBlockBootstrapper.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
namespace OpenConext\EngineBlockBundle\Bridge;
2020

2121
use EngineBlock_ApplicationSingleton;
22-
use OpenConext\EngineBlock\Service\Wayf\IdpSplitter;
23-
use OpenConext\EngineBlockBundle\Service\WayfViewModelFactory;
22+
use OpenConext\EngineBlockBundle\Service\WayfRenderer;
2423
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
2524
use Symfony\Component\HttpKernel\KernelEvents;
2625
use Twig\Environment;
@@ -31,11 +30,10 @@ class EngineBlockBootstrapper implements EventSubscriberInterface
3130

3231
public function __construct(
3332
Environment $twig,
34-
IdpSplitter $idpSplitter,
35-
WayfViewModelFactory $wayfViewModelFactory,
33+
WayfRenderer $wayfRenderer,
3634
array $preferredIdpEntityIds = [],
3735
) {
38-
$this->diContainerRuntime = new DiContainerRuntime($twig, $idpSplitter, $wayfViewModelFactory, $preferredIdpEntityIds);
36+
$this->diContainerRuntime = new DiContainerRuntime($twig, $wayfRenderer, $preferredIdpEntityIds);
3937
}
4038

4139
public function onKernelRequest(): void
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/**
4+
* Copyright 2026 SURFnet B.V.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
declare(strict_types=1);
20+
21+
namespace OpenConext\EngineBlockBundle\Service;
22+
23+
use OpenConext\EngineBlock\Metadata\Entity\ServiceProvider;
24+
use OpenConext\EngineBlock\Service\Wayf\IdpSplitter;
25+
use Twig\Environment;
26+
27+
class WayfRenderer
28+
{
29+
public function __construct(
30+
private readonly WayfViewModelFactory $factory,
31+
private readonly IdpSplitter $splitter,
32+
private readonly Environment $twig,
33+
) {
34+
}
35+
36+
/**
37+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
38+
*/
39+
public function render(
40+
array $idpList,
41+
array $preferredIdpEntityIds,
42+
string $action,
43+
string $currentLocale,
44+
string $defaultIdpEntityId,
45+
bool $shouldDisplayBanner,
46+
bool $backLink,
47+
int $cutoffPoint,
48+
bool $rememberChoice,
49+
bool $showRequestAccess,
50+
string $requestId,
51+
ServiceProvider $serviceProvider,
52+
): string {
53+
$split = $this->splitter->split($idpList, $preferredIdpEntityIds);
54+
$showPreferredIdps = !empty($split['preferred']);
55+
$isDefaultIdpPreferred = in_array($defaultIdpEntityId, $preferredIdpEntityIds, true);
56+
57+
$showIdPBanner = $shouldDisplayBanner
58+
&& $this->isDefaultIdpPresent($idpList)
59+
&& (!$showPreferredIdps || !$isDefaultIdpPreferred);
60+
61+
$viewModel = $this->factory->create(
62+
idpList: $idpList,
63+
regularIdpList: $split['regular'],
64+
preferredIdpList: $split['preferred'],
65+
showPreferredIdps: $showPreferredIdps,
66+
action: $action,
67+
greenHeader: $serviceProvider->getDisplayName($currentLocale),
68+
helpLink: '/authentication/idp/help-discover?lang=' . $currentLocale,
69+
backLink: $backLink,
70+
cutoffPointForShowingUnfilteredIdps: $cutoffPoint,
71+
showIdPBanner: $showIdPBanner,
72+
rememberChoiceFeature: $rememberChoice,
73+
showRequestAccess: $showRequestAccess,
74+
requestId: $requestId,
75+
serviceProvider: $serviceProvider,
76+
showRequestAccessContainer: true,
77+
);
78+
79+
return $this->twig->render('@theme/Authentication/View/Proxy/wayf.html.twig', $viewModel->toArray());
80+
}
81+
82+
private function isDefaultIdpPresent(array $idpList): bool
83+
{
84+
return array_any($idpList, fn($idp) => ($idp['isDefaultIdp'] ?? false) === true);
85+
}
86+
}

src/OpenConext/EngineBlockBundle/Service/WayfViewModelFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
/**
4-
* Copyright 2025 SURFnet B.V.
4+
* Copyright 2026 SURFnet B.V.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

src/OpenConext/EngineBlockBundle/ViewModel/WayfViewModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
/**
4-
* Copyright 2025 SURFnet B.V.
4+
* Copyright 2026 SURFnet B.V.
55
*
66
* Licensed under the Apache License, Version 2.0 (the "License");
77
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)