diff options
36 files changed, 512 insertions, 144 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4b22af65..0acf77785c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The Product Changelog at **[piwik.org/changelog](http://piwik.org/changelog)** l Read more about migrating a plugin from Piwik 2.X to Piwik 3 in [our Migration guide](http://developer.piwik.org/guides/migrate-piwik-2-to-3). ### Breaking Changes +* The UserManager API methods do no longer return any `token_auth` properties when requesting a user * Reporting API: if you call the Reporting API requesting data in `json` format then you may be affected. The `json` formatting has changed, a bug was fixed so that API methods that return simple associative arrays like `array('name' => 'value', 'name2' => 'value2')` will now appear correctly as `{"name":"value","name2":"value2"}` in JSON API output instead of `[{"name":"value","name2":"value2"}]` (as it used to be in Piwik 2). API methods like **SitesManager.getSiteFromId** & **UsersManager.getUser** and others are affected. * The menu classes `Piwik\Menu\MenuReporting` and `Piwik\Menu\MenuMain` have been removed * The class `Piwik\Plugin\Widgets` has been removed and replaced by `Piwik\Widget\Widget`. For each widget one class is needed from now on. You can generate a widget via `./console generate:widget`. diff --git a/core/Auth/Password.php b/core/Auth/Password.php new file mode 100644 index 0000000000..0767fad7ce --- /dev/null +++ b/core/Auth/Password.php @@ -0,0 +1,69 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Auth; + +/** + * Main class to handle actions related to password hashing and verification. + * + * @api + */ +class Password +{ + /** + * Hashes a password with the configured algorithm. + * + * @param string $password + * @return string + */ + public function hash($password) + { + return password_hash($password, PASSWORD_BCRYPT); + } + + /** + * Returns information about a hashed password (algo, options, ...). + * + * Can be used to verify whether a string is compatible with password_hash(). + * + * @param string + * @return array + */ + public function info($hash) + { + return password_get_info($hash); + } + + /** + * Rehashes a user's password if necessary. + * + * This method expects the password to be pre-hashed by + * \Piwik\Plugins\UsersManager\UsersManager::getPasswordHash(). + * + * @param string $hash + * @return boolean + */ + public function needsRehash($hash) + { + return password_needs_rehash($hash, PASSWORD_BCRYPT); + } + + /** + * Verifies a user's password against the provided hash. + * + * This method expects the password to be pre-hashed by + * \Piwik\Plugins\UsersManager\UsersManager::getPasswordHash(). + * + * @param string $password + * @param string $hash + * @return boolean + */ + public function verify($password, $hash) + { + return password_verify($password, $hash); + } +} diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 146ee77068..7a65c23d2c 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -35,7 +35,7 @@ class Mysql implements SchemaInterface $tables = array( 'user' => "CREATE TABLE {$prefixTables}user ( login VARCHAR(100) NOT NULL, - password CHAR(32) NOT NULL, + password VARCHAR(255) NOT NULL, alias VARCHAR(45) NOT NULL, email VARCHAR(100) NOT NULL, token_auth CHAR(32) NOT NULL, diff --git a/core/Updates/3.0.0-b4.php b/core/Updates/3.0.0-b4.php new file mode 100644 index 0000000000..c45e19e1c9 --- /dev/null +++ b/core/Updates/3.0.0-b4.php @@ -0,0 +1,96 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Updates; + +use Piwik\Common; +use Piwik\Db; +use Piwik\Updater; +use Piwik\Updater\Migration; +use Piwik\Updater\Migration\Factory as MigrationFactory; +use Piwik\Updates; + +class Updates_3_0_0_b4 extends Updates +{ + /** + * @var MigrationFactory + */ + private $migration; + + /** + * @var string + */ + private $userTable = 'user'; + + public function __construct(MigrationFactory $factory) + { + $this->migration = $factory; + } + + /** + * Here you can define one or multiple SQL statements that should be executed during the update. + * @param Updater $updater + * @return Migration[] + */ + public function getMigrations(Updater $updater) + { + $migrations = []; + $migrations = $this->getUserDatabaseMigrations($migrations); + + return $migrations; + } + + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + $updater->executeMigrations(__FILE__, $this->getUserPasswordMigrations([])); + } + + /** + * Returns database migrations for this update. + * @param Migration[] $queries + * @return Migration[] + */ + private function getUserDatabaseMigrations($queries) + { + $queries[] = $this->migration->db->changeColumn($this->userTable, 'password', 'password', 'VARCHAR(255) NOT NULL'); + + return $queries; + } + + /** + * Returns migrations to hash existing password with bcrypt. + * @param Migration[] $queries + * @return Migration[] + */ + private function getUserPasswordMigrations($queries) + { + $db = Db::get(); + $userTable = Common::prefixTable($this->userTable); + + $users = $db->fetchAll( + 'SELECT `login`, `password` FROM `' . $userTable . '` WHERE LENGTH(`password`) = 32' + ); + + foreach ($users as $user) { + $queries[] = $this->migration->db->boundSql( + 'UPDATE `' . $userTable . '`' + . ' SET `password` = ?' + . ' WHERE `login` = ?', + [ + password_hash($user['password'], PASSWORD_BCRYPT), + $user['login'], + ] + ); + } + + return $queries; + } +} diff --git a/core/Version.php b/core/Version.php index d70cd0b1f2..993e7cd131 100644 --- a/core/Version.php +++ b/core/Version.php @@ -20,7 +20,7 @@ final class Version * The current Piwik version. * @var string */ - const VERSION = '3.0.0-b3'; + const VERSION = '3.0.0-b4'; public function isStableVersion($version) { diff --git a/plugins/API/lang/en.json b/plugins/API/lang/en.json index 95152566a2..1697353df0 100644 --- a/plugins/API/lang/en.json +++ b/plugins/API/lang/en.json @@ -1,5 +1,6 @@ { "API": { + "ChangeTokenHint": "If you want to change this token, please go to your %1$spersonal settings page%2$s.", "GenerateVisits": "If you don't have data for today you can first generate some data using the %1$s plugin. You can enable the %2$s plugin, then click on the 'Visitor Generator' menu in the Piwik Admin area.", "KeepTokenSecret": "This token_auth is as secret as your login and password, %1$s do not share it%2$s!", "LoadedAPIs": "Successfully loaded %s APIs", diff --git a/plugins/API/templates/listAllAPI.twig b/plugins/API/templates/listAllAPI.twig index 267e365a2a..258b74c49d 100644 --- a/plugins/API/templates/listAllAPI.twig +++ b/plugins/API/templates/listAllAPI.twig @@ -21,7 +21,11 @@ <p> {{ 'API_UsingTokenAuth'|translate('','',"")|raw }}<br/> <pre piwik-select-on-focus id='token_auth'>&token_auth=<strong>{{ token_auth }}</strong></pre><br/> - {{ 'API_KeepTokenSecret'|translate('<b>','</b>')|raw }} + {{ 'API_KeepTokenSecret'|translate('<b>','</b>')|raw }}<br /> + {{ 'API_ChangeTokenHint'|translate('<a href="' ~ linkTo({ + 'module': 'UsersManager', + 'action': 'userSettings', + }) ~ '">', '</a>')|raw }} </p> </div> {{ list_api_methods_with_links|raw }} diff --git a/plugins/BulkTracking/tests/Integration/RequestsTest.php b/plugins/BulkTracking/tests/Integration/RequestsTest.php index ab45204303..7f6236d82c 100644 --- a/plugins/BulkTracking/tests/Integration/RequestsTest.php +++ b/plugins/BulkTracking/tests/Integration/RequestsTest.php @@ -89,7 +89,7 @@ class RequestsTest extends IntegrationTestCase */ public function test_authenticateRequests_shouldThrowAnException_IfTokenIsNotValid() { - $dummyToken = API::getInstance()->getTokenAuth('test', UsersManager::getPasswordHash('2')); + $dummyToken = API::getInstance()->createTokenAuth('test'); $superUserToken = $this->getSuperUserToken(); $requests = array($this->buildDummyRequest($superUserToken), $this->buildDummyRequest($dummyToken)); diff --git a/plugins/Login/Auth.php b/plugins/Login/Auth.php index 2de99c6934..08bb693108 100644 --- a/plugins/Login/Auth.php +++ b/plugins/Login/Auth.php @@ -8,13 +8,11 @@ */ namespace Piwik\Plugins\Login; -use Exception; use Piwik\AuthResult; -use Piwik\Db; +use Piwik\Auth\Password; use Piwik\Piwik; use Piwik\Plugins\UsersManager\Model; use Piwik\Plugins\UsersManager\UsersManager; -use Piwik\Session; class Auth implements \Piwik\Auth { @@ -27,9 +25,15 @@ class Auth implements \Piwik\Auth */ private $userModel; + /** + * @var Password + */ + private $passwordHelper; + public function __construct() { - $this->userModel = new Model(); + $this->userModel = new Model(); + $this->passwordHelper = new Password(); } /** @@ -49,7 +53,7 @@ class Auth implements \Piwik\Auth */ public function authenticate() { - if (!empty($this->hashedPassword)) { // favor authenticating by password + if (!empty($this->hashedPassword)) { return $this->authenticateWithPassword($this->login, $this->getTokenAuthSecret()); } elseif (is_null($this->login)) { return $this->authenticateWithToken($this->token_auth); @@ -64,7 +68,17 @@ class Auth implements \Piwik\Auth { $user = $this->userModel->getUser($login); - if (!empty($user['login']) && $user['password'] === $passwordHash) { + if (empty($user['login'])) { + return new AuthResult(AuthResult::FAILURE, $login, null); + } + + if ($this->passwordHelper->verify($passwordHash, $user['password'])) { + if ($this->passwordHelper->needsRehash($user['password'])) { + $newPasswordHash = $this->passwordHelper->hash($passwordHash); + + $this->userModel->updateUser($login, $newPasswordHash, $user['email'], $user['alias'], $user['token_auth']); + } + return $this->authenticationSuccess($user); } diff --git a/plugins/Login/PasswordResetter.php b/plugins/Login/PasswordResetter.php index ebbc7577cd..a9190ed659 100644 --- a/plugins/Login/PasswordResetter.php +++ b/plugins/Login/PasswordResetter.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\Login; use Exception; use Piwik\Access; +use Piwik\Auth\Password; use Piwik\Common; use Piwik\Config; use Piwik\IP; @@ -60,6 +61,11 @@ use Piwik\Url; class PasswordResetter { /** + * @var Password + */ + protected $passwordHelper; + + /** * @var UsersManagerAPI */ protected $usersManagerApi; @@ -104,9 +110,10 @@ class PasswordResetter * @param string|null $confirmPasswordAction * @param string|null $emailFromName * @param string|null $emailFromAddress + * @param Password $passwordHelper */ public function __construct($usersManagerApi = null, $confirmPasswordModule = null, $confirmPasswordAction = null, - $emailFromName = null, $emailFromAddress = null) + $emailFromName = null, $emailFromAddress = null, $passwordHelper = null) { if (empty($usersManagerApi)) { $usersManagerApi = UsersManagerAPI::getInstance(); @@ -130,6 +137,11 @@ class PasswordResetter $emailFromAddress = Config::getInstance()->General['login_password_recovery_email_address']; } $this->emailFromAddress = $emailFromAddress; + + if (empty($passwordHelper)) { + $passwordHelper = new Password(); + } + $this->passwordHelper = $passwordHelper; } /** @@ -383,7 +395,11 @@ class PasswordResetter */ protected function checkPasswordHash($passwordHash) { - UsersManager::checkPasswordHash($passwordHash, Piwik::translate('Login_ExceptionPasswordMD5HashExpected')); + $hashInfo = $this->passwordHelper->info($passwordHash); + + if (!isset($hashInfo['algo']) || 0 >= $hashInfo['algo']) { + throw new Exception(Piwik::translate('Login_ExceptionPasswordMD5HashExpected')); + } } /** @@ -436,7 +452,7 @@ class PasswordResetter private function savePasswordResetInfo($login, $newPassword) { $optionName = $this->getPasswordResetInfoOptionName($login); - $optionData = UsersManager::getPasswordHash($newPassword); + $optionData = $this->passwordHelper->hash(UsersManager::getPasswordHash($newPassword)); Option::set($optionName, $optionData); } diff --git a/plugins/Login/tests/Integration/LoginTest.php b/plugins/Login/tests/Integration/LoginTest.php index f199ae7069..4aac46dbc1 100644 --- a/plugins/Login/tests/Integration/LoginTest.php +++ b/plugins/Login/tests/Integration/LoginTest.php @@ -433,7 +433,10 @@ class LoginTest extends IntegrationTestCase API::getInstance()->addUser($user['login'], $user['password'], $user['email'], $user['alias']); - $user['tokenAuth'] = API::getInstance()->getTokenAuth($user['login'], md5($user['password'])); + $model = new \Piwik\Plugins\UsersManager\Model(); + $dbUser = $model->getUser($user['login']); + + $user['tokenAuth'] = $dbUser['token_auth']; return $user; } diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php index c1f5965f96..ae9189e653 100644 --- a/plugins/UsersManager/API.php +++ b/plugins/UsersManager/API.php @@ -10,12 +10,14 @@ namespace Piwik\Plugins\UsersManager; use Exception; use Piwik\Access; +use Piwik\Auth\Password; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Date; use Piwik\Option; use Piwik\Piwik; +use Piwik\SettingsPiwik; use Piwik\Site; use Piwik\Tracker\Cache; @@ -41,6 +43,11 @@ class API extends \Piwik\Plugin\API private $model; /** + * @var Password + */ + private $password; + + /** * @var UserAccessFilter */ private $userFilter; @@ -50,10 +57,11 @@ class API extends \Piwik\Plugin\API private static $instance = null; - public function __construct(Model $model, UserAccessFilter $filter) + public function __construct(Model $model, UserAccessFilter $filter, Password $password) { $this->model = $model; $this->userFilter = $filter; + $this->password = $password; } /** @@ -208,6 +216,7 @@ class API extends \Piwik\Plugin\API $users = $this->model->getUsers($logins); $users = $this->userFilter->filterUsers($users); + $users = $this->enrichUsers($users); // Non Super user can only access login & alias if (!Piwik::hasUserSuperUserAccess()) { @@ -355,7 +364,10 @@ class API extends \Piwik\Plugin\API $user = $this->model->getUser($userLogin); - return $this->userFilter->filterUser($user); + $user = $this->userFilter->filterUser($user); + $user = $this->enrichUser($user); + + return $user; } /** @@ -372,7 +384,10 @@ class API extends \Piwik\Plugin\API $user = $this->model->getUserByEmail($userEmail); - return $this->userFilter->filterUser($user); + $user = $this->userFilter->filterUser($user); + $user = $this->enrichUser($user); + + return $user; } private function checkLogin($userLogin) @@ -436,9 +451,9 @@ class API extends \Piwik\Plugin\API $passwordTransformed = $password; } - $alias = $this->getCleanAlias($alias, $userLogin); - - $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed); + $alias = $this->getCleanAlias($alias, $userLogin); + $passwordTransformed = $this->password->hash($passwordTransformed); + $token_auth = $this->createTokenAuth($userLogin); $this->model->addUser($userLogin, $passwordTransformed, $email, $alias, $token_auth, Date::now()->getDatetime()); @@ -500,11 +515,7 @@ class API extends \Piwik\Plugin\API Piwik::checkUserIsNotAnonymous(); $users = $this->model->getUsersHavingSuperUserAccess(); - - foreach($users as &$user) { - // remove token_auth in API response - unset($user['token_auth']); - } + $users = $this->enrichUsers($users); // we do not filter these users by access and return them all since we need to print this information in the // UI and they are allowed to see this. @@ -512,11 +523,52 @@ class API extends \Piwik\Plugin\API return $users; } + private function enrichUsers($users) + { + if (!empty($users)) { + foreach ($users as $index => $user) { + $users[$index] = $this->enrichUser($user); + } + } + return $users; + } + + private function enrichUser($user) + { + if (!empty($user)) { + unset($user['token_auth']); + } + + return $user; + } + + /** + * Regenerate the token_auth associated with a user. + * + * If the user currently logged in regenerates his own token, he will be logged out. + * His previous token will be rendered invalid. + * + * @param string $userLogin + * @throws Exception + */ + public function regenerateTokenAuth($userLogin) + { + $this->checkUserIsNotAnonymous($userLogin); + + Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin); + + $this->model->updateUserTokenAuth( + $userLogin, + $this->createTokenAuth($userLogin) + ); + } + /** * Updates a user in the database. * Only login and password are required (case when we update the password). - * When the password changes, the key token for this user will change, which could break - * its API calls. + * + * If the password changes and the user has an old token_auth (legacy MD5 format) associated, + * the token will be regenerated. This could break a user's API calls. * * @see addUser() for all the parameters */ @@ -525,18 +577,30 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin); $this->checkUserIsNotAnonymous($userLogin); - $userInfo = $this->getUser($userLogin); + $this->checkUserExists($userLogin); + + $userInfo = $this->model->getUser($userLogin); + $token_auth = $userInfo['token_auth']; + $passwordHasBeenUpdated = false; if (empty($password)) { $password = $userInfo['password']; } else { $password = Common::unsanitizeInputValue($password); + if (!$_isPasswordHashed) { UsersManager::checkPassword($password); $password = UsersManager::getPasswordHash($password); } + $passwordInfo = $this->password->info($password); + + if (!isset($passwordInfo['algo']) || 0 >= $passwordInfo['algo']) { + // password may have already been fully hashed + $password = $this->password->hash($password); + } + $passwordHasBeenUpdated = true; } @@ -552,8 +616,7 @@ class API extends \Piwik\Plugin\API $this->checkEmail($email); } - $alias = $this->getCleanAlias($alias, $userLogin); - $token_auth = $this->getTokenAuth($userLogin, $password); + $alias = $this->getCleanAlias($alias, $userLogin); $this->model->updateUser($userLogin, $password, $email, $alias, $token_auth); @@ -780,7 +843,20 @@ class API extends \Piwik\Plugin\API } /** - * Generates a unique MD5 for the given login & password + * Generates a new random authentication token. + * + * @param string $userLogin Login + * @return string + */ + public function createTokenAuth($userLogin) + { + return md5($userLogin . microtime(true) . Common::generateUniqId() . SettingsPiwik::getSalt()); + } + + /** + * Returns the user's API token. + * + * If the username/password combination is incorrect an invalid token will be returned. * * @param string $userLogin Login * @param string $md5Password hashed string of the password (using current hash function; MD5-named for historical reasons) @@ -790,6 +866,16 @@ class API extends \Piwik\Plugin\API { UsersManager::checkPasswordHash($md5Password, Piwik::translate('UsersManager_ExceptionPasswordMD5HashExpected')); - return md5($userLogin . $md5Password); + $user = $this->model->getUser($userLogin); + + if (!$this->password->verify($md5Password, $user['password'])) { + return md5($userLogin . microtime(true) . Common::generateUniqId()); + } + + if ($this->password->needsRehash($user['password'])) { + $this->updateUser($userLogin, $this->password->hash($md5Password)); + } + + return $user['token_auth']; } } diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php index 189b6f58dd..0be04abda4 100644 --- a/plugins/UsersManager/Controller.php +++ b/plugins/UsersManager/Controller.php @@ -240,6 +240,7 @@ class Controller extends ControllerAdmin $user = Request::processRequest('UsersManager.getUser', array('userLogin' => $userLogin)); $view->userAlias = $user['alias']; $view->userEmail = $user['email']; + $view->userTokenAuth = Piwik::getCurrentUserTokenAuth(); $view->ignoreSalt = $this->getIgnoreCookieSalt(); diff --git a/plugins/UsersManager/Model.php b/plugins/UsersManager/Model.php index db0285eab6..a45aebaf2d 100644 --- a/plugins/UsersManager/Model.php +++ b/plugins/UsersManager/Model.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\UsersManager; +use Piwik\Auth\Password; use Piwik\Common; use Piwik\Db; use Piwik\Piwik; @@ -29,8 +30,14 @@ class Model private static $rawPrefix = 'user'; private $table; + /** + * @var Password + */ + private $passwordHelper; + public function __construct() { + $this->passwordHelper = new Password(); $this->table = Common::prefixTable(self::$rawPrefix); } @@ -183,11 +190,11 @@ class Model return $db->fetchRow('SELECT * FROM ' . $this->table . ' WHERE token_auth = ?', $tokenAuth); } - public function addUser($userLogin, $passwordTransformed, $email, $alias, $tokenAuth, $dateRegistered) + public function addUser($userLogin, $hashedPassword, $email, $alias, $tokenAuth, $dateRegistered) { $user = array( 'login' => $userLogin, - 'password' => $passwordTransformed, + 'password' => $hashedPassword, 'alias' => $alias, 'email' => $email, 'token_auth' => $tokenAuth, @@ -238,13 +245,20 @@ class Model return $users; } - public function updateUser($userLogin, $password, $email, $alias, $tokenAuth) + public function updateUser($userLogin, $hashedPassword, $email, $alias, $tokenAuth) + { + $this->updateUserFields($userLogin, array( + 'password' => $hashedPassword, + 'alias' => $alias, + 'email' => $email, + 'token_auth' => $tokenAuth + )); + } + + public function updateUserTokenAuth($userLogin, $tokenAuth) { $this->updateUserFields($userLogin, array( - 'password' => $password, - 'alias' => $alias, - 'email' => $email, - 'token_auth' => $tokenAuth + 'token_auth' => $tokenAuth )); } diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php index b8840276d4..e4c3673fa4 100644 --- a/plugins/UsersManager/UsersManager.php +++ b/plugins/UsersManager/UsersManager.php @@ -9,7 +9,6 @@ namespace Piwik\Plugins\UsersManager; use Exception; -use Piwik\Db; use Piwik\Option; use Piwik\Piwik; use Piwik\SettingsPiwik; @@ -23,7 +22,7 @@ class UsersManager extends \Piwik\Plugin const PASSWORD_MIN_LENGTH = 6; /** - * @see Piwik\Plugin::registerEvents + * @see \Piwik\Plugin::registerEvents */ public function registerEvents() { @@ -56,7 +55,14 @@ class UsersManager extends \Piwik\Plugin public function recordAdminUsersInCache(&$attributes, $idSite) { // add the 'hosts' entry in the website array - $users = API::getInstance()->getUsersWithSiteAccess($idSite, 'admin'); + $model = new Model(); + $logins = $model->getUsersLoginWithSiteAccess($idSite, 'admin'); + + if (empty($logins)) { + return; + } + + $users = $model->getUsers($logins); $tokens = array(); foreach ($users as $user) { @@ -87,7 +93,7 @@ class UsersManager extends \Piwik\Plugin /** * Return list of plug-in specific JavaScript files to be imported by the asset manager * - * @see Piwik\AssetManager + * @see \Piwik\AssetManager */ public function getJsFiles(&$jsFiles) { diff --git a/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js b/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js index 6e561a6ea5..621113355c 100644 --- a/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js +++ b/plugins/UsersManager/angularjs/manage-users/manage-users.controller.js @@ -124,14 +124,9 @@ }) ).prepend($('<input type="submit" class="btn updateuser" value="' + _pk_translate('General_Save') + '" />') .click(function () { - var onValidate = function () { - sendUpdateUserAJAX($('tr#' + idRow)); - }; - if ($('tr#' + idRow).find('input#password').val() != '-') { - piwikHelper.modalConfirm('#confirmPasswordChange', {yes: onValidate}); - } else { - onValidate(); - } + var $tr = $('tr#' + idRow); + + sendUpdateUserAJAX($tr); }) ); } @@ -176,6 +171,29 @@ }}); }; + this.regenerateUserTokenAuth = function (userLogin) { + var parameters = { userLogin: userLogin }; + var confirm = '#confirmTokenRegenerate'; + + if (userLogin == piwik.userLogin) { + confirm = '#confirmTokenRegenerateSelf'; + } + + piwikHelper.modalConfirm(confirm, {yes: function () { + setIsLoading(); + + piwikApi.post({ + module: 'API', + method: 'UsersManager.regenerateTokenAuth' + }, parameters).then(function () { + piwik.helper.redirect(); + self.isLoading = false; + }, function () { + self.isLoading = false; + }); + }}); + }; + $(document).ready(function () { var alreadyEdited = []; // when click on edituser, the cells become editable @@ -183,8 +201,9 @@ // Show the token_auth $('.token_auth').click(function () { var token = $(this).data('token'); - if ($(this).text() != token) { - $(this).text(token); + + if ($('.token_auth_content', this).text() != token) { + $('.token_auth_content', this).text(token); } }); }); diff --git a/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js b/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js index a14c47390a..811fd9466e 100644 --- a/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js +++ b/plugins/UsersManager/angularjs/personal-settings/personal-settings.controller.js @@ -7,13 +7,13 @@ (function () { angular.module('piwikApp').controller('PersonalSettingsController', PersonalSettingsController); - PersonalSettingsController.$inject = ['piwikApi']; + PersonalSettingsController.$inject = ['piwikApi', '$window']; - function PersonalSettingsController(piwikApi) { + function PersonalSettingsController(piwikApi, $window) { // remember to keep controller very simple. Create a service/factory (model) if needed var self = this; - + function updateSettings(postParams) { self.loading = true; @@ -34,6 +34,26 @@ }); } + this.regenerateTokenAuth = function () { + var parameters = { userLogin: piwik.userLogin }; + + self.loading = true; + + piwikHelper.modalConfirm('#confirmTokenRegenerate', {yes: function () { + piwikApi.withTokenInUrl(); + piwikApi.post({ + module: 'API', + method: 'UsersManager.regenerateTokenAuth' + }, parameters).then(function (success) { + $window.location.reload(); + + self.loading = false; + }, function (errorMessage) { + self.loading = false; + }); + }}); + }; + this.save = function () { var postParams = { @@ -49,16 +69,7 @@ postParams.passwordBis = this.passwordBis; } - if (this.password) { - postParams.password = this.password; - - piwikHelper.modalConfirm('#confirmPasswordChange', {yes: function () { - updateSettings(postParams); - }}); - } else { - updateSettings(postParams); - } - + updateSettings(postParams); }; } })();
\ No newline at end of file diff --git a/plugins/UsersManager/lang/en.json b/plugins/UsersManager/lang/en.json index 6c9f8cb170..3fdedcc01f 100644 --- a/plugins/UsersManager/lang/en.json +++ b/plugins/UsersManager/lang/en.json @@ -8,7 +8,6 @@ "AnonymousUserHasViewAccess2": "Your analytics reports and your visitors information are publicly viewable.", "ApplyToAllWebsites": "Apply to all websites", "ChangeAllConfirm": "Are you sure you want to give '%s' access to all the websites?", - "ChangePasswordConfirm": "Changing the password will also change the user's token_auth. Do you really want to continue?", "ClickHereToDeleteTheCookie": "Click here to delete the cookie and have Piwik track your visits", "ClickHereToSetTheCookieOnDomain": "Click here to set a cookie that will exclude your visits on websites tracked by Piwik on %s", "ConfirmGrantSuperUserAccess": "Do you really want to grant '%s' Super User access? Warning: the user will have access to all websites and will be able to perform administrative tasks.", @@ -63,6 +62,11 @@ "SuperUserAccessManagementMainDescription": "Super users have the highest permissions. They can perform all administrative tasks such as adding new websites to monitor, adding users, changing user permissions, activating and deactivating plugins and even installing new plugins from the Marketplace.", "TheLoginScreen": "The login screen", "ThereAreCurrentlyNRegisteredUsers": "There are currently %s registered users.", + "TokenAuth": "API Authentication Token", + "TokenRegenerateConfirm": "Changing the API authentication token will invalidate the user's current token. If the user is currently logged in, he may be required to log in again. Do you really want to continue?", + "TokenRegenerateConfirmSelf": "Changing the API authentication token will invalidate your own token. You may be required to log in again if you proceed. Do you really want to change your authentication token?", + "TokenRegenerateLogoutWarning": "When you re-generate your token you will be logged out and need to log in again.", + "TokenRegenerateTitle": "Regenerate", "TypeYourPasswordAgain": "Type your new password again.", "User": "User", "UsersManagement": "Users Management", diff --git a/plugins/UsersManager/stylesheets/usersManager.less b/plugins/UsersManager/stylesheets/usersManager.less index 90355b5b4f..f4b22ced4a 100644 --- a/plugins/UsersManager/stylesheets/usersManager.less +++ b/plugins/UsersManager/stylesheets/usersManager.less @@ -26,10 +26,6 @@ } } -#users #token_auth_user { - border-color: @theme-color-background-tinyContrast; -} - #users .canceluser { margin-top: 10px; margin-bottom: 10px; diff --git a/plugins/UsersManager/templates/index.twig b/plugins/UsersManager/templates/index.twig index b4f75df6cc..4b232c63e5 100644 --- a/plugins/UsersManager/templates/index.twig +++ b/plugins/UsersManager/templates/index.twig @@ -154,11 +154,18 @@ <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/> <input role="no" type="button" value="{{ 'General_No'|translate }}"/> </div> - <div class="ui-confirm" id="confirmPasswordChange"> - <h2>{{ 'UsersManager_ChangePasswordConfirm'|translate }}</h2> + + <div class="ui-confirm" id="confirmTokenRegenerate"> + <h2>{{ 'UsersManager_TokenRegenerateConfirm'|translate }}</h2> <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/> <input role="no" type="button" value="{{ 'General_No'|translate }}"/> </div> + <div class="ui-confirm" id="confirmTokenRegenerateSelf"> + <h2>{{ 'UsersManager_TokenRegenerateConfirmSelf'|translate }}</h2> + <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/> + <input role="no" type="button" value="{{ 'General_No'|translate }}"/> + </div> + <br/> <p>{{ 'UsersManager_UsersManagementMainDescription'|translate }} {{ 'UsersManager_ThereAreCurrentlyNRegisteredUsers'|translate("<b>"~usersCount~"</b>")|raw }}</p> @@ -174,7 +181,6 @@ <th>{{ 'General_Password'|translate }}</th> <th>{{ 'UsersManager_Email'|translate }}</th> <th>{{ 'UsersManager_Alias'|translate }}</th> - <th>token_auth</th> {% if showLastSeen is defined and showLastSeen %} <th>{{ 'UsersManager_LastSeen'|translate }}</th> {% endif %} @@ -191,7 +197,6 @@ <td id="password" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>-</td> <td id="email" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>{{ user.email }}</td> <td id="alias" class="editable" ng-click='manageUsers.editUser("row{{ i|e('js') }}")'>{{ user.alias|raw }}</td> - <td id="token_auth_user" class="token_auth" data-token="{{ user.token_auth }}">{{ user.token_auth|slice(0, 8) }}…</td> {% if user.last_seen is defined %} <td id="last_seen">{% if user.last_seen is empty %}-{% else %}{{ 'General_TimeAgo'|translate(user.last_seen)|raw }}{% endif %}</td> {% endif %} diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig index 53f46d20e6..2824262acd 100644 --- a/plugins/UsersManager/templates/userSettings.twig +++ b/plugins/UsersManager/templates/userSettings.twig @@ -4,8 +4,8 @@ {% block content %} -<div class="ui-confirm" id="confirmPasswordChange"> - <h2>{{ 'UsersManager_ChangePasswordConfirm'|translate }}</h2> +<div class="ui-confirm" id="confirmTokenRegenerate"> + <h2>{{ 'UsersManager_TokenRegenerateConfirmSelf'|translate }}</h2> <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/> <input role="no" type="button" value="{{ 'General_No'|translate }}"/> </div> @@ -107,6 +107,16 @@ </form> </div> +<div piwik-content-block + content-title="{{ 'UsersManager_TokenAuth'|translate|e('html_attr') }}"> + <pre piwik-select-on-focus id="token_auth_user">{{ userTokenAuth }}</pre> + + <p>{{ 'UsersManager_TokenRegenerateLogoutWarning'|translate }}</p> + <button class="btn btn-link" + ng-controller="PersonalSettingsController as personalSettings" + ng-click="personalSettings.regenerateTokenAuth()">{{ 'UsersManager_TokenRegenerateTitle'|translate }}</button> +</div> + <div piwik-plugin-settings mode="user"></div> <div piwik-content-block diff --git a/plugins/UsersManager/tests/Fixtures/ManyUsers.php b/plugins/UsersManager/tests/Fixtures/ManyUsers.php index b9c6821f67..b2be04550d 100644 --- a/plugins/UsersManager/tests/Fixtures/ManyUsers.php +++ b/plugins/UsersManager/tests/Fixtures/ManyUsers.php @@ -8,6 +8,7 @@ namespace Piwik\Plugins\UsersManager\tests\Fixtures; use Piwik\Plugins\UsersManager\API; +use Piwik\Plugins\UsersManager\Model; use Piwik\Tests\Framework\Fixture; /** @@ -51,6 +52,7 @@ class ManyUsers extends Fixture protected function setUpUsers() { + $model = new Model(); $api = API::getInstance(); foreach ($this->users as $login => $permissions) { $api->addUser($login, 'password', $login . '@example.com'); @@ -59,7 +61,8 @@ class ManyUsers extends Fixture $api->setUserAccess($login, $access, $idSites); } } - $user = $api->getUser($login); + + $user = $model->getUser($login); $this->users[$login]['token'] = $user['token_auth']; } diff --git a/plugins/UsersManager/tests/Integration/APITest.php b/plugins/UsersManager/tests/Integration/APITest.php index 892deedc58..91f8e0f49f 100644 --- a/plugins/UsersManager/tests/Integration/APITest.php +++ b/plugins/UsersManager/tests/Integration/APITest.php @@ -8,9 +8,11 @@ namespace Piwik\Plugins\UsersManager\tests; +use Piwik\Auth\Password; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugins\UsersManager\API; +use Piwik\Plugins\UsersManager\UsersManager; use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\Mock\FakeAccess; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -184,9 +186,12 @@ class APITest extends IntegrationTestCase $user = $this->api->getUser($this->login); - $this->assertSame('14a88b9d2f52c55b5fbcf9c5d9c11875', $user['password']); $this->assertSame('email@example.com', $user['email']); $this->assertSame('newAlias', $user['alias']); + + $passwordHelper = new Password(); + + $this->assertTrue($passwordHelper->verify(UsersManager::getPasswordHash('newPassword'), $user['password'])); } public function test_getSitesAccessFromUser_forSuperUser() diff --git a/plugins/UsersManager/tests/Integration/UsersManagerTest.php b/plugins/UsersManager/tests/Integration/UsersManagerTest.php index 0d24d5f5b2..4a849cb155 100644 --- a/plugins/UsersManager/tests/Integration/UsersManagerTest.php +++ b/plugins/UsersManager/tests/Integration/UsersManagerTest.php @@ -9,9 +9,11 @@ namespace Piwik\Plugins\UsersManager\tests\Integration; use Piwik\Access; +use Piwik\Auth\Password; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Plugins\UsersManager\API; use Piwik\Plugins\UsersManager\Model; +use Piwik\Plugins\UsersManager\UsersManager; use Piwik\Tests\Framework\Mock\FakeAccess; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; use Exception; @@ -74,14 +76,20 @@ class UsersManagerTest extends IntegrationTestCase } $userAfter = $this->api->getUser($user["login"]); unset($userAfter['date_registered']); + unset($userAfter['password']); + + // implicitly checks password! + $userModel = $this->model->getUser($user['login']); + $userAfter['token_auth'] = $userModel['token_auth']; - // we now compute what the token auth should be, it should always be a hash of the login and the current password - // if the password has changed then the token_auth has changed! $user['token_auth'] = $this->api->getTokenAuth($user["login"], md5($newPassword)); - $user['password'] = md5($newPassword); - $user['email'] = $newEmail; - $user['alias'] = $newAlias; + + $user['email'] = $newEmail; + $user['alias'] = $newAlias; $user['superuser_access'] = 0; + + unset($user['password']); + $this->assertEquals($user, $userAfter); } @@ -238,17 +246,23 @@ class UsersManagerTest extends IntegrationTestCase "the date_registered " . strtotime($user['date_registered']) . " is different from the time() " . time()); $this->assertTrue($user['date_registered'] <= time()); - // check that token is 32 chars - $this->assertEquals(32, strlen($user['password'])); + // check that password and token are properly set + $this->assertEquals(60, strlen($user['password'])); - // that the password has been md5 - $this->assertEquals(md5($login . md5($password)), $user['token_auth']); + $userModel = $this->model->getUser($login); + $this->assertEquals(32, strlen($userModel['token_auth'])); + + $userModel = $this->model->getUser($login); + $this->assertEquals($userModel['token_auth'], $this->api->getTokenAuth($login, UsersManager::getPasswordHash($password))); // check that all fields are the same $this->assertEquals($login, $user['login']); - $this->assertEquals(md5($password), $user['password']); $this->assertEquals($email, $user['email']); $this->assertEquals($alias, $user['alias']); + + $passwordHelper = new Password(); + + $this->assertTrue($passwordHelper->verify(UsersManager::getPasswordHash($password), $user['password'])); } /** @@ -381,9 +395,9 @@ class UsersManagerTest extends IntegrationTestCase $users = $this->api->getUsers(); $users = $this->_removeNonTestableFieldsFromUsers($users); - $user1 = array('login' => "gegg4564eqgeqag", 'password' => md5("geqgegagae"), 'alias' => "alias", 'email' => "tegst@tesgt.com", 'superuser_access' => 0); - $user2 = array('login' => "geggeqge632ge56a4qag", 'password' => md5("geqgegeagae"), 'alias' => "alias", 'email' => "tesggt@tesgt.com", 'superuser_access' => 0); - $user3 = array('login' => "geggeqgeqagqegg", 'password' => md5("geqgeaggggae"), 'alias' => 'geggeqgeqagqegg', 'email' => "tesgggt@tesgt.com", 'superuser_access' => 0); + $user1 = array('login' => "gegg4564eqgeqag", 'alias' => "alias", 'email' => "tegst@tesgt.com", 'superuser_access' => 0); + $user2 = array('login' => "geggeqge632ge56a4qag", 'alias' => "alias", 'email' => "tesggt@tesgt.com", 'superuser_access' => 0); + $user3 = array('login' => "geggeqgeqagqegg", 'alias' => 'geggeqgeqagqegg', 'email' => "tesgggt@tesgt.com", 'superuser_access' => 0); $expectedUsers = array($user1, $user2, $user3); $this->assertEquals($expectedUsers, $users); $this->assertEquals(array($user1), $this->_removeNonTestableFieldsFromUsers($this->api->getUsers('gegg4564eqgeqag'))); @@ -408,6 +422,7 @@ class UsersManagerTest extends IntegrationTestCase protected function _removeNonTestableFieldsFromUsers($users) { foreach ($users as &$user) { + unset($user['password']); unset($user['token_auth']); unset($user['date_registered']); } diff --git a/plugins/UsersManager/tests/System/ApiTest.php b/plugins/UsersManager/tests/System/ApiTest.php index b72a956e10..4192b62d0c 100644 --- a/plugins/UsersManager/tests/System/ApiTest.php +++ b/plugins/UsersManager/tests/System/ApiTest.php @@ -38,8 +38,9 @@ class ApiTest extends SystemTestCase // login1 = super user, login2 = some admin access, login4 = only view access foreach ($logins as $login => $appendix) { $params['token_auth'] = self::$fixture->users[$login]['token']; + $xmlFieldsToRemove = array('date_registered', 'password', 'token_auth'); - $this->runAnyApiTest($api, $apiId . '_' . $appendix, $params, array('xmlFieldsToRemove' => array('date_registered'))); + $this->runAnyApiTest($api, $apiId . '_' . $appendix, $params, array('xmlFieldsToRemove' => $xmlFieldsToRemove)); } } diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login1_when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login1_when_superuseraccess.xml index b96941238e..e233776f69 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login1_when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login1_when_superuseraccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login1</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login1</alias> <email>login1@example.com</email> - <token_auth>367ea0b18ee1e641089e5d0a4d5f276d</token_auth> <superuser_access>1</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_adminaccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_adminaccess.xml index 3516ee8eab..7b9a2b37ca 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_adminaccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_adminaccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login2</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login2</alias> <email>login2@example.com</email> - <token_auth>ef3cb848005bffc2e2f3c8edbd95c58f</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_superuseraccess.xml index 3516ee8eab..7b9a2b37ca 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login2_when_superuseraccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login2</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login2</alias> <email>login2@example.com</email> - <token_auth>ef3cb848005bffc2e2f3c8edbd95c58f</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_superuseraccess.xml index 16caa1d378..28d1197732 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_superuseraccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login4</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login4</alias> <email>login4@example.com</email> - <token_auth>dc6fb0514c143d97c72b8be165e7ee0a</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_viewaccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_viewaccess.xml index 16caa1d378..28d1197732 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_viewaccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login4_when_viewaccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login4</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login4</alias> <email>login4@example.com</email> - <token_auth>dc6fb0514c143d97c72b8be165e7ee0a</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login6_when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login6_when_superuseraccess.xml index 2cd79e6cd4..ebf69209da 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login6_when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUser_login6_when_superuseraccess.xml @@ -2,10 +2,9 @@ <result> <row> <login>login6</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login6</alias> <email>login6@example.com</email> - <token_auth>2cafd6512d8b2739a7b2b01ab6609272</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsersWithSiteAccess_3_admin_when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsersWithSiteAccess_3_admin_when_superuseraccess.xml index 9af31e8a0c..aa93d367ad 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsersWithSiteAccess_3_admin_when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsersWithSiteAccess_3_admin_when_superuseraccess.xml @@ -2,19 +2,17 @@ <result> <row> <login>login5</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login5</alias> <email>login5@example.com</email> - <token_auth>4550293427ba5d0a0c96d6123429e9d3</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login6</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login6</alias> <email>login6@example.com</email> - <token_auth>2cafd6512d8b2739a7b2b01ab6609272</token_auth> <superuser_access>0</superuser_access> </row> diff --git a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsers__when_superuseraccess.xml b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsers__when_superuseraccess.xml index af284f900d..e8eb10f4b1 100644 --- a/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsers__when_superuseraccess.xml +++ b/plugins/UsersManager/tests/System/expected/test___UsersManager.getUsers__when_superuseraccess.xml @@ -2,82 +2,73 @@ <result> <row> <login>login1</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login1</alias> <email>login1@example.com</email> - <token_auth>367ea0b18ee1e641089e5d0a4d5f276d</token_auth> <superuser_access>1</superuser_access> </row> <row> <login>login2</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login2</alias> <email>login2@example.com</email> - <token_auth>ef3cb848005bffc2e2f3c8edbd95c58f</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login3</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login3</alias> <email>login3@example.com</email> - <token_auth>4298f4654bddcccac23e3d38c7d8a79d</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login4</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login4</alias> <email>login4@example.com</email> - <token_auth>dc6fb0514c143d97c72b8be165e7ee0a</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login5</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login5</alias> <email>login5@example.com</email> - <token_auth>4550293427ba5d0a0c96d6123429e9d3</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login6</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login6</alias> <email>login6@example.com</email> - <token_auth>2cafd6512d8b2739a7b2b01ab6609272</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login7</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login7</alias> <email>login7@example.com</email> - <token_auth>8bda247657d9b13c20843fd97c3fb427</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>login8</login> - <password>5f4dcc3b5aa765d61d8327deb882cf99</password> + <alias>login8</alias> <email>login8@example.com</email> - <token_auth>8fdfef11755e29a8369a57fe2709445b</token_auth> <superuser_access>0</superuser_access> </row> <row> <login>superUserLogin</login> - <password>1e56c228742c0189d261500852e27a02</password> + <alias>superUserLogin</alias> <email>hello@example.org</email> - <token_auth>9ad1de7f8b329ab919d854c556f860c1</token_auth> <superuser_access>1</superuser_access> </row> diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php index c0ea0ce4cd..131af64a0f 100644 --- a/tests/PHPUnit/Framework/Fixture.php +++ b/tests/PHPUnit/Framework/Fixture.php @@ -12,6 +12,7 @@ use Piwik\Application\Environment; use Piwik\Archive; use Piwik\ArchiveProcessor\PluginsArchiver; use Piwik\Auth; +use Piwik\Auth\Password; use Piwik\Cache\Backend\File; use Piwik\Cache as PiwikCache; use Piwik\Common; @@ -663,34 +664,38 @@ class Fixture extends \PHPUnit_Framework_Assert */ public static function getTokenAuth() { - return APIUsersManager::getInstance()->getTokenAuth( - self::ADMIN_USER_LOGIN, - UsersManager::getPasswordHash(self::ADMIN_USER_PASSWORD) - ); + $model = new \Piwik\Plugins\UsersManager\Model(); + $user = $model->getUser(self::ADMIN_USER_LOGIN); + + return $user['token_auth']; } public static function createSuperUser($removeExisting = true) { - $login = self::ADMIN_USER_LOGIN; - $password = UsersManager::getPasswordHash(self::ADMIN_USER_PASSWORD); - $token = self::getTokenAuth(); + $passwordHelper = new Password(); + + $login = self::ADMIN_USER_LOGIN; + $password = $passwordHelper->hash(UsersManager::getPasswordHash(self::ADMIN_USER_PASSWORD)); + $token = APIUsersManager::getInstance()->createTokenAuth($login); $model = new \Piwik\Plugins\UsersManager\Model(); + $user = $model->getUser($login); + if ($removeExisting) { $model->deleteUserOnly($login); } - $user = $model->getUser($login); - - if (empty($user)) { + if (!empty($user) && !$removeExisting) { + $token = $user['token_auth']; + } + if (empty($user) || $removeExisting) { $model->addUser($login, $password, 'hello@example.org', $login, $token, Date::now()->getDatetime()); } else { $model->updateUser($login, $password, 'hello@example.org', $login, $token); } - if (empty($user['superuser_access'])) { - $model->setSuperUserAccess($login, true); - } + $setSuperUser = empty($user) || !empty($user['superuser_access']); + $model->setSuperUserAccess($login, $setSuperUser); return $model->getUserByTokenAuth($token); } diff --git a/tests/PHPUnit/Integration/Tracker/RequestTest.php b/tests/PHPUnit/Integration/Tracker/RequestTest.php index bff3c80b08..05eee7ea30 100644 --- a/tests/PHPUnit/Integration/Tracker/RequestTest.php +++ b/tests/PHPUnit/Integration/Tracker/RequestTest.php @@ -293,7 +293,7 @@ class RequestTest extends IntegrationTestCase $login = 'myadmin'; $passwordHash = UsersManager::getPasswordHash('password'); - $token = API::getInstance()->getTokenAuth($login, $passwordHash); + $token = API::getInstance()->createTokenAuth($login); $user = new Model(); $user->addUser($login, $passwordHash, 'admin@piwik', 'alias', $token, '2014-01-01 00:00:00'); diff --git a/tests/resources/OmniFixture-dump.sql b/tests/resources/OmniFixture-dump.sql index 350c46c1d1..209e820824 100644 --- a/tests/resources/OmniFixture-dump.sql +++ b/tests/resources/OmniFixture-dump.sql @@ -814,10 +814,10 @@ DROP TABLE IF EXISTS `user`; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `user` ( `login` varchar(100) NOT NULL, - `password` char(32) NOT NULL, + `password` varchar(255) NOT NULL, `alias` varchar(45) NOT NULL, `email` varchar(100) NOT NULL, - `token_auth` char(32) NOT NULL, + `token_auth` char(64) NOT NULL, `superuser_access` tinyint(2) unsigned NOT NULL DEFAULT '0', `date_registered` timestamp NULL DEFAULT NULL, PRIMARY KEY (`login`), @@ -831,7 +831,7 @@ CREATE TABLE `user` ( LOCK TABLES `user` WRITE; /*!40000 ALTER TABLE `user` DISABLE KEYS */; -INSERT INTO `user` VALUES ('superUserLogin','1e56c228742c0189d261500852e27a02','superUserLogin','hello@example.org','9ad1de7f8b329ab919d854c556f860c1',1,'2016-03-09 09:10:19'); +INSERT INTO `user` VALUES ('superUserLogin','$2y$10$vh5d/W6S9M7u.1G2gc1TOeetRi.6HN6OG1jq47j5JhixtqTxkdGVi','superUserLogin','hello@example.org','9ad1de7f8b329ab919d854c556f860c1',1,'2016-03-09 09:10:19'); /*!40000 ALTER TABLE `user` ENABLE KEYS */; UNLOCK TABLES; |