[ Index ] |
PHP Cross Reference of moodle-2.8 |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * 19 * Login library file of login/password related Moodle functions. 20 * 21 * @package core 22 * @subpackage lib 23 * @copyright Catalyst IT 24 * @copyright Peter Bulmer 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 define('PWRESET_STATUS_NOEMAILSENT', 1); 28 define('PWRESET_STATUS_TOKENSENT', 2); 29 define('PWRESET_STATUS_OTHEREMAILSENT', 3); 30 define('PWRESET_STATUS_ALREADYSENT', 4); 31 32 /** 33 * Processes a user's request to set a new password in the event they forgot the old one. 34 * If no user identifier has been supplied, it displays a form where they can submit their identifier. 35 * Where they have supplied identifier, the function will check their status, and send email as appropriate. 36 */ 37 function core_login_process_password_reset_request() { 38 global $DB, $OUTPUT, $CFG, $PAGE; 39 $systemcontext = context_system::instance(); 40 $mform = new login_forgot_password_form(); 41 42 if ($mform->is_cancelled()) { 43 redirect(get_login_url()); 44 45 } else if ($data = $mform->get_data()) { 46 // Requesting user has submitted form data. 47 // Next find the user account in the database which the requesting user claims to own. 48 if (!empty($data->username)) { 49 // Username has been specified - load the user record based on that. 50 $username = core_text::strtolower($data->username); // Mimic the login page process. 51 $userparams = array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 0, 'suspended' => 0); 52 $user = $DB->get_record('user', $userparams); 53 } else { 54 // Try to load the user record based on email address. 55 // this is tricky because 56 // 1/ the email is not guaranteed to be unique - TODO: send email with all usernames to select the account for pw reset 57 // 2/ mailbox may be case sensitive, the email domain is case insensitive - let's pretend it is all case-insensitive. 58 59 $select = $DB->sql_like('email', ':email', false, true, false, '|') . 60 " AND mnethostid = :mnethostid AND deleted=0 AND suspended=0"; 61 $params = array('email' => $DB->sql_like_escape($data->email, '|'), 'mnethostid' => $CFG->mnet_localhost_id); 62 $user = $DB->get_record_select('user', $select, $params, '*', IGNORE_MULTIPLE); 63 } 64 65 // Target user details have now been identified, or we know that there is no such account. 66 // Send email address to account's email address if appropriate. 67 $pwresetstatus = PWRESET_STATUS_NOEMAILSENT; 68 if ($user and !empty($user->confirmed)) { 69 $userauth = get_auth_plugin($user->auth); 70 if (!$userauth->can_reset_password() or !is_enabled_auth($user->auth) 71 or !has_capability('moodle/user:changeownpassword', $systemcontext, $user->id)) { 72 if (send_password_change_info($user)) { 73 $pwresetstatus = PWRESET_STATUS_OTHEREMAILSENT; 74 } else { 75 print_error('cannotmailconfirm'); 76 } 77 } else { 78 // The account the requesting user claims to be is entitled to change their password. 79 // Next, check if they have an existing password reset in progress. 80 $resetinprogress = $DB->get_record('user_password_resets', array('userid' => $user->id)); 81 if (empty($resetinprogress)) { 82 // Completely new reset request - common case. 83 $resetrecord = core_login_generate_password_reset($user); 84 $sendemail = true; 85 } else if ($resetinprogress->timerequested < (time() - $CFG->pwresettime)) { 86 // Preexisting, but expired request - delete old record & create new one. 87 // Uncommon case - expired requests are cleaned up by cron. 88 $DB->delete_records('user_password_resets', array('id' => $resetinprogress->id)); 89 $resetrecord = core_login_generate_password_reset($user); 90 $sendemail = true; 91 } else if (empty($resetinprogress->timererequested)) { 92 // Preexisting, valid request. This is the first time user has re-requested the reset. 93 // Re-sending the same email once can actually help in certain circumstances 94 // eg by reducing the delay caused by greylisting. 95 $resetinprogress->timererequested = time(); 96 $DB->update_record('user_password_resets', $resetinprogress); 97 $resetrecord = $resetinprogress; 98 $sendemail = true; 99 } else { 100 // Preexisting, valid request. User has already re-requested email. 101 $pwresetstatus = PWRESET_STATUS_ALREADYSENT; 102 $sendemail = false; 103 } 104 105 if ($sendemail) { 106 $sendresult = send_password_change_confirmation_email($user, $resetrecord); 107 if ($sendresult) { 108 $pwresetstatus = PWRESET_STATUS_TOKENSENT; 109 } else { 110 print_error('cannotmailconfirm'); 111 } 112 } 113 } 114 } 115 116 // Any email has now been sent. 117 // Next display results to requesting user if settings permit. 118 echo $OUTPUT->header(); 119 120 if (!empty($CFG->protectusernames)) { 121 // Neither confirm, nor deny existance of any username or email address in database. 122 // Print general (non-commital) message. 123 notice(get_string('emailpasswordconfirmmaybesent'), $CFG->wwwroot.'/index.php'); 124 die; // Never reached. 125 } else if (empty($user)) { 126 // Protect usernames is off, and we couldn't find the user with details specified. 127 // Print failure advice. 128 notice(get_string('emailpasswordconfirmnotsent'), $CFG->wwwroot.'/forgot_password.php'); 129 die; // Never reached. 130 } else if (empty($user->email)) { 131 // User doesn't have an email set - can't send a password change confimation email. 132 notice(get_string('emailpasswordconfirmnoemail'), $CFG->wwwroot.'/index.php'); 133 die; // Never reached. 134 } else if ($pwresetstatus == PWRESET_STATUS_ALREADYSENT) { 135 // User found, protectusernames is off, but user has already (re) requested a reset. 136 // Don't send a 3rd reset email. 137 $stremailalreadysent = get_string('emailalreadysent'); 138 notice($stremailalreadysent, $CFG->wwwroot.'/index.php'); 139 die; // Never reached. 140 } else if ($pwresetstatus == PWRESET_STATUS_NOEMAILSENT) { 141 // User found, protectusernames is off, but user is not confirmed. 142 // Pretend we sent them an email. 143 // This is a big usability problem - need to tell users why we didn't send them an email. 144 // Obfuscate email address to protect privacy. 145 $protectedemail = preg_replace('/([^@]*)@(.*)/', '******@$2', $user->email); 146 $stremailpasswordconfirmsent = get_string('emailpasswordconfirmsent', '', $protectedemail); 147 notice($stremailpasswordconfirmsent, $CFG->wwwroot.'/index.php'); 148 die; // Never reached. 149 } else { 150 // Confirm email sent. (Obfuscate email address to protect privacy). 151 $protectedemail = preg_replace('/([^@]*)@(.*)/', '******@$2', $user->email); 152 // This is a small usability problem - may be obfuscating the email address which the user has just supplied. 153 $stremailresetconfirmsent = get_string('emailresetconfirmsent', '', $protectedemail); 154 notice($stremailresetconfirmsent, $CFG->wwwroot.'/index.php'); 155 die; // Never reached. 156 } 157 die; // Never reached. 158 } 159 160 // Make sure we really are on the https page when https login required. 161 $PAGE->verify_https_required(); 162 163 // DISPLAY FORM. 164 165 echo $OUTPUT->header(); 166 echo $OUTPUT->box(get_string('passwordforgotteninstructions2'), 'generalbox boxwidthnormal boxaligncenter'); 167 $mform->display(); 168 169 echo $OUTPUT->footer(); 170 } 171 172 /** 173 * This function processes a user's submitted token to validate the request to set a new password. 174 * If the user's token is validated, they are prompted to set a new password. 175 * @param string $token the one-use identifier which should verify the password reset request as being valid. 176 * @return void 177 */ 178 function core_login_process_password_set($token) { 179 global $DB, $CFG, $OUTPUT, $PAGE, $SESSION; 180 $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800; 181 $sql = "SELECT u.*, upr.token, upr.timerequested, upr.id as tokenid 182 FROM {user} u 183 JOIN {user_password_resets} upr ON upr.userid = u.id 184 WHERE upr.token = ?"; 185 $user = $DB->get_record_sql($sql, array($token)); 186 187 $forgotpasswordurl = "{$CFG->httpswwwroot}/login/forgot_password.php"; 188 if (empty($user) or ($user->timerequested < (time() - $pwresettime - DAYSECS))) { 189 // There is no valid reset request record - not even a recently expired one. 190 // (suspicious) 191 // Direct the user to the forgot password page to request a password reset. 192 echo $OUTPUT->header(); 193 notice(get_string('noresetrecord'), $forgotpasswordurl); 194 die; // Never reached. 195 } 196 if ($user->timerequested < (time() - $pwresettime)) { 197 // There is a reset record, but it's expired. 198 // Direct the user to the forgot password page to request a password reset. 199 $pwresetmins = floor($pwresettime / MINSECS); 200 echo $OUTPUT->header(); 201 notice(get_string('resetrecordexpired', '', $pwresetmins), $forgotpasswordurl); 202 die; // Never reached. 203 } 204 205 if ($user->auth === 'nologin' or !is_enabled_auth($user->auth)) { 206 // Bad luck - user is not able to login, do not let them set password. 207 echo $OUTPUT->header(); 208 print_error('forgotteninvalidurl'); 209 die; // Never reached. 210 } 211 212 // Check this isn't guest user. 213 if (isguestuser($user)) { 214 print_error('cannotresetguestpwd'); 215 } 216 217 // Token is correct, and unexpired. 218 $mform = new login_set_password_form(null, null, 'post', '', 'autocomplete="yes"'); 219 $data = $mform->get_data(); 220 if (empty($data)) { 221 // User hasn't submitted form, they got here directly from email link. 222 // Next, display the form. 223 $setdata = new stdClass(); 224 $setdata->username = $user->username; 225 $setdata->username2 = $user->username; 226 $setdata->token = $user->token; 227 $mform->set_data($setdata); 228 $PAGE->verify_https_required(); 229 echo $OUTPUT->header(); 230 echo $OUTPUT->box(get_string('setpasswordinstructions'), 'generalbox boxwidthnormal boxaligncenter'); 231 $mform->display(); 232 echo $OUTPUT->footer(); 233 return; 234 } else { 235 // User has submitted form. 236 // Delete this token so it can't be used again. 237 $DB->delete_records('user_password_resets', array('id' => $user->tokenid)); 238 $userauth = get_auth_plugin($user->auth); 239 if (!$userauth->user_update_password($user, $data->password)) { 240 print_error('errorpasswordupdate', 'auth'); 241 } 242 // Reset login lockout (if present) before a new password is set. 243 login_unlock_account($user); 244 // Clear any requirement to change passwords. 245 unset_user_preference('auth_forcepasswordchange', $user); 246 unset_user_preference('create_password', $user); 247 248 if (!empty($user->lang)) { 249 // Unset previous session language - use user preference instead. 250 unset($SESSION->lang); 251 } 252 complete_user_login($user); // Triggers the login event. 253 254 $urltogo = core_login_get_return_url(); 255 unset($SESSION->wantsurl); 256 redirect($urltogo, get_string('passwordset'), 1); 257 } 258 } 259 260 /** Create a new record in the database to track a new password set request for user. 261 * @param object $user the user record, the requester would like a new password set for. 262 * @return record created. 263 */ 264 function core_login_generate_password_reset ($user) { 265 global $DB; 266 $resetrecord = new stdClass(); 267 $resetrecord->timerequested = time(); 268 $resetrecord->userid = $user->id; 269 $resetrecord->token = random_string(32); 270 $resetrecord->id = $DB->insert_record('user_password_resets', $resetrecord); 271 return $resetrecord; 272 } 273 274 /** Determine where a user should be redirected after they have been logged in. 275 * @return string url the user should be redirected to. 276 */ 277 function core_login_get_return_url() { 278 global $CFG, $SESSION, $USER; 279 // Prepare redirection. 280 if (user_not_fully_set_up($USER)) { 281 $urltogo = $CFG->wwwroot.'/user/edit.php'; 282 // We don't delete $SESSION->wantsurl yet, so we get there later. 283 284 } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0 285 or strpos($SESSION->wantsurl, str_replace('http://', 'https://', $CFG->wwwroot)) === 0)) { 286 $urltogo = $SESSION->wantsurl; // Because it's an address in this site. 287 unset($SESSION->wantsurl); 288 } else { 289 // No wantsurl stored or external - go to homepage. 290 $urltogo = $CFG->wwwroot.'/'; 291 unset($SESSION->wantsurl); 292 } 293 294 // If the url to go to is the same as the site page, check for default homepage. 295 if ($urltogo == ($CFG->wwwroot . '/')) { 296 $homepage = get_home_page(); 297 // Go to my-moodle page instead of site homepage if defaulthomepage set to homepage_my. 298 if ($homepage == HOMEPAGE_MY && !is_siteadmin() && !isguestuser()) { 299 if ($urltogo == $CFG->wwwroot or $urltogo == $CFG->wwwroot.'/' or $urltogo == $CFG->wwwroot.'/index.php') { 300 $urltogo = $CFG->wwwroot.'/my/'; 301 } 302 } 303 } 304 return $urltogo; 305 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:29:05 2014 | Cross-referenced by PHPXref 0.7.1 |