Specify pass algorithm, remove some duplication
This commit is contained in:
parent
65e25c608f
commit
634cb477ef
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "fwdekker/death-notifier",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"version": "0.0.30", "_comment_version": "Also update version in `package.json`!",
|
||||
"version": "0.0.31", "_comment_version": "Also update version in `package.json`!",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "death-notifier",
|
||||
"version": "0.0.30", "_comment_version": "Also update version in `composer.json`!",
|
||||
"version": "0.0.31", "_comment_version": "Also update version in `composer.json`!",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"author": "Florine W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
;<?php exit(); ?>
|
||||
|
||||
# TODO: Add feature for global site message
|
||||
# TODO: Add i18n
|
||||
|
||||
[admin]
|
||||
# Password to use the CLI of `api.php`. Until this value is changed from its default, the feature is disabled
|
||||
cli_secret = REPLACE THIS WITH A SECRET VALUE
|
||||
|
|
|
@ -173,6 +173,7 @@
|
|||
<div class="row hidden" id="accountRow">
|
||||
<div class="column">
|
||||
<h2>Manage account</h2>
|
||||
<!-- TODO: Position these buttons more nicely -->
|
||||
<form id="logoutForm" novalidate>
|
||||
<p class="formValidationInfo"><span class="validationInfo"></span></p>
|
||||
<button id="logoutButton">Log out</button>
|
||||
|
@ -228,6 +229,7 @@
|
|||
</label>
|
||||
<button id="updatePasswordButton">Change password</button>
|
||||
</form>
|
||||
<!-- TODO: Add forgot password button after logging in -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -78,6 +78,22 @@ class UserManager
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns `true` if there is a user with the given email address.
|
||||
*
|
||||
* @param string $email the email address to check
|
||||
* @return bool `true` if there is a user with the given email address
|
||||
*/
|
||||
private function email_is_used(string $email): bool
|
||||
{
|
||||
$stmt = $this->conn->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE email=:email);");
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch();
|
||||
return $result[0] === 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new user.
|
||||
*
|
||||
|
@ -99,11 +115,7 @@ class UserManager
|
|||
$this->conn->beginTransaction();
|
||||
|
||||
// Check if email address is already in use
|
||||
$stmt = $this->conn->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE email=:email);");
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch();
|
||||
if ($result[0] === 1) {
|
||||
if ($this->email_is_used($email)) {
|
||||
$this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: ["target" => "email", "message" => "Email address already in use."],
|
||||
|
@ -116,8 +128,7 @@ class UserManager
|
|||
VALUES (:email, :password)
|
||||
RETURNING email_verification_token;");
|
||||
$stmt->bindValue(":email", $email);
|
||||
// TODO: Specify password hash function, for forwards compatibility
|
||||
$stmt->bindValue(":password", password_hash($password, PASSWORD_DEFAULT));
|
||||
$stmt->bindValue(":password", password_hash($password, PASSWORD_BCRYPT));
|
||||
$stmt->execute();
|
||||
$email_verification_token = $stmt->fetchAll(PDO::FETCH_ASSOC)[0]["email_verification_token"];
|
||||
$this->mailer->queue_register_password($email, $email_verification_token);
|
||||
|
@ -239,14 +250,8 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
// TODO: Reject update if email address was changed too recently (within, say, 5 minutes)
|
||||
|
||||
// Check if email address is already in use
|
||||
$stmt = $this->conn->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE email=:email);");
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch();
|
||||
if ($result[0] === 1) {
|
||||
if ($this->email_is_used($email)) {
|
||||
$this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: ["target" => "email", "message" => "Email address already in use."],
|
||||
|
@ -283,6 +288,7 @@ class UserManager
|
|||
{
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
// Check if email is verified
|
||||
$stmt = $this->conn->prepare("SELECT email, email_verification_token, email_verification_token_timestamp
|
||||
FROM users
|
||||
WHERE uuid=:uuid;");
|
||||
|
@ -297,6 +303,7 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
// Check if new verification can be sent
|
||||
$minutes_since_last_verify_email =
|
||||
(new DateTime("@{$user["email_verification_token_timestamp"]}"))->diff(new DateTime(), absolute: true)->i;
|
||||
if ($minutes_since_last_verify_email < self::MINUTES_BETWEEN_VERIFICATION_EMAILS) {
|
||||
|
@ -313,6 +320,7 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
// Queue verification email
|
||||
$stmt = $this->conn->prepare("UPDATE users
|
||||
SET email_verification_token_timestamp=unixepoch()
|
||||
WHERE uuid=:uuid;");
|
||||
|
@ -336,7 +344,7 @@ class UserManager
|
|||
{
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
|
||||
// Check if token is correct for email
|
||||
$stmt = $this->conn->prepare("SELECT email_verification_token_timestamp
|
||||
FROM users
|
||||
WHERE email=:email AND email_verification_token=:token;");
|
||||
|
@ -357,6 +365,7 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
// Check if token is still valid
|
||||
$token_timestamp = $results[0]["email_verification_token_timestamp"];
|
||||
$minutes_since_creation = (new DateTime("@$token_timestamp"))->diff(new DateTime(), absolute: true)->i;
|
||||
if ($minutes_since_creation > self::MINUTES_VALID_VERIFICATION) {
|
||||
|
@ -370,6 +379,7 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
// Set as verified
|
||||
$stmt = $this->conn->prepare("UPDATE users SET email_verification_token=null WHERE email=:email;");
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->execute();
|
||||
|
@ -419,7 +429,7 @@ class UserManager
|
|||
password_reset_token=null
|
||||
WHERE uuid=:uuid;");
|
||||
$stmt->bindValue(":uuid", $uuid);
|
||||
$stmt->bindValue(":password", password_hash($password_new, PASSWORD_DEFAULT));
|
||||
$stmt->bindValue(":password", password_hash($password_new, PASSWORD_BCRYPT));
|
||||
$stmt->execute();
|
||||
|
||||
// Respond
|
||||
|
@ -491,9 +501,9 @@ class UserManager
|
|||
* @return Response a satisfied `Response` with payload `null` if the password reset token is currently valid, or an
|
||||
* unsatisfied `Response` otherwise
|
||||
*/
|
||||
public function validate_password_reset_token(string $email, string $token): Response
|
||||
public function validate_password_reset_token(string $email, string $token, bool $use_transaction = true): Response
|
||||
{
|
||||
$this->conn->beginTransaction();
|
||||
if ($use_transaction) $this->conn->beginTransaction();
|
||||
|
||||
$stmt = $this->conn->prepare("SELECT password_reset_token_timestamp
|
||||
FROM users WHERE email=:email AND password_reset_token=:token;");
|
||||
|
@ -502,7 +512,7 @@ class UserManager
|
|||
$stmt->execute();
|
||||
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (sizeof($results) === 0) {
|
||||
$this->conn->rollBack();
|
||||
if ($use_transaction) $this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: [
|
||||
"target" => null,
|
||||
|
@ -515,7 +525,7 @@ class UserManager
|
|||
$token_timestamp = $results[0]["password_reset_token_timestamp"];
|
||||
$minutes_since_creation = (new DateTime("@$token_timestamp"))->diff(new DateTime(), absolute: true)->i;
|
||||
if ($minutes_since_creation > self::MINUTES_VALID_PASSWORD_RESET) {
|
||||
$this->conn->rollBack();
|
||||
if ($use_transaction) $this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: [
|
||||
"target" => null,
|
||||
|
@ -525,7 +535,7 @@ class UserManager
|
|||
);
|
||||
}
|
||||
|
||||
$this->conn->commit();
|
||||
if ($use_transaction) $this->conn->commit();
|
||||
return new Response(payload: null, satisfied: true);
|
||||
}
|
||||
|
||||
|
@ -544,35 +554,9 @@ class UserManager
|
|||
{
|
||||
$this->conn->beginTransaction();
|
||||
|
||||
$stmt = $this->conn->prepare("SELECT password_reset_token_timestamp
|
||||
FROM users WHERE email=:email AND password_reset_token=:token;");
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->bindValue(":token", $token);
|
||||
$stmt->execute();
|
||||
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if (sizeof($results) === 0) {
|
||||
$this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: [
|
||||
"target" => null,
|
||||
"message" => "This password reset link is invalid. Maybe you already reset your password?"
|
||||
],
|
||||
satisfied: false
|
||||
);
|
||||
}
|
||||
|
||||
$token_timestamp = $results[0]["password_reset_token_timestamp"];
|
||||
$minutes_since_creation = (new DateTime("@$token_timestamp"))->diff(new DateTime(), absolute: true)->i;
|
||||
if ($minutes_since_creation > self::MINUTES_VALID_PASSWORD_RESET) {
|
||||
$this->conn->rollBack();
|
||||
return new Response(
|
||||
payload: [
|
||||
"target" => null,
|
||||
"message" => "This password reset link has expired."
|
||||
],
|
||||
satisfied: false
|
||||
);
|
||||
}
|
||||
$token_is_valid = $this->validate_password_reset_token($email, $token, false);
|
||||
if (!$token_is_valid->satisfied)
|
||||
return $token_is_valid;
|
||||
|
||||
if ($password_new !== $password_confirm) {
|
||||
$this->conn->rollBack();
|
||||
|
@ -583,10 +567,9 @@ class UserManager
|
|||
}
|
||||
|
||||
$stmt = $this->conn->prepare("UPDATE users
|
||||
SET password=:password,
|
||||
password_reset_token=null
|
||||
SET password=:password, password_reset_token=null
|
||||
WHERE email=:email;");
|
||||
$stmt->bindValue(":password", password_hash($password_new, PASSWORD_DEFAULT));
|
||||
$stmt->bindValue(":password", password_hash($password_new, PASSWORD_BCRYPT));
|
||||
$stmt->bindValue(":email", $email);
|
||||
$stmt->execute();
|
||||
|
||||
|
|
Loading…
Reference in New Issue