Commit 115d3e48 authored by Nigel McNie's avatar Nigel McNie Committed by Nigel McNie
Browse files

Handle sessions properly now. They're only given out if the user logs in,

and then persist for the user even if they log out (note: session != login
session!).

This means that bots crawling the pages won't get sessions, which is a good
thing.

Made the authentication logic more solid too. The transient login page is
now implemented to a basic level.
parent 5d7ceb51
......@@ -26,46 +26,34 @@
defined('INTERNAL') || die();
// Constants for authentication
define('AUTH_PASSED', 1);
define('AUTH_FAILED', 2);
/** Exception - unknown user */
class AuthUnknownUserException extends Exception {}
/**
* Base authentication class
*
* Institutions are tied to a particular plugin
* Base authentication class. Provides a common interface with which
* authentication can be carried out for system users.
*/
abstract class Auth {
/**
* Given a username, password and institution, attempts to log the use in.
*
* This method should return one of two values:
*
* <ul>
* <li>AUTH_PASSED - the user has provided correct credentials</li>
* <li>AUTH_FAILED - the user has provided incorrect credentials</li>
* </ul>
*
* @param string $username The username to attempt to authenticate
* @param string $password The password to use for the attempt
* @param string $institute The institution the user belongs to
* @return bool Whether the authentication was successful
* @throws AuthUnknownUserException If the user is unknown to the
* authentication method
*/
public static abstract function authenticate_user_account($username, $password, $institute);
/**
* Given a username, returns a hash of information about a user.
*
* Should throw an exception if the authentication method doesn't know
* about the user, since this method should only be called after a
* successful authentication method (so we know the user exists)
*
* @param string $username The username to look up information for
* @return array The information for the user
* @throws AuthUnknownUserException
* @throws AuthUnknownUserException If the user is unknown to the
* authentication method
*/
public static abstract function get_user_info ($username);
......@@ -80,127 +68,211 @@ abstract class Auth {
}
/**
* Performs an authentication attempt, by cycling through all of the available
* authentication methods allowed for the user.
*
*/
function authenticate_user($username, $password, $institute) {
//
// Implementation:
//
// Well, institutes are tied to a particular authentication method - ONE particular authentication method
// And users are tied to an institution
// So they have ONE go at authentication, not like the rubbish mentioned in the technical spec.
// So, the algorithm should be roughly:
//
// based on the institute, get the auth method
// include the auth method implementation
// try {
// authenticate the user using username, password
// }
// catch (whothehellisthisexception) {
// return appropriate message
// }
// catch (wrongpasswordexception) {
// return appropriate message
// }
//
// all happy, return OK
//
//
// So, how is this function called exactly?
//
// Well, the login pages are generally completely transient, which means that once this is
// called successfully, the get and post information needs to be sent back to where we came
// from, which is the page name itself.
//
// Basically, in init.php or similar:
//
// do_authentication();
//
// do_authentication:
// if user logged in (check session data)
// if session timed out or otherwise invalid
// display login form
// else
// all good, continue
// elseif has correct guest key
// all good
// else
// display login form
//
// if user logged in (check session data) == this function
}
/**
* So how will this work? written above.
* try {
* authenticate();
* }
* catch (AuthenticationException $e) {
* // can't authenticate again, something bad happened
* // fall through to the default exception handler where this is a default, or otherwise exit the script
* Handles authentication by setting up a session for a user if they are logged
* in.
*
* This function combined with the Session class is smart - if the user is not
* logged in then they do not get a session, which prevents simple curl hits
* or search engine crawls to a page from getting sessions they won't use.
*
* Once the user has a session, they keep it even if the log out, so it can
* be reused. The session does expire, but the expiry time is typically a week
* or more.
*/
function auth_setup () {
// auth stuff is run before init.php finishes, and index.php does the check
// for install. So this function might need to detect not installed and skip
// logging in
if (!session_id()) {
@session_start();
if (!session_id()) {
throw new AuthException('Could not start a session. Perhaps '
. 'something has been output before the page begins?');
}
}
$s =& $_SESSION;
$username = clean_requestdata('login_username', PARAM_ALPHA);
$password = clean_requestdata('login_password', PARAM_ALPHA);
global $SESSION;
$time = time();
// If the system is not installed, let the user through in the hope that
// they can fix this little problem :)
if (!get_config('version')) {
// Not installed, so let the user through
log_dbg('system not installed, letting user through');
return;
}
if (isset($s['logged_in']) && $s['username'] != '') {
log_dbg('user logged in, fine just fine (user is ' . $s['username']);
return;
}
if ($username != '' && $password != '') {
log_dbg('auth attempt with username "' . $username . '" and password "' . $password . '"');
if (!auth_user($username, $password, $institution)) {
auth_draw_login_form();
// Check the time that the session is set to log out. If the user does
// not have a session, this time will be 0.
$sessionlogouttime = $SESSION->get('logout_time');
if ($sessionlogouttime > $time) {
if (isset($_GET['logout'])) {
log_dbg('logging user ' . $SESSION->get('username') . ' out');
$SESSION->logout();
$SESSION->add_ok_msg('You have been logged out successfully');
header('Location: ' . get_config('wwwroot'));
exit;
}
// Login went fine
return;
}
if (false /* guest key is available */) {
return;
// The session is still active, so continue it.
log_dbg('session still active from previous time');
return $SESSION->renew();
}
if (false /* site config claims public access ok */) {
return;
else if ($sessionlogouttime > 0) {
// The session timed out
log_dbg('session timed out');
$SESSION->logout();
$SESSION->add_info_msg(get_string('sessiontimedout', 'auth'));
// @todo<nigel>: if page is public, no need to show the login page again
auth_draw_login_page();
exit;
}
else {
log_dbg('dunno who this is, better get them to tell us');
auth_draw_login_form();
exit;
// There is no session, so we check to see if one needs to be started.
// First, check if the page is public or the site is configured to be public.
// @todo<nigel>: implement this :)
if (false) {
// No need to hand out a session for such pages
return;
}
$username = (isset($_POST['login_username'])) ? $_POST['login_username'] : '';//clean_requestdata('login_username', PARAM_ALPHA);
if ($username != '') {
log_dbg('auth details supplied, attempting to log user in');
$password = clean_requestdata('login_password', PARAM_ALPHA);
$institution = clean_requestdata('login_institution', PARAM_INT);
$authtype = auth_get_authtype_for_institution($institution);
require('auth/' . $authtype . '/auth.php');
$authclass = 'Auth_' . ucfirst($authtype);
try {
if (eval("return $authclass::authenticate_user_account(\$username, \$password, \$institution);")) {
log_dbg('user ' . $username . ' logged in OK');
$USER = eval("return $authclass::get_user_info(\$username);");
$SESSION->login($USER);
$USER->logout_time = $SESSION->get('logout_time');
return $USER;
}
else {
// Login attempt failed
log_dbg('login attempt FAILED');
$SESSION->add_err_msg(get_string('loginfailed', 'auth'));
auth_draw_login_page();
exit;
}
}
catch (AuthUnknownUserException $e) {
log_dbg('unknown user ' . $username);
$SESSION->add_err_msg(get_string('loginfailed', 'auth'));
auth_draw_login_page();
exit;
}
}
else {
// No login attempt, no session and page is private
log_dbg('no session or old session, and page is private');
auth_draw_login_page();
exit;
}
}
}
function auth_user ($username, $password, $institution) {
log_dbg('login attempt from user ' . $username);
return true;
/**
* Given an institution, returns the authentication method used by it.
*
* @return string
* @todo<nigel>: Currently, the system doesn't have a concept of institution
* at the database level, so the internal authentication method is assumed.
*/
function auth_get_authtype_for_institution($institution) {
return 'internal';
}
function auth_draw_login_form() {
/**
* Creates and displays the transient login page
*
*/
function auth_draw_login_page() {
$smarty = smarty();
require_once('form.php');
$elements = array(
'login' => array(
'type' => 'fieldset',
'legend' => get_string('Login', 'mahara'),
'elements' => array(
'login_username' => array(
'type' => 'text',
'title' => get_string('Username', 'mahara'),
'description' => get_string('Your username', 'mahara'),
'help' => get_string('help for username', 'help'),
'required' => true
),
'login_password' => array(
'type' => 'password',
'title' => get_string('Password', 'maraha'),
'description' => get_string('Your password', 'mahara'),
'help' => get_string('help for password', 'help'),
'required' => true,
'value' => ''
)
)
),
'submit' => array(
'type' => 'submit',
'value' => get_string('Log in', 'mahara')
)
);
// The login page is completely transient, and it is smart because it
// remembers the GET and POST data sent to it and resends that on
// afterwards.
$action = '';
if ($_GET) {
if (isset($_GET['logout'])) {
// You can log the user out on any particular page by appending
// ?logout to the URL. In this case, we don't want the "action"
// of the url to include that, or be blank, else the next time
// the user logs in they will be logged out again.
$action = hsc(substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], '?')));
} else {
$action = '?';
foreach ($_GET as $key => $value) {
if ($key != 'logout') {
$action .= hsc($key) . '=' . hsc($value) . '&amp;';
}
}
$action = substr($action, 0, -5);
}
}
if ($_POST) {
foreach ($_POST as $key => $value) {
// @todo<nigel>: probably won't pass arrays properly
if (!isset($elements[$key]) && !isset($elements['login']['elements'][$key])) {
$elements[$key] = array(
'type' => 'hidden',
'value' => $value
);
}
}
}
$form = array(
'name' => 'login',
'method' => 'post',
'action' => $action,
'elements' => $elements
);
$smarty->assign('login_form', form($form));
$smarty->display('login.tpl');
exit;
}
function login_validate($values) {
if (!validate_username($values['login_username'])) {
form_set_error('login_username', get_string('Username is not in valid form, it can only'
. ' contain alphanumeric characters, underscores, full stops and @ symbols', 'mahara'));
}
}
function login_submit($values) {
// Do nothing with the form submission - auth_setup() will handle it
//global $SESSION;
//$SESSION->add_ok_msg('logged in!');
//header('Location: ' . get_config('wwwroot'));
//exit;
}
?>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment