From 0c98f13eee10a61cabdc13e7aa75916d50b8b078 Mon Sep 17 00:00:00 2001 From: Philip Häusler Date: Thu, 26 Dec 2013 13:34:48 +0100 Subject: user password recovery --- db/update.sql | 4 + includes/controller/users_controller.php | 84 +++++++++++++++++++ includes/helper/email_helper.php | 16 ++++ includes/helper/internationalization_helper.php | 16 +++- includes/model/User_model.php | 46 ++++++++++- includes/pages/guest_login.php | 3 + includes/sys_auth.php | 2 +- includes/view/User_view.php | 66 ++++++++++----- locale/de_DE.UTF-8/LC_MESSAGES/default.mo | Bin 16110 -> 16555 bytes locale/de_DE.UTF-8/LC_MESSAGES/default.po | 34 ++++++-- public/index.php | 102 +++++++++++++----------- 11 files changed, 295 insertions(+), 78 deletions(-) create mode 100644 includes/controller/users_controller.php create mode 100644 includes/helper/email_helper.php diff --git a/db/update.sql b/db/update.sql index 7da5f3e6..c8079f3a 100644 --- a/db/update.sql +++ b/db/update.sql @@ -1,3 +1,7 @@ +/* password recovery */ +ALTER TABLE `User` ADD `password_recovery_token` VARCHAR( 32 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL AFTER `Passwort` , +ADD INDEX ( `password_recovery_token` ); + /* Update für #27, iCal-Export */ ALTER TABLE `User` ADD `ical_key` VARCHAR( 32 ) NOT NULL; ALTER TABLE `User` ADD INDEX ( `ical_key` ); diff --git a/includes/controller/users_controller.php b/includes/controller/users_controller.php new file mode 100644 index 00000000..8e0aace7 --- /dev/null +++ b/includes/controller/users_controller.php @@ -0,0 +1,84 @@ += MIN_PASSWORD_LENGTH) { + if ($_REQUEST['password'] != $_REQUEST['password2']) { + $ok = false; + error(_("Your passwords don't match.")); + } + } else { + $ok = false; + error(_("Your password is to short (please use at least 6 characters).")); + } + + if ($ok) { + $result = set_password($user_source['UID'], $_REQUEST['password']); + if ($result === false) + engelsystem_error(_("Password could not be updated.")); + + success(_("Password saved.")); + redirect(page_link_to('login')); + } + } + + return User_password_set_view(); + } else { + if (isset($_REQUEST['submit'])) { + $ok = true; + + if (isset($_REQUEST['email']) && strlen(strip_request_item('email')) > 0) { + $email = strip_request_item('email'); + if (check_email($email)) { + $user_source = User_by_email($email); + if ($user_source === false) + engelsystem_error("Unable to load user."); + if ($user_source == null) { + $ok = false; + $msg .= error(_("E-mail address is not correct."), true); + } + } else { + $ok = false; + $msg .= error(_("E-mail address is not correct."), true); + } + } else { + $ok = false; + $msg .= error(_("Please enter your e-mail."), true); + } + + if ($ok) { + $token = User_generate_password_recovery_token($user_source); + if ($token === false) + engelsystem_error("Unable to generate password recovery token."); + $result = engelsystem_email_to_user($user_source, _("Password recovery"), sprintf(_("Please visit %s to recover your password."), page_link_to_absolute('user_password_recovery') . '&token=' . $token)); + if ($result === false) + engelsystem_error("Unable to send password recovery email."); + + success(_("We sent an email containing your password recovery link.")); + redirect(page_link_to('login')); + } + } + + return User_password_recovery_view(); + } +} + +function user_password_recovery_title() { + return _("Password recovery"); +} + +?> \ No newline at end of file diff --git a/includes/helper/email_helper.php b/includes/helper/email_helper.php new file mode 100644 index 00000000..98cfd0f3 --- /dev/null +++ b/includes/helper/email_helper.php @@ -0,0 +1,16 @@ +"); +} + +?> \ No newline at end of file diff --git a/includes/helper/internationalization_helper.php b/includes/helper/internationalization_helper.php index ae88eb21..db150fec 100644 --- a/includes/helper/internationalization_helper.php +++ b/includes/helper/internationalization_helper.php @@ -17,13 +17,25 @@ function gettext_init() { elseif (! isset($_SESSION['locale'])) $_SESSION['locale'] = $default_locale; - putenv('LC_ALL=' . $_SESSION['locale']); - setlocale(LC_ALL, $_SESSION['locale']); + gettext_locale(); bindtextdomain('default', '../locale'); bind_textdomain_codeset('default', 'UTF-8'); textdomain('default'); } +/** + * Swich gettext locale. + * + * @param string $locale + */ +function gettext_locale($locale = null) { + if ($locale == null) + $locale = $_SESSION['locale']; + + putenv('LC_ALL=' . $locale); + setlocale(LC_ALL, $locale); +} + /** * Renders language selection. * diff --git a/includes/model/User_model.php b/includes/model/User_model.php index 884aeae8..523685df 100644 --- a/includes/model/User_model.php +++ b/includes/model/User_model.php @@ -30,10 +30,40 @@ function User_by_api_key($api_key) { return $user[0]; } +/** + * Returns User by email. + * + * @param string $email + * @return Matching user, null or false on error + */ +function User_by_email($email) { + $user = sql_select("SELECT * FROM `User` WHERE `email`='" . sql_escape($email) . "' LIMIT 1"); + if ($user === false) + return false; + if (count($user) == 0) + return null; + return $user[0]; +} + +/** + * Returns User by password token. + * + * @param string $token + * @return Matching user, null or false on error + */ +function User_by_password_recovery_token($token) { + $user = sql_select("SELECT * FROM `User` WHERE `password_recovery_token`='" . sql_escape($token) . "' LIMIT 1"); + if ($user === false) + return false; + if (count($user) == 0) + return null; + return $user[0]; +} + /** * Generates a new api key for given user. * - * @param User $user + * @param User $user */ function User_reset_api_key(&$user) { $user['api_key'] = md5($user['Nick'] . time() . rand()); @@ -43,4 +73,18 @@ function User_reset_api_key(&$user) { engelsystem_log("API key resetted."); } +/** + * Generates a new password recovery token for given user. + * + * @param User $user + */ +function User_generate_password_recovery_token(&$user) { + $user['password_recovery_token'] = md5($user['Nick'] . time() . rand()); + $result = sql_query("UPDATE `User` SET `password_recovery_token`='" . sql_escape($user['password_recovery_token']) . "' WHERE `UID`='" . sql_escape($user['UID']) . "' LIMIT 1"); + if ($result === false) + return false; + engelsystem_log("Password recovery for " . $user['Nick'] . " started."); + return $user['password_recovery_token']; +} + ?> \ No newline at end of file diff --git a/includes/pages/guest_login.php b/includes/pages/guest_login.php index f072e411..605d32b1 100644 --- a/includes/pages/guest_login.php +++ b/includes/pages/guest_login.php @@ -216,6 +216,9 @@ function guest_login() { form_password('password', _("Password")), form_submit('submit', _("Login")) )), + buttons(array( + button(page_link_to('user_password_recovery'), _("I forgot my password")) + )), info(_("Please note: You have to activate cookies!"), true) )); } diff --git a/includes/sys_auth.php b/includes/sys_auth.php index 3902aea2..e9fa197d 100644 --- a/includes/sys_auth.php +++ b/includes/sys_auth.php @@ -30,7 +30,7 @@ function generate_salt($length = 16) { // set the password of a user function set_password($uid, $password) { - return sql_query("UPDATE `User` SET `Passwort` = '" . sql_escape(crypt($password, CRYPT_ALG . '$' . generate_salt(16) . '$')) . "' WHERE `UID` = " . intval($uid) . " LIMIT 1"); + return sql_query("UPDATE `User` SET `Passwort` = '" . sql_escape(crypt($password, CRYPT_ALG . '$' . generate_salt(16) . '$')) . "', `password_recovery_token`=NULL WHERE `UID` = " . intval($uid) . " LIMIT 1"); } // verify a password given a precomputed salt. diff --git a/includes/view/User_view.php b/includes/view/User_view.php index ef4f9831..eda29d58 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -3,25 +3,55 @@ /** * Available T-Shirt sizes */ -$tshirt_sizes = array ( - '' => _("Please select..."), - 'S' => "S", - 'M' => "M", - 'L' => "L", - 'XL' => "XL", - '2XL' => "2XL", - '3XL' => "3XL", - '4XL' => "4XL", - '5XL' => "5XL", - 'S-G' => "S Girl", - 'M-G' => "M Girl", - 'L-G' => "L Girl", - 'XL-G' => "XL Girl" +$tshirt_sizes = array( + '' => _("Please select..."), + 'S' => "S", + 'M' => "M", + 'L' => "L", + 'XL' => "XL", + '2XL' => "2XL", + '3XL' => "3XL", + '4XL' => "4XL", + '5XL' => "5XL", + 'S-G' => "S Girl", + 'M-G' => "M Girl", + 'L-G' => "L Girl", + 'XL-G' => "XL Girl" ); +/** + * View for password recovery step 1: E-Mail + */ +function User_password_recovery_view() { + return page(array( + msg(), + _("We will send you an e-mail with a password recovery link. Please use the email address you used for registration."), + form(array( + form_text('email', _("E-Mail"), ""), + form_submit('submit', _("Recover")) + )) + )); +} + +/** + * View for password recovery step 2: New password + */ +function User_password_set_view() { + return page(array( + msg(), + _("Please enter a new password."), + form(array( + form_password('password', _("Password")), + form_password('password2', _("Confirm password")), + form_submit('submit', _("Save")) + )) + )); +} + /** * Render a users avatar. - * @param User $user + * + * @param User $user * @return string */ function User_Avatar_render($user) { @@ -30,16 +60,16 @@ function User_Avatar_render($user) { /** * Render a user nickname. - * @param User $user_source + * + * @param User $user_source * @return string */ function User_Nick_render($user_source) { global $user, $privileges; - if($user['UID'] == $user_source['UID'] || in_array('user_shifts_admin', $privileges)) + if ($user['UID'] == $user_source['UID'] || in_array('user_shifts_admin', $privileges)) return '' . htmlspecialchars($user_source['Nick']) . ''; else return htmlspecialchars($user_source['Nick']); } - ?> \ No newline at end of file diff --git a/locale/de_DE.UTF-8/LC_MESSAGES/default.mo b/locale/de_DE.UTF-8/LC_MESSAGES/default.mo index 47a0aa3f..224b676b 100644 Binary files a/locale/de_DE.UTF-8/LC_MESSAGES/default.mo and b/locale/de_DE.UTF-8/LC_MESSAGES/default.mo differ diff --git a/locale/de_DE.UTF-8/LC_MESSAGES/default.po b/locale/de_DE.UTF-8/LC_MESSAGES/default.po index 64d09ad7..2946965a 100644 --- a/locale/de_DE.UTF-8/LC_MESSAGES/default.po +++ b/locale/de_DE.UTF-8/LC_MESSAGES/default.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: Engelsystem 2.0\n" -"POT-Creation-Date: 2013-12-03 16:58+0100\n" -"PO-Revision-Date: 2013-12-03 16:59+0100\n" +"POT-Creation-Date: 2013-12-23 21:57+0100\n" +"PO-Revision-Date: 2013-12-23 23:16+0100\n" "Last-Translator: msquare \n" "Language-Team: \n" "Language: de_DE\n" @@ -21,7 +21,25 @@ msgstr "" msgid "No data found." msgstr "Nichts gefunden." -#: /Users/msquare/workspace/projects/engelsystem/includes/helper/internationalization_helper.php:40 +#: /Users/msquare/workspace/projects/engelsystem/includes/helper/email_helper.php:6 +#, php-format +msgid "Hi %s," +msgstr "Hallo %s," + +#: /Users/msquare/workspace/projects/engelsystem/includes/helper/email_helper.php:6 +msgid "here is a message for you from the engelsystem:" +msgstr "hier ist eine Nachricht aus dem Engelsystem für Dich:" + +#: /Users/msquare/workspace/projects/engelsystem/includes/helper/email_helper.php:6 +msgid "" +"This email is autogenerated and has not to be signed. You got this email " +"because you are registered in the engelsystem." +msgstr "" +"Diese E-Mail wurde automatisch generiert und muss daher nicht unterschrieben " +"werden. Du hast diese E-Mail bekommen, weil Du im Engelsystem registriert " +"bist." + +#: /Users/msquare/workspace/projects/engelsystem/includes/helper/internationalization_helper.php:47 msgid "Language" msgstr "Sprache" @@ -1016,11 +1034,11 @@ msgstr "Kommentar (nur für Dich):" msgid "Please select..." msgstr "Bitte auswählen..." -#: /Users/msquare/workspace/projects/engelsystem/public/index.php:174 +#: /Users/msquare/workspace/projects/engelsystem/public/index.php:188 msgid "No Access" msgstr "Kein Zugriff" -#: /Users/msquare/workspace/projects/engelsystem/public/index.php:175 +#: /Users/msquare/workspace/projects/engelsystem/public/index.php:189 msgid "" "You don't have permission to view this page. You probably have to sign in or " "register in order to gain access!" @@ -1028,7 +1046,7 @@ msgstr "" "Du hast keinen Zugriff auf diese Seite. Registriere Dich und logge Dich " "bitte ein, um Zugriff zu erhalten!" -#: /Users/msquare/workspace/projects/engelsystem/public/index.php:188 +#: /Users/msquare/workspace/projects/engelsystem/public/index.php:202 msgid "" "You are not marked as arrived. Please go to heaven's desk, get your angel " "badge and/or tell them that you arrived already." @@ -1036,11 +1054,11 @@ msgstr "" "Du bist nicht als angekommen markiert. Bitte gehe zur Himmelsverwaltung, " "hole Dein Badge ab und/oder erkläre ihnen, dass Du bereits angekommen bist." -#: /Users/msquare/workspace/projects/engelsystem/public/index.php:191 +#: /Users/msquare/workspace/projects/engelsystem/public/index.php:205 msgid "You need to specify a tshirt size in your settings!" msgstr "" -#: /Users/msquare/workspace/projects/engelsystem/public/index.php:194 +#: /Users/msquare/workspace/projects/engelsystem/public/index.php:208 msgid "" "You need to specify a DECT phone number in your settings! If you don't have " "a DECT phone, just enter \"-\"." diff --git a/public/index.php b/public/index.php index fccbc69b..fb0d7966 100644 --- a/public/index.php +++ b/public/index.php @@ -24,6 +24,7 @@ require_once ('includes/view/User_view.php'); require_once ('includes/helper/internationalization_helper.php'); require_once ('includes/helper/message_helper.php'); require_once ('includes/helper/error_helper.php'); +require_once ('includes/helper/email_helper.php'); require_once ('config/config.default.php'); if (file_exists('../config/config.php')) @@ -62,37 +63,40 @@ load_auth(); if (isset($_REQUEST['auth'])) json_auth_service(); -$api_pages = array( +$free_pages = array( 'stats', - 'shifts_json_export_all' + 'shifts_json_export_all', + 'user_password_recovery' ); // Gewünschte Seite/Funktion $p = isset($user) ? "news" : "login"; -if (isset($_REQUEST['p']) && preg_match("/^[a-z0-9_]*$/i", $_REQUEST['p']) && (in_array($_REQUEST['p'], $api_pages) || (sql_num_query("SELECT * FROM `Privileges` WHERE `name`='" . sql_escape($_REQUEST['p']) . "' LIMIT 1") > 0))) +if (isset($_REQUEST['p']) && preg_match("/^[a-z0-9_]*$/i", $_REQUEST['p']) && (in_array($_REQUEST['p'], $free_pages) || in_array($_REQUEST['p'], $privileges))) { $p = $_REQUEST['p']; - -$title = $p; -$content = ""; - -if ($p == "ical") { - require_once ('includes/pages/user_ical.php'); - user_ical(); -} elseif ($p == "atom") { - require_once ('includes/pages/user_atom.php'); - user_atom(); -} elseif ($p == "shifts_json_export") { - require_once ('includes/controller/shifts_controller.php'); - shifts_json_export_controller(); -} elseif ($p == "shifts_json_export_all") { - require_once ('includes/controller/shifts_controller.php'); - shifts_json_export_all_controller(); -} elseif ($p == "stats") { - require_once ('includes/pages/guest_stats.php'); - guest_stats(); -} // Recht dafür vorhanden? -elseif (in_array($p, $privileges)) { - if ($p == "news") { + + $title = $p; + $content = ""; + + if ($p == "ical") { + require_once ('includes/pages/user_ical.php'); + user_ical(); + } elseif ($p == "atom") { + require_once ('includes/pages/user_atom.php'); + user_atom(); + } elseif ($p == "shifts_json_export") { + require_once ('includes/controller/shifts_controller.php'); + shifts_json_export_controller(); + } elseif ($p == "shifts_json_export_all") { + require_once ('includes/controller/shifts_controller.php'); + shifts_json_export_all_controller(); + } elseif ($p == "stats") { + require_once ('includes/pages/guest_stats.php'); + guest_stats(); + } elseif ($p == "user_password_recovery") { + require_once ('includes/controller/users_controller.php'); + $title = user_password_recovery_title(); + $content = user_password_recovery_controller(); + } elseif ($p == "news") { $title = news_title(); $content = user_news(); } elseif ($p == "news_comments") { @@ -171,14 +175,14 @@ elseif (in_array($p, $privileges)) { } elseif ($p == "admin_log") { $title = admin_log_title(); $content = admin_log(); + } elseif ($p == "credits") { + require_once ('includes/pages/guest_credits.php'); + $title = credits_title(); + $content = guest_credits(); } else { require_once ('includes/pages/guest_start.php'); $content = guest_start(); } -} elseif ($p == "credits") { - require_once ('includes/pages/guest_credits.php'); - $title = credits_title(); - $content = guest_credits(); } else { // Wenn schon eingeloggt, keine-Berechtigung-Seite anzeigen if (isset($user)) { @@ -190,27 +194,29 @@ elseif (in_array($p, $privileges)) { } } -// Hinweis für ungelesene Nachrichten -if (isset($user) && $p != "user_messages") - $content = user_unread_messages() . $content; +if (isset($user)) { + // Hinweis für ungelesene Nachrichten + if ($p != "user_messages") + $content = user_unread_messages() . $content; + + // Hinweis für Engel, die noch nicht angekommen sind + if ($user['Gekommen'] == 0) + $content = error(_("You are not marked as arrived. Please go to heaven's desk, get your angel badge and/or tell them that you arrived already."), true) . $content; - // Hinweis für Engel, die noch nicht angekommen sind -if (isset($user) && $user['Gekommen'] == 0) - $content = error(_("You are not marked as arrived. Please go to heaven's desk, get your angel badge and/or tell them that you arrived already."), true) . $content; - -if (isset($user) && $enable_tshirt_size && $user['Size'] == "") - $content = error(_("You need to specify a tshirt size in your settings!"), true) . $content; - -if (isset($user) && $user['DECT'] == "") - $content = error(_("You need to specify a DECT phone number in your settings! If you don't have a DECT phone, just enter \"-\"."), true) . $content; - - // Erzengel Hinweis für unbeantwortete Fragen -if (isset($user) && $p != "admin_questions") - $content = admin_new_questions() . $content; + if ($enable_tshirt_size && $user['Size'] == "") + $content = error(_("You need to specify a tshirt size in your settings!"), true) . $content; - // Erzengel Hinweis für freizuschaltende Engeltypen -if (isset($user) && $p != "admin_user_angeltypes") - $content = admin_new_user_angeltypes() . $content; + if ($user['DECT'] == "") + $content = error(_("You need to specify a DECT phone number in your settings! If you don't have a DECT phone, just enter \"-\"."), true) . $content; + + // Erzengel Hinweis für unbeantwortete Fragen + if ($p != "admin_questions") + $content = admin_new_questions() . $content; + + // Erzengel Hinweis für freizuschaltende Engeltypen + if ($p != "admin_user_angeltypes") + $content = admin_new_user_angeltypes() . $content; +} echo template_render('../templates/layout.html', array( 'theme' => isset($user) ? $user['color'] : $default_theme, -- cgit v1.2.3-54-g00ecf