Start writing password reset
This commit is contained in:
parent
1735c00d53
commit
f112e3b143
|
@ -0,0 +1,59 @@
|
||||||
|
import { setupInfoModal } from "../setupListeners";
|
||||||
|
import { request } from "./helpers";
|
||||||
|
|
||||||
|
export function renderForgotPasswordForm() {
|
||||||
|
const modal = document.createElement('section');
|
||||||
|
modal.classList.add('modal');
|
||||||
|
modal.innerHTML = `<div class="modal-background"></div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<a class="close-button">×︎</a>
|
||||||
|
<section class="info-modal" id="forgotPasswordForm">
|
||||||
|
<h2>Forgot Password</h2>
|
||||||
|
<p>Enter the email address associated with your Lexiconga account to initiate a password reset.</p>
|
||||||
|
<label>Email<br>
|
||||||
|
<input type="email" id="forgotPasswordEmailField" style="max-width:250px;" maxlength="100">
|
||||||
|
</label>
|
||||||
|
<section id="forgotPasswordErrorMessages"></section>
|
||||||
|
<button class="button" id="forgotPasswordSubmit">Email Password Reset Key</button>
|
||||||
|
</section>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
setupStartResetForm();
|
||||||
|
setupInfoModal(modal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupStartResetForm() {
|
||||||
|
document.getElementById('forgotPasswordSubmit').addEventListener('click', sendPasswordReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPasswordReset() {
|
||||||
|
const email = document.getElementById('forgotPasswordEmailField').value.trim();
|
||||||
|
const errorMessageElement = document.getElementById('forgotPasswordErrorMessages');
|
||||||
|
let errorMessage = '';
|
||||||
|
|
||||||
|
if (email === '') {
|
||||||
|
errorMessage += '<p class="red bold">Please enter an email address.</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessageElement.innerHTML = errorMessage;
|
||||||
|
|
||||||
|
if (errorMessage === '') {
|
||||||
|
request({
|
||||||
|
action: 'initiate-password-reset',
|
||||||
|
email,
|
||||||
|
}, success => {
|
||||||
|
console.log(success);
|
||||||
|
}, error => {
|
||||||
|
errorMessage += '<p class="red bold">' + error + '</p>';
|
||||||
|
}).then(() => {
|
||||||
|
errorMessageElement.innerHTML = errorMessage;
|
||||||
|
if (errorMessage === '') {
|
||||||
|
document.getElementById('forgotPasswordForm').innerHTML = `<h2>Password Reset Key Sent</h2>
|
||||||
|
<p>Go check your email for the password reset link.</p>
|
||||||
|
<p><em>Note that it may be sent to your spam/junk folder by mistake.</em></p>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import { logIn, createAccount } from "./login";
|
||||||
import { setCookie } from "../StackOverflow/cookie";
|
import { setCookie } from "../StackOverflow/cookie";
|
||||||
import { changeDictionary, createNewDictionary } from "./dictionaryManagement";
|
import { changeDictionary, createNewDictionary } from "./dictionaryManagement";
|
||||||
import { addMessage } from "../utilities";
|
import { addMessage } from "../utilities";
|
||||||
|
import { renderForgotPasswordForm } from "./passwordReset";
|
||||||
|
|
||||||
export function setupLoginModal(modal) {
|
export function setupLoginModal(modal) {
|
||||||
const closeElements = modal.querySelectorAll('.modal-background, .close-button');
|
const closeElements = modal.querySelectorAll('.modal-background, .close-button');
|
||||||
|
@ -36,6 +37,7 @@ export function setupLoginModal(modal) {
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('loginSubmit').addEventListener('click', logIn);
|
document.getElementById('loginSubmit').addEventListener('click', logIn);
|
||||||
|
document.getElementById('forgotPasswordButton').addEventListener('click', renderForgotPasswordForm);
|
||||||
document.getElementById('createAccountSubmit').addEventListener('click', createAccount);
|
document.getElementById('createAccountSubmit').addEventListener('click', createAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -267,6 +267,74 @@ VALUES (?, ?, ?, ?, ?)';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setPasswordReset($email) {
|
||||||
|
$date = date("Y-m-d H:i:s");
|
||||||
|
$reset_code = random_int(0, 999999999);
|
||||||
|
$reset_code_hash = $this->token->hash($reset_code);
|
||||||
|
$query = "UPDATE `users` SET `password_reset_code`=?, `password_reset_date`=? WHERE `email`=?;";
|
||||||
|
$reset = $this->db->execute($query, array(
|
||||||
|
$reset_code,
|
||||||
|
$date,
|
||||||
|
$email,
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($reset) {
|
||||||
|
$user_data = $this->getUserDataByEmailForPasswordReset($email);
|
||||||
|
if ($user_data) {
|
||||||
|
$to = $email;
|
||||||
|
$subject = "Here's your Lexiconga password reset link";
|
||||||
|
$message = "Hello " . $user_data['public_name'] . "\r\n\r\nSomeone has requested a password reset link for your Lexiconga account. If it was you, you can reset your password by going to the link below and entering a new password for yourself:\r\n";
|
||||||
|
$message .= "http://lexicon.ga/passwordreset?account=" . $user_data['id'] . "&code=" . $reset_code_hash . "\r\n\r\n";
|
||||||
|
$message .= "If it wasn't you who requested the link, you can ignore this email since it was only sent to you, but you might want to consider changing your password when you have a chance.\r\n\r\n";
|
||||||
|
$message .= "The password link will only be valid for today until you use it.\r\n\r\n";
|
||||||
|
$message .= "Thanks!\r\nThe Lexiconga Admins";
|
||||||
|
$header = "From: Lexiconga Password Reset <donotreply@lexicon.ga>\r\n"
|
||||||
|
. "Reply-To: help@lexicon.ga\r\n"
|
||||||
|
. "X-Mailer: PHP/" . phpversion();
|
||||||
|
if (mail($to, $subject, $message, $header)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return array(
|
||||||
|
'error' => 'Could not send email to ' . $email,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return array(
|
||||||
|
'error' => $this->db->last_error_info,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkPasswordReset($id, $code) {
|
||||||
|
$date = date("Y-m-d");
|
||||||
|
$daterange = "'" . $date . " 00:00:00' AND '" . $date . " 23:59:59'";
|
||||||
|
$unhashed_code = $this->token->unhash($code);
|
||||||
|
$query = "SELECT * FROM `users` WHERE `id`=? AND `password_reset_code`=? AND `password_reset_date` BETWEEN " . $daterange . ";";
|
||||||
|
$stmt = $this->db->query($query, array(
|
||||||
|
$id,
|
||||||
|
$unhashed_code,
|
||||||
|
));
|
||||||
|
$results = $stmt->fetchAll();
|
||||||
|
|
||||||
|
if ($stmt && $results) {
|
||||||
|
return count($results) === 1;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetPassword($password, $email) {
|
||||||
|
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$query = "UPDATE `users` SET `password`=?, `password_reset_date`='0000-00-00 00:00:00' WHERE `email`=?;";
|
||||||
|
return $this->db->execute($query, array(
|
||||||
|
$password_hash,
|
||||||
|
$email,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
private function generateUserToken ($user_id, $dictionary_id) {
|
private function generateUserToken ($user_id, $dictionary_id) {
|
||||||
$user_data = array(
|
$user_data = array(
|
||||||
'id' => intval($user_id),
|
'id' => intval($user_id),
|
||||||
|
@ -287,4 +355,17 @@ VALUES (?, ?, ?, ?, ?)';
|
||||||
$stmt = $this->db->query($update_query, array($new_password));
|
$stmt = $this->db->query($update_query, array($new_password));
|
||||||
return $stmt->rowCount() === 1;
|
return $stmt->rowCount() === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getUserDataByEmailForPasswordReset($email) {
|
||||||
|
$query = 'SELECT id, public_name FROM users WHERE email=?';
|
||||||
|
$stmt = $this->db->query($query, array($email));
|
||||||
|
$result = $stmt->fetch();
|
||||||
|
if ($stmt && $result) {
|
||||||
|
return array(
|
||||||
|
'id' => $result['id'],
|
||||||
|
'public_name' => $result['public_name'] ? $result['public_name'] : 'Lexiconger',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -418,6 +418,32 @@ switch ($action) {
|
||||||
'error' => true,
|
'error' => true,
|
||||||
), 400);
|
), 400);
|
||||||
}
|
}
|
||||||
|
case 'initiate-password-reset': {
|
||||||
|
if (isset($request['email'])) {
|
||||||
|
$user = new User();
|
||||||
|
$password_reset = $user->setPasswordReset($request['email']);
|
||||||
|
if ($password_reset === true) {
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => $password_reset,
|
||||||
|
'error' => false,
|
||||||
|
), 200);
|
||||||
|
}
|
||||||
|
if (isset($password_reset['error'])) {
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => $password_reset['error'],
|
||||||
|
'error' => true,
|
||||||
|
), 500);
|
||||||
|
}
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => 'Could not send password reset key: email not found',
|
||||||
|
'error' => true,
|
||||||
|
), 401);
|
||||||
|
}
|
||||||
|
return Response::json(array(
|
||||||
|
'data' => 'Could not send password reset key: required data missing',
|
||||||
|
'error' => true,
|
||||||
|
), 400);
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return Response::html('Hi!');
|
return Response::html('Hi!');
|
||||||
|
|
Loading…
Reference in New Issue