From 2fdd394b7b8b237be3f8bb928d236e4966e7310e Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Wed, 14 Sep 2011 08:14:50 +0000
Subject: [PATCH] Logout-IFrame: Add per-SP timeout.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2898 44740490-163a-0410-bde0-09ae8108e29a
---
 .../core/templates/logout-iframe-wrapper.php  |  2 +-
 modules/core/templates/logout-iframe.php      |  5 ++--
 modules/core/www/idp/logout-iframe.js         |  7 ++++--
 modules/core/www/idp/logout-iframe.php        | 25 +++++++++++++------
 modules/saml/lib/IdP/SAML2.php                | 17 +++++++++++++
 5 files changed, 43 insertions(+), 13 deletions(-)

diff --git a/modules/core/templates/logout-iframe-wrapper.php b/modules/core/templates/logout-iframe-wrapper.php
index 3fafb77ee..00ce6be3a 100644
--- a/modules/core/templates/logout-iframe-wrapper.php
+++ b/modules/core/templates/logout-iframe-wrapper.php
@@ -4,7 +4,7 @@ $id = $this->data['id'];
 $SPs = $this->data['SPs'];
 $timeout = $this->data['timeout'];
 
-$iframeURL = 'logout-iframe.php?type=embed&id=' . urlencode($id) . '&timeout=' . (string)$timeout;
+$iframeURL = 'logout-iframe.php?type=embed&id=' . urlencode($id);
 
 /* Pretty arbitrary height, but should have enough safety margins for most cases. */
 $iframeHeight = 25 + count($SPs) * 4;
diff --git a/modules/core/templates/logout-iframe.php b/modules/core/templates/logout-iframe.php
index 1ae4073f6..c632e61a5 100644
--- a/modules/core/templates/logout-iframe.php
+++ b/modules/core/templates/logout-iframe.php
@@ -4,7 +4,6 @@ $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',
@@ -23,12 +22,14 @@ $stateText = array(
 );
 
 $spStatus = array();
+$spTimeout = 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;
+	$spTimeout[sha1($assocId)] = $sp['core:Logout-IFrame:Timeout'] - time();
 	if ($state === 'failed') {
 		$nFailed += 1;
 	} elseif ($state === 'inprogress') {
@@ -52,8 +53,8 @@ $this->data['head'] .= '
 window.stateImage = ' . json_encode($stateImage) . ';
 window.stateText = ' . json_encode($stateText) . ';
 window.spStatus = ' . json_encode($spStatus) . ';
+window.spTimeout = ' . json_encode($spTimeout) . ';
 window.type = "' . $type . '";
-window.timeoutIn = ' . (string)($timeout - time()) . ';
 window.asyncURL = "logout-iframe.php?id=' . $id . '&type=async";
 </script>';
 
diff --git a/modules/core/www/idp/logout-iframe.js b/modules/core/www/idp/logout-iframe.js
index 62e91daf6..282425ba6 100644
--- a/modules/core/www/idp/logout-iframe.js
+++ b/modules/core/www/idp/logout-iframe.js
@@ -53,16 +53,19 @@ function logoutFailed(spId, reason) {
 }
 
 function timeoutSPs() {
+	var cTime = ( (new Date()).getTime() - window.startTime ) / 1000;
 	for (sp in window.spStatus) {
-		if (window.spStatus[sp] == 'inprogress') {
+		if (window.spTimeout[sp] <= cTime && window.spStatus[sp] == 'inprogress') {
 			logoutFailed(sp, 'Timeout');
 		}
 	}
+	window.timeoutID = window.setTimeout(timeoutSPs, 1000);
 }
 
 $('document').ready(function(){
+	window.startTime = (new Date()).getTime();
 	if (window.type == 'js') {
-		window.timeoutID = window.setTimeout(timeoutSPs, window.timeoutIn * 1000);
+		window.timeoutID = window.setTimeout(timeoutSPs, 1000);
 		updateStatus();
 	} else if (window.type == 'init') {
 		$('#logout-type-selector').attr('value', 'js');
diff --git a/modules/core/www/idp/logout-iframe.php b/modules/core/www/idp/logout-iframe.php
index 1754c4f08..5b3c3e1ab 100644
--- a/modules/core/www/idp/logout-iframe.php
+++ b/modules/core/www/idp/logout-iframe.php
@@ -14,12 +14,6 @@ if (isset($_REQUEST['type'])) {
 	$type = 'init';
 }
 
-if (isset($_REQUEST['timeout'])) {
-	$timeout = (int)$_REQUEST['timeout'];
-} else {
-	$timeout = time() + 5;
-}
-
 if ($type !== 'embed' && $type !== 'async') {
 	SimpleSAML_Logger::stats('slo-iframe ' . $type);
 }
@@ -49,6 +43,13 @@ if ($type !== 'init') {
 			}
 		}
 
+		/* Check for timeout. */
+		if (isset($sp['core:Logout-IFrame:Timeout']) && $sp['core:Logout-IFrame:Timeout'] < time()) {
+			if ($sp['core:Logout-IFrame:State'] === 'inprogress') {
+				$sp['core:Logout-IFrame:State'] = 'failed';
+			}
+		}
+
 		/* In case we are refreshing a page. */
 		if (!isset($associations[$assocId])) {
 			$sp['core:Logout-IFrame:State'] = 'completed';
@@ -58,6 +59,16 @@ if ($type !== 'init') {
 		if ($sp['core:Logout-IFrame:State'] === 'completed') {
 			$idp->terminateAssociation($assocId);
 		}
+
+		if (!isset($sp['core:Logout-IFrame:Timeout'])) {
+			if (method_exists($sp['Handler'], 'getAssociationConfig')) {
+				$assocIdP = SimpleSAML_IdP::getByState($sp);
+				$assocConfig = call_user_func(array($sp['Handler'], 'getAssociationConfig'), $assocIdP, $sp);
+				$sp['core:Logout-IFrame:Timeout'] = $assocConfig->getInteger('core:logout-timeout', 5) + time();
+			} else {
+				$sp['core:Logout-IFrame:Timeout'] = time() + 5;
+			}
+		}
 	}
 }
 
@@ -87,7 +98,6 @@ 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);
 }
@@ -97,6 +107,5 @@ $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);
diff --git a/modules/saml/lib/IdP/SAML2.php b/modules/saml/lib/IdP/SAML2.php
index 803edfcc3..26a0d47c9 100644
--- a/modules/saml/lib/IdP/SAML2.php
+++ b/modules/saml/lib/IdP/SAML2.php
@@ -491,6 +491,23 @@ class sspmod_saml_IdP_SAML2 {
 	}
 
 
+	/**
+	 * Retrieve the metadata for the given SP association.
+	 *
+	 * @param SimpleSAML_IdP $idp  The IdP the association belongs to.
+	 * @param array $association  The SP association.
+	 * @return SimpleSAML_Configuration|NULL  Configuration object for the SP metadata.
+	 */
+	public static function getAssociationConfig(SimpleSAML_IdP $idp, array $association, $relayState) {
+		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+		try {
+			return $metadata->getMetaDataConfig($association['saml:entityID'], 'saml20-sp-remote');
+		} catch (Exception $e) {
+			SimpleSAML_Configuration::loadFromArray(array(), 'Unknown SAML 2 entity.');
+		}
+	}
+
+
 	/**
 	 * Calculate the NameID value that should be used.
 	 *
-- 
GitLab