Skip to content

Commit

Permalink
MDL-42891 administration: Re-implement incorrect login notification, …
Browse files Browse the repository at this point in the history
…without using the logtable.
  • Loading branch information
ankitagarwal committed Apr 1, 2014
1 parent 4b92f2b commit 52dc1de
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 56 deletions.
6 changes: 2 additions & 4 deletions admin/settings/security.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,8 @@

// "notifications" settingpage
$temp = new admin_settingpage('notifications', new lang_string('notifications', 'admin'));
$temp->add(new admin_setting_configselect('displayloginfailures', new lang_string('displayloginfailures', 'admin'), new lang_string('configdisplayloginfailures', 'admin'), '', array('' => new lang_string('nobody'),
'admin' => new lang_string('administrators'),
'teacher' => new lang_string('administratorsandteachers'),
'everybody' => new lang_string('everybody'))));
$temp->add(new admin_setting_configcheckbox('displayloginfailures', new lang_string('displayloginfailures', 'admin'),
new lang_string('configdisplayloginfailures', 'admin'), 0));
$temp->add(new admin_setting_users_with_capability('notifyloginfailures', new lang_string('notifyloginfailures', 'admin'), new lang_string('confignotifyloginfailures', 'admin'), array(), 'moodle/site:config'));
$options = array();
for ($i = 1; $i <= 100; $i++) {
Expand Down
4 changes: 2 additions & 2 deletions lang/en/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@
$string['configenableblogs'] = 'This switch provides all site users with their own blog.';
$string['configenabledevicedetection'] = 'Enables detection of mobiles, smartphones, tablets or default devices (desktop PCs, laptops, etc) for the application of themes and other features.';
$string['configdisableuserimages'] = 'Disable the ability for users to change user profile images.';
$string['configdisplayloginfailures'] = 'This will display information to selected users about previous failed logins.';
$string['configdisplayloginfailures'] = 'This will display information to users about previous failed logins.';
$string['configdndallowtextandlinks'] = 'Enable or disable the dragging and dropping of text and links onto a course page, alongside the dragging and dropping of files. Note that the dragging of text into Firefox or between different browsers is unreliable and may result in no data being uploaded, or corrupted text being uploaded.';
$string['configdoclang'] = 'This language will be used in links for the documentation pages.';
$string['configdocroot'] = 'Defines the path to the Moodle Docs for providing context-specific documentation via \'Moodle Docs for this page\' links in the footer of each page. If the field is left blank, links will not be displayed.';
Expand Down Expand Up @@ -428,7 +428,7 @@
$string['devicetype'] = 'Device type';
$string['disableuserimages'] = 'Disable user profile images';
$string['displayerrorswarning'] = 'Enabling the PHP setting <em>display_errors</em> is not recommended on production sites because some error messages may reveal sensitive information about your server.';
$string['displayloginfailures'] = 'Display login failures to';
$string['displayloginfailures'] = 'Display login failures';
$string['dndallowtextandlinks'] = 'Drag and drop upload of text/links';
$string['doclang'] = 'Language for docs';
$string['docroot'] = 'Moodle Docs document root';
Expand Down
1 change: 0 additions & 1 deletion lang/en/moodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,6 @@
$string['extendenrol'] = 'Extend enrolment (individual)';
$string['extendperiod'] = 'Extended period';
$string['failedloginattempts'] = '{$a->attempts} failed logins since your last login';
$string['failedloginattemptsall'] = '{$a->attempts} failed logins for {$a->accounts} accounts';
$string['feedback'] = 'Feedback';
$string['file'] = 'File';
$string['fileexists'] = 'There is already a file called {$a}';
Expand Down
9 changes: 6 additions & 3 deletions lib/authlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -647,16 +647,19 @@ function login_attempt_failed($user) {
return;
}

$count = get_user_preferences('login_failed_count', 0, $user);
$last = get_user_preferences('login_failed_last', 0, $user);
$sincescuccess = get_user_preferences('login_failed_count_since_success', $count, $user);
$sincescuccess = $sincescuccess + 1;
set_user_preference('login_failed_count_since_success', $sincescuccess, $user);

if (empty($CFG->lockoutthreshold)) {
// No threshold means no lockout.
// Always unlock here, there might be some race conditions or leftovers when switching threshold.
login_unlock_account($user);
return;
}

$count = get_user_preferences('login_failed_count', 0, $user);
$last = get_user_preferences('login_failed_last', 0, $user);

if (!empty($CFG->lockoutwindow) and time() - $last > $CFG->lockoutwindow) {
$count = 0;
}
Expand Down
37 changes: 0 additions & 37 deletions lib/datalib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1832,43 +1832,6 @@ function get_logs_userday($userid, $courseid, $daystart) {
GROUP BY FLOOR((time - $daystart)/". HOURSECS .") ", $params);
}

/**
* Returns an object with counts of failed login attempts
*
* Returns information about failed login attempts. If the current user is
* an admin, then two numbers are returned: the number of attempts and the
* number of accounts. For non-admins, only the attempts on the given user
* are shown.
*
* @global moodle_database $DB
* @uses CONTEXT_SYSTEM
* @param string $mode Either 'admin' or 'everybody'
* @param string $username The username we are searching for
* @param string $lastlogin The date from which we are searching
* @return int
*/
function count_login_failures($mode, $username, $lastlogin) {
global $DB;

$params = array('mode'=>$mode, 'username'=>$username, 'lastlogin'=>$lastlogin);
$select = "module='login' AND action='error' AND time > :lastlogin";

$count = new stdClass();

if (is_siteadmin()) {
if ($count->attempts = $DB->count_records_select('log', $select, $params)) {
$count->accounts = $DB->count_records_select('log', $select, $params, 'COUNT(DISTINCT info)');
return $count;
}
} else if ($mode == 'everybody') {
if ($count->attempts = $DB->count_records_select('log', "$select AND info = :username", $params)) {
return $count;
}
}
return NULL;
}


/// GENERAL HELPFUL THINGS ///////////////////////////////////

/**
Expand Down
13 changes: 13 additions & 0 deletions lib/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3347,5 +3347,18 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2014032700.01);
}

if ($oldversion < 2014032700.02) {

// Update displayloginfailures setting.
if (empty($CFG->displayloginfailures)) {
set_config('displayloginfailures', 0);
} else {
set_config('displayloginfailures', 1);
}

// Main savepoint reached.
upgrade_main_savepoint(true, 2014032700.02);
}

return true;
}
40 changes: 40 additions & 0 deletions lib/deprecatedlib.php
Original file line number Diff line number Diff line change
Expand Up @@ -4335,3 +4335,43 @@ function can_use_html_editor() {
debugging('can_use_html_editor has been deprecated please update your code to assume it returns true.', DEBUG_DEVELOPER);
return true;
}


/**
* Returns an object with counts of failed login attempts
*
* Returns information about failed login attempts. If the current user is
* an admin, then two numbers are returned: the number of attempts and the
* number of accounts. For non-admins, only the attempts on the given user
* are shown.
*
* @deprecate since Moodle 2.7, use {@link user_count_login_failures()} instead.
* @global moodle_database $DB
* @uses CONTEXT_SYSTEM
* @param string $mode Either 'admin' or 'everybody'
* @param string $username The username we are searching for
* @param string $lastlogin The date from which we are searching
* @return int
*/
function count_login_failures($mode, $username, $lastlogin) {
global $DB;

debugging('This method has been deprecated. Please use user_count_login_failures() instead.', DEBUG_DEVELOPER);

$params = array('mode'=>$mode, 'username'=>$username, 'lastlogin'=>$lastlogin);
$select = "module='login' AND action='error' AND time > :lastlogin";

$count = new stdClass();

if (is_siteadmin()) {
if ($count->attempts = $DB->count_records_select('log', $select, $params)) {
$count->accounts = $DB->count_records_select('log', $select, $params, 'COUNT(DISTINCT info)');
return $count;
}
} else if ($mode == 'everybody') {
if ($count->attempts = $DB->count_records_select('log', "$select AND info = :username", $params)) {
return $count;
}
}
return NULL;
}
16 changes: 8 additions & 8 deletions lib/outputrenderers.php
Original file line number Diff line number Diff line change
Expand Up @@ -666,16 +666,16 @@ public function login_info($withlinks = null) {
unset($SESSION->justloggedin);
if (!empty($CFG->displayloginfailures)) {
if (!isguestuser()) {
if ($count = count_login_failures($CFG->displayloginfailures, $USER->username, $USER->lastlogin)) {
// Include this file only when required.
require_once($CFG->dirroot . '/user/lib.php');
if ($count = user_count_login_failures($USER)) {
$loggedinas .= '&nbsp;<div class="loginfailures">';
if (empty($count->accounts)) {
$loggedinas .= get_string('failedloginattempts', '', $count);
} else {
$loggedinas .= get_string('failedloginattemptsall', '', $count);
}
$a = new stdClass();
$a->attempts = $count;
$loggedinas .= get_string('failedloginattempts', '', $a);
if (file_exists("$CFG->dirroot/report/log/index.php") and has_capability('report/log:view', context_system::instance())) {
$loggedinas .= ' (<a href="'.$CFG->wwwroot.'/report/log/index.php'.
'?chooselog=1&amp;id=1&amp;modid=site_errors">'.get_string('logs').'</a>)';
$loggedinas .= html_writer::link(new moodle_url('/report/log/index.php', array('chooselog' => 1,
'id' => 0 , 'modid' => 'site_errors')), '(' . get_string('logs') . ')');
}
$loggedinas .= '</div>';
}
Expand Down
1 change: 1 addition & 0 deletions lib/upgrade.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ DEPRECATIONS:
* Various cm_info methods have been deprecated in favour of their read-only properties (get_url(), get_content(), get_extra_classes(),
get_on_click(), get_custom_data(), get_after_link, get_after_edit_icons)
* The ajaxenabled function has been deprecated and always returns true. All code should be fully functional in Javascript.
* count_login_failures() has been deprecated, use user_count_login_failures() instead. Refer MDL-42891 for details.

YUI:
* The lightbox attribute for moodle-core-notification-dialogue has been
Expand Down
26 changes: 26 additions & 0 deletions user/lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,29 @@ function can_view_user_details_cap($user, $course = null) {
function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array('user-profile' => get_string('page-user-profile', 'pagetype'));
}

/**
* Count the number of failed login attempts for the given user, since last successful login.
*
* @param int|stdclass $user user id or object.
* @param bool $reset Resets failed login count, if set to true.
*
* @return int number of failed login attempts since the last successful login.
*/
function user_count_login_failures($user, $reset = true) {
global $DB;

if (!is_object($user)) {
$user = $DB->get_record('user', array('id' => $user), '*', MUST_EXIST);
}
if ($user->deleted) {
// Deleted user, nothing to do.
return 0;
}
$count = get_user_preferences('login_failed_count_since_success', 0, $user);
if ($reset) {
set_user_preference('login_failed_count_since_success', 0, $user);
}
return $count;
}

24 changes: 24 additions & 0 deletions user/tests/userlib_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,28 @@ public function test_create_users() {
$expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
$this->assertEventLegacyLogData($expectedlogdata, $event);
}

/**
* Test function user_count_login_failures().
*/
public function test_user_count_login_failures() {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
for ($i = 0; $i < 10; $i++) {
login_attempt_failed($user);
}
$this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
$count = user_count_login_failures($user); // Reset count.
$this->assertEquals(10, $count);
$this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));

for ($i = 0; $i < 10; $i++) {
login_attempt_failed($user);
}
$this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
$count = user_count_login_failures($user, false); // Do not reset count.
$this->assertEquals(10, $count);
$this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
}
}
2 changes: 1 addition & 1 deletion version.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

defined('MOODLE_INTERNAL') || die();

$version = 2014032700.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2014032700.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.

Expand Down

0 comments on commit 52dc1de

Please sign in to comment.