Allow resetting password through email.
This also adopts the use of an Alert template for error and success messages.
This commit is contained in:
@@ -44,6 +44,31 @@ class Authentication
|
||||
return empty($res) ? false : $res;
|
||||
}
|
||||
|
||||
public static function setResetKey($id_user)
|
||||
{
|
||||
return Registry::get('db')->query('
|
||||
UPDATE users
|
||||
SET reset_key = {string:key}
|
||||
WHERE id_user = {int:id}',
|
||||
[
|
||||
'id' => $id_user,
|
||||
'key' => self::newActivationKey(),
|
||||
]);
|
||||
}
|
||||
|
||||
public static function checkResetKey($id_user, $reset_key)
|
||||
{
|
||||
$key = Registry::get('db')->queryValue('
|
||||
SELECT reset_key
|
||||
FROM users
|
||||
WHERE id_user = {int:id}',
|
||||
[
|
||||
'id' => $id_user,
|
||||
]);
|
||||
|
||||
return $key == $reset_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies whether the user is currently logged in.
|
||||
*/
|
||||
@@ -62,6 +87,18 @@ class Authentication
|
||||
return isset($_SESSION['user_id']) && self::checkExists($_SESSION['user_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new activation key.
|
||||
*/
|
||||
public static function newActivationKey()
|
||||
{
|
||||
$alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
$string = '';
|
||||
for ($i = 0; $i < 16; $i++)
|
||||
$string .= $alpha[mt_rand(0, strlen($alpha) - 1)];
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a password for a given username against the database.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ class Dispatcher
|
||||
'managetags' => 'ManageTags',
|
||||
'manageusers' => 'ManageUsers',
|
||||
'people' => 'ViewPeople',
|
||||
'resetpassword' => 'ResetPassword',
|
||||
'suggest' => 'ProvideAutoSuggest',
|
||||
'timeline' => 'ViewTimeline',
|
||||
'uploadmedia' => 'UploadMedia',
|
||||
@@ -77,6 +78,11 @@ class Dispatcher
|
||||
else
|
||||
self::trigger403();
|
||||
}
|
||||
catch (UserFacingException $e)
|
||||
{
|
||||
$debug_info = ErrorHandler::getDebugInfo($e->getTrace());
|
||||
ErrorHandler::display($e->getMessage(), $debug_info, false);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
ErrorHandler::handleError(E_USER_ERROR, 'Unspecified exception: ' . $e->getMessage(), $e->getFile(), $e->getLine());
|
||||
@@ -93,7 +99,7 @@ class Dispatcher
|
||||
public static function kickGuest()
|
||||
{
|
||||
$form = new LogInForm('Log in');
|
||||
$form->setErrorMessage('Admin access required. Please log in.');
|
||||
$form->adopt(new Alert('', 'You need to be logged in to view this page.', 'error'));
|
||||
$form->setRedirectUrl($_SERVER['REQUEST_URI']);
|
||||
|
||||
$page = new MainTemplate('Login required');
|
||||
|
||||
100
models/Email.php
Normal file
100
models/Email.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* Email.php
|
||||
* Contains key class Email.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2016, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class Email
|
||||
{
|
||||
public static function send($address, $addressee, $subject, $body, $headers = '')
|
||||
{
|
||||
// Set a boundary.
|
||||
$boundary = uniqid('sr');
|
||||
|
||||
if (empty($headers))
|
||||
$headers .= "From: HashRU Pics <no-reply@pics.hashru.nl>\r\n";
|
||||
|
||||
// Set up headers.
|
||||
$headers .= "MIME-Version: 1.0\r\n";
|
||||
$headers .= "Content-Type: multipart/alternative;boundary=$boundary\r\n";
|
||||
|
||||
// Start the message with a plaintext version of the mail.
|
||||
$message = "This is a MIME encoded message.";
|
||||
$message .= "\r\n\r\n--$boundary\r\n";
|
||||
$message .= "Content-type: text/plain;charset=utf-8\r\n\r\n";
|
||||
$message .= self::wrapLines($body);
|
||||
|
||||
// Autolink URLs and wrap lines.
|
||||
$html_body = preg_replace('~\b(?!")https?://[^"\s]+~', '<a href="$0">$0</a>', $body);
|
||||
$html_body = preg_replace('~(\s+)(www\..+\.[^"\s]+?)(\.|\s+)~', '$1<a href="https://$2">$2</a>$3', $html_body);
|
||||
$html_body = preg_replace('~={10,}~', '<hr>', $html_body);
|
||||
$html_body = self::wrapLines(str_replace("\r", "<br>\r", $html_body), 80, "\r\n");
|
||||
|
||||
// Then, more excitingly, add an HTML version!
|
||||
$message .= "\r\n\r\n--" . $boundary . "\r\n";
|
||||
$message .= "Content-type: text/html;charset=utf-8\r\n\r\n";
|
||||
$message .= "<html>\r\n<head><style type=\"text/css\">\r\nbody { font: 13px Helvetica, Arial, sans-serif; }\r\n</style></head>\r\n<body><p>" .
|
||||
$html_body . "</p></body>\r\n</html>";
|
||||
|
||||
// End off with a final boundary.
|
||||
$message .= "\r\n\r\n--" . $boundary . "--";
|
||||
|
||||
if (DEBUG)
|
||||
return file_put_contents(BASEDIR . '/mail_dumps.txt', "To: \"$addressee\" <$address>\r\n$headers\r\nSubject: $subject\r\n" . self::wrapLines($message), FILE_APPEND);
|
||||
else
|
||||
return mail("\"$addressee\" <$address>", $subject, $message, $headers, '-fbounces@pics.hashru.nl');
|
||||
}
|
||||
|
||||
public static function wrapLines($body, $maxlength = 80, $break = "\r\n")
|
||||
{
|
||||
$lines = explode("\n", $body);
|
||||
$wrapped = "";
|
||||
foreach ($lines as $line)
|
||||
$wrapped .= wordwrap($line, $maxlength, $break) . $break;
|
||||
return $wrapped;
|
||||
}
|
||||
|
||||
private static function parseTemplate($template, $replacements)
|
||||
{
|
||||
$replacement_keys = array_map(function($el) { return "%$el%"; }, array_keys($replacements));
|
||||
$subject = str_replace($replacement_keys, array_values($replacements), $template['subject']);
|
||||
$body = str_replace($replacement_keys, array_values($replacements), $template['body']);
|
||||
return [$subject, $body];
|
||||
}
|
||||
|
||||
public static function resetMail($id_user)
|
||||
{
|
||||
$row = Registry::get('db')->queryAssoc('
|
||||
SELECT first_name, surname, emailaddress, reset_key
|
||||
FROM users
|
||||
WHERE id_user = {int:id_user}',
|
||||
[
|
||||
'id_user' => $id_user,
|
||||
]);
|
||||
|
||||
if (empty($row))
|
||||
return false;
|
||||
|
||||
|
||||
list($subject, $body) = self::parseTemplate([
|
||||
'subject' => 'Information on how to reset your HashRU password',
|
||||
'body' => str_replace("\n", "\r\n", 'Dear %FIRST_NAME%,
|
||||
|
||||
You are receiving this email because a password reset request was issued on our website.
|
||||
|
||||
If you did not request a password reset, please disregard this email. Otherwise, please follow the link below.
|
||||
|
||||
%RESET_LINK%
|
||||
|
||||
The HashRU Pics team'),
|
||||
], [
|
||||
'FIRST_NAME' => $row['first_name'],
|
||||
'RESET_LINK' => BASEURL . '/resetpassword/?step=2&email=' . rawurlencode($row['emailaddress']) . '&key=' . $row['reset_key'],
|
||||
]);
|
||||
|
||||
$addressee = trim($row['first_name'] . ' ' . $row['surname']);
|
||||
self::send($row['emailaddress'], $addressee, $subject, $body);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* ErrorHandler.php
|
||||
* Contains key class ErrorHandler.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
||||
* Kabuki CMS (C) 2013-2016, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class ErrorHandler
|
||||
@@ -11,6 +11,18 @@ class ErrorHandler
|
||||
private static $error_count = 0;
|
||||
private static $handling_error;
|
||||
|
||||
public static function enable()
|
||||
{
|
||||
set_error_handler('ErrorHandler::handleError');
|
||||
ini_set("display_errors", DEBUG ? "On" : "Off");
|
||||
}
|
||||
|
||||
public static function disable()
|
||||
{
|
||||
set_error_handler(NULL);
|
||||
ini_set("display_errors", "Off");
|
||||
}
|
||||
|
||||
// Handler for standard PHP error messages.
|
||||
public static function handleError($error_level, $error_message, $file, $line, $context = null)
|
||||
{
|
||||
@@ -121,14 +133,27 @@ class ErrorHandler
|
||||
return $error_message;
|
||||
}
|
||||
|
||||
public static function display($message, $debug_info)
|
||||
public static function display($message, $debug_info, $is_sensitive = true)
|
||||
{
|
||||
$is_admin = Registry::has('user') && Registry::get('user')->isAdmin();
|
||||
|
||||
// Just show the message if we're running in a console.
|
||||
if (empty($_SERVER['HTTP_HOST']))
|
||||
{
|
||||
echo $message;
|
||||
exit;
|
||||
}
|
||||
// JSON request?
|
||||
elseif (isset($_GET['json']) || isset($_GET['format']) && $_GET['format'] == 'json')
|
||||
{
|
||||
if (DEBUG || $is_admin)
|
||||
echo json_encode(['error' => $message . "\n\n" . $debug_info]);
|
||||
elseif (!$is_sensitive)
|
||||
echo json_encode(['error' => $message]);
|
||||
else
|
||||
echo json_encode(['error' => 'Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Initialise the main template to present a nice message to the user.
|
||||
$page = new MainTemplate('An error occured!');
|
||||
@@ -146,6 +171,8 @@ class ErrorHandler
|
||||
$page->adopt(new AdminBar());
|
||||
}
|
||||
}
|
||||
elseif (!$is_sensitive)
|
||||
$page->adopt(new DummyBox('An error occured!', '<p>' . $message . '</p>'));
|
||||
else
|
||||
$page->adopt(new DummyBox('An error occured!', '<p>Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.</p>'));
|
||||
|
||||
|
||||
12
models/UserFacingException.php
Normal file
12
models/UserFacingException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/*****************************************************************************
|
||||
* UserFacingException.php
|
||||
* Contains exception class UserFacingException.
|
||||
*
|
||||
* Kabuki CMS (C) 2013-2016, Aaron van Geffen
|
||||
*****************************************************************************/
|
||||
|
||||
class UserFacingException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user