Skip to content
Browse files

MDL-23692 forgotpw: Simplify forgotpw process.

  • Loading branch information...
1 parent f4491c2 commit 92de749fc715108098df5f874d1cfb22af31bf38 @peterbulmer peterbulmer committed
Showing with 322 additions and 73 deletions.
  1. +16 −0 admin/settings/security.php
  2. +2 −0 lang/en/admin.php
  3. +25 −1 lang/en/moodle.php
  4. +9 −0 lib/cronlib.php
  5. +14 −1 lib/db/install.xml
  6. +21 −0 lib/db/upgrade.php
  7. +8 −4 lib/moodlelib.php
  8. +7 −7 login/forgot_password.php
  9. +133 −59 login/lib.php
  10. +86 −0 login/set_password_form.php
  11. +1 −1 version.php
View
16 admin/settings/security.php
@@ -70,6 +70,22 @@
$temp->add(new admin_setting_configtext('minpasswordupper', new lang_string('minpasswordupper', 'admin'), new lang_string('configminpasswordupper', 'admin'), 1, PARAM_INT));
$temp->add(new admin_setting_configtext('minpasswordnonalphanum', new lang_string('minpasswordnonalphanum', 'admin'), new lang_string('configminpasswordnonalphanum', 'admin'), 1, PARAM_INT));
$temp->add(new admin_setting_configtext('maxconsecutiveidentchars', new lang_string('maxconsecutiveidentchars', 'admin'), new lang_string('configmaxconsecutiveidentchars', 'admin'), 0, PARAM_INT));
+ $pwresetoptions = array(
+ 300 => new lang_string('numminutes', '', 5),
+ 900 => new lang_string('numminutes', '', 15),
+ 1800 => new lang_string('numminutes', '', 30),
+ 2700 => new lang_string('numminutes', '', 45),
+ 3600 => new lang_string('numminutes', '', 60),
+ 7200 => new lang_string('numminutes', '', 120),
+ 14400 => new lang_string('numminutes', '', 240)
+ );
+ $adminsetting = new admin_setting_configselect(
+ 'pwresettime',
+ new lang_string('passwordresettime','admin'),
+ new lang_string('configpasswordresettime','admin'),
+ 1800,
+ $pwresetoptions);
+ $temp->add($adminsetting);
$temp->add(new admin_setting_configcheckbox('groupenrolmentkeypolicy', new lang_string('groupenrolmentkeypolicy', 'admin'), new lang_string('groupenrolmentkeypolicy_desc', 'admin'), 1));
$temp->add(new admin_setting_configcheckbox('disableuserimages', new lang_string('disableuserimages', 'admin'), new lang_string('configdisableuserimages', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('emailchangeconfirmation', new lang_string('emailchangeconfirmation', 'admin'), new lang_string('configemailchangeconfirmation', 'admin'), 1));
View
2 lang/en/admin.php
@@ -277,6 +277,7 @@
$string['configopentogoogle'] = 'If you enable this setting, then Google will be allowed to enter your site as a Guest. In addition, people coming in to your site via a Google search will automatically be logged in as a Guest. Note that this only provides transparent access to courses that already allow guest access.';
$string['configoverride'] = 'Defined in config.php';
$string['configpasswordpolicy'] = 'Turning this on will make Moodle check user passwords against a valid password policy. Use the settings below to specify your policy (they will be ignored if you set this to \'No\').';
+$string['configpasswordresettime'] = 'This specifies the amount of time people have to validate a password reset request before it expires. Usually 30 minutes is a good value.';
$string['configpathtoclam'] = 'Path to clam AV. Probably something like /usr/bin/clamscan or /usr/bin/clamdscan. You need this in order for clam AV to run.';
$string['configpathtodu'] = 'Path to du. Probably something like /usr/bin/du. If you enter this, pages that display directory contents will run much faster for directories with a lot of files.';
$string['configperfdebug'] = 'If you turn this on, performance info will be printed in the footer of the standard theme';
@@ -778,6 +779,7 @@
$string['order3'] = 'Third';
$string['order4'] = 'Fourth';
$string['passwordpolicy'] = 'Password policy';
+$string['passwordresettime'] = 'Maximum time to validate password reset request';
$string['pathconvert'] = 'Path of <i>convert</i> binary';
$string['pathdvips'] = 'Path of <i>dvips</i> binary';
$string['pathlatex'] = 'Path of <i>latex</i> binary';
View
26 lang/en/moodle.php
@@ -535,6 +535,7 @@
$string['editthiscategory'] = 'Edit this category';
$string['edituser'] = 'Edit user accounts';
$string['email'] = 'Email address';
+$string['emailalreadysent'] = 'A Password reset email has already been sent. Please check your email.';
$string['emailactive'] = 'Email activated';
$string['emailagain'] = 'Email (again)';
$string['emailconfirm'] = 'Confirm your account';
@@ -609,7 +610,7 @@
$string['emailpasswordconfirmnoemail'] = '<p>The user account you specified does not have a recorded email address.</p>
<p>Please contact the site administrator.</p>';
$string['emailpasswordconfirmnotsent'] = '<p>The user detail you supplied does not identify an existing user account.</p>
- <p>Please check the detail you entered, then try again.
+ <p>Please check the information you entered, then try again.
If you continue to have difficulty, please contact the site administrator.</p>';
$string['emailpasswordconfirmsent'] = 'An email should have been sent to your address at <b>{$a}</b>.
<br />It contains easy instructions to confirm and complete this password change.
@@ -651,6 +652,24 @@
An email containing your new password has been sent to your address at<br /><b>{$a->email}</b>.<br />
The new password was automatically generated - you might like to
<a href="{$a->link}">change your password</a> to something easier to remember.';
+$string['emailresetconfirmation'] = 'Hi {$a->firstname},
+
+A password reset was requested for your account \'{$a->username}\' at {$a->sitename}.
+
+To confirm this request, and set a new password for your account, please
+go to the following web address:
+
+{$a->link}
+(This link is valid for {$a->resetminutes} minutes from the time this reset was first requested)
+
+If this password reset was not requested by you, no action is needed.
+
+If you need help, please contact the site administrator,
+{$a->admin}';
+$string['emailresetconfirmationsubject'] = '{$a}: Password reset request';
+$string['emailresetconfirmsent'] = 'An email has been sent to your address at <b>{$a}</b>.
+<br />It contains easy instructions to confirm and complete this password change.
+If you continue to have difficulty, contact the site administrator.';
$string['emptydragdropregion'] = 'empty region';
$string['enable'] = 'Enable';
$string['encryptedcode'] = 'Encrypted code';
@@ -1226,6 +1245,7 @@
Following is the content of your email:';
$string['noreplybouncesubject'] = '{$a} - bounced email.';
$string['noreplyname'] = 'Do not reply to this email';
+$string['noresetrecord'] = 'There is no record of that reset request. Please initiate a new password reset request.';
$string['noresults'] = 'No results';
$string['normal'] = 'Normal';
$string['normalfilter'] = 'Normal search';
@@ -1304,6 +1324,7 @@
$string['passwordrecovery'] = 'Yes, help me log in';
$string['passwordsdiffer'] = 'These passwords do not match';
$string['passwordsent'] = 'Password has been sent';
+$string['passwordset'] = 'Your password has been set.';
$string['passwordsenttext'] = '<p>An email has been sent to your address at {$a->email}.</p>
<p><b>Please check your email for your new password</b></p>
<p>The new password was automatically generated, so you might like to
@@ -1418,6 +1439,7 @@
$string['resetcourse'] = 'Reset course';
$string['resetinfo'] = 'This page allows you to empty a course of user data, while retaining the activities and other settings. Please be warned that by choosing items below and submitting this page you will delete your chosen user data from this course forever!';
$string['resetnotimplemented'] = 'Reset not implemented';
+$string['resetrecordexpired'] = 'The password reset link you used is more than {$a} minutes old and has expired. Please initiate a new password reset.';
$string['resetstartdate'] = 'Reset start date';
$string['resetstatus'] = 'Status';
$string['resettask'] = 'Task';
@@ -1542,6 +1564,8 @@
$string['servererror'] = 'An error occurred whilst communicating with the server';
$string['serverlocaltime'] = 'Server\'s local time';
$string['setcategorytheme'] = 'Set category theme';
+$string['setpassword'] = 'Set Password';
+$string['setpasswordinstructions'] = 'Please enter and repeat your new password below, then click "Set Password". <br />Your new password will be saved, and you will be logged in.';
$string['settings'] = 'Settings';
$string['shortname'] = 'Short name'; // @deprecated MDL-34652 - use shortnamecourse or shortnameuser or some own context specific string
$string['shortnamecollisionwarning'] = '[*] = This shortname is already in use by a course and will need to be changed upon approval';
View
9 lib/cronlib.php
@@ -173,6 +173,15 @@ function cron_run() {
mtrace(' Deleting temporary files...');
cron_delete_from_temp();
+ // Cleanup user password reset records
+ // Delete any reset request records which are expired by more than a day.
+ // (We keep recently expired requests around so we can give a different error msg to users who
+ // are trying to user a recently expired reset attempt).
+ $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800;
+ $earliestvalid = time() - $pwresettime - DAYSECS;
+ $DB->delete_records_select('user_password_resets', "timerequested < ?", array($earliestvalid));
+ mtrace(' Cleaned up old password reset records');
+
mtrace("...finished clean-up tasks");
} // End of occasional clean-up tasks
View
15 lib/db/install.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20130921" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20130927" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
@@ -3050,5 +3050,18 @@
<KEY NAME="fk_backpackid" TYPE="foreign" FIELDS="backpackid" REFTABLE="badge_backpack" REFFIELDS="id"/>
</KEYS>
</TABLE>
+ <TABLE NAME="user_password_resets" COMMENT="table tracking password reset confirmation tokens">
+ <FIELDS>
+ <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
+ <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="id of the user account which requester claimed to be"/>
+ <FIELD NAME="timerequested" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The time that the user first requested this password reset"/>
+ <FIELD NAME="timererequested" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The time the user re-requested the password reset."/>
+ <FIELD NAME="token" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="secret set and emailed to user"/>
+ </FIELDS>
+ <KEYS>
+ <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+ <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id"/>
+ </KEYS>
+ </TABLE>
</TABLES>
</XMLDB>
View
21 lib/db/upgrade.php
@@ -2555,5 +2555,26 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2013092001.02);
}
+ if ($oldversion < 2013092700.02) {
+ // Create a new 'user_password_resets' table.
+ $table = new xmldb_table('user_password_resets');
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null);
+ $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null);
+ $table->add_field('timerequested', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null);
+ $table->add_field('timererequested', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, 0, null);
+ $table->add_field('token', XMLDB_TYPE_CHAR, '32', null, XMLDB_NOTNULL, null, null, null);
+
+ // Adding keys to table.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+ $table->add_key('fk_userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+
+ // Conditionally launch create table.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+ upgrade_main_savepoint(true, 2013092700.02);
+ }
+
+
return true;
}
View
12 lib/moodlelib.php
@@ -5947,23 +5947,27 @@ function send_confirmation_email($user) {
* Sends a password change confirmation email.
*
* @param stdClass $user A {@link $USER} object
+ * @param stdClass $resetrecord An object tracking metadata regarding password reset request
* @return bool Returns true if mail was sent OK and false if there was an error.
*/
-function send_password_change_confirmation_email($user) {
+function send_password_change_confirmation_email($user, $resetrecord) {
global $CFG;
$site = get_site();
$supportuser = core_user::get_support_user();
+ $pwresetmins = isset($CFG->pwresettime) ? floor($CFG->pwresettime / MINSECS) : 30;
$data = new stdClass();
$data->firstname = $user->firstname;
$data->lastname = $user->lastname;
+ $data->username = $user->username;
$data->sitename = format_string($site->fullname);
- $data->link = $CFG->httpswwwroot .'/login/forgot_password.php?p='. $user->secret .'&s='. urlencode($user->username);
+ $data->link = $CFG->httpswwwroot .'/login/forgot_password.php?token='. $resetrecord->token;
$data->admin = generate_email_signoff();
+ $data->resetminutes = $pwresetmins;
- $message = get_string('emailpasswordconfirmation', '', $data);
- $subject = get_string('emailpasswordconfirmationsubject', '', format_string($site->fullname));
+ $message = get_string('emailresetconfirmation', '', $data);
+ $subject = get_string('emailresetconfirmationsubject', '', format_string($site->fullname));
// Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
return email_to_user($user, $supportuser, $subject, $message);
View
14 login/forgot_password.php
@@ -28,11 +28,11 @@
require('../config.php');
require_once($CFG->libdir.'/authlib.php');
-require_once('lib.php');
+require_once(__DIR__ . '/lib.php');
require_once('forgot_password_form.php');
+require_once('set_password_form.php');
-$p_secret = optional_param('p', false, PARAM_RAW);
-$p_username = optional_param('s', false, PARAM_RAW);
+$token = optional_param('token', false, PARAM_RAW);
//HTTPS is required in this page when $CFG->loginhttps enabled
$PAGE->https_required();
@@ -60,12 +60,12 @@
redirect($CFG->wwwroot.'/index.php', get_string('loginalready'), 5);
}
-if (empty($p_secret)) {
- // This is a new password reset request
- // identify the user & send confirmation email
+if (empty($token)) {
+ // This is a new password reset request.
+ // Process the request; identify the user & send confirmation email:
forgotpw_process_request();
} else {
// User clicked on confirmation link in email message
// validate the token & set new password
- forgotpw_process_pwset($p_secret, $p_username);
+ forgotpw_process_pwset($token);
}
View
192 login/lib.php
@@ -2,6 +2,7 @@
define('PWRESET_STATUS_NOEMAILSENT', 1);
define('PWRESET_STATUS_TOKENSENT', 2);
define('PWRESET_STATUS_OTHEREMAILSENT', 3);
+define('PWRESET_STATUS_ALREADYSENT', 4);
/* This function processes a user's request to set a new password in the event they forgot the old one.
If no user identifier has been supplied, it displays a form where they can submit their identifier.
@@ -39,31 +40,54 @@ function forgotpw_process_request() {
$pwresetstatus = PWRESET_STATUS_NOEMAILSENT;
if ($user and !empty($user->confirmed)) {
$userauth = get_auth_plugin($user->auth);
- if ($userauth->can_reset_password() and is_enabled_auth($user->auth)
- and has_capability('moodle/user:changeownpassword', $systemcontext, $user->id)) {
- // send reset password confirmation
-
- // set 'secret' string
- $user->secret = random_string(15);
- $DB->set_field('user', 'secret', $user->secret, array('id'=>$user->id));
-
- if (send_password_change_confirmation_email($user)) {
- $pwresetstatus = PWRESET_STATUS_TOKENSENT;
+ if (!$userauth->can_reset_password() or !is_enabled_auth($user->auth)
+ or !has_capability('moodle/user:changeownpassword', $systemcontext, $user->id)) {
+ if (send_password_change_info($user)) {
+ $pwresetstatus = PWRESET_STATUS_OTHEREMAILSENT;
} else {
print_error('cannotmailconfirm');
}
-
} else {
- if (send_password_change_info($user)) {
- $pwresetstatus = PWRESET_STATUS_OTHEREMAILSENT;
+ // The account the requesting user claims to be is entitled to change their password.
+ // Check if they have an existing password reset in progress:
+ $resetinprogress = $DB->get_record('user_password_resets', array('userid' => $user->id));
+ if (empty($resetinprogress)) {
+ // Completely new reset request - common case
+ $resetrecord = create_reset_record($user);
+ $sendemail = true;
+ } elseif ($resetinprogress->timerequested < (time() - $CFG->pwresettime)) {
+ // Preexisting, but expired request - delete old record & create new one.
+ // Uncommon case - expired requests are cleaned up by cron.
+ $DB->delete_records('user_password_resets', array('id' => $resetinprogress->id));
+ $resetrecord = create_reset_record($user);
+ $sendemail = true;
+ } elseif (empty($resetinprogress->timererequested)) {
+ // Preexisting, valid request. This is the first time user has re-requested the reset
+ // Re-sending the same email once can actually help in certain circumstances
+ // eg by reducing the delay caused by greylisting.
+ $resetinprogress->timererequested = time();
+ $DB->update_record('user_password_resets', $resetinprogress);
+ $resetrecord = $resetinprogress;
+ $sendemail = true;
} else {
- print_error('cannotmailconfirm');
+ // Preexisting, valid request. User has already re-requested email.
+ $pwresetstatus = PWRESET_STATUS_ALREADYSENT;
+ $sendemail = false;
+ }
+
+ if ($sendemail) {
+ $sendresult = send_password_change_confirmation_email($user, $resetrecord);
+ if ($sendresult) {
+ $pwresetstatus = PWRESET_STATUS_TOKENSENT;
+ } else {
+ print_error('cannotmailconfirm');
+ }
}
}
}
// Any email has now been sent.
- // Next display results to requesting user if settings permit:
+ // Display results to requesting user if settings permit:
echo $OUTPUT->header();
if (!empty($CFG->protectusernames)) {
@@ -79,6 +103,12 @@ function forgotpw_process_request() {
} elseif (empty($user->email)) {
// User doesn't have an email set - can't send a password change confimation email.
notice(get_string('emailpasswordconfirmnoemail'), $CFG->wwwroot.'/index.php');
+ die; // never reached.
+ } else if ($pwresetstatus == PWRESET_STATUS_ALREADYSENT) {
+ // User found, protectusernames is off, but user has already (re) requested a reset.
+ // Don't send a 3rd reset email.
+ $stremailalreadysent = get_string('emailalreadysent');
+ notice($stremailalreadysent, $CFG->wwwroot.'/index.php');
die; // never reached
} elseif ($pwresetstatus == PWRESET_STATUS_NOEMAILSENT) {
// User found, protectusernames is off, but user is not confirmed
@@ -92,8 +122,8 @@ function forgotpw_process_request() {
// Confirm email sent
$protectedemail = preg_replace('/([^@]*)@(.*)/', '******@$2', $user->email); // obfuscate the email address to protect privacy
// This is a small usability problem - may be obfuscating the email address which the user has just supplied.
- $stremailpasswordconfirmsent = get_string('emailpasswordconfirmsent', '', $protectedemail);
- notice($stremailpasswordconfirmsent, $CFG->wwwroot.'/index.php');
+ $stremailresetconfirmsent = get_string('emailresetconfirmsent', '', $protectedemail);
+ notice($stremailresetconfirmsent, $CFG->wwwroot.'/index.php');
die; // never reached
}
die; // never reached
@@ -113,61 +143,105 @@ function forgotpw_process_request() {
}
/* This function processes a user's submitted token to validate the request to set a new password
- If the user's token is validated, they are emailed with a new password.
+ If the user's token is validated, they are prompted to set a new password.
+ * @param string $token the one-use identifier which should verify the password reset request as being valid
*/
-function forgotpw_process_pwset($token, $username) {
- global $DB, $CFG, $OUTPUT;
+function forgotpw_process_pwset($token) {
+ global $DB, $CFG, $OUTPUT, $PAGE, $SESSION;
$systemcontext = context_system::instance();
- $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0, 'suspended'=>0));
+ $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800;
+ $sql = 'SELECT u.*, upr.token, upr.timerequested, upr.id as tokenid FROM ' . $CFG->prefix . 'user u' .
+ ' INNER JOIN ' . $CFG->prefix . 'user_password_resets upr ON upr.userid = u.id ' .
+ 'WHERE upr.token = ?';
+ $user = $DB->get_record_sql($sql, array($token));
- if ($user and ($user->auth === 'nologin' or !is_enabled_auth($user->auth))) {
- // bad luck - user is not able to login, do not let them reset password
- $user = false;
+ $forgotpasswordurl = "{$CFG->httpswwwroot}/login/forgot_password.php";
+ if (empty($user) or ($user->timerequested < (time() - $pwresettime - DAYSECS))) {
+ // There is no valid reset request record - not even a recently expired one.
+ // (suspicious)
+ // Direct the user to the forgot password page to request a password reset
+ echo $OUTPUT->header();
+ notice(get_string('noresetrecord'), $forgotpasswordurl);
+ die; // never reached
}
-
- if (!empty($user) and $user->secret === '') {
+ if ($user->timerequested < (time() - $pwresettime)) {
+ // There is a reset record, but it's expired
+ // Direct the user to the forgot password page to request a password reset
+ $pwresetmins = floor($pwresettime / MINSECS);
echo $OUTPUT->header();
- print_error('secretalreadyused');
- } else if (!empty($user) and $user->secret == $token) {
- // make sure that url relates to a valid user
-
- // check this isn't guest user
- if (isguestuser($user)) {
- print_error('cannotresetguestpwd');
- }
-
- // Reset login lockout even of the password reset fails.
- login_unlock_account($user);
-
- // make sure user is allowed to change password
- require_capability('moodle/user:changeownpassword', $systemcontext, $user->id);
-
- if (!reset_password_and_mail($user)) {
- print_error('cannotresetmail');
- }
+ notice(get_string('resetrecordexpired', '', $pwresetmins), $forgotpasswordurl);
+ die; // never reached
+ }
- // Clear secret so that it can not be used again
- $user->secret = '';
- $DB->set_field('user', 'secret', $user->secret, array('id'=>$user->id));
+ if ($user->auth === 'nologin' or !is_enabled_auth($user->auth)) {
+ // bad luck - user is not able to login, do not let them set password
+ echo $OUTPUT->header();
+ print_error('forgotteninvalidurl');
+ die; // never reached
+ }
- $changepasswordurl = "{$CFG->httpswwwroot}/login/change_password.php";
- $a = new stdClass();
- $a->email = $user->email;
- $a->link = $changepasswordurl;
+ // check this isn't guest user
+ if (isguestuser($user)) {
+ print_error('cannotresetguestpwd');
+ }
+ // Token is correct, and unexpired.
+ $mform = new login_set_password_form(null, null, 'post', '', 'autocomplete="yes"');
+ $data = $mform->get_data();
+ if (empty($data)) {
+ // User hasn't submitted form, they got here directly from email link.
+ // Display the form:
+ $setdata = new stdClass();
+ $setdata->username = $user->username;
+ $setdata->username2 = $user->username;
+ $setdata->token = $user->token;
+ $mform->set_data($setdata);
+ $PAGE->verify_https_required();
echo $OUTPUT->header();
- notice(get_string('emailpasswordsent', '', $a), $changepasswordurl);
-
+ echo $OUTPUT->box(get_string('setpasswordinstructions'), 'generalbox boxwidthnormal boxaligncenter');
+ $mform->display();
+ echo $OUTPUT->footer();
+ return;
} else {
- if (!empty($user) and strlen($token) === 15) {
- // somebody probably tries to hack in by guessing secret - stop them!
- $DB->set_field('user', 'secret', '', array('id'=>$user->id));
+ // User has submitted form.
+ // Delete this token so it can't be used again:
+ $DB->delete_records('user_password_resets', array('id' => $user->tokenid));
+ $userauth = get_auth_plugin($user->auth);
+ if (!$userauth->user_update_password($user, $data->password)) {
+ print_error('errorpasswordupdate', 'auth');
}
- echo $OUTPUT->header();
- print_error('forgotteninvalidurl');
+ add_to_log(SITEID, 'user', 'set password', "view.php?id=$user->id&amp;course=" . SITEID, $user->id);
+ // Reset login lockout (if present) before a new password is set:
+ login_unlock_account($user);
+ // Clear any requirement to change passwords
+ unset_user_preference('auth_forcepasswordchange', $user);
+ unset_user_preference('create_password', $user);
+
+ if (!empty($user->lang)) {
+ // unset previous session language - use user preference instead
+ unset($SESSION->lang);
+ }
+ add_to_log(SITEID, 'user', 'login', "view.php?id=$user->id&course=".SITEID, $user->id, 0, $user->id);
+ complete_user_login($user);
+ $urltogo = get_postlogin_redirection();
+ unset($SESSION->wantsurl);
+ redirect($urltogo, get_string('passwordset'), 1);
}
+}
- die; //never reached
+/*
+ * Create a new record in the database to track a new password set request for user
+ * @param object $user the user record, the requester would like a new password set for.
+ * @return record created
+*/
+function create_reset_record ($user) {
+ global $CFG, $DB;
+ $resetrecord = new stdClass();
+ $resetrecord->timerequested = time();
+ $resetrecord->userid = $user->id;
+ $resetrecord->token = random_string(32);
+ $resetrecord->id = $DB->insert_record('user_password_resets', $resetrecord);
+ return $resetrecord;
}
/* Determine where a user should be redirected after they have been logged in
View
86 login/set_password_form.php
@@ -0,0 +1,86 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Set password form definition.
+ *
+ * @package core
+ * @subpackage auth
+ * @copyright 2006 Petr Skoda {@link http://skodak.org}
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once $CFG->libdir.'/formslib.php';
+
+class login_set_password_form extends moodleform {
+
+ function definition() {
+ global $USER, $CFG;
+
+ $mform = $this->_form;
+ $mform->setDisableShortforms(true);
+ $mform->addElement('header', 'setpassword', get_string('setpassword'), '');
+
+ // Include the username in the form so browsers will recognise that a password is being set.
+ $mform->addElement('text', 'username','','style="display: none;" autocomplete="on"');
+ $mform->setType('username', PARAM_RAW);
+ // Token gives authority to change password.
+ $mform->addElement('hidden', 'token', '');
+ $mform->setType('token', PARAM_ALPHANUM);
+
+ // visible elements
+ $mform->addElement('static', 'username2', get_string('username'));
+
+ if (!empty($CFG->passwordpolicy)){
+ $mform->addElement('static', 'passwordpolicyinfo', '', print_password_policy());
+ }
+ $mform->addElement('password', 'password', get_string('newpassword'), 'autocomplete="on"');
+ $mform->addRule('password', get_string('required'), 'required', null, 'client');
+ $mform->setType('password', PARAM_RAW);
+
+ $mform->addElement('password', 'password2', get_string('newpassword').' ('.get_String('again').')', 'autocomplete="on"');
+ $mform->addRule('password2', get_string('required'), 'required', null, 'client');
+ $mform->setType('password2', PARAM_RAW);
+
+ // buttons
+ $this->add_action_buttons(true);
+ }
+
+ // perform extra password change validation
+ function validation($data, $files) {
+ global $USER;
+ $errors = parent::validation($data, $files);
+
+ // Ignore submitted username.
+ // TODO: Validate submitted token.
+ if ($data['password'] <> $data['password2']) {
+ $errors['password'] = get_string('passwordsdiffer');
+ $errors['password2'] = get_string('passwordsdiffer');
+ return $errors;
+ }
+
+ $errmsg = '';//prevents eclipse warnings
+ if (!check_password_policy($data['password'], $errmsg)) {
+ $errors['password'] = $errmsg;
+ $errors['password2'] = $errmsg;
+ return $errors;
+ }
+
+ return $errors;
+ }
+}
View
2 version.php
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
-$version = 2013092700.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2013092700.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.

0 comments on commit 92de749

Please sign in to comment.
Something went wrong with that request. Please try again.