From 22e1567abbe3a0ae01d4113c4fa4a6bbfbe1c296 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20=C3=85kre=20Solberg?= <andreas.solberg@uninett.no>
Date: Fri, 12 Oct 2007 08:35:07 +0000
Subject: [PATCH] Added SAML 2.0 IdP Discovery service. Read more here:
 http://rnd.feide.no/2007/10/12/saml-20-idp-discovery-service-implemented/

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@23 44740490-163a-0410-bde0-09ae8108e29a
---
 config/config-template.php                  |  6 ++
 lib/SimpleSAML/Session.php                  |  8 ++
 lib/SimpleSAML/XML/MetaDataStore.php        |  9 +++
 lib/SimpleSAML/XML/SAML20/AuthnResponse.php |  1 +
 metadata-templates/saml20-idp-remote.php    |  4 +
 templates/selectidp.php                     | 90 +++++++++++++++++++++
 www/saml2/sp/idpdisco.php                   | 64 +++++++++++++++
 www/saml2/sp/initSLO.php                    |  3 +-
 www/saml2/sp/initSSO.php                    | 17 +++-
 9 files changed, 197 insertions(+), 5 deletions(-)
 create mode 100644 templates/selectidp.php
 create mode 100644 www/saml2/sp/idpdisco.php

diff --git a/config/config-template.php b/config/config-template.php
index dc051d6af..c99b833f6 100644
--- a/config/config-template.php
+++ b/config/config-template.php
@@ -32,6 +32,12 @@ $config = array (
 	/*
 	 * Default IdPs. If you do not enter an idpentityid in the SSO initialization endpoints,
 	 * the default IdP configured here will be used.
+	 *
+	 * To enable the SAML 2.0 IdP Discovery service for a SAML 2.0 SP, you need to set the
+	 * default-saml20-idp to be null, like this:
+	 *
+	 * 		'default-saml20-idp'	=> null,
+	 *
 	 */
 	'default-saml20-idp'	=> 'sam.feide.no',
 	'default-shib13-idp'	=> 'urn:mace:switch.ch:aaitest:dukono.switch.ch',
diff --git a/lib/SimpleSAML/Session.php b/lib/SimpleSAML/Session.php
index db523b019..814be119f 100644
--- a/lib/SimpleSAML/Session.php
+++ b/lib/SimpleSAML/Session.php
@@ -37,6 +37,7 @@ class SimpleSAML_Session {
 	private $shibauthreq = null;
 	
 	private $authnresponse = null;
+	private $idp = null;
 	
 	private $logoutrequest = null;
 	
@@ -147,6 +148,13 @@ class SimpleSAML_Session {
 		return $this->authnresponse;
 	}
 	
+	public function setIdP($idp) {
+		$this->idp = $idp;
+	}
+	public function getIdP() {
+		return $this->idp;
+	}
+	
 	public function setLogoutRequest(SimpleSAML_XML_SAML20_LogoutRequest $lr) {
 		$this->logoutrequest = $lr;
 	}
diff --git a/lib/SimpleSAML/XML/MetaDataStore.php b/lib/SimpleSAML/XML/MetaDataStore.php
index 1ae3bc1a0..dd426d327 100644
--- a/lib/SimpleSAML/XML/MetaDataStore.php
+++ b/lib/SimpleSAML/XML/MetaDataStore.php
@@ -108,6 +108,15 @@ class SimpleSAML_XML_MetaDataStore {
 		return $this->metadata[$set][$entityid];
 	}
 	
+	public function getList($set = 'saml20-idp-remote') {
+		if (!isset($this->metadata[$set])) {
+			$this->load($set);
+		}
+		return $this->metadata[$set];
+	}
+	
+	
+	
 	public function getGenerated($property, $set = 'saml20-sp-hosted') {
 		
 		$baseurl = SimpleSAML_Utilities::selfURLhost() . '/' . $this->configuration->getValue('baseurlpath');
diff --git a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
index 96c52e07b..9bc4bf8f4 100644
--- a/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
+++ b/lib/SimpleSAML/XML/SAML20/AuthnResponse.php
@@ -161,6 +161,7 @@ class SimpleSAML_XML_SAML20_AuthnResponse extends SimpleSAML_XML_AuthnResponse {
 		$session->setNameID($nameid['NameID']);
 		$session->setNameIDFormat($nameid['Format']);
 		$session->setSessionIndex($this->getSessionIndex());
+		$session->setIdP($this->getIssuer());
 		/*
 		$nameID["NameID"] = $node->nodeValue;
 		
diff --git a/metadata-templates/saml20-idp-remote.php b/metadata-templates/saml20-idp-remote.php
index b6436dadd..1062e92c3 100644
--- a/metadata-templates/saml20-idp-remote.php
+++ b/metadata-templates/saml20-idp-remote.php
@@ -25,6 +25,8 @@ $metadata = array(
 	 * Metadata for Feide's test environment.
 	 */
 	'max.feide.no' =>  array(
+		'name'					=>	'Test environment of Feide',
+		'description'			=> 'max.feide.no: the test environment of Feide.',
 		'SingleSignOnService'	=>	'https://max.feide.no/amserver/SSORedirect/metaAlias/idp',
 		'SingleLogoutService'	=>	'https://max.feide.no/amserver/IDPSloRedirect/metaAlias/idp',
 		'certFingerprint'		=>	'3fa158e8abfd4b5203315b08c0b791b6ee4715f6',
@@ -35,6 +37,8 @@ $metadata = array(
 	 * Metadata for Feide's production environment.
 	 */
 	'sam.feide.no' =>  array( 
+		'name'					=>	'Feide',
+		'description'			=> 'Authenticate with your identity from a school or university in Norway.',
 		'SingleSignOnService'	=>	'https://sam.feide.no/amserver/SSORedirect/metaAlias/idp',
 		'SingleLogoutService'	=>	'https://sam.feide.no/amserver/IDPSloRedirect/metaAlias/idp',
 		'certFingerprint'		=>	'3a:e7:d3:d3:06:ba:57:fd:7f:62:6a:4b:a8:64:b3:4a:53:d9:5d:d0',
diff --git a/templates/selectidp.php b/templates/selectidp.php
new file mode 100644
index 000000000..c1174f709
--- /dev/null
+++ b/templates/selectidp.php
@@ -0,0 +1,90 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<title><?php echo $data['header']; ?></title>
+
+<style type="text/css">
+
+/* these styles are in the head of this page because this is a unique page */
+
+/* THE BIG GUYS */
+* {margin:0;padding:0}
+body {text-align:center;padding: 20px 0;background: #222;color:#333;font:83%/1.5 arial,tahoma,verdana,sans-serif}
+img {border:none;display:block}
+hr {margin: 1em 0;background:#eee;height:1px;color:#eee;border:none;clear:both}
+
+/* LINKS */
+a,a:link,a:link,a:link,a:hover {font-weight:bold;background:transparent;text-decoration:underline;cursor:pointer} 
+a:link {color:#c00} 
+a:visited {color:#999} 
+a:hover,a:active {color:#069} 
+
+/* LISTS */
+ul {margin: .3em 0 1.5em 2em}
+	ul.related {margin-top:-1em}
+li {margin-left:2em}
+dt {font-weight:bold}
+#wrap {border: 1px solid #fff;position:relative;background:#fff;width:600px;margin: 0 auto;text-align:left}
+#header {background: #666 url("/<?php echo $data['baseurlpath']; ?>resources/sprites.gif") repeat-x 0 100%;margin: 0 0 25px;padding: 0 0 8px}
+#header h1 {color:#fff;font-size: 145%;padding:20px 20px 12px}
+#poweredby {width:96px;height:63px;position:absolute;top:0;right:0}
+#content {padding: 0 20px}
+
+/* TYPOGRAPHY */
+p, ul, ol {margin: 0 0 1.5em}
+h1, h2, h3, h4, h5, h6 {letter-spacing: -1px;font-family: arial,verdana,sans-serif;margin: 1.2em 0 .3em;color:#000;border-bottom: 1px solid #eee;padding-bottom: .1em}
+h1 {font-size: 196%;margin-top:0;border:none}
+h2 {font-size: 136%}
+h3 {font-size: 126%}
+h4 {font-size: 116%}
+h5 {font-size: 106%}
+h6 {font-size: 96%}
+
+.old {text-decoration:line-through}
+</style>
+</head>
+<body>
+
+<div id="wrap">
+
+	<div id="header">
+		<h1>SAML 2.0 IdP Discovery Service</h1>
+		<div id="poweredby"><img src="/<?php echo $data['baseurlpath']; ?>resources/icons/bino.png" alt="Bino" /></div>
+	</div>
+	
+	<div id="content">
+
+		<h2><?php if (isset($data['header'])) { echo $data['header']; } else { echo "Select your IdP"; } ?></h2>
+		
+		<p>Please select the identity provider where you want to authenticate:</p>
+		
+		
+		<?php
+		
+		foreach ($data['idplist'] AS $idpentry) {
+		
+			echo '<h3>' . $idpentry['name'] . '</h3>';
+			echo '<p>' . $idpentry['description'] . '<br />';
+			echo '[ <a href="' . $data['urlpattern'] . $idpentry['entityid'] . '">Select this IdP</a>]</p>';
+		
+		}
+		
+		
+		?>
+
+		
+
+
+		<hr />
+		
+		Copyright &copy; 2007 <a href="http://rnd.feide.no/">Feide RnD</a>
+		
+		<hr />
+	
+	</div>
+
+</div>
+
+</body>
+</html>
diff --git a/www/saml2/sp/idpdisco.php b/www/saml2/sp/idpdisco.php
new file mode 100644
index 000000000..d7af8c2fd
--- /dev/null
+++ b/www/saml2/sp/idpdisco.php
@@ -0,0 +1,64 @@
+<?php
+
+require_once('../../_include.php');
+
+
+require_once('SimpleSAML/Utilities.php');
+require_once('SimpleSAML/Session.php');
+require_once('SimpleSAML/XHTML/Template.php');
+require_once('SimpleSAML/XML/MetaDataStore.php');
+require_once('SimpleSAML/XML/SAML20/AuthnRequest.php');
+//require_once('SimpleSAML/XML/SAML20/AuthnResponse.php');
+require_once('SimpleSAML/Bindings/SAML20/HTTPRedirect.php');
+//require_once('SimpleSAML/Bindings/SAML20/HTTPPost.php');
+
+session_start();
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = new SimpleSAML_XML_MetaDataStore($config);
+
+
+$session = SimpleSAML_Session::getInstance();
+		
+try {
+
+	if (!isset($_GET['entityID'])) throw new Exception('Missing parameter: entityID');
+	if (!isset($_GET['return'])) throw new Exception('Missing parameter: return');
+	if (!isset($_GET['returnIDParam'])) throw new Exception('Missing parameter: returnIDParam');
+
+	$spentityid = $_GET['entityID'];
+	$return = $_GET['return'];
+	$returnidparam = $_GET['returnIDParam'];
+	
+} catch (Exception $exception) {
+
+	$et = new SimpleSAML_XHTML_Template($config, 'error.php');
+	$et->data['message'] = 'Error getting required parameters for IdP Discovery Service';	
+	$et->data['e'] = $exception;	
+	$et->show();
+	exit(0);
+}
+
+
+if (isset($_GET['idpentityid'])) {
+
+	$idpentityid = $_GET['idpentityid'];
+
+	$returnurl = SimpleSAML_Utilities::addURLparameter($return, $returnidparam . '=' . $idpentityid);
+	header('Location: ' . $returnurl);
+	exit(0);
+}
+
+
+$idplist = $metadata->getList('saml20-idp-remote');
+
+
+$t = new SimpleSAML_XHTML_Template($config, 'selectidp.php');
+$t->data['header'] = 'Select your identity provider';
+$t->data['idplist'] = $idplist;
+$t->data['urlpattern'] = htmlentities(SimpleSAML_Utilities::selfURL() . '&idpentityid=');
+$t->show();
+
+
+
+?>
\ No newline at end of file
diff --git a/www/saml2/sp/initSLO.php b/www/saml2/sp/initSLO.php
index f2e4a8f05..0d3f7ebd0 100644
--- a/www/saml2/sp/initSLO.php
+++ b/www/saml2/sp/initSLO.php
@@ -17,7 +17,8 @@ $metadata = new SimpleSAML_XML_MetaDataStore($config);
 
 $session = SimpleSAML_Session::getInstance();
 
-$idpentityid = isset($_GET['idpentityid']) ? $_GET['idpentityid'] : $config->getValue('default-saml20-idp') ;
+$idpentityid = $session->getIdP();
+//	isset($_GET['idpentityid']) ? $_GET['idpentityid'] : $config->getValue('default-saml20-idp') ;
 $spentityid = isset($_GET['spentityid']) ? $_GET['spentityid'] : $metadata->getMetaDataCurrentEntityID();
 
 
diff --git a/www/saml2/sp/initSSO.php b/www/saml2/sp/initSSO.php
index 183ba2901..4c24f7998 100644
--- a/www/saml2/sp/initSSO.php
+++ b/www/saml2/sp/initSSO.php
@@ -36,13 +36,23 @@ try {
 
 if (!isset($session) || !$session->isValid() ) {
 	
+	
+	if ($idpentityid == null) {
+	
+		$returnURL = urlencode(SimpleSAML_Utilities::selfURL());
+		$discservice = '/' . $config->getValue('baseurlpath') . 'saml2/sp/idpdisco.php?entityID=' . $spentityid . 
+			'&return=' . $returnURL . '&returnIDParam=idpentityid';
+		header('Location: ' . $discservice);
+		exit(0);
+		
+	}
+	
+	
 	try {
 		$sr = new SimpleSAML_XML_SAML20_AuthnRequest($config, $metadata);
 	
 		$req = $sr->generate($spentityid);
 		
-	
-		
 		$httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata);
 		
 		$relayState = SimpleSAML_Utilities::selfURL();
@@ -65,11 +75,10 @@ if (!isset($session) || !$session->isValid() ) {
 	}
 
 } else {
-
 	
 	
 	$relaystate = $session->getRelayState();
-	
+		
 	if (isset($relaystate) && !empty($relaystate)) {
 		header('Location: ' . $relaystate );
 	} else {
-- 
GitLab