From 12e01c433621b07f4b8a848b3a5243d06218dbde Mon Sep 17 00:00:00 2001 From: Pysis868 Date: Sun, 4 Feb 2018 18:05:13 -0500 Subject: [PATCH] Finished lost/reset password feature by adding the email functionality. Obviously this needs any, and possibly per-node configuration based on deployment details, so I have added configuration fields for sending mail. I left in some sample values to help give an idea of what may be used for each, along with others already filled in with their actual value for the project that is less likely to change. I also cleaned up some of the other fields in the same way. The body of the message could be large, so I moved this to be loaded from a file instead. Used SwiftMailer based on recommendation over PHP's built-in mail function. PHPMailer was also a recommendation. So this needs a dependency install. Heard we use PHP 7 in production and/or stage. Not sure how I got started with 5. Maybe a nice common denominator. Also thinking that's the version of php/php-fpm that came with my Mac too. Added more layers to the API between success and fails. There must be a better way instead of so many nested if statements!! :P but we'll wait until a back-end API refactor for that.. Having the mail code in a separate file keeps it nice and clean too, plus keeps a lot of related code together in case you need to modify it in the future. So far we're just using Google's Gmail servers in production, so I'll leave SSL ((START)TLS?) hard-coded enabled on for now, and that seems good to be secure by default too! :) --- .env.example | 14 ++++-- ajax/lost_password.php | 25 ++++++---- composer.json | 3 +- composer.lock | 56 +++++++++++++++++++++- config.php | 22 +++++++-- content/lostPasswordEmailBodyTemplate.txt | 5 ++ lib/zmailer.php | 57 +++++++++++++++++++++++ 7 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 content/lostPasswordEmailBodyTemplate.txt create mode 100644 lib/zmailer.php diff --git a/.env.example b/.env.example index 327379929..9bba34b3f 100644 --- a/.env.example +++ b/.env.example @@ -2,10 +2,18 @@ DBMS=mysql DBHOST=localhost DBPORT= -DBNAME=zmap_v2 -DBUSER=root -DBPASSWD="" +DBNAME=zeldamaps +DBUSER= +DBPASSWD="" # Quotes at least around the password to allow for special characters. PREFIX= [SECURITY] LOST_PASSWORD_RANDOM_GENERATOR_STRENGTH=MEDIUM +[MAIL] +server="smtp.server.com" +port=465 +username="noreply@server.com" +password="" +replyToAddress="noreply@server.com" +lostPasswordSubject="Zelda Maps - Password Reset" +lostPasswordBodyTemplateFilePath="content/lostPasswordEmailBodyTemplate.txt" diff --git a/ajax/lost_password.php b/ajax/lost_password.php index 6c296fe12..71896ae5e 100644 --- a/ajax/lost_password.php +++ b/ajax/lost_password.php @@ -17,19 +17,28 @@ $randomPassword = $generator->generateString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/`~!@#$%^&*()-_=+[{]}\|;:\'",<.>/?'); $hash = password_hash($randomPassword, PASSWORD_DEFAULT, ['cost' => 13]); - $query = "SELECT `id` FROM `{$map_prefix}user` WHERE `email` = '$email'"; - $result = $mysqli->query($query); - $row = $result->fetch_assoc(); + $querySelectUser = "SELECT `id`, `name` FROM `{$map_prefix}user` WHERE `email` = '$email'"; + $resultSelectUser = $mysqli->query($querySelectUser); + $rowSelectUser = $resultSelectUser->fetch_assoc(); - if($row) { + if($rowSelectUser) { $query = "UPDATE `{$map_prefix}user` SET `password` = '$hash' WHERE `email` = '$email'"; - //echo $query; $result = $mysqli->query($query); if ($result) { - commit(); - # TODO: Remove and put password in email instead!! - echo json_encode(array("success"=>true, "msg"=>"Password reset.")); + $commitResult = commit(); + + if($commitResult) { + include_once("$path/lib/zmailer.php"); + $mailResult = sendMail(createResetPasswordEmail($email, $rowSelectUser['name'], $randomPassword)); + if($mailResult) { + echo json_encode(array("success"=>true, "msg"=>"Password reset. Email sent.")); + } else { + echo json_encode(array("success"=>false, "msg"=>"Password reset. Email not sent.")); + } + } else { + echo json_encode(array("success"=>false, "msg"=>"Password not reset. Database error.")); + } } else { rollback(); echo json_encode(array("success"=>false, "msg"=>"Password not reset.")); diff --git a/composer.json b/composer.json index 792c5a231..dd84f6849 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,6 @@ { "require": { - "ircmaxell/random-lib": "^1.2" + "ircmaxell/random-lib": "^1.2", + "swiftmailer/swiftmailer": "^5.4" } } diff --git a/composer.lock b/composer.lock index c875d6518..9a745d1e8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "3cbf23e3304389ac7d9ac1000f77eff7", + "content-hash": "28f5f43ddb35e24fbf53ea9b7304769b", "packages": [ { "name": "ircmaxell/random-lib", @@ -106,6 +106,60 @@ "description": "A Base Security Library", "homepage": "https://github.com/ircmaxell/SecurityLib", "time": "2015-03-20T14:31:23+00:00" + }, + { + "name": "swiftmailer/swiftmailer", + "version": "v5.4.9", + "source": { + "type": "git", + "url": "https://github.com/swiftmailer/swiftmailer.git", + "reference": "7ffc1ea296ed14bf8260b6ef11b80208dbadba91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/7ffc1ea296ed14bf8260b6ef11b80208dbadba91", + "reference": "7ffc1ea296ed14bf8260b6ef11b80208dbadba91", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "mockery/mockery": "~0.9.1", + "symfony/phpunit-bridge": "~3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "files": [ + "lib/swift_required.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Corbyn" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Swiftmailer, free feature-rich PHP mailer", + "homepage": "https://swiftmailer.symfony.com", + "keywords": [ + "email", + "mail", + "mailer" + ], + "time": "2018-01-23T07:37:21+00:00" } ], "packages-dev": [], diff --git a/config.php b/config.php index 955ea6346..e4b74b32d 100644 --- a/config.php +++ b/config.php @@ -50,6 +50,18 @@ } $lostPasswordRandomGeneratorStrengthConstant = new SecurityLib\Strength((new SecurityLib\Strength)->getConstList()[$lostPasswordRandomGeneratorStrengthString]); + $mailServer = $ENV['server']; + $mailPort = $ENV['port']; + $mailUsername = $ENV['username']; + $mailPassword = $ENV['password']; + $mailReplyToAddress = $ENV['replyToAddress']; + + $lostPasswordSubject = $ENV["lostPasswordSubject"]; + if(isset($ENV["lostPasswordBodyTemplateFilePath"])) $lostPasswordBodyTemplateFilePath = $ENV["lostPasswordBodyTemplateFilePath"]; + if(isset($lostPasswordBodyTemplateFilePath) && !empty($lostPasswordBodyTemplateFilePath)) { + $lostPasswordBodyTemplate = file_get_contents($lostPasswordBodyTemplateFilePath); + } + $_ENV = array_merge($ENV,$_ENV); } @@ -62,25 +74,25 @@ function begin() { global $mysqli; - @$mysqli->query("BEGIN"); + return $mysqli->query("BEGIN"); } function commit() { global $mysqli; - @$mysqli->query("COMMIT"); + return $mysqli->query("COMMIT"); } function rollback() { global $mysqli; - @$mysqli->query("ROLLBACK"); + return $mysqli->query("ROLLBACK"); } function start_session($name="zmap") { if(!defined("PHP_MAJOR_VERSION") || PHP_MAJOR_VERSION<7) { - session_start($name); + return session_start($name); } else { $opts = ["name"=>$name]; - session_start($opts); + return session_start($opts); } } ?> diff --git a/content/lostPasswordEmailBodyTemplate.txt b/content/lostPasswordEmailBodyTemplate.txt new file mode 100644 index 000000000..e76a44b2b --- /dev/null +++ b/content/lostPasswordEmailBodyTemplate.txt @@ -0,0 +1,5 @@ +Hello {{name}}, + +You have recently requested to have your password reset. + +Your new password will be: {{newPassword}} diff --git a/lib/zmailer.php b/lib/zmailer.php new file mode 100644 index 000000000..f74ea8cde --- /dev/null +++ b/lib/zmailer.php @@ -0,0 +1,57 @@ +setUsername($mailUsername) + ->setPassword($mailPassword) + ; +} + +function createResetPasswordEmail($toAddress, $toName, $newPassword) { + global $lostPasswordBodyTemplate, $mailReplyToAddress, $lostPasswordSubject; + $body = ""; + if(isset($lostPasswordBodyTemplate)) { + $body = $lostPasswordBodyTemplate; + $body = str_replace("{{name}}", $toName, $body); + $body = str_replace("{{newPassword}}", $newPassword, $body); + } else { + error_log("Error: No lost password mail body message template available; exiting..."); + exit; + } + + // Debug + // error_log("Generating reset password email with the following parameters:\n" . + // "reply-to: $mailReplyToAddress\n" . + // "to: $toName <$toAddress>\n" . + // "subject: $lostPasswordSubject\n" . + // "body: $body" + // ); + + return (new Swift_Message($lostPasswordSubject)) + ->setFrom([$mailReplyToAddress]) + ->setTo([$toAddress => $toName]) + ->setBody($body) + ; +} + +function sendMail($message) { + global $mailer; + return ($mailer->send($message)); +} + +$mailer = new Swift_Mailer(prepareMailTransport()); +?>