Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsgiehl <stefan@matomo.org>2020-07-24 14:11:05 +0300
committersgiehl <stefan@matomo.org>2020-07-24 15:28:48 +0300
commit679e73f1236969db0c2d767655cb84456a727d24 (patch)
tree648722fa79cb524f8819857e79163e0c1cf16d59 /plugins
parent6b5f8138180716d5088d764f0b41d5787159b28a (diff)
parent3e1234a887f56a1cf853e29ba89370b234af5127 (diff)
Merge branch '3.x-dev' into 4.x-dev
Diffstat (limited to 'plugins')
-rw-r--r--plugins/API/Menu.php4
-rw-r--r--plugins/API/tests/System/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml8
-rw-r--r--plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__API.getSuggestedValuesForSegment.xml4
-rw-r--r--plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__VisitsSummary.get_range.xml10
m---------plugins/CustomVariables0
-rw-r--r--plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getBrand_day.xml2
-rw-r--r--plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getModel_day.xml2
-rw-r--r--plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getType_day.xml2
-rw-r--r--plugins/Installation/tests/UI/expected-screenshots/Installation_js_tracking.png4
-rw-r--r--plugins/Live/templates/getLastVisitsStart.twig4
-rw-r--r--plugins/Login/Controller.php44
-rw-r--r--plugins/Login/PasswordResetter.php48
-rw-r--r--plugins/Login/lang/en.json3
-rw-r--r--plugins/Login/templates/confirmResetPassword.twig45
-rw-r--r--plugins/Login/tests/Integration/PasswordResetterTest.php23
-rw-r--r--plugins/Login/tests/UI/Login_spec.js11
-rw-r--r--plugins/Login/tests/UI/expected-screenshots/Login_password_reset_confirm.png3
-rw-r--r--plugins/Morpheus/templates/javascriptCode.twig4
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getImageTrackingCode.xml2
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getJavascriptTag.xml4
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getImageTrackingCode.xml2
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getJavascriptTag.xml4
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getImageTrackingCode.xml2
-rw-r--r--plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getJavascriptTag.xml4
-rw-r--r--plugins/UsersManager/API.php32
-rw-r--r--plugins/UsersManager/config/config.php4
-rw-r--r--plugins/UsersManager/tests/Integration/APITest.php26
-rw-r--r--plugins/UsersManager/tests/Integration/UserPreferencesTest.php32
28 files changed, 243 insertions, 90 deletions
diff --git a/plugins/API/Menu.php b/plugins/API/Menu.php
index e79b5be6af..84ac6faf9d 100644
--- a/plugins/API/Menu.php
+++ b/plugins/API/Menu.php
@@ -8,7 +8,7 @@
*/
namespace Piwik\Plugins\API;
-use Piwik\DeviceDetector\DeviceDetectorCache;
+use Piwik\Container\StaticContainer;
use Piwik\Menu\MenuAdmin;
use Piwik\Menu\MenuTop;
use Piwik\Piwik;
@@ -51,7 +51,7 @@ class Menu extends \Piwik\Plugin\Menu
}
$ua = new OperatingSystem($_SERVER['HTTP_USER_AGENT']);
- $ua->setCache(new DeviceDetectorCache(86400));
+ $ua->setCache(StaticContainer::get('DeviceDetector\Cache\Cache'));
$parsedOS = $ua->parse();
if (!empty($parsedOS['short_name']) && in_array($parsedOS['short_name'], array(self::DD_SHORT_NAME_ANDROID, self::DD_SHORT_NAME_IOS))) {
diff --git a/plugins/API/tests/System/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml b/plugins/API/tests/System/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
index 50f6f02561..e4a75f4309 100644
--- a/plugins/API/tests/System/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
+++ b/plugins/API/tests/System/expected/test_AutoSuggestAPITest__Live.getLastVisitsDetails_range.xml
@@ -3216,7 +3216,7 @@
<idVisit>28</idVisit>
<visitIp>2003:f6:93bf:26f:9ec7:a6ff:fe29:27df</visitIp>
- <fingerprint>b6f1d5120b2b15a2</fingerprint>
+ <fingerprint>47b25ed9972126db</fingerprint>
<actionDetails>
<row>
<type>action</type>
@@ -3610,7 +3610,7 @@
<idVisit>27</idVisit>
<visitIp>2003:f6:93bf:26f:9ec7:a6ff:fe29:27df</visitIp>
- <fingerprint>b6f1d5120b2b15a2</fingerprint>
+ <fingerprint>47b25ed9972126db</fingerprint>
<actionDetails>
<row>
<type>action</type>
@@ -4214,7 +4214,7 @@
<idVisit>26</idVisit>
<visitIp>137.82.0.0</visitIp>
- <fingerprint>31ea532c06bf48a0</fingerprint>
+ <fingerprint>02bc7f4e9b9d3069</fingerprint>
<actionDetails>
<row>
<type>action</type>
@@ -4986,7 +4986,7 @@
<idVisit>25</idVisit>
<visitIp>137.82.0.0</visitIp>
- <fingerprint>31ea532c06bf48a0</fingerprint>
+ <fingerprint>02bc7f4e9b9d3069</fingerprint>
<actionDetails>
<row>
<type>action</type>
diff --git a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__API.getSuggestedValuesForSegment.xml b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__API.getSuggestedValuesForSegment.xml
index 77a1416d61..53b737fd3a 100644
--- a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__API.getSuggestedValuesForSegment.xml
+++ b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__API.getSuggestedValuesForSegment.xml
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
+ <row>02bc7f4e9b9d3069</row>
<row>05f59d2b325126c8</row>
<row>0fc8a7b5c1071e7d</row>
<row>17b5ac19cce8a192</row>
<row>289e2fcbb06929fa</row>
<row>28a7ee52024f3a89</row>
- <row>31ea532c06bf48a0</row>
<row>3e5f540b8952a4ab</row>
+ <row>47b25ed9972126db</row>
<row>65eefc1c2b3aa35c</row>
<row>85aaa85a2071daf5</row>
<row>9b641f2d195745f4</row>
<row>a36244db4114afa7</row>
- <row>b6f1d5120b2b15a2</row>
<row>cc20676f98f5d568</row>
<row>d5a95c7fe2a8286d</row>
<row>e61a3357f12d99f5</row>
diff --git a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__VisitsSummary.get_range.xml b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__VisitsSummary.get_range.xml
index d2daf5d2f8..18529da9ad 100644
--- a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__VisitsSummary.get_range.xml
+++ b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_fingerprint__VisitsSummary.get_range.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
<nb_visits>2</nb_visits>
- <nb_actions>6</nb_actions>
+ <nb_actions>5</nb_actions>
<nb_visits_converted>2</nb_visits_converted>
<bounce_count>1</bounce_count>
- <sum_visit_length>1623</sum_visit_length>
- <max_actions>5</max_actions>
+ <sum_visit_length>1622</sum_visit_length>
+ <max_actions>4</max_actions>
<bounce_rate>50%</bounce_rate>
- <nb_actions_per_visit>3</nb_actions_per_visit>
- <avg_time_on_site>812</avg_time_on_site>
+ <nb_actions_per_visit>2.5</nb_actions_per_visit>
+ <avg_time_on_site>811</avg_time_on_site>
</result> \ No newline at end of file
diff --git a/plugins/CustomVariables b/plugins/CustomVariables
-Subproject 26b5f6d98bf3a89e5ed11f40c94eee8491d6b08
+Subproject 1aee5523fb807e0d8a8cc5290e81133ca7dd563
diff --git a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getBrand_day.xml b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getBrand_day.xml
index 703ec64682..2bf75e28d9 100644
--- a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getBrand_day.xml
+++ b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getBrand_day.xml
@@ -24,7 +24,7 @@
</row>
<row>
<label>Unknown</label>
- <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
<nb_visits>3</nb_visits>
<nb_actions>3</nb_actions>
<nb_users>0</nb_users>
diff --git a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getModel_day.xml b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getModel_day.xml
index 2d6509f655..1e4dc2c09c 100644
--- a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getModel_day.xml
+++ b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getModel_day.xml
@@ -44,7 +44,7 @@
</row>
<row>
<label>Unknown</label>
- <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
<nb_visits>2</nb_visits>
<nb_actions>2</nb_actions>
<nb_users>0</nb_users>
diff --git a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getType_day.xml b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getType_day.xml
index d959ebf6cb..6a22c1926e 100644
--- a/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getType_day.xml
+++ b/plugins/DevicesDetection/tests/System/expected/test___DevicesDetection.getType_day.xml
@@ -46,7 +46,7 @@
</row>
<row>
<label>Unknown</label>
- <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
<nb_visits>2</nb_visits>
<nb_actions>2</nb_actions>
<nb_users>0</nb_users>
diff --git a/plugins/Installation/tests/UI/expected-screenshots/Installation_js_tracking.png b/plugins/Installation/tests/UI/expected-screenshots/Installation_js_tracking.png
index bf3a20799c..d267226719 100644
--- a/plugins/Installation/tests/UI/expected-screenshots/Installation_js_tracking.png
+++ b/plugins/Installation/tests/UI/expected-screenshots/Installation_js_tracking.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a6f5e6034ef4353021b67bee75de9d6ae942157d7216917bf8600602e35aa31f
-size 239288
+oid sha256:705ed714271df281728087c668dee48f4d5c4d05cc745059c3369d82574dca98
+size 240228
diff --git a/plugins/Live/templates/getLastVisitsStart.twig b/plugins/Live/templates/getLastVisitsStart.twig
index c0a380a501..a1b9c1f590 100644
--- a/plugins/Live/templates/getLastVisitsStart.twig
+++ b/plugins/Live/templates/getLastVisitsStart.twig
@@ -73,7 +73,7 @@
{% else %}
{% if action.url is defined and action.url is not empty %}
- <a href="{{ action.url|safelink|e('html_attr') }}" target="_blank">
+ <a href="{{ action.url|safelink|e('html_attr') }}" target="_blank" rel="noreferrer noopener">
{% endif %}
{% if action.type == 'action' %}
{# white spacing matters as Chrome tooltip display whitespaces #}
@@ -131,4 +131,4 @@ $(function () {
});
});
</script>
-{% endif %} \ No newline at end of file
+{% endif %}
diff --git a/plugins/Login/Controller.php b/plugins/Login/Controller.php
index aa26ed0406..a340368d41 100644
--- a/plugins/Login/Controller.php
+++ b/plugins/Login/Controller.php
@@ -9,6 +9,8 @@
namespace Piwik\Plugins\Login;
use Exception;
+use Piwik\Access;
+use Piwik\Auth\Password;
use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
@@ -18,6 +20,7 @@ use Piwik\Nonce;
use Piwik\Piwik;
use Piwik\Plugins\Login\Security\BruteForceDetection;
use Piwik\Plugins\UsersManager\Model AS UsersModel;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\QuickForm2;
use Piwik\Session;
use Piwik\Url;
@@ -30,6 +33,8 @@ use Piwik\View;
*/
class Controller extends \Piwik\Plugin\ControllerAdmin
{
+ const NONCE_CONFIRMRESETPASSWORD = 'loginConfirmResetPassword';
+
/**
* @var PasswordResetter
*/
@@ -424,25 +429,49 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
*/
public function confirmResetPassword()
{
+ if (!Url::isValidHost()) {
+ throw new Exception("Cannot confirm reset password with untrusted hostname!");
+ }
+
$errorMessage = null;
+ $passwordHash = null;
- $login = Common::getRequestVar('login', '');
- $resetToken = Common::getRequestVar('resetToken', '');
+ $login = Common::getRequestVar('login');
+ $resetToken = Common::getRequestVar('resetToken');
try {
- $this->passwordResetter->confirmNewPassword($login, $resetToken);
+ $passwordHash = $this->passwordResetter->checkValidConfirmPasswordToken($login, $resetToken);
} catch (Exception $ex) {
Log::debug($ex);
$errorMessage = $ex->getMessage();
}
- if (is_null($errorMessage)) { // if success, show login w/ success message
- return $this->resetPasswordSuccess();
- } else {
- // show login page w/ error. this will keep the token in the URL
+ if (!empty($errorMessage)) {
return $this->login($errorMessage);
}
+
+ if (!empty($_POST['nonce'])
+ && !empty($_POST['mtmpasswordconfirm'])
+ && !empty($resetToken)
+ && !empty($login)
+ && !empty($passwordHash)
+ && empty($errorMessage)) {
+ Nonce::checkNonce(self::NONCE_CONFIRMRESETPASSWORD, $_POST['nonce']);
+ if ($this->passwordResetter->doesResetPasswordHashMatchesPassword($_POST['mtmpasswordconfirm'], $passwordHash)) {
+ $this->passwordResetter->setHashedPasswordForLogin($login, $passwordHash);
+ return $this->resetPasswordSuccess();
+ } else {
+ $errorMessage = Piwik::translate('Login_ConfirmPasswordResetWrongPassword');
+ }
+ }
+
+ $nonce = Nonce::getNonce(self::NONCE_CONFIRMRESETPASSWORD);
+
+ return $this->renderTemplateAs('confirmResetPassword', array(
+ 'nonce' => $nonce,
+ 'errorMessage' => $errorMessage
+ ), 'basic');
}
/**
@@ -452,6 +481,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
*/
public function resetPasswordSuccess()
{
+ $_POST = array(); // prevent showing error message username and password is missing
return $this->login($errorMessage = null, $infoMessage = Piwik::translate('Login_PasswordChanged'));
}
diff --git a/plugins/Login/PasswordResetter.php b/plugins/Login/PasswordResetter.php
index e9ce0d75f6..9c706440f9 100644
--- a/plugins/Login/PasswordResetter.php
+++ b/plugins/Login/PasswordResetter.php
@@ -191,20 +191,7 @@ class PasswordResetter
}
}
- /**
- * Confirms a password reset. This should be called after {@link initiatePasswordResetProcess()}
- * is called.
- *
- * This method will get the new password associated with a reset token and set it
- * as the specified user's password.
- *
- * @param string $login The login of the user whose password is being reset.
- * @param string $resetToken The generated string token contained in the reset password
- * email.
- * @throws Exception If there is no user with login '$login', if $resetToken is not a
- * valid token or if the token has expired.
- */
- public function confirmNewPassword($login, $resetToken)
+ public function checkValidConfirmPasswordToken($login, $resetToken)
{
// get password reset info & user info
$user = self::getUserInformation($login);
@@ -224,15 +211,32 @@ class PasswordResetter
// check that the stored password hash is valid (sanity check)
$resetPassword = $resetInfo['hash'];
+
$this->checkPasswordHash($resetPassword);
- // reset password of user
- $usersManager = $this->usersManagerApi;
- Access::doAsSuperUser(function () use ($usersManager, $user, $resetPassword) {
+ return $resetPassword;
+ }
+
+ /**
+ * Confirms a password reset. This should be called after {@link initiatePasswordResetProcess()}
+ * is called.
+ *
+ * This method will get the new password associated with a reset token and set it
+ * as the specified user's password.
+ *
+ * @param string $login The login of the user whose password is being reset.
+ * @param string $passwordHash The generated string token contained in the reset password
+ * email.
+ * @throws Exception If there is no user with login '$login', if $resetToken is not a
+ * valid token or if the token has expired.
+ */
+ public function setHashedPasswordForLogin($login, $passwordHash)
+ {
+ Access::doAsSuperUser(function () use ($login, $passwordHash) {
$userUpdater = new UserUpdater();
$userUpdater->updateUserWithoutCurrentPassword(
- $user['login'],
- $resetPassword,
+ $login,
+ $passwordHash,
$email = false,
$isPasswordHashed = true
);
@@ -293,6 +297,12 @@ class PasswordResetter
return $token;
}
+ public function doesResetPasswordHashMatchesPassword($passwordPlain, $passwordHash)
+ {
+ $passwordPlain = UsersManager::getPasswordHash($passwordPlain);
+ return $this->passwordHelper->verify($passwordPlain, $passwordHash);
+ }
+
/**
* Generates a hash using a hash "identifier" and some data to hash. The hash identifier is
* a string that differentiates the hash in some way.
diff --git a/plugins/Login/lang/en.json b/plugins/Login/lang/en.json
index d13dffbca8..8b72632048 100644
--- a/plugins/Login/lang/en.json
+++ b/plugins/Login/lang/en.json
@@ -20,6 +20,9 @@
"SettingBruteForceMaxFailedLoginsHelp": "If more than this number of failed logins are recorded within the time range configured below, block the IP.",
"SettingBruteForceTimeRange": "Count login retries within this time range in minutes",
"SettingBruteForceTimeRangeHelp": "Enter a number in minutes.",
+ "ConfirmPasswordReset": "Confirm password reset",
+ "ConfirmPasswordResetIntro": "To confirm it is really you who requested this password change, please enter your new password again.",
+ "ConfirmPasswordResetWrongPassword": "The entered password does not match your new password. If you don't remember your newly chosen password you can reset your password again. If you didn't request the password change, simply do nothing and your password won't be changed.",
"LoginNotAllowedBecauseBlocked": "You are currently not allowed to log in because you had too many failed logins, try again later.",
"CurrentlyBlockedIPs": "Currently blocked IPs",
"IPsAlwaysBlocked": "These IPs are always blocked",
diff --git a/plugins/Login/templates/confirmResetPassword.twig b/plugins/Login/templates/confirmResetPassword.twig
new file mode 100644
index 0000000000..4abb1b82dd
--- /dev/null
+++ b/plugins/Login/templates/confirmResetPassword.twig
@@ -0,0 +1,45 @@
+{% extends '@Login/loginLayout.twig' %}
+
+{% set title %}{{ 'Login_ConfirmPasswordToContinue'|translate }}{% endset %}
+
+{% block loginContent %}
+ <div class="contentForm loginForm confirmPasswordForm">
+ {% embed 'contentBlock.twig' with {'title': ('Login_ConfirmPasswordReset'|translate)} %}
+ {% block content %}
+ <p>{{ 'Login_ConfirmPasswordResetIntro'|translate }}</p>
+
+ <div class="message_container">
+ {% if errorMessage is not empty %}
+ <div piwik-notification
+ noclear="true"
+ context="error">
+ <strong>{{ 'General_Error'|translate }}</strong>: {{ errorMessage }}<br/>
+ </div>
+ {% endif %}
+ </div>
+ <br>
+
+ <form action="{{ linkTo({'module': 'Login', 'action': 'confirmResetPassword'}) }}" ng-non-bindable method="post">
+ <div class="row">
+ <div class="col s12 input-field">
+ <input type="hidden" name="nonce" value="{{ nonce }}"/>
+ <input type="password" placeholder="" name="mtmpasswordconfirm" id="mtmpasswordconfirm" class="input" value="" size="20"
+ autocorrect="off" autocapitalize="none"
+ tabindex="20" />
+ <label for="mtmpasswordconfirm"><i class="icon-locked icon"></i> {{ 'Login_NewPassword'|translate }}</label>
+ </div>
+ </div>
+
+ <div class="row actions">
+ <div class="col s12">
+ <input class="submit btn" id='login_reset_confirm' type="submit" value="{{ 'General_Confirm'|translate }}"
+ tabindex="100"/>
+ </div>
+ </div>
+
+ </form>
+ {% endblock %}
+ {% endembed %}
+ </div>
+
+{% endblock %} \ No newline at end of file
diff --git a/plugins/Login/tests/Integration/PasswordResetterTest.php b/plugins/Login/tests/Integration/PasswordResetterTest.php
index fe6f3f0b31..b600d031c4 100644
--- a/plugins/Login/tests/Integration/PasswordResetterTest.php
+++ b/plugins/Login/tests/Integration/PasswordResetterTest.php
@@ -54,19 +54,7 @@ class PasswordResetterTest extends IntegrationTestCase
public function test_passwordReset_processWorksAsExpected()
{
- $user = $this->userModel->getUser('superUserLogin');
- $password = $user['password'];
- $passwordModified = $user['ts_password_modified'];
-
- $this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD);
-
- $this->assertNotEmpty($this->capturedToken);
-
- $user = $this->userModel->getUser('superUserLogin');
- $this->assertEquals($password, $user['password']);
- $this->assertEquals($passwordModified, $user['ts_password_modified']);
-
- $this->passwordResetter->confirmNewPassword('superUserLogin', $this->capturedToken);
+ $this->passwordResetter->setHashedPasswordForLogin('superUserLogin', $this->capturedToken);
$this->checkPasswordIs(self::NEWPASSWORD);
}
@@ -118,6 +106,9 @@ class PasswordResetterTest extends IntegrationTestCase
Option::set($optionName, json_encode($data));
+ $this->assertTrue($this->passwordResetter->doesResetPasswordHashMatchesPassword(self::NEWPASSWORD, $data['hash']));
+ $this->assertFalse($this->passwordResetter->doesResetPasswordHashMatchesPassword('foobar', $data['hash']));
+
$this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD);
$optionName = $this->passwordResetter->getPasswordResetInfoOptionName('superUserLogin');
@@ -134,7 +125,7 @@ class PasswordResetterTest extends IntegrationTestCase
$this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD);
$this->assertNotEmpty($this->capturedToken);
- $this->passwordResetter->confirmNewPassword('superUserLogin', $this->capturedToken);
+ $this->passwordResetter->checkValidConfirmPasswordToken('superUserLogin', $this->capturedToken);
$this->checkPasswordIs(self::NEWPASSWORD);
sleep(1);
@@ -143,7 +134,7 @@ class PasswordResetterTest extends IntegrationTestCase
$this->passwordResetter->initiatePasswordResetProcess('superUserLogin', 'anotherpassword');
$this->assertNotEquals($oldCapturedToken, $this->capturedToken);
- $this->passwordResetter->confirmNewPassword('superUserLogin', $oldCapturedToken);
+ $this->passwordResetter->checkValidConfirmPasswordToken('superUserLogin', $oldCapturedToken);
}
public function test_passwordReset_shouldNeverGenerateTheSameToken()
@@ -172,7 +163,7 @@ class PasswordResetterTest extends IntegrationTestCase
$this->passwordResetter->initiatePasswordResetProcess('superUserLogin', self::NEWPASSWORD);
$this->assertNotEquals($oldCapturedToken, $this->capturedToken);
- $this->passwordResetter->confirmNewPassword('superUserLogin', $oldCapturedToken);
+ $this->passwordResetter->checkValidConfirmPasswordToken('superUserLogin', $oldCapturedToken);
}
/**
diff --git a/plugins/Login/tests/UI/Login_spec.js b/plugins/Login/tests/UI/Login_spec.js
index 28f4020b20..89fc353559 100644
--- a/plugins/Login/tests/UI/Login_spec.js
+++ b/plugins/Login/tests/UI/Login_spec.js
@@ -149,7 +149,7 @@ describe("Login", function () {
expect(await page.screenshot({ fullPage: true })).to.matchImage('password_reset');
});
- it("should reset password when password reset link is clicked", async function() {
+ it("should show reset password confirmation page when password reset link is clicked", async function() {
var expectedMailOutputFile = PIWIK_INCLUDE_PATH + '/tmp/Login.resetPassword.mail.json',
fileContents = require("fs").readFileSync(expectedMailOutputFile),
mailSent = JSON.parse(fileContents),
@@ -163,6 +163,15 @@ describe("Login", function () {
await page.goto(resetUrl);
await page.waitForNetworkIdle();
+ expect(await page.screenshot({ fullPage: true })).to.matchImage('password_reset_confirm');
+ });
+
+ it("should reset password when password reset link is clicked", async function() {
+
+ await page.type("#mtmpasswordconfirm", "superUserPass2");
+ await page.click("#login_reset_confirm");
+ await page.waitForNetworkIdle();
+
expect(await page.screenshot({ fullPage: true })).to.matchImage('password_reset_complete');
});
diff --git a/plugins/Login/tests/UI/expected-screenshots/Login_password_reset_confirm.png b/plugins/Login/tests/UI/expected-screenshots/Login_password_reset_confirm.png
new file mode 100644
index 0000000000..5cbabdf2f9
--- /dev/null
+++ b/plugins/Login/tests/UI/expected-screenshots/Login_password_reset_confirm.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ec46909c1a4f613f345285313d8b3b41fc446d123bddc096a2d45759f2c14c0b
+size 34444
diff --git a/plugins/Morpheus/templates/javascriptCode.twig b/plugins/Morpheus/templates/javascriptCode.twig
index dfa863d0d2..20780230d2 100644
--- a/plugins/Morpheus/templates/javascriptCode.twig
+++ b/plugins/Morpheus/templates/javascriptCode.twig
@@ -1,6 +1,6 @@
<!-- Matomo -->
<script type="text/javascript">
- var _paq = window._paq || [];
+ var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
{$options} _paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
@@ -9,7 +9,7 @@
{$optionsBeforeTrackerUrl}_paq.push(['setTrackerUrl', u+'{$matomoPhpFilename}']);
_paq.push(['setSiteId', '{$idSite}']);
{% if loadAsync %}var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
- g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'{$matomoJsFilename}'; s.parentNode.insertBefore(g,s);{% endif %}
+ g.type='text/javascript'; g.async=true; g.src=u+'{$matomoJsFilename}'; s.parentNode.insertBefore(g,s);{% endif %}
})();
</script>
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getImageTrackingCode.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getImageTrackingCode.xml
index a4f8633329..0c3bab6d2e 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getImageTrackingCode.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getImageTrackingCode.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo Image Tracker--&gt;
-&lt;img referrerpolicy="no-referrer-when-downgrade" src=&quot;http://http://example.org/piwik/matomo.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
+&lt;img referrerpolicy=&quot;no-referrer-when-downgrade&quot; src=&quot;http://http://example.org/piwik/matomo.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
&lt;!-- End Matomo --&gt;</result> \ No newline at end of file
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getJavascriptTag.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getJavascriptTag.xml
index 17afa1ddc3..2ed83de275 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getJavascriptTag.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_after3_7_0__SitesManager.getJavascriptTag.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
- var _paq = window._paq || [];
+ var _paq = window._paq = window._paq || [];
/* tracker methods like &quot;setCustomDimension&quot; should be called before &quot;trackPageView&quot; */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
@@ -10,7 +10,7 @@
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
- g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
+ g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
&lt;/script&gt;
&lt;!-- End Matomo Code --&gt;
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getImageTrackingCode.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getImageTrackingCode.xml
index 86650d8ab1..f961aa82ed 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getImageTrackingCode.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getImageTrackingCode.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo Image Tracker--&gt;
-&lt;img referrerpolicy="no-referrer-when-downgrade" src=&quot;http://http://example.org/piwik/piwik.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
+&lt;img referrerpolicy=&quot;no-referrer-when-downgrade&quot; src=&quot;http://http://example.org/piwik/piwik.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
&lt;!-- End Matomo --&gt;</result> \ No newline at end of file
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getJavascriptTag.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getJavascriptTag.xml
index 2c0a7d1cf9..f18e88255b 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getJavascriptTag.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0__SitesManager.getJavascriptTag.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
- var _paq = window._paq || [];
+ var _paq = window._paq = window._paq || [];
/* tracker methods like &quot;setCustomDimension&quot; should be called before &quot;trackPageView&quot; */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
@@ -10,7 +10,7 @@
_paq.push(['setTrackerUrl', u+'piwik.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
- g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
+ g.type='text/javascript'; g.async=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();
&lt;/script&gt;
&lt;!-- End Matomo Code --&gt;
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getImageTrackingCode.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getImageTrackingCode.xml
index a4f8633329..0c3bab6d2e 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getImageTrackingCode.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getImageTrackingCode.xml
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo Image Tracker--&gt;
-&lt;img referrerpolicy="no-referrer-when-downgrade" src=&quot;http://http://example.org/piwik/matomo.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
+&lt;img referrerpolicy=&quot;no-referrer-when-downgrade&quot; src=&quot;http://http://example.org/piwik/matomo.php?idsite=1&amp;amp;rec=1&quot; style=&quot;border:0&quot; alt=&quot;&quot; /&gt;
&lt;!-- End Matomo --&gt;</result> \ No newline at end of file
diff --git a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getJavascriptTag.xml b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getJavascriptTag.xml
index 17afa1ddc3..2ed83de275 100644
--- a/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getJavascriptTag.xml
+++ b/plugins/SitesManager/tests/System/expected/test_SitesManager_prior3_7_0_but_forced__SitesManager.getJavascriptTag.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>&lt;!-- Matomo --&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
- var _paq = window._paq || [];
+ var _paq = window._paq = window._paq || [];
/* tracker methods like &quot;setCustomDimension&quot; should be called before &quot;trackPageView&quot; */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
@@ -10,7 +10,7 @@
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', '1']);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
- g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
+ g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
&lt;/script&gt;
&lt;!-- End Matomo Code --&gt;
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php
index 5286973b84..1b64000e23 100644
--- a/plugins/UsersManager/API.php
+++ b/plugins/UsersManager/API.php
@@ -179,7 +179,13 @@ class API extends \Piwik\Plugin\API
}
/**
- * Sets a user preference
+ * Sets a user preference. Plugins can add custom preference names by declaring them in their plugin config/config.php
+ * like this:
+ *
+ * ```php
+ * return array('usersmanager.user_preference_names' => DI\add(array('preference_name_1', 'preference_name_2')));
+ * ```
+ *
* @param string $userLogin
* @param string $preferenceName
* @param string $preferenceValue
@@ -188,7 +194,17 @@ class API extends \Piwik\Plugin\API
public function setUserPreference($userLogin, $preferenceName, $preferenceValue)
{
Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
- Option::set($this->getPreferenceId($userLogin, $preferenceName), $preferenceValue);
+
+ if (!$this->model->userExists($userLogin)) {
+ throw new Exception('User does not exist: ' . $userLogin);
+ }
+
+ if ($userLogin === 'anonymous') {
+ Piwik::checkUserHasSuperUserAccess();
+ }
+
+ $nameIfSupported = $this->getPreferenceId($userLogin, $preferenceName);
+ Option::set($nameIfSupported, $preferenceValue);
}
/**
@@ -267,6 +283,18 @@ class API extends \Piwik\Plugin\API
if(false !== strpos($preference, self::OPTION_NAME_PREFERENCE_SEPARATOR)) {
throw new Exception("Preference name cannot contain underscores.");
}
+ $names = array(
+ self::PREFERENCE_DEFAULT_REPORT,
+ self::PREFERENCE_DEFAULT_REPORT_DATE,
+ 'isLDAPUser', // used in loginldap
+ 'hideSegmentDefinitionChangeMessage',// used in JS
+ );
+ $customPreferences = StaticContainer::get('usersmanager.user_preference_names');
+
+ if (!in_array($preference, $names, true)
+ && !in_array($preference, $customPreferences, true)) {
+ throw new Exception('Not supported preference name: ' . $preference);
+ }
return $login . self::OPTION_NAME_PREFERENCE_SEPARATOR . $preference;
}
diff --git a/plugins/UsersManager/config/config.php b/plugins/UsersManager/config/config.php
index d266508bcd..4463e40397 100644
--- a/plugins/UsersManager/config/config.php
+++ b/plugins/UsersManager/config/config.php
@@ -1,2 +1,4 @@
<?php
-return array();
+return array(
+ 'usersmanager.user_preference_names' => []
+);
diff --git a/plugins/UsersManager/tests/Integration/APITest.php b/plugins/UsersManager/tests/Integration/APITest.php
index e69da89f95..dcc235a563 100644
--- a/plugins/UsersManager/tests/Integration/APITest.php
+++ b/plugins/UsersManager/tests/Integration/APITest.php
@@ -12,7 +12,6 @@ use Piwik\Access\Role\View;
use Piwik\Access\Role\Write;
use Piwik\Auth\Password;
use Piwik\Config;
-use Piwik\Container\StaticContainer;
use Piwik\Mail;
use Piwik\Option;
use Piwik\Piwik;
@@ -203,7 +202,7 @@ class APITest extends IntegrationTestCase
public function test_getAllUsersPreferences_isEmpty_whenNoPreferenceAndMultipleRequested()
{
- $preferences = $this->api->getAllUsersPreferences(array('preferenceName', 'otherOne'));
+ $preferences = $this->api->getAllUsersPreferences(array('preferenceName', 'randomDoesNotExist'));
$this->assertEmpty($preferences);
}
@@ -254,24 +253,24 @@ class APITest extends IntegrationTestCase
$user2 = 'userLogin2';
$user3 = 'userLogin3';
$this->api->addUser($user2, 'password', 'userlogin2@password.de');
- $this->api->setUserPreference($user2, 'myPreferenceName', 'valueForUser2');
+ $this->api->setUserPreference($user2, API::PREFERENCE_DEFAULT_REPORT, 'valueForUser2');
$this->api->setUserPreference($user2, 'RandomNOTREQUESTED', 'RandomNOTREQUESTED');
$this->api->addUser($user3, 'password', 'userlogin3@password.de');
- $this->api->setUserPreference($user3, 'myPreferenceName', 'valueForUser3');
- $this->api->setUserPreference($user3, 'otherPreferenceHere', 'otherPreferenceVALUE');
+ $this->api->setUserPreference($user3, API::PREFERENCE_DEFAULT_REPORT, 'valueForUser3');
+ $this->api->setUserPreference($user3, API::PREFERENCE_DEFAULT_REPORT_DATE, 'otherPreferenceVALUE');
$this->api->setUserPreference($user3, 'RandomNOTREQUESTED', 'RandomNOTREQUESTED');
$expected = array(
$user2 => array(
- 'myPreferenceName' => 'valueForUser2'
+ API::PREFERENCE_DEFAULT_REPORT => 'valueForUser2'
),
$user3 => array(
- 'myPreferenceName' => 'valueForUser3',
- 'otherPreferenceHere' => 'otherPreferenceVALUE',
+ API::PREFERENCE_DEFAULT_REPORT => 'valueForUser3',
+ API::PREFERENCE_DEFAULT_REPORT_DATE => 'otherPreferenceVALUE',
),
);
- $result = $this->api->getAllUsersPreferences(array('myPreferenceName', 'otherPreferenceHere', 'randomDoesNotExist'));
+ $result = $this->api->getAllUsersPreferences(array(API::PREFERENCE_DEFAULT_REPORT, API::PREFERENCE_DEFAULT_REPORT_DATE, 'randomDoesNotExist'));
$this->assertSame($expected, $result);
}
@@ -280,15 +279,15 @@ class APITest extends IntegrationTestCase
{
$user2 = 'user_Login2';
$this->api->addUser($user2, 'password', 'userlogin2@password.de');
- $this->api->setUserPreference($user2, 'myPreferenceName', 'valueForUser2');
- $this->api->setUserPreference($user2, 'RandomNOTREQUESTED', 'RandomNOTREQUESTED');
+ $this->api->setUserPreference($user2, API::PREFERENCE_DEFAULT_REPORT, 'valueForUser2');
+ $this->api->setUserPreference($user2, API::PREFERENCE_DEFAULT_REPORT_DATE, 'RandomNOTREQUESTED');
$expected = array(
$user2 => array(
- 'myPreferenceName' => 'valueForUser2'
+ API::PREFERENCE_DEFAULT_REPORT => 'valueForUser2'
),
);
- $result = $this->api->getAllUsersPreferences(array('myPreferenceName', 'otherPreferenceHere', 'randomDoesNotExist'));
+ $result = $this->api->getAllUsersPreferences(array(API::PREFERENCE_DEFAULT_REPORT, 'randomDoesNotExist'));
$this->assertSame($expected, $result);
}
@@ -1016,6 +1015,7 @@ class APITest extends IntegrationTestCase
{
return array(
'Piwik\Access' => new FakeAccess(),
+ 'usersmanager.user_preference_names' => \DI\add(['randomDoesNotExist', 'RandomNOTREQUESTED', 'preferenceName']),
'observers.global' => \DI\add([
['Access.Capability.addCapabilities', function (&$capabilities) {
$capabilities[] = new TestCap1();
diff --git a/plugins/UsersManager/tests/Integration/UserPreferencesTest.php b/plugins/UsersManager/tests/Integration/UserPreferencesTest.php
index d7a442065f..bf17cf688e 100644
--- a/plugins/UsersManager/tests/Integration/UserPreferencesTest.php
+++ b/plugins/UsersManager/tests/Integration/UserPreferencesTest.php
@@ -36,6 +36,38 @@ class UserPreferencesTest extends IntegrationTestCase
$this->userPreferences = new UserPreferences();
$this->setSuperUser();
+
+ $identity = FakeAccess::$identity;
+ FakeAccess::$identity = 'foo'; // avoids error user already exists when it doesn't
+ APIUsersManager::getInstance()->addUser($identity, '22111214k4,mdw<L', 'foo@example.com');
+ FakeAccess::$identity = $identity;
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage User does not exist
+ */
+ public function test_getDefaultReport_WhenLoginNotExists()
+ {
+ APIUsersManager::getInstance()->setUserPreference(
+ 'foo',
+ APIUsersManager::PREFERENCE_DEFAULT_REPORT,
+ '1'
+ );
+ }
+
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Not supported preference name
+ */
+ public function test_getDefaultReport_WhenWrongPreference()
+ {
+ APIUsersManager::getInstance()->setUserPreference(
+ Piwik::getCurrentUserLogin(),
+ 'foo',
+ '1'
+ );
}
public function test_getDefaultReport_ShouldReturnFalseByDefault()