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