| 
<?php
 /**
 * Logout endpoint handler for SAML SP authentication client.
 *
 * This endpoint handles both logout requests and logout responses.
 */
 
 if (!array_key_exists('PATH_INFO', $_SERVER)) {
 throw new SimpleSAML_Error_BadRequest('Missing authentication source id in logout URL');
 }
 
 $sourceId = substr($_SERVER['PATH_INFO'], 1);
 
 $source = SimpleSAML_Auth_Source::getById($sourceId);
 if ($source === NULL) {
 throw new Exception('Could not find authentication source with id ' . $sourceId);
 }
 if (!($source instanceof sspmod_saml_Auth_Source_SP)) {
 throw new SimpleSAML_Error_Exception('Source type changed?');
 }
 
 $binding = SAML2_Binding::getCurrentBinding();
 $message = $binding->receive();
 
 $idpEntityId = $message->getIssuer();
 if ($idpEntityId === NULL) {
 /* Without an issuer we have no way to respond to the message. */
 throw new SimpleSAML_Error_BadRequest('Received message on logout endpoint without issuer.');
 }
 
 $spEntityId = $source->getEntityId();
 
 $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
 $idpMetadata = $source->getIdPMetadata($idpEntityId);
 $spMetadata = $source->getMetadata();
 
 sspmod_saml_Message::validateMessage($idpMetadata, $spMetadata, $message);
 
 $destination = $message->getDestination();
 if ($destination !== NULL && $destination !== SimpleSAML_Utilities::selfURLNoQuery()) {
 throw new SimpleSAML_Error_Exception('Destination in logout message is wrong.');
 }
 
 if ($message instanceof SAML2_LogoutResponse) {
 
 $relayState = $message->getRelayState();
 if ($relayState === NULL) {
 /* Somehow, our RelayState has been lost. */
 throw new SimpleSAML_Error_BadRequest('Missing RelayState in logout response.');
 }
 
 if (!$message->isSuccess()) {
 SimpleSAML_Logger::warning('Unsuccessful logout. Status was: ' . sspmod_saml_Message::getResponseError($message));
 }
 
 // sanitize the input
 $sid = SimpleSAML_Utilities::parseStateID($relayState);
 if (!is_null($sid['url'])) {
 SimpleSAML_Utilities::checkURLAllowed($sid['url']);
 }
 
 $state = SimpleSAML_Auth_State::loadState($relayState, 'saml:slosent');
 $state['saml:sp:LogoutStatus'] = $message->getStatus();
 SimpleSAML_Auth_Source::completeLogout($state);
 
 } elseif ($message instanceof SAML2_LogoutRequest) {
 
 SimpleSAML_Logger::debug('module/saml2/sp/logout: Request from ' . $idpEntityId);
 SimpleSAML_Logger::stats('saml20-idp-SLO idpinit ' . $spEntityId . ' ' . $idpEntityId);
 
 if ($message->isNameIdEncrypted()) {
 try {
 $keys = sspmod_saml_Message::getDecryptionKeys($idpMetadata, $spMetadata);
 } catch (Exception $e) {
 throw new SimpleSAML_Error_Exception('Error decrypting NameID: ' . $e->getMessage());
 }
 
 $blacklist = sspmod_saml_Message::getBlacklistedAlgorithms($idpMetadata, $spMetadata);
 
 $lastException = NULL;
 foreach ($keys as $i => $key) {
 try {
 $message->decryptNameId($key, $blacklist);
 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' succeeded.');
 $lastException = NULL;
 break;
 } catch (Exception $e) {
 SimpleSAML_Logger::debug('Decryption with key #' . $i . ' failed with exception: ' . $e->getMessage());
 $lastException = $e;
 }
 }
 if ($lastException !== NULL) {
 throw $lastException;
 }
 }
 
 $nameId = $message->getNameId();
 $sessionIndexes = $message->getSessionIndexes();
 
 $numLoggedOut = sspmod_saml_SP_LogoutStore::logoutSessions($sourceId, $nameId, $sessionIndexes);
 if ($numLoggedOut === FALSE) {
 /* This type of logout was unsupported. Use the old method. */
 $source->handleLogout($idpEntityId);
 $numLoggedOut = count($sessionIndexes);
 }
 
 /* Create an send response. */
 $lr = sspmod_saml_Message::buildLogoutResponse($spMetadata, $idpMetadata);
 $lr->setRelayState($message->getRelayState());
 $lr->setInResponseTo($message->getId());
 
 if ($numLoggedOut < count($sessionIndexes)) {
 SimpleSAML_Logger::warning('Logged out of ' . $numLoggedOut  . ' of ' . count($sessionIndexes) . ' sessions.');
 }
 
 $dst = $idpMetadata->getEndpointPrioritizedByBinding('SingleLogoutService', array(
 SAML2_Const::BINDING_HTTP_REDIRECT,
 SAML2_Const::BINDING_HTTP_POST)
 );
 
 if (!$binding instanceof SAML2_SOAP) {
 $binding = SAML2_Binding::getBinding($dst['Binding']);
 if (isset($dst['ResponseLocation'])) {
 $dst = $dst['ResponseLocation'];
 } else {
 $dst = $dst['Location'];
 }
 $binding->setDestination($dst);
 }
 $lr->setDestination($dst);
 
 $binding->send($lr);
 } else {
 throw new SimpleSAML_Error_BadRequest('Unknown message received on logout endpoint: ' . get_class($message));
 }
 
 |