From 9c9ba64a7fe8c1c6b90e05eace3c2addbead06dc Mon Sep 17 00:00:00 2001 From: Sylvain Tissot Date: Mon, 17 Oct 2016 18:12:14 +0200 Subject: [PATCH] Allows a user or admin to reset his/her forgotten password with a code sent by email/SMS #18 --- config.inc.php | 20 ++++++ functions.inc.php | 17 +++++ languages/en.lang | 15 ++++ languages/fr.lang | 16 +++++ login.php | 1 + model/AdminHandler.php | 4 ++ model/MailboxHandler.php | 2 + password-change.php | 1 + password-recover.php | 1 + templates/index.tpl | 2 +- templates/login.tpl | 3 +- templates/password-change.tpl | 28 ++++++++ templates/password-recover.tpl | 26 +++++++ upgrade.php | 13 ++++ users/login.php | 1 + users/password-change.php | 104 +++++++++++++++++++++++++++ users/password-recover.php | 124 +++++++++++++++++++++++++++++++++ 17 files changed, 376 insertions(+), 2 deletions(-) create mode 120000 password-change.php create mode 120000 password-recover.php create mode 100644 templates/password-change.tpl create mode 100644 templates/password-recover.tpl create mode 100644 users/password-change.php create mode 100644 users/password-recover.php diff --git a/config.inc.php b/config.inc.php index 7a791862..1be94c07 100644 --- a/config.inc.php +++ b/config.inc.php @@ -119,6 +119,14 @@ $CONF['database_tables'] = array ( // Leave blank to send email from the logged-in Admin's Email address. $CONF['admin_email'] = ''; +// Site admin name +// This will be used as signature in notification messages +$CONF['admin_name'] = 'Postmaster'; + +// Site admin phone number +// This will be used if a user cannot access his/her email and needs support +$CONF['admin_phone'] = ''; + // Mail Server // Hostname (FQDN) of your mail server. // This is used to send email to Postfix in order to create mailboxes. @@ -572,6 +580,18 @@ $CONF['new_quota_table'] = 'YES'; // http://www.php.net/manual/en/function.imap-open.php $CONF['create_mailbox_subdirs_hostoptions'] = array(); +// Optional: +// Allows a user to reset his forgotten password with a code sent by email/SMS +$CONF['forgotten_user_password_reset'] = true; +// Allows an admin to reset his forgotten password with a code sent by email/SMS +$CONF['forgotten_admin_password_reset'] = true; + +// Clickatell gateway to send SMS code for password reset +// API type: HTTP +$CONF['clickatell_api_id'] = ''; +$CONF['clickatell_user'] = ''; +$CONF['clickatell_password'] = ''; +$CONF['clickatell_sender'] = ''; // Theme Config // Specify your own logo and CSS file diff --git a/functions.inc.php b/functions.inc.php index 1662558f..67943f20 100644 --- a/functions.inc.php +++ b/functions.inc.php @@ -1939,4 +1939,21 @@ function getRemoteAddr() { return $REMOTE_ADDR; } + +/** + * Returns a hash for a username valid for one day + * + * @param String $username user name + * @return String password recovery code + */ +function getPasswordRecoveryCode($username) +{ + $username = trim(strtolower($username)); + $date = date('Y-m-d'); + + $code = substr(strtoupper(md5('SECRET SALTING PHRASE' . $username . $date)), 0, 6); + + return $code; +} + /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */ diff --git a/languages/en.lang b/languages/en.lang index d486043b..4b0c65b2 100644 --- a/languages/en.lang +++ b/languages/en.lang @@ -146,6 +146,10 @@ $PALANG['pCreate_mailbox_username_text_error1'] = 'The EMAIL is not valid!'; $PALANG['pCreate_mailbox_username_text_error3'] = 'You have reached your limit to create mailboxes!'; $PALANG['pCreate_mailbox_password_text'] = 'Password for POP3/IMAP'; $PALANG['pCreate_mailbox_name_text'] = 'Full name'; +$PALANG['pCreate_mailbox_phone'] = 'Mobile phone'; +$PALANG['pCreate_mailbox_phone_desc'] = "Used to send a SMS if the password is forgotten"; +$PALANG['pCreate_mailbox_email'] = 'Other e-mail'; +$PALANG['pCreate_mailbox_email_desc'] = "Used if the password is forgotten"; $PALANG['pCreate_mailbox_mail'] = 'Send Welcome mail'; $PALANG['pCreate_mailbox_result_error'] = 'Creating the mailbox %s failed!'; $PALANG['pCreate_mailbox_result_success'] = 'The mailbox %s has been added to the mailbox table.'; @@ -174,6 +178,16 @@ $PALANG['change_password'] = 'Change Password'; $PALANG['pPassword_result_error'] = 'Changing the password for %s failed!'; $PALANG['pPassword_result_success'] = 'The password for %s has been changed.'; +$PALANG['pPassword_recovery_title'] = 'Follow the instructions to reset your password.'; +$PALANG['pPassword_recovery_button'] = 'Send me the code'; +$PALANG['pPassword_recovery_email_body'] = "Hello,\n\nUse the following link to change your email password:\n%s\n\nRegards,\n\n" . $CONF['admin_name']; +$PALANG['pPassword_recovery_email_sent'] = 'An email was sent to:'; +$PALANG['pPassword_recovery_sms_body'] = "Hello,\nThe code to change your password is: %s\n" . $CONF['admin_name']; +$PALANG['pPassword_recovery_sms_sent'] = 'An SMS was sent to:'; +$PALANG['pPassword_recovery_no_alternative'] = 'No alternative contact info were found. Please contact the support at ' . $CONF['admin_email'] . 'or by phone to ' . $CONF['admin_phone']; +$PALANG['pPassword_password_code'] = 'Code sent by email/SMS'; +$PALANG['pPassword_code_text_error'] = 'Invalid code'; + $PALANG['pEdit_vacation_set'] = 'Change / Set away message'; $PALANG['pEdit_vacation_remove'] = 'Remove away message'; @@ -298,6 +312,7 @@ $PALANG['pAdminEdit_admin_result_success'] = 'The admin %s has been modified.'; $PALANG['pUsersLogin_welcome'] = 'Mailbox users login to change your password and aliases.'; $PALANG['pUsersLogin_username_incorrect'] = 'Your login is not correct. Make sure that you login with your email address!'; $PALANG['pUsersLogin_password_incorrect'] = 'Your password is not correct!'; +$PALANG['pUsersLogin_password_recover'] = 'I forgot my password'; $PALANG['pUsersMenu_vacation'] = 'Auto Response'; $PALANG['pUsersMenu_edit_alias'] = 'Change your forward'; diff --git a/languages/fr.lang b/languages/fr.lang index 2f9f4d7e..9f7835cf 100644 --- a/languages/fr.lang +++ b/languages/fr.lang @@ -145,6 +145,10 @@ $PALANG['pCreate_mailbox_username_text_error1'] = 'L\'adresse courriel est inval $PALANG['pCreate_mailbox_username_text_error3'] = 'Vous avez atteint le nombre maximum de comptes courriel !'; $PALANG['pCreate_mailbox_password_text'] = 'Mot de passe pour le compte POP3/IMAP'; $PALANG['pCreate_mailbox_name_text'] = 'Nom complet'; +$PALANG['pCreate_mailbox_phone'] = 'Téléphone mobile'; +$PALANG['pCreate_mailbox_phone_desc'] = "Utilisé pour l'envoi de SMS en cas d'oubli du mot de passe"; +$PALANG['pCreate_mailbox_email'] = 'E-mail secondaire'; +$PALANG['pCreate_mailbox_email_desc'] = "Utilisé en cas d'oubli du mot de passe"; $PALANG['pCreate_mailbox_mail'] = 'Envoyer le message de bienvenue'; $PALANG['pCreate_mailbox_result_error'] = 'Échec de la création du compte courriel %s !'; $PALANG['pCreate_mailbox_result_success'] = 'Le compte courriel %s a été ajouté à la table des comptes !'; @@ -172,6 +176,17 @@ $PALANG['pPassword_password_text_error'] = 'Le mot de passe fourni ne correspond $PALANG['change_password'] = 'Changer le mot de passe'; $PALANG['pPassword_result_error'] = 'Impossible de changer le mot de passe de %s !'; $PALANG['pPassword_result_success'] = 'Le mot de passe de %s a été changé !'; +$PALANG['pPassword_recovery_title'] = 'Suivez les instructions pour réinitialiser votre mot de passe.'; +$PALANG['pPassword_recovery_button'] = 'Envoyez-moi le code'; +$PALANG['pPassword_recovery_email_body'] = "Bonjour,\n\nUtilisez le lien suivant pour modifier votre mot de passe :\n%s\n\nSalutations,\n\n" . $CONF['admin_name']; +$PALANG['pPassword_recovery_email_sent'] = 'Un code a été envoyé à :'; +$PALANG['pPassword_recovery_sms_body'] = "Bonjour,\nLe code pour modifier votre mot de passe: %s\n" . $CONF['admin_name']; +$PALANG['pPassword_recovery_sms_sent'] = 'Un code a été envoyé par SMS à :'; +$PALANG['pPassword_recovery_no_alternative'] = "Aucun moyen de contact alternatif n'a été trouvé. Contactez le support à " . $CONF['admin_email'] . ' ou par téléphone ' . $CONF['admin_phone']; +$PALANG['pPassword_password_code'] = 'Code reçu par email/SMS'; +$PALANG['pPassword_code_text_error'] = 'Code invalide'; + +>>>>>>> Allows a user or admin to reset his/her forgotten password with a code sent by email/SMS #18 $PALANG['pEdit_vacation_set'] = 'Activer le répondeur'; $PALANG['pEdit_vacation_remove'] = 'Désactiver le répondeur'; $PALANG['pVacation_result_error'] = 'Impossible de mettre à jour les réglages du répondeur de %s !'; @@ -294,6 +309,7 @@ $PALANG['pAdminEdit_admin_result_success'] = 'L\'administrateur %s a été ajout $PALANG['pUsersLogin_welcome'] = 'Entrer votre adresse courriel pour modifier votre mot de passe et vos transferts.'; $PALANG['pUsersLogin_username_incorrect'] = 'L\'adresse courriel est invalide. Assurez-vous d\'avoir correctement saisi votre adresse courriel !'; $PALANG['pUsersLogin_password_incorrect'] = 'Votre mot de passe est invalide !'; +$PALANG['pUsersLogin_password_recover'] = 'J\'ai oublié mon mot de passe'; $PALANG['pUsersMenu_vacation'] = 'Réponse Automatique'; $PALANG['pUsersMenu_edit_alias'] = 'Modifier votre transfert'; diff --git a/login.php b/login.php index 42acda0c..23ea392f 100644 --- a/login.php +++ b/login.php @@ -85,6 +85,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") $smarty->assign ('language_selector', language_selector(), false); $smarty->assign ('smarty_template', 'login'); $smarty->assign ('logintype', 'admin'); +$smarty->assign ('forgotten_password_reset', Config::read('forgotten_admin_password_reset')); $smarty->display ('index.tpl'); /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */ diff --git a/model/AdminHandler.php b/model/AdminHandler.php index bfee687b..e33a6232 100644 --- a/model/AdminHandler.php +++ b/model/AdminHandler.php @@ -47,6 +47,10 @@ class AdminHandler extends PFAHandler { /*select*/ 'password as password2' ), + 'phone' => pacol( 1, 1, 0, 'text', 'pCreate_mailbox_phone', 'pCreate_mailbox_phone_desc', ''), + + 'email_other' => pacol( 1, 1, 0, 'mail', 'pCreate_mailbox_email', 'pCreate_mailbox_email_desc', ''), + 'superadmin' => pacol( 1, 1, 0, 'bool', 'super_admin' , 'super_admin_desc' , 0 # TODO: (finally) replace the ALL domain with a column in the admin table # TODO: current status: 'superadmin' column exists and is written when storing an admin with AdminHandler, diff --git a/model/MailboxHandler.php b/model/MailboxHandler.php index 15f47431..4efe6f90 100644 --- a/model/MailboxHandler.php +++ b/model/MailboxHandler.php @@ -35,6 +35,8 @@ class MailboxHandler extends PFAHandler { # read_from_db_postprocess() also sets 'quotabytes' for use in init() # TODO: read used quota from quota/quota2 table 'active' => pacol( 1, 1, 1, 'bool', 'active' , '' , 1 ), + 'phone' => pacol( 1, 1, 0, 'text', 'pCreate_mailbox_phone' , 'pCreate_mailbox_phone_desc' , ''), + 'email_other' => pacol( 1, 1, 0, 'mail', 'pCreate_mailbox_email' , 'pCreate_mailbox_email_desc' , ''), 'welcome_mail' => pacol( $this->new, $this->new, 0, 'bool', 'pCreate_mailbox_mail' , '' , 1, /*options*/ '', /*not_in_db*/ 1 ), diff --git a/password-change.php b/password-change.php new file mode 120000 index 00000000..c974035c --- /dev/null +++ b/password-change.php @@ -0,0 +1 @@ +users/password-change.php \ No newline at end of file diff --git a/password-recover.php b/password-recover.php new file mode 120000 index 00000000..64aa57a6 --- /dev/null +++ b/password-recover.php @@ -0,0 +1 @@ +users/password-recover.php \ No newline at end of file diff --git a/templates/index.tpl b/templates/index.tpl index 58b1bc22..8926dd01 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -1,7 +1,7 @@ {strip} {include file="header.tpl"} -{if $smarty_template != 'login'} +{if $smarty_template !== 'login' && $smarty_template !== 'password-recover' && $smarty_template !== 'password-change'} {config_load file="menu.conf" section=$smarty_template} {if $authentication_has_role.user} {include file='users_menu.tpl'} diff --git a/templates/login.tpl b/templates/login.tpl index 333b3693..cde0c80d 100644 --- a/templates/login.tpl +++ b/templates/login.tpl @@ -14,7 +14,8 @@ - + {if $forgotten_password_reset}
+ {$PALANG.pUsersLogin_password_recover}{/if} diff --git a/templates/password-change.tpl b/templates/password-change.tpl new file mode 100644 index 00000000..1c7e99f9 --- /dev/null +++ b/templates/password-change.tpl @@ -0,0 +1,28 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + +

{$PALANG.pPassword_welcome}

{$PALANG.pLogin_username} :
{$PALANG.pPassword_password_code} :
{$PALANG.pPassword_password} :
{$PALANG.pPassword_password2} :
+
+
diff --git a/templates/password-recover.tpl b/templates/password-recover.tpl new file mode 100644 index 00000000..ebe0b017 --- /dev/null +++ b/templates/password-recover.tpl @@ -0,0 +1,26 @@ +
+
+ + + + + + + + + + + + +
{$PALANG.pPassword_recovery_title}
  + +
+
+{literal} + +{/literal} +
diff --git a/upgrade.php b/upgrade.php index 8695bd25..b6e5b7a0 100644 --- a/upgrade.php +++ b/upgrade.php @@ -1672,6 +1672,19 @@ function upgrade_1836_mysql() { } } + +function upgrade_1837_mysql() { + # alternative contact means to reset a forgotten password + foreach(array('admin', 'mailbox') as $table_to_change) { + $table = table_by_key($table_to_change); + if(!_mysql_field_exists($table, 'phone')) { + db_query_parsed("ALTER TABLE `$table` ADD COLUMN `phone` varchar(30) NOT NULL DEFAULT ''"); + } + if(!_mysql_field_exists($table, 'email_other')) { + db_query_parsed("ALTER TABLE `$table` ADD COLUMN `email_other` varchar(255) NOT NULL DEFAULT ''"); + } + } +} # TODO MySQL: # - various varchar fields do not have a default value # https://sourceforge.net/projects/postfixadmin/forums/forum/676076/topic/3419725 diff --git a/users/login.php b/users/login.php index 41fbd214..ec6ed72d 100644 --- a/users/login.php +++ b/users/login.php @@ -62,6 +62,7 @@ if ($_SERVER['REQUEST_METHOD'] == "POST") $smarty->assign ('language_selector', language_selector(), false); $smarty->assign ('smarty_template', 'login'); $smarty->assign ('logintype', 'user'); +$smarty->assign ('forgotten_password_reset', Config::read('forgotten_user_password_reset')); $smarty->display ('index.tpl'); /* vim: set expandtab softtabstop=3 tabstop=3 shiftwidth=3: */ diff --git a/users/password-change.php b/users/password-change.php new file mode 100644 index 00000000..b21eb92a --- /dev/null +++ b/users/password-change.php @@ -0,0 +1,104 @@ +init($tUsername)) { + flash_error($handler->errormsg); + } else { + $values = $handler->result; + $values[$handler->getId_field()] = $tUsername; + $values['password'] = $fPassword; + $values['password2'] = $fPassword2; + if ($handler->set($values) && $handler->store()) { + flash_info(Config::lang_f('pPassword_result_success', $tUsername)); + header('Location: ' . dirname($_SERVER['REQUEST_URI']) . '/main.php'); + exit(0); + } else { + foreach($handler->errormsg as $msg) { + flash_error($msg); + } + } + } + } +} + +$smarty->assign ('language_selector', language_selector(), false); +$smarty->assign('tUsername', @$tUsername); +$smarty->assign('tCode', @$tCode); +$smarty->assign ('smarty_template', 'password-change'); +$smarty->display ('index.tpl'); + +/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */ +?> diff --git a/users/password-recover.php b/users/password-recover.php new file mode 100644 index 00000000..92d617f7 --- /dev/null +++ b/users/password-recover.php @@ -0,0 +1,124 @@ +assign ('language_selector', language_selector(), false); +$smarty->assign ('smarty_template', 'password-recover'); +$smarty->display ('index.tpl'); + +/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */ +?>