From faaf36bb457255c96131b2c8536e3ea8597539ef Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 14 Mar 2026 22:04:39 +0100 Subject: [PATCH 1/6] Convert admin-module to symfony-responses --- modules/admin/src/Controller/Config.php | 31 +++++++++++-------- modules/admin/src/Controller/Federation.php | 33 ++++++++++++++------- modules/admin/src/Controller/Test.php | 12 +++++--- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/modules/admin/src/Controller/Config.php b/modules/admin/src/Controller/Config.php index 09b26c031b..d2cc93afeb 100644 --- a/modules/admin/src/Controller/Config.php +++ b/modules/admin/src/Controller/Config.php @@ -15,6 +15,7 @@ use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Contracts\HttpClient\Exception\ExceptionInterface; @@ -79,12 +80,14 @@ public function setAuthUtils(Utils\Auth $authUtils): void * Display basic diagnostic information on hostname, port and protocol. * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\XHTML\Template + * @param \Symfony\Component\HttpFoundation\Response */ - public function diagnostics(Request $request): Template + public function diagnostics(Request $request): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $t = new Template($this->config, 'admin:diagnostics.twig'); $t->data = [ @@ -113,12 +116,14 @@ public function diagnostics(Request $request): Template * Display the main admin page. * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\XHTML\Template + * @return \Symfony\Component\HttpFoundation\Response */ - public function main(/** @scrutinizer ignore-unused */ Request $request): Template + public function main(/** @scrutinizer ignore-unused */ Request $request): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $t = new Template($this->config, 'admin:config.twig'); $t->data = [ @@ -172,12 +177,14 @@ protected function getModuleList(): array * Display the output of phpinfo(). * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \Symfony\Component\HttpFoundation\StreamedResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function phpinfo(/** @scrutinizer ignore-unused */ Request $request): StreamedResponse + public function phpinfo(/** @scrutinizer ignore-unused */ Request $request): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $response = new StreamedResponse('phpinfo'); $response->headers->set( diff --git a/modules/admin/src/Controller/Federation.php b/modules/admin/src/Controller/Federation.php index f6486e2b32..69f84a27bb 100644 --- a/modules/admin/src/Controller/Federation.php +++ b/modules/admin/src/Controller/Federation.php @@ -120,13 +120,17 @@ public function setMetadataStorageHandler(MetadataStorageHandler $mdHandler): vo * Display the federation page. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\XHTML\Template + * @return \Symfony\Component\HttpFoundation\Response + * * @throws \SimpleSAML\Error\Exception * @throws \SimpleSAML\Error\Exception */ - public function main(/** @scrutinizer ignore-unused */ Request $request): Template + public function main(/** @scrutinizer ignore-unused */ Request $request): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } // initialize basic metadata array $hostedSPs = $this->getHostedSP(); @@ -416,12 +420,15 @@ private function getHostedSP(): array * Metadata converter * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\XHTML\Template + * @return \Symfony\Component\HttpFoundation\Response */ public function metadataConverter(Request $request): Template { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } + $xmldata = null; if ($xmlfile = $request->files->get('xmlfile')) { // Some security-tooling will falsely think that request-data is passed into file_get_contents(), @@ -507,12 +514,14 @@ public function metadataConverter(Request $request): Template * Download a certificate for a given entity. * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * * @return \Symfony\Component\HttpFoundation\Response PEM-encoded certificate. */ public function downloadCert(Request $request): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $set = $request->query->get('set'); $prefix = $request->query->get('prefix', ''); @@ -550,12 +559,14 @@ public function downloadCert(Request $request): Response * Show remote entity metadata * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\XHTML\Template + * @return \Symfony\Component\HttpFoundation\Response */ public function showRemoteEntity(Request $request): Template { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $entityId = $request->query->get('entityid'); $set = $request->query->get('set'); diff --git a/modules/admin/src/Controller/Test.php b/modules/admin/src/Controller/Test.php index 7e45977bdf..0cf5903ed3 100644 --- a/modules/admin/src/Controller/Test.php +++ b/modules/admin/src/Controller/Test.php @@ -99,11 +99,15 @@ public function setAuthState(Auth\State $authState): void * * @param \Symfony\Component\HttpFoundation\Request $request * @param string|null $as - * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ public function main(Request $request, ?string $as = null): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } + if (is_null($as)) { $t = new Template($this->config, 'admin:authsource_list.twig'); $t->data = [ @@ -114,7 +118,7 @@ public function main(Request $request, ?string $as = null): Response $authsource = new $this->authSimple($as); if (!is_null($request->query->get('logout'))) { - return new RunnableResponse([$authsource, 'logout'], [Module::getModuleURL('admin/logout')]); + return $authsource->logout(Module::getModuleURL('admin/logout')); } elseif (!is_null($request->query->get(Auth\State::EXCEPTION_PARAM))) { // This is just a simple example of an error /** @var array $state */ @@ -130,7 +134,7 @@ public function main(Request $request, ?string $as = null): Response 'ReturnTo' => $url, Auth\State::RESTART => $url, ]; - return new RunnableResponse([$authsource, 'login'], [$params]); + return $authsource->login($params); } $attributes = $authsource->getAttributes(); From a4a9d04db67c75697a2f7ea53b087d32dc34c0f3 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 14 Mar 2026 22:38:41 +0100 Subject: [PATCH 2/6] Convert core-module to symfony-responses --- modules/core/src/Auth/Process/Cardinality.php | 4 +-- .../src/Auth/Process/CardinalitySingle.php | 4 +-- .../src/Auth/Process/WarnShortSSOInterval.php | 3 +- .../Auth/Source/AbstractSourceSelector.php | 18 +++++++---- modules/core/src/Auth/UserPassBase.php | 17 +++++----- modules/core/src/Auth/UserPassOrgBase.php | 11 ++++--- modules/core/src/Controller/ErrorReport.php | 7 ++-- modules/core/src/Controller/Exception.php | 12 +++---- modules/core/src/Controller/Login.php | 13 +++++--- modules/core/src/Controller/Logout.php | 32 +++++++++---------- 10 files changed, 69 insertions(+), 52 deletions(-) diff --git a/modules/core/src/Auth/Process/Cardinality.php b/modules/core/src/Auth/Process/Cardinality.php index e22686804e..ec53eb5db2 100644 --- a/modules/core/src/Auth/Process/Cardinality.php +++ b/modules/core/src/Auth/Process/Cardinality.php @@ -195,8 +195,8 @@ public function process(array &$state): void if (array_key_exists('core:cardinality:errorAttributes', $state)) { $id = Auth\State::saveState($state, 'core:cardinality'); $url = Module::getModuleURL('core/error/cardinality'); - $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); - return; + $response = $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response->send(); } } } diff --git a/modules/core/src/Auth/Process/CardinalitySingle.php b/modules/core/src/Auth/Process/CardinalitySingle.php index c559e295a2..b02ddfeca5 100644 --- a/modules/core/src/Auth/Process/CardinalitySingle.php +++ b/modules/core/src/Auth/Process/CardinalitySingle.php @@ -122,8 +122,8 @@ public function process(array &$state): void if (array_key_exists('core:cardinality:errorAttributes', $state)) { $id = Auth\State::saveState($state, 'core:cardinality'); $url = Module::getModuleURL('core/error/cardinality'); - $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); - return; + $response = $this->httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response->send(); } } } diff --git a/modules/core/src/Auth/Process/WarnShortSSOInterval.php b/modules/core/src/Auth/Process/WarnShortSSOInterval.php index 0f3449371e..e83a25c1a8 100644 --- a/modules/core/src/Auth/Process/WarnShortSSOInterval.php +++ b/modules/core/src/Auth/Process/WarnShortSSOInterval.php @@ -53,6 +53,7 @@ public function process(array &$state): void $id = Auth\State::saveState($state, 'core:short_sso_interval'); $url = Module::getModuleURL('core/short_sso_interval'); $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response = $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response->send(); } } diff --git a/modules/core/src/Auth/Source/AbstractSourceSelector.php b/modules/core/src/Auth/Source/AbstractSourceSelector.php index 6098dde907..e3a6167429 100644 --- a/modules/core/src/Auth/Source/AbstractSourceSelector.php +++ b/modules/core/src/Auth/Source/AbstractSourceSelector.php @@ -8,6 +8,8 @@ use SimpleSAML\Auth; use SimpleSAML\Configuration; use SimpleSAML\Error; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; /** * Authentication source which delegates authentication to secondary @@ -51,9 +53,10 @@ public function __construct(array $info, array $config) * save the state, and at a later stage, load the state, update it with the authentication * information about the user, and call completeAuth with the state array. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. */ - public function authenticate(array &$state): void + public function authenticate(Request $request, array &$state): ?Response { $source = $this->selectAuthSource($state); $as = Auth\Source::getById($source); @@ -62,19 +65,22 @@ public function authenticate(array &$state): void } $state['sourceSelector:selected'] = $source; - static::doAuthentication($as, $state); + return static::doAuthentication($as, $state); } /** + * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\Auth\Source $as * @param array $state - * @return void */ - public static function doAuthentication(Auth\Source $as, array &$state): void + public static function doAuthentication(Request $request, Auth\Source $as, array &$state): ?Response { try { - $as->authenticate($state); + $response = $as->authenticate($request, $state); + if ($response instanceof Response) { + return $response; + } } catch (Error\Exception $e) { Auth\State::throwException($state, $e); } catch (Exception $e) { @@ -82,7 +88,7 @@ public static function doAuthentication(Auth\Source $as, array &$state): void Auth\State::throwException($state, $e); } - Auth\Source::completeAuth($state); + return parent::completeAuth($state); } diff --git a/modules/core/src/Auth/UserPassBase.php b/modules/core/src/Auth/UserPassBase.php index f96bb22e65..a0ccd83593 100644 --- a/modules/core/src/Auth/UserPassBase.php +++ b/modules/core/src/Auth/UserPassBase.php @@ -12,6 +12,8 @@ use SimpleSAML\Logger; use SimpleSAML\Module; use SimpleSAML\Utils; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; /** * Helper class for username/password authentication. @@ -194,9 +196,11 @@ public function isRememberMeChecked(): bool * This function saves the information about the login, and redirects to a * login page. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. + * @return \Symfony\Component\HttpFoundation\Response */ - public function authenticate(array &$state): void + public function authenticate(Request $request, array &$state): ?Response { /* * Save the identifier of this authentication source, so that we can @@ -236,7 +240,7 @@ public function authenticate(array &$state): void $attributes = $this->login($username, $password); $state['Attributes'] = $attributes; - return; + return null; } // Save the $state-array, so that we can restore it after a redirect @@ -249,10 +253,7 @@ public function authenticate(array &$state): void $url = Module::getModuleURL('core/loginuserpass'); $params = ['AuthState' => $id]; $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, $params); - - // The previous function never returns, so this code is never executed. - Assert::true(false); + return $httpUtils->redirectTrustedURL($url, $params); } @@ -283,7 +284,7 @@ abstract protected function login(string $username, string $password): array; * @param string $username The username the user wrote. * @param string $password The password the user wrote. */ - public static function handleLogin(string $authStateId, string $username, string $password): void + public static function handleLogin(string $authStateId, string $username, string $password): Response { // Here we retrieve the state array we saved in the authenticate-function. $state = Auth\State::loadState($authStateId, self::STAGEID); @@ -316,6 +317,6 @@ public static function handleLogin(string $authStateId, string $username, string $state['Attributes'] = $attributes; // Return control to SimpleSAMLphp after successful authentication. - Auth\Source::completeAuth($state); + return parent::completeAuth($state); } } diff --git a/modules/core/src/Auth/UserPassOrgBase.php b/modules/core/src/Auth/UserPassOrgBase.php index 1f52b850f9..fd9820ed73 100644 --- a/modules/core/src/Auth/UserPassOrgBase.php +++ b/modules/core/src/Auth/UserPassOrgBase.php @@ -10,6 +10,8 @@ use SimpleSAML\Logger; use SimpleSAML\Module; use SimpleSAML\Utils; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; /** * Helper class for username/password/organization authentication. @@ -206,9 +208,10 @@ public function getRememberOrganizationChecked(): bool * This function saves the information about the login, and redirects to a * login page. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. */ - public function authenticate(array &$state): void + public function authenticate(Request $request, array &$state): ?Response { // We are going to need the authId in order to retrieve this authentication source later $state[self::AUTHID] = $this->authId; @@ -218,7 +221,7 @@ public function authenticate(array &$state): void $url = Module::getModuleURL('core/loginuserpassorg'); $params = ['AuthState' => $id]; $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, $params); + return $httpUtils->redirectTrustedURL($url, $params); } @@ -269,7 +272,7 @@ public static function handleLogin( string $username, string $password, string $organization, - ): void { + ): Response { /* Retrieve the authentication state. */ $state = Auth\State::loadState($authStateId, self::STAGEID); @@ -314,7 +317,7 @@ public static function handleLogin( $state['PersistentAuthData'][] = self::ORGID; $state['Attributes'] = $attributes; - Auth\Source::completeAuth($state); + return parent::completeAuth($state); } diff --git a/modules/core/src/Controller/ErrorReport.php b/modules/core/src/Controller/ErrorReport.php index a5a9d6ea9b..d6cd3ab9e0 100644 --- a/modules/core/src/Controller/ErrorReport.php +++ b/modules/core/src/Controller/ErrorReport.php @@ -12,6 +12,7 @@ use SimpleSAML\Session; use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -45,9 +46,9 @@ public function __construct( /** * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse + * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse */ - public function main(Request $request): Response + public function main(Request $request): Template|RedirectResponse { // this page will redirect to itself after processing a POST request and sending the email if ($request->server->get('REQUEST_METHOD') !== 'POST') { @@ -103,6 +104,6 @@ public function main(Request $request): Response // redirect the user back to this page to clear the POST request $httpUtils = new Utils\HTTP(); - return new RunnableResponse([$httpUtils, 'redirectTrustedURL'], [$httpUtils->getSelfURLNoQuery()]); + return $httpUtils->redirectTrustedURL($httpUtils->getSelfURLNoQuery()); } } diff --git a/modules/core/src/Controller/Exception.php b/modules/core/src/Controller/Exception.php index a9fcfc5c53..f266656613 100644 --- a/modules/core/src/Controller/Exception.php +++ b/modules/core/src/Controller/Exception.php @@ -14,6 +14,7 @@ use SimpleSAML\Session; use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -122,7 +123,7 @@ public function error(Request $request, string $code): Response * * @param \Symfony\Component\HttpFoundation\Request $request The request that lead to this login operation. * @throws \SimpleSAML\Error\BadRequest - * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse + * @return \SimpleSAML\XHTML\Template * An HTML template or a redirection if we are not authenticated. */ public function cardinality(Request $request): Response @@ -158,7 +159,7 @@ public function cardinality(Request $request): Response * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse * An HTML template or a redirection if we are not authenticated. */ - public function nocookie(Request $request): Response + public function nocookie(Request $request): Template|RedirectResponse { $retryURL = $request->query->get('retryURL', null); if ($retryURL !== null) { @@ -181,13 +182,12 @@ public function nocookie(Request $request): Response * * @return ( * \SimpleSAML\XHTML\Template| - * \SimpleSAML\HTTP\RunnableResponse| * \Symfony\Component\HttpFoundation\RedirectResponse - * ) An HTML template, a redirect or a "runnable" response. + * ) An HTML template, or a redirect response. * * @throws \SimpleSAML\Error\BadRequest */ - public function shortSsoInterval(Request $request): Response + public function shortSsoInterval(Request $request): Template|Response { $stateId = $request->query->get('StateId', false); if ($stateId === false) { @@ -199,7 +199,7 @@ public function shortSsoInterval(Request $request): Response $continue = $request->query->get('continue', false); if ($continue !== false) { // The user has pressed the continue/retry-button - Auth\ProcessingChain::resumeProcessing($state); + return Auth\ProcessingChain::resumeProcessing($state); } $t = new Template($this->config, 'core:short_sso_interval.twig'); diff --git a/modules/core/src/Controller/Login.php b/modules/core/src/Controller/Login.php index 4bb708883b..361bec19a5 100644 --- a/modules/core/src/Controller/Login.php +++ b/modules/core/src/Controller/Login.php @@ -14,6 +14,7 @@ use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -101,6 +102,7 @@ public function welcome(): Template * username/password authentication. * * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response */ public function loginuserpass(Request $request): Response { @@ -147,6 +149,7 @@ public static function registerErrorCodeClass(ErrorCodes $ecc): void * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\Module\core\Auth\UserPassBase|\SimpleSAML\Module\core\Auth\UserPassOrgBase $source * @param array $state + * @return \Symfony\Component\HttpFoundation\Response */ private function handleLogin(Request $request, UserPassBase|UserPassOrgBase $source, array $state): Response { @@ -238,9 +241,9 @@ private function handleLogin(Request $request, UserPassBase|UserPassOrgBase $sou try { if ($source instanceof UserPassOrgBase) { - $source::handleLogin($authStateId, $username, $password, $organization); + return $source::handleLogin($authStateId, $username, $password, $organization); } else { - $source::handleLogin($authStateId, $username, $password); + return $source::handleLogin($authStateId, $username, $password); } } catch (Error\Error $e) { // Login failed. Extract error code and parameters, to display the error @@ -362,6 +365,7 @@ private function handleLogin(Request $request, UserPassBase|UserPassOrgBase $sou * username/password/organization authentication. * * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response */ public function loginuserpassorg(Request $request): Response { @@ -506,8 +510,9 @@ private function getReturnPath(Request $request): string * This clears the user's IdP discovery choices. * * @param \Symfony\Component\HttpFoundation\Request $request The request that lead to this login operation. + * @return \Symfony\Component\HttpFoundation\RedirectResponse */ - public function cleardiscochoices(Request $request): void + public function cleardiscochoices(Request $request): RedirectResponse { $httpUtils = new Utils\HTTP(); @@ -527,6 +532,6 @@ public function cleardiscochoices(Request $request): void $returnTo = $this->getReturnPath($request); // Redirect to destination. - $httpUtils->redirectTrustedURL($returnTo); + return $httpUtils->redirectTrustedURL($returnTo); } } diff --git a/modules/core/src/Controller/Logout.php b/modules/core/src/Controller/Logout.php index aef2fa3645..7098df99fd 100644 --- a/modules/core/src/Controller/Logout.php +++ b/modules/core/src/Controller/Logout.php @@ -19,6 +19,7 @@ use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use function call_user_func; use function in_array; @@ -74,19 +75,15 @@ public function setAuthState(Auth\State $authState): void * * @param \Symfony\Components\HttpFoundation\Request $request The request that lead to this logout operation. * @param string $as The name of the auth source. - * - * @return \SimpleSAML\HTTP\RunnableResponse A runnable response which will actually perform logout. + * @return \Symfony\Component\HttpFoundation\Response * * @throws \SimpleSAML\Error\CriticalConfigurationError */ - public function logout(Request $request, string $as): RunnableResponse + public function logout(Request $request, string $as): Response { $auth = new Auth\Simple($as); $returnTo = $this->getReturnPath($request); - return new RunnableResponse( - [$auth, 'logout'], - [$returnTo], - ); + return $auth->logout($returnTo); } @@ -113,9 +110,9 @@ private function getReturnPath(Request $request): string /** * @param \Symfony\Component\HttpFoundation\Request $request The request that lead to this logout operation. - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function logoutIframeDone(Request $request): RunnableResponse + public function logoutIframeDone(Request $request): Response { if (!$request->query->has('id')) { throw new Error\BadRequest('Missing required parameter: id'); @@ -174,15 +171,15 @@ public function logoutIframeDone(Request $request): RunnableResponse } // we are done - return new RunnableResponse([$idp, 'finishLogout'], [$state]); + return $idp->finishLogout($state); } /** * @param \Symfony\Component\HttpFoundation\Request $request The request that lead to this logout operation. - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function logoutIframePost(Request $request): RunnableResponse + public function logoutIframePost(Request $request): Response { if (!$request->query->has('idp')) { throw new Error\BadRequest('Missing required parameter: idp'); @@ -240,7 +237,9 @@ public function logoutIframePost(Request $request): RunnableResponse $lr->setDestination($dst['Location']); $lr->setRelayState($relayState); - return new RunnableResponse([$binding, 'send'], [$lr]); + $psrResponse = $binding->send($lr); + $httpFoundationFactory = new HttpFoundationFactory(); + return $httpFoundationFactory->createResponse($psrResponse); } @@ -392,9 +391,9 @@ public function logoutIframe(Request $request): Template /** * @param \Symfony\Component\HttpFoundation\Request $request The request that lead to this logout operation. - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function resumeLogout(Request $request): RunnableResponse + public function resumeLogout(Request $request): Response { if (!$request->query->has('id')) { throw new Error\BadRequest('Missing required parameter: id'); @@ -405,6 +404,7 @@ public function resumeLogout(Request $request): RunnableResponse $idp = IdP::getByState($state); $assocId = $state['core:TerminatedAssocId']; - return new RunnableResponse([$idp->getLogoutHandler(), 'startLogout'], [&$state, $assocId]); + $logoutHandler = $idp->getLogoutHandler(); + return $logoutHandler->startLogout($state, $assocId); } } From 49db1dd6fbb7664ea8480b67917d92dbc13ab020 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 14 Mar 2026 23:29:49 +0100 Subject: [PATCH 3/6] Convert cron-module to symfony-responses --- modules/cron/src/Controller/Cron.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/cron/src/Controller/Cron.php b/modules/cron/src/Controller/Cron.php index 3d6c66807f..71412d8817 100644 --- a/modules/cron/src/Controller/Cron.php +++ b/modules/cron/src/Controller/Cron.php @@ -66,12 +66,16 @@ public function setAuthUtils(Utils\Auth $authUtils): void /** * Show cron info. * - * @return \SimpleSAML\XHTML\Template + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response * An HTML template or a redirection if we are not authenticated. */ - public function info(): Template + public function info(): Response { - $this->authUtils->requireAdmin(); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } $key = $this->cronconfig->getOptionalString('key', 'secret'); $tags = $this->cronconfig->getOptionalArray('allowed_tags', []); @@ -104,16 +108,16 @@ public function info(): Template * * This controller will start a cron operation * + * @param \Symfony\Component\HttpFoundation\Request $request * @param string $tag The tag * @param string $key The secret key * @param string $output The output format, defaulting to xhtml * - * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\Response - * An HTML template, a redirect or a "runnable" response. + * @return \SimpleSAML\XHTML\Template An HTML template, a redirect or a "runnable" response. * * @throws \SimpleSAML\Error\Exception */ - public function run(string $tag, string $key, string $output = 'xhtml'): Response + public function run(Request $request, string $tag, string $key, string $output = 'xhtml'): Template { $configKey = $this->cronconfig->getString('key'); @@ -170,6 +174,7 @@ public function run(string $tag, string $key, string $output = 'xhtml'): Respons $t->data['summary'] = $summary; return $t; } - return new Response(); + + throw new Error\Exception('Unknown output type.'); } } From 2300a7b8d887f66113dd7abbf9f240d283ce23b4 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sat, 14 Mar 2026 23:43:02 +0100 Subject: [PATCH 4/6] Convert exampleauth-module to symfony-responses --- .../src/Auth/Process/RedirectTest.php | 3 +- .../exampleauth/src/Auth/Source/External.php | 63 +++++++++---------- .../src/Auth/Source/StaticSource.php | 5 +- .../src/Controller/ExampleAuth.php | 25 ++++---- 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/modules/exampleauth/src/Auth/Process/RedirectTest.php b/modules/exampleauth/src/Auth/Process/RedirectTest.php index 3167fb6866..62b3107814 100644 --- a/modules/exampleauth/src/Auth/Process/RedirectTest.php +++ b/modules/exampleauth/src/Auth/Process/RedirectTest.php @@ -32,6 +32,7 @@ public function process(array &$state): void $url = Module::getModuleURL('exampleauth/redirecttest'); $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response = $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response->send(); } } diff --git a/modules/exampleauth/src/Auth/Source/External.php b/modules/exampleauth/src/Auth/Source/External.php index a18b290e5a..b259c708b0 100644 --- a/modules/exampleauth/src/Auth/Source/External.php +++ b/modules/exampleauth/src/Auth/Source/External.php @@ -10,6 +10,7 @@ use SimpleSAML\Module; use SimpleSAML\Utils; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Session as SymfonySession; /** @@ -66,12 +67,12 @@ private function getUser(): ?array * stored in the users PHP session, but this could be replaced * with anything. */ - $session = new SymfonySession(); - if (!$session->getId()) { - $session->start(); + if (!session_id()) { + // session_start not called before. Do it here + @session_start(); } - if (!$session->has('uid')) { + if (!isset($_SESSION['uid'])) { // The user isn't authenticated return null; } @@ -82,14 +83,14 @@ private function getUser(): ?array * to store them as arrays. */ $attributes = [ - 'uid' => [$session->get('uid')], - 'displayName' => [$session->get('name')], - 'mail' => [$session->get('mail')], + 'uid' => [$_SESSION['uid']], + 'displayName' => [$_SESSION['name']], + 'mail' => [$_SESSION['mail']], ]; // Here we generate a multivalued attribute based on the account type $attributes['eduPersonAffiliation'] = [ - $session->get('type'), /* In this example, either 'student' or 'employee'. */ + $_SESSION['type'], /* In this example, either 'student' or 'employee'. */ 'member', ]; @@ -100,9 +101,11 @@ private function getUser(): ?array /** * Log in using an external authentication helper. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. + * @return \Symfony\Component\HttpFoundation\Response */ - public function authenticate(array &$state): void + public function authenticate(Request $request, array &$state): ?Response { $attributes = $this->getUser(); if ($attributes !== null) { @@ -113,7 +116,7 @@ public function authenticate(array &$state): void * to the authentication process. */ $state['Attributes'] = $attributes; - return; + return null; } /* @@ -148,7 +151,7 @@ public function authenticate(array &$state): void * option to return the user to a specific page afterwards. */ $returnTo = Module::getModuleURL('exampleauth/resume', [ - 'State' => $stateId, + 'AuthState' => $stateId, ]); /* @@ -167,14 +170,9 @@ public function authenticate(array &$state): void * the real name of the parameter for the login page. */ $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($authPage, [ + return $httpUtils->redirectTrustedURL($authPage, [ 'ReturnTo' => $returnTo, ]); - - /* - * The redirect function never returns, so we never get this far. - */ - Assert::true(false); } @@ -185,17 +183,19 @@ public function authenticate(array &$state): void * entered his or her credentials. * * @param \Symfony\Component\HttpFoundation\Request $request + * @param \SimpleSAML\Auth\State $authState + * @return \Symfony\Component\HttpFoundation\Response * * @throws \SimpleSAML\Error\BadRequest * @throws \SimpleSAML\Error\Exception */ - public static function resume(Request $request): void + public static function resume(Request $request, Auth\State $authState): Response { /* * First we need to restore the $state-array. We should have the identifier for * it in the 'State' request parameter. */ - if (!$request->query->has('State')) { + if (!$request->query->has('AuthState')) { throw new Error\BadRequest('Missing "State" parameter.'); } @@ -203,7 +203,7 @@ public static function resume(Request $request): void * Once again, note the second parameter to the loadState function. This must * match the string we used in the saveState-call above. */ - $state = Auth\State::loadState($request->query->get('State'), 'exampleauth:External'); + $state = $authState::loadState($request->query->get('AuthState'), 'exampleauth:External'); /* * Now we have the $state-array, and can use it to locate the authentication @@ -249,12 +249,7 @@ public static function resume(Request $request): void */ $state['Attributes'] = $attributes; - Auth\Source::completeAuth($state); - - /* - * The completeAuth-function never returns, so we never get this far. - */ - Assert::true(false); + return parent::completeAuth($state); } @@ -264,18 +259,22 @@ public static function resume(Request $request): void * * @param array &$state The logout state array. */ - public function logout(array &$state): void + public function logout(array &$state): null { - $session = new SymfonySession(); - if (!$session->getId()) { - $session->start(); + if (!session_id()) { + // session_start not called before. Do it here + @session_start(); } - $session->clear(); + /** + * In this example we simply remove the 'uid' from the session + */ + unset($_SESSION['uid']); - /* + /** * If we need to do a redirect to a different page, we could do this * here, but in this example we don't need to do this. */ + return null; } } diff --git a/modules/exampleauth/src/Auth/Source/StaticSource.php b/modules/exampleauth/src/Auth/Source/StaticSource.php index 7be3c04637..40095d6dcc 100644 --- a/modules/exampleauth/src/Auth/Source/StaticSource.php +++ b/modules/exampleauth/src/Auth/Source/StaticSource.php @@ -51,10 +51,13 @@ public function __construct(array $info, array $config) /** * Log in using static attributes. * + * @param \Symfony\Component\HttpFoundation\Request $request The current request * @param array &$state Information about the current authentication. + * @return \Symfony\Component\HttpFoundation\Response */ - public function authenticate(array &$state): void + public function authenticate(Request $request, array &$state): ?Response { $state['Attributes'] = $this->attributes; + return null; } } diff --git a/modules/exampleauth/src/Controller/ExampleAuth.php b/modules/exampleauth/src/Controller/ExampleAuth.php index 561816e9f1..98b205eec9 100644 --- a/modules/exampleauth/src/Controller/ExampleAuth.php +++ b/modules/exampleauth/src/Controller/ExampleAuth.php @@ -12,6 +12,7 @@ use SimpleSAML\Session; use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\Session as SymfonySession; @@ -68,9 +69,9 @@ public function setAuthState(Auth\State $authState): void * * @param \Symfony\Component\HttpFoundation\Request $request The current request. * - * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse + * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\RedirectResponse */ - public function authpage(Request $request): Response + public function authpage(Request $request): RedirectResponse { /** * This page serves as a dummy login page. @@ -145,7 +146,7 @@ public function authpage(Request $request): Response $session->set('mail', $user['mail']); $session->set('type', $user['type']); - return new RunnableResponse([$httpUtils, 'redirectTrustedURL'], [$returnTo]); + return $httpUtils->redirectTrustedURL($returnTo); } } @@ -162,23 +163,22 @@ public function authpage(Request $request): Response * Redirect testpage. * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function redirecttest(Request $request): RunnableResponse + public function redirecttest(Request $request): Response { /** * Request handler for redirect filter test. */ - $stateId = $request->query->get('StateId'); + $stateId = $request->query->get('AuthState'); if ($stateId === null) { - throw new Error\BadRequest('Missing required StateId query parameter.'); + throw new Error\BadRequest('Missing required AuthState query parameter.'); } $state = $this->authState::loadState($stateId, 'exampleauth:redirectfilter-test'); $state['Attributes']['RedirectTest2'] = ['OK']; - return new RunnableResponse([Auth\ProcessingChain::class, 'resumeProcessing'], [$state]); + return Auth\ProcessingChain::resumeProcessing($state); } @@ -186,10 +186,9 @@ public function redirecttest(Request $request): RunnableResponse * Resume testpage. * * @param \Symfony\Component\HttpFoundation\Request $request The current request. - * - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function resume(Request $request): RunnableResponse + public function resume(Request $request): Response { /** * This page serves as the point where the user's authentication @@ -197,6 +196,6 @@ public function resume(Request $request): RunnableResponse * * It simply passes control back to the class. */ - return new RunnableResponse([External::class, 'resume'], [$request]); + return External::resume($request, $this->authState); } } From e3f18b322e935065210f8ed4f0a8e8480a806f51 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Sun, 15 Mar 2026 00:10:59 +0100 Subject: [PATCH 5/6] Convert multiauth-module to symfony-responses --- .../multiauth/src/Auth/Source/MultiAuth.php | 43 +++++++++++-------- .../src/Controller/DiscoController.php | 8 ++-- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/modules/multiauth/src/Auth/Source/MultiAuth.php b/modules/multiauth/src/Auth/Source/MultiAuth.php index fd6c45304d..8f203c096a 100644 --- a/modules/multiauth/src/Auth/Source/MultiAuth.php +++ b/modules/multiauth/src/Auth/Source/MultiAuth.php @@ -86,15 +86,15 @@ public function __construct(array $info, array $config) * Prompt the user with a list of authentication sources. * * This method saves the information about the configured sources, - * and redirects to a page where the user must select one of these - * authentication sources. + * and redirects to a page where the user must select one of these authentication sources. * - * This method never return. The authentication process is finished - * in the delegateAuthentication method. + * The authentication process is finished in the delegateAuthentication method. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. + * @return \Symfony\Component\HttpFoundation\Response */ - public function authenticate(array &$state): never + public function authenticate(Request $request, array &$state): Response { $state[self::AUTHID] = $this->authId; $state[self::SOURCESID] = $this->sources; @@ -125,7 +125,7 @@ public function authenticate(array &$state): never 'No authentication sources exist for the requested AuthnContextClassRefs: ' . implode(', ', $refs), ); } elseif ($number_of_sources === 1) { - MultiAuth::delegateAuthentication(array_key_first($new_sources), $state); + return MultiAuth::delegateAuthentication(array_key_first($new_sources), $state); } } @@ -139,12 +139,12 @@ public function authenticate(array &$state): never $params = ['AuthState' => $id]; // Allows the user to specify the auth source to be used - if (isset($_GET['source'])) { - $params['source'] = $_GET['source']; + if ($request->query->has('source')) { + $params['source'] = $request->query->get('source'); } $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, $params); + return $httpUtils->redirectTrustedURL($url, $params); } @@ -158,10 +158,10 @@ public function authenticate(array &$state): never * * @param string $authId Selected authentication source * @param array $state Information about the current authentication. - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response * @throws \Exception */ - public static function delegateAuthentication(string $authId, array $state): RunnableResponse + public static function delegateAuthentication(string $authId, array $state): Response { $as = Auth\Source::getById($authId); if ($as === null || !array_key_exists($authId, $state[self::SOURCESID])) { @@ -177,26 +177,32 @@ public static function delegateAuthentication(string $authId, array $state): Run Session::DATA_TIMEOUT_SESSION_END, ); - return new RunnableResponse([self::class, 'doAuthentication'], [$as, $state]); + return self::doAuthentication($as, $state); } /** * @param \SimpleSAML\Auth\Source $as * @param array $state - * @return void + * @return Symfony\Component\HttpFoundation\Response */ - public static function doAuthentication(Auth\Source $as, array $state): void + public static function doAuthentication(Auth\Source $as, array $state): Response { + $request = Request::createFromGlobals(); + try { - $as->authenticate($state); + $response = $as->authenticate($state); + if ($response instanceof Response) { + return $response; + } } catch (Error\Exception $e) { Auth\State::throwException($state, $e); } catch (Exception $e) { $e = new Error\UnserializableException($e); Auth\State::throwException($state, $e); } - Auth\Source::completeAuth($state); + + return parent::completeAuth($state); } @@ -207,8 +213,9 @@ public static function doAuthentication(Auth\Source $as, array $state): void * session and then call the logout method on it. * * @param array &$state Information about the current logout operation. + * @return \Symfony\Component\HttpFoundation\Response */ - public function logout(array &$state): void + public function logout(array &$state): Response { // Get the source that was used to authenticate $session = Session::getSessionFromRequest(); @@ -219,7 +226,7 @@ public function logout(array &$state): void throw new Exception('Invalid authentication source during logout: ' . $authId); } // Then, do the logout on it - $source->logout($state); + return $source->logout($state); } diff --git a/modules/multiauth/src/Controller/DiscoController.php b/modules/multiauth/src/Controller/DiscoController.php index 6ed67974fb..ec972ab498 100644 --- a/modules/multiauth/src/Controller/DiscoController.php +++ b/modules/multiauth/src/Controller/DiscoController.php @@ -81,10 +81,10 @@ public function setAuthState(Auth\State $authState): void * delegateAuthentication method on it. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\XHTML\Template|\SimpleSAML\HTTP\RunnableResponse + * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\Response * An HTML template or a redirection if we are not authenticated. */ - public function discovery(Request $request): Response + public function discovery(Request $request): Template|Response { // Retrieve the authentication state $authStateId = $request->query->get('AuthState', null); @@ -103,8 +103,8 @@ public function discovery(Request $request): Response } // Get a preselected source either from the URL or the discovery page - $urlSource = $request->get('source', null); - $discoSource = $request->get('sourceChoice', null); + $urlSource = $request->query->get('source', null); + $discoSource = $request->query->get('sourceChoice', null); $selectedSource = null; if ($urlSource !== null) { From 3d315ac194f0c0bf1f21b7a1f4176fdc7f3ce8f1 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Tue, 17 Mar 2026 00:08:58 +0100 Subject: [PATCH 6/6] Convert saml-module to symfony-responses --- .../Process/ExpectedAuthnContextClassRef.php | 3 +- modules/saml/src/Auth/Source/SP.php | 95 ++++++++++++------- modules/saml/src/Controller/Disco.php | 12 ++- modules/saml/src/Controller/Metadata.php | 10 +- modules/saml/src/Controller/Proxy.php | 8 +- .../saml/src/Controller/ServiceProvider.php | 26 ++--- modules/saml/src/Controller/SingleLogout.php | 28 +++--- .../src/Controller/WebBrowserSingleSignOn.php | 6 +- modules/saml/src/IdP/SAML2.php | 66 +++++++------ 9 files changed, 141 insertions(+), 113 deletions(-) diff --git a/modules/saml/src/Auth/Process/ExpectedAuthnContextClassRef.php b/modules/saml/src/Auth/Process/ExpectedAuthnContextClassRef.php index b287cbf5b0..9bebee8b2a 100644 --- a/modules/saml/src/Auth/Process/ExpectedAuthnContextClassRef.php +++ b/modules/saml/src/Auth/Process/ExpectedAuthnContextClassRef.php @@ -107,6 +107,7 @@ protected function unauthorized(array &$state): void ); $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response = $httpUtils->redirectTrustedURL($url, ['StateId' => $id]); + $response->send(); } } diff --git a/modules/saml/src/Auth/Source/SP.php b/modules/saml/src/Auth/Source/SP.php index 12efdc3231..a96719195b 100644 --- a/modules/saml/src/Auth/Source/SP.php +++ b/modules/saml/src/Auth/Source/SP.php @@ -88,6 +88,13 @@ class SP extends Auth\Source */ private bool $requestInitiation; + /** + * The global configuration. + * + * @var \SimpleSAML\Configuration + */ + private Configuration $config; + /** * Constructor for SAML SP authentication source. @@ -126,6 +133,7 @@ public function __construct(array $info, array $config) 'Please set a valid and unique SP entityID', ); + $this->config = Configuration::getInstance(); $this->entityId = $entityId; $this->idp = $this->metadata->getOptionalString('idp', null); $this->discoURL = $this->metadata->getOptionalString('discoURL', null); @@ -232,12 +240,11 @@ public function getHostedMetadata(): array } // add technical contact - $globalConfig = Configuration::getInstance(); - $email = $globalConfig->getOptionalString('technicalcontact_email', 'na@example.org'); + $email = $this->config->getOptionalString('technicalcontact_email', 'na@example.org'); if (!empty($email) && $email !== 'na@example.org') { $contact = [ 'emailAddress' => $email, - 'givenName' => $globalConfig->getOptionalString('technicalcontact_name', null), + 'givenName' => $this->config->getOptionalString('technicalcontact_name', null), 'contactType' => 'technical', ]; $metadata['contacts'][] = Utils\Config\Metadata::getContact($contact); @@ -320,17 +327,18 @@ public function getHostedMetadata(): array /** * Retrieve the metadata of an IdP. * + * @param \SimpleSAML\Configuration $config The configuration * @param string $entityId The entity id of the IdP. * @return \SimpleSAML\Configuration The metadata of the IdP. */ - public function getIdPMetadata(string $entityId): Configuration + public function getIdPMetadata(Configuration $config, string $entityId): Configuration { if ($this->idp !== null && $this->idp !== $entityId) { throw new Error\Exception('Cannot retrieve metadata for IdP ' . var_export($entityId, true) . ' because it isn\'t a valid IdP for this SP.'); } - $metadataHandler = MetaDataStorageHandler::getMetadataHandler(); + $metadataHandler = MetaDataStorageHandler::getMetadataHandler($config); return $metadataHandler->getMetaDataConfig($entityId, 'saml20-idp-remote'); } @@ -423,8 +431,7 @@ private function getACSEndpoints(): array */ private function getSLOEndpoints(): array { - $config = Configuration::getInstance(); - $storeType = $config->getOptionalString('store.type', 'phpsession'); + $storeType = $this->config->getOptionalString('store.type', 'phpsession'); $store = StoreFactory::getInstance($storeType); $bindings = $this->metadata->getOptionalArray( @@ -726,12 +733,13 @@ public function sendSAML2LogoutRequest(Binding $binding, LogoutRequest $lr): nev /** * Send a SSO request to an IdP. * + * @param \SimpleSAML\Configuration $config * @param string $idp The entity ID of the IdP. * @param array $state The state array for the current authentication. */ - public function startSSO(string $idp, array $state): never + public function startSSO(Configuration $config, string $idp, array $state): never { - $idpMetadata = $this->getIdPMetadata($idp); + $idpMetadata = $this->getIdPMetadata($config, $idp); $type = $idpMetadata->getString('metadata-set'); Assert::oneOf($type, ['saml20-idp-remote']); @@ -744,8 +752,9 @@ public function startSSO(string $idp, array $state): never * Start an IdP discovery service operation. * * @param array $state The state array. + * @return \Symfony\Component\HttpFoundation\RedirectResponse */ - private function startDisco(array $state): never + private function startDisco(array $state): RedirectResponse { $id = Auth\State::saveState($state, 'saml:sp:sso'); @@ -772,7 +781,7 @@ private function startDisco(array $state): never } $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($discoURL, $params); + return $httpUtils->redirectTrustedURL($discoURL, $params); } @@ -781,9 +790,10 @@ private function startDisco(array $state): never * * This function saves the information about the login, and redirects to the IdP. * + * @param \Symfony\Component\HttpFoundation\Request The current request * @param array &$state Information about the current authentication. */ - public function authenticate(array &$state): never + public function authenticate(Request $request, array &$state): Response { // We are going to need the authId in order to retrieve this authentication source later $state['saml:sp:AuthId'] = $this->authId; @@ -796,7 +806,7 @@ public function authenticate(array &$state): never if (isset($state['saml:IDPList']) && sizeof($state['saml:IDPList']) > 0) { // we have a SAML IDPList (we are a proxy): filter the list of IdPs available - $mdh = MetaDataStorageHandler::getMetadataHandler(); + $mdh = MetaDataStorageHandler::getMetadataHandler($this->config); $matchedEntities = $mdh->getMetaDataForEntities($state['saml:IDPList'], 'saml20-idp-remote'); if (empty($matchedEntities)) { @@ -820,10 +830,12 @@ public function authenticate(array &$state): never } if ($idp === null) { - $this->startDisco($state); + $response = $this->startDisco($state); } else { - $this->startSSO($idp, $state); + $response = $this->startSSO($this->config, $idp, $state); } + + return $response; } @@ -861,7 +873,7 @@ public function reauthenticate(array &$state): void * First, check if we recognize any of the IdPs requested. */ - $mdh = MetaDataStorageHandler::getMetadataHandler(); + $mdh = MetaDataStorageHandler::getMetadataHandler($this->config); $known_idps = $mdh->getList(); $intersection = array_intersect($state['saml:IDPList'], array_keys($known_idps)); @@ -896,9 +908,11 @@ public function reauthenticate(array &$state): void $state['core:SP'], )); - $state['saml:sp:IdPMetadata'] = $this->getIdPMetadata($state['saml:sp:IdP']); + $state['saml:sp:IdPMetadata'] = $this->getIdPMetadata($this->config, $state['saml:sp:IdP']); $state['saml:sp:AuthId'] = $this->authId; - self::askForIdPChange($state); + + $response = self::askForIdPChange($state); + return $response; } /* @@ -943,10 +957,11 @@ public function reauthenticate(array &$state): void * - 'saml:sp:AuthId': the identifier of the current authentication source. * - 'core:IdP': the identifier of the local IdP. * - 'SPMetadata': an array with the metadata of this local SP. + * @return \Symfony\Component\HttpFoundation\RedirectResponse * * @throws \SAML2\Exception\Protocol\NoPassiveException In case the authentication request was passive. */ - public static function askForIdPChange(array &$state): never + public static function askForIdPChange(array &$state): RedirectResponse { Assert::keyExists($state, 'saml:sp:IdPMetadata'); Assert::keyExists($state, 'saml:sp:AuthId'); @@ -965,7 +980,7 @@ public static function askForIdPChange(array &$state): never $url = Module::getModuleURL('saml/proxy/invalidSession'); $httpUtils = new Utils\HTTP(); - $httpUtils->redirectTrustedURL($url, ['AuthState' => $id]); + return $httpUtils->redirectTrustedURL($url, ['AuthState' => $id]); } @@ -1006,7 +1021,7 @@ public static function tryStepUpAuth(array &$state): never * * @param array $state The state array. */ - public static function reauthLogout(array $state): never + public static function reauthLogout(Configuration $config, array $state): never { Logger::debug('Proxy: logging the user out before re-authentication.'); @@ -1024,8 +1039,9 @@ public static function reauthLogout(array $state): never * Complete login operation after re-authenticating the user on another IdP. * * @param array $state The authentication state. + * @return \Symfony\Component\HttpFoundation\Response */ - public static function reauthPostLogin(array $state): never + public static function reauthPostLogin(array $state): Response { Assert::keyExists($state, 'ReturnCallback'); @@ -1035,7 +1051,7 @@ public static function reauthPostLogin(array $state): never $session->doLogin($authId, Auth\State::getPersistentAuthData($state)); // resume the login process - call_user_func($state['ReturnCallback'], $state); + return call_user_func($state['ReturnCallback'], $state); } @@ -1046,8 +1062,9 @@ public static function reauthPostLogin(array $state): never * * @param \SimpleSAML\IdP $idp The IdP we are logging out from. * @param array &$state The state array with the state during logout. + * @return \Symfony\Component\HttpFoundation\Response */ - public static function reauthPostLogout(IdP $idp, array $state): never + public static function reauthPostLogout(IdP $idp, array $state): Response { Assert::keyExists($state, 'saml:sp:AuthId'); @@ -1058,19 +1075,21 @@ public static function reauthPostLogout(IdP $idp, array $state): never } /** @var \SimpleSAML\Module\saml\Auth\Source\SP $sp */ - $sp = Auth\Source::getById($state['saml:sp:AuthId'], Module\saml\Auth\Source\SP::class); + $sp = Auth\Source::getById($state['saml:sp:AuthId'], static::class); Logger::debug('Proxy: logging in again.'); - $sp->authenticate($state); + $request = Request::createFromGlobals(); + return $sp->authenticate($request, $state); } /** * Start a SAML 2 logout operation. * + * @param \SimpleSAML\Configuration $config The configuration * @param array $state The logout state. */ - public function startSLO2(array &$state): void + public function startSLO2(Configuration $config, array &$state): void { Assert::keyExists($state, 'saml:logout:IdP'); Assert::keyExists($state, 'saml:logout:NameID'); @@ -1082,7 +1101,7 @@ public function startSLO2(array &$state): void $nameId = $state['saml:logout:NameID']; $sessionIndex = $state['saml:logout:SessionIndex']; - $idpMetadata = $this->getIdPMetadata($idp); + $idpMetadata = $this->getIdPMetadata($config, $idp); /** @var array $endpoint */ $endpoint = $idpMetadata->getEndpointPrioritizedByBinding( @@ -1151,13 +1170,14 @@ public function logout(array &$state): void * @param array $state The authentication state. * @param string $idp The entity id of the IdP. * @param array $attributes The attributes. + * @return \Symfony\Component\HttpFoundation\Response */ - public function handleResponse(array $state, string $idp, array $attributes): never + public function handleResponse(array $state, string $idp, array $attributes): Response { Assert::keyExists($state, 'LogoutState'); Assert::keyExists($state['LogoutState'], 'saml:logout:Type'); - $idpMetadata = $this->getIdPMetadata($idp); + $idpMetadata = $this->getIdPMetadata($this->config, $idp); $spMetadataArray = $this->metadata->toArray(); $idpMetadataArray = $idpMetadata->toArray(); @@ -1187,7 +1207,7 @@ public function handleResponse(array $state, string $idp, array $attributes): ne $pc = new Auth\ProcessingChain($idpMetadataArray, $spMetadataArray, 'sp'); $pc->processState($authProcState); - self::onProcessingCompleted($authProcState); + return self::onProcessingCompleted($authProcState); } @@ -1195,11 +1215,12 @@ public function handleResponse(array $state, string $idp, array $attributes): ne * Handle a logout request from an IdP. * * @param string $idpEntityId The entity ID of the IdP. + * @return \Symfony\Component\HttpFoundation\Response */ - public function handleLogout(string $idpEntityId): never + public function handleLogout(string $idpEntityId): Response { /* Call the logout callback we registered in onProcessingCompleted(). */ - $this->callLogoutCallback($idpEntityId); + return $this->callLogoutCallback($idpEntityId); } @@ -1223,7 +1244,8 @@ public static function handleUnsolicitedAuth(string $authId, array $state, strin $session->doLogin($authId, Auth\State::getPersistentAuthData($state)); $httpUtils = new Utils\HTTP(); - $httpUtils->redirectUntrustedURL($redirectTo); + $response = $httpUtils->redirectUntrustedURL($redirectTo); + $response->send(); } @@ -1231,8 +1253,9 @@ public static function handleUnsolicitedAuth(string $authId, array $state, strin * Called when we have completed the procssing chain. * * @param array $authProcState The processing chain state. + * @return \Symfony\Component\HttpFoundation\Response */ - public static function onProcessingCompleted(array $authProcState): never + public static function onProcessingCompleted(array $authProcState): Response { Assert::keyExists($authProcState, 'saml:sp:IdP'); Assert::keyExists($authProcState, 'saml:sp:State'); @@ -1264,6 +1287,6 @@ public static function onProcessingCompleted(array $authProcState): never self::handleUnsolicitedAuth($sourceId, $state, $redirectTo); } - Auth\Source::completeAuth($state); + return Auth\Source::completeAuth($state); } } diff --git a/modules/saml/src/Controller/Disco.php b/modules/saml/src/Controller/Disco.php index d3b9ed9d4f..18ec1c0ca0 100644 --- a/modules/saml/src/Controller/Disco.php +++ b/modules/saml/src/Controller/Disco.php @@ -5,8 +5,9 @@ namespace SimpleSAML\Module\saml\Controller; use SimpleSAML\Configuration; -use SimpleSAML\HTTP\RunnableResponse; use SimpleSAML\XHTML\IdPDisco; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; /** * Controller class for the saml module. @@ -33,11 +34,12 @@ public function __construct( /** * Built-in IdP discovery service * - * @return \SimpleSAML\HTTP\RunnableResponse + * @param \Symfony\Component\HttpFoundation\Request $request + * @return \Symfony\Component\HttpFoundation\Response */ - public function disco(): RunnableResponse + public function disco(Request $request): Response { - $disco = new IdPDisco(['saml20-idp-remote'], 'saml'); - return new RunnableResponse([$disco, 'handleRequest']); + $disco = new IdPDisco($request, ['saml20-idp-remote'], 'saml'); + return $disco->handleRequest(); } } diff --git a/modules/saml/src/Controller/Metadata.php b/modules/saml/src/Controller/Metadata.php index e9766dea97..9168357199 100644 --- a/modules/saml/src/Controller/Metadata.php +++ b/modules/saml/src/Controller/Metadata.php @@ -7,7 +7,6 @@ use Exception; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\HTTP\RunnableResponse; use SimpleSAML\Metadata as SSPMetadata; use SimpleSAML\Metadata\MetaDataStorageHandler; use SimpleSAML\Module; @@ -43,7 +42,7 @@ public function __construct( protected Configuration $config, ) { $this->authUtils = new Utils\Auth(); - $this->mdHandler = MetaDataStorageHandler::getMetadataHandler(); + $this->mdHandler = MetaDataStorageHandler::getMetadataHandler($config); } @@ -71,7 +70,7 @@ public function setMetadataStorageHandler(MetadataStorageHandler $mdHandler): vo * This endpoint will offer the SAML 2.0 IdP metadata. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\HTTP\RunnableResponse|\Symfony\Component\HttpFoundation\Response + * @return \Symfony\Component\HttpFoundation\Response */ public function metadata(Request $request): Response { @@ -82,7 +81,10 @@ public function metadata(Request $request): Response // check if valid local session exists $protectedMetadata = $this->config->getOptionalBoolean('admin.protectmetadata', false); if ($protectedMetadata && !$this->authUtils->isAdmin()) { - return new RunnableResponse([$this->authUtils, 'requireAdmin']); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } } try { diff --git a/modules/saml/src/Controller/Proxy.php b/modules/saml/src/Controller/Proxy.php index 73ff5c5903..f78e7a5cf3 100644 --- a/modules/saml/src/Controller/Proxy.php +++ b/modules/saml/src/Controller/Proxy.php @@ -64,7 +64,7 @@ public function setAuthState(Auth\State $authState): void * @param \Symfony\Component\HttpFoundation\Request $request * @return \SimpleSAML\XHTML\Template|\Symfony\Component\HttpFoundation\Response */ - public function invalidSession(Request $request): Response + public function invalidSession(Request $request): Template|Response { // retrieve the authentication state $stateId = $request->query->get('AuthState'); // GET @@ -85,8 +85,8 @@ public function invalidSession(Request $request): Response $state = $this->authState::loadState($stateId, 'core:Logout:afterbridge'); // success! Try to continue with reauthentication, since we no longer have a valid session here - $idp = IdP::getById($state['core:IdP']); - return new RunnableResponse([SP::class, 'reauthPostLogout'], [$idp, $state]); + $idp = IdP::getById($this->config, $state['core:IdP']); + return SP::reauthPostLogout($idp, $state); } if ($request->request->has('cancel')) { @@ -105,7 +105,7 @@ public function invalidSession(Request $request): Response $as = new \SimpleSAML\Auth\Simple($state['saml:sp:AuthId']); // log the user out before being able to login again - return new RunnableResponse([$as, 'login'], [$state]); + return $as->reauthLogout($this->config, $state); } $template = new Template($this->config, 'saml:proxy/invalid_session.twig'); diff --git a/modules/saml/src/Controller/ServiceProvider.php b/modules/saml/src/Controller/ServiceProvider.php index 91b2c38e2e..c6bd5ef6bc 100644 --- a/modules/saml/src/Controller/ServiceProvider.php +++ b/modules/saml/src/Controller/ServiceProvider.php @@ -30,6 +30,7 @@ use SimpleSAML\Store\StoreFactory; use SimpleSAML\Utils; use SimpleSAML\XHTML\Template; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -105,10 +106,10 @@ public function setAuthUtils(Utils\Auth $authUtils): void * @param \Symfony\Component\HttpFoundation\Request $request * @param string $sourceId * - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\RedirectResponse * @throws \SimpleSAML\Error\Exception */ - public function login(Request $request, string $sourceId): RunnableResponse + public function login(Request $request, string $sourceId): RedirectResponse { // Initialize all the dependencies $authSource = new Auth\Simple($sourceId); @@ -123,7 +124,7 @@ public function login(Request $request, string $sourceId): RunnableResponse $returnTo = $this->loginHandler($request, $authSource, $spSource, $httpUtils); // Redirect to the returnTo destination - return new RunnableResponse([$httpUtils, 'redirectTrustedURL'], [$returnTo]); + return $httpUtils->redirectTrustedURL($returnTo); } @@ -213,9 +214,9 @@ protected function loginHandler( * Handler for response from IdP discovery service. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function discoResponse(Request $request): RunnableResponse + public function discoResponse(Request $request): Response { if (!$request->query->has('AuthID')) { throw new Error\BadRequest('Missing AuthID to discovery service response handler'); @@ -242,7 +243,7 @@ public function discoResponse(Request $request): RunnableResponse throw new Error\Exception('Source type changed?'); } - return new RunnableResponse([$source, 'startSSO'], [$idpEntityId, $state]); + return $source->startSSO($idpEntityId, $state); } @@ -359,7 +360,7 @@ public function assertionConsumerService(string $sourceId): RunnableResponse // check that the issuer is the one we are expecting Assert::keyExists($state, 'ExpectedIssuer'); if ($state['ExpectedIssuer'] !== $issuer) { - $idpMetadata = $source->getIdPMetadata($issuer); + $idpMetadata = $source->getIdPMetadata($this->config, $issuer); $idplist = $idpMetadata->getOptionalArrayize('IDPList', []); if (!in_array($state['ExpectedIssuer'], $idplist, true)) { Logger::warning( @@ -380,7 +381,7 @@ public function assertionConsumerService(string $sourceId): RunnableResponse Logger::debug('Received SAML2 Response from ' . var_export($issuer, true) . '.'); if (is_null($idpMetadata)) { - $idpMetadata = $source->getIdPmetadata($issuer); + $idpMetadata = $source->getIdPmetadata($this->config, $issuer); } try { @@ -561,7 +562,7 @@ public function singleLogoutService(string $sourceId): RunnableResponse $spEntityId = $source->getEntityId(); - $idpMetadata = $source->getIdPMetadata($idpEntityId); + $idpMetadata = $source->getIdPMetadata($this->config, $idpEntityId); $spMetadata = $source->getMetadata(); Module\saml\Message::validateMessage($idpMetadata, $spMetadata, $message); @@ -688,13 +689,16 @@ public function singleLogoutService(string $sourceId): RunnableResponse * * @param \Symfony\Component\HttpFoundation\Request $request * @param string $sourceId - * @return \Symfony\Component\HttpFoundation\Response|\SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ public function metadata(Request $request, string $sourceId): Response { $protectedMetadata = $this->config->getOptionalBoolean('admin.protectmetadata', false); if ($protectedMetadata && !$this->authUtils->isAdmin()) { - return new RunnableResponse([$this->authUtils, 'requireAdmin']); + $response = $this->authUtils->requireAdmin(); + if ($response instanceof Response) { + return $response; + } } $source = Auth\Source::getById($sourceId); diff --git a/modules/saml/src/Controller/SingleLogout.php b/modules/saml/src/Controller/SingleLogout.php index 75443265f9..91a3680eb2 100644 --- a/modules/saml/src/Controller/SingleLogout.php +++ b/modules/saml/src/Controller/SingleLogout.php @@ -7,13 +7,13 @@ use SAML2\Exception\Protocol\UnsupportedBindingException; use SimpleSAML\Configuration; use SimpleSAML\Error; -use SimpleSAML\HTTP\RunnableResponse; use SimpleSAML\IdP; use SimpleSAML\Logger; use SimpleSAML\Metadata\MetaDataStorageHandler; use SimpleSAML\Module; use SimpleSAML\Utils; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; /** * Controller class for the Single Logout Profile. @@ -44,7 +44,7 @@ class SingleLogout public function __construct( protected Configuration $config, ) { - $this->mdHandler = MetaDataStorageHandler::getMetadataHandler(); + $this->mdHandler = MetaDataStorageHandler::getMetadataHandler($config); } @@ -75,9 +75,9 @@ public function setMetadataStorageHandler(MetaDataStorageHandler $mdHandler): vo * and LogoutRequests and also receive LogoutResponses. It is implementing SLO at the SAML 2.0 IdP. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function singleLogout(Request $request): RunnableResponse + public function singleLogout(Request $request): Response { Logger::info('SAML2.0 - IdP.SingleLogoutService: Accessing SAML 2.0 IdP endpoint SingleLogoutService'); @@ -87,12 +87,11 @@ public function singleLogout(Request $request): RunnableResponse $httpUtils = new Utils\HTTP(); $idpEntityId = $this->mdHandler->getMetaDataCurrentEntityID('saml20-idp-hosted'); - $idp = $this->idp::getById('saml2:' . $idpEntityId); + $idp = $this->idp::getById($this->config, 'saml2:' . $idpEntityId); if ($request->query->has('ReturnTo')) { - return new RunnableResponse( - [$idp, 'doLogoutRedirect'], - [$httpUtils->checkURLAllowed($request->query->get('ReturnTo'))], + return $idp->doLogoutRedirect( + $httpUtils->checkURLAllowed($request->query->get('ReturnTo')), ); } elseif ($request->request->has('ReturnTo')) { return $idp->doLogoutRedirect( @@ -101,7 +100,7 @@ public function singleLogout(Request $request): RunnableResponse } try { - return new RunnableResponse([Module\saml\IdP\SAML2::class, 'receiveLogoutMessage'], [$idp]); + return Module\saml\IdP\SAML2::receiveLogoutMessage($request, $idp); } catch (UnsupportedBindingException $e) { throw new Error\Error(Error\ErrorCodes::SLOSERVICEPARAMS, $e, 400); } @@ -112,9 +111,9 @@ public function singleLogout(Request $request): RunnableResponse * This endpoint will initialize the SLO flow at the SAML 2.0 IdP. * * @param \Symfony\Component\HttpFoundation\Request $request - * @return \SimpleSAML\HTTP\RunnableResponse + * @return \Symfony\Component\HttpFoundation\Response */ - public function initSingleLogout(Request $request): RunnableResponse + public function initSingleLogout(Request $request): Response { Logger::info('SAML2.0 - IdP.initSLO: Accessing SAML 2.0 IdP endpoint init Single Logout'); @@ -123,16 +122,15 @@ public function initSingleLogout(Request $request): RunnableResponse } $idpEntityId = $this->mdHandler->getMetaDataCurrentEntityID('saml20-idp-hosted'); - $idp = $this->idp::getById('saml2:' . $idpEntityId); + $idp = $this->idp::getById($this->config, 'saml2:' . $idpEntityId); if (!$request->query->has('RelayState')) { throw new Error\Error(Error\ErrorCodes::NORELAYSTATE); } $httpUtils = new Utils\HTTP(); - return new RunnableResponse( - [$idp, 'doLogoutRedirect'], - [$httpUtils->checkURLAllowed($request->query->get('RelayState'))], + return $idp->doLogoutRedirect( + $httpUtils->checkURLAllowed($request->query->get('RelayState')), ); } } diff --git a/modules/saml/src/Controller/WebBrowserSingleSignOn.php b/modules/saml/src/Controller/WebBrowserSingleSignOn.php index 0fbb2dc625..9dab564006 100644 --- a/modules/saml/src/Controller/WebBrowserSingleSignOn.php +++ b/modules/saml/src/Controller/WebBrowserSingleSignOn.php @@ -55,7 +55,7 @@ public function artifactResolutionService(): RunnableResponse throw new Error\Error(Error\ErrorCodes::NOACCESS, null, 403); } - $metadata = Metadata\MetaDataStorageHandler::getMetadataHandler(); + $metadata = Metadata\MetaDataStorageHandler::getMetadataHandler($this->config); $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); $idpMetadata = $metadata->getMetaDataConfig($idpEntityId, 'saml20-idp-hosted'); @@ -123,9 +123,9 @@ public function singleSignOnService(): RunnableResponse throw new Error\Error(Error\ErrorCodes::NOACCESS, null, 403); } - $metadata = Metadata\MetaDataStorageHandler::getMetadataHandler(); + $metadata = Metadata\MetaDataStorageHandler::getMetadataHandler($this->config); $idpEntityId = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); - $idp = IdP::getById('saml2:' . $idpEntityId); + $idp = IdP::getById($this->config, 'saml2:' . $idpEntityId); try { return new RunnableResponse([Module\saml\IdP\SAML2::class, 'receiveAuthnRequest'], [$idp]); diff --git a/modules/saml/src/IdP/SAML2.php b/modules/saml/src/IdP/SAML2.php index dbab0a355e..55c80ca6d6 100644 --- a/modules/saml/src/IdP/SAML2.php +++ b/modules/saml/src/IdP/SAML2.php @@ -72,7 +72,7 @@ public static function sendResponse(array $state): void $consumerURL = $state['saml:ConsumerURL']; $protocolBinding = $state['saml:Binding']; - $idp = IdP::getByState($state); + $idp = IdP::getByState(Configuration::getInstance(), $state); $idpMetadata = $idp->getConfig(); @@ -146,7 +146,7 @@ public static function handleAuthError(Error\Exception $exception, array $state) $consumerURL = $state['saml:ConsumerURL']; $protocolBinding = $state['saml:Binding']; - $idp = IdP::getByState($state); + $idp = IdP::getByState(Configuration::getInstance(), $state); $idpMetadata = $idp->getConfig(); @@ -299,12 +299,13 @@ private static function getAssertionConsumerService( /** * Receive an authentication request. * + * @param \Symfony\Component\HttpFoundation\Request $request * @param \SimpleSAML\IdP $idp The IdP we are receiving it for. * @throws \SimpleSAML\Error\BadRequest In case an error occurs when trying to receive the request. */ - public static function receiveAuthnRequest(IdP $idp): void + public static function receiveAuthnRequest(Request $request, IdP $idp): void { - $metadata = MetaDataStorageHandler::getMetadataHandler(); + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); $idpMetadata = $idp->getConfig(); $httpUtils = new Utils\HTTP(); @@ -322,11 +323,11 @@ public static function receiveAuthnRequest(IdP $idp): void $authnRequestSigned = false; $username = null; - if (isset($_REQUEST['spentityid']) || isset($_REQUEST['providerId'])) { + if ($request->query->has('spentityid') || $request->query->has('providerId')) { /* IdP initiated authentication. */ - if (isset($_REQUEST['cookieTime'])) { - $cookieTime = (int) $_REQUEST['cookieTime']; + if ($request->query->has('cookieTime')) { + $cookieTime = intval($request->query->get('cookieTime')); if ($cookieTime + 5 > time()) { /* * Less than five seconds has passed since we were @@ -336,35 +337,33 @@ public static function receiveAuthnRequest(IdP $idp): void } } - $spEntityId = (string) isset($_REQUEST['spentityid']) ? $_REQUEST['spentityid'] : $_REQUEST['providerId']; + $spEntityId = $request->query->has('spentityid') + ? $request->query->get('spentityid') + | $request->query->get('providerId'); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote'); - if (isset($_REQUEST['RelayState'])) { - $relayState = (string) $_REQUEST['RelayState']; - } elseif (isset($_REQUEST['target'])) { - $relayState = (string) $_REQUEST['target']; - } else { - $relayState = null; + $relayState = null; + if ($request->query->has('RelayState')) { + $relayState = $request->query->get('RelayState'); + } elseif ($request->query->has('target')) { + $relayState = $request->query->get('target'); } - if (isset($_REQUEST['binding'])) { - $protocolBinding = (string) $_REQUEST['binding']; - } else { - $protocolBinding = null; + $protocolBinding = null; + if ($request->query->has('binding')) { + $protocolBinding = $request->query->get('binding'); } - if (isset($_REQUEST['NameIDFormat'])) { - $nameIDFormat = (string) $_REQUEST['NameIDFormat']; - } else { - $nameIDFormat = null; + $nameIDFormat = null; + if ($request->query->has('NameIDFormat')) { + $nameIDFormat = $request->query->get('NameIDFormat'); } - if (isset($_REQUEST['ConsumerURL'])) { - $consumerURL = (string) $_REQUEST['ConsumerURL']; - } elseif (isset($_REQUEST['shire'])) { - $consumerURL = (string) $_REQUEST['shire']; - } else { - $consumerURL = null; + $consumerURL = null; + if ($request->query->has('ConsumerURL')) { + $consumerURL = $request->query->get('ConsumerURL'); + } elseif ($request->query->has('shire')) { + $consumerURL = $request->query->get('shire'); } $requestId = null; @@ -543,7 +542,7 @@ public static function sendLogoutRequest(IdP $idp, array $association, ?string $ { Logger::info('Sending SAML 2.0 LogoutRequest to: ' . var_export($association['saml:entityID'], true)); - $metadata = MetaDataStorageHandler::getMetadataHandler(); + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); @@ -647,7 +646,7 @@ public static function receiveLogoutMessage(IdP $idp): void $spEntityId = $issuer->getValue(); } - $metadata = MetaDataStorageHandler::getMetadataHandler(); + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote'); @@ -714,7 +713,7 @@ public static function getLogoutURL(IdP $idp, array $association, ?string $relay { Logger::info('Sending SAML 2.0 LogoutRequest to: ' . var_export($association['saml:entityID'], true)); - $metadata = MetaDataStorageHandler::getMetadataHandler(); + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); @@ -752,7 +751,7 @@ public static function getLogoutURL(IdP $idp, array $association, ?string $relay */ public static function getAssociationConfig(IdP $idp, array $association): Configuration { - $metadata = MetaDataStorageHandler::getMetadataHandler(); + $metadata = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); try { return $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); } catch (Exception $e) { @@ -775,9 +774,8 @@ public static function getAssociationConfig(IdP $idp, array $association): Confi */ public static function getHostedMetadata(string $entityid, ?MetaDataStorageHandler $handler = null): array { - $globalConfig = Configuration::getInstance(); if ($handler === null) { - $handler = MetaDataStorageHandler::getMetadataHandler(); + $handler = MetaDataStorageHandler::getMetadataHandler(Configuration::getInstance()); } $config = $handler->getMetaDataConfig($entityid, 'saml20-idp-hosted');