Revision: 2139
Author: olavmrk
Date: Wed Jan 27 01:26:23 2010
Log: IdP core: Add logout processing.
http://code.google.com/p/simplesamlphp/source/detail?r=2139
Added:
/trunk/lib/SimpleSAML/IdP
/trunk/lib/SimpleSAML/IdP/LogoutHandler.php
/trunk/lib/SimpleSAML/IdP/LogoutIFrame.php
/trunk/lib/SimpleSAML/IdP/LogoutTraditional.php
/trunk/modules/core/templates/logout-iframe-wrapper.php
/trunk/modules/core/templates/logout-iframe.php
/trunk/modules/core/www/idp/logout-iframe-done.php
/trunk/modules/core/www/idp/logout-iframe.js
/trunk/modules/core/www/idp/logout-iframe.php
Modified:
/trunk/dictionaries/logout.definition.json
/trunk/dictionaries/logout.translation.json
/trunk/lib/SimpleSAML/IdP.php
=======================================
--- /dev/null
+++ /trunk/lib/SimpleSAML/IdP/LogoutHandler.php Wed Jan 27 01:26:23 2010
@@ -0,0 +1,56 @@
+<?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. */
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/lib/SimpleSAML/IdP/LogoutIFrame.php Wed Jan 27 01:26:23 2010
@@ -0,0 +1,92 @@
+<?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);
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/lib/SimpleSAML/IdP/LogoutTraditional.php Wed Jan 27 01:26:23 2010
@@ -0,0 +1,92 @@
+<?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);
+ }
+
+}
=======================================
--- /dev/null
+++ /trunk/modules/core/templates/logout-iframe-wrapper.php Wed Jan 27
01:26:23 2010
@@ -0,0 +1,29 @@
+<?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');
=======================================
--- /dev/null
+++ /trunk/modules/core/templates/logout-iframe.php Wed Jan 27 01:26:23 2010
@@ -0,0 +1,205 @@
+<?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');
+}
=======================================
--- /dev/null
+++ /trunk/modules/core/www/idp/logout-iframe-done.php Wed Jan 27 01:26:23
2010
@@ -0,0 +1,52 @@
+<?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);
=======================================
--- /dev/null
+++ /trunk/modules/core/www/idp/logout-iframe.js Wed Jan 27 01:26:23 2010
@@ -0,0 +1,75 @@
+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();
+ }
+});
=======================================
--- /dev/null
+++ /trunk/modules/core/www/idp/logout-iframe.php Wed Jan 27 01:26:23 2010
@@ -0,0 +1,118 @@
+<?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);
=======================================
--- /trunk/dictionaries/logout.definition.json Tue Jan 12 03:33:22 2010
+++ /trunk/dictionaries/logout.definition.json Wed Jan 27 01:26:23 2010
@@ -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>."
}
}
=======================================
--- /trunk/dictionaries/logout.translation.json Tue Jan 26 00:19:26 2010
+++ /trunk/dictionaries/logout.translation.json Wed Jan 27 01:26:23 2010
@@ -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>."
}
}
=======================================
--- /trunk/lib/SimpleSAML/IdP.php Sun Jan 24 23:19:07 2010
+++ /trunk/lib/SimpleSAML/IdP.php Wed Jan 27 01:26:23 2010
@@ -105,6 +105,84 @@
return $this->config;
}
+
+
+ /**
+ * 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));
+ }
+ }
/**
@@ -278,5 +356,150 @@
SimpleSAML_Auth_State::throwException($state, $e);
}
}
+
+
+ /**
+ * 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');
+ }
}
--
You received this message because you are subscribed to the Google Groups
"simpleSAMLphp commits" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/simplesamlphp-commits?hl=en.