قرینه از
https://github.com/matomo-org/matomo.git
synced 2025-08-22 15:07:44 +00:00

* Add token expiry warning email notification to scheduler * Add token expiry warning email notification to scheduler * add logic for -1 disable notification' * Fix spelling mistake * Fix some bugs around rendering the email * Update version * Refactor rotation code to use a standardised naming convention with new code * PHPStan * PHPCS fix * add test, not finished * finish off implementing test * Fix regression test failure * Update config to disable feature when set to 0 * update text to make more sense * Update token logic to more accurately determine when tokens are created * Update broken UI test * Add support for custom auth token expire date in UI and API (#23340) * Add support for custom auth token expire date in UI and API * Apply initial review feedback - require token expire date to be in future - refactor and simplify controller logic - remove unsude model method params - always display expire column in the list of tokens - pass default expiration to help text * Update config name to match emails-related work * Update UI test screenshot where expire date column is always visible * Update UI test screenshot with new global config options * Move initial expire date calculation to back-end * Use mock date for tests * Update UI tests * Update UI test screenshots * Update UI test screenshots * Move logic to override Tests.now to the Date class * Remove unused use statement * Update UI test screenshots * Tweak Date logic around Tests.now * Fix datepicker visibility * Add UI test for token expiration date picker * Fix 'label for a missing id' accessibility issue * Use custom fixture not to impact other tests * Fix CS * Fix fixture namespacing and use correct fixture in tests * Uncomment code commented out when debugging * Ensure initial date is set correctly for the date picker * Test config change to a different token expire interval * Update screenshot after config wording change * Remove unused function * Build vue components * Update expected screenshots * Remove obsolete comments * Allow numeric strings in Tests.now * Accept only values representing dates over 1990-01-01 in Tests.now * set date correctly for tests --------- Co-authored-by: Marc Neudert <marc@innocraft.com> Co-authored-by: sgiehl <stefan@matomo.org> --------- Co-authored-by: Nathan Gavin <nathangavin987@gmail.com> Co-authored-by: Marc Neudert <marc@innocraft.com> Co-authored-by: sgiehl <stefan@matomo.org>
394 خطوط
17 KiB
PHP
394 خطوط
17 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*/
|
|
|
|
namespace Piwik\Plugins\UsersManager\tests\Integration;
|
|
|
|
use Piwik\Access\Role\View;
|
|
use Piwik\Access\Role\Write;
|
|
use Piwik\Common;
|
|
use Piwik\Date;
|
|
use Piwik\Db;
|
|
use Piwik\Plugins\SitesManager\API as SitesManagerAPI;
|
|
use Piwik\Plugins\UsersManager\API;
|
|
use Piwik\Plugins\UsersManager\Model;
|
|
use Piwik\Tests\Framework\Fixture;
|
|
use Piwik\Tests\Framework\Mock\FakeAccess;
|
|
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
|
|
|
|
/**
|
|
* @group UsersManager
|
|
* @group APITest
|
|
* @group Plugins
|
|
*/
|
|
class ModelTest extends IntegrationTestCase
|
|
{
|
|
/**
|
|
* @var API
|
|
*/
|
|
private $api;
|
|
|
|
/**
|
|
* @var Model
|
|
*/
|
|
private $model;
|
|
|
|
private $login = 'userLogin';
|
|
private $login2 = 'userLogin2';
|
|
private $login3 = 'pendingLogin3';
|
|
|
|
public function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->api = API::getInstance();
|
|
$this->model = new Model();
|
|
|
|
FakeAccess::clearAccess();
|
|
FakeAccess::$superUser = true;
|
|
|
|
Fixture::createWebsite('2014-01-01 00:00:00');
|
|
Fixture::createWebsite('2014-01-01 00:00:00');
|
|
Fixture::createWebsite('2014-01-01 00:00:00');
|
|
$this->api->addUser($this->login, 'password', 'userlogin@password.de');
|
|
$this->api->addUser($this->login2, 'password2', 'userlogin2@password.de');
|
|
}
|
|
|
|
public function testGetSitesAccessFromUserNoAccess()
|
|
{
|
|
$this->assertSame(array(), $this->model->getSitesAccessFromUser($this->login));
|
|
}
|
|
|
|
public function testGetSitesAccessFromUserAccessOneSite()
|
|
{
|
|
$this->model->addUserAccess($this->login, Write::ID, array(2));
|
|
$this->assertEquals(array(
|
|
array('site' => '2', 'access' => Write::ID)
|
|
), $this->model->getSitesAccessFromUser($this->login));
|
|
}
|
|
|
|
public function testGetSitesAccessFromUserMultipleSites()
|
|
{
|
|
$this->model->addUserAccess($this->login, Write::ID, array(3));
|
|
$this->model->addUserAccess($this->login, Write::ID, array(2));
|
|
$this->model->addUserAccess($this->login, View::ID, array(1));
|
|
$access = $this->model->getSitesAccessFromUser($this->login);
|
|
// The order might differ depending on the database, so sort by 'site'
|
|
usort($access, function ($a, $b) {
|
|
return $a['site'] - $b['site'];
|
|
});
|
|
$this->assertEquals(array(
|
|
array('site' => '1', 'access' => View::ID),
|
|
array('site' => '2', 'access' => Write::ID),
|
|
array('site' => '3', 'access' => Write::ID),
|
|
), $access);
|
|
}
|
|
|
|
public function testGetSitesAccessFromUserMultipleSitesSomeNoLongerExist()
|
|
{
|
|
$this->model->addUserAccess($this->login, Write::ID, array(3));
|
|
$this->model->addUserAccess($this->login, Write::ID, array(2));
|
|
$this->model->addUserAccess($this->login, View::ID, array(1));
|
|
SitesManagerAPI::getInstance()->deleteSite(2);
|
|
SitesManagerAPI::getInstance()->deleteSite(1);
|
|
$this->assertEquals(array(
|
|
array('site' => '3', 'access' => Write::ID),
|
|
), $this->model->getSitesAccessFromUser($this->login));
|
|
}
|
|
|
|
public function testGetSitesAccessFromUserSiteDeletedManually()
|
|
{
|
|
$this->model->addUserAccess($this->login, Write::ID, array(3));
|
|
$this->model->addUserAccess($this->login, Write::ID, array(2));
|
|
$this->model->addUserAccess($this->login, View::ID, array(1));
|
|
Db::query('DELETE FROM ' . Common::prefixTable('site') . ' where idsite = 1');
|
|
Db::query('DELETE FROM ' . Common::prefixTable('site') . ' where idsite = 2');
|
|
$this->assertEquals(array(
|
|
array('site' => '3', 'access' => Write::ID),
|
|
), $this->model->getSitesAccessFromUser($this->login));
|
|
}
|
|
|
|
public function testGetAllNonSystemTokensForLoginWhenNoTokenConfigured()
|
|
{
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertSame(array(), $tokens);
|
|
}
|
|
|
|
public function testAddTokenAuthMinimal()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05');
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals(array(array(
|
|
'idusertokenauth' => '1',
|
|
'login' => 'userLogin',
|
|
'description' => 'MyDescription',
|
|
'password' => '2265daba0872fc3aef169d079365e590f0cbc8ed46c2a7984c8a642803cfd96cb47804a63cf22a79f6ca469268c29ee9e72a5059b62d0a598fe42dfc8dcc51bc',
|
|
'hash_algo' => 'sha512',
|
|
'system_token' => '0',
|
|
'last_used' => null,
|
|
'date_created' => '2020-01-02 03:04:05',
|
|
'date_expired' => null,
|
|
'secure_only' => '0',
|
|
'ts_rotation_notified' => null,
|
|
'ts_expiration_warning_notified' => null,
|
|
)), $tokens);
|
|
}
|
|
|
|
public function testAddTokenAuthExpire()
|
|
{
|
|
$id = $this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', '2030-01-05 03:04:05');
|
|
$this->assertEquals(1, $id);
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals(array(array(
|
|
'idusertokenauth' => '1',
|
|
'login' => 'userLogin',
|
|
'description' => 'MyDescription',
|
|
'password' => '2265daba0872fc3aef169d079365e590f0cbc8ed46c2a7984c8a642803cfd96cb47804a63cf22a79f6ca469268c29ee9e72a5059b62d0a598fe42dfc8dcc51bc',
|
|
'hash_algo' => 'sha512',
|
|
'system_token' => '0',
|
|
'last_used' => null,
|
|
'date_created' => '2020-01-02 03:04:05',
|
|
'date_expired' => '2030-01-05 03:04:05',
|
|
'secure_only' => '0',
|
|
'ts_rotation_notified' => null,
|
|
'ts_expiration_warning_notified' => null,
|
|
)), $tokens);
|
|
}
|
|
|
|
public function testAddTokenAuthThrowsExceptionIfUserNotExists()
|
|
{
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('does not exist');
|
|
$this->model->addTokenAuth('foobar', 'token', 'MyDescription', '2020-01-02 03:04:05', '2030-01-05 03:04:05');
|
|
}
|
|
|
|
public function testAddTokenAuthThrowsExceptionFailsAddingSameTwice()
|
|
{
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('Duplicate entry');
|
|
$this->model->addTokenAuth($this->login, 'token', 'My description', '2020-01-02 03:04:05');
|
|
$this->model->addTokenAuth($this->login, 'token', 'My duplicate', '2020-01-03 03:04:05');
|
|
}
|
|
|
|
public function testAddTokenAuthReturnsId()
|
|
{
|
|
$id = $this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05');
|
|
$this->assertEquals(1, $id);
|
|
$id = $this->model->addTokenAuth($this->login, 'token2', 'MyDescription', '2020-01-02 03:04:05');
|
|
$this->assertEquals(2, $id);
|
|
}
|
|
|
|
public function testAddTokenAuthThrowsExceptionNoDescription()
|
|
{
|
|
$this->expectException(\Exception::class);
|
|
$this->expectExceptionMessage('General_ValidatorErrorEmptyValue');
|
|
$this->model->addTokenAuth($this->login, 'token', '', '2020-01-02 03:04:05');
|
|
}
|
|
|
|
public function testGetAllNonSystemTokensForLoginDoesNotReturnSystemTokens()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token2', 'api usage token', '2020-01-02 03:04:05', null, true);
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals(array(), $tokens);
|
|
}
|
|
|
|
public function testGetAllNonSystemTokensForLoginDoesNotReturnExpiredTokens()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token2', 'api usage token', '2020-01-02 03:04:05', '2019-01-05 03:04:05');
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals(array(), $tokens);
|
|
}
|
|
|
|
public function testGetAllNonSystemTokensForLoginReturnsNotExpiredToken()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', '2030-01-05 03:04:05');
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals(array(array(
|
|
'idusertokenauth' => '1',
|
|
'login' => 'userLogin',
|
|
'description' => 'MyDescription',
|
|
'password' => '2265daba0872fc3aef169d079365e590f0cbc8ed46c2a7984c8a642803cfd96cb47804a63cf22a79f6ca469268c29ee9e72a5059b62d0a598fe42dfc8dcc51bc',
|
|
'hash_algo' => 'sha512',
|
|
'system_token' => '0',
|
|
'last_used' => null,
|
|
'date_created' => '2020-01-02 03:04:05',
|
|
'date_expired' => '2030-01-05 03:04:05',
|
|
'secure_only' => '0',
|
|
'ts_rotation_notified' => null,
|
|
'ts_expiration_warning_notified' => null,
|
|
)), $tokens);
|
|
}
|
|
|
|
public function testGetUserByTokenAuthFindsUserWhenTokenNotYetExpired()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', '2030-01-05 03:04:05');
|
|
$user = $this->model->getUserByTokenAuth('token');
|
|
$this->assertSame($this->login, $user['login']);
|
|
}
|
|
|
|
public function testGetUserByTokenAuthFindsUserWhenNoExpireDateSet()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05');
|
|
$user = $this->model->getUserByTokenAuth('token');
|
|
$this->assertSame($this->login, $user['login']);
|
|
}
|
|
|
|
public function testGetUserByTokenAuthNotFindsUserWhenTokenIsExpired()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', '2019-03-04 00:05:06');
|
|
$user = $this->model->getUserByTokenAuth('token');
|
|
$this->assertEmpty($user);
|
|
}
|
|
|
|
public function testGetUserByTokenAuthFindsUserWhenTokenIsSystemToken()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', null, true);
|
|
$user = $this->model->getUserByTokenAuth('token');
|
|
$this->assertSame($this->login, $user['login']);
|
|
}
|
|
|
|
public function testGenerateRandomTokenAuthCorrectFormat()
|
|
{
|
|
$token = $this->model->generateRandomTokenAuth();
|
|
$this->assertSame(32, strlen($token));
|
|
$this->assertTrue(ctype_xdigit($token));
|
|
}
|
|
|
|
public function testGenerateRandomTokenAuthIsAlwaysDifferent()
|
|
{
|
|
$this->assertNotEquals($this->model->generateRandomTokenAuth(), $this->model->generateRandomTokenAuth());
|
|
}
|
|
|
|
public function testHashTokenAuth()
|
|
{
|
|
$this->assertSame('2265daba0872fc3aef169d079365e590f0cbc8ed46c2a7984c8a642803cfd96cb47804a63cf22a79f6ca469268c29ee9e72a5059b62d0a598fe42dfc8dcc51bc', $this->model->hashTokenAuth('token'));
|
|
$this->assertSame('02c2e43dcb393097a1221465812a4e9b1e1e80f16e92b313fd4ce8c5ee5b8272a17cd8cdc1ce63578494eaba739c6f7abba7890506ef6bf8d607538778f2a849', $this->model->hashTokenAuth('token2'));
|
|
}
|
|
|
|
public function testGetAllHashedTokensForLoginsNoLoginsSet()
|
|
{
|
|
$this->assertSame(array(), $this->model->getAllHashedTokensForLogins(array()));
|
|
}
|
|
|
|
public function testGetAllHashedTokensForLoginsNoTokensExist()
|
|
{
|
|
$this->assertSame(array(), $this->model->getAllHashedTokensForLogins(array('foo', 'bar')));
|
|
}
|
|
|
|
public function testGetAllHashedTokensForLogins()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription', '2020-01-02 03:04:05', null, true);
|
|
$this->model->addTokenAuth($this->login, 'token2', 'MyDescription', '2020-01-02 03:04:05', null, false);
|
|
// does not return expired tokens
|
|
$this->model->addTokenAuth($this->login, 'token3', 'MyDescription', '2020-01-02 03:04:05', '2019-02-03 00:01:02', true);
|
|
|
|
$this->assertSame(array(), $this->model->getAllHashedTokensForLogins(array('foo', 'bar')));
|
|
|
|
$this->assertSame(array(
|
|
'2265daba0872fc3aef169d079365e590f0cbc8ed46c2a7984c8a642803cfd96cb47804a63cf22a79f6ca469268c29ee9e72a5059b62d0a598fe42dfc8dcc51bc',
|
|
'02c2e43dcb393097a1221465812a4e9b1e1e80f16e92b313fd4ce8c5ee5b8272a17cd8cdc1ce63578494eaba739c6f7abba7890506ef6bf8d607538778f2a849'
|
|
), $this->model->getAllHashedTokensForLogins(array('foo', $this->login, 'bar')));
|
|
}
|
|
|
|
public function testDeleteToken()
|
|
{
|
|
$id1 = $this->model->addTokenAuth($this->login, 'token', 'MyDescription1', '2020-01-02 03:04:05');
|
|
$id2 = $this->model->addTokenAuth($this->login, 'token2', 'MyDescription2', '2020-01-03 03:04:05');
|
|
|
|
// should not have deleted anything as it doesn't match
|
|
$this->model->deleteToken(999, $this->login);
|
|
$this->model->deleteToken($id1, 'foobar');
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertCount(2, $tokens);
|
|
$this->assertEquals($id1, $tokens[0]['idusertokenauth']);
|
|
$this->assertEquals($id2, $tokens[1]['idusertokenauth']);
|
|
|
|
// should only delete that id
|
|
$this->model->deleteToken($id1, $this->login);
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertCount(1, $tokens);
|
|
$this->assertEquals($id2, $tokens[0]['idusertokenauth']);
|
|
}
|
|
|
|
public function testDeleteAllTokensForUser()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token', 'MyDescription1', '2020-01-02 03:04:05');
|
|
$this->model->addTokenAuth($this->login, 'token2', 'MyDescription2', '2020-01-03 03:04:05');
|
|
$this->model->addTokenAuth($this->login2, 'token3', 'MyDescription2', '2020-01-03 03:04:05');
|
|
|
|
// should not have deleted anything as it doesn't match
|
|
$this->model->deleteAllTokensForUser('foobar');
|
|
|
|
$this->assertCount(2, $this->model->getAllNonSystemTokensForLogin($this->login));
|
|
$this->assertCount(1, $this->model->getAllNonSystemTokensForLogin($this->login2));
|
|
|
|
// should only delete tokens for that login
|
|
$this->model->deleteAllTokensForUser($this->login);
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertCount(0, $this->model->getAllNonSystemTokensForLogin($this->login));
|
|
$this->assertCount(1, $this->model->getAllNonSystemTokensForLogin($this->login2));
|
|
}
|
|
|
|
public function testSetTokenAuthWasUsed()
|
|
{
|
|
$this->model->addTokenAuth($this->login, 'token2', 'MyDescription', '2020-01-02 03:04:05');
|
|
$this->model->setTokenAuthWasUsed('token2', '2025-01-02 03:04:05');
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertSame('2025-01-02 03:04:05', $tokens[0]['last_used']);
|
|
|
|
// this should not update the token usage again, as it's within 10 minutes
|
|
$this->model->setTokenAuthWasUsed('token2', '2025-01-02 03:08:05');
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertSame('2025-01-02 03:04:05', $tokens[0]['last_used']);
|
|
|
|
// this should update the token usage again, as it's after 10 minutes
|
|
$this->model->setTokenAuthWasUsed('token2', '2025-01-02 03:15:05');
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertSame('2025-01-02 03:15:05', $tokens[0]['last_used']);
|
|
}
|
|
|
|
public function testSetTokenAuthWasUsedDoesNotFailWhenTokenNotExists()
|
|
{
|
|
$this->expectNotToPerformAssertions();
|
|
$this->model->setTokenAuthWasUsed('tokenFooBar', '2025-01-02 03:04:05');
|
|
}
|
|
|
|
public function testDeleteExpiredTokens()
|
|
{
|
|
$date = Date::factory('now')->addMonth(1)->getDatetime();
|
|
$dateNotExpired = Date::factory('now')->addMonth(24)->getDatetime();
|
|
$dateExpired = Date::factory('now')->subMonth(1)->getDatetime();
|
|
|
|
$id1 = $this->model->addTokenAuth($this->login, 'token', 'MyDescription1', '2020-01-01 03:04:05', $dateExpired);
|
|
$id2 = $this->model->addTokenAuth($this->login, 'token2', 'MyDescription2', '2020-01-02 03:04:05');
|
|
$id3 = $this->model->addTokenAuth($this->login, 'token3', 'MyDescription3', '2020-01-03 03:04:05', $dateNotExpired);
|
|
$id4 = $this->model->addTokenAuth($this->login2, 'token4', 'MyDescription4', '2020-01-04 03:04:05', $dateNotExpired);
|
|
$id5 = $this->model->addTokenAuth($this->login2, 'token5', 'MyDescription5', '2020-01-05 03:04:05');
|
|
$id6 = $this->model->addTokenAuth($this->login2, 'token6', 'MyDescription6', '2020-01-06 03:04:05', '2018-01-02 03:04:05');
|
|
|
|
// id1 and id6 are expired and should have been deleted
|
|
$this->model->deleteExpiredTokens($date);
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login);
|
|
$this->assertEquals($id2, $tokens[0]['idusertokenauth']);
|
|
$this->assertEquals($id3, $tokens[1]['idusertokenauth']);
|
|
$this->assertCount(2, $tokens);
|
|
|
|
$tokens = $this->model->getAllNonSystemTokensForLogin($this->login2);
|
|
$this->assertEquals($id4, $tokens[0]['idusertokenauth']);
|
|
$this->assertEquals($id5, $tokens[1]['idusertokenauth']);
|
|
$this->assertCount(2, $tokens);
|
|
}
|
|
}
|