From e8aa5965d610efcd142cc97d6b6cf6463270f1ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=85kre=20Solberg?= <andreas.solberg@uninett.no>
Date: Mon, 25 Aug 2008 19:56:28 +0000
Subject: [PATCH] A lot of bugfixes and improvements to iFrame SLO.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@832 44740490-163a-0410-bde0-09ae8108e29a
---
 lib/SimpleSAML/Session.php                    |  12 +-
 templates/default/logout-iframe.php           |  40 ++--
 www/resources/default.css                     |  39 +++-
 www/saml2/idp/SingleLogoutService.php         |  10 +
 www/saml2/idp/SingleLogoutServiceiFrame.php   | 183 +++++-------------
 .../idp/SingleLogoutServiceiFrameResponse.php |  11 +-
 6 files changed, 143 insertions(+), 152 deletions(-)

diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index b80831289..19a18b362 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -203,13 +203,23 @@ class SimpleSAML_Session {
 		return $list;
 	}
 	
+	public function sp_logout_completed() {
+
+		if (!$this->sp_at_idpsessions) return TRUE;
+		
+		foreach ($this->sp_at_idpsessions AS $entityid => $sp) {
+			if ($sp != self::STATE_LOGGEDOUT) return FALSE;
+		}
+		return TRUE;
+	}
+	
+	
 	public function set_sp_logout_completed($entityid) {
 		SimpleSAML_Logger::debug('Library - Session: Setting SP state completed for : ' . $entityid);
 		$this->dirty = true;
 		$this->sp_at_idpsessions[$entityid] = self::STATE_LOGGEDOUT;
 	}
 	
-	
 	public function dump_sp_sessions() {
 		foreach ($this->sp_at_idpsessions AS $entityid => $sp) {
 			SimpleSAML_Logger::debug('Dump sp sessions: ' . $entityid . ' status: ' . $sp);
diff --git a/templates/default/logout-iframe.php b/templates/default/logout-iframe.php
index c6d8f44ec..35cdda2ac 100644
--- a/templates/default/logout-iframe.php
+++ b/templates/default/logout-iframe.php
@@ -20,25 +20,37 @@ for (j=1; j<=10; j++) {
 ?>
 
 	<div id="content">
-
-	<p style="margin: .2em">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</p>
 	
-	<?php
+		<p>You have initiated a <strong>global logout</strong> from the service <strong><?php echo $this->data['requesterName']; ?></strong>. Global logout means you will be logged out from all services connected to this identity provider. This page will show the status of the logout proccess for all of the services you are logged into.</p>
+	
+
 	
-		foreach ($this->data['sparray'] AS $sp) {
-			echo '<iframe class="hiddeniframe" style="width: 200px; height: 100px" src="' . $sp['url'] . '" ></iframe>';
-		}
+
 		
-		foreach ($this->data['sparray'] AS $spentityid => $sp) {
-			echo '<div class="inprogress" style="border: 1px solid #888; background: #eee; color: #444; padding: .2em; margin: .7em" id="' . $spentityid . '">
-				<img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from ' . $spentityid . '</div>';
-		}
+		<?php
 		
-	
-	?>
+			foreach ($this->data['sparray'] AS $sp) {
+				echo '<iframe class="hiddeniframe" style="border: 1px solid #888; width: 80%; height: 100px" src="' . $sp['url'] . '" ></iframe>';
+			}
+			
+			foreach ($this->data['sparray'] AS $spentityid => $sp) {
+				echo '<div class="inprogress" id="' . $spentityid . '">
+					<img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from <strong>' . $sp['name'] . '</strong></div>';
+			}
+			
+		
+		?>
+		
+		<div id="interrupt">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</div>
+		<div id="iscompleted">You have successfully logged out from all services listed above.
+			<!-- form method="get" action="<?php echo $this->data['logoutresponse']; ?>">
+				<input type="submit" name="s" value="OK, continue back to <?php echo $this->data['requesterName']; ?> to complete the logout process." />
+			</form  -->
+			<br />[ <a href="<?php echo $this->data['logoutresponse']; ?>">OK, continue back to <?php echo $this->data['requesterName']; ?> to complete the logout process.</a> ]
+		</div>
 	</div>
-
+	
 
 	
 
-<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
+<?php $this->includeAtTemplateBase('includes/footer.php'); ?>
\ No newline at end of file
diff --git a/www/resources/default.css b/www/resources/default.css
index 64cd82114..88dcf6f36 100644
--- a/www/resources/default.css
+++ b/www/resources/default.css
@@ -256,10 +256,47 @@ table.attributes tr td {
 	vertical-align: top;
 }
 
+
+div.allcompleted#interrupt {
+	display: none;
+}
+div#interrupt a:link {
+	color: #036;
+	border-bottom: 1px dotted #036;
+	text-decoration: none;
+}
+div#interrupt a:hover {
+	border-bottom: 1px solid #036;
+}
+div#interrupt {
+	display: block;
+	border: 3px solid #036;
+	background: #39F;
+	padding: 1em;	
+	margin: 1em;
+}
+div#iscompleted {
+	display: none;
+	border: 3px solid #993;
+	background: #FF9;
+	padding: 1em;
+	margin: 1em;
+}
+div.allcompleted#iscompleted {
+	display: block ! important;
+}
+div.inprogress, div.loggedout {
+
+	background: #eee; 
+	color: #444; 
+	padding: 1em;
+	margin: 1em;
+}
 div.inprogress {
-	background: #eee;
+	border: 1px dotted #888; 
 }
 div.loggedout {
+	border: 1px solid #888; 
 	background: #9f9 ! important ;
 }
 iframe.hiddeniframe {
diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php
index 95ae16c02..f38e74e70 100644
--- a/www/saml2/idp/SingleLogoutService.php
+++ b/www/saml2/idp/SingleLogoutService.php
@@ -31,6 +31,16 @@ try {
 SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got IdP entity id: ' . $idpentityid);
 
 
+$logouttype = 'traditional';
+$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted');
+if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype'];
+
+if ($logouttype !== 'traditional') 
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [traditional]'));
+
+
+
+
 /**
  * The $logoutInfo contains information about the current logout operation.
  * It can have the following attributes:
diff --git a/www/saml2/idp/SingleLogoutServiceiFrame.php b/www/saml2/idp/SingleLogoutServiceiFrame.php
index 5daf09545..03a200266 100644
--- a/www/saml2/idp/SingleLogoutServiceiFrame.php
+++ b/www/saml2/idp/SingleLogoutServiceiFrame.php
@@ -29,6 +29,16 @@ try {
 SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid);
 
 
+
+$logouttype = 'traditional';
+$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted');
+if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype'];
+
+if ($logouttype !== 'iframe') 
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [iframe]'));
+
+
+
 /**
  * The $logoutInfo contains information about the current logout operation.
  * It can have the following attributes:
@@ -80,6 +90,8 @@ require_once(SimpleSAML_Utilities::resolvePath('libextinc') . '/xajax/xajax.inc.
  */
 function updateslostatus() {
 
+	SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutServiceiFrame: Accessing SAML 2.0 IdP endpoint SingleLogoutService (iFrame version) within updateslostatus() ');
+
 	$config = SimpleSAML_Configuration::getInstance();
 	$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
 	$session = SimpleSAML_Session::getInstance();
@@ -96,10 +108,36 @@ function updateslostatus() {
 		error_log('Completed ' . $spentityid);
 		// add a command to the response to assign the innerHTML attribute of
 		// the element with id="SomeElementId" to whatever the new content is
+		
+		$spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote');
+		$name = array_key_exists('name', $spmetadata) ? $spmetadata['name'] : $spentityid;
+
+		
 		$objResponse->addAssign($spentityid, "className", 'loggedout');
-		$objResponse->addAssign($spentityid, "innerHTML", 'Logging out from ' . $spentityid . ' successfully completed');
+		$objResponse->addAssign($spentityid, "innerHTML", 'Logging out from <strong>' . $name . '</strong> successfully completed');
 
 	}
+	
+	if ($session->sp_logout_completed() === TRUE) {
+		$objResponse->addAssign('iscompleted', "className", 'allcompleted');
+		$objResponse->addAssign('interrupt', "className", 'allcompleted');
+
+		/**
+		 * Clean up session object to save storage.
+		 */
+		if ($config->getValue('debug', false)) 
+			SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize());
+			
+		$session->clean();
+		
+		if ($config->getValue('debug', false)) 
+			SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize());
+
+
+
+	} else {
+		SimpleSAML_Logger::debug('SAML2.0 - sp_logout_completed FALSE');
+	}
     
     //return the  xajaxResponse object
     return $objResponse;
@@ -244,12 +282,15 @@ foreach ($listofsps AS $spentityid) {
 	// $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP'
 	$url = $httpredirect->getRedirectURL($req, $idpentityid, $spentityid, NULL, 'SingleLogoutService', 'SAMLRequest', 'IdP');
 
-	$sparray[$spentityid] = array('url' => $url);
+	$spmetadata = $metadata->getMetaData($spentityid, 'saml20-sp-remote');
+	$name = array_key_exists('name', $spmetadata) ? $spmetadata['name'] : $spentityid;
 
-}
+	$sparray[$spentityid] = array('url' => $url, 'name' => $name);
 
+}
 
 
+#print_r($sparray);
 
 
 
@@ -271,16 +312,7 @@ try {
 	
 	SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Found logout info with these keys: ' . join(',', array_keys($logoutInfo)));
 	
-	/**
-	 * Clean up session object to save storage.
-	 */
-	if ($config->getValue('debug', false)) 
-		SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize());
-		
-	$session->clean();
-	
-	if ($config->getValue('debug', false)) 
-		SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize());
+
 	
 	
 	/*
@@ -327,7 +359,9 @@ try {
 
 
 
-
+$spmeta = $metadata->getMetaData($requester, 'saml20-sp-remote');
+$spname = $requester;
+if (array_key_exists('name', $spmeta)) $spname = $spmeta['name'];
 
 
 
@@ -362,6 +396,7 @@ $et->data['header'] = 'SAML 2.0 IdP Ajax Logout';
 $et->data['sparray'] = $sparray;
 $et->data['logoutresponse'] = $logoutresponse;
 $et->data['xajax'] = $xajax;
+$et->data['requesterName'] = $spname;
 
 $et->data['head'] = $xajax->printJavascript();
 
@@ -372,124 +407,4 @@ exit(0);
 
 
 
-
-
-/*
- * If we get an LogoutRequest then we initiate the logout process.
- */
-if (isset($_GET['SAMLRequest'])) {
-
-	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-	$logoutrequest = $binding->decodeLogoutRequest($_GET);
-	
-	$session->setAuthenticated(false);
-
-	//$requestid = $authnrequest->getRequestID();
-	//$session->setAuthnRequest($requestid, $authnrequest);
-	
-	//echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>';
-	
-	error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . '  ');
-	
-	$session->set_sp_logout_completed($logoutrequest->getIssuer() );
-	$session->setLogoutRequest($logoutrequest);
-
-/*
- * We receive a Logout Response to a Logout Request that we have issued earlier.
- */
-} elseif (isset($_GET['SAMLResponse'])) {
-
-	$binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-	$loginresponse = $binding->decodeLogoutResponse($_GET);
-	
-	$session->set_sp_logout_completed($loginresponse->getIssuer());
-	
-	error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . '  ');
-}
-
-/*
- * We proceed to send logout requests to all remaining SPs.
- */
-$spentityid = $session->get_next_sp_logout();
-if ($spentityid) {
-
-	error_log('IdP LogoutService: next SP ' . $spentityid);
-
-	try {
-		$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
-	
-		// ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) {
-		$req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP');
-		
-		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-		
-		$relayState = SimpleSAML_Utilities::selfURL();
-		if (isset($_GET['RelayState'])) {
-			$relayState = $_GET['RelayState'];
-		}
-		
-		//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
-		$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP');
-		
-		exit();
-
-	} catch(Exception $exception) {
-		
-		$et = new SimpleSAML_XHTML_Template($config, 'error.php');
-		
-		$et->data['header'] = 'Error sending logout request to service';
-		$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
-		$et->data['e'] = $exception;
-		
-		$et->show();
-		exit(0);
-	}
-
-
-}
-
-/*
- * Logout procedure is done and we send a Logout Response back to the SP
- */
-error_log('IdP LogoutService:  SPs done ');
-try {
-
-	$logoutrequest = $session->getLogoutRequest();
-	if (!$logoutrequest) {
-		throw new Exception('Could not get reference to the logout request.');
-	}
-
-	$rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata);
-	
-	// generate($issuer, $receiver, $inresponseto, $mode )
-	
-	$logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP');
-	
-	//	echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>';
-	//	exit();
-	
-	$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-	
-	$relayState = SimpleSAML_Utilities::selfURL();
-	if (isset($_GET['RelayState'])) {
-		$relayState = $_GET['RelayState'];
-	}
-	
-	//$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP'
-	$httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP');
-
-} catch(Exception $exception) {
-	
-	$et = new SimpleSAML_XHTML_Template($config, 'error.php');
-	
-	$et->data['header'] = 'Error sending response to service';
-	$et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.';	
-	$et->data['e'] = $exception;
-	
-	$et->show();
-
-}
-
-
-
 ?>
\ No newline at end of file
diff --git a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php
index cd7a8f57c..ec9b18038 100644
--- a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php
+++ b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php
@@ -34,6 +34,12 @@ try {
 
 SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid);
 
+$logouttype = 'traditional';
+$idpmeta = $metadata->getMetaDataCurrent('saml20-idp-hosted');
+if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype'];
+
+if ($logouttype !== 'iframe') 
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'NOACCESS', new Exception('This IdP is configured to use logout type [' . $logouttype . '], but this endpoint is only available for IdP using logout type [iframe]'));
 
 
 
@@ -60,8 +66,9 @@ if (isset($_GET['SAMLResponse'])) {
 	
 } else {
 
-	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS' new Exception('No valid SAMLResponse found? Probably some error in remote partys metadata that sends something to this endpoint that is not SAML LogoutResponses') );
-	echo 'Not set: SAMLResponse';
+	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS', 
+		new Exception('No valid SAMLResponse found? Probably some error in remote partys metadata that sends something to this endpoint that is not SAML LogoutResponses') );
+#	echo 'Not set: SAMLResponse';
 }
 
 ?>
\ No newline at end of file
-- 
GitLab