From b170f27d1be48e2069d99e8e53cfd6d328d21637 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Wed, 19 Aug 2009 08:30:48 +0000
Subject: [PATCH] SimpleSAML_XHTML_IdPDisco: Support multiple metadata sets.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1694 44740490-163a-0410-bde0-09ae8108e29a
---
 lib/SimpleSAML/XHTML/IdPDisco.php        | 110 ++++++++++++++---------
 modules/discopower/lib/PowerIdPDisco.php |  13 +--
 modules/discopower/www/disco.php         |   2 +-
 modules/saml2/www/disco.php              |   2 +-
 www/saml2/sp/idpdisco.php                |   2 +-
 www/shib13/sp/idpdisco.php               |   2 +-
 www/wsfed/sp/idpdisco.php                |   2 +-
 7 files changed, 82 insertions(+), 51 deletions(-)

diff --git a/lib/SimpleSAML/XHTML/IdPDisco.php b/lib/SimpleSAML/XHTML/IdPDisco.php
index c952b75dc..d694111e3 100644
--- a/lib/SimpleSAML/XHTML/IdPDisco.php
+++ b/lib/SimpleSAML/XHTML/IdPDisco.php
@@ -15,25 +15,16 @@
 class SimpleSAML_XHTML_IdPDisco {
 
 	/**
-	 * The various discovery services we can host.
+	 * An instance of the configuration class.
 	 */
-	protected static $discoTypes = array(
-		'saml20' => array(
-			'metadata' => 'saml20-idp-remote',
-			),
-		'shib13' => array(
-			'metadata' => 'shib13-idp-remote',
-			),
-		'wsfed' => array(
-			'metadata' => 'wsfed-idp-remote',
-			),
-		);
-
+	protected $config;
 
 	/**
-	 * An instance of the configuration class.
+	 * The identifier of this discovery service.
+	 *
+	 * @var string
 	 */
-	protected $config;
+	protected $instance;
 
 
 	/**
@@ -49,9 +40,11 @@ class SimpleSAML_XHTML_IdPDisco {
 
 
 	/**
-	 * Our discovery service type.
+	 * The metadata sets we find allowed entities in, in prioritized order.
+	 *
+	 * @var array
 	 */
-	protected $discoType;
+	protected $metadataSets;
 
 
 	/**
@@ -90,22 +83,18 @@ class SimpleSAML_XHTML_IdPDisco {
 	 * The constructor does the parsing of the request. If this is an invalid request, it will
 	 * throw an exception.
 	 *
-	 * @param $discoType  String which identifies the type of discovery service.
+	 * @param array $metadataSets  Array with metadata sets we find remote entities in.
+	 * @param string $instance  The name of this instance of the discovery service.
 	 */
-	public function __construct($discoType) {
+	public function __construct(array $metadataSets, $instance) {
+		assert('is_string($instance)');
 
 		/* Initialize standard classes. */
 		$this->config = SimpleSAML_Configuration::getInstance();
 		$this->metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
 		$this->session = SimpleSAML_Session::getInstance();
-
-
-		if(!array_key_exists($discoType, self::$discoTypes)) {
-			throw new Exception('Unknown discovery service type: ' . $discoType);
-		}
-
-		$this->discoType = self::$discoTypes[$discoType];
-		$this->discoType['type'] = $discoType;
+		$this->instance = $instance;
+		$this->metadataSets = $metadataSets;
 
 		$this->log('Accessing discovery service.');
 
@@ -156,7 +145,7 @@ class SimpleSAML_XHTML_IdPDisco {
 	 * @param $message  The message which should be logged.
 	 */
 	protected function log($message) {
-		SimpleSAML_Logger::info('idpDisco.' . $this->discoType['type'] . ': ' . $message);
+		SimpleSAML_Logger::info('idpDisco.' . $this->instance . ': ' . $message);
 	}
 
 
@@ -170,7 +159,7 @@ class SimpleSAML_XHTML_IdPDisco {
 	 * @return  The value of the cookie with the given name, or NULL if no cookie with that name exists.
 	 */
 	protected function getCookie($name) {
-		$prefixedName = 'idpdisco_' . $this->discoType['type'] . '_' . $name;
+		$prefixedName = 'idpdisco_' . $this->instance . '_' . $name;
 		if(array_key_exists($prefixedName, $_COOKIE)) {
 			return $_COOKIE[$prefixedName];
 		} else {
@@ -189,7 +178,7 @@ class SimpleSAML_XHTML_IdPDisco {
 	 * @param $value  The value of the cookie.
 	 */
 	protected function setCookie($name, $value) {
-		$prefixedName = 'idpdisco_' . $this->discoType['type'] . '_' . $name;
+		$prefixedName = 'idpdisco_' . $this->instance . '_' . $name;
 
 		/* We save the cookies for 90 days. */
 		$saveUntil = time() + 60*60*24*90;
@@ -219,14 +208,16 @@ class SimpleSAML_XHTML_IdPDisco {
 			return $idp;
 		}
 
-		try {
-			$this->metadata->getMetaData($idp, $this->discoType['metadata']);
-			return $idp;
-		} catch(Exception $e) {
-			$this->log('Unable to validate IdP entity id [' . $idp . '].');
-			/* The entity id wasn't valid. */
-			return NULL;
+		foreach ($this->metadataSets AS $metadataSet) {
+			try {
+				$this->metadata->getMetaData($idp, $metadataSet);
+				return $idp;
+			} catch(Exception $e) { }
 		}
+
+		$this->log('Unable to validate IdP entity id [' . $idp . '].');
+		/* The entity id wasn't valid. */
+		return NULL;
 	}
 
 
@@ -305,6 +296,24 @@ class SimpleSAML_XHTML_IdPDisco {
 	}
 
 
+	/**
+	 * Retrieve a recommended IdP based on the IP address of the client.
+	 *
+	 * @return string|NULL  The entity ID of the IdP if one is found, or NULL if not.
+	 */
+	protected function getFromCIDRhint() {
+
+		foreach ($this->metadataSets as $metadataSet) {
+			$idp = $this->metadata->getPreferredEntityIdFromCIDRhint($metadataSet, $_SERVER['REMOTE_ADDR']);
+			if (!empty($idp)) {
+				return $idp;
+			}
+		}
+
+		return NULL;
+	}
+
+
 	/**
 	 * Try to determine which IdP the user should most likely use.
 	 *
@@ -321,8 +330,7 @@ class SimpleSAML_XHTML_IdPDisco {
 			return $idp;
 		}
 
-		$idp = $this->metadata->getPreferredEntityIdFromCIDRhint(
-			$this->discoType['metadata'], $_SERVER['REMOTE_ADDR']);
+		$idp = $this->getFromCIDRhint();
 
 		if(!empty($idp)) {
 			$this->log('Preferred IdP from CIDR hint [' . $idp . '].');
@@ -389,6 +397,28 @@ class SimpleSAML_XHTML_IdPDisco {
 	}
 
 
+	/**
+	 * Retrieve the list of IdPs which are stored in the metadata.
+	 *
+	 * @return array  Array with entityid=>metadata mappings.
+	 */
+	protected function getIdPList() {
+
+		$idpList = array();
+		foreach ($this->metadataSets AS $metadataSet) {
+			$newList = $this->metadata->getList($metadataSet);
+			/*
+			 * Note that we merge the entities in reverse order. This ensuers
+			 * that it is the entity in the first metadata set that "wins" if
+			 * two metadata sets have the same entity.
+			 */
+			$idpList = array_merge($newList, $idpList);
+		}
+
+		return $idpList;
+	}
+
+
 	/**
 	 * Handles a request to this discovery service.
 	 *
@@ -427,7 +457,7 @@ class SimpleSAML_XHTML_IdPDisco {
 
 		/* No choice made. Show discovery service page. */
 
-		$idpList = $this->metadata->getList($this->discoType['metadata']);
+		$idpList = $this->getIdPList();
 		$preferredIdP = $this->getRecommendedIdP();
 
 		/*
diff --git a/modules/discopower/lib/PowerIdPDisco.php b/modules/discopower/lib/PowerIdPDisco.php
index e49b134f6..42b64be2b 100644
--- a/modules/discopower/lib/PowerIdPDisco.php
+++ b/modules/discopower/lib/PowerIdPDisco.php
@@ -21,11 +21,12 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 	 * The constructor does the parsing of the request. If this is an invalid request, it will
 	 * throw an exception.
 	 *
-	 * @param $discoType  String which identifies the type of discovery service.
+	 * @param array $metadataSets  Array with metadata sets we find remote entities in.
+	 * @param string $instance  The name of this instance of the discovery service.
 	 */
-	public function __construct($discoType) {
+	public function __construct(array $metadataSets, $instance) {
 
-		parent::__construct($discoType);
+		parent::__construct($metadataSets, $instance);
 
 		$this->discoconfig = SimpleSAML_Configuration::getConfig('module_discopower.php');
 
@@ -41,7 +42,7 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 	 * @param $message  The message which should be logged.
 	 */
 	protected function log($message) {
-		SimpleSAML_Logger::info('PowerIdPDisco.' . $this->discoType['type'] . ': ' . $message);
+		SimpleSAML_Logger::info('PowerIdPDisco.' . $this->instance . ': ' . $message);
 	}
 
 	/*
@@ -159,8 +160,8 @@ class sspmod_discopower_PowerIdPDisco extends SimpleSAML_XHTML_IdPDisco {
 		}
 
 		/* No choice made. Show discovery service page. */
-
-		$idpList = $this->idplistStructured($this->filterList($this->metadata->getList($this->discoType['metadata'])));
+		$idpList = $this->getIdPList();
+		$idpList = $this->idplistStructured($this->filterList($idpList));
 		$preferredIdP = $this->getRecommendedIdP();
 
 		$t = new SimpleSAML_XHTML_Template($this->config, 'discopower:disco-tpl.php', 'disco');
diff --git a/modules/discopower/www/disco.php b/modules/discopower/www/disco.php
index 8db43947f..daf51ea78 100644
--- a/modules/discopower/www/disco.php
+++ b/modules/discopower/www/disco.php
@@ -5,7 +5,7 @@ require_once('../www/_include.php');
 $session = SimpleSAML_Session::getInstance();
 
 try {
-	$discoHandler = new sspmod_discopower_PowerIdPDisco('saml20');
+	$discoHandler = new sspmod_discopower_PowerIdPDisco(array('saml20-idp-remote', 'shib13-idp-remote'), 'poweridpdisco');
 } catch (Exception $exception) {
 	/* An error here should be caused by invalid query parameters. */
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'DISCOPARAMS', $exception);
diff --git a/modules/saml2/www/disco.php b/modules/saml2/www/disco.php
index f3e526c5a..c1608dc44 100644
--- a/modules/saml2/www/disco.php
+++ b/modules/saml2/www/disco.php
@@ -4,7 +4,7 @@
  * Builtin IdP discovery service.
  */
 
-$discoHandler = new SimpleSAML_XHTML_IdPDisco('saml20');
+$discoHandler = new SimpleSAML_XHTML_IdPDisco(array('saml20-idp-remote'), 'saml20');
 $discoHandler->handleRequest();
 
 ?>
\ No newline at end of file
diff --git a/www/saml2/sp/idpdisco.php b/www/saml2/sp/idpdisco.php
index 0566025b2..3cfdc17e3 100644
--- a/www/saml2/sp/idpdisco.php
+++ b/www/saml2/sp/idpdisco.php
@@ -5,7 +5,7 @@ require_once('../../_include.php');
 $session = SimpleSAML_Session::getInstance();
 
 try {
-	$discoHandler = new SimpleSAML_XHTML_IdPDisco('saml20');
+	$discoHandler = new SimpleSAML_XHTML_IdPDisco(array('saml20-idp-remote'), 'saml20');
 } catch (Exception $exception) {
 	/* An error here should be caused by invalid query parameters. */
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'DISCOPARAMS', $exception);
diff --git a/www/shib13/sp/idpdisco.php b/www/shib13/sp/idpdisco.php
index ce4c7233b..dfaf3e86b 100644
--- a/www/shib13/sp/idpdisco.php
+++ b/www/shib13/sp/idpdisco.php
@@ -5,7 +5,7 @@ require_once('../../_include.php');
 $session = SimpleSAML_Session::getInstance();
 
 try {
-	$discoHandler = new SimpleSAML_XHTML_IdPDisco('shib13');
+	$discoHandler = new SimpleSAML_XHTML_IdPDisco(array('shib13-idp-remote'), 'shib13');
 } catch (Exception $exception) {
 	/* An error here should be caused by invalid query parameters. */
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'DISCOPARAMS', $exception);
diff --git a/www/wsfed/sp/idpdisco.php b/www/wsfed/sp/idpdisco.php
index ab83d6d6e..1a9e58473 100644
--- a/www/wsfed/sp/idpdisco.php
+++ b/www/wsfed/sp/idpdisco.php
@@ -5,7 +5,7 @@ require_once('../../_include.php');
 $session = SimpleSAML_Session::getInstance();
 
 try {
-	$discoHandler = new SimpleSAML_XHTML_IdPDisco('wsfed');
+	$discoHandler = new SimpleSAML_XHTML_IdPDisco(array('wsfed-idp-remote'), 'wsfed');
 } catch (Exception $exception) {
 	/* An error here should be caused by invalid query parameters. */
 	SimpleSAML_Utilities::fatalError($session->getTrackID(), 'DISCOPARAMS', $exception);
-- 
GitLab