From 93b9a22a071601cf6c4b7933df08a773d5f46b71 Mon Sep 17 00:00:00 2001 From: Hoang Pham Date: Fri, 28 Jun 2024 18:51:10 +0700 Subject: [PATCH] feat: Post login Idp Signed-off-by: Hoang Pham --- js/admin.js | 2 + lib/Controller/SAMLController.php | 92 ++++++++++++------ lib/SAMLSettings.php | 2 + lib/Settings/Admin.php | 7 +- templates/login_post.php | 34 +++++++ tests/integration/features/Shibboleth.feature | 18 ++++ .../features/bootstrap/FeatureContext.php | 95 +++++++++++++++++++ tests/unit/Settings/AdminTest.php | 8 +- 8 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 templates/login_post.php diff --git a/js/admin.js b/js/admin.js index 35ac06177..e7a580739 100644 --- a/js/admin.js +++ b/js/admin.js @@ -355,10 +355,12 @@ $(function() { } else { $(this).val("0"); } + if(key === 'require_provisioned_account') { $('#user-saml-attribute-mapping').toggleClass('hidden'); $('#user-saml-filtering').toggleClass('hidden'); } + OCA.User_SAML.Admin.setSamlConfigValue('general', key, $(this).val(), true); }); }); diff --git a/lib/Controller/SAMLController.php b/lib/Controller/SAMLController.php index 984cc6603..e6e9365bf 100644 --- a/lib/Controller/SAMLController.php +++ b/lib/Controller/SAMLController.php @@ -6,6 +6,7 @@ namespace OCA\User_SAML\Controller; +use Exception; use Firebase\JWT\JWT; use Firebase\JWT\Key; use OC\Core\Controller\ClientFlowLoginController; @@ -33,6 +34,8 @@ use OneLogin\Saml2\Error; use OneLogin\Saml2\Settings; use OneLogin\Saml2\ValidationError; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; class SAMLController extends Controller { @@ -65,19 +68,19 @@ class SAMLController extends Controller { private ITrustedDomainHelper $trustedDomainHelper; public function __construct( - string $appName, - IRequest $request, - ISession $session, - IUserSession $userSession, - SAMLSettings $samlSettings, - UserBackend $userBackend, - IConfig $config, - IURLGenerator $urlGenerator, - LoggerInterface $logger, - IL10N $l, - UserResolver $userResolver, - UserData $userData, - ICrypto $crypto, + string $appName, + IRequest $request, + ISession $session, + IUserSession $userSession, + SAMLSettings $samlSettings, + UserBackend $userBackend, + IConfig $config, + IURLGenerator $urlGenerator, + LoggerInterface $logger, + IL10N $l, + UserResolver $userResolver, + UserData $userData, + ICrypto $crypto, ITrustedDomainHelper $trustedDomainHelper ) { parent::__construct($appName, $request); @@ -177,9 +180,9 @@ protected function assertGroupMemberships(): void { * @OnlyUnauthenticatedUsers * @NoCSRFRequired * - * @throws \Exception + * @throws Exception */ - public function login(int $idp = 1): Http\RedirectResponse { + public function login(int $idp = 1): Http\RedirectResponse|Http\TemplateResponse { $originalUrl = (string)$this->request->getParam('originalUrl', ''); if (!$this->trustedDomainHelper->isTrustedUrl($originalUrl)) { $originalUrl = ''; @@ -192,7 +195,34 @@ public function login(int $idp = 1): Http\RedirectResponse { $returnUrl = $originalUrl ?: $this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.login'); $ssoUrl = $auth->login($returnUrl, [], false, false, true); - $response = new Http\RedirectResponse($ssoUrl); + + $settings = $this->samlSettings->get($idp); + $isSAMLRequestUsingPost = isset($settings['general-is_saml_request_using_post']) && $settings['general-is_saml_request_using_post'] === '1'; + if ($isSAMLRequestUsingPost) { + $query = parse_url($ssoUrl, PHP_URL_QUERY); + parse_str($query, $params); + + $samlRequest = $params['SAMLRequest']; + $relayState = $params['RelayState'] ?? ''; + $sigAlg = $params['SigAlg'] ?? ''; + $signature = $params['Signature'] ?? ''; + $ssoUrl = explode('?', $ssoUrl)[0]; + + $nonce = base64_encode(random_bytes(16)); + + $response = new Http\TemplateResponse($this->appName, 'login_post', [ + 'ssoUrl' => $ssoUrl, + 'samlRequest' => $samlRequest, + 'relayState' => $relayState, + 'sigAlg' => $sigAlg, + 'signature' => $signature, + 'nonce' => $nonce, + ], 'guest'); + + $response->addHeader('Content-Security-Policy', "script-src 'self' 'nonce-$nonce' 'strict-dynamic' 'unsafe-eval';"); + } else { + $response = new Http\RedirectResponse($ssoUrl); + } // Small hack to make user_saml work with the loginflows $flowData = []; @@ -257,7 +287,7 @@ public function login(int $idp = 1): Http\RedirectResponse { } break; default: - throw new \Exception( + throw new Exception( sprintf( 'Type of "%s" is not supported for user_saml', $type @@ -283,7 +313,7 @@ public function getMetadata(int $idp = 1): Http\DataDownloadResponse { return new Http\DataDownloadResponse($metadata, 'metadata.xml', 'text/xml'); } else { throw new Error( - 'Invalid SP metadata: '.implode(', ', $errors), + 'Invalid SP metadata: ' . implode(', ', $errors), Error::METADATA_SP_INVALID ); } @@ -314,7 +344,7 @@ public function assertionConsumerService(): Http\RedirectResponse { // Decrypt and deserialize try { $cookie = $this->crypto->decrypt($cookie); - } catch (\Exception) { + } catch (Exception) { $this->logger->debug('Could not decrypt SAML cookie', ['app' => 'user_saml']); return new Http\RedirectResponse($this->urlGenerator->getAbsoluteURL('/')); } @@ -394,7 +424,7 @@ public function assertionConsumerService(): Http\RedirectResponse { } } catch (NoUserFoundException) { throw new \InvalidArgumentException('User "' . $this->userBackend->getCurrentUserId() . '" is not valid'); - } catch (\Exception $e) { + } catch (Exception $e) { $this->logger->critical($e->getMessage(), ['exception' => $e, 'app' => $this->appName]); $response = new Http\RedirectResponse($this->urlGenerator->linkToRouteAbsolute('user_saml.SAML.notProvisioned')); $response->invalidateCookie('saml_data'); @@ -426,7 +456,7 @@ public function assertionConsumerService(): Http\RedirectResponse { */ public function singleLogoutService(): Http\RedirectResponse { $isFromGS = ($this->config->getSystemValue('gs.enabled', false) && - $this->config->getSystemValue('gss.mode', '') === 'master'); + $this->config->getSystemValue('gss.mode', '') === 'master'); // Some IDPs send the SLO request via POST, but OneLogin php-saml only handles GET. // To hack around this issue we copy the request from _POST to _GET. @@ -439,7 +469,7 @@ public function singleLogoutService(): Http\RedirectResponse { if ($isFromIDP) { // requests comes from the IDP so let it manage the logout // (or raise Error if request is invalid) - $pass = true ; + $pass = true; } elseif ($isFromGS) { // Request is from master GlobalScale $jwt = $this->request->getParam('jwt', ''); @@ -450,7 +480,7 @@ public function singleLogoutService(): Http\RedirectResponse { $idp = $decoded['idp'] ?? null; $pass = true; - } catch (\Exception) { + } catch (Exception) { } } else { // standard request : need read CRSF check @@ -603,6 +633,11 @@ private function getIdps(string $redirectUrl): array { return $result; } + /** + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws \OCP\DB\Exception + */ private function getSSOUrl(string $redirectUrl, string $idp): string { $originalUrl = ''; if (!empty($redirectUrl)) { @@ -612,16 +647,19 @@ private function getSSOUrl(string $redirectUrl, string $idp): string { /** @var CsrfTokenManager $csrfTokenManager */ $csrfTokenManager = Server::get(CsrfTokenManager::class); $csrfToken = $csrfTokenManager->getToken(); - $ssoUrl = $this->urlGenerator->linkToRouteAbsolute( + + $settings = $this->samlSettings->get((int)$idp); + $method = $settings['general-is_saml_request_using_post'] ?? 'get'; + + return $this->urlGenerator->linkToRouteAbsolute( 'user_saml.SAML.login', [ 'requesttoken' => $csrfToken->getEncryptedValue(), 'originalUrl' => $originalUrl, - 'idp' => $idp + 'idp' => $idp, + 'method' => $method, ] ); - - return $ssoUrl; } /** diff --git a/lib/SAMLSettings.php b/lib/SAMLSettings.php index 7eb464be9..746d86fa7 100644 --- a/lib/SAMLSettings.php +++ b/lib/SAMLSettings.php @@ -26,6 +26,8 @@ class SAMLSettings { public const IDP_CONFIG_KEYS = [ 'general-idp0_display_name', 'general-uid_mapping', + 'general-is_saml_request_using_post', + 'general-saml_request_method', 'idp-entityId', 'idp-singleLogoutService.responseUrl', 'idp-singleLogoutService.url', diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 12e0079e8..324722b35 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -85,7 +85,7 @@ public function getForm() { 'text' => $this->l10n->t('Only allow authentication if an account exists on some other backend (e.g. LDAP).'), 'type' => 'checkbox', 'global' => true, - ] + ], ]; $attributeMappingSettings = [ 'displayName_mapping' => [ @@ -199,6 +199,11 @@ public function getForm() { 'type' => 'line', 'required' => false, ]; + $generalSettings['is_saml_request_using_post'] = [ + 'text' => $this->l10n->t('Use POST method for SAML request (default: GET)'), + 'type' => 'checkbox', + 'required' => false, + ]; $generalSettings['allow_multiple_user_back_ends'] = [ 'text' => $this->l10n->t('Allow the use of multiple user back-ends (e.g. LDAP)'), 'type' => 'checkbox', diff --git a/templates/login_post.php b/templates/login_post.php new file mode 100644 index 000000000..2d75185fe --- /dev/null +++ b/templates/login_post.php @@ -0,0 +1,34 @@ +t('Please wait while you are redirected to the SSO server.')); +?> + +
+ + + + + +
+ diff --git a/tests/integration/features/Shibboleth.feature b/tests/integration/features/Shibboleth.feature index 5503a5b37..58858ce43 100644 --- a/tests/integration/features/Shibboleth.feature +++ b/tests/integration/features/Shibboleth.feature @@ -1,5 +1,23 @@ Feature: Shibboleth + Scenario: Authenticating using Shibboleth with SAML and POST binding and no check if user exists on backend + Given The setting "type" is set to "saml" + And The setting "general-uid_mapping" is set to "urn:oid:0.9.2342.19200300.100.1.1" + And The setting "general-is_saml_request_using_post" is set to "1" + And The setting "idp-entityId" is set to "https://shibboleth-integration-nextcloud.localdomain/idp/shibboleth" + And The setting "idp-singleSignOnService.url" is set to "https://localhost:4443/idp/profile/SAML2/Redirect/SSO" + And The setting "idp-x509cert" is set to "MIIDnTCCAoWgAwIBAgIUGPx9uPjCu7c172IUgV84Tm94pBcwDQYJKoZIhvcNAQEL BQAwNzE1MDMGA1UEAwwsc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQu bG9jYWxkb21haW4wHhcNMTcwMTA0MTAxMTI3WhcNMzcwMTA0MTAxMTI3WjA3MTUw MwYDVQQDDCxzaGliYm9sZXRoLWludGVncmF0aW9uLW5leHRjbG91ZC5sb2NhbGRv bWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKH+bPu45tk8/JRk XOxkyfbxocWZlY4mRumEUxscd3fn0oVzOrdWbHH7lCZV4bus4KxvJljc0Nm2K+Zr LoiRUUnf/LQ4LlehWVm5Kbc4kRgOXS0iGZN3SslAWPKyIg0tywg+TLOBPoS6EtST 1WuYg1JPMFxPfeFDWQ0dQYPlXIJWBFh6F2JMTb0FLECqA5l/ryYE13QisX5l+Mqo 6y3Dh7qIgaH0IJNobXoAcEWza7Kb2RnfhZRh9e0qjZIwBqTJUFM/6I86RYXn829s INUvYQQbez6VkGTdUQJ/GuXb/dD5sMQfOyK8hrRY5MozOmK32cz3JaAzSXpiSRS9 NxFwvicCAwEAAaOBoDCBnTAdBgNVHQ4EFgQUKn8+TV0WXSDeavvF0M8mWn1o8ukw fAYDVR0RBHUwc4Isc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xvdWQubG9j YWxkb21haW6GQ2h0dHBzOi8vc2hpYmJvbGV0aC1pbnRlZ3JhdGlvbi1uZXh0Y2xv dWQubG9jYWxkb21haW4vaWRwL3NoaWJib2xldGgwDQYJKoZIhvcNAQELBQADggEB ABI6uzoIeLZT9Az2KTlLxIc6jZ4MDmhaVja4ZuBxTXEb7BFLfeASEJmQm1wgIMOn pJId3Kh3njW+3tOBWKm7lj8JxVVpAu4yMFSoQGPaVUgYB1AVm+pmAyPLzfJ/XGhf esCU2F/b0eHWcaIb3x+BZFX089Cd/PBtP84MNXdo+TccibxC8N39sr45qJM/7SC7 TfDYU0L4q2WZHJr4S7+0F+F4KaxLx9NzCvN4h6XaoWofZWir2iHO4NzbrVQGC0ei QybS/neBfni4A2g1lyzCb6xFB58JBvNCn7AAnDJULOE7S5XWUKsDAQVQrxTNkUq7 pnhlCQqZDwUdgmIXd1KB1So=" + And The setting "security-authnRequestsSigned" is set to "1" + And The setting "security-wantAssertionsEncrypted" is set to "1" + And The setting "sp-x509cert" is set to "-----BEGIN CERTIFICATE-----MIIC+zCCAeOgAwIBAgIJAIgZuvWDBIrdMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNzAxMDQxMTM5MjFaFw0yNzAxMDIxMTM5MjFaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3ESWaDH1JiJTy9yRJQV7kahPOxgBkIH2xwcYDL1k9deKNhSKLx7aGfxE244+HBcC6WLHKVUnOm0ld2qxQ4bMYiJXzZuqL67r07L5wxGAssv12lO92qohGmlHy3+VzRYUBmovu6upqOv3R2F8HBbo7Jc7Hvt7hOEJn/jPuFuF/fHit3mqU8l6IkrIZjpaW8T9fIWOXRq98U4+hkgWpqEZWsqlfE8BxAs9DeIMZab0GxO9stHLp+GYKx10uE4ezFcaDS8W+g2C8enCTt1HXGvcnj4o5zkC1lITGvcFTsiFqfIWyXeSufcxdc0W7HoG6J3ks0WJyK38sfFn0t2Ao6kX0CAwEAAaNQME4wHQYDVR0OBBYEFAoJzX6TVYAwC1GSPe6nObBG54zaMB8GA1UdIwQYMBaAFAoJzX6TVYAwC1GSPe6nObBG54zaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJia9R70uXdUZtgujUPjLas4+sVajzlBFmqhBqpLAo934vljf9HISsHrPtdBcbS0d0rucqXwabDf0MlR18ksnT/NYpsTwMbMx76CrXi4zYEEW5lISKEO65aIkzVTcqKWSuhjtSnRdB6iOLsFiKmNMWXaIKMR5T0+AbR9wdQgn08W+3EEeHGvafVQfE3STVsSgNb1ft7DvcSUnfPXGU7KzvmTpZa0Hfmc7uY4vpdEEhLAdRhgLReS7USZskov7ooiPSoD+JRFi2gM4klBxTemHdNUa9oFnHMXuYKOkLbkgFvHxyy+QlLq2ELQTga5e7I83ZyOfGctyf8Ul6vGw10vbQ=-----END CERTIFICATE-----" + And The setting "sp-privateKey" is set to "-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA3cRJZoMfUmIlPL3JElBXuRqE87GAGQgfbHBxgMvWT114o2FIovHtoZ/ETbjj4cFwLpYscpVSc6bSV3arFDhsxiIlfNm6ovruvTsvnDEYCyy/XaU73aqiEaaUfLf5XNFhQGai+7q6mo6/dHYXwcFujslzse+3uE4Qmf+M+4W4X98eK3eapTyXoiSshmOlpbxP18hY5dGr3xTj6GSBamoRlayqV8TwHECz0N4gxlpvQbE72y0cun4ZgrHXS4Th7MVxoNLxb6DYLx6cJO3Udca9yePijnOQLWUhMa9wVOyIWp8hbJd5K59zF1zRbsegboneSzRYnIrfyx8WfS3YCjqRfQIDAQABAoIBAQC5CQAdcqZ9vLpJNilBCJxJLCFmm+HAAREHD8MErg9A5UK1P4S1wJp/0qieGPi68wXBOTgY2xKSwMycgb04/+NyZidVRu388takOW/+KNBg8pMxdZ6/05GqnI0kivSbR3CXpYuz8hekwhpo9+fWmKjApsHL47ItK6WaeKmPbAFsq1YJGzfp/DXg7LIvh9GA3C1LWWGV7SuCGOyX/2Moi8xRa7qBtH4hDo/0NRhTx7zjYjlBgNEr330pJUopc3+AtHE40R+xMr2zkGvq9RsCZxYxD2VWbLwQW0yNjWmQ2OTuMgJJvk2+N73QLHcB+tea82ZTszsNzRS9DLtc6qbsKEPZAoGBAO78U3vEuRyY56f/1hpo0xuCDwOkWGzgBQWkjJl6dlyVz/zKkhXBHpEYImyt8XRN0W3iGZYpZ2hCFJGTcDp32R6UiEyGLz0Uc8R/tva/TiRVW1FdNczzSHcB24b9OMK4vE9JLs8mA8Rp8YBgtLr5DDuMfYt/a/rZJbg/HIfIN98nAoGBAO2OInCX93t2I6zzRPIqKtI6q6FYNp64VIQjvw9Y8l0x3IdJZRP9H5C8ZhCeYPsgEqTXcXa4j5hL4rQzoUtxfxflBUUH60bcnd4LGaTCMYLS14G011E3GZlIP0sJi5OjEhy8fq3zt6jVzS9V/lPHB8i+w1D7CbPrMpW7B3k32vC7AoGAX/HvdkYhZyjAAEOG6m1hK68IZhbp5TP+8CgCxm9S65K9wKh3A8LXibrdvzIKOP4w8WOPkCipOkMlTNibeu24vj01hztr5aK7Y40+oEtnjNCz67N3MQQO+LBHOSeaTRqrh01DPKjvZECAU2D/zfzEe3fIw2Nxr3DUYub7hkvMmosCgYAzxbVVypjiLGYsDDyrdmsstCKxoDMPNmcdAVljc+QmUXaZeXJw/8qAVb78wjeqo1vM1zNgR2rsKyW2VkZB1fN39q7GU6qAIBa7zLmDAduegmr7VrlSduq6UFeS9/qWa4TIBICrUqFlR2tXdKtgANF+e6y/mmaL8qdsoH1JetXZfwKBgQC1vscRpdAXivjOOZAh+mzJWzS4BUl4CTJLYYIuOEXikmN5g0EdV2fhUEdkewmyKnXHsd0x83167bYgpTDNs71jUxDHy5NXlg2qIjLkf09X9wr19gBzDApfWzfh3vUqttyMZuQMLVNepGCWM2vjlY9KGl5OvZqY6d+7yO0mLV9GmQ==-----END RSA PRIVATE KEY-----" + And The setting "security-wantAssertionsSigned" is set to "1" + When I send a GET request to "http://localhost:8080/index.php/login" + Then I should be redirected to "http://localhost:8080/index.php/apps/user_saml/saml/login" + And The response should contain the form with action "https://localhost:4443/idp/profile/SAML2/Redirect/SSO" + And The form method should be POST + And The form should contain input fields "SAMLRequest,RelayState,SigAlg,Signature" + Scenario: Authenticating using Shibboleth with SAML and no check if user exists on backend Given The setting "type" is set to "saml" And The setting "general-uid_mapping" is set to "urn:oid:0.9.2342.19200300.100.1.1" diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 51f6c8727..c625215af 100644 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -7,6 +7,8 @@ use Behat\Behat\Context\Context; use Behat\Gherkin\Node\TableNode; use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Exception\GuzzleException; +use GuzzleHttp\Exception\RequestException; class FeatureContext implements Context { /** @var \GuzzleHttp\Message\Response */ @@ -617,4 +619,97 @@ public function iExpectNoBackgroundJobForClassOCAUser_SAMLJobsMigrateGroups(stri throw new UnexpectedValueException('Background job axctuaslly was enqueued!'); } } + + /** + * @Then The form method should be POST + */ + public function theFormMethodShouldBePost() { + $responseBody = (string) $this->response->getBody(); + $domDocument = new DOMDocument(); + @$domDocument->loadHTML($responseBody); + $xpath = new DOMXpath($domDocument); + $formElements = $xpath->query("//form[@method='post' or @method='POST']"); + if ($formElements->length === 0) { + throw new \Exception("Expected form method 'POST' not found in response"); + } + } + + /** + * @Then The response should contain the form with action :action + */ + public function theResponseShouldContainTheFormWithAction($action) { + $responseBody = (string) $this->response->getBody(); + if (strpos($responseBody, 'action="' . $action . '"') === false) { + throw new \Exception("Expected form action '$action' not found in response"); + } + } + + /** + * @Then The form should contain input fields :fields + */ + public function theFormShouldContainInputFields($fields) { + $responseBody = (string) $this->response->getBody(); + $domDocument = new DOMDocument(); + @$domDocument->loadHTML($responseBody); + $xpath = new DOMXpath($domDocument); + $fieldsArray = explode(',', $fields); + foreach ($fieldsArray as $field) { + $inputElements = $xpath->query("//input[@name='" . trim($field) . "']"); + if ($inputElements->length === 0) { + throw new \Exception("Expected input field '$field' not found in response"); + } + } + } + + /** + * @When I submit the SAML form + */ + public function iSubmitTheSAMLForm() { + $responseBody = (string) $this->response->getBody(); + $domDocument = new DOMDocument(); + @$domDocument->loadHTML($responseBody); + $xpath = new DOMXpath($domDocument); + + // Find the form action + $formAction = $xpath->query("//form")->item(0)->getAttribute('action'); + + // Get the specified hidden input fields + $fields = ['SAMLRequest', 'RelayState', 'SigAlg', 'Signature']; + $postData = []; + foreach ($fields as $field) { + $inputElement = $xpath->query("//input[@type='hidden' and @name='" . $field . "']"); + if ($inputElement->length === 0) { + throw new \Exception("Expected hidden input field '$field' not found in response"); + } + $postData[$field] = $inputElement->item(0)->getAttribute('value'); + } + + // Send the POST request with the hidden input data + try { + $this->response = $this->client->request( + 'POST', + $formAction, + [ + 'form_params' => $postData, + 'headers' => [ + 'Content-Type' => 'application/x-www-form-urlencoded', + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36', + ], + 'cookies' => $this->cookieJar, + ] + ); + } catch (RequestException $e) { + echo "RequestException: " . $e->getMessage() . "\n"; + if ($e->hasResponse()) { + $response = $e->getResponse(); + echo "Status Code: " . $response->getStatusCode() . "\n"; + echo "Headers: " . json_encode($response->getHeaders()) . "\n"; + echo "Body: " . $response->getBody() . "\n"; + echo "Request Headers: " . json_encode($e->getRequest()->getHeaders()) . "\n"; + echo "Request Body: " . $e->getRequest()->getBody() . "\n"; + } + } catch (GuzzleException $e) { + echo "GuzzleException: " . $e->getMessage() . "\n"; + } + } } diff --git a/tests/unit/Settings/AdminTest.php b/tests/unit/Settings/AdminTest.php index 2fbdd61fc..ae0136855 100644 --- a/tests/unit/Settings/AdminTest.php +++ b/tests/unit/Settings/AdminTest.php @@ -84,6 +84,11 @@ public function formDataProvider() { 'type' => 'line', 'required' => false, ], + 'is_saml_request_using_post' => [ + 'text' => $this->l10n->t('Use POST method for SAML request (default: GET)'), + 'type' => 'checkbox', + 'required' => false, + ], 'uid_mapping' => [ 'text' => 'Attribute to map the UID to.', 'type' => 'line', @@ -226,8 +231,7 @@ public function testGetFormWithoutType() { ->willReturn(''); $params = $this->formDataProvider(); - unset($params['general']['idp0_display_name']); - unset($params['general']['allow_multiple_user_back_ends']); + unset($params['general']['idp0_display_name'], $params['general']['is_saml_request_using_post'], $params['general']['allow_multiple_user_back_ends']); $params['type'] = ''; $expected = new TemplateResponse('user_saml', 'admin', $params);