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:
authorThomas Steur <tsteur@users.noreply.github.com>2018-07-18 07:47:13 +0300
committerdiosmosis <diosmosis@users.noreply.github.com>2018-07-18 07:47:13 +0300
commit105e007721b5c0ea12ff2596d8d82c721021fb4e (patch)
tree558162844ba663781fdf0ec691642f0dc453e94e /plugins/UsersManager
parent74334d8d0908910ed3cc4a9a918436d9f9ccc3f6 (diff)
Introducing a new role "write" and possibility to define capabilities (#13163)
* started working on some ACL concept * acl implementation * add category * small tweaks * more tweaks * more api methods and fixes * cache capabilities * various enhancements, fixes, tweaks * more tweaks * added more tests and fixed some bugs * fix parameter * make sure to be BC * make sure to be BC * fix some tests * more apis, translations, changelog entry, ... * update db * correct error message * fix capabilities were not detected in tests * directly access provider * fix and add test * JS api to check capabilities, better structure for capabilities in tests * add ability to inject permissions * apply review changes * fix test
Diffstat (limited to 'plugins/UsersManager')
-rw-r--r--plugins/UsersManager/API.php298
-rw-r--r--plugins/UsersManager/Model.php12
-rw-r--r--plugins/UsersManager/UsersManager.php31
-rw-r--r--plugins/UsersManager/lang/en.json9
-rw-r--r--plugins/UsersManager/tests/Integration/APITest.php372
-rw-r--r--plugins/UsersManager/tests/Integration/UsersManagerTest.php37
6 files changed, 711 insertions, 48 deletions
diff --git a/plugins/UsersManager/API.php b/plugins/UsersManager/API.php
index f134d4647e..241595f571 100644
--- a/plugins/UsersManager/API.php
+++ b/plugins/UsersManager/API.php
@@ -52,16 +52,35 @@ class API extends \Piwik\Plugin\API
*/
private $userFilter;
+ /**
+ * @var Access\RolesProvider
+ */
+ private $roleProvider;
+
+ /**
+ * @var Access\CapabilitiesProvider
+ */
+ private $capabilityProvider;
+
const PREFERENCE_DEFAULT_REPORT = 'defaultReport';
const PREFERENCE_DEFAULT_REPORT_DATE = 'defaultReportDate';
private static $instance = null;
- public function __construct(Model $model, UserAccessFilter $filter, Password $password)
+ public function __construct(Model $model, UserAccessFilter $filter, Password $password, Access\RolesProvider $roleProvider = null, Access\CapabilitiesProvider $capabilityProvider = null)
{
$this->model = $model;
$this->userFilter = $filter;
$this->password = $password;
+
+ if (!isset($roleProvider)) {
+ $roleProvider = StaticContainer::get('Piwik\Access\RolesProvider');
+ }
+ if (!isset($capabilityProvider)) {
+ $capabilityProvider = StaticContainer::get('Piwik\Access\CapabilitiesProvider');
+ }
+ $this->roleProvider = $roleProvider;
+ $this->capabilityProvider = $capabilityProvider;
}
/**
@@ -93,6 +112,52 @@ class API extends \Piwik\Plugin\API
}
/**
+ * Get the list of all available roles.
+ * It does not return the super user role, and neither the "noaccess" role.
+ * @return array[] Returns an array containing information about each role
+ */
+ public function getAvailableRoles()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+
+ $response = array();
+
+ foreach ($this->roleProvider->getAllRoles() as $role) {
+ $response[] = array(
+ 'id' => $role->getId(),
+ 'name' => $role->getName(),
+ 'description' => $role->getDescription(),
+ 'helpUrl' => $role->getHelpUrl()
+ );
+ }
+
+ return $response;
+ }
+
+ /**
+ * Get the list of all available capabilities.
+ * @return array[] Returns an array containing information about each capability
+ */
+ public function getAvailableCapabilities()
+ {
+ Piwik::checkUserHasSomeAdminAccess();
+
+ $response = array();
+
+ foreach ($this->capabilityProvider->getAllCapabilities() as $capability) {
+ $response[] = array(
+ 'id' => $capability->getId(),
+ 'name' => $capability->getName(),
+ 'description' => $capability->getDescription(),
+ 'helpUrl' => $capability->getHelpUrl(),
+ 'includedInRoles' => $capability->getIncludedInRoles()
+ );
+ }
+
+ return $response;
+ }
+
+ /**
* Sets a user preference
* @param string $userLogin
* @param string $preferenceName
@@ -269,6 +334,17 @@ class API extends \Piwik\Plugin\API
return $userSites;
}
+ private function checkAccessType($access)
+ {
+ $roles = $this->roleProvider->getAllRoleIds();
+ $capabilities = $this->capabilityProvider->getAllCapabilityIds();
+ $list = array_merge($roles, $capabilities);
+
+ if (!in_array($access, $list, true)) {
+ throw new Exception(Piwik::translate("UsersManager_ExceptionAccessValues", implode(", ", $list)));
+ }
+ }
+
/**
* For each user, returns their access level for the given $idSite.
* If a user doesn't have any access to the $idSite ('noaccess'),
@@ -727,28 +803,201 @@ class API extends \Piwik\Plugin\API
* If access = 'view' or 'admin' the current access level is deleted and updated with the new value.
*
* @param string $userLogin The user login
- * @param string $access Access to grant. Must have one of the following value : noaccess, view, admin
+ * @param string|array $access Access to grant. Must have one of the following value : noaccess, view, write, admin.
+ * May also be an array to sent additional capabilities
* @param int|array $idSites The array of idSites on which to apply the access level for the user.
* If the value is "all" then we apply the access level to all the websites ID for which the current authentificated user has an 'admin' access.
*
* @throws Exception if the user doesn't exist
* @throws Exception if the access parameter doesn't have a correct value
* @throws Exception if any of the given website ID doesn't exist
- *
- * @return bool true on success
*/
public function setUserAccess($userLogin, $access, $idSites)
{
- $this->checkAccessType($access);
+ $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+
+ if ($userLogin === 'anonymous' &&
+ (is_array($access) || !in_array($access, array('view', 'noaccess'), true))) {
+ throw new Exception(Piwik::translate("UsersManager_ExceptionAnonymousAccessNotPossible", array('noaccess', 'view')));
+ }
+
+ $roles = array();
+ $capabilities = array();
+
+ if (is_array($access)) {
+ // we require one role, and optionally multiple capabilties
+ $roles = array();
+ foreach ($access as $entry) {
+ if ($this->roleProvider->isValidRole($entry)) {
+ $roles[] = $entry;
+ } else {
+ $this->checkAccessType($entry);
+ $capabilities[] = $entry;
+ }
+ }
+
+ if (count($roles) < 1) {
+ $ids = implode(', ', $this->roleProvider->getAllRoleIds());
+ throw new Exception(Piwik::translate('UsersManager_ExceptionNoRoleSet', $ids));
+ }
+
+ if (count($roles) > 1) {
+ $ids = implode(', ', $this->roleProvider->getAllRoleIds());
+ throw new Exception(Piwik::translate('UsersManager_ExceptionMultipleRoleSet', $ids));
+ }
+
+ } else {
+ // as only one access is set, we require it to be a role or "noaccess"...
+ if ($access !== 'noaccess') {
+ $this->roleProvider->checkValidRole($access);
+ $roles[] = $access;
+ }
+ }
+
+ $this->checkUserExists($userLogin);
+ $this->checkUserHasNotSuperUserAccess($userLogin);
+
+ $this->model->deleteUserAccess($userLogin, $idSites);
+
+ if ($access === 'noaccess') {
+ // if the access is noaccess then we don't save it as this is the default value
+ // when no access are specified
+ Piwik::postEvent('UsersManager.removeSiteAccess', array($userLogin, $idSites));
+ } else {
+ $role = array_shift($roles);
+ $this->model->addUserAccess($userLogin, $role, $idSites);
+ }
+
+ if (!empty($capabilities)) {
+ $this->addCapabilities($userLogin, $capabilities, $idSites);
+ }
+
+ // we reload the access list which doesn't yet take in consideration this new user access
+ $this->reloadPermissions();
+ }
+
+ /**
+ * Adds the given capabilities to the given user for the given sites.
+ * The capability will be added only when the user also has access to a site, for example View, Write, or Admin.
+ * Note: You can neither add any capability to a super user, nor to the anonymous user.
+ * Note: If the user has assigned a role which already grants the given capability, the capability will not be added in
+ * the backend.
+ *
+ * @param string $userLogin The user login
+ * @param string|string[] $capabilities To fetch a list of available capabilities call "UsersManager.getAvailableCapabilities".
+ * @param int|int[] $idSites
+ * @throws Exception
+ */
+ public function addCapabilities($userLogin, $capabilities, $idSites)
+ {
+ $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+
+ if ($userLogin == 'anonymous') {
+ throw new Exception(Piwik::translate("UsersManager_ExceptionAnonymousNoCapabilities"));
+ }
+
$this->checkUserExists($userLogin);
$this->checkUserHasNotSuperUserAccess($userLogin);
- if ($userLogin == 'anonymous'
- && $access == 'admin'
- ) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionAdminAnonymous"));
+ if (!is_array($capabilities)){
+ $capabilities = array($capabilities);
+ }
+
+ foreach ($capabilities as $entry) {
+ $this->capabilityProvider->checkValidCapability($entry);
+ }
+
+ list($sitesIdWithRole, $sitesIdWithCapability) = $this->getRolesAndCapabilitiesForLogin($userLogin);
+
+ foreach ($capabilities as $entry) {
+ $cap = $this->capabilityProvider->getCapability($entry);
+
+ foreach ($idSites as $idSite) {
+ $hasRole = array_key_exists($idSite, $sitesIdWithRole);
+ $hasCapabilityAlready = array_key_exists($idSite, $sitesIdWithCapability) && in_array($entry, $sitesIdWithCapability[$idSite], true);
+
+ // so far we are adding the capability only to people that also have a role...
+ // to be defined how to handle this... eg we are not throwing an exception currently
+ // as it might be used as part of bulk action etc.
+ if ($hasRole && !$hasCapabilityAlready) {
+ $theRole = $sitesIdWithRole[$idSite];
+ if ($cap->hasRoleCapability($theRole)) {
+ // todo this behaviour needs to be defined...
+ // when the role already supports this capability we do not add it again
+ continue;
+ }
+
+ $this->model->addUserAccess($userLogin, $entry, array($idSite));
+ }
+ }
+
+ }
+
+ // we reload the access list which doesn't yet take in consideration this new user access
+ $this->reloadPermissions();
+ }
+
+ private function getRolesAndCapabilitiesForLogin($userLogin)
+ {
+ $sites = $this->model->getSitesAccessFromUser($userLogin);
+ $roleIds = $this->roleProvider->getAllRoleIds();
+
+ $sitesIdWithRole = array();
+ $sitesIdWithCapability = array();
+ foreach ($sites as $site) {
+ if (in_array($site['access'], $roleIds, true)) {
+ $sitesIdWithRole[(int) $site['site']] = $site['access'];
+ } else {
+ if (!isset($sitesIdWithCapability[(int) $site['site']])) {
+ $sitesIdWithCapability[(int) $site['site']] = array();
+ }
+ $sitesIdWithCapability[(int) $site['site']][] = $site['access'];
+ }
+ }
+ return [$sitesIdWithRole, $sitesIdWithCapability];
+ }
+
+ /**
+ * Removes the given capabilities from the given user for the given sites.
+ * The capability will be only removed if it is actually granted as a separate capability. If the user has a role
+ * that includes a specific capability, for example "Admin", then the capability will not be removed because the
+ * assigned role will always include this capability.
+ *
+ * @param string $userLogin The user login
+ * @param string|string[] $capabilities To fetch a list of available capabilities call "UsersManager.getAvailableCapabilities".
+ * @param int|int[] $idSites
+ * @throws Exception
+ */
+ public function removeCapabilities($userLogin, $capabilities, $idSites)
+ {
+ $idSites = $this->getIdSitesCheckAdminAccess($idSites);
+
+ $this->checkUserExists($userLogin);
+
+ if (!is_array($capabilities)){
+ $capabilities = array($capabilities);
+ }
+
+ foreach ($capabilities as $capability) {
+ $this->capabilityProvider->checkValidCapability($capability);
+ }
+
+ foreach ($capabilities as $capability) {
+ $this->model->removeUserAccess($userLogin, $capability, $idSites);
}
+ // we reload the access list which doesn't yet take in consideration this removed capability
+ $this->reloadPermissions();
+ }
+
+ private function reloadPermissions()
+ {
+ Access::getInstance()->reloadAccess();
+ Cache::deleteTrackerCache();
+ }
+
+ private function getIdSitesCheckAdminAccess($idSites)
+ {
// in case idSites is all we grant access to all the websites on which the current connected user has an 'admin' access
if ($idSites === 'all') {
$idSites = \Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAdminAccess();
@@ -760,27 +1009,16 @@ class API extends \Piwik\Plugin\API
if (empty($idSites)) {
throw new Exception('Specify at least one website ID in &idSites=');
}
+
// it is possible to set user access on websites only for the websites admin
// basically an admin can give the view or the admin access to any user for the websites they manage
Piwik::checkUserHasAdminAccess($idSites);
- $this->model->deleteUserAccess($userLogin, $idSites);
-
- // if the access is noaccess then we don't save it as this is the default value
- // when no access are specified
- if ($access != 'noaccess') {
- $this->model->addUserAccess($userLogin, $access, $idSites);
- } else {
- if (!empty($idSites) && !is_array($idSites)) {
- $idSites = array($idSites);
- }
-
- Piwik::postEvent('UsersManager.removeSiteAccess', array($userLogin, $idSites));
+ if (!is_array($idSites)) {
+ $idSites = array($idSites);
}
- // we reload the access list which doesn't yet take in consideration this new user access
- Access::getInstance()->reloadAccess();
- Cache::deleteTrackerCache();
+ return $idSites;
}
/**
@@ -823,18 +1061,6 @@ class API extends \Piwik\Plugin\API
}
}
- private function checkAccessType($access)
- {
- $accessList = Access::getListAccess();
-
- // do not allow to set the superUser access
- unset($accessList[array_search("superuser", $accessList)]);
-
- if (!in_array($access, $accessList)) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionAccessValues", implode(", ", $accessList)));
- }
- }
-
private function isUserTheOnlyUserHavingSuperUserAccess($userLogin)
{
$superUsers = $this->getUsersHavingSuperUserAccess();
diff --git a/plugins/UsersManager/Model.php b/plugins/UsersManager/Model.php
index a45aebaf2d..d33c808c48 100644
--- a/plugins/UsersManager/Model.php
+++ b/plugins/UsersManager/Model.php
@@ -291,6 +291,18 @@ class Model
}
}
+ public function removeUserAccess($userLogin, $access, $idSites)
+ {
+ $db = $this->getDb();
+
+ $table = Common::prefixTable("access");
+
+ foreach ($idSites as $idsite) {
+ $bind = array($userLogin, $idsite, $access);
+ $db->query("DELETE FROM " . $table . " WHERE login = ? and idsite = ? and access = ?", $bind);
+ }
+ }
+
public function deleteUserOnly($userLogin)
{
$db = $this->getDb();
diff --git a/plugins/UsersManager/UsersManager.php b/plugins/UsersManager/UsersManager.php
index 95620a6182..4f699aab5f 100644
--- a/plugins/UsersManager/UsersManager.php
+++ b/plugins/UsersManager/UsersManager.php
@@ -9,7 +9,10 @@
namespace Piwik\Plugins\UsersManager;
use Exception;
+use Piwik\Access\Role\Admin;
+use Piwik\Access\Role\Write;
use Piwik\API\Request;
+use Piwik\Common;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugins\CoreHome\SystemSummary;
@@ -69,22 +72,30 @@ class UsersManager extends \Piwik\Plugin
*/
public function recordAdminUsersInCache(&$attributes, $idSite)
{
- // add the 'hosts' entry in the website array
$model = new Model();
- $logins = $model->getUsersLoginWithSiteAccess($idSite, 'admin');
+ $adminLogins = $model->getUsersLoginWithSiteAccess($idSite, Admin::ID);
+ $writeLogins = $model->getUsersLoginWithSiteAccess($idSite, Write::ID);
- if (empty($logins)) {
- return;
- }
+ $attributes['tracking_token_auth'] = array();
- $users = $model->getUsers($logins);
+ if (!empty($adminLogins)) {
+ $users = $model->getUsers($adminLogins);
+ foreach ($users as $user) {
+ $attributes['tracking_token_auth'][] = self::hashTrackingToken($user['token_auth'], $idSite);
+ }
+ }
- $tokens = array();
- foreach ($users as $user) {
- $tokens[] = $user['token_auth'];
+ if (!empty($writeLogins)) {
+ $users = $model->getUsers($writeLogins);
+ foreach ($users as $user) {
+ $attributes['tracking_token_auth'][] = self::hashTrackingToken($user['token_auth'], $idSite);
+ }
}
+ }
- $attributes['admin_token_auth'] = $tokens;
+ public static function hashTrackingToken($tokenAuth, $idSite)
+ {
+ return sha1($idSite . $tokenAuth . SettingsPiwik::getSalt());
}
public function getCronArchiveTokenAuth(&$tokens)
diff --git a/plugins/UsersManager/lang/en.json b/plugins/UsersManager/lang/en.json
index 90112e56ba..158c12e225 100644
--- a/plugins/UsersManager/lang/en.json
+++ b/plugins/UsersManager/lang/en.json
@@ -19,7 +19,10 @@
"EmailYourAdministrator": "%1$sE-mail your administrator about this problem%2$s.",
"EnterUsernameOrEmail": "Enter a username or email address",
"ExceptionAccessValues": "The parameter access must have one of the following values: [ %s ]",
- "ExceptionAdminAnonymous": "You cannot grant 'admin' access to the 'anonymous' user.",
+ "ExceptionNoRoleSet": "No role is set but one of these needs to be set: %s",
+ "ExceptionMultipleRoleSet": "Only one role can be set but multiple have been set. Use only one of: %s",
+ "ExceptionAnonymousNoCapabilities": "You cannot grant any capability to the 'anonymous' user.",
+ "ExceptionAnonymousAccessNotPossible": "You can only set access %1$s or %2$s access to the 'anonymous' user.",
"ExceptionDeleteDoesNotExist": "User '%s' doesn't exist therefore it can't be deleted.",
"ExceptionDeleteOnlyUserWithSuperUserAccess": "Deleting user '%s' is not possible.",
"ExceptionEditAnonymous": "The anonymous user cannot be edited or deleted. It is used by Matomo to define a user that has not logged in yet. For example, you can make your statistics public by granting the 'view' access to the 'anonymous' user.",
@@ -55,8 +58,12 @@
"NoUsersExist": "There are no users yet.",
"PluginDescription": "Users Management lets you add new users, edit existing users and give them access to view or administrate websites. ",
"PrivAdmin": "Admin",
+ "PrivAdminDescription": "Users with this role can manage a website and give other users access to the website. They can also do everything the %s role can do.",
+ "PrivWrite": "Write",
+ "PrivWriteDescription": "Users with this role can view all content plus create, manage and delete entities such as Goals, Funnels, Heatmaps, Session Recordings and Forms for this website.",
"PrivNone": "No access",
"PrivView": "View",
+ "PrivViewDescription": "A user with this role can view all reports.",
"RemoveUserAccess":"Remove access for '%1$s' for %2$s.",
"ReportDateToLoadByDefault": "Report date to load by default",
"ReportToLoadByDefault": "Report to load by default",
diff --git a/plugins/UsersManager/tests/Integration/APITest.php b/plugins/UsersManager/tests/Integration/APITest.php
index 91f8e0f49f..4c23d5f7f0 100644
--- a/plugins/UsersManager/tests/Integration/APITest.php
+++ b/plugins/UsersManager/tests/Integration/APITest.php
@@ -8,14 +8,114 @@
namespace Piwik\Plugins\UsersManager\tests;
+use Piwik\Access\Role\View;
+use Piwik\Access\Role\Write;
use Piwik\Auth\Password;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugins\UsersManager\API;
+use Piwik\Plugins\UsersManager\Model;
use Piwik\Plugins\UsersManager\UsersManager;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Access\Role\Admin;
+use Piwik\Access\Capability;
+
+class TestCap1 extends Capability
+{
+ const ID = 'test_cap1';
+
+ public function getId()
+ {
+ return self::ID;
+ }
+
+ public function getCategory()
+ {
+ return 'Test';
+ }
+
+ public function getName()
+ {
+ return 'Cap1';
+ }
+
+ public function getDescription()
+ {
+ return '';
+ }
+
+ public function getIncludedInRoles()
+ {
+ return array(
+ Admin::ID
+ );
+ }
+}
+
+class TestCap2 extends Capability
+{
+ const ID = 'test_cap2';
+
+ public function getId()
+ {
+ return self::ID;
+ }
+
+ public function getCategory()
+ {
+ return 'Test';
+ }
+
+ public function getName()
+ {
+ return 'Cap2';
+ }
+
+ public function getDescription()
+ {
+ return '';
+ }
+
+ public function getIncludedInRoles()
+ {
+ return array(
+ Write::ID, Admin::ID
+ );
+ }
+}
+
+class TestCap3 extends Capability
+{
+ const ID = 'test_cap3';
+
+ public function getId()
+ {
+ return self::ID;
+ }
+
+ public function getCategory()
+ {
+ return 'Test';
+ }
+
+ public function getName()
+ {
+ return 'Cap3';
+ }
+
+ public function getDescription()
+ {
+ return '';
+ }
+
+ public function getIncludedInRoles()
+ {
+ return array(Admin::ID);
+ }
+}
+
/**
* @group UsersManager
@@ -28,6 +128,11 @@ class APITest extends IntegrationTestCase
* @var API
*/
private $api;
+
+ /**
+ * @var Model
+ */
+ private $model;
private $login = 'userLogin';
@@ -36,6 +141,7 @@ class APITest extends IntegrationTestCase
parent::setUp();
$this->api = API::getInstance();
+ $this->model = new Model();
FakeAccess::$superUser = true;
@@ -224,6 +330,263 @@ class APITest extends IntegrationTestCase
$this->assertEquals($expected, $access);
}
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionMultipleRoleSet
+ */
+ public function test_setUserAccess_MultipleRolesCannotBeSet()
+ {
+ $this->api->setUserAccess($this->login, array('view', 'admin'), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionNoRoleSet
+ */
+ public function test_setUserAccess_NeedsAtLeastOneRole()
+ {
+ $this->api->setUserAccess($this->login, array(TestCap2::ID), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_setUserAccess_NeedsAtLeastOneRoleAsString()
+ {
+ $this->api->setUserAccess($this->login, TestCap2::ID, array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_setUserAccess_InvalidCapability()
+ {
+ $this->api->setUserAccess($this->login, array('admin', 'foobar'), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionNoRoleSet
+ */
+ public function test_setUserAccess_NeedsAtLeastOneRoleNoneGiven()
+ {
+ $this->api->setUserAccess($this->login, array(), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAnonymousAccessNotPossible
+ */
+ public function test_setUserAccess_CannotSetAdminToAnonymous()
+ {
+ $this->api->setUserAccess('anonymous', 'admin', array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAnonymousAccessNotPossible
+ */
+ public function test_setUserAccess_CannotSetWriteToAnonymous()
+ {
+ $this->api->setUserAccess('anonymous', 'write', array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionUserDoesNotExist
+ */
+ public function test_setUserAccess_UserDoesNotExist()
+ {
+ $this->api->setUserAccess('foobar', Admin::ID, array(1));
+ }
+
+ public function test_setUserAccess_SetRoleAndCapabilities()
+ {
+ $access = array(TestCap2::ID, View::ID, TestCap3::ID);
+ $this->api->setUserAccess($this->login, $access, array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ $expected = array(
+ array('site' => '1', 'access' => 'view'),
+ array('site' => '1', 'access' => TestCap2::ID),
+ array('site' => '1', 'access' => TestCap3::ID),
+ );
+ $this->assertEquals($expected, $access);
+ }
+
+ public function test_setUserAccess_SetRoleAsString()
+ {
+ $this->api->setUserAccess($this->login, View::ID, array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+ $this->assertEquals(array(array('site' => '1', 'access' => 'view')), $access);
+ }
+
+ public function test_setUserAccess_SetRoleAsArray()
+ {
+ $this->api->setUserAccess($this->login, array(View::ID), array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+ $this->assertEquals(array(array('site' => '1', 'access' => 'view')), $access);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_addCapabilities_failsWhenNotCapabilityIsGivenAsString()
+ {
+ $this->api->addCapabilities($this->login, View::ID, array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_addCapabilities_failsWhenNotCapabilityIsGivenAsArray()
+ {
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, View::ID), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionUserDoesNotExist
+ */
+ public function test_addCapabilities_failsWhenUserDoesNotExist()
+ {
+ $this->api->addCapabilities('foobar', array(TestCap2::ID), array(1));
+ }
+
+ public function test_addCapabilities_DoesNotAddSameCapabilityTwice()
+ {
+ $addAccess = array(TestCap2::ID, View::ID, TestCap3::ID);
+ $this->api->setUserAccess($this->login, $addAccess, array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ $expected = array(
+ array('site' => '1', 'access' => 'view'),
+ array('site' => '1', 'access' => TestCap2::ID),
+ array('site' => '1', 'access' => TestCap3::ID),
+ );
+ $this->assertEquals($expected, $access);
+
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, TestCap3::ID), array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+ $this->assertEquals($expected, $access);
+
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, TestCap1::ID, TestCap3::ID), array(1));
+
+ $expected[] = array('site' => '1', 'access' => TestCap1::ID);
+ $access = $this->model->getSitesAccessFromUser($this->login);
+ $this->assertEquals($expected, $access);
+ }
+
+ public function test_addCapabilities_DoesNotAddCapabilityToUserWithNoRole()
+ {
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ $this->assertEquals(array(), $access);
+
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, TestCap3::ID), array(1));
+
+ $this->assertEquals(array(), $access);
+ }
+
+ public function test_addCapabilities_DoesNotAddCapabilitiesWhichAreIncludedInRoleAlready()
+ {
+ $this->api->setUserAccess($this->login, Write::ID, array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ $expected = array(
+ array('site' => '1', 'access' => 'write'),
+ );
+ $this->assertEquals($expected, $access);
+
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, TestCap3::ID), array(1));
+
+ $expected[] = array('site' => '1', 'access' => TestCap3::ID);
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ // did not add TestCap2
+ $this->assertEquals($expected, $access);
+ }
+
+ public function test_addCapabilities_DoesAddCapabilitiesWhichAreNotIncludedInRoleYetAlready()
+ {
+ $this->api->setUserAccess($this->login, Admin::ID, array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+
+ $expected = array(
+ array('site' => '1', 'access' => 'admin'),
+ );
+ $this->assertEquals($expected, $access);
+
+ $this->api->addCapabilities($this->login, array(TestCap2::ID, TestCap1::ID, TestCap3::ID), array(1));
+
+ $access = $this->model->getSitesAccessFromUser($this->login);
+ $this->assertEquals($expected, $access);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_removeCapabilities_failsWhenNotCapabilityIsGivenAsString()
+ {
+ $this->api->removeCapabilities($this->login, View::ID, array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionAccessValues
+ */
+ public function test_removeCapabilities_failsWhenNotCapabilityIsGivenAsArray()
+ {
+ $this->api->removeCapabilities($this->login, array(TestCap2::ID, View::ID), array(1));
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage UsersManager_ExceptionUserDoesNotExist
+ */
+ public function test_removeCapabilities_failsWhenUserDoesNotExist()
+ {
+ $this->api->removeCapabilities('foobar', array(TestCap2::ID), array(1));
+ }
+
+ public function test_removeCapabilities()
+ {
+ $addAccess = array(View::ID, TestCap2::ID, TestCap3::ID, TestCap1::ID);
+ $this->api->setUserAccess($this->login, $addAccess, array(1));
+
+ $access = $this->getAccessInSite($this->login, 1);
+ $this->assertEquals($addAccess, $access);
+
+ $this->api->removeCapabilities($this->login, array(TestCap3::ID, TestCap2::ID), 1);
+
+ $access = $this->getAccessInSite($this->login, 1);
+ $this->assertEquals(array(View::ID, TestCap1::ID), $access);
+ }
+
+ private function getAccessInSite($login, $idSite)
+ {
+ $access = $this->model->getSitesAccessFromUser($login);
+ $ids = array();
+ foreach ($access as $entry) {
+ if ($entry['site'] == $idSite) {
+ $ids[] = $entry['access'];
+ }
+ }
+ return $ids;
+ }
+
private function getPreferenceId($preferenceName)
{
return $this->login . '_' . $preferenceName;
@@ -232,7 +595,14 @@ class APITest extends IntegrationTestCase
public function provideContainerConfig()
{
return array(
- 'Piwik\Access' => new FakeAccess()
+ 'Piwik\Access' => new FakeAccess(),
+ 'observers.global' => \DI\add([
+ ['Access.Capability.addCapabilities', function (&$capabilities) {
+ $capabilities[] = new TestCap1();
+ $capabilities[] = new TestCap2();
+ $capabilities[] = new TestCap3();
+ }],
+ ]),
);
}
}
diff --git a/plugins/UsersManager/tests/Integration/UsersManagerTest.php b/plugins/UsersManager/tests/Integration/UsersManagerTest.php
index 4a849cb155..790387ce8b 100644
--- a/plugins/UsersManager/tests/Integration/UsersManagerTest.php
+++ b/plugins/UsersManager/tests/Integration/UsersManagerTest.php
@@ -480,6 +480,7 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetUserAccessNoLogin()
{
+ FakeAccess::clearAccess($superUser = false, $admin =array(1), $view = array());
$this->api->setUserAccess("nologin", "view", 1);
}
@@ -490,6 +491,7 @@ class UsersManagerTest extends IntegrationTestCase
public function testSetUserAccessWrongAccessSpecified()
{
$this->api->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
+ FakeAccess::clearAccess($superUser = false, $admin =array(1), $view = array());
$this->api->setUserAccess("gegg4564eqgeqag", "viewnotknown", 1);
}
@@ -500,6 +502,7 @@ class UsersManagerTest extends IntegrationTestCase
public function testSetUserAccess_ShouldFail_SuperUserAccessIsNotAllowed()
{
$this->api->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
+ FakeAccess::clearAccess($superUser = false, $admin =array(1), $view = array());
$this->api->setUserAccess("gegg4564eqgeqag", "superuser", 1);
}
@@ -509,6 +512,7 @@ class UsersManagerTest extends IntegrationTestCase
*/
public function testSetUserAccess_ShouldFail_IfLoginIsConfigSuperUserLogin()
{
+ FakeAccess::clearAccess($superUser = false, $admin =array(1), $view = array());
$this->api->setUserAccess('superusertest', 'view', 1);
}
@@ -521,6 +525,7 @@ class UsersManagerTest extends IntegrationTestCase
$this->api->addUser("gegg4564eqgeqag", "geqgegagae", "tegst@tesgt.com", "alias");
$this->api->setSuperUserAccess('gegg4564eqgeqag', true);
+ FakeAccess::clearAccess($superUser = false, $idSitesAdmin = array(1));
$this->api->setUserAccess('gegg4564eqgeqag', 'view', 1);
}
@@ -969,6 +974,38 @@ class UsersManagerTest extends IntegrationTestCase
$this->assertEquals('yesterday', $this->api->getUserPreference('someUser', $defaultReportDatePref));
}
+ public function testGetAvailableRoles()
+ {
+ $this->addSites(1);
+ $roles = $this->api->getAvailableRoles();
+ $expected = array(
+ array (
+ 'id' => 'view',
+ 'name' => 'UsersManager_PrivView',
+ 'description' => 'UsersManager_PrivViewDescription',
+ 'helpUrl' => 'https://matomo.org/faq/general/faq_70/'
+ ), array (
+ 'id' => 'write',
+ 'name' => 'UsersManager_PrivWrite',
+ 'description' => 'UsersManager_PrivWriteDescription',
+ 'helpUrl' => ''
+ ),
+ array (
+ 'id' => 'admin',
+ 'name' => 'UsersManager_PrivAdmin',
+ 'description' => 'UsersManager_PrivAdminDescription',
+ 'helpUrl' => 'https://matomo.org/faq/general/faq_69/',
+ )
+ );
+ $this->assertEquals($expected, $roles);
+ }
+
+ public function testGetAvailableCapabilities()
+ {
+ $this->addSites(1);
+ $this->assertSame(array(), $this->api->getAvailableCapabilities());
+ }
+
private function addSites($numberOfSites)
{
$idSites = array();