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:
authordizzy <diosmosis@users.noreply.github.com>2021-04-24 06:28:08 +0300
committerGitHub <noreply@github.com>2021-04-24 06:28:08 +0300
commit6227cb05197d4dfd0aa0d695eb665b6c1ef455d6 (patch)
tree3d100156c4d2c0effc981ebb8c041a840ee76ac1 /plugins/CorePluginsAdmin
parentd1422903bb698fac80e1ab0590d62c63dd6574bf (diff)
Require password confirmation for more plugin operations. (#17345)
* Require password confirmation for more plugin operations. * renormalize * add optional password confirmation to CorePluginsAdmin.setSystemSettings * Add developer changelog entry. * ask for password confirmation when saving plugin settings and use onOpenEnd materializecss modal event handler instead of ready since ready no longer exists in used version * Fix redirectTo==referrer for other plugin actions that now have password confirmation. * fix build * try fixing build again
Diffstat (limited to 'plugins/CorePluginsAdmin')
-rw-r--r--plugins/CorePluginsAdmin/API.php31
-rw-r--r--plugins/CorePluginsAdmin/Controller.php54
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js32
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html16
-rw-r--r--plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less8
-rw-r--r--plugins/CorePluginsAdmin/tests/Integration/ApiTest.php92
-rw-r--r--plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js6
7 files changed, 231 insertions, 8 deletions
diff --git a/plugins/CorePluginsAdmin/API.php b/plugins/CorePluginsAdmin/API.php
index 1de34393d4..5b4450b52e 100644
--- a/plugins/CorePluginsAdmin/API.php
+++ b/plugins/CorePluginsAdmin/API.php
@@ -7,9 +7,12 @@
*/
namespace Piwik\Plugins\CorePluginsAdmin;
+use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\SettingsProvider;
use Exception;
+use Piwik\Plugins\Login\PasswordVerifier;
+use Piwik\Version;
/**
* API for plugin CorePluginsAdmin
@@ -28,10 +31,16 @@ class API extends \Piwik\Plugin\API
*/
private $settingsProvider;
- public function __construct(SettingsProvider $settingsProvider, SettingsMetadata $settingsMetadata)
+ /**
+ * @var PasswordVerifier
+ */
+ private $passwordVerifier;
+
+ public function __construct(SettingsProvider $settingsProvider, SettingsMetadata $settingsMetadata, PasswordVerifier $passwordVerifier)
{
$this->settingsProvider = $settingsProvider;
$this->settingsMetadata = $settingsMetadata;
+ $this->passwordVerifier = $passwordVerifier;
}
/**
@@ -39,10 +48,15 @@ class API extends \Piwik\Plugin\API
* @param array $settingValues Format: array('PluginName' => array(array('name' => 'SettingName1', 'value' => 'SettingValue1), ..))
* @throws Exception
*/
- public function setSystemSettings($settingValues)
+ public function setSystemSettings($settingValues, $passwordConfirmation = false)
{
Piwik::checkUserHasSuperUserAccess();
+ $skipPasswordConfirm = $passwordConfirmation === false && version_compare(Version::VERSION, '4.4.0-b1', '<');
+ if (!$skipPasswordConfirm) {
+ $this->confirmCurrentUserPassword($passwordConfirmation);
+ }
+
$pluginsSettings = $this->settingsProvider->getAllSystemSettings();
$this->settingsMetadata->setPluginSettings($pluginsSettings, $settingValues);
@@ -110,4 +124,17 @@ class API extends \Piwik\Plugin\API
return $this->settingsMetadata->formatSettings($userSettings);
}
+ private function confirmCurrentUserPassword($passwordConfirmation)
+ {
+ if (empty($passwordConfirmation)) {
+ throw new Exception(Piwik::translate('UsersManager_ConfirmWithPassword'));
+ }
+
+ $passwordConfirmation = Common::unsanitizeInputValue($passwordConfirmation);
+
+ $loginCurrentUser = Piwik::getCurrentUserLogin();
+ if (!$this->passwordVerifier->isPasswordCorrect($loginCurrentUser, $passwordConfirmation)) {
+ throw new Exception(Piwik::translate('UsersManager_CurrentPasswordNotCorrect'));
+ }
+ }
}
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index df3f1de171..3501de4737 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -425,9 +425,23 @@ class Controller extends Plugin\ControllerAdmin
public function activate($redirectAfter = true)
{
- $pluginName = $this->initPluginModification(static::ACTIVATE_NONCE);
$this->dieIfPluginsAdminIsDisabled();
+ $params = [
+ 'module' => 'CorePluginsAdmin',
+ 'action' => 'activate',
+ 'pluginName' => Common::getRequestVar('pluginName'),
+ 'nonce' => Common::getRequestVar('nonce'),
+ 'redirectTo' => Common::getRequestVar('redirectTo'),
+ 'referrer' => urlencode(Url::getReferrer()),
+ ];
+
+ if (!$this->passwordVerify->requirePasswordVerifiedRecently($params)) {
+ return;
+ }
+
+ $pluginName = $this->initPluginModification(static::ACTIVATE_NONCE);
+
$this->pluginManager->activatePlugin($pluginName);
if ($redirectAfter) {
@@ -469,6 +483,18 @@ class Controller extends Plugin\ControllerAdmin
public function deactivate($redirectAfter = true)
{
+ $params = [
+ 'module' => 'CorePluginsAdmin',
+ 'action' => 'deactivate',
+ 'pluginName' => Common::getRequestVar('pluginName'),
+ 'nonce' => Common::getRequestVar('nonce'),
+ 'redirectTo' => Common::getRequestVar('redirectTo'),
+ 'referrer' => urlencode(Url::getReferrer()),
+ ];
+ if (!$this->passwordVerify->requirePasswordVerifiedRecently($params)) {
+ return;
+ }
+
if($this->isAllowedToTroubleshootAsSuperUser()) {
Access::doAsSuperUser(function() use ($redirectAfter) {
$this->doDeactivatePlugin($redirectAfter);
@@ -480,9 +506,21 @@ class Controller extends Plugin\ControllerAdmin
public function uninstall($redirectAfter = true)
{
- $pluginName = $this->initPluginModification(static::UNINSTALL_NONCE);
$this->dieIfPluginsAdminIsDisabled();
+ $params = [
+ 'module' => 'CorePluginsAdmin',
+ 'action' => 'uninstall',
+ 'pluginName' => Common::getRequestVar('pluginName'),
+ 'nonce' => Common::getRequestVar('nonce'),
+ 'referrer' => urlencode(Url::getReferrer()),
+ ];
+ if (!$this->passwordVerify->requirePasswordVerifiedRecently($params)) {
+ return;
+ }
+
+ $pluginName = $this->initPluginModification(static::UNINSTALL_NONCE);
+
$uninstalled = $this->pluginManager->uninstallPlugin($pluginName);
if (!$uninstalled) {
@@ -552,7 +590,17 @@ class Controller extends Plugin\ControllerAdmin
protected function redirectAfterModification($redirectAfter)
{
- if ($redirectAfter) {
+ if (!$redirectAfter) {
+ return;
+ }
+
+ $referrer = Common::getRequestVar('referrer', false);
+ $referrer = Common::unsanitizeInputValue($referrer);
+ if (!empty($referrer)
+ && Url::isLocalUrl($referrer)
+ ) {
+ Url::redirectToUrl($referrer);
+ } else {
Url::redirectToReferrer();
}
}
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
index 01bb921004..5806abe3a3 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js
@@ -7,15 +7,17 @@
(function () {
angular.module('piwikApp').controller('PluginSettingsController', PluginSettingsController);
- PluginSettingsController.$inject = ['$scope', 'piwikApi'];
+ PluginSettingsController.$inject = ['$scope', 'piwikApi', '$element'];
- function PluginSettingsController($scope, piwikApi) {
+ function PluginSettingsController($scope, piwikApi, $element) {
// remember to keep controller very simple. Create a service/factory (model) if needed
var self = this;
this.isLoading = true;
this.isSaving = {};
+ this.passwordConfirmation = '';
+ this.settingsToSave = null;
var apiMethod = 'CorePluginsAdmin.getUserSettings';
@@ -34,6 +36,27 @@
var apiMethod = 'CorePluginsAdmin.setUserSettings';
if ($scope.mode === 'admin') {
apiMethod = 'CorePluginsAdmin.setSystemSettings';
+
+ if (!this.passwordConfirmation) {
+ this.settingsToSave = settings;
+
+ function onEnter(event){
+ var keycode = (event.keyCode ? event.keyCode : event.which);
+ if (keycode == '13'){
+ $element.find('.confirm-password-modal').modal('close');
+ self.save();
+ }
+ }
+
+ $element.find('.confirm-password-modal').modal({ dismissible: false, onOpenEnd: function () {
+ $('.modal.open #currentUserPassword').focus();
+ $('.modal.open #currentUserPassword').off('keypress').keypress(onEnter);
+ }}).modal('open');
+
+ return;
+ } else {
+ settings = this.settingsToSave;
+ }
}
this.isSaving[settings.pluginName] = true;
@@ -56,7 +79,7 @@
});
});
- piwikApi.post({method: apiMethod}, {settingValues: values}).then(function (success) {
+ piwikApi.post({method: apiMethod}, {settingValues: values, passwordConfirmation: this.passwordConfirmation}).then(function (success) {
self.isSaving[settings.pluginName] = false;
var UI = require('piwik/UI');
@@ -69,6 +92,9 @@
}, function () {
self.isSaving[settings.pluginName] = false;
});
+
+ this.passwordConfirmation = '';
+ this.settingsToSave = null;
};
}
})();
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
index 6f77711ecc..4e082e94bf 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.html
@@ -22,4 +22,20 @@
</div>
+ <div class="confirm-password-modal modal">
+ <div class="modal-content">
+ <h2>{{:: 'UsersManager_ConfirmWithPassword'|translate }}</h2>
+
+ <div piwik-field uicontrol="password" name="currentUserPassword" autocomplete="off"
+ ng-model="pluginSettings.passwordConfirmation"
+ full-width="true"
+ title="{{:: 'UsersManager_YourCurrentPassword'|translate }}"
+ value="">
+ </div>
+ </div>
+ <div class="modal-footer">
+ <a href="" class="modal-action modal-close btn" ng-disabled="!pluginSettings.passwordConfirmation" ng-click="pluginSettings.save()">{{:: 'General_Yes'|translate }}</a>
+ <a href="" class="modal-action modal-close modal-no">{{:: 'General_No'|translate }}</a>
+ </div>
+ </div>
</div>
diff --git a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
index 3b86a59aba..affc515e84 100644
--- a/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
+++ b/plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less
@@ -2,6 +2,14 @@
textarea {
display: block;
}
+
+ .confirm-password-modal {
+ .modal-no {
+ margin-left: 1em;
+ margin-right: 1em;
+ margin-top: 1em;
+ }
+ }
}
.pluginsSettingsSubmit {
margin-top: 30px;
diff --git a/plugins/CorePluginsAdmin/tests/Integration/ApiTest.php b/plugins/CorePluginsAdmin/tests/Integration/ApiTest.php
new file mode 100644
index 0000000000..b497f999d4
--- /dev/null
+++ b/plugins/CorePluginsAdmin/tests/Integration/ApiTest.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CorePluginsAdmin\tests\Integration;
+
+use Piwik\Access;
+use Piwik\Auth;
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugins\CoreUpdater\SystemSettings;
+use Piwik\Plugins\UsersManager\API;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Version;
+
+class ApiTest extends IntegrationTestCase
+{
+ const TEST_USER = 'atestuser';
+ const TEST_PASSWORD = 'testpassword';
+
+ private $testSystemSettingsPayload = [
+ 'CoreUpdater' => [
+ ['name' => 'release_channel', 'value' => 'latest_beta'],
+ ],
+ ];
+
+ protected static function beforeTableDataCached()
+ {
+ parent::beforeTableDataCached();
+
+ API::getInstance()->addUser(self::TEST_USER, self::TEST_PASSWORD, 'someuser@email.com');
+ API::getInstance()->setSuperUserAccess(self::TEST_USER, true, Fixture::ADMIN_USER_PASSWORD);
+ }
+
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ Access::getInstance()->setSuperUserAccess(false);
+ $auth = StaticContainer::get(Auth::class);
+ $auth->setLogin(self::TEST_USER);
+ $auth->setPassword(self::TEST_PASSWORD);
+ Access::getInstance()->reloadAccess($auth);
+ }
+
+ public function test_setSystemSettings_throwsIfNoPasswordConfirmation()
+ {
+ if (version_compare(Version::VERSION, '4.4.0-b1', '<')) {
+ $this->markTestSkipped('Skipping test since passwordConfirmation is optional until version 4.4.');
+ }
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('UsersManager_ConfirmWithPassword');
+
+ $settingValues = $this->testSystemSettingsPayload;
+ \Piwik\Plugins\CorePluginsAdmin\API::getInstance()->setSystemSettings($settingValues);
+ }
+
+ public function test_setSystemSettings_throwsIfPasswordConfirmationWrong()
+ {
+ if (version_compare(Version::VERSION, '4.4.0-b1', '<')) {
+ $this->markTestSkipped('Skipping test since passwordConfirmation is optional until version 4.4.');
+ }
+
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessage('UsersManager_CurrentPasswordNotCorrect');
+
+ $settingValues = $this->testSystemSettingsPayload;
+ \Piwik\Plugins\CorePluginsAdmin\API::getInstance()->setSystemSettings($settingValues, 'blahblah');
+ }
+
+ public function test_setSystemSettings_correctlySetsSettings()
+ {
+ $settingValues = $this->testSystemSettingsPayload;
+ \Piwik\Plugins\CorePluginsAdmin\API::getInstance()->setSystemSettings($settingValues, self::TEST_PASSWORD);
+
+ $coreUpdaterSettings = StaticContainer::get(SystemSettings::class);
+ $value = $coreUpdaterSettings->releaseChannel->getValue();
+ $this->assertEquals('latest_beta', $value);
+ }
+
+ protected static function configureFixture($fixture)
+ {
+ parent::configureFixture($fixture);
+ $fixture->createSuperUser = true;
+ }
+} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js b/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js
index 8020bb159b..a9ac485f90 100644
--- a/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js
+++ b/plugins/CorePluginsAdmin/tests/UI/TagManagerTeaser_spec.js
@@ -58,7 +58,13 @@ describe("TagManagerTeaser", function () {
it('should be possible to activate plugin and redirect to tag manager', async function () {
await page.click('.activateTagManager .activateTagManagerPlugin');
await page.waitForNetworkIdle();
+
+ await page.type('#login_form_password', 'superUserPass');
+ await page.click('#login_form_submit');
+
+ await page.waitForNetworkIdle();
await page.waitFor(250);
+
expect(await page.screenshotSelector('.pageWrap')).to.matchImage('super_user_activate_plugin');
});