diff --git a/TODO.md b/TODO.md index fae32df..8911bdb 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,9 @@ TODO: * Draaiing van foto's bij importeren goedzetten -* Import asset ownership * Import users +* Import asset ownership * Pagina om één foto te bekijken * Taggen door gebruikers * Uploaden door gebruikers * Album management -* Password reset via e-mail diff --git a/controllers/Login.php b/controllers/Login.php index b75c0b9..700b171 100644 --- a/controllers/Login.php +++ b/controllers/Login.php @@ -29,9 +29,12 @@ class Login extends HTMLController if (isset($_POST['redirect_url'])) header('Location: ' . base64_decode($_POST['redirect_url'])); elseif (isset($_SESSION['login_url'])) + { + unset($_SESSION['redirect_url']); header('Location: ' . $_SESSION['redirect_url']); + } else - header('Location: ' . BASEURL . '/admin/'); + header('Location: ' . BASEURL . '/'); exit; } else @@ -39,15 +42,28 @@ class Login extends HTMLController } parent::__construct('Log in - ' . SITE_TITLE); - $this->page->appendStylesheet(BASEURL . '/css/admin.css'); $form = new LogInForm('Log in'); if ($login_error) - $form->setErrorMessage('Invalid email address or password.'); + $form->adopt(new Alert('', 'Invalid email address or password.', 'error')); // Tried anything? Be helpful, at least. if (isset($_POST['emailaddress'])) $form->setEmail($_POST['emailaddress']); + // A message from the past/present/future? + if (isset($_SESSION['login_msg'])) + { + $form->adopt(new Alert($_SESSION['login_msg'][0], $_SESSION['login_msg'][1], $_SESSION['login_msg'][2])); + unset($_SESSION['login_msg']); + } + + // Going somewhere? + if (!empty($_GET['redirect']) && ($url = base64_decode($_GET['redirect']))) + { + $_SESSION['login_url'] = $url; + $form->setRedirectUrl($url); + } + $this->page->adopt($form); } } diff --git a/controllers/ResetPassword.php b/controllers/ResetPassword.php new file mode 100644 index 0000000..7153498 --- /dev/null +++ b/controllers/ResetPassword.php @@ -0,0 +1,81 @@ +isLoggedIn()) + throw new UserFacingException('You are already logged in.'); + + // Verifying an existing reset key? + if (isset($_GET['step'], $_GET['email'], $_GET['key']) && $_GET['step'] == 2) + { + $email = rawurldecode($_GET['email']); + $id_user = Authentication::getUserid($email); + if ($id_user === false) + throw new UserFacingException('Invalid email address. Please make sure you copied the full link in the email you received.'); + + $key = $_GET['key']; + if (!Authentication::checkResetKey($id_user, $key)) + throw new UserFacingException('Invalid reset token. Please make sure you copied the full link in the email you received. Note: you cannot use the same token twice.'); + + parent::__construct('Reset password - ' . SITE_TITLE); + $form = new PasswordResetForm($email, $key); + $this->page->adopt($form); + + // Are they trying to set something already? + if (isset($_POST['password1'], $_POST['password2'])) + { + $missing = []; + if (strlen($_POST['password1']) < 6 || !preg_match('~[^A-z]~', $_POST['password1'])) + $missing[] = 'Please fill in a password that is at least six characters long and contains at least one non-alphabetic character (e.g. a number or symbol).'; + if ($_POST['password1'] != $_POST['password2']) + $missing[] = 'The passwords you entered do not match.'; + + // So, are we good to go? + if (empty($missing)) + { + Authentication::updatePassword($id_user, Authentication::computeHash($_POST['password1'])); + $_SESSION['login_msg'] = ['Your password has been reset', 'You can now use the form below to log in to your account.', 'success']; + header('Location: ' . BASEURL . '/login/'); + exit; + } + else + $form->adopt(new Alert('Some fields require your attention', '
" . + $html_body . "
\r\n"; + + // 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); + } +} diff --git a/models/ErrorHandler.php b/models/ErrorHandler.php index 214f6ec..da650e9 100644 --- a/models/ErrorHandler.php +++ b/models/ErrorHandler.php @@ -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!', '' . $message . '
')); else $page->adopt(new DummyBox('An error occured!', 'Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.
')); diff --git a/models/UserFacingException.php b/models/UserFacingException.php new file mode 100644 index 0000000..031aa43 --- /dev/null +++ b/models/UserFacingException.php @@ -0,0 +1,12 @@ + p, +.alert-block > ul { + margin-bottom: 0; +} +.alert-block p + p { + margin-top: 5px; +} + + /* Responsive: smartphone in portrait ---------------------------------------*/ @media only screen and (max-width: 895px) { diff --git a/templates/Alert.php b/templates/Alert.php new file mode 100644 index 0000000..1267eca --- /dev/null +++ b/templates/Alert.php @@ -0,0 +1,24 @@ +_title = $title; + $this->_message = $message; + $this->_type = in_array($type, ['alert', 'error', 'success', 'info']) ? $type : 'alert'; + } + + protected function html_content() + { + echo ' +Please fill in the email address you used to sign up in the form below. You will be sent a reset link to your email address.
+ + '; + } +} diff --git a/templates/LogInForm.php b/templates/LogInForm.php index c051b4d..c2b8835 100644 --- a/templates/LogInForm.php +++ b/templates/LogInForm.php @@ -8,15 +8,9 @@ class LogInForm extends SubTemplate { - private $error_message = ''; private $redirect_url = ''; private $emailaddress = ''; - public function setErrorMessage($message) - { - $this->error_message = $message; - } - public function setRedirectUrl($url) { $_SESSION['login_url'] = $url; @@ -32,12 +26,10 @@ class LogInForm extends SubTemplate { echo ' '; } } diff --git a/templates/PasswordResetForm.php b/templates/PasswordResetForm.php new file mode 100644 index 0000000..4bc9c5e --- /dev/null +++ b/templates/PasswordResetForm.php @@ -0,0 +1,46 @@ +email = $email; + $this->key = $key; + } + + protected function html_content() + { + echo ' +You have successfully confirmed your identify. Please use the form below to set a new password.
+ +