Skip to content

Commit

Permalink
MDL-41115 add option to allow login via email
Browse files Browse the repository at this point in the history
  • Loading branch information
skodak committed Mar 14, 2014
1 parent c0e8812 commit 50f5c84
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 17 deletions.
1 change: 1 addition & 0 deletions admin/settings/plugins.php
Expand Up @@ -72,6 +72,7 @@
$temp->add(new admin_setting_manageauths());
$temp->add(new admin_setting_heading('manageauthscommonheading', new lang_string('commonsettings', 'admin'), ''));
$temp->add(new admin_setting_special_registerauth());
$temp->add(new admin_setting_configcheckbox('authloginviaemail', new lang_string('authloginviaemail', 'core_auth'), new lang_string('authloginviaemail_desc', 'core_auth'), 0));
$temp->add(new admin_setting_configcheckbox('authpreventaccountcreation', new lang_string('authpreventaccountcreation', 'admin'), new lang_string('authpreventaccountcreation_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('loginpageautofocus', new lang_string('loginpageautofocus', 'admin'), new lang_string('loginpageautofocus_help', 'admin'), 0));
$temp->add(new admin_setting_configselect('guestloginbutton', new lang_string('guestloginbutton', 'auth'),
Expand Down
2 changes: 2 additions & 0 deletions lang/en/auth.php
Expand Up @@ -108,6 +108,8 @@
$string['instructions'] = 'Instructions';
$string['internal'] = 'Internal';
$string['locked'] = 'Locked';
$string['authloginviaemail'] = 'Allow login via email';
$string['authloginviaemail_desc'] = 'Allow users to use both username and email address (if unique) for site login.';
$string['md5'] = 'MD5 hash';
$string['nopasswordchange'] = 'Password can not be changed';
$string['nopasswordchangeforced'] = 'You cannot proceed without changing your password, however there is no available page for changing it. Please contact your Moodle Administrator.';
Expand Down
49 changes: 41 additions & 8 deletions lib/moodlelib.php
Expand Up @@ -4052,16 +4052,31 @@ function create_user_record($username, $password, $auth = 'manual') {
*/
function update_user_record($username) {
global $DB, $CFG;
require_once($CFG->dirroot."/user/profile/lib.php");
require_once($CFG->dirroot.'/user/lib.php');
// Just in case check text case.
$username = trim(core_text::strtolower($username));

$oldinfo = $DB->get_record('user', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id), '*', MUST_EXIST);
return update_user_record_by_id($oldinfo->id);
}

/**
* Will update a local user record from an external source (MNET users can not be updated using this method!).
*
* @param int $id user id
* @return stdClass A complete user object
*/
function update_user_record_by_id($id) {
global $DB, $CFG;
require_once($CFG->dirroot."/user/profile/lib.php");
require_once($CFG->dirroot.'/user/lib.php');

$params = array('mnethostid' => $CFG->mnet_localhost_id, 'id' => $id, 'deleted' => 0);
$oldinfo = $DB->get_record('user', $params, '*', MUST_EXIST);

$newuser = array();
$userauth = get_auth_plugin($oldinfo->auth);

if ($newinfo = $userauth->get_userinfo($username)) {
if ($newinfo = $userauth->get_userinfo($oldinfo->username)) {
$newinfo = truncate_userinfo($newinfo);
$customfields = $userauth->get_custom_user_profile_fields();

Expand Down Expand Up @@ -4325,7 +4340,7 @@ function guest_user() {
*
* Note: this function works only with non-mnet accounts!
*
* @param string $username User's username
* @param string $username User's username (or also email if $CFG->authloginviaemail enabled)
* @param string $password User's password
* @param bool $ignorelockout useful when guessing is prevented by other mechanism such as captcha or SSO
* @param int $failurereason login failure reason, can be used in renderers (it may disclose if account exists)
Expand All @@ -4335,9 +4350,27 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
global $CFG, $DB;
require_once("$CFG->libdir/authlib.php");

if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) {
// we have found the user

} else if (!empty($CFG->authloginviaemail)) {
if ($email = clean_param($username, PARAM_EMAIL)) {
$select = "mnethostid = :mnethostid AND LOWER(email) = LOWER(:email) AND deleted = 0";
$params = array('mnethostid' => $CFG->mnet_localhost_id, 'email' => $email);
$users = $DB->get_records_select('user', $select, $params, 'id', 'id', 0, 2);
if (count($users) === 1) {
// Use email for login only if unique.
$user = reset($users);
$user = get_complete_user_data('id', $user->id);
$username = $user->username;
}
unset($users);
}
}

$authsenabled = get_enabled_auth_plugins();

if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) {
if ($user) {
// Use manual if auth not set.
$auth = empty($user->auth) ? 'manual' : $user->auth;
if (!empty($user->suspended)) {
Expand Down Expand Up @@ -4376,7 +4409,7 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
return false;
}

// Do not try to authenticate non-existent accounts when user creation is not disabled.
// Do not try to authenticate non-existent accounts when user creation is disabled.
if (!empty($CFG->authpreventaccountcreation)) {
$failurereason = AUTH_LOGIN_NOUSER;

Expand Down Expand Up @@ -4429,7 +4462,7 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f
// User already exists in database.
if (empty($user->auth)) {
// For some reason auth isn't set yet.
$DB->set_field('user', 'auth', $auth, array('username' => $username));
$DB->set_field('user', 'auth', $auth, array('id' => $user->id));
$user->auth = $auth;
}

Expand All @@ -4439,7 +4472,7 @@ function authenticate_user_login($username, $password, $ignorelockout=false, &$f

if ($authplugin->is_synchronised_with_external()) {
// Update user record from external DB.
$user = update_user_record($username);
$user = update_user_record_by_id($user->id);
}
} else {
// Create account, we verified above that user creation is allowed.
Expand Down
43 changes: 34 additions & 9 deletions lib/tests/authlib_test.php
Expand Up @@ -130,32 +130,57 @@ public function test_authenticate_user_login() {

$_SERVER['HTTP_USER_AGENT'] = 'no browser'; // Hack around missing user agent in CLI scripts.

$user1 = $this->getDataGenerator()->create_user(array('username'=>'username1', 'password'=>'password1'));
$user2 = $this->getDataGenerator()->create_user(array('username'=>'username2', 'password'=>'password2', 'suspended'=>1));
$user3 = $this->getDataGenerator()->create_user(array('username'=>'username3', 'password'=>'password3', 'auth'=>'nologin'));
// Capture events.
$user1 = $this->getDataGenerator()->create_user(array('username'=>'username1', 'password'=>'password1', 'email'=>'email1@example.com'));
$user2 = $this->getDataGenerator()->create_user(array('username'=>'username2', 'password'=>'password2', 'email'=>'email2@example.com', 'suspended'=>1));
$user3 = $this->getDataGenerator()->create_user(array('username'=>'username3', 'password'=>'password3', 'email'=>'email2@example.com', 'auth'=>'nologin'));

// Normal login.
$sink = $this->redirectEvents();
$result = authenticate_user_login('username1', 'password1');
$events = $sink->get_events();
$sink->close();

// No event is triggred.
$this->assertEmpty($events);
$this->assertInstanceOf('stdClass', $result);
$this->assertEquals($user1->id, $result->id);

// Normal login with reason.
$reason = null;
// Capture event.
$sink = $this->redirectEvents();
$result = authenticate_user_login('username1', 'password1', false, $reason);
$events = $sink->get_events();
$sink->close();

// No event is triggred.
$this->assertEmpty($events);
$this->assertInstanceOf('stdClass', $result);
$this->assertEquals(AUTH_LOGIN_OK, $reason);

// Test login via email
$reason = null;
$this->assertEmpty($CFG->authloginviaemail);
$sink = $this->redirectEvents();
$result = authenticate_user_login('email1@example.com', 'password1', false, $reason);
$sink->close();
$this->assertFalse($result);
$this->assertEquals(AUTH_LOGIN_NOUSER, $reason);

set_config('authloginviaemail', 1);
$this->assertNotEmpty($CFG->authloginviaemail);
$sink = $this->redirectEvents();
$result = authenticate_user_login('email1@example.com', 'password1');
$events = $sink->get_events();
$sink->close();
$this->assertEmpty($events);
$this->assertInstanceOf('stdClass', $result);
$this->assertEquals($user1->id, $result->id);

$reason = null;
$sink = $this->redirectEvents();
$result = authenticate_user_login('email2@example.com', 'password2', false, $reason);
$events = $sink->get_events();
$sink->close();
$this->assertFalse($result);
$this->assertEquals(AUTH_LOGIN_NOUSER, $reason);
set_config('authloginviaemail', 0);

$reason = null;
// Capture failed login event.
$sink = $this->redirectEvents();
Expand Down

0 comments on commit 50f5c84

Please sign in to comment.