Permalink
Browse files

Merge branch 'w51_MDL-21342_m25_lockout' of git://github.com/skodak/m…

…oodle
  • Loading branch information...
2 parents 17e5c6e + c4844bf commit 70601b94336039c5bab172c64efab18ae590ef6a Sam Hemelryk committed Jan 7, 2013
@@ -59,6 +59,11 @@
$temp->add(new admin_setting_configcheckbox('cronclionly', new lang_string('cronclionly', 'admin'), new lang_string('configcronclionly', 'admin'), 0));
$temp->add(new admin_setting_configpasswordunmask('cronremotepassword', new lang_string('cronremotepassword', 'admin'), new lang_string('configcronremotepassword', 'admin'), ''));
+ $options = array(0=>get_string('no'), 3=>3, 5=>5, 7=>7, 10=>10, 20=>20, 30=>30, 50=>50, 100=>100);
+ $temp->add(new admin_setting_configselect('lockoutthreshold', new lang_string('lockoutthreshold', 'admin'), new lang_string('lockoutthreshold_desc', 'admin'), 0, $options));
+ $temp->add(new admin_setting_configduration('lockoutwindow', new lang_string('lockoutwindow', 'admin'), new lang_string('lockoutwindow_desc', 'admin'), 60*30));
+ $temp->add(new admin_setting_configduration('lockoutduration', new lang_string('lockoutduration', 'admin'), new lang_string('lockoutduration_desc', 'admin'), 60*30));
+
$temp->add(new admin_setting_configcheckbox('passwordpolicy', new lang_string('passwordpolicy', 'admin'), new lang_string('configpasswordpolicy', 'admin'), 1));
$temp->add(new admin_setting_configtext('minpasswordlength', new lang_string('minpasswordlength', 'admin'), new lang_string('configminpasswordlength', 'admin'), 8, PARAM_INT));
$temp->add(new admin_setting_configtext('minpassworddigits', new lang_string('minpassworddigits', 'admin'), new lang_string('configminpassworddigits', 'admin'), 1, PARAM_INT));
View
@@ -2,6 +2,7 @@
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
+ require_once($CFG->libdir.'/authlib.php');
require_once($CFG->dirroot.'/user/filters/lib.php');
$delete = optional_param('delete', 0, PARAM_INT);
@@ -16,6 +17,7 @@
$acl = optional_param('acl', '0', PARAM_INT); // id of user to tweak mnet ACL (requires $access)
$suspend = optional_param('suspend', 0, PARAM_INT);
$unsuspend = optional_param('unsuspend', 0, PARAM_INT);
+ $unlock = optional_param('unlock', 0, PARAM_INT);
admin_externalpage_setup('editusers');
@@ -32,6 +34,7 @@
$strshowallusers = get_string('showallusers');
$strsuspend = get_string('suspenduser', 'admin');
$strunsuspend = get_string('unsuspenduser', 'admin');
+ $strunlock = get_string('unlockaccount', 'admin');
$strconfirm = get_string('confirm');
if (empty($CFG->loginhttps)) {
@@ -143,6 +146,14 @@
}
}
redirect($returnurl);
+
+ } else if ($unlock and confirm_sesskey()) {
+ require_capability('moodle/user:update', $sitecontext);
+
+ if ($user = $DB->get_record('user', array('id'=>$unlock, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
+ login_unlock_account($user);
+ }
+ redirect($returnurl);
}
// create the user filter form
@@ -303,6 +314,9 @@
}
}
+ if (login_is_lockedout($user)) {
+ $buttons[] = html_writer::link(new moodle_url($returnurl, array('unlock'=>$user->id, 'sesskey'=>sesskey())), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/unlock'), 'alt'=>$strunlock, 'class'=>'iconsmall')), array('title'=>$strunlock));
+ }
}
}
View
@@ -1643,7 +1643,7 @@ function ntlmsso_finish() {
$username = $cf[$key];
// Here we want to trigger the whole authentication machinery
// to make sure no step is bypassed...
- $user = authenticate_user_login($username, $key);
+ $user = authenticate_user_login($username, $key, false);
if ($user) {
add_to_log(SITEID, 'user', 'login', "view.php?id=$USER->id&course=".SITEID,
$user->id, 0, $user->id);
View
@@ -625,6 +625,28 @@
$string['location'] = 'Location';
$string['locationsettings'] = 'Location settings';
$string['locked'] = 'locked';
+$string['lockoutduration'] = 'Account lockout duration';
+$string['lockoutduration_desc'] = 'Locked out account is automatically unlocked after this duration.';
+$string['lockoutemailbody'] = 'Your account with username {$a->username} on server \'{$a->sitename}\'
+was locked out after multiple invalid login attempts.
+
+To unlock the account immediately go to the following address
+
+{$a->link}
+
+In most mail programs, this should appear as a blue link
+which you can just click on. If that doesn\'t work,
+then cut and paste the address into the address
+line at the top of your web browser window.
+
+If you need help, please contact the site administrator,
+{$a->admin}';
+$string['lockoutemailsubject'] = 'Your account on {$a} was locked out';
+$string['lockouterrorunlock'] = 'Invalid account unlock information supplied.';
+$string['lockoutthreshold'] = 'Account lockout threshold';
+$string['lockoutthreshold_desc'] = 'Select number of failed login attempts that result in account lockout. This feature may be abused in denial of service attacks.';
+$string['lockoutwindow'] = 'Account lockout observation window';
+$string['lockoutwindow_desc'] = 'Observation time for lockout threshold, if there are no failed attempts the threshold counter is reset after this time.';
$string['log'] = 'Logs';
$string['logguests'] = 'Log guest access';
$string['logguests_help'] = 'This setting enables logging of actions by guest account and not logged in users. High profile sites may want to disable this logging for performance reasons. It is recommended to keep this setting enabled on production sites.';
@@ -989,6 +1011,7 @@
$string['unicoderecommended'] = 'Storing all your data in Unicode (UTF-8) is recommended. New installations should be performed into databases that have their default character set as Unicode. If you are upgrading, you should perform the UTF-8 migration process (see the Admin page).';
$string['unicoderequired'] = 'It is required that you store all your data in Unicode format (UTF-8). New installations must be performed into databases that have their default character set as Unicode. If you are upgrading, you should perform the UTF-8 migration process (see the Admin page).';
$string['uninstallplugin'] = 'Uninstall';
+$string['unlockaccount'] = 'Unlock account';
$string['unsettheme'] = 'Unset theme';
$string['unsupported'] = 'Unsupported';
$string['unsuspenduser'] = 'Activate user account';
View
@@ -649,7 +649,6 @@
$string['errorcreatingactivity'] = 'Unable to create an instance of activity \'{$a}\'';
$string['errorfiletoobig'] = 'The file was bigger than the limit of {$a} bytes';
$string['errornouploadrepo'] = 'There is no upload repository enabled for this site';
-$string['errortoomanylogins'] = 'Sorry, you have exceeded the allowed number of login attempts. Restart your browser.';
$string['errorwhenconfirming'] = 'You are not confirmed yet because an error occurred. If you clicked on a link in an email to get here, make sure that the line in your email wasn\'t broken or wrapped. You may have to use cut and paste to reconstruct the link properly.';
$string['everybody'] = 'Everybody';
$string['executeat'] = 'Execute at';
View
@@ -61,6 +61,22 @@
define('AUTH_REMOVEUSER_SUSPEND', 1);
define('AUTH_REMOVEUSER_FULLDELETE', 2);
+/** Login attempt successful. */
+define('AUTH_LOGIN_OK', 0);
+
+/** Can not login because user does not exist. */
+define('AUTH_LOGIN_NOUSER', 1);
+
+/** Can not login because user is suspended. */
+define('AUTH_LOGIN_SUSPENDED', 2);
+
+/** Can not login, most probably password did not match. */
+define('AUTH_LOGIN_FAILED', 3);
+
+/** Can not login because user is locked out. */
+define('AUTH_LOGIN_LOCKOUT', 4);
+
+
/**
* Abstract authentication plugin.
*
@@ -507,3 +523,178 @@ function loginpage_idp_list($wantsurl) {
}
}
+
+/**
+ * Verify if user is locked out.
+ *
+ * @param stdClass $user
+ * @return bool true if user locked out
+ */
+function login_is_lockedout($user) {
+ global $CFG;
+
+ if ($user->mnethostid != $CFG->mnet_localhost_id) {
+ return false;
+ }
+ if (isguestuser($user)) {
+ return false;
+ }
+
+ if (empty($CFG->lockoutthreshold)) {
+ // Lockout not enabled.
+ return false;
+ }
+
+ if (get_user_preferences('login_lockout_ignored', 0, $user)) {
+ // This preference may be used for accounts that must not be locked out.
+ return false;
+ }
+
+ $locked = get_user_preferences('login_lockout', 0, $user);
+ if (!$locked) {
+ return false;
+ }
+
+ if (empty($CFG->lockoutduration)) {
+ // Locked out forever.
+ return true;
+ }
+
+ if (time() - $locked < $CFG->lockoutduration) {
+ return true;
+ }
+
+ login_unlock_account($user);
+
+ return false;
+}
+
+/**
+ * To be called after valid user login.
+ * @param stdClass $user
+ */
+function login_attempt_valid($user) {
+ global $CFG;
+
+ if ($user->mnethostid != $CFG->mnet_localhost_id) {
+ return;
+ }
+ if (isguestuser($user)) {
+ return;
+ }
+
+ // Always unlock here, there might be some race conditions or leftovers when switching threshold.
+ login_unlock_account($user);
+}
+
+/**
+ * To be called after failed user login.
+ * @param stdClass $user
+ */
+function login_attempt_failed($user) {
+ global $CFG;
+
+ if ($user->mnethostid != $CFG->mnet_localhost_id) {
+ return;
+ }
+ if (isguestuser($user)) {
+ return;
+ }
+
+ 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;
+ }
+
+ $count = $count+1;
+
+ set_user_preference('login_failed_count', $count, $user);
+ set_user_preference('login_failed_last', time(), $user);
+
+ if ($count >= $CFG->lockoutthreshold) {
+ login_lock_account($user);
+ }
+}
+
+/**
+ * Lockout user and send notification email.
+ *
+ * @param stdClass $user
+ */
+function login_lock_account($user) {
+ global $CFG, $SESSION;
+
+ if ($user->mnethostid != $CFG->mnet_localhost_id) {
+ return;
+ }
+ if (isguestuser($user)) {
+ return;
+ }
+
+ if (get_user_preferences('login_lockout_ignored', 0, $user)) {
+ // This user can not be locked out.
+ return;
+ }
+
+ $alreadylockedout = get_user_preferences('login_lockout', 0, $user);
+
+ set_user_preference('login_lockout', time(), $user);
+
+ if ($alreadylockedout == 0) {
+ $secret = random_string(15);
+ set_user_preference('login_lockout_secret', $secret, $user);
+
+ // Some nasty hackery to get strings and dates localised for target user.
+ $sessionlang = isset($SESSION->lang) ? $SESSION->lang : null;
+ if (get_string_manager()->translation_exists($user->lang, false)) {
+ $SESSION->lang = $user->lang;
+ moodle_setlocale();
+ }
+
+ $site = get_site();
+ $supportuser = generate_email_supportuser();
+
+ $data = new stdClass();
+ $data->firstname = $user->firstname;
+ $data->lastname = $user->lastname;
+ $data->username = $user->username;
+ $data->sitename = format_string($site->fullname);
+ $data->link = $CFG->wwwroot.'/login/unlock_account.php?u='.$user->id.'&s='.$secret;
+ $data->admin = generate_email_signoff();
+
+ $message = get_string('lockoutemailbody', 'admin', $data);
+ $subject = get_string('lockoutemailsubject', 'admin', format_string($site->fullname));
+
+ if ($message) {
+ // Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
+ email_to_user($user, $supportuser, $subject, $message);
+ }
+
+ if ($SESSION->lang !== $sessionlang) {
+ $SESSION->lang = $sessionlang;
+ moodle_setlocale();
+ }
+ }
+}
+
+/**
+ * Unlock user account and reset timers.
+ *
+ * @param stdClass $user
+ */
+function login_unlock_account($user) {
+ unset_user_preference('login_lockout', $user);
+ unset_user_preference('login_failed_count', $user);
+ unset_user_preference('login_failed_last', $user);
+
+ // Note: do not clear the lockout secret because user might click on the link repeatedly.
+}
View
@@ -31,6 +31,25 @@
defined('MOODLE_INTERNAL') || die();
/**
+ * Not used any more, the account lockout handling is now
+ * part of authenticate_user_login().
+ * @deprecated
+ */
+function update_login_count() {
+ // TODO: delete function in Moodle 2.6
+ debugging('update_login_count() is deprecated, all calls need to be removed');
+}
+
+/**
+ * Not used any more, replaced by proper account lockout.
+ * @deprecated
+ */
+function reset_login_count() {
+ // TODO: delete function in Moodle 2.6
+ debugging('reset_login_count() is deprecated, all calls need to be removed');
+}
+
+/**
* Unsupported session id rewriting.
* @deprecated
* @param string $buffer
Oops, something went wrong.

0 comments on commit 70601b9

Please sign in to comment.