From 32584cbeffa871c55251d68fcaf83bcde0689ea3 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Wed, 8 Sep 2010 07:04:37 +0000
Subject: [PATCH] discopower: CDC support.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@2532 44740490-163a-0410-bde0-09ae8108e29a
---
 .../config-templates/module_discopower.php    |   8 ++
 modules/discopower/lib/PowerIdPDisco.php      | 114 ++++++++++++++++++
 2 files changed, 122 insertions(+)

diff --git a/modules/discopower/config-templates/module_discopower.php b/modules/discopower/config-templates/module_discopower.php
index 2bdfee6bc..5764dd285 100644
--- a/modules/discopower/config-templates/module_discopower.php
+++ b/modules/discopower/config-templates/module_discopower.php
@@ -28,6 +28,14 @@ $config = array (
 	  * 'score' => 'suggest', 
 	  */
 
+	/*
+	 * The domain to use for common domain cookie support.
+	 * This must be a parent domain of the domain hosting the discovery service.
+	 *
+	 * If this is NULL (the default), common domain cookie support will be disabled.
+	 */
+	'cdc.domain' => NULL,
+
 );
 
 ?>
diff --git a/modules/discopower/lib/PowerIdPDisco.php b/modules/discopower/lib/PowerIdPDisco.php
index 8d07bc6f6..1ed76f7f5 100644
--- a/modules/discopower/lib/PowerIdPDisco.php
+++ b/modules/discopower/lib/PowerIdPDisco.php
@@ -15,6 +15,16 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 
 	private $discoconfig;
 
+
+	/**
+	 * The domain to use when saving common domain cookies.
+	 * This is NULL if support for common domain cookies is disabled.
+	 *
+	 * @var string|NULL
+	 */
+	private $cdcDomain;
+
+
 	/**
 	 * Initializes this discovery service.
 	 *
@@ -30,6 +40,11 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 
 		$this->discoconfig = SimpleSAML_Configuration::getConfig('module_discopower.php');
 
+		$this->cdcDomain = $this->discoconfig->getString('cdc.domain', NULL);
+		if ($this->cdcDomain !== NULL && $this->cdcDomain[0] !== '.') {
+			/* Ensure that the CDC domain starts with a dot ('.') as required by the spec. */
+			$this->cdcDomain = '.' . $this->cdcDomain;
+		}
 	}
 
 
@@ -207,6 +222,105 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 		$t->data['score'] = $this->discoconfig->getValue('score', 'quicksilver');
 		$t->show();
 	}
+
+
+	/**
+	 * Get the IdP entities saved in the common domain cookie.
+	 *
+	 * @return array  List of IdP entities.
+	 */
+	private function getCDC() {
+
+		if (!isset($_COOKIE['_saml_idp'])) {
+			return array();
+		}
+
+		$ret = (string)$_COOKIE['_saml_idp'];
+		$ret = explode(' ', $ret);
+		foreach ($ret as &$idp) {
+			$idp = base64_decode($idp);
+			if ($idp === FALSE) {
+				/* Not properly base64 encoded. */
+				return array();
+			}
+		}
+
+		return $ret;
+	}
+
+
+	/**
+	 * Save the current IdP choice to a cookie.
+	 *
+	 * This function overrides the corresponding function in the parent class,
+	 * to add support for common domain cookie.
+	 *
+	 * @param string $idp  The entityID of the IdP.
+	 */
+	protected function setPreviousIdP($idp) {
+		assert('is_string($idp)');
+
+		if ($this->cdcDomain === NULL) {
+			parent::setPreviousIdP($idp);
+			return;
+		}
+
+		$list = $this->getCDC();
+
+		$prevIndex = array_search($idp, $list, TRUE);
+		if ($prevIndex !== FALSE) {
+			unset($list[$prevIndex]);
+		}
+		$list[] = $idp;
+
+		foreach ($list as &$value) {
+			$value = base64_encode($value);
+		}
+		$newCookie = implode(' ', $list);
+
+		while (strlen($newCookie) > 4000) {
+			/* The cookie is too long. Remove the oldest elements until it is short enough. */
+			$tmp = explode(' ', $newCookie, 2);
+			if (count($tmp) === 1) {
+				/*
+				 * We are left with a single entityID whose base64
+				 * representation is too long to fit in a cookie.
+				 */
+				break;
+			}
+			$newCookie = $tmp[1];
+		}
+
+		setcookie('_saml_idp', $newCookie, time() + 180*24*60*60, '/', $this->cdcDomain, TRUE);
+	}
+
+
+	/**
+	 * Retrieve the previous IdP the user used.
+	 *
+	 * This function overrides the corresponding function in the parent class,
+	 * to add support for common domain cookie.
+	 *
+	 * @return string|NULL  The entity id of the previous IdP the user used, or NULL if this is the first time.
+	 */
+	protected function getPreviousIdP() {
+
+		if ($this->cdcDomain === NULL) {
+			return parent::getPreviousIdP();
+		}
+
+		$prevIdPs = $this->getCDC();
+		while (count($prevIdPs) > 0) {
+			$idp = array_pop($prevIdPs);
+			$idp = $this->validateIdP($idp);
+			if ($idp !== NULL) {
+				return $idp;
+			}
+		}
+
+		return NULL;
+	}
+
 }
 
 ?>
\ No newline at end of file
-- 
GitLab