From 49bd764aed8eaaeef55cc94822b52f1747aa847f Mon Sep 17 00:00:00 2001
From: Olav Morken <olav.morken@uninett.no>
Date: Thu, 5 Mar 2009 09:13:51 +0000
Subject: [PATCH] Add base class for username,password&organization
 authentication.

git-svn-id: https://simplesamlphp.googlecode.com/svn/trunk@1371 44740490-163a-0410-bde0-09ae8108e29a
---
 modules/core/lib/Auth/UserPassOrgBase.php | 179 ++++++++++++++++++++++
 modules/core/templates/loginuserpass.php  |  40 ++++-
 modules/core/www/loginuserpassorg.php     |  56 +++++++
 3 files changed, 272 insertions(+), 3 deletions(-)
 create mode 100644 modules/core/lib/Auth/UserPassOrgBase.php
 create mode 100644 modules/core/www/loginuserpassorg.php

diff --git a/modules/core/lib/Auth/UserPassOrgBase.php b/modules/core/lib/Auth/UserPassOrgBase.php
new file mode 100644
index 000000000..ea45fba57
--- /dev/null
+++ b/modules/core/lib/Auth/UserPassOrgBase.php
@@ -0,0 +1,179 @@
+<?php
+
+/**
+ * Helper class for username/password/organization authentication.
+ *
+ * This helper class allows for implementations of username/password/organization
+ * authentication by implementing two functions:
+ * - login($username, $password, $organization)
+ * - getOrganizations()
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+abstract class sspmod_core_Auth_UserPassOrgBase extends SimpleSAML_Auth_Source {
+
+
+	/**
+	 * The string used to identify our states.
+	 */
+	const STAGEID = 'sspmod_core_Auth_UserPassOrgBase.state';
+
+
+	/**
+	 * The key of the AuthId field in the state.
+	 */
+	const AUTHID = 'sspmod_core_Auth_UserPassOrgBase.AuthId';
+
+
+	/**
+	 * Constructor for this authentication source.
+	 *
+	 * All subclasses who implement their own constructor must call this constructor before
+	 * using $config for anything.
+	 *
+	 * @param array $info  Information about this authentication source.
+	 * @param array &$config  Configuration for this authentication source.
+	 */
+	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);
+	}
+
+
+	/**
+	 * Initialize login.
+	 *
+	 * This function saves the information about the login, and redirects to a
+	 * login page.
+	 *
+	 * @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::STAGEID);
+
+		$url = SimpleSAML_Module::getModuleURL('core/loginuserpassorg.php');
+		$params = array('AuthState' => $id);
+		SimpleSAML_Utilities::redirect($url, $params);
+	}
+
+
+	/**
+	 * Attempt to log in using the given username, password and organization.
+	 *
+	 * On a successful login, this function should return the users attributes. On failure,
+	 * it should throw an exception/error. If the error was caused by the user entering the wrong
+	 * username or password, a SimpleSAML_Error_Error('WRONGUSERPASS') should be thrown.
+	 *
+	 * Note that both the username and the password are UTF-8 encoded.
+	 *
+	 * @param string $username  The username the user wrote.
+	 * @param string $password  The password the user wrote.
+	 * @param string $organization  The id of the organization the user chose.
+	 * @return array  Associative array with the user's attributes.
+	 */
+	abstract protected function login($username, $password, $organization);
+
+
+	/**
+	 * Retrieve list of organizations.
+	 *
+	 * The list of organizations is an associative array. The key of the array is the
+	 * id of the organization, and the value is the description. The value can be another
+	 * array, in which case that array is expected to contain language-code to
+	 * description mappings.
+	 *
+	 * @return array  Associative array with the organizations.
+	 */
+	abstract protected function getOrganizations();
+
+
+	/**
+	 * Handle login request.
+	 *
+	 * This function is used by the login form (core/www/loginuserpassorg.php) when the user
+	 * enters a username and password. On success, it will not return. On wrong
+	 * username/password failure, it will return the error code. Other failures will throw an
+	 * exception.
+	 *
+	 * @param string $authStateId  The identifier of the authentication state.
+	 * @param string $username  The username the user wrote.
+	 * @param string $password  The password the user wrote.
+	 * @param string $organization  The id of the organization the user chose.
+	 * @return string Error code in the case of an error.
+	 */
+	public static function handleLogin($authStateId, $username, $password, $organization) {
+		assert('is_string($authStateId)');
+		assert('is_string($username)');
+		assert('is_string($password)');
+		assert('is_string($organization)');
+
+		/* Retrieve the authentication state. */
+		$state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID);
+
+		/* Find authentication source. */
+		assert('array_key_exists(self::AUTHID, $state)');
+		$source = SimpleSAML_Auth_Source::getById($state[self::AUTHID]);
+		if ($source === NULL) {
+			throw new Exception('Could not find authentication source with id ' . $state[self::AUTHID]);
+		}
+
+
+		try {
+			/* Attempt to log in. */
+			$attributes = $source->login($username, $password, $organization);
+		} catch (SimpleSAML_Error_Error $e) {
+			/* An error occured during login. Check if it is because of the wrong
+			 * username/password - if it is, we pass that error up to the login form,
+			 * if not, we let the generic error handler deal with it.
+			 */
+			if ($e->getErrorCode() === 'WRONGUSERPASS') {
+				return 'WRONGUSERPASS';
+			}
+
+			/* Some other error occured. Rethrow exception and let the generic error
+			 * handler deal with it.
+			 */
+			throw $e;
+		}
+
+		$state['Attributes'] = $attributes;
+		SimpleSAML_Auth_Source::completeAuth($state);
+	}
+
+
+	/**
+	 * Get available organizations.
+	 *
+	 * This function is used by the login form to get the available organizations.
+	 *
+	 * @param string $authStateId  The identifier of the authentication state.
+	 * @return array  Array of organizations.
+	 */
+	public static function listOrganizations($authStateId) {
+		assert('is_string($authStateId)');
+
+		/* Retrieve the authentication state. */
+		$state = SimpleSAML_Auth_State::loadState($authStateId, self::STAGEID);
+
+		/* Find authentication source. */
+		assert('array_key_exists(self::AUTHID, $state)');
+		$source = SimpleSAML_Auth_Source::getById($state[self::AUTHID]);
+		if ($source === NULL) {
+			throw new Exception('Could not find authentication source with id ' . $state[self::AUTHID]);
+		}
+
+		return $source->getOrganizations();
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/modules/core/templates/loginuserpass.php b/modules/core/templates/loginuserpass.php
index cb95d1743..108c9b568 100644
--- a/modules/core/templates/loginuserpass.php
+++ b/modules/core/templates/loginuserpass.php
@@ -31,17 +31,51 @@ if ($this->data['errorcode'] !== NULL) {
 	<form action="?" method="post" name="f">
 	<table>
 		<tr>
-			<td rowspan="2"><img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/pencil.png" alt="" /></td>
+			<td rowspan="3"><img src="/<?php echo $this->data['baseurlpath']; ?>resources/icons/pencil.png" alt="" /></td>
 			<td style="padding: .3em;"><?php echo $this->t('{login:username}'); ?></td>
 			<td><input type="text" id="username" tabindex="1" name="username" value="<?php echo htmlspecialchars($this->data['username']); ?>" /></td>
-			<td style="padding: .4em;" rowspan="2">
-				<input type="submit" tabindex="3" value="<?php echo $this->t('{login:login_button}'); ?>" />
+			<td style="padding: .4em;" rowspan="3">
+				<input type="submit" tabindex="4" value="<?php echo $this->t('{login:login_button}'); ?>" />
 			</td>
 		</tr>
 		<tr>
 			<td style="padding: .3em;"><?php echo $this->t('{login:password}'); ?></td>
 			<td><input id="password" type="password" tabindex="2" name="password" /></td>
 		</tr>
+
+<?php
+if (array_key_exists('organizations', $this->data)) {
+?>
+		<tr>
+			<td style="padding: .3em;"><?php echo $this->t('{login:organization}'); ?></td>
+			<td><select name="organization" tabindex="3">
+<?php
+if (array_key_exists('selectedOrg', $this->data)) {
+	$selectedOrg = $this->data['selectedOrg'];
+} else {
+	$selectedOrg = NULL;
+}
+
+foreach ($this->data['organizations'] as $orgId => $orgDesc) {
+	if (is_array($orgDesc)) {
+		$orgDesc = $this->t($orgDesc);
+	}
+
+	if ($orgId === $selectedOrg) {
+		$selected = 'selected="selected" ';
+	} else {
+		$selected = '';
+	}
+
+	echo '<option ' . $selected . 'value="' . htmlspecialchars($orgId) . '">' . htmlspecialchars($orgDesc) . '</option>';
+}
+?>
+			</select></td>
+		</tr>
+<?php
+}
+?>
+
 	</table>
 
 <?php
diff --git a/modules/core/www/loginuserpassorg.php b/modules/core/www/loginuserpassorg.php
new file mode 100644
index 000000000..27cb90206
--- /dev/null
+++ b/modules/core/www/loginuserpassorg.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * This page shows a username/password/organization login form, and passes information from
+ * itto the sspmod_core_Auth_UserPassBase class, which is a generic class for
+ * username/password/organization authentication.
+ *
+ * @author Olav Morken, UNINETT AS.
+ * @package simpleSAMLphp
+ * @version $Id$
+ */
+
+if (!array_key_exists('AuthState', $_REQUEST)) {
+	throw new SimpleSAML_Error_BadRequest('Missing AuthState parameter.');
+}
+$authStateId = $_REQUEST['AuthState'];
+
+if (array_key_exists('username', $_REQUEST)) {
+	$username = $_REQUEST['username'];
+} else {
+	$username = '';
+}
+
+if (array_key_exists('password', $_REQUEST)) {
+	$password = $_REQUEST['password'];
+} else {
+	$password = '';
+}
+
+if (array_key_exists('organization', $_REQUEST)) {
+	$organization = $_REQUEST['organization'];
+} else {
+	$organization = NULL;
+}
+
+if (!empty($organization) && (!empty($username) || !empty($password))) {
+	/* Organization and either username or password set - attempt to log in. */
+	$errorCode = sspmod_core_Auth_UserPassOrgBase::handleLogin($authStateId, $username, $password, $organization);
+} else {
+	$errorCode = NULL;
+}
+
+$organizations = sspmod_core_Auth_UserPassOrgBase::listOrganizations($authStateId);
+
+$globalConfig = SimpleSAML_Configuration::getInstance();
+$t = new SimpleSAML_XHTML_Template($globalConfig, 'core:loginuserpass.php');
+$t->data['stateparams'] = array('AuthState' => $authStateId);
+$t->data['selectedOrg'] = $organization;
+$t->data['organizations'] = $organizations;
+$t->data['username'] = $username;
+$t->data['errorcode'] = $errorCode;
+$t->show();
+exit();
+
+
+?>
\ No newline at end of file
-- 
GitLab