From 0f810a7abab8ff2eeecbb63959b2f5836b71a8f2 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Mon, 13 Jul 2009 09:03:58 +0000
Subject: [PATCH] saml2:SP: Add discovery service support.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1584 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/saml2/lib/Auth/Source/SP.php | 63 ++++++++++++++++++++++++++--
 modules/saml2/www/disco.php          | 10 +++++
 modules/saml2/www/sp/discoresp.php   | 28 +++++++++++++
 3 files changed, 97 insertions(+), 4 deletions(-)
 create mode 100644 modules/saml2/www/disco.php
 create mode 100644 modules/saml2/www/sp/discoresp.php

diff --git a/modules/saml2/lib/Auth/Source/SP.php b/modules/saml2/lib/Auth/Source/SP.php
index 6dc6ce7fa..3af1c5d02 100644
--- a/modules/saml2/lib/Auth/Source/SP.php
+++ b/modules/saml2/lib/Auth/Source/SP.php
@@ -15,10 +15,17 @@
 class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 
 	/**
-	 * The string used to identify our states.
+	 * The identifier for the stage where we have sent a discovery service request.
+	 */
+	const STAGE_DISCO = 'saml2:SP-DiscoSent';
+
+
+	/**
+	 * The identifier for the stage where we have sent a SSO request.
 	 */
 	const STAGE_SENT = 'saml2:SP-SSOSent';
 
+
 	/**
 	 * The string used to identify our logout state.
 	 */
@@ -82,7 +89,7 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 		if (array_key_exists('idp', $config)) {
 			$this->idp = $config['idp'];
 		} else {
-			throw new Exception('TODO: Add support for IdP discovery.');
+			$this->idp = NULL;
 		}
 	}
 
@@ -100,17 +107,65 @@ class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
 		/* We are going to need the authId in order to retrieve this authentication source later. */
 		$state[self::AUTHID] = $this->authId;
 
+		if ($this->idp === NULL) {
+			$this->initDisco($state);
+		}
+
+		$this->initSSO($this->idp, $state);
+	}
+
+
+	/**
+	 * Send authentication request to specified IdP.
+	 *
+	 * @param string $idp  The IdP we should send the request to.
+	 * @param array $state  Our state array.
+	 */
+	public function initDisco($state) {
+		assert('is_array($state)');
+
+		$id = SimpleSAML_Auth_State::saveState($state, self::STAGE_DISCO);
+
+		$config = SimpleSAML_Configuration::getInstance();
+
+		$discoURL = $config->getString('idpdisco.url.saml20', NULL);
+		if ($discoURL === NULL) {
+			/* Fallback to internal discovery service. */
+			$discoURL = SimpleSAML_Module::getModuleURL('saml2/disco.php');
+		}
+
+		$returnTo = SimpleSAML_Module::getModuleURL('saml2/sp/discoresp.php');
+		$returnTo = SimpleSAML_Utilities::addURLparameter($returnTo, array('AuthID' => $id));
+
+		SimpleSAML_Utilities::redirect($discoURL, array(
+			'entityID' => $this->entityId,
+			'return' => $returnTo,
+			'returnIDParam' => 'idpentityid')
+			);
+	}
+
+
+	/**
+	 * Send authentication request to specified IdP.
+	 *
+	 * @param string $idp  The IdP we should send the request to.
+	 * @param array $state  Our state array.
+	 */
+	public function initSSO($idp, $state) {
+		assert('is_string($idp)');
+		assert('is_array($state)');
+
 		$id = SimpleSAML_Auth_State::saveState($state, self::STAGE_SENT);
 
 		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
-		$idpMetadata = $metadata->getMetaData($this->idp, 'saml20-idp-remote');
+		$idpMetadata = $metadata->getMetaData($idp, 'saml20-idp-remote');
 
 		$config = SimpleSAML_Configuration::getInstance();
 		$sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata);
 		$req = $sr->generate($this->entityId, $idpMetadata['SingleSignOnService']);
 
 		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
-		$httpredirect->sendMessage($req, $this->entityId, $this->idp, $id);
+		$httpredirect->sendMessage($req, $this->entityId, $idp, $id);
 		exit(0);
 	}
 
diff --git a/modules/saml2/www/disco.php b/modules/saml2/www/disco.php
new file mode 100644
index 000000000..f3e526c5a
--- /dev/null
+++ b/modules/saml2/www/disco.php
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * Builtin IdP discovery service.
+ */
+
+$discoHandler = new SimpleSAML_XHTML_IdPDisco('saml20');
+$discoHandler->handleRequest();
+
+?>
\ No newline at end of file
diff --git a/modules/saml2/www/sp/discoresp.php b/modules/saml2/www/sp/discoresp.php
new file mode 100644
index 000000000..0ce9dd907
--- /dev/null
+++ b/modules/saml2/www/sp/discoresp.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Handler for response from IdP discovery service.
+ */
+
+if (!array_key_exists('AuthID', $_REQUEST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing AuthID to discovery service response handler');
+}
+
+if (!array_key_exists('idpentityid', $_REQUEST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing idpentityid to discovery service response handler');
+}
+
+$state = SimpleSAML_Auth_State::loadState($_REQUEST['AuthID'], sspmod_saml2_Auth_Source_SP::STAGE_DISCO);
+
+/* Find authentication source. */
+assert('array_key_exists(sspmod_saml2_Auth_Source_SP::AUTHID, $state)');
+$sourceId = $state[sspmod_saml2_Auth_Source_SP::AUTHID];
+
+$source = SimpleSAML_Auth_Source::getById($sourceId);
+if ($source === NULL) {
+	throw new Exception('Could not find authentication source with id ' . $sourceId);
+}
+
+$source->initSSO($_REQUEST['idpentityid'], $state);
+
+?>
\ No newline at end of file
-- 
GitLab