Skip to content
Snippets Groups Projects
Commit 0a1f8bf3 authored by Olav Morken's avatar Olav Morken
Browse files

IdP core: Add logout processing.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2139 44740490-163a-0410-bde0-09ae8108e29a
parent 1b73030b
No related branches found
No related tags found
No related merge requests found
......@@ -79,5 +79,11 @@
},
"no": {
"en": "No"
},
"logging_out_from": {
"en": "Logging out of the following services:"
},
"failedsps": {
"en": "Unable to log out of one or more services. To ensure that all your sessions are closed, you are encouraged to <i>close your webbrowser<\/i>."
}
}
......@@ -453,5 +453,13 @@
"pt": "N\u00e3o",
"pl": "Nie",
"tr": "Hay\u0131r"
},
"logging_out_from": {
"sl": "Odjava iz naslednjih storitev:",
"da": "Du logger ud af f\u00f8lgende services:"
},
"failedsps": {
"sl": "Odjava z ene ali ve\u010d storitev ni uspela. Odjavo dokon\u010dajte tako, da <i>zaprete spletni brskalnik<\/i>.",
"da": "Kan ikke logge ud af en eller flere services. For at sikre at alle dine sessioner er lukket <i>skal du lukke din browser<\/i>."
}
}
......@@ -107,6 +107,84 @@ class SimpleSAML_IdP {
}
/**
* Get SP name.
*
* @param string $assocId The association identifier.
* @return array|NULL The name of the SP, as an associative array of language=>text, or NULL if this isn't an SP.
*/
public function getSPName($assocId) {
assert('is_string($assocId)');
if (substr($assocId, 0, 5) !== 'saml:') {
return NULL;
}
$spEntityId = substr($assocId, 5);
$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
try {
$spMetadata = $metadata->getMetaDataConfig($spEntityId, 'saml20-sp-remote');
} catch (Exception $e) {
try {
$spMetadata = $metadata->getMetaDataConfig($spEntityId, 'shib13-sp-remote');
} catch (Exception $e) {
return NULL;
}
}
return $spMetadata->getLocalizedString('name', array('en' => $spEntityId));
}
/**
* Retrieve list of SP associations.
*
* @return array List of SP associations.
*/
public function getAssociations() {
$session = SimpleSAML_Session::getInstance();
$associations = array();
foreach ($session->get_sp_list() as $spEntityId) {
$nameId = $session->getSessionNameId('saml20-sp-remote', $spEntityId);
if($nameId === NULL) {
$nameId = $this->getNameID();
}
$id = 'saml:' . $spEntityId;
$associations[$id] = array(
'id' => $id,
'Handler' => 'sspmod_saml_IdP_SAML2',
'saml:entityID' => $spEntityId,
'saml:NameID' => $nameId,
'saml:SessionIndex' => $session->getSessionIndex(),
);
}
return $associations;
}
/**
* Remove an SP association.
*
* @param string $assocId The association id.
*/
public function terminateAssociation($assocId) {
assert('is_string($assocId)');
$session = SimpleSAML_Session::getInstance();
if (substr($assocId, 0, 5) === 'saml:') {
$session->set_sp_logout_completed(substr($assocId, 5));
}
}
/**
* Is the current user authenticated?
*
......@@ -279,4 +357,149 @@ class SimpleSAML_IdP {
}
}
/**
* Find the logout handler of this IdP.
*
* @return string The logout handler class.
*/
public function getLogoutHandler() {
/* Find the logout handler. */
$logouttype = $this->getConfig()->getString('logouttype', 'traditional');
switch ($logouttype) {
case 'traditional':
$handler = 'SimpleSAML_IdP_LogoutTraditional';
break;
case 'iframe':
$handler = 'SimpleSAML_IdP_LogoutIFrame';
break;
default:
throw new SimpleSAML_Error_Exception('Unknown logout handler: ' . var_export($logouttype, TRUE));
}
return new $handler($this);
}
/**
* Finish the logout operation.
*
* This function will never return.
*
* @param array &$state The logout request state.
*/
public function finishLogout(array &$state) {
assert('isset($state["Responder"])');
call_user_func($state['Responder'], $this, $state);
assert('FALSE');
}
/**
* Process a logout request.
*
* This function will never return.
*
* @param array &$state The logout request state.
* @param string|NULL $assocId The association we received the logout request from, or NULL if there was no association.
*/
public function handleLogoutRequest(array &$state, $assocId) {
assert('isset($state["Responder"])');
assert('is_string($assocId) || is_null($assocId)');
$state['core:IdP'] = $this->id;
$state['core:TerminatedAssocId'] = $assocId;
if ($assocId !== NULL) {
$this->terminateAssociation($assocId);
}
/* Terminate the local session. */
$session = SimpleSAML_Session::getInstance();
$authority = $session->getAuthority();
if ($authority !== NULL) {
/* We are logged in. */
$id = SimpleSAML_Auth_State::saveState($state, 'core:Logout:afterbridge');
$returnTo = SimpleSAML_Module::getModuleURL('core/idp/resumelogout.php',
array('id' => $id)
);
if ($authority === $this->config->getString('auth')) {
/* This is probably an authentication source. */
SimpleSAML_Auth_Default::initLogoutReturn($returnTo);
} elseif ($authority === 'saml2') {
/* SAML 2 SP which isn't an authentication source. */
SimpleSAML_Utilities::redirect('/' . $config->getBaseURL() . 'saml2/sp/initSLO.php',
array('RelayState' => $returnTo)
);
} else {
/* A different old-style authentication file. */
$session->doLogout();
}
}
$handler = $this->getLogoutHandler();
$handler->startLogout($state, $assocId);
assert('FALSE');
}
/**
* Process a logout response.
*
* This function will never return.
*
* @param string $assocId The association that is terminated.
* @param string|NULL $relayState The RelayState from the start of the logout.
* @param SimpleSAML_Error_Exception|NULL $error The error that occured during session termination (if any).
*/
public function handleLogoutResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
assert('is_string($assocId)');
assert('is_string($relayState) || is_null($relayState)');
$handler = $this->getLogoutHandler();
$handler->onResponse($assocId, $relayState, $error);
assert('FALSE');
}
/**
* Log out, then redirect to an URL.
*
* This function never returns.
*
* @param string $url The URL the user should be returned to after logout.
*/
public function doLogoutRedirect($url) {
assert('is_string($url)');
$state = array(
'Responder' => array('SimpleSAML_IdP', 'finishLogoutRedirect'),
'core:Logout:URL' => $url,
);
$this->handleLogoutRequest($state, NULL);
assert('FALSE');
}
/**
* Redirect to an URL after logout.
*
* This function never returns.
*
* @param array &$state The logout state from doLogoutRedirect().
*/
public static function finishLogoutRedirect(SimpleSAML_IdP $idp, array $state) {
assert('isset($state["core:Logout:URL"])');
SimpleSAML_Utilities::redirect($state['core:Logout:URL']);
assert('FALSE');
}
}
<?php
/**
* Base class for logout handlers.
*
* @package simpleSAMLphp
* @version $Id$
*/
abstract class SimpleSAML_IdP_LogoutHandler {
/**
* The IdP we are logging out from.
*
* @var SimpleSAML_IdP
*/
protected $idp;
/**
* Initialize this logout handler.
*
* @param SimpleSAML_IdP $idp The IdP we are logging out from.
*/
public function __construct(SimpleSAML_IdP $idp) {
$this->idp = $idp;
}
/**
* Start a logout operation.
*
* This function must never return.
*
* @param array &$state The logout state.
* @param string|NULL $assocId The association that started the logout.
*/
abstract public function startLogout(array &$state, $assocId);
/**
* Handles responses to our logout requests.
*
* This function will never return.
*
* @param string $assocId The association that is terminated.
* @param string|NULL $relayState The RelayState from the start of the logout.
* @param SimpleSAML_Error_Exception|NULL $error The error that occured during session termination (if any).
*/
public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
assert('is_string($assocId)');
assert('is_string($relayState) || is_null($relayState)');
/* Don't do anything by default. */
}
}
<?php
/**
* Class which handles iframe logout.
*
* @package simpleSAMLphp
* @version $Id$
*/
class SimpleSAML_IdP_LogoutIFrame extends SimpleSAML_IdP_LogoutHandler {
/**
* Start the logout operation.
*
* @param array &$state The logout state.
* @param string|NULL $assocId The SP we are logging out from.
*/
public function startLogout(array &$state, $assocId) {
assert('is_string($assocId) || is_null($assocId)');
$associations = $this->idp->getAssociations();
if (count($associations) === 0) {
$this->idp->finishLogout($state);
}
foreach ($associations as $id => &$association) {
$association['core:Logout-IFrame:Name'] = $this->idp->getSPName($id);
$association['core:Logout-IFrame:State'] = 'onhold';
}
$state['core:Logout-IFrame:Associations'] = $associations;
if (!is_null($assocId)) {
$spName = $this->idp->getSPName($assocId);
if ($spName === NULL) {
$spName = array('en' => $assocId);
}
$state['core:Logout-IFrame:From'] = $spName;
} else {
$state['core:Logout-IFrame:From'] = NULL;
}
$id = SimpleSAML_Auth_State::saveState($state, 'core:Logout-IFrame');
$url = SimpleSAML_Module::getModuleURL('core/idp/logout-iframe.php', array('id' => $id));
SimpleSAML_Utilities::redirect($url);
}
/**
* Continue the logout operation.
*
* This function will never return.
*
* @param string $assocId The association that is terminated.
* @param string|NULL $relayState The RelayState from the start of the logout.
* @param SimpleSAML_Error_Exception|NULL $error The error that occured during session termination (if any).
*/
public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
assert('is_string($assocId)');
$spId = sha1($assocId);
$cookieId = 'logout-iframe-' . $spId;
$globalConfig = SimpleSAML_Configuration::getInstance();
$cookiePath = '/' . $globalConfig->getBaseURL();
setcookie($cookieId, ($error ? 'failed' : 'completed'), time() + 5*60, $cookiePath);
echo('<!DOCTYPE html>
<html>
<head>
<title>Logout response from ' . htmlspecialchars(var_export($assocId, TRUE)) . '</title>
<script>
');
if ($error) {
$errorMsg = $error->getMessage();
echo('window.parent.logoutFailed("' . $spId . '", "' . addslashes($errorMsg) . '");');
} else {
echo('window.parent.logoutCompleted("' . $spId . '");');
}
echo('
</script>
</head>
<body>
</body>
</html>
');
exit(0);
}
}
<?php
/**
* Class which handles traditional logout.
*
* @package simpleSAMLphp
* @version $Id$
*/
class SimpleSAML_IdP_LogoutTraditional extends SimpleSAML_IdP_LogoutHandler {
/**
* Picks the next SP and issues a logout request.
*
* This function never returns.
*
* @param array &$state The logout state.
*/
private function logoutNextSP(array &$state) {
$association = array_pop($state['core:LogoutTraditional:Remaining']);
if ($association === NULL) {
$this->idp->finishLogout($state);
}
$relayState = SimpleSAML_Auth_State::saveState($state, 'core:LogoutTraditional', TRUE);
$id = $association['id'];
SimpleSAML_Logger::info('Logging out of ' . var_export($id, TRUE) . '.');
try {
$url = call_user_func(array($association['Handler'], 'getLogoutURL'), $this->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);
$state['core:Failed'] = TRUE;
/* Try the next SP. */
$this->logoutNextSP($state);
assert('FALSE');
}
}
/**
* Start the logout operation.
*
* This function never returns.
*
* @param array &$state The logout state.
* @param string $assocId The association that started the logout.
*/
public function startLogout(array &$state, $assocId) {
$state['core:LogoutTraditional:Remaining'] = $this->idp->getAssociations();
self::logoutNextSP($state);
}
/**
* Continue the logout operation.
*
* This function will never return.
*
* @param string $assocId The association that is terminated.
* @param string|NULL $relayState The RelayState from the start of the logout.
* @param SimpleSAML_Error_Exception|NULL $error The error that occured during session termination (if any).
*/
public function onResponse($assocId, $relayState, SimpleSAML_Error_Exception $error = NULL) {
assert('is_string($assocId)');
assert('is_string($relayState) || is_null($relayState)');
if ($relayState === NULL) {
throw new SimpleSAML_Error_Exception('RelayState lost during logout.');
}
$state = SimpleSAML_Auth_State::loadState($relayState, 'core:LogoutTraditional');
if ($error === NULL) {
SimpleSAML_Logger::info('Logged out of ' . var_export($assocId, TRUE) . '.');
$this->idp->terminateAssociation($assocId);
} else {
SimpleSAML_Logger::warning('Error received from ' . var_export($assocId, TRUE) . ' during logout:');
$error->logWarning();
$state['core:Failed'] = TRUE;
}
self::logoutNextSP($state);
}
}
<?php
$id = $this->data['id'];
$SPs = $this->data['SPs'];
$timeout = $this->data['timeout'];
$iframeURL = 'logout-iframe.php?type=embed&id=' . urlencode($id) . '&timeout=' . (string)$timeout;
/* Pretty arbitrary height, but should have enough safety margins for most cases. */
$iframeHeight = 25 + count($SPs) * 4;
$this->data['header'] = $this->t('{logout:progress}');
$this->includeAtTemplateBase('includes/header.php');
echo '<iframe style="width:100%; height:' . $iframeHeight . 'em; border:0;" src="' . htmlspecialchars($iframeURL) . '"></iframe>';
foreach ($SPs AS $assocId => $sp) {
$spId = sha1($assocId);
if ($sp['core:Logout-IFrame:State'] !== 'inprogress') {
continue;
}
assert('isset($sp["core:Logout-IFrame:URL"])');
$url = $sp["core:Logout-IFrame:URL"];
echo('<iframe style="width:0; height:0; border:0;" src="' . htmlspecialchars($url) . '"></iframe>');
}
$this->includeAtTemplateBase('includes/footer.php');
<?php
$id = $this->data['id'];
$type = $this->data['type'];
$from = $this->data['from'];
$SPs = $this->data['SPs'];
$timeout = $this->data['timeout'];
$stateImage = array(
'unsupported' => '/' . $this->data['baseurlpath'] . 'resources/icons/silk/delete.png',
'completed' => '/' . $this->data['baseurlpath'] . 'resources/icons/silk/accept.png',
'onhold' => '/' . $this->data['baseurlpath'] . 'resources/icons/bullet16_grey.png',
'inprogress' => '/' . $this->data['baseurlpath'] . 'resources/progress.gif',
'failed' => '/' . $this->data['baseurlpath'] . 'resources/icons/silk/exclamation.png',
);
$stateText = array(
'unsupported' => '',
'completed' => $this->t('{logout:completed}'),
'onhold' => '',
'inprogress' => $this->t('{logout:progress}'),
'failed' => $this->t('{logout:failed}'),
);
$spStatus = array();
$nFailed = 0;
$nProgress = 0;
foreach ($SPs as $assocId => $sp) {
assert('isset($sp["core:Logout-IFrame:State"])');
$state = $sp['core:Logout-IFrame:State'];
$spStatus[sha1($assocId)] = $state;
if ($state === 'failed') {
$nFailed += 1;
} elseif ($state === 'inprogress') {
$nProgress += 1;
}
}
if ($from !== NULL) {
$from = $this->getTranslation($from);
}
if (!isset($this->data['head'])) {
$this->data['head'] = '';
}
$this->data['head'] .= '<script type="text/javascript" src="/' . $this->data['baseurlpath'] . 'resources/jquery.js"></script>';
$this->data['head'] .= '
<script type="text/javascript" language="JavaScript">
window.stateImage = ' . json_encode($stateImage) . ';
window.stateText = ' . json_encode($stateText) . ';
window.spStatus = ' . json_encode($spStatus) . ';
window.type = "' . $type . '";
window.timeoutIn = ' . (string)($timeout - time()) . ';
window.asyncURL = "logout-iframe.php?id=' . $id . '&type=async";
</script>';
$this->data['head'] .= '<script type="text/javascript" src="logout-iframe.js"></script>';
if ($type === 'embed') {
$this->data['head'] .= '<meta http-equiv="refresh" content="1" />';
}
$this->data['header'] = $this->t('{logout:progress}');
if ($type === 'embed') {
$this->includeAtTemplateBase('includes/header-embed.php');
} else {
$this->includeAtTemplateBase('includes/header.php');
}
if ($from !== NULL) {
echo('<div><img style="float: left; margin-right: 12px" src="/' . $this->data['baseurlpath'] . 'resources/icons/checkmark48.png" alt="Successful logout" />');
echo('<p style="padding-top: 16px; ">' . $this->t('{logout:loggedoutfrom}', array('%SP%' => '<strong>' .htmlspecialchars($from).'</strong>')) . '</p>');
echo('<p style="height: 0px; clear: left;"></p>');
echo('</div>');
}
echo('<div style="margin-top: 3em; clear: both">');
echo('<p style="margin-bottom: .5em">');
if ($type === 'init') {
echo($this->t('{logout:also_from}'));
} else {
echo($this->t('{logout:logging_out_from}'));
}
echo('</p>');
echo '<table id="slostatustable">';
foreach ($SPs AS $assocId => $sp) {
if (isset($sp['core:Logout-IFrame:Name'])) {
$spName = $this->getTranslation($sp['core:Logout-IFrame:Name']);
} else {
$spName = $assocId;
}
assert('isset($sp["core:Logout-IFrame:State"])');
$spState = $sp['core:Logout-IFrame:State'];
$spId = sha1($assocId);
echo '<tr>';
echo '<td>';
echo '<img class="logoutstatusimage" id="statusimage-' . $spId . '" src="' . htmlspecialchars($stateImage[$spState]) . '" alt="' . htmlspecialchars($stateText[$spState]) . '"/>';
echo '</td>';
echo '<td>' . htmlspecialchars($spName) . '</td>';
echo '</tr>';
}
if (isset($from)) {
$logoutCancelText = $this->t('{logout:logout_only}', array('%SP%' => htmlspecialchars($from)));
} else {
$logoutCancelText = $this->t('{logout:no}');
}
?>
</table>
</div>
<?php
if ($type === 'init') {
?>
<div id="confirmation" style="margin-top: 1em" >
<p><?php echo $this->t('{logout:logout_all_question}'); ?> <br /></p>
<form id="startform" method="get" action="logout-iframe.php">
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="hidden" id="logout-type-selector" name="type" value="nojs" />
<input type="submit" id="logout-all" name="ok" value="<?php echo $this->t('{logout:logout_all}'); ?>" />
</form>
<form method="get" action="logout-iframe-done.php">
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="submit" name="cancel" value="<?php echo $logoutCancelText; ?>" />
</form>
</div>
<?php
} else {
?>
<?php
if ($nFailed > 0) {
$displayStyle = '';
} else {
$displayStyle = 'display: none;';
}
echo('<div id="logout-failed-message" style="margin-top: 1em; border: 1px solid #ccc; padding: 1em; background: #eaeaea;' . $displayStyle . '">');
echo('<img src="/' . $this->data['baseurlpath'] . 'resources/icons/caution.png" alt="" style="float: left; margin-right: 5px;" />');
echo('<p>' . $this->t('{logout:failedsps}') . '</p>');
echo('<form method="get" action="logout-iframe-done.php" target="_top">');
echo('<input type="hidden" name="id" value="' . $id . '" />');
echo('<input type="submit" name="continue" value="' . $this->t('{logout:return}'). '" />');
echo('</form>');
echo('</div>');
if ($nProgress == 0 && $nFailed == 0) {
echo('<div id="logout-completed">');
} else {
echo('<div id="logout-completed" style="display:none;">');
}
echo('<p>' . $this->t('{logout:success}') . '</p>');
?>
<form method="get" action="logout-iframe-done.php" id="done-form" target="_top">
<input type="hidden" name="id" value="<?php echo $id; ?>" />
<input type="submit" name="continue" value="<?php echo $this->t('{logout:return}'); ?>" />
</form>
</div>
<?php
if ($type === 'js') {
foreach ($SPs AS $assocId => $sp) {
$spId = sha1($assocId);
if ($sp['core:Logout-IFrame:State'] !== 'inprogress') {
continue;
}
assert('isset($sp["core:Logout-IFrame:URL"])');
$url = $sp["core:Logout-IFrame:URL"];
echo('<iframe style="width:0; height:0; border:0;" src="' . htmlspecialchars($url) . '"></iframe>');
}
}
?>
<?php
}
?>
<?php
if ($type === 'embed') {
$this->includeAtTemplateBase('includes/footer-embed.php');
} else {
$this->includeAtTemplateBase('includes/footer.php');
}
<?php
if (!isset($_REQUEST['id'])) {
throw new SimpleSAML_Error_BadRequest('Missing required parameter: id');
}
$id = (string)$_REQUEST['id'];
$state = SimpleSAML_Auth_State::loadState($id, 'core:Logout-IFrame');
$idp = SimpleSAML_IdP::getByState($state);
$associations = $idp->getAssociations();
$SPs = $state['core:Logout-IFrame:Associations'];
$globalConfig = SimpleSAML_Configuration::getInstance();
$cookiePath = '/' . $globalConfig->getBaseURL();
/* Find the status of all SPs. */
foreach ($SPs as $assocId => &$sp) {
$spId = sha1($assocId);
$cookieId = 'logout-iframe-' . $spId;
if (isset($_COOKIE[$cookieId])) {
$cookie = $_COOKIE[$cookieId];
if ($cookie == 'completed' || $cookie == 'failed') {
$sp['core:Logout-IFrame:State'] = $cookie;
}
setcookie($cookieId, '', time() - 3600, $cookiePath);
}
if (!isset($associations[$assocId])) {
$sp['core:Logout-IFrame:State'] = 'completed';
}
}
/* Terminate the associations. */
foreach ($SPs as $assocId => $sp) {
if ($sp['core:Logout-IFrame:State'] === 'completed') {
$idp->terminateAssociation($assocId);
} else {
SimpleSAML_Logger::warning('Unable to terminate association with ' . var_export($assocId, TRUE) . '.');
$state['core:Failed'] = TRUE;
}
}
/* We are done. */
$idp->finishLogout($state);
function updateStatus() {
var nFailed = 0;
var nProgress = 0;
for (sp in window.spStatus) {
switch (window.spStatus[sp]) {
case 'failed':
nFailed += 1;
break;
case 'inprogress':
nProgress += 1;
break;
}
}
if (nFailed > 0) {
$('#logout-failed-message').show();
}
if (nProgress == 0 && nFailed == 0) {
$('#logout-completed').show();
$('#done-form').submit();
}
}
function updateSPStatus(spId, status, reason) {
if (window.spStatus[spId] == status) {
/* Unchanged. */
return;
}
$('#statusimage-' + spId).attr('src', window.stateImage[status]).attr('alt', window.stateText[status]).attr('title', reason);
window.spStatus[spId] = status;
updateStatus();
}
function logoutCompleted(spId) {
updateSPStatus(spId, 'completed', '');
}
function logoutFailed(spId, reason) {
updateSPStatus(spId, 'failed', reason);
}
function timeoutSPs() {
for (sp in window.spStatus) {
if (window.spStatus[sp] == 'inprogress') {
logoutFailed(sp, 'Timeout');
}
}
}
function asyncUpdate() {
jQuery.getJSON(window.asyncURL, window.spStatus, function(data, textStatus) {
for (sp in data) {
if (data[sp] == 'completed') {
logoutCompleted(sp);
} else if (data[sp] == 'failed') {
logoutFailed(sp, 'async update');
}
}
window.setTimeout(asyncUpdate, 1000);
});
}
$('document').ready(function(){
if (window.type == 'js') {
window.timeoutID = window.setTimeout(timeoutSPs, window.timeoutIn * 1000);
window.setTimeout(asyncUpdate, 1000);
updateStatus();
} else if (window.type == 'init') {
$('#logout-type-selector').attr('value', 'js');
$('#logout-all').focus();
}
});
<?php
if (!isset($_REQUEST['id'])) {
throw new SimpleSAML_Error_BadRequest('Missing required parameter: id');
}
$id = (string)$_REQUEST['id'];
if (isset($_REQUEST['type'])) {
$type = (string)$_REQUEST['type'];
if (!in_array($type, array('init', 'js', 'nojs', 'embed', 'async'), TRUE)) {
throw new SimpleSAML_Error_BadRequest('Invalid value for type.');
}
} else {
$type = 'init';
}
if (isset($_REQUEST['timeout'])) {
$timeout = (int)$_REQUEST['timeout'];
} else {
$timeout = time() + 10;
}
$state = SimpleSAML_Auth_State::loadState($id, 'core:Logout-IFrame');
$idp = SimpleSAML_IdP::getByState($state);
if ($type !== 'init') {
/* Update association state. */
$associations = $idp->getAssociations();
foreach ($state['core:Logout-IFrame:Associations'] as $assocId => &$sp) {
$spId = sha1($assocId);
/* Move SPs from 'onhold' to 'inprogress'. */
if ($sp['core:Logout-IFrame:State'] === 'onhold') {
$sp['core:Logout-IFrame:State'] = 'inprogress';
}
/* Check for update by cookie. */
$cookieId = 'logout-iframe-' . $spId;
if (isset($_COOKIE[$cookieId])) {
$cookie = $_COOKIE[$cookieId];
if ($cookie == 'completed' || $cookie == 'failed') {
$sp['core:Logout-IFrame:State'] = $cookie;
}
}
/* Check for update through request. */
if (isset($_REQUEST[$spId])) {
$s = $_REQUEST[$spId];
if ($s == 'completed' || $s == 'failed') {
$sp['core:Logout-IFrame:State'] = $s;
}
}
/* In case we are refreshing a page. */
if (!isset($associations[$assocId])) {
$sp['core:Logout-IFrame:State'] = 'completed';
}
/* Update the IdP. */
if ($sp['core:Logout-IFrame:State'] === 'completed') {
$idp->terminateAssociation($assocId);
}
}
}
if ($type === 'js' || $type === 'nojs') {
foreach ($state['core:Logout-IFrame:Associations'] as $assocId => &$sp) {
if ($sp['core:Logout-IFrame:State'] !== 'inprogress') {
/* This SP isn't logging out. */
continue;
}
try {
$url = call_user_func(array($sp['Handler'], 'getLogoutURL'), $idp, $sp, NULL);
$sp['core:Logout-IFrame:URL'] = $url;
} catch (Exception $e) {
$sp['core:Logout-IFrame:State'] = 'failed';
}
}
}
$id = SimpleSAML_Auth_State::saveState($state, 'core:Logout-IFrame');
$globalConfig = SimpleSAML_Configuration::getInstance();
if ($type === 'nojs') {
$t = new SimpleSAML_XHTML_Template($globalConfig, 'core:logout-iframe-wrapper.php');
$t->data['id'] = $id;
$t->data['SPs'] = $state['core:Logout-IFrame:Associations'];
$t->data['timeout'] = $timeout;
$t->show();
exit(0);
} elseif ($type == 'async') {
header('Content-Type: application/json');
$res = array();
foreach ($state['core:Logout-IFrame:Associations'] as $assocId => $sp) {
if ($sp['core:Logout-IFrame:State'] !== 'completed') {
continue;
}
$res[sha1($assocId)] = 'completed';
}
echo(json_encode($res));
exit(0);
}
$t = new SimpleSAML_XHTML_Template($globalConfig, 'core:logout-iframe.php');
$t->data['id'] = $id;
$t->data['type'] = $type;
$t->data['from'] = $state['core:Logout-IFrame:From'];
$t->data['SPs'] = $state['core:Logout-IFrame:Associations'];
$t->data['timeout'] = $timeout;
$t->show();
exit(0);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment