diff --git a/lib/SimpleSAML/IdP/LogoutTraditional.php b/lib/SimpleSAML/IdP/LogoutTraditional.php index 202db7b33a64ec970f06d2e3857c9c75e96b45de..5a4846608ca34a8ce995b3b0945d486b34c9bfdf 100644 --- a/lib/SimpleSAML/IdP/LogoutTraditional.php +++ b/lib/SimpleSAML/IdP/LogoutTraditional.php @@ -29,7 +29,8 @@ class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler { try { $idp = SimpleSAML_IdP::getByState($association); - call_user_func(array($association['Handler'], 'sendLogoutRequest'), $idp, $association, $relayState); + $url = call_user_func(array($association['Handler'], 'getLogoutURL'), $idp, $association, $relayState); + SimpleSAML_Utilities::redirect($url); } catch (Exception $e) { SimpleSAML_Logger::warning('Unable to initialize logout to ' . var_export($id, TRUE) . '.'); $this->idp->terminateAssociation($id); diff --git a/modules/core/www/idp/logout-iframe-post.php b/modules/core/www/idp/logout-iframe-post.php new file mode 100644 index 0000000000000000000000000000000000000000..061ef97506fe0b79fb34227ac4befb0e0219ed45 --- /dev/null +++ b/modules/core/www/idp/logout-iframe-post.php @@ -0,0 +1,59 @@ +<?php + +if (!isset($_REQUEST['idp'])) { + throw new SimpleSAML_Error_BadRequest('Missing "idp" parameter.'); +} +$idp = (string)$_REQUEST['idp']; +$idp = SimpleSAML_IdP::getById($idp); + +if (!isset($_REQUEST['association'])) { + throw new SimpleSAML_Error_BadRequest('Missing "association" parameter.'); +} +$assocId = urldecode($_REQUEST['association']); + +$relayState = NULL; +if (isset($_REQUEST['RelayState'])) { + $relayState = (string)$_REQUEST['RelayState']; +} + +$associations = $idp->getAssociations(); +if (!isset($associations[$assocId])) { + throw new SimpleSAML_Error_BadRequest('Invalid association id.'); +} +$association = $associations[$assocId]; + +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); +$idpMetadata = $idp->getConfig(); +$spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); + +$lr = sspmod_saml_Message::buildLogoutRequest($idpMetadata, $spMetadata); +$lr->setSessionIndex($association['saml:SessionIndex']); +$lr->setNameId($association['saml:NameID']); + +$assertionLifetime = $spMetadata->getInteger('assertion.lifetime', NULL); +if ($assertionLifetime === NULL) { + $assertionLifetime = $idpMetadata->getInteger('assertion.lifetime', 300); +} +$lr->setNotOnOrAfter(time() + $assertionLifetime); + +$encryptNameId = $spMetadata->getBoolean('nameid.encryption', NULL); +if ($encryptNameId === NULL) { + $encryptNameId = $idpMetadata->getBoolean('nameid.encryption', FALSE); +} +if ($encryptNameId) { + $lr->encryptNameId(sspmod_saml_Message::getEncryptionKey($spMetadata)); +} + +SimpleSAML_Stats::log('saml:idp:LogoutRequest:sent', array( + 'spEntityID' => $association['saml:entityID'], + 'idpEntityID' => $idpMetadata->getString('entityid'), +)); + +$bindings = array(SAML2_Const::BINDING_HTTP_POST); + +$dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', $bindings); +$binding = SAML2_Binding::getBinding($dst['Binding']); +$lr->setDestination($dst['Location']); +$lr->setRelayState($relayState); + +$binding->send($lr); diff --git a/modules/core/www/idp/logout-iframe.php b/modules/core/www/idp/logout-iframe.php index 9d77bebe8ae8589b50060c39b6da5cb22144bdc6..1b751e931b2ba112e6ae6dacad25c6297b5c3f5a 100644 --- a/modules/core/www/idp/logout-iframe.php +++ b/modules/core/www/idp/logout-iframe.php @@ -83,8 +83,7 @@ if ($type === 'js' || $type === 'nojs') { try { $assocIdP = SimpleSAML_IdP::getByState($sp); - $params = array('id' => $id, 'sp' => $sp['id'], 'type' => $type); - $url = SimpleSAML_Module::getModuleURL('core/idp/performlogout.php', $params); + $url = call_user_func(array($sp['Handler'], 'getLogoutURL'), $assocIdP, $sp, NULL); $sp['core:Logout-IFrame:URL'] = $url; } catch (Exception $e) { $sp['core:Logout-IFrame:State'] = 'failed'; diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php index bc5613fd9d0be752261b87840b883045ab13fb92..7fe20d936b4882cc7afcbccb362cc82fc60a3de3 100644 --- a/modules/saml/lib/IdP/SAML2.php +++ b/modules/saml/lib/IdP/SAML2.php @@ -546,8 +546,7 @@ class sspmod_saml_IdP_SAML2 { /** - * Retrieve a logout URL for a given logout association. Only for logout endpoints that support - * HTTP-Redirect binding. + * Retrieve a logout URL for a given logout association. * * @param SimpleSAML_IdP $idp The IdP we are sending a logout request from. * @param array $association The association that should be terminated. @@ -562,9 +561,18 @@ class sspmod_saml_IdP_SAML2 { $idpMetadata = $idp->getConfig(); $spMetadata = $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote'); - // It doesn't make sense to use HTTP-Post when asking for a logout URL, therefore we allow only - // HTTP-Redirect. - $dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', array(SAML2_Const::BINDING_HTTP_REDIRECT)); + $bindings = array(SAML2_Const::BINDING_HTTP_REDIRECT, + SAML2_Const::BINDING_HTTP_POST); + $dst = $spMetadata->getDefaultEndpoint('SingleLogoutService', $bindings); + + if ($dst['Binding'] === SAML2_Const::BINDING_HTTP_POST) { + $params = array('association' => $association['id'], 'idp' => $idp->getId()); + if ($relayState !== NULL) { + $params['RelayState'] = $relayState; + } + return SimpleSAML_Module::getModuleURL('core/idp/logout-iframe-post.php', $params); + } + $lr = self::buildLogoutRequest($idpMetadata, $spMetadata, $association, $relayState); $lr->setDestination($dst['Location']);