From 8878597d4dff4414e5d97af1c8dfd4a0356da047 Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Mon, 27 Oct 2008 16:13:12 +0000
Subject: [PATCH] Experimental SAML 2.0 SP authentication source.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@949 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/saml2/default-enable         |   3 +
 modules/saml2/lib/Auth/Source/SP.php | 109 +++++++++++++++++++++++++++
 modules/saml2/www/sp/acs.php         |  51 +++++++++++++
 modules/saml2/www/sp/metadata.php    |  36 +++++++++
 4 files changed, 199 insertions(+)
 create mode 100644 modules/saml2/default-enable
 create mode 100644 modules/saml2/lib/Auth/Source/SP.php
 create mode 100644 modules/saml2/www/sp/acs.php
 create mode 100644 modules/saml2/www/sp/metadata.php

diff --git a/modules/saml2/default-enable b/modules/saml2/default-enable
new file mode 100644
index 000000000..25615cb47
--- /dev/null
+++ b/modules/saml2/default-enable
@@ -0,0 +1,3 @@
+This file indicates that the default state of this module
+is enabled. To disable, create a file named disable in the
+same directory as this file.
diff --git a/modules/saml2/lib/Auth/Source/SP.php b/modules/saml2/lib/Auth/Source/SP.php
new file mode 100644
index 000000000..b7217aef9
--- /dev/null
+++ b/modules/saml2/lib/Auth/Source/SP.php
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * SAML 2.0 SP authentication client.
+ *
+ * Example:
+ * 'example-openidp' => array(
+ *   'saml2:SP',
+ *   'idp' => 'https://openidp.feide.no',
+ * ),
+ *
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+class sspmod_saml2_Auth_Source_SP extends SimpleSAML_Auth_Source {
+
+	/**
+	 * The string used to identify our states.
+	 */
+	const STAGE_SENT = 'saml2:SP-SSOSent';
+
+
+	/**
+	 * The key of the AuthId field in the state.
+	 */
+	const AUTHID = 'saml2:AuthId';
+
+
+	/**
+	 * The entity id of this SP.
+	 */
+	private $entityId;
+
+
+	/**
+	 * The entity id of the IdP we connect to.
+	 */
+	private $idp;
+
+
+	/**
+	 * Constructor for SAML 2.0 SP authentication source.
+	 *
+	 * @param array $info  Information about this authentication source.
+	 * @param array $config  Configuration.
+	 */
+	public function __construct($info, $config) {
+		assert('is_array($info)');
+		assert('is_array($config)');
+
+		/* Call the parent constructor first, as required by the interface. */
+		parent::__construct($info, $config);
+
+		if (array_key_exists('entityId', $config)) {
+			$this->entityId = $config['entityId'];
+		} else {
+			$this->entityId = SimpleSAML_Module::getModuleURL('saml2/sp/metadata.php?source=' .
+				urlencode($this->authId) . '&tmp');
+		}
+
+		if (array_key_exists('idp', $config)) {
+			$this->idp = $config['idp'];
+		} else {
+			throw new Exception('TODO: Add support for IdP discovery.');
+		}
+	}
+
+
+	/**
+	 * Start login.
+	 *
+	 * This function saves the information about the login, and redirects to  the IdP.
+	 *
+	 * @param array &$state  Information about the current authentication.
+	 */
+	public function authenticate(&$state) {
+		assert('is_array($state)');
+
+		/* We are going to need the authId in order to retrieve this authentication source later. */
+		$state[self::AUTHID] = $this->authId;
+
+		$id = SimpleSAML_Auth_State::saveState($state, self::STAGE_SENT);
+
+		$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+		$idpMetadata = $metadata->getMetaData($this->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);
+		exit(0);
+	}
+
+
+	/**
+	 * Retrieve the entity id of this SP.
+	 *
+	 * @return string  Entity id of this SP.
+	 */
+	public function getEntityId() {
+
+		return $this->entityId;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/modules/saml2/www/sp/acs.php b/modules/saml2/www/sp/acs.php
new file mode 100644
index 000000000..8cf9f622e
--- /dev/null
+++ b/modules/saml2/www/sp/acs.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Assertion consumer service handler for SAML 2.0 SP authentication client.
+ */
+
+if (!array_key_exists('SAMLResponse', $_POST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing SAMLResponse to AssertionConsumerService');
+}
+
+if (!array_key_exists('RelayState', $_POST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing RelayState to AssertionConsumerService');
+}
+
+$state = SimpleSAML_Auth_State::loadState($_POST['RelayState'], sspmod_saml2_Auth_Source_SP::STAGE_SENT);
+
+/* 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);
+}
+
+$config = SimpleSAML_Configuration::getInstance();
+$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler();
+
+$binding = new SimpleSAML_Bindings_SAML20_HTTPPost($config, $metadata);
+$authnResponse = $binding->decodeResponse($_POST);
+
+$result = $authnResponse->process();
+
+/* Check status code. */
+if($result === FALSE) {
+	/* Not successful. */
+	$statusCode = $authnResponse->findstatus();
+	throw new Exception('Error authenticating: ' . $statusCode);
+}
+
+/* The response should include the entity id of the IdP. */
+$idp = $authnResponse->getIssuer();
+
+/* TODO: Check that IdP is the correct IdP. */
+
+/* TODO: Save NameID & SessionIndex for logout. */
+
+$state['Attributes'] = $authnResponse->getAttributes();
+SimpleSAML_Auth_Source::completeAuth($state);
+
+?>
\ No newline at end of file
diff --git a/modules/saml2/www/sp/metadata.php b/modules/saml2/www/sp/metadata.php
new file mode 100644
index 000000000..a24dfcd06
--- /dev/null
+++ b/modules/saml2/www/sp/metadata.php
@@ -0,0 +1,36 @@
+<?php
+
+if (!array_key_exists('source', $_GET)) {
+	throw new SimpleSAML_Error_BadRequest('Missing source parameter');
+}
+
+$sourceId = $_GET['source'];
+$source = SimpleSAML_Auth_Source::getById($sourceId);
+if ($source === NULL) {
+	throw new SimpleSAML_Error_NotFound('Could not find authentication source with id ' . $sourceId);
+}
+
+if (!($source instanceof sspmod_saml2_Auth_Source_SP)) {
+	throw new SimpleSAML_Error_NotFound('Source isn\'t a SAML 2.0 SP: ' . $sourceId);
+}
+
+$entityId = $source->getEntityId();
+
+$metaArray = array(
+	'AssertionConsumerService' => SimpleSAML_Module::getModuleURL('saml2/sp/acs.php'),
+	);
+
+$metaBuilder = new SimpleSAML_Metadata_SAMLBuilder($entityId);
+$metaBuilder->addMetadataSP20($metaArray);
+
+$config = SimpleSAML_Configuration::getInstance();
+$metaBuilder->addContact('technical', array(
+	'emailAddress' => $config->getValue('technicalcontact_email'),
+	'name' => $config->getValue('technicalcontact_name'),
+	));
+
+$xml = $metaBuilder->getEntityDescriptorText();
+
+echo($xml);
+
+?>
-- 
GitLab