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:
authordiosmosis <diosmosis@users.noreply.github.com>2019-05-16 03:12:05 +0300
committerGitHub <noreply@github.com>2019-05-16 03:12:05 +0300
commit05017ba88ec611f63bf223728990351212ff560f (patch)
tree79c20127a6584a1316bb864b329d0cba713add10 /plugins
parentcecec674a65e4dc2a1aa7c33722a5380be2fd719 (diff)
Require password confirmation before setting/removing superuser access. (#13975)
* Require password confirmation for changing superuser access and fix issue where getSiteAccess is called w/ superuser when toggling superuser access. * apply review feedback * Allow bypassing password confirmation in certain scenarios. * Fixing tests & adding UI test. * Update submodule. * test fixes + remove return; from 2fa tests. * update submodule * Fixing tests * Couple tweaks for screenshot testing. * test fixes * Fix TwoFactorAuthUsersManager test. * More test fixes. * try to disable all transitions * More UI test fixes + disable materialize animations globally in UI tests. * 2fa ui tests now working
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Actions/tests/UI/ActionsDataTable_spec.js1
-rw-r--r--plugins/Dashboard/tests/UI/Dashboard_spec.js4
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_create_new.png4
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_change_layout.png4
-rw-r--r--plugins/Installation/Controller.php8
-rw-r--r--plugins/Login/tests/Integration/LoginTest.php4
m---------plugins/LoginLdap0
-rw-r--r--plugins/TestRunner/Commands/TestsSetupFixture.php3
-rw-r--r--plugins/TwoFactorAuth/tests/Fixtures/TwoFactorFixture.php9
-rw-r--r--plugins/TwoFactorAuth/tests/Integration/TwoFactorAuthTest.php4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/TwoFactorAuthUsersManager_spec.js10
-rw-r--r--plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js32
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_edit_with_2fa_reset_confirmed.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_list.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_not_verified_wrong_code.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_verified.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step4.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step1.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step2.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step3.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled.png4
-rw-r--r--plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled_required.png4
-rw-r--r--plugins/UsersManager/API.php42
-rw-r--r--plugins/UsersManager/UserUpdater.php12
-rw-r--r--plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html24
-rw-r--r--plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.js14
-rw-r--r--plugins/UsersManager/lang/en.json4
-rw-r--r--plugins/UsersManager/tests/Fixtures/ManyUsers.php4
-rw-r--r--plugins/UsersManager/tests/Integration/APITest.php19
-rw-r--r--plugins/UsersManager/tests/Integration/UsersManagerTest.php56
-rw-r--r--plugins/UsersManager/tests/UI/UsersManager_spec.js44
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_manage_users_back.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_bulk_access_set_all.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_capability_single_site.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_filters.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_select_all.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_single_site_access.png4
-rw-r--r--plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_superuser_confirm.png4
44 files changed, 266 insertions, 128 deletions
diff --git a/plugins/Actions/tests/UI/ActionsDataTable_spec.js b/plugins/Actions/tests/UI/ActionsDataTable_spec.js
index 3778bd3cf1..b09a55eb7c 100644
--- a/plugins/Actions/tests/UI/ActionsDataTable_spec.js
+++ b/plugins/Actions/tests/UI/ActionsDataTable_spec.js
@@ -32,6 +32,7 @@ describe("ActionsDataTable", function () {
await page.mouse.move(-10, -10);
await page.waitForNetworkIdle();
+ await page.waitFor(250); // rendering
expect(await page.screenshot({ fullPage: true })).to.matchImage('subtables_loaded');
});
diff --git a/plugins/Dashboard/tests/UI/Dashboard_spec.js b/plugins/Dashboard/tests/UI/Dashboard_spec.js
index af060caac9..f539219829 100644
--- a/plugins/Dashboard/tests/UI/Dashboard_spec.js
+++ b/plugins/Dashboard/tests/UI/Dashboard_spec.js
@@ -168,8 +168,8 @@ describe("Dashboard", function () {
var button = await page.jQuery('.modal.open .modal-footer a:contains(Yes)');
await button.click();
- await page.waitFor(250);
await page.mouse.move(-10, -10);
+ await page.waitFor(250);
expect(await page.screenshot({ fullPage: true })).to.matchImage('widget_move_removed');
});
@@ -181,6 +181,7 @@ describe("Dashboard", function () {
var button = await page.jQuery('.modal.open .modal-footer a:contains(Save)');
await button.click();
await page.mouse.move(-10, -10);
+ await page.waitFor(500); // animation
expect(await page.screenshot({ fullPage: true })).to.matchImage('change_layout');
});
@@ -263,6 +264,7 @@ describe("Dashboard", function () {
await page.type('#createDashboardName', 'newdash2');
var button = await page.jQuery('.modal.open .modal-footer a:contains(Ok)');
await button.click();
+ await page.mouse.move(-10, -10);
await page.waitForNetworkIdle();
expect(await page.screenshot({ fullPage: true })).to.matchImage('create_new');
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_create_new.png b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_create_new.png
index d3b0e150b5..c0429584d7 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_create_new.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/DashboardManager_create_new.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e17ceb26b2b536e9a0f90eb78aed0681a38bd1748d569d3f0db003fcbf76cba9
-size 249197
+oid sha256:03a4cb2baeafcf12b51121737f80187f3a26dafb5bc4cc3df946c4c8f975d80f
+size 247542
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_change_layout.png b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_change_layout.png
index 495e8a6c80..dceb3257f8 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_change_layout.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_change_layout.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:595d831aa440d54ddd8a6a8cf4b8f731646dbc35846f22cde7f13cfa513c8d6f
-size 29673
+oid sha256:26908474390de37b62d846f9a560c78286a8b0b431402a5a1c94e3006905adbd
+size 28570
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index 8dd65f6899..ee9160af07 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -26,6 +26,7 @@ use Piwik\Plugins\Diagnostics\DiagnosticService;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\Plugins\UsersManager\API as APIUsersManager;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\ProxyHeaders;
use Piwik\SettingsPiwik;
use Piwik\Tracker\TrackerCodeGenerator;
@@ -680,11 +681,12 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
private function createSuperUser($login, $password, $email)
{
- $self = $this;
- Access::doAsSuperUser(function () use ($self, $login, $password, $email) {
+ Access::doAsSuperUser(function () use ($login, $password, $email) {
$api = APIUsersManager::getInstance();
$api->addUser($login, $password, $email);
- $api->setSuperUserAccess($login, true);
+
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($login, true);
});
}
diff --git a/plugins/Login/tests/Integration/LoginTest.php b/plugins/Login/tests/Integration/LoginTest.php
index 9f43512f0b..9ef38b17a9 100644
--- a/plugins/Login/tests/Integration/LoginTest.php
+++ b/plugins/Login/tests/Integration/LoginTest.php
@@ -15,6 +15,7 @@ use Piwik\DbHelper;
use Piwik\NoAccessException;
use Piwik\Plugins\Login\Auth;
use Piwik\Plugins\UsersManager\API;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
@@ -346,7 +347,8 @@ class LoginTest extends IntegrationTestCase
private function _setUpSuperUserAccessViaDb()
{
- API::getInstance()->setSuperUserAccess('user', true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword('user', true);
}
private function authenticate($login, $tokenAuth)
diff --git a/plugins/LoginLdap b/plugins/LoginLdap
-Subproject c2de63df1887ec0409dceb5fa64f7fb735bce8e
+Subproject 84b2d67b762ffbb66c26392c52acc7463534e0c
diff --git a/plugins/TestRunner/Commands/TestsSetupFixture.php b/plugins/TestRunner/Commands/TestsSetupFixture.php
index e97319b633..294a08c41c 100644
--- a/plugins/TestRunner/Commands/TestsSetupFixture.php
+++ b/plugins/TestRunner/Commands/TestsSetupFixture.php
@@ -10,6 +10,7 @@ namespace Piwik\Plugins\TestRunner\Commands;
use Piwik\Application\Environment;
use Piwik\Config;
+use Piwik\Db;
use Piwik\Plugin\ConsoleCommand;
use Piwik\Tests\Framework\TestingEnvironmentManipulator;
use Piwik\Tests\Framework\TestingEnvironmentVariables;
@@ -113,7 +114,7 @@ class TestsSetupFixture extends ConsoleCommand
$this->requireFixtureFiles($input);
$this->setIncludePathAsInTestBootstrap();
- $host = Url::getHost();
+ $host = Config::getHostname();
if (empty($host)) {
$host = 'localhost';
Url::setHost('localhost');
diff --git a/plugins/TwoFactorAuth/tests/Fixtures/TwoFactorFixture.php b/plugins/TwoFactorAuth/tests/Fixtures/TwoFactorFixture.php
index 37e2777214..3b71c0eba7 100644
--- a/plugins/TwoFactorAuth/tests/Fixtures/TwoFactorFixture.php
+++ b/plugins/TwoFactorAuth/tests/Fixtures/TwoFactorFixture.php
@@ -12,6 +12,7 @@ use Piwik\Date;
use Piwik\Plugins\TwoFactorAuth\Dao\RecoveryCodeDao;
use Piwik\Plugins\TwoFactorAuth\TwoFactorAuthentication;
use Piwik\Plugins\UsersManager\Model;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\Fixture;
use Piwik\Plugins\UsersManager\API as UsersAPI;
@@ -26,6 +27,7 @@ class TwoFactorFixture extends Fixture
private $userWithout2Fa = 'without2FA';
private $userNo2Fa = 'no2FA';
private $userPassword = '123abcDk3_l3';
+ private $superUserWith2Fa = 'superWith2FA';
const USER_2FA_SECRET = '1111111111111111';
@@ -68,6 +70,11 @@ class TwoFactorFixture extends Fixture
public function setUpUsers()
{
+ \Piwik\Plugins\UsersManager\API::getInstance()->addUser($this->superUserWith2Fa, $this->userPassword,
+ $this->superUserWith2Fa . '@matomo.org');
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($this->superUserWith2Fa, true);
+
foreach ([$this->userWith2Fa, $this->userWithout2Fa, $this->userWith2FaDisable, $this->userNo2Fa] as $user) {
\Piwik\Plugins\UsersManager\API::getInstance()->addUser($user, $this->userPassword, $user . '@matomo.org');
// we cannot set superuser as logme won't work for super user
@@ -79,7 +86,7 @@ class TwoFactorFixture extends Fixture
}
}
- foreach ([$this->userWith2Fa, $this->userWith2FaDisable] as $user) {
+ foreach ([$this->userWith2Fa, $this->userWith2FaDisable, $this->superUserWith2Fa] as $user) {
$this->dao->insertRecoveryCode($user, '123456');
$this->dao->insertRecoveryCode($user, '234567');
$this->dao->insertRecoveryCode($user, '345678');
diff --git a/plugins/TwoFactorAuth/tests/Integration/TwoFactorAuthTest.php b/plugins/TwoFactorAuth/tests/Integration/TwoFactorAuthTest.php
index 2df336f491..07429e2916 100644
--- a/plugins/TwoFactorAuth/tests/Integration/TwoFactorAuthTest.php
+++ b/plugins/TwoFactorAuth/tests/Integration/TwoFactorAuthTest.php
@@ -15,6 +15,7 @@ use Piwik\Plugins\TwoFactorAuth\Dao\TwoFaSecretRandomGenerator;
use Piwik\Plugins\TwoFactorAuth\SystemSettings;
use Piwik\Plugins\TwoFactorAuth\TwoFactorAuthentication;
use Piwik\Plugins\UsersManager\API;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
/**
@@ -49,7 +50,8 @@ class TwoFactorAuthTest extends IntegrationTestCase
foreach ([$this->userWith2Fa, $this->userWithout2Fa] as $user) {
API::getInstance()->addUser($user, $this->userPassword, $user . '@matomo.org');
- API::getInstance()->setSuperUserAccess($user, 1);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($user, 1);
}
$this->dao = StaticContainer::get(RecoveryCodeDao::class);
diff --git a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuthUsersManager_spec.js b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuthUsersManager_spec.js
index 4e07f7cc97..4d5a405dbd 100644
--- a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuthUsersManager_spec.js
+++ b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuthUsersManager_spec.js
@@ -8,8 +8,6 @@
*/
describe("TwoFactorAuthUsersManager", function () {
- this.timeout(0);
-
this.fixture = "Piwik\\Plugins\\TwoFactorAuth\\tests\\Fixtures\\TwoFactorUsersManagerFixture";
var generalParams = 'idSite=1&period=day&date=2010-01-03',
@@ -37,19 +35,21 @@ describe("TwoFactorAuthUsersManager", function () {
await page.evaluate(function () {
$('.userEditForm .menuUserTwoFa a').click();
});
+ await page.waitFor(250);
+ await page.waitFor('.twofa-reset > p', { visible: true });
expect(await page.screenshotSelector('#content,#notificationContainer')).to.matchImage('edit_with_2fa');
});
it('should ask for confirmation before resetting 2fa', async function () {
await page.click('.userEditForm .twofa-reset .resetTwoFa .btn');
- await page.waitFor(500);
- const modal = await page.$('.modal.open');
+ const modal = await page.waitFor('.modal.open', { visible: true });
+ await page.waitFor(1000); // animation
expect(await modal.screenshot()).to.matchImage('edit_with_2fa_reset_confirm');
});
it('should be possible to confirm the reset', async function () {
await page.click('.twofa-confirm-modal .modal-close:not(.modal-no)');
- await page.waitFor(250); // wait for modal to close
+ await page.waitFor(500); // wait for modal to close
expect(await page.screenshotSelector('#content,#notificationContainer')).to.matchImage('edit_with_2fa_reset_confirmed');
});
diff --git a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js
index 5e216d793d..8b702b230f 100644
--- a/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js
+++ b/plugins/TwoFactorAuth/tests/UI/TwoFactorAuth_spec.js
@@ -19,7 +19,7 @@ describe("TwoFactorAuth", function () {
async function selectModalButton(button)
{
- await page.click('.modal.open .modal-footer a:contains('+button+')');
+ await (await page.jQuery('.modal.open .modal-footer a:contains('+button+')')).click();
}
async function loginUser(username, doAuth)
@@ -70,11 +70,13 @@ describe("TwoFactorAuth", function () {
async function confirmPassword()
{
+ await page.waitFor('.confirmPasswordForm');
await page.evaluate(function(){
$('.confirmPasswordForm #login_form_password').val('123abcDk3_l3');
$('.confirmPasswordForm #login_form_submit').click();
});
- await page.waitFor(750);
+ await page.waitForNetworkIdle();
+ await page.waitFor(100);
}
it('a user with 2fa can open the widgetized view by token without needing to verify', async function () {
@@ -86,7 +88,7 @@ describe("TwoFactorAuth", function () {
await loginUser('with2FA', false);
expect(await page.screenshotSelector('.loginSection')).to.matchImage('logme_not_verified');
});
-return;
+
it('when logging in and providing wrong code an error is shown', async function () {
await page.type('.loginTwoFaForm #login_form_authcode', '555555');
await page.evaluate(function(){
@@ -101,14 +103,18 @@ return;
await page.evaluate(function(){
$('.loginTwoFaForm #login_form_submit').click();
});
- await page.waitFor(1500);
- expect(await page.screenshotSelector('#content')).to.matchImage('logme_verified');
+ await page.waitForNetworkIdle();
+ await page.waitFor('.widget');
+ expect(await page.screenshotSelector('.pageWrap')).to.matchImage('logme_verified');
});
it('should show user settings when two-fa enabled', async function () {
await loginUser('with2FA');
await page.goto(userSettings);
- expect(await page.screenshotSelector('.userSettings2FA')).to.matchImage('usersettings_twofa_enabled');
+ await page.waitFor('.userSettings2FA', { visible: true });
+ await page.waitFor(500); // animation
+ const elem = await page.$('.userSettings2FA');
+ expect(await elem.screenshot()).to.matchImage('usersettings_twofa_enabled');
});
it('should be possible to show recovery codes step1 authentication', async function () {
@@ -145,7 +151,9 @@ return;
it('should be possible to disable two factor step 3 verified', async function () {
await confirmPassword();
- expect(await page.screenshotSelector('.userSettings2FA')).to.matchImage('usersettings_twofa_disable_step3');
+ await page.waitFor('.userSettings2FA');
+ const elem = await page.$('.userSettings2FA');
+ expect(await elem.screenshot()).to.matchImage('usersettings_twofa_disable_step3');
});
it('should show setup screen - step 1', async function () {
@@ -157,21 +165,15 @@ return;
});
it('should move to second step in setup - step 2', async function () {
- console.log('start');
await page.evaluate(function(){
$('.setupTwoFactorAuthentication .backupRecoveryCode:first').click();
});
- console.log(0);
await page.waitForNetworkIdle();
- console.log(1);
await page.click('.setupTwoFactorAuthentication .goToStep2');
- console.log(2);
await page.waitForNetworkIdle();
- console.log(3);
await page.evaluate(function () {
$('#qrcode').hide();
});
- console.log(4);
expect(await page.screenshotSelector('#content')).to.matchImage('twofa_setup_step2');
});
@@ -190,6 +192,9 @@ return;
await page.evaluate(function () {
$('.setupConfirmAuthCodeForm .confirmAuthCode').click();
});
+ await page.waitForNetworkIdle();
+ await page.waitFor('.widget', { visible: true });
+ await page.waitForNetworkIdle();
expect(await page.screenshotSelector('#content')).to.matchImage('twofa_setup_step4');
});
@@ -220,6 +225,7 @@ return;
await page.evaluate(function () {
$('.setupConfirmAuthCodeForm .confirmAuthCode').click();
});
+ await page.waitForNetworkIdle();
expect(await page.screenshotSelector('.loginSection,#content,#notificationContainer')).to.matchImage('twofa_forced_step4');
});
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_edit_with_2fa_reset_confirmed.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_edit_with_2fa_reset_confirmed.png
index 631b48a8e7..820faa151d 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_edit_with_2fa_reset_confirmed.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_edit_with_2fa_reset_confirmed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e51ce8c2d7cf9e6e81b84e89e83a0f6a453196102265b52da8be1eac7aa13b83
-size 28365
+oid sha256:99b7384aaf0b2a6c63fdda6611b323883bc735ecebd29e6f181f07d16a724fce
+size 30075
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_list.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_list.png
index dd1ee87fdd..1a7af9b184 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_list.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuthUsersManager_list.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7b94002780dbc44e7382858defa946a81fe67239ea2deccfeeb75217de7aa71f
-size 53075
+oid sha256:baac8634397639a2ad11797e1390d5ed467ec1fe11a1c14dbe0033f78e67dbb0
+size 58717
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_not_verified_wrong_code.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_not_verified_wrong_code.png
index 6409ebc3c6..613960079c 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_not_verified_wrong_code.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_not_verified_wrong_code.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:69c1dbbf415620f8d133bfd5894d5e698a732f5f82aa3aa90d3a142f14b03c0d
-size 49008
+oid sha256:7b830596d955d1db652a5877e1591e616dd611d9422dad8475271ae175964833
+size 49340
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_verified.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_verified.png
index db5c9c5d34..26f8a16927 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_verified.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_logme_verified.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:432d04719b40530913c1c0230d3a36c98a7d04f76977ff5f4795055f3b0d395d
-size 139500
+oid sha256:276d31c54153cd889f9ff5a7095c0ba0311a75f4d15a3579653e3379e4730943
+size 137776
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step1.png
index 5a439faa51..d511b3665a 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step1.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b6d5c8f0591b7c3455ec903881f1f0466b6b63029f2a950d83a7a2f51d304a7a
-size 13390
+oid sha256:bf4e7b2dd1d68df9db0826eb9051f796bf3c25426659a9e84792ae4879835f17
+size 13422
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png
index 1fe16418ca..7a0f177274 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_show_recovery_codes_step2.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:80a3c4babfdfbf6040181cdf4d7059502d3af8df0c0572a16f17cda3e852dc22
-size 63846
+oid sha256:9c10cc5cb40f56bd3a71513c262d91d99a474ff8f975ea67ad24ee1bb8ead0a9
+size 60823
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png
index 40e3aad93f..07e63fa139 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:750bbc270525ea990e3a541b7d4ce5e819aefe1a05e937dd538af5d2cec34178
-size 103348
+oid sha256:db93961069bfc3e3fb3f2fb7204921eb835545c2ce7535b068f5502474822591
+size 93376
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step4.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step4.png
index db5c9c5d34..07e63fa139 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step4.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_forced_step4.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:432d04719b40530913c1c0230d3a36c98a7d04f76977ff5f4795055f3b0d395d
-size 139500
+oid sha256:db93961069bfc3e3fb3f2fb7204921eb835545c2ce7535b068f5502474822591
+size 93376
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png
index 2b525ac946..bab5330078 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d0bc9e697dff2fd9f91451a033ba778c3742e397c2d6f934a8a6b780fa5f02aa
-size 74454
+oid sha256:66a49cb6fc2e73a20ac31e54911199f3b3c8646bea27c43c66b2597f73c249ed
+size 67812
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png
index 474486f7df..239a6747b4 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step2.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f5a3d9c0bf5c0cacfbcf1b7062a9741a5f2b3cbe093448dd3ffac81202c84f3b
-size 93330
+oid sha256:7816b91a497e36536fcae8c6a361ea5b1e1ddce8c03361746de5e5e06b947a40
+size 86881
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png
index 6960b50d5b..e53a9cbd52 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_twofa_setup_step3.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ea30e34f67d85fad191e90f6d10f63275bd243a7357b020e9875babd1a2e2f47
-size 121508
+oid sha256:428605cbed55a690c110dd4db5c863b352d55a9add141e565398f1e7d728239e
+size 115196
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step1.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step1.png
index 95fcf8073c..2fb49cb840 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step1.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:035f9173979d5650c2ae6dbca4a19c80601b62c4f984c2be912b03cdf57e304a
-size 21734
+oid sha256:bdd3ba9255f3e954c863fcb73e3437e8e1fcc80aa54279f67a07375d4d2021a7
+size 14250
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step2.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step2.png
index a98ade64c8..d511b3665a 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step2.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step2.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9dddb94c30224362115ae13ebd5170c96caa80be88b18583f7920e0fd213117a
-size 15127
+oid sha256:bf4e7b2dd1d68df9db0826eb9051f796bf3c25426659a9e84792ae4879835f17
+size 13422
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step3.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step3.png
index 0418149865..b3b8979820 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step3.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_disable_step3.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8af9fbcce1498486fd7f42bf18bed791db6dc49e9fb5e9f94f21ea31ab105383
-size 45375
+oid sha256:d81de0a72d903346c4525603144ba753c40a76c3beb90031978226dc6f9fdd06
+size 43691
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled.png
index 8d7197e95b..98a68ce59f 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d11dc17a3b0d891e8dbee1734ec31ff22230d71aa0bedab23f4389e0bb8e8e1e
-size 46583
+oid sha256:34c0c73879e1a8ba3a300ba70f7dcf3b07f4713e6a411e8ca3217f38d24eb3eb
+size 46631
diff --git a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled_required.png b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled_required.png
index 2639572760..b7f9641396 100644
--- a/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled_required.png
+++ b/plugins/TwoFactorAuth/tests/UI/expected-screenshots/TwoFactorAuth_usersettings_twofa_enabled_required.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9d6bba78762b389d8902269840b1bb590918c7bbfe17ec62f452e19411a268fc
-size 52910
+oid sha256:1c6bc6770a97cd4b571f1fae0a2d21a2c43d01c302eb8f4e3d5fc170b40a9b50
+size 51621
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php
index fb3fa2e3c6..2696c9ffe2 100644
--- a/plugins/UsersManager/API.php
+++ b/plugins/UsersManager/API.php
@@ -48,6 +48,7 @@ class API extends \Piwik\Plugin\API
const OPTION_NAME_PREFERENCE_SEPARATOR = '_';
public static $UPDATE_USER_REQUIRE_PASSWORD_CONFIRMATION = true;
+ public static $SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true;
/**
* @var Model
@@ -701,12 +702,23 @@ class API extends \Piwik\Plugin\API
* @param string $userLogin the user login.
* @param bool|int $hasSuperUserAccess true or '1' to grant Super User access, false or '0' to remove Super User
* access.
+ * @param string $passwordConfirmation the current user's password.
* @throws \Exception
*/
- public function setSuperUserAccess($userLogin, $hasSuperUserAccess)
+ public function setSuperUserAccess($userLogin, $hasSuperUserAccess, $passwordConfirmation = null)
{
Piwik::checkUserHasSuperUserAccess();
$this->checkUserIsNotAnonymous($userLogin);
+
+ $requirePasswordConfirmation = self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION;
+ self::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true;
+
+ $isCliMode = Common::isPhpCliMode() && !(defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE);
+ if (!$isCliMode
+ && $requirePasswordConfirmation
+ ) {
+ $this->confirmCurrentUserPassword($passwordConfirmation);
+ }
$this->checkUserExists($userLogin);
if (!$hasSuperUserAccess && $this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
@@ -853,9 +865,10 @@ class API extends \Piwik\Plugin\API
$_isPasswordHashed = false, $passwordConfirmation = false)
{
$requirePasswordConfirmation = self::$UPDATE_USER_REQUIRE_PASSWORD_CONFIRMATION;
- $isEmailNotificationOnInConfig = Config::getInstance()->General['enable_update_users_email'];
self::$UPDATE_USER_REQUIRE_PASSWORD_CONFIRMATION = true;
+ $isEmailNotificationOnInConfig = Config::getInstance()->General['enable_update_users_email'];
+
Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
$this->checkUserIsNotAnonymous($userLogin);
$this->checkUserExists($userLogin);
@@ -901,16 +914,7 @@ class API extends \Piwik\Plugin\API
}
if ($changeShouldRequirePasswordConfirmation && $requirePasswordConfirmation) {
- 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'));
- }
+ $this->confirmCurrentUserPassword($passwordConfirmation);
}
$alias = $this->getCleanAlias($alias, $userLogin);
@@ -1392,6 +1396,20 @@ class API extends \Piwik\Plugin\API
return [$roles, $capabilities];
}
+ 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'));
+ }
+ }
+
private function sendEmailChangedEmail($user, $newEmail)
{
// send the mail to both the old email and the new email
diff --git a/plugins/UsersManager/UserUpdater.php b/plugins/UsersManager/UserUpdater.php
index 87d748a6dd..4d49b5a50f 100644
--- a/plugins/UsersManager/UserUpdater.php
+++ b/plugins/UsersManager/UserUpdater.php
@@ -41,4 +41,16 @@ class UserUpdater
}
}
+ public function setSuperUserAccessWithoutCurrentPassword($userLogin, $hasSuperUserAccess)
+ {
+ API::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = false;
+ try {
+ Request::processRequest('UsersManager.setSuperUserAccess', [
+ 'userLogin' => $userLogin,
+ 'hasSuperUserAccess' => $hasSuperUserAccess,
+ ], $default = []);
+ } finally {
+ API::$SET_SUPERUSER_ACCESS_REQUIRE_PASSWORD_CONFIRMATION = true;
+ }
+ }
} \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html b/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html
index a92fbd4b95..8c831c87c5 100644
--- a/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html
+++ b/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.html
@@ -4,6 +4,7 @@
class="userEditForm"
ng-class="{ loading: $ctrl.isSavingUserInfo }"
>
+
<div class="row" piwik-form>
<div class="col m2 entityList" ng-if="!$ctrl.isAdd">
<ul class="listCircle">
@@ -98,8 +99,8 @@
<div ng-if="!$ctrl.isAdd" ng-show="$ctrl.activeTab === 'permissions'" class="user-permissions">
<piwik-user-permissions-edit
- user-login="$ctrl.user.login"
ng-if="!$ctrl.user.superuser_access"
+ user-login="$ctrl.user.login"
on-user-has-access-detected="$ctrl.userHasAccess = hasAccess"
on-access-change="$ctrl.isUserModified = true"
access-levels="$ctrl.accessLevels"
@@ -120,7 +121,7 @@
piwik-field
uicontrol="checkbox"
name="superuser_access"
- ng-model="$ctrl.user.superuser_access"
+ ng-model="$ctrl.superUserAccessChecked"
ng-attr-title="{{:: 'UsersManager_HasSuperUserAccess'|translate }}"
ng-click="$ctrl.confirmSuperUserChange()"
data-disabled="$ctrl.isSavingUserInfo"
@@ -130,21 +131,28 @@
<div class="superuser-confirm-modal modal">
<div class="modal-content">
<h2>{{:: 'UsersManager_AreYouSure'|translate }}</h2>
- <p ng-if="!$ctrl.user.superuser_access">
+ <p ng-if="$ctrl.user.superuser_access">
{{:: 'UsersManager_RemoveSuperuserAccessConfirm'|translate }}
</p>
- <p ng-if="$ctrl.user.superuser_access">
+ <p ng-if="!$ctrl.user.superuser_access">
{{:: 'UsersManager_AddSuperuserAccessConfirm'|translate }}
</p>
+
+ <div piwik-field uicontrol="password" name="currentUserPasswordForSuperUser" autocomplete="off"
+ ng-model="$ctrl.passwordConfirmationForSuperUser"
+ full-width="true"
+ title="{{ 'UsersManager_YourCurrentPassword'|translate }}"
+ value="">
+ </div>
</div>
<div class="modal-footer">
<a href="" class="modal-action modal-close btn" ng-click="$ctrl.toggleSuperuserAccess()">{{:: 'General_Yes'|translate }}</a>
- <a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.user.superuser_access = !$ctrl.user.superuser_access">{{:: 'General_No'|translate }}</a>
+ <a href="" class="modal-action modal-close modal-no" ng-click="$ctrl.setSuperUserAccessChecked()">{{:: 'General_No'|translate }}</a>
</div>
</div>
</div>
- <div ng-if="$ctrl.activeTab === '2fa' && $ctrl.currentUserRole == 'superuser' && !$ctrl.isAdd" class="twofa-reset">
+ <div ng-show="$ctrl.activeTab === '2fa'" ng-if="$ctrl.currentUserRole == 'superuser' && !$ctrl.isAdd" class="twofa-reset">
<p>{{:: 'UsersManager_ResetTwoFactorAuthenticationInfo'|translate }}</p>
<div piwik-save-button
@@ -168,6 +176,7 @@
</div>
</div>
+
<div class="change-password-modal modal">
<div class="modal-content">
<h2 piwik-translate="UsersManager_AreYouSureChangeDetails"><strong>{{ $ctrl.user.login }}</strong></h2>
@@ -185,4 +194,5 @@
<a href="" class="modal-action modal-close modal-no">{{:: 'General_No'|translate }}</a>
</div>
</div>
-</div>
+
+</div> \ No newline at end of file
diff --git a/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.js b/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.js
index bb929d5749..852469dbc1 100644
--- a/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.js
+++ b/plugins/UsersManager/angularjs/user-edit-form/user-edit-form.component.js
@@ -46,6 +46,7 @@
vm.saveUserInfo = saveUserInfo;
vm.reset2FA = reset2FA;
vm.updateUser = updateUser;
+ vm.setSuperUserAccessChecked = setSuperUserAccessChecked;
function $onInit() {
vm.firstSiteAccess = {
@@ -65,6 +66,8 @@
if (!vm.isAdd) {
vm.user.password = 'XXXXXXXX'; // make sure password is not stored in the client after update/save
}
+
+ setSuperUserAccessChecked();
}
function getFormTitle() {
@@ -105,15 +108,24 @@
method: 'UsersManager.setSuperUserAccess'
}, {
userLogin: vm.user.login,
- hasSuperUserAccess: vm.user.superuser_access ? '1' : '0'
+ hasSuperUserAccess: vm.user.superuser_access ? '0' : '1',
+ passwordConfirmation: vm.passwordConfirmationForSuperUser,
+ }).then(function () {
+ vm.user.superuser_access = !vm.user.superuser_access;
}).catch(function () {
// ignore error (still displayed to user)
}).then(function () {
vm.isSavingUserInfo = false;
vm.isUserModified = true;
+ vm.passwordConfirmationForSuperUser = null;
+ setSuperUserAccessChecked();
});
}
+ function setSuperUserAccessChecked() {
+ vm.superUserAccessChecked = !! vm.user.superuser_access;
+ }
+
function saveUserInfo() {
if (vm.isAdd) {
createUser();
diff --git a/plugins/UsersManager/lang/en.json b/plugins/UsersManager/lang/en.json
index 69179b7cf5..866b52567a 100644
--- a/plugins/UsersManager/lang/en.json
+++ b/plugins/UsersManager/lang/en.json
@@ -127,8 +127,8 @@
"SuperUserIntro2": "Please use this feature carefully.",
"HasSuperUserAccess": "Has Superuser Access",
"AreYouSure": "Are you sure?",
- "RemoveSuperuserAccessConfirm": "Removing superuser access will leave the user with no permissions (you will have to add them afterwards). Do you wish to continue?",
- "AddSuperuserAccessConfirm": "Giving a user superuser access will allow the user to have full control over Matomo and should be done sparingly. Do you wish to continue?",
+ "RemoveSuperuserAccessConfirm": "Removing superuser access will leave the user with no permissions (you will have to add them afterwards). Enter your password to continue.",
+ "AddSuperuserAccessConfirm": "Giving a user superuser access will allow the user to have full control over Matomo and should be done sparingly. Enter your password to continue.",
"DeleteUsers": "Delete Users",
"UserSearch": "User search",
"FilterByAccess": "Filter by access",
diff --git a/plugins/UsersManager/tests/Fixtures/ManyUsers.php b/plugins/UsersManager/tests/Fixtures/ManyUsers.php
index 13a998e456..1382a55fd2 100644
--- a/plugins/UsersManager/tests/Fixtures/ManyUsers.php
+++ b/plugins/UsersManager/tests/Fixtures/ManyUsers.php
@@ -9,6 +9,7 @@ namespace Piwik\Plugins\UsersManager\tests\Fixtures;
use Piwik\Plugins\UsersManager\API;
use Piwik\Plugins\UsersManager\Model;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\Fixture;
/**
@@ -121,7 +122,8 @@ class ManyUsers extends Fixture
}
if ($access == 'superuser') {
- $api->setSuperUserAccess($login, true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($login, true);
} else {
$api->setUserAccess($login, $access, $idSites);
}
diff --git a/plugins/UsersManager/tests/Integration/APITest.php b/plugins/UsersManager/tests/Integration/APITest.php
index a512660e6b..27c83ec306 100644
--- a/plugins/UsersManager/tests/Integration/APITest.php
+++ b/plugins/UsersManager/tests/Integration/APITest.php
@@ -20,6 +20,7 @@ use Piwik\Plugins\SitesManager\API as SitesManagerAPI;
use Piwik\Plugins\UsersManager\API;
use Piwik\Plugins\UsersManager\Model;
use Piwik\Plugins\UsersManager\UsersManager;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
@@ -381,7 +382,8 @@ class APITest extends IntegrationTestCase
$access = $this->api->getSitesAccessFromUser($user2);
$this->assertEmpty($access);
- $this->api->setSuperUserAccess($user2, true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($user2, true);
// super user has admin access for every site
$access = $this->api->getSitesAccessFromUser($user2);
@@ -546,7 +548,8 @@ class APITest extends IntegrationTestCase
public function test_getUsersPlusRole_shouldSearchForSuperUsersCorrectly()
{
$this->addUserWithAccess('userLogin2', 'admin', 1);
- $this->api->setSuperUserAccess('userLogin2', true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword('userLogin2', true);
$this->addUserWithAccess('userLogin3', 'view', 1);
$this->addUserWithAccess('userLogin4', 'superuser', 1);
$this->addUserWithAccess('userLogin5', null, 1);
@@ -983,6 +986,15 @@ class APITest extends IntegrationTestCase
$this->assertEquals(array(View::ID, TestCap1::ID), $access);
}
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage abc
+ */
+ public function test_setSuperUserAccess_failsIfCurrentPasswordIsIncorrect()
+ {
+ $this->api->setSuperUserAccess($this->login, true, 'asldfkjds');
+ }
+
private function getAccessInSite($login, $idSite)
{
$access = $this->model->getSitesAccessFromUser($login);
@@ -1018,7 +1030,8 @@ class APITest extends IntegrationTestCase
{
$this->api->addUser($username, 'password', $email ?: "$username@password.de", $alias);
if ($accessLevel == 'superuser') {
- $this->api->setSuperUserAccess($username, true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword($username, true);
} else if ($accessLevel) {
$this->api->setUserAccess($username, $accessLevel, $idSite);
}
diff --git a/plugins/UsersManager/tests/Integration/UsersManagerTest.php b/plugins/UsersManager/tests/Integration/UsersManagerTest.php
index abaf5d6590..43d4bbb5ee 100644
--- a/plugins/UsersManager/tests/Integration/UsersManagerTest.php
+++ b/plugins/UsersManager/tests/Integration/UsersManagerTest.php
@@ -10,10 +10,12 @@ namespace Piwik\Plugins\UsersManager\tests\Integration;
use Piwik\Access;
use Piwik\Auth\Password;
+use Piwik\Common;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\Plugins\UsersManager\API;
use Piwik\Plugins\UsersManager\Model;
use Piwik\Plugins\UsersManager\UsersManager;
+use Piwik\Plugins\UsersManager\UserUpdater;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
@@ -380,7 +382,8 @@ class UsersManagerTest extends IntegrationTestCase
//add user and set some rights
$this->api->addUser("regularuser", "geqgeagae1", "test1@test.com", "alias1");
$this->api->addUser("superuser", "geqgeagae2", "test2@test.com", "alias2");
- $this->api->setSuperUserAccess('superuser', true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword('superuser', true);
// delete the user
$this->api->deleteUser("superuser");
@@ -604,7 +607,8 @@ class UsersManagerTest extends IntegrationTestCase
public function testSetUserAccess_ShouldFail_IfLoginIsUserWithSuperUserAccess()
{
$this->api->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
- $this->api->setSuperUserAccess('gegg4564eqgeqag', true);
+ $userUpdater = new UserUpdater();
+ $userUpdater->setSuperUserAccessWithoutCurrentPassword('gegg4564eqgeqag', true);
FakeAccess::clearAccess($superUser = false, $idSitesAdmin = array(1));
$this->api->setUserAccess('gegg4564eqgeqag', 'view', 1);
@@ -806,8 +810,10 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetSuperUserAccess_ShouldFail_IfUserHasNotSuperUserPermission()
{
+ $pwd = $this->createCurrentUser();
+
FakeAccess::$superUser= false;
- $this->api->setSuperUserAccess('nologin', false);
+ $this->api->setSuperUserAccess('nologin', false, $pwd);
}
/**
@@ -816,7 +822,8 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetSuperUserAccess_ShouldFail_IfUserWithGivenLoginDoesNotExist()
{
- $this->api->setSuperUserAccess('nologin', false);
+ $pwd = $this->createCurrentUser();
+ $this->api->setSuperUserAccess('nologin', false, $pwd);
}
/**
@@ -825,7 +832,8 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetSuperUserAccess_ShouldFail_IfUserIsAnonymous()
{
- $this->api->setSuperUserAccess('anonymous', true);
+ $pwd = $this->createCurrentUser();
+ $this->api->setSuperUserAccess('anonymous', true, $pwd);
}
/**
@@ -834,14 +842,18 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetSuperUserAccess_ShouldFail_IfUserIsOnlyRemainingUserWithSuperUserAccess()
{
+ $pwd = $this->createCurrentUser();
+
$this->api->addUser('login1', 'password1', 'test@example.com', false);
- $this->api->setSuperUserAccess('login1', true);
+ $this->api->setSuperUserAccess('login1', true, $pwd);
- $this->api->setSuperUserAccess('login1', false);
+ $this->api->setSuperUserAccess('login1', false, $pwd);
}
public function testSetSuperUserAccess_ShouldDeleteAllExistingAccessEntries()
{
+ $pwd = $this->createCurrentUser();
+
list($id1, $id2) = $this->addSites(2);
$this->api->addUser('login1', 'password1', 'test@example.com', false);
$this->api->setUserAccess('login1', 'view', array($id1));
@@ -851,7 +863,7 @@ class UsersManagerTest extends IntegrationTestCase
$access = $this->_flatten($this->api->getSitesAccessFromUser('login1'));
$this->assertEquals(array($id1 => 'view', $id2 => 'admin'), $access);
- $this->api->setSuperUserAccess('login1', true);
+ $this->api->setSuperUserAccess('login1', true, $pwd);
// verify no longer any access
$this->assertEquals(array(), $this->model->getSitesAccessFromUser('login1'));
@@ -859,11 +871,13 @@ class UsersManagerTest extends IntegrationTestCase
public function testSetSuperUserAccess_ShouldAddAndRemoveSuperUserAccessOnlyForGivenLogin()
{
+ $pwd = $this->createCurrentUser();
+
$this->api->addUser('login1', 'password1', 'test1@example.com', false);
$this->api->addUser('login2', 'password2', 'test2@example.com', false);
$this->api->addUser('login3', 'password3', 'test3@example.com', false);
- $this->api->setSuperUserAccess('login2', true);
+ $this->api->setSuperUserAccess('login2', true, $pwd);
// test add Super User access
$users = $this->api->getUsers();
@@ -874,9 +888,9 @@ class UsersManagerTest extends IntegrationTestCase
$this->assertEquals(0, $users[2]['superuser_access']);
// should also accept string '1' to add Super User access
- $this->api->setSuperUserAccess('login1', '1');
+ $this->api->setSuperUserAccess('login1', '1', $pwd);
// test remove Super User access
- $this->api->setSuperUserAccess('login2', false);
+ $this->api->setSuperUserAccess('login2', false, $pwd);
$users = $this->api->getUsers();
$this->assertEquals(1, $users[0]['superuser_access']);
@@ -884,9 +898,9 @@ class UsersManagerTest extends IntegrationTestCase
$this->assertEquals(0, $users[1]['superuser_access']);
$this->assertEquals(0, $users[2]['superuser_access']);
- $this->api->setSuperUserAccess('login3', true);
+ $this->api->setSuperUserAccess('login3', true, $pwd);
// should also accept string '0' to remove Super User access
- $this->api->setSuperUserAccess('login1', '0');
+ $this->api->setSuperUserAccess('login1', '0', $pwd);
$users = $this->api->getUsers();
$this->assertEquals(0, $users[0]['superuser_access']);
@@ -1203,4 +1217,20 @@ class UsersManagerTest extends IntegrationTestCase
$this->assertRegExp("(UsersManager_ExceptionUserDoesNotExist)", $expected->getMessage());
}
}
+
+ private function createCurrentUser()
+ {
+ $identity = FakeAccess::$identity;
+ FakeAccess::$identity = 'lskfjs';
+
+ $pwd = 'testpwd';
+
+ try {
+ $this->api->addUser($identity, $pwd, 'someuser@email.com');
+ } finally {
+ FakeAccess::$identity = $identity;
+ }
+
+ return $pwd;
+ }
}
diff --git a/plugins/UsersManager/tests/UI/UsersManager_spec.js b/plugins/UsersManager/tests/UI/UsersManager_spec.js
index 6a7482ba5d..9ba0556c3e 100644
--- a/plugins/UsersManager/tests/UI/UsersManager_spec.js
+++ b/plugins/UsersManager/tests/UI/UsersManager_spec.js
@@ -74,9 +74,9 @@ describe("UsersManager", function () {
});
it('should select rows when individual row select is clicked', async function () {
- await (await page.jQuery('td.select-cell label:eq(0)')).click();
- await (await page.jQuery('td.select-cell label:eq(3)')).click();
- await (await page.jQuery('td.select-cell label:eq(8)')).click();
+ await (await page.jQuery('td.select-cell label:eq(0)', { waitFor: true })).click();
+ await (await page.jQuery('td.select-cell label:eq(3)', { waitFor: true })).click();
+ await (await page.jQuery('td.select-cell label:eq(8)', { waitFor: true })).click();
await page.mouse.move(0, 0);
await page.waitFor(500); // for checkbox animations
@@ -116,7 +116,7 @@ describe("UsersManager", function () {
await page.click('.toggle-select-all-in-search'); // reselect all in search
await page.click('.bulk-actions.btn');
- await (await page.jQuery('#user-list-bulk-actions>li:first')).hover();
+ await (await page.jQuery('#user-list-bulk-actions>li:first > a')).hover();
await (await page.jQuery('#bulk-set-access a:contains(Admin)')).click();
await page.waitFor(350); // wait for animation
@@ -231,7 +231,7 @@ describe("UsersManager", function () {
it('should add access to all websites when bulk access is used on all websites in search', async function () {
await page.click('.userPermissionsEdit .bulk-actions > .dropdown-trigger.btn');
- await (await page.jQuery('#user-permissions-edit-bulk-actions>li:first')).hover();
+ await (await page.jQuery('#user-permissions-edit-bulk-actions>li:first>a')).hover();
await (await page.jQuery('#user-permissions-edit-bulk-actions a:contains(Write)')).click();
await page.waitFor('.change-access-confirm-modal', { visible: true });
@@ -277,7 +277,7 @@ describe("UsersManager", function () {
it('should set access to selected sites when set bulk access is used', async function () {
await page.click('.userPermissionsEdit .bulk-actions > .dropdown-trigger.btn');
await page.waitFor(250); // animation
- await (await page.jQuery('#user-permissions-edit-bulk-actions>li:first:visible', { waitFor: true })).hover();
+ await page.evaluate(() => $('#user-permissions-edit-bulk-actions>li:first > a:visible').mouseenter());
await page.waitFor(250); // animation
await (await page.jQuery('#user-permissions-edit-bulk-actions a:contains(Admin):visible', { waitFor: true })).click();
@@ -291,11 +291,12 @@ describe("UsersManager", function () {
});
it('should filter the permissions when the filters are used', async function () {
- await page.type('div.site-filter>input', 'nova');
await page.evaluate(function () {
- $('.access-filter select').val('string:admin').change();
+ $('.userPermissionsEdit .access-filter select').val('string:admin').change();
});
await page.waitForNetworkIdle();
+ await page.type('.userPermissionsEdit div.site-filter>input', 'hunter');
+ await page.waitForNetworkIdle();
await page.waitFor('#sitesForPermission tr', { visible: true });
await page.waitFor(1000);
@@ -311,18 +312,20 @@ describe("UsersManager", function () {
it('should set access to all sites selected when set bulk access is used', async function () {
await page.click('.userPermissionsEdit .bulk-actions > .dropdown-trigger.btn');
- await page.waitFor(100); // animation
- await (await page.jQuery('#user-permissions-edit-bulk-actions>li:first', { waitFor: true })).hover();
- await page.waitFor(100); // animation
+ await page.waitFor(250); // animation
+ await page.evaluate(() => $('#user-permissions-edit-bulk-actions>li:first > a:visible').mouseenter());
+ await page.waitFor(250); // animation
await (await page.jQuery('#user-permissions-edit-bulk-actions a:contains(View)', { waitFor: true })).click();
await page.waitFor(250); // animation
await page.evaluate(() => $('.change-access-confirm-modal .modal-close:not(.modal-no):visible').click());
+ await page.waitForNetworkIdle();
await page.evaluate(function () { // remove filter
$('.access-filter select').val('string:some').change();
});
await page.waitForNetworkIdle();
+ await page.waitFor(250); // animation
expect(await page.screenshotSelector('.usersManager')).to.matchImage('permissions_bulk_access_set_all');
});
@@ -347,12 +350,12 @@ describe("UsersManager", function () {
await page.waitFor(250); // animation
- await page.evaluate(() => $('.confirmCapabilityToggle .modal-close:not(.modal-no):visible').click());
+ await page.evaluate(() => $('.userPermissionsEdit .confirmCapabilityToggle .modal-close:not(.modal-no):visible').click());
await page.waitForNetworkIdle();
await page.waitFor(250); // animation
- expect(await page.screenshotSelector('.admin#content')).to.matchImage('permissions_capability_single_site');
+ expect(await page.screenshotSelector('.usersManager')).to.matchImage('permissions_capability_single_site');
});
it('should remove access to displayed rows when remove bulk access is clicked', async function () {
@@ -394,7 +397,22 @@ describe("UsersManager", function () {
expect(await elem.screenshot()).to.matchImage('superuser_confirm');
});
+ it('should fail to set superuser access if password is wrong', async function () {
+ await page.type('input#currentUserPasswordForSuperUser', 'wrongpassword');
+ await page.evaluate(() => $('.superuser-confirm-modal .modal-close:not(.modal-no):visible').click());
+ await page.waitForNetworkIdle();
+
+ await page.waitFor('.notification-error', { visible: true });
+
+ const notificationHtml = await page.evaluate(() => $('.notification-error>div').html());
+ expect(notificationHtml).to.equal('The current password you entered is not correct.');
+ });
+
it('should give the user superuser access when the superuser modal is confirmed', async function () {
+ await page.click('.userEditForm #superuser_access+label');
+ await page.waitFor(500);
+
+ await page.type('input#currentUserPasswordForSuperUser', 'superUserPass');
await page.evaluate(() => $('.superuser-confirm-modal .modal-close:not(.modal-no):visible').click());
await page.waitForNetworkIdle();
await page.waitFor(500);
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_manage_users_back.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_manage_users_back.png
index 9ffaaa81a1..228c7cc0cd 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_manage_users_back.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_manage_users_back.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f16ad832b1d64c645664e7e64408f25e491c31f3926e2da9773cc40916193009
-size 147836
+oid sha256:434264e176f6f4aa217c247fdcf032fca1d7304d8a33359e28505dc867231fcc
+size 147842
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_bulk_access_set_all.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_bulk_access_set_all.png
index 323e7bce5e..117d590b71 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_bulk_access_set_all.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_bulk_access_set_all.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4af9d284d62698df449b104597006d73dc06855761fad90333def4febc11506c
-size 85673
+oid sha256:6806c1587d23ff706a62b908aeb141b98a052440f8220d14a21b688faae8b33c
+size 78284
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_capability_single_site.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_capability_single_site.png
index f2623bd5fb..c12b0af534 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_capability_single_site.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_capability_single_site.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:750fa07d8b3c6b5d7da0e68ede73131af3c1460c00aee070f2f1eda0e310d036
-size 96385
+oid sha256:c700040d1c3a9c7288d7a1f5d3354089709762dffb4764aa5f114f3bbca7c65f
+size 91417
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_filters.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_filters.png
index 1cda86e3aa..28b8a16607 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_filters.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_filters.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:0dbb9ad0fad0e51c572ce7b84cc59db7dea7083b702fc663fd3ab5cccffdb245
-size 28695
+oid sha256:595839d882d0c1d20f5ccd23dff2dbab68b24940e01683b9da44dfbea28e4cdb
+size 65467
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_select_all.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_select_all.png
index 9ffa892dc0..462864ba99 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_select_all.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_select_all.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ad2b46f639d5a1d0d59299444b791011be309a71883f764cf1a22f7d712c5bbc
-size 29843
+oid sha256:4162bd79000733235fe891ccf4fa90f60af9862d6a39b02cc6ef6c35d96874a1
+size 67291
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_single_site_access.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_single_site_access.png
index 2d2a9d7736..93d4420205 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_single_site_access.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_permissions_single_site_access.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5606bd6b54b2776f8a0df93ff31d5cd7a4f527e9b24fc0727a6e33286d234c01
-size 92464
+oid sha256:ae69632d1e3c8c2c8cae6d6d1bc3d0142ba4d5ff870e85d23637e149fad76311
+size 87826
diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_superuser_confirm.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_superuser_confirm.png
index 604d4b89c1..d30c26ebb8 100644
--- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_superuser_confirm.png
+++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_superuser_confirm.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:556e3e32f67793bfb45a7fb1b618bc3f20a5c81fca3cc8049374c88674a5ee91
-size 15764
+oid sha256:8aca0c9d68a8f97ac9a9c82947fb3dc00ce51455a8f3abb34c5e055a1004471c
+size 19858