From f6e406dc76f93a9bc0317e7aa2d2c571e6b31ba6 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Mon, 10 Nov 2008 13:01:50 +0000
Subject: [PATCH] modules/saml2: Add support for logout.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@985 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/saml2/lib/Auth/Source/SP.php | 78 ++++++++++++++++++++++++++++
 modules/saml2/www/sp/acs.php         | 10 +++-
 modules/saml2/www/sp/metadata.php    |  1 +
 3 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/modules/saml2/lib/Auth/Source/SP.php b/modules/saml2/lib/Auth/Source/SP.php
index 5b0d8f6f4..b4ade68e2 100644
--- a/modules/saml2/lib/Auth/Source/SP.php
+++ b/modules/saml2/lib/Auth/Source/SP.php
@@ -19,6 +19,11 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 	 */
 	const STAGE_SENT = 'saml2:SP-SSOSent';
 
+	/**
+	 * The string used to identify our logout state.
+	 */
+	const STAGE_LOGOUTSENT = 'saml2:SP-LogoutSent';
+
 
 	/**
 	 * The key of the AuthId field in the state.
@@ -26,6 +31,23 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 	const AUTHID = 'saml2:AuthId';
 
 
+	/**
+	 * The key for the IdP entity id in the logout state.
+	 */
+	const LOGOUT_IDP = 'saml2:SP-Logout-IdP';
+
+	/**
+	 * The key for the NameID in the logout state.
+	 */
+	const LOGOUT_NAMEID = 'saml2:SP-Logout-NameID';
+
+
+	/**
+	 * The key for the SessionIndex in the logout state.
+	 */
+	const LOGOUT_SESSIONINDEX = 'saml2:SP-Logout-SessionIndex';
+
+
 	/**
 	 * The entity id of this SP.
 	 */
@@ -126,6 +148,62 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 		return FALSE;
 	}
 
+
+	/**
+	 * Handle logout operation.
+	 *
+	 * @param array $state  The logout state.
+	 */
+	public function logout(&$state) {
+		assert('is_array($state)');
+		assert('array_key_exists(self::LOGOUT_IDP, $state)');
+		assert('array_key_exists(self::LOGOUT_NAMEID, $state)');
+		assert('array_key_exists(self::LOGOUT_SESSIONINDEX, $state)');
+
+		$id = SimpleSAML_Auth_State::saveState($state, self::STAGE_LOGOUTSENT);
+
+		$idp = $state[self::LOGOUT_IDP];
+		$nameId = $state[self::LOGOUT_NAMEID];
+		$sessionIndex = $state[self::LOGOUT_SESSIONINDEX];
+
+		$config = SimpleSAML_Configuration::getInstance();
+		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+		$lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata);
+		$req = $lr->generate($this->entityId, $idp, $nameId, $sessionIndex, 'SP');
+
+		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
+		$httpredirect->sendMessage($req, $this->entityId, $idp, $id, 'SingleLogoutService', 'SAMLRequest', 'SP');
+
+		exit(0);
+	}
+
+
+	/**
+	 * Called when we are logged in.
+	 *
+	 * @param string $idpEntityId  Entity id of the IdP.
+	 * @param array $state  The state of the authentication operation.
+	 */
+	public function onLogin($idpEntityId, $state) {
+		assert('is_string($idpEntityId)');
+		assert('is_array($state)');
+
+		$this->addLogoutCallback($idpEntityId, $state);
+	}
+
+
+	/**
+	 * Called when we receive a logout request.
+	 *
+	 * @param string $idpEntityId  Entity id of the IdP.
+	 */
+	public function onLogout($idpEntityId) {
+		assert('is_string($idpEntityId)');
+
+		$this->callLogoutCallback($idpEntityId);
+	}
+
 }
 
 ?>
\ No newline at end of file
diff --git a/modules/saml2/www/sp/acs.php b/modules/saml2/www/sp/acs.php
index 6c0ba0e0c..929257e5f 100644
--- a/modules/saml2/www/sp/acs.php
+++ b/modules/saml2/www/sp/acs.php
@@ -47,7 +47,15 @@ if (!$source->isIdPValid($idp)) {
 		'. The IdP was ' . var_export($idp, TRUE));
 }
 
-/* TODO: Save NameID & SessionIndex for logout. */
+/* We need to save the NameID and SessionIndex for logout. */
+$logoutState = array(
+	sspmod_saml2_Auth_Source_SP::LOGOUT_IDP => $idp,
+	sspmod_saml2_Auth_Source_SP::LOGOUT_NAMEID => $authnResponse->getNameID(),
+	sspmod_saml2_Auth_Source_SP::LOGOUT_SESSIONINDEX => $authnResponse->getSessionIndex(),
+	);
+$state['LogoutState'] = $logoutState;
+
+$source->onLogin($idp, $state);
 
 $state['Attributes'] = $authnResponse->getAttributes();
 SimpleSAML_Auth_Source::completeAuth($state);
diff --git a/modules/saml2/www/sp/metadata.php b/modules/saml2/www/sp/metadata.php
index a24dfcd06..74449495b 100644
--- a/modules/saml2/www/sp/metadata.php
+++ b/modules/saml2/www/sp/metadata.php
@@ -18,6 +18,7 @@ $entityId = $source->getEntityId();
 
 $metaArray = array(
 	'AssertionConsumerService' => SimpleSAML_Module::getModuleURL('saml2/sp/acs.php'),
+	'SingleLogoutService' => SimpleSAML_Module::getModuleURL('saml2/sp/logout.php/' . $sourceId),
 	);
 
 $metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($entityId);
-- 
GitLab