* Copyright © 2014 Strangecode, LLC * * This program 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. * * This program 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 this program. If not, see . */ /* * reset.php * * Facilitate password resets. * * @author Quinn Comendant * @version 1.0 * @since 13 Sep 2014 12:44:26 */ /******************************************************************** * CONFIG ********************************************************************/ require_once dirname(__FILE__) . '/_config.inc.php'; /******************************************************************** * MAIN ********************************************************************/ // If boomerang is set remember which page we came from so we can go back there. if (getFormData('boomerang', false) && isset($_SERVER['HTTP_REFERER'])) { $app->setBoomerangURL($_SERVER['HTTP_REFERER'], 'signup'); $app->setBoomerangURL($_SERVER['HTTP_REFERER'], 'previoussignup'); } $app->sslOn(); if (getFormData('c')) { // // Reset code provided, let's see about resetting that password. // $nav->add(_("Reset your password")); $main_template = 'reset_form.inc.html'; // Code validation occurs on GET and POST. if (!$user_id = validatePasswordReset(getFormData('c'))) { $app->dieURL('/reset.php'); } if ('POST' == $_SERVER['REQUEST_METHOD']) { $app->requireValidCSRFToken(); if ($fv->notEmpty('userpass', sprintf(_("%s cannot be blank."), _("Password")))) { if (getFormData('complexity') < 20) { $fv->addError('userpass', sprintf(_("Please choose a more complex password. Make it longer or add numbers and punctuation."), null), MSG_ERR, __FILE__, __LINE__); } } if (!$fv->anyErrors()) { // Set user password. $auth->setPassword($user_id, getFormData('userpass')); $user = User::get(array('user_id' => $user_id)); $app->raiseMsg(sprintf(_("Thanks %s, you have successfully changed your password. Go ahead and try it now. (Remember, your username is ‘%s’.)"), $user['first_name'], $user['username']), MSG_SUCCESS, __FILE__, __LINE__); $app->dieURL('/login.php'); } } } else { // // No code provided, we assume they need one sent to them. // $nav->add(_("Doh, I forgot my credentials!")); $main_template = 'forgot_form.inc.html'; if ('POST' == $_SERVER['REQUEST_METHOD']) { $app->requireValidCSRFToken(); $user = User::get(array('email' => getFormData('email'))); if ($fv->notEmpty('email', sprintf(_("%s cannot be blank."), _("Email")))) { $fv->stringLength('email', 0, 255, sprintf(_("%s must be %d-to-%d characters in length."), _("Email address"), 0, 255)); $fv->validateEmail('email'); if (false === $user) { $fv->addError('email', sprintf(_("We couldn’t find an account with the email address %s."), getFormData('email')), MSG_ERR, __FILE__, __LINE__); } } if (!$fv->anyErrors()) { // Send email reset link. if (requestPasswordReset($user)) { $app->raiseMsg(sprintf(_("A password reset link was sent to %s. It will expire in one hour."), getFormData('email')), MSG_SUCCESS, __FILE__, __LINE__); $app->dieURL('/login.php'); } else { $app->raiseMsg(sprintf(_("Oops, something went wrong. Please try that again."), null), MSG_ERR, __FILE__, __LINE__); } } } } /******************************************************************** * OUTPUT ********************************************************************/ $frm = resetForm(getFormData()); // Templates. include 'header.inc.html'; include $main_template; include 'footer.inc.html'; /******************************************************************** * FUNCTIONS ********************************************************************/ /* * Reset form values to default, optionally merging posted form data. * * @access public * @param bool $merge Merge existing values from $_REQUEST? * @param array $new New values to merge with default values. * @return array Initialized array of form values. * @author Quinn Comendant * @version 1.0 * @since 08 Nov 2014 20:14:22 */ function resetForm($merge=false, $new=array()) { $frm = array( 'email' => '', 'password' => '', ); return $merge ? array_merge($frm, getFormData(), $new) : array_merge($frm, $new); } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ function requestPasswordReset($user) { $app =& App::getInstance(); // Reset code is the user_id + timestamp in hex, using the user's modified_datetime as part of the salt, which causes the code to become invalid after the user is modified. $reset_code = addSignature(sprintf('%s-%s', $user['user_id'], dechex(time())), $user['modified_datetime'] . 'pybjojreoveulaw'); $email = new Email(array( 'to' => sprintf('%s %s <%s>', $user['first_name'], $user['last_name'], $user['email']), 'from' => sprintf('%s <%s>', $app->getParam('site_name'), $app->getParam('site_email')), 'subject' => _("Reset your password"), )); $email->setTemplate('password_reset.inc.eml'); $email->replace(array( 'first_name' => $user['first_name'], 'username' => $user['username'], 'reset_url' => sprintf('%s/reset.php?c=%s', $app->getParam('site_url'), $reset_code), )); return $email->send(); } /* * * * @access public * @param * @return * @author Quinn Comendant * @version 1.0 * @since 16 Nov 2014 17:45:40 */ function validatePasswordReset($code) { $app =& App::getInstance(); // We can't trust this data until the signature is verified. list($user_id, $time) = explode('-', removeSignature($code)); $user = User::get(array('user_id' => $user_id)); // Reset code is the user_id + timestamp in hex, using the user's modified_datetime as part of the salt, which causes the code to become invalid after the user is modified. if (!$user || !verifySignature($code, $user['modified_datetime'] . 'pybjojreoveulaw')) { // Reset code invalid. $app->raiseMsg(_("Sorry, that password reset link is not valid."), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Invalid reset code: %s', $code), LOG_NOTICE, __FILE__, __LINE__); return false; } if (hexdec($time) < time() - 3600) { // Reset code has expired (>= 1 hour ago). $app->raiseMsg(_("Sorry, that password reset link has expired. Try again?"), MSG_ERR, __FILE__, __LINE__); $app->logMsg(sprintf('Expired reset code %s (from %s)', $code, date('Y-m-d', hexdec($time))), LOG_NOTICE, __FILE__, __LINE__); return false; } return $user_id; }