1
0
Fork 0

Finalize "forgot password" feature

master
Anonymous Contributor 2023-08-23 18:16:36 +02:00
parent 1ee795a399
commit eda4c5a030
6 changed files with 66 additions and 45 deletions

View File

@ -38,10 +38,10 @@ function htmlToPlain($message): string {
return $messageNew; return $messageNew;
} }
function sendMail($message, $subject, $title, $preheader): bool { function sendMail($email, $message, $subject, $title, $preheader): bool {
include_once('../lib/phpmailer/Exception.php'); include_once('lib/phpmailer/Exception.php');
include_once('../lib/phpmailer/PHPMailer.php'); include_once('lib/phpmailer/PHPMailer.php');
include_once('../lib/phpmailer/SMTP.php'); include_once('lib/phpmailer/SMTP.php');
include_once('app/HTML.php'); include_once('app/HTML.php');
global $RUNTIME; global $RUNTIME;
@ -53,6 +53,7 @@ function sendMail($message, $subject, $title, $preheader): bool {
$mailer->Port = $RUNTIME['SMTP']['PORT']; $mailer->Port = $RUNTIME['SMTP']['PORT'];
$mailer->Username = $RUNTIME['SMTP']['ADDRESS']; $mailer->Username = $RUNTIME['SMTP']['ADDRESS'];
$mailer->Password = $RUNTIME['SMTP']['PASS']; $mailer->Password = $RUNTIME['SMTP']['PASS'];
$mailer->SMTPAuth = true;
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; $mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mailer->setFrom($RUNTIME['SMTP']['ADDRESS'], $RUNTIME['SMTP']['NAME']); $mailer->setFrom($RUNTIME['SMTP']['ADDRESS'], $RUNTIME['SMTP']['NAME']);
@ -61,7 +62,7 @@ function sendMail($message, $subject, $title, $preheader): bool {
$mailer->isHTML(true); $mailer->isHTML(true);
$mailer->Subject = $subject; $mailer->Subject = $subject;
$mailHtml = new HTML(); $mailHtml = new HTML();
$mailHtml->importHTML("email.html"); $mailHtml->importHTML("mail.html");
$mailHtml->setHTMLTitle($title); $mailHtml->setHTMLTitle($title);
$mailHtml->ReplaceLayoutInhalt('%%MESSAGE%%', $message); $mailHtml->ReplaceLayoutInhalt('%%MESSAGE%%', $message);
$mailHtml->ReplaceLayoutInhalt('%%PREHEADER%%', $preheader); $mailHtml->ReplaceLayoutInhalt('%%PREHEADER%%', $preheader);

View File

@ -15,21 +15,25 @@
if(!$validator->isValid($_POST)) { if(!$validator->isValid($_POST)) {
$HTML->ReplaceLayoutInhalt('%%MESSAGE%%', 'Bitte gebe deinen Benutzernamen (Vor- und Nachname) und die dazugehörige E-Mail-Adresse ein'); $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', 'Bitte gebe deinen Benutzernamen (Vor- und Nachname) und die dazugehörige E-Mail-Adresse ein');
$HTML->ReplaceLayoutInhalt('%%MESSAGECOLOR%%', 'red'); $HTML->ReplaceLayoutInhalt('%%MESSAGECOLOR%%', 'red');
$HTML->build();
echo $HTML->ausgabe();
} }
else { else {
$nameParts = explode(" ", $_POST['username']); $nameParts = explode(" ", $_POST['username']);
$email = strtolower(trim($_POST['email'])); $email = strtolower(trim($_POST['email']));
$getAccount = $RUNTIME['pdo']->prepare('SELECT Email,FirstName,LastName,PrincipalID FROM UserAccounts WHERE FirstName = ? AND LastName = ? AND Email = ?'); $getAccount = $RUNTIME['PDO']->prepare('SELECT Email,FirstName,LastName,PrincipalID FROM UserAccounts WHERE FirstName = ? AND LastName = ? AND Email = ?');
$getAccount->execute([trim($nameParts[0]), trim($nameParts[1]), $email]); $getAccount->execute([trim($nameParts[0]), trim($nameParts[1]), $email]);
$validRequest = $getAccount->rowCount() == 1; $validRequest = $getAccount->rowCount() == 1;
$uuid;
$name;
if($res = $getAccount->fetch()) { if($res = $getAccount->fetch()) {
$email = $res['Email']; $email = $res['Email'];
$uuid = $res['PrincipalID']; $uuid = $res['PrincipalID'];
$name = $res['FirstName'].' '.$res['LastName']; $name = $res['FirstName'].' '.$res['LastName'];
} }
foreach($blockedDomains as $domain) { foreach($RUNTIME['RESET_BLOCKED_DOMAINS'] as $domain) {
if(str_ends_with($email, $domain)) { if(str_ends_with($email, $domain)) {
$validRequest = false; $validRequest = false;
} }
@ -42,7 +46,7 @@
fastcgi_finish_request(); fastcgi_finish_request();
if($validRequest) { if($validRequest) {
$getReqTime = $RUNTIME['pdo']->prepare('SELECT RequestTime FROM PasswordResetTokens WHERE PrincipalID=?'); $getReqTime = $RUNTIME['PDO']->prepare('SELECT RequestTime FROM PasswordResetTokens WHERE PrincipalID=?');
$getReqTime->execute([$uuid]); $getReqTime->execute([$uuid]);
if(($res = $getReqTime->fetch()) && time() - $res['RequestTime'] < 900) { if(($res = $getReqTime->fetch()) && time() - $res['RequestTime'] < 900) {
return; return;
@ -50,10 +54,10 @@
require_once 'app/utils.php'; require_once 'app/utils.php';
$token = generateToken(32); $token = generateToken(32);
$setToken = $RUNTIME['pdo']->prepare('REPLACE INTO PasswordResetTokens(PrincipalID,Token,RequestTime) VALUES(?,?,?)'); $setToken = $RUNTIME['PDO']->prepare('REPLACE INTO PasswordResetTokens(PrincipalID,Token,RequestTime) VALUES(?,?,?)');
$setToken->execute([$uuid, $token, time()]); $setToken->execute([$uuid, $token, time()]);
sendMail(str_replace('%%NAME%%', $name, str_replace('%%RESET_LINK%%', 'https://'.$RUNTIME['DOMAIN'].'/index.php?page=reset-password&token='.$token, MESSAGE)), "Zurücksetzung des Passworts für ".$name, 'Dein Passwort zurücksetzen', 'Folge diesen Anweisungen, um ein neues Passwort für deinen 4Creative-Account festzulegen'); sendMail($email, str_replace('%%NAME%%', $name, str_replace('%%RESET_LINK%%', 'https://'.$RUNTIME['DOMAIN'].'/index.php?page=reset-password&token='.$token, MESSAGE)), "Zurücksetzung des Passworts für ".$name, 'Dein Passwort zurücksetzen', 'Folge diesen Anweisungen, um ein neues Passwort für deinen 4Creative-Account festzulegen');
} }
} }
} }

View File

@ -1,19 +1,27 @@
<?php <?php
const MESSAGE = 'Hallo %%NAME%%,<br/><br/>das Passwort für deinen 4Creative-Account wurde soeben über die Funktion "Passwort vergessen" geändert.<br/><br/>Solltest du diese Änderung nicht selbst durchgeführt haben, wende dich bitte umgehend per E-Mail (info@4creative.net) oder Discord (@ikeytan) an uns.'; const MESSAGE = 'Hallo %%NAME%%,<br/><br/>das Passwort für deinen 4Creative-Account wurde soeben über die Funktion "Passwort vergessen" geändert.<br/><br/>Solltest du diese Änderung nicht selbst durchgeführt haben, wende dich bitte umgehend per E-Mail (info@4creative.net) oder Discord (@ikeytan) an uns.';
const TOKEN_INVALID = 'Dieser Link zur Passwortzurücksetzung ist nicht gültig. Bitte klicke oder kopiere den Link aus der E-Mail, die du erhalten hast.';
const TOKEN_EXPIRED = 'Dein Link zur Passwortzurücksetzung ist abgelaufen. Klicke <a href="index.php?page=forgot">hier</a>, um eine neue Anfrage zu senden.';
function displayTokenError() { function displayTokenError($message) {
$HTML = new HTML(); $HTML = new HTML();
$HTML->importHTML("error.html"); $HTML->importHTML("error.html");
$HTML->ReplaceLayoutInhalt('%%MESSAGE%%', 'Dieser Link zur Passwortzurücksetzung ist nicht gültig. Bitte klicke oder kopiere den Link aus der E-Mail, die du erhalten hast.'); $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', $message);
$HTML->build(); $HTML->build();
echo $HTML->ausgabe();
exit(); exit();
} }
function displayPage($err) { function displayPage($err) {
if(!isset($_GET['token']) || !preg_match('/^[a-z0-9A-Z]{32}$/', $_GET['token'])) {
displayTokenError(TOKEN_INVALID);
}
$HTML = new HTML(); $HTML = new HTML();
$HTML->setHTMLTitle(""); $HTML->setHTMLTitle("");
$HTML->importHTML("reset-password.html"); $HTML->importHTML("reset-password.html");
$HTML->ReplaceLayoutInhalt('%%MESSAGE%%', $err); $HTML->ReplaceLayoutInhalt('%%MESSAGE%%', $err);
$HTML->ReplaceLayoutInhalt('%%RESET_TOKEN%%', htmlspecialchars($_GET['token']));
$HTML->build(); $HTML->build();
echo $HTML->ausgabe(); echo $HTML->ausgabe();
exit(); exit();
@ -36,16 +44,29 @@
displayPage('Dein Passwort muss mindestens '.$RUNTIME['PASSWORD_MIN_LENGTH'].' Zeichen lang sein.'); displayPage('Dein Passwort muss mindestens '.$RUNTIME['PASSWORD_MIN_LENGTH'].' Zeichen lang sein.');
} }
$getUuid = $RUNTIME['PDO']->prepare('SELECT PrincipalID,FirstName,LastName FROM PasswordResetTokens JOIN UserAccounts ON PasswordResetTokens.PrincipalID = PasswordResetTokens.PrincipalID WHERE Token = ?'); $getReq = $RUNTIME['PDO']->prepare('SELECT UserAccounts.PrincipalID AS UUID,FirstName,LastName,Email,Token,RequestTime FROM PasswordResetTokens JOIN UserAccounts ON UserAccounts.PrincipalID = PasswordResetTokens.PrincipalID WHERE Token = ?');
if($getUuid->rowCount() == 0) { $getReq->execute([$_POST['resetToken']]);
displayTokenError(); if($getReq->rowCount() == 0) {
displayTokenError(TOKEN_INVALID);
} }
$res = $getUuid->fetch(); $res = $getReq->fetch();
$uuid = $res['PrincipalID'];
if(!hash_equals($res['Token'], $_POST['resetToken'])) {
displayTokenError(TOKEN_INVALID);
}
$uuid = $res['UUID'];
$name = $res['FirstName'].' '.$res['LastName']; $name = $res['FirstName'].' '.$res['LastName'];
$getToken = $RUNTIME['PDO']->prepare('DELETE FROM PasswordResetTokens WHERE Token = ?'); $getToken = $RUNTIME['PDO']->prepare('DELETE FROM PasswordResetTokens WHERE PrincipalID = ? AND Token = ?');
$getToken->execute([$_POST['resetToken']]); $getToken->execute([$uuid, $_POST['resetToken']]);
if($getToken->rowCount() == 0) {
displayTokenError(TOKEN_INVALID);
}
if(time() - $res['RequestTime'] > 86400) {
displayTokenError(TOKEN_EXPIRED);
}
$salt = bin2hex(random_bytes(16)); $salt = bin2hex(random_bytes(16));
$hash = md5(md5(trim($_POST['password'])).':'.$salt); $hash = md5(md5(trim($_POST['password'])).':'.$salt);
@ -57,7 +78,7 @@
$_SESSION['loginMessageColor'] = 'darkgreen'; $_SESSION['loginMessageColor'] = 'darkgreen';
require_once 'app/utils.php'; require_once 'app/utils.php';
sendMail(str_replace('%%NAME%%', $name, MESSAGE), 'Passwort für '.$name.' zurückgesetzt', 'Passwort geändert', 'Das Passwort für deinen 4Creative-Account wurde soeben zurückgesetzt'); sendMail($res['Email'], str_replace('%%NAME%%', $name, MESSAGE), 'Passwort für '.$name.' zurückgesetzt', 'Passwort geändert', 'Das Passwort für deinen 4Creative-Account wurde soeben zurückgesetzt');
header('Location: index.php?page=login'); header('Location: index.php?page=login');
exit(); exit();
@ -65,8 +86,4 @@
} }
displayPage(''); displayPage('');
if(!isset($_GET['token']) || !preg_match('/^[a-z0-9A-Z]{32}$/', $_GET['token'])) {
displayTokenError();
}
?> ?>

View File

@ -22,7 +22,7 @@
<div class="limiter"> <div class="limiter">
<div class="container-login100"> <div class="container-login100">
<div class="wrap-login100 p-t-50 p-b-90"> <div class="wrap-login100 p-t-50 p-b-90">
<form class="login100-form validate-form flex-sb flex-w" action="index.php?page=forgot-request" method="post"> <form class="login100-form validate-form flex-sb flex-w" action="index.php?page=forgot" method="post">
<span class="login100-form-title p-b-51"> <span class="login100-form-title p-b-51">
Passwort vergessen Passwort vergessen
</span> </span>
@ -32,7 +32,7 @@
</div> </div>
<div class="wrap-input100 validate-input m-b-16" data-validate="Bitte gebe deinen Benutzernamen an."> <div class="wrap-input100 validate-input m-b-16" data-validate="Bitte gebe deinen Benutzernamen an.">
<input class="input100" type="text" name="username" value="%%LASTUSERNAME%%" placeholder="Benutzername"> <input class="input100" type="text" name="username" placeholder="Benutzername">
<span class="focus-input100"></span> <span class="focus-input100"></span>
</div> </div>

View File

@ -1,9 +1,8 @@
<!doctype html> <!DOCTYPE html>
<html> <html lang="de">
<head> <head>
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
<style> <style>
img { img {
border: none; border: none;
@ -23,17 +22,17 @@
-webkit-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;
} }
</style> </style>
</head> </head>
<body> <body>
<span class="preheader" style="display: none">%%PREHEADER%%</span> <span class="preheader" style="display: none">%%PREHEADER%%</span>
<div class="container" style="background-color: #afafaf"> <div class="container" style="background-color: #afafaf">
<div class="header" style="background-color: #434343; height: 128px"> <div class="header" style="background-color: #434343; height: 64px">
<img style="float: left; height: 100%" src="https://4creative.net/images/4Creative-Logo-neu.png" alt="Logo"> <img style="vertical-align: middle; height: 100%" src="https://4creative.net/images/4Creative-Logo-neu.png" alt="Logo">
<h2 style="vertical-align: middle">%%EchoTitle%%</h2> <h2 style="vertical-align: middle; color: #fff; font-weight: bold; margin: 0 0 0 10px; display: inline">%%EchoTitle%%</h2>
</div> </div>
<div class="content" style="background-color: #fff"> <div class="content" style="background-color: #fff; padding: 2px">
%%MESSAGE%% %%MESSAGE%%
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -22,7 +22,7 @@
<div class="limiter"> <div class="limiter">
<div class="container-login100"> <div class="container-login100">
<div class="wrap-login100 p-t-50 p-b-90"> <div class="wrap-login100 p-t-50 p-b-90">
<form class="login100-form validate-form flex-sb flex-w" action="index.php?page=forgot-request" method="post"> <form class="login100-form validate-form flex-sb flex-w" action="index.php?page=reset-password&token=%%RESET_TOKEN%%" method="post">
<span class="login100-form-title p-b-51"> <span class="login100-form-title p-b-51">
Neues Passwort festlegen Neues Passwort festlegen
</span> </span>