[ Index ] |
PHP Cross Reference of Unnamed Project |
[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 require_once($CFG->dirroot.'/user/lib.php'); 181 182 $pwresettime = isset($CFG->pwresettime) ? $CFG->pwresettime : 1800; 183 $sql = "SELECT u.*, upr.token, upr.timerequested, upr.id as tokenid 184 FROM {user} u 185 JOIN {user_password_resets} upr ON upr.userid = u.id 186 WHERE upr.token = ?"; 187 $user = $DB->get_record_sql($sql, array($token)); 188 189 $forgotpasswordurl = "{$CFG->httpswwwroot}/login/forgot_password.php"; 190 if (empty($user) or ($user->timerequested < (time() - $pwresettime - DAYSECS))) { 191 // There is no valid reset request record - not even a recently expired one. 192 // (suspicious) 193 // Direct the user to the forgot password page to request a password reset. 194 echo $OUTPUT->header(); 195 notice(get_string('noresetrecord'), $forgotpasswordurl); 196 die; // Never reached. 197 } 198 if ($user->timerequested < (time() - $pwresettime)) { 199 // There is a reset record, but it's expired. 200 // Direct the user to the forgot password page to request a password reset. 201 $pwresetmins = floor($pwresettime / MINSECS); 202 echo $OUTPUT->header(); 203 notice(get_string('resetrecordexpired', '', $pwresetmins), $forgotpasswordurl); 204 die; // Never reached. 205 } 206 207 if ($user->auth === 'nologin' or !is_enabled_auth($user->auth)) { 208 // Bad luck - user is not able to login, do not let them set password. 209 echo $OUTPUT->header(); 210 print_error('forgotteninvalidurl'); 211 die; // Never reached. 212 } 213 214 // Check this isn't guest user. 215 if (isguestuser($user)) { 216 print_error('cannotresetguestpwd'); 217 } 218 219 // Token is correct, and unexpired. 220 $mform = new login_set_password_form(null, $user, 'post', '', 'autocomplete="yes"'); 221 $data = $mform->get_data(); 222 if (empty($data)) { 223 // User hasn't submitted form, they got here directly from email link. 224 // Next, display the form. 225 $setdata = new stdClass(); 226 $setdata->username = $user->username; 227 $setdata->username2 = $user->username; 228 $setdata->token = $user->token; 229 $mform->set_data($setdata); 230 $PAGE->verify_https_required(); 231 echo $OUTPUT->header(); 232 echo $OUTPUT->box(get_string('setpasswordinstructions'), 'generalbox boxwidthnormal boxaligncenter'); 233 $mform->display(); 234 echo $OUTPUT->footer(); 235 return; 236 } else { 237 // User has submitted form. 238 // Delete this token so it can't be used again. 239 $DB->delete_records('user_password_resets', array('id' => $user->tokenid)); 240 $userauth = get_auth_plugin($user->auth); 241 if (!$userauth->user_update_password($user, $data->password)) { 242 print_error('errorpasswordupdate', 'auth'); 243 } 244 user_add_password_history($user->id, $data->password); 245 if (!empty($CFG->passwordchangelogout)) { 246 \core\session\manager::kill_user_sessions($user->id, session_id()); 247 } 248 // Reset login lockout (if present) before a new password is set. 249 login_unlock_account($user); 250 // Clear any requirement to change passwords. 251 unset_user_preference('auth_forcepasswordchange', $user); 252 unset_user_preference('create_password', $user); 253 254 if (!empty($user->lang)) { 255 // Unset previous session language - use user preference instead. 256 unset($SESSION->lang); 257 } 258 complete_user_login($user); // Triggers the login event. 259 260 \core\session\manager::apply_concurrent_login_limit($user->id, session_id()); 261 262 $urltogo = core_login_get_return_url(); 263 unset($SESSION->wantsurl); 264 redirect($urltogo, get_string('passwordset'), 1); 265 } 266 } 267 268 /** Create a new record in the database to track a new password set request for user. 269 * @param object $user the user record, the requester would like a new password set for. 270 * @return record created. 271 */ 272 function core_login_generate_password_reset ($user) { 273 global $DB; 274 $resetrecord = new stdClass(); 275 $resetrecord->timerequested = time(); 276 $resetrecord->userid = $user->id; 277 $resetrecord->token = random_string(32); 278 $resetrecord->id = $DB->insert_record('user_password_resets', $resetrecord); 279 return $resetrecord; 280 } 281 282 /** Determine where a user should be redirected after they have been logged in. 283 * @return string url the user should be redirected to. 284 */ 285 function core_login_get_return_url() { 286 global $CFG, $SESSION, $USER; 287 // Prepare redirection. 288 if (user_not_fully_set_up($USER)) { 289 $urltogo = $CFG->wwwroot.'/user/edit.php'; 290 // We don't delete $SESSION->wantsurl yet, so we get there later. 291 292 } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0 293 or strpos($SESSION->wantsurl, str_replace('http://', 'https://', $CFG->wwwroot)) === 0)) { 294 $urltogo = $SESSION->wantsurl; // Because it's an address in this site. 295 unset($SESSION->wantsurl); 296 } else { 297 // No wantsurl stored or external - go to homepage. 298 $urltogo = $CFG->wwwroot.'/'; 299 unset($SESSION->wantsurl); 300 } 301 302 // If the url to go to is the same as the site page, check for default homepage. 303 if ($urltogo == ($CFG->wwwroot . '/')) { 304 $homepage = get_home_page(); 305 // Go to my-moodle page instead of site homepage if defaulthomepage set to homepage_my. 306 if ($homepage == HOMEPAGE_MY && !is_siteadmin() && !isguestuser()) { 307 if ($urltogo == $CFG->wwwroot or $urltogo == $CFG->wwwroot.'/' or $urltogo == $CFG->wwwroot.'/index.php') { 308 $urltogo = $CFG->wwwroot.'/my/'; 309 } 310 } 311 } 312 return $urltogo; 313 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |