diff options
author | diosmosis <diosmosis@users.noreply.github.com> | 2019-03-08 01:42:03 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-08 01:42:03 +0300 |
commit | 0648513483a7c12eec6cc0aa55d49f87c061f6b0 (patch) | |
tree | 55e49065186371f8504c4dd9eb23df17f0a9c49c /plugins/UsersManager | |
parent | 7153700fe2e0ca4144854f60af2145457c3e00ce (diff) |
Send email notification when user email changes. (#14136)
* Send email notification when user email changes.
* Forgot to add file.
* Apply pr fixes + send email for password changes too.
* Add quick test for new emails.
* Translate text
* Refactor according to review.
* ucfirst device name
* Fixing integration test
Diffstat (limited to 'plugins/UsersManager')
-rw-r--r-- | plugins/UsersManager/API.php | 77 | ||||
-rw-r--r-- | plugins/UsersManager/lang/en.json | 8 | ||||
-rw-r--r-- | plugins/UsersManager/templates/_userInfoChangedEmail.twig | 14 | ||||
-rw-r--r-- | plugins/UsersManager/tests/Integration/APITest.php | 13 |
4 files changed, 110 insertions, 2 deletions
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php index be3ea0d103..dba779cb41 100644 --- a/plugins/UsersManager/API.php +++ b/plugins/UsersManager/API.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\UsersManager; +use DeviceDetector\DeviceDetector; use Exception; use Piwik\Access; use Piwik\Access\CapabilitiesProvider; @@ -17,6 +18,8 @@ use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Date; +use Piwik\IP; +use Piwik\Mail; use Piwik\Metrics\Formatter; use Piwik\NoAccessException; use Piwik\Option; @@ -26,7 +29,7 @@ use Piwik\Plugins\Login\PasswordVerifier; use Piwik\SettingsPiwik; use Piwik\Site; use Piwik\Tracker\Cache; -use Piwik\Url; +use Piwik\View; /** * The UsersManager API lets you Manage Users and their permissions to access specific websites. @@ -915,6 +918,14 @@ class API extends \Piwik\Plugin\API Cache::deleteTrackerCache(); + if ($email != $userInfo['email']) { + $this->sendEmailChangedEmail($userInfo, $email); + } + + if ($passwordHasBeenUpdated) { + $this->sendPasswordChangedEmail($userInfo); + } + /** * Triggered after an existing user has been updated. * Event notify about password change. @@ -1379,4 +1390,68 @@ class API extends \Piwik\Plugin\API } return [$roles, $capabilities]; } + + private function sendEmailChangedEmail($user, $newEmail) + { + // send the mail to both the old email and the new email + foreach ([$newEmail, $user['email']] as $emailTo) { + $this->sendUserInfoChangedEmail('email', $user, $newEmail, $emailTo, 'UsersManager_EmailChangeNotificationSubject'); + } + } + + private function sendUserInfoChangedEmail($type, $user, $newValue, $emailTo, $subject) + { + $deviceDescription = $this->getDeviceDescription(); + + $view = new View('@UsersManager/_userInfoChangedEmail.twig'); + $view->type = $type; + $view->accountName = Common::sanitizeInputValue($user['login']); + $view->newEmail = Common::sanitizeInputValue($newValue); + $view->ipAddress = IP::getIpFromHeader(); + $view->deviceDescription = $deviceDescription; + + $mail = new Mail(); + + $mail->addTo($emailTo, $user['login']); + $mail->setSubject(Piwik::translate($subject)); + $mail->setDefaultFromPiwik(); + $mail->setWrappedHtmlBody($view); + + $replytoEmailName = Config::getInstance()->General['login_password_recovery_replyto_email_name']; + $replytoEmailAddress = Config::getInstance()->General['login_password_recovery_replyto_email_address']; + $mail->setReplyTo($replytoEmailAddress, $replytoEmailName); + + $mail->send(); + } + + private function sendPasswordChangedEmail($user) + { + $this->sendUserInfoChangedEmail('password', $user, null, $user['email'], 'UsersManager_PasswordChangeNotificationSubject'); + } + + private function getDeviceDescription() + { + $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; + + $uaParser = new DeviceDetector($userAgent); + $uaParser->parse(); + + $deviceName = ucfirst($uaParser->getDeviceName()); + if (!empty($deviceName)) { + $description = $deviceName; + } else { + $description = Piwik::translate('General_Unknown'); + } + + $deviceBrand = $uaParser->getBrandName(); + $deviceModel = $uaParser->getModel(); + if (!empty($deviceBrand) + || !empty($deviceModel) + ) { + $parts = array_filter([$deviceBrand, $deviceModel]); + $description .= ' (' . implode(' ', $parts) . ')'; + } + + return $description; + } } diff --git a/plugins/UsersManager/lang/en.json b/plugins/UsersManager/lang/en.json index a878d74971..5c54a7d238 100644 --- a/plugins/UsersManager/lang/en.json +++ b/plugins/UsersManager/lang/en.json @@ -156,6 +156,12 @@ "AreYouSureAddCapability": "Are you sure you want to give %1$s the %2$s capability for %3$s?", "AreYouSureRemoveCapability": "Are you sure you want to remove the %1$s capability from %2$s for %3$s?", "IncludedInUsersRole": "Included in this user's role.", - "Capability": "Capability" + "Capability": "Capability", + "EmailChangeNotificationSubject": "Your Matomo account's email address has just been changed", + "EmailChangedEmail1": "The email address associated with your account has been changed to %1$s", + "EmailChangedEmail2": "This change was initiated from the following device: %1$s (IP address = %2$s).", + "IfThisWasYouIgnoreIfNot": "If this was you, feel free to ignore this email. If this was not you, please login, correct your email address, change your password and contact your Matomo administrator.", + "PasswordChangeNotificationSubject": "Your Matomo account's password has just been changed", + "PasswordChangedEmail": "Your password has just been changed. The change was initiated from the following device: %1$s (IP address = %2$s)." } } diff --git a/plugins/UsersManager/templates/_userInfoChangedEmail.twig b/plugins/UsersManager/templates/_userInfoChangedEmail.twig new file mode 100644 index 0000000000..73d0b15c77 --- /dev/null +++ b/plugins/UsersManager/templates/_userInfoChangedEmail.twig @@ -0,0 +1,14 @@ +<p>{{ 'General_HelloUser'|translate('<strong>' ~ accountName ~ '</strong>')|raw }}</p> + +{% if type == 'email' %} +<p>{{ 'UsersManager_EmailChangedEmail1'|translate('<strong>' ~ newEmail ~ '</strong>')|raw }}.</p> + +<p>{{ 'UsersManager_EmailChangedEmail2'|translate(deviceDescription, ipAddress) }} {{ 'UsersManager_IfThisWasYouIgnoreIfNot'|translate }}</p> +{% elseif type == 'password' %} +<p>{{ 'UsersManager_PasswordChangedEmail'|translate(deviceDescription, ipAddress) }}</p> + +<p>{{ 'UsersManager_IfThisWasYouIgnoreIfNot'|translate }}</p> +{% endif %} + +<p>{{ 'General_ThankYouForUsingMatomo'|translate }}! +<br/>{{ 'General_TheMatomoTeam'|translate }}</p>
\ No newline at end of file diff --git a/plugins/UsersManager/tests/Integration/APITest.php b/plugins/UsersManager/tests/Integration/APITest.php index 3942d1371d..e75f27a127 100644 --- a/plugins/UsersManager/tests/Integration/APITest.php +++ b/plugins/UsersManager/tests/Integration/APITest.php @@ -12,6 +12,7 @@ use Piwik\Access\Role\View; use Piwik\Access\Role\Write; use Piwik\Auth\Password; use Piwik\Container\StaticContainer; +use Piwik\Mail; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugins\SitesManager\API as SitesManagerAPI; @@ -293,6 +294,11 @@ class APITest extends IntegrationTestCase public function test_updateUser() { + $capturedMails = []; + Piwik::addAction('Mail.send', function (Mail $mail) use (&$capturedMails) { + $capturedMails[] = $mail; + }); + $identity = FakeAccess::$identity; FakeAccess::$identity = $this->login; // ensure password will be checked against this user $this->api->updateUser($this->login, 'newPassword', 'email@example.com', 'newAlias', false, $this->password); @@ -307,6 +313,13 @@ class APITest extends IntegrationTestCase $passwordHelper = new Password(); $this->assertTrue($passwordHelper->verify(UsersManager::getPasswordHash('newPassword'), $user['password'])); + + $subjects = array_map(function (Mail $mail) { return $mail->getSubject(); }, $capturedMails); + $this->assertEquals([ + 'UsersManager_EmailChangeNotificationSubject', // sent twice to old email and new + 'UsersManager_EmailChangeNotificationSubject', + 'UsersManager_PasswordChangeNotificationSubject', + ], $subjects); } public function test_updateUser_doesNotChangePasswordIfFalsey() |