/plugins/UsersManager/API.php
PHP | 651 lines | 325 code | 89 blank | 237 comment | 43 complexity | fa831d19b29b21b647bcbb0b01ff1ccb MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
- <?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\Plugins\UsersManager;
- use Exception;
- use Piwik\Access;
- use Piwik\Common;
- use Piwik\Config;
- use Piwik\Date;
- use Piwik\Option;
- use Piwik\Piwik;
- use Piwik\Site;
- use Piwik\Tracker\Cache;
- /**
- * The UsersManager API lets you Manage Users and their permissions to access specific websites.
- *
- * You can create users via "addUser", update existing users via "updateUser" and delete users via "deleteUser".
- * There are many ways to list users based on their login "getUser" and "getUsers", their email "getUserByEmail",
- * or which users have permission (view or admin) to access the specified websites "getUsersWithSiteAccess".
- *
- * Existing Permissions are listed given a login via "getSitesAccessFromUser", or a website ID via "getUsersAccessFromSite",
- * or you can list all users and websites for a given permission via "getUsersSitesFromAccess". Permissions are set and updated
- * via the method "setUserAccess".
- * See also the documentation about <a href='http://piwik.org/docs/manage-users/' target='_blank'>Managing Users</a> in Piwik.
- */
- class API extends \Piwik\Plugin\API
- {
- /**
- * @var Model
- */
- private $model;
- const PREFERENCE_DEFAULT_REPORT = 'defaultReport';
- const PREFERENCE_DEFAULT_REPORT_DATE = 'defaultReportDate';
- static private $instance = null;
- protected function __construct()
- {
- $this->model = new Model();
- }
- /**
- * You can create your own Users Plugin to override this class.
- * Example of how you would overwrite the UsersManager_API with your own class:
- * Call the following in your plugin __construct() for example:
- *
- * Registry::set('UsersManager_API', \Piwik\Plugins\MyCustomUsersManager\API::getInstance());
- *
- * @throws Exception
- * @return \Piwik\Plugins\UsersManager\API
- */
- static public function getInstance()
- {
- try {
- $instance = \Piwik\Registry::get('UsersManager_API');
- if (!($instance instanceof API)) {
- // Exception is caught below and corrected
- throw new Exception('UsersManager_API must inherit API');
- }
- self::$instance = $instance;
- } catch (Exception $e) {
- self::$instance = new self;
- \Piwik\Registry::set('UsersManager_API', self::$instance);
- }
- return self::$instance;
- }
- /**
- * Sets a user preference
- * @param string $userLogin
- * @param string $preferenceName
- * @param string $preferenceValue
- * @return void
- */
- public function setUserPreference($userLogin, $preferenceName, $preferenceValue)
- {
- Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
- Option::set($this->getPreferenceId($userLogin, $preferenceName), $preferenceValue);
- }
- /**
- * Gets a user preference
- * @param string $userLogin
- * @param string $preferenceName
- * @return bool|string
- */
- public function getUserPreference($userLogin, $preferenceName)
- {
- Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
- $optionValue = Option::get($this->getPreferenceId($userLogin, $preferenceName));
- if ($optionValue !== false) {
- return $optionValue;
- }
- return $this->getDefaultUserPreference($preferenceName, $userLogin);
- }
- private function getPreferenceId($login, $preference)
- {
- return $login . '_' . $preference;
- }
- private function getDefaultUserPreference($preferenceName, $login)
- {
- switch ($preferenceName) {
- case self::PREFERENCE_DEFAULT_REPORT:
- $viewableSiteIds = \Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAtLeastViewAccess($login);
- return reset($viewableSiteIds);
- case self::PREFERENCE_DEFAULT_REPORT_DATE:
- return Config::getInstance()->General['default_day'];
- default:
- return false;
- }
- }
- /**
- * Returns the list of all the users
- *
- * @param string $userLogins Comma separated list of users to select. If not specified, will return all users
- * @return array the list of all the users
- */
- public function getUsers($userLogins = '')
- {
- Piwik::checkUserHasSomeAdminAccess();
- $logins = array();
- if (!empty($userLogins)) {
- $logins = explode(',', $userLogins);
- }
- $users = $this->model->getUsers($logins);
- // Non Super user can only access login & alias
- if (!Piwik::hasUserSuperUserAccess()) {
- foreach ($users as &$user) {
- $user = array('login' => $user['login'], 'alias' => $user['alias']);
- }
- }
- return $users;
- }
- /**
- * Returns the list of all the users login
- *
- * @return array the list of all the users login
- */
- public function getUsersLogin()
- {
- Piwik::checkUserHasSomeAdminAccess();
- return $this->model->getUsersLogin();
- }
- /**
- * For each user, returns the list of website IDs where the user has the supplied $access level.
- * If a user doesn't have the given $access to any website IDs,
- * the user will not be in the returned array.
- *
- * @param string Access can have the following values : 'view' or 'admin'
- *
- * @return array The returned array has the format
- * array(
- * login1 => array ( idsite1,idsite2),
- * login2 => array(idsite2),
- * ...
- * )
- */
- public function getUsersSitesFromAccess($access)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkAccessType($access);
- return $this->model->getUsersSitesFromAccess($access);
- }
- /**
- * For each user, returns his access level for the given $idSite.
- * If a user doesn't have any access to the $idSite ('noaccess'),
- * the user will not be in the returned array.
- *
- * @param int $idSite website ID
- *
- * @return array The returned array has the format
- * array(
- * login1 => 'view',
- * login2 => 'admin',
- * login3 => 'view',
- * ...
- * )
- */
- public function getUsersAccessFromSite($idSite)
- {
- Piwik::checkUserHasAdminAccess($idSite);
- return $this->model->getUsersAccessFromSite($idSite);
- }
- public function getUsersWithSiteAccess($idSite, $access)
- {
- Piwik::checkUserHasAdminAccess($idSite);
- $this->checkAccessType($access);
- $logins = $this->model->getUsersLoginWithSiteAccess($idSite, $access);
- if (empty($logins)) {
- return array();
- }
- $logins = implode(',', $logins);
- return $this->getUsers($logins);
- }
- /**
- * For each website ID, returns the access level of the given $userLogin.
- * If the user doesn't have any access to a website ('noaccess'),
- * this website will not be in the returned array.
- * If the user doesn't have any access, the returned array will be an empty array.
- *
- * @param string $userLogin User that has to be valid
- *
- * @return array The returned array has the format
- * array(
- * idsite1 => 'view',
- * idsite2 => 'admin',
- * idsite3 => 'view',
- * ...
- * )
- */
- public function getSitesAccessFromUser($userLogin)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkUserExists($userLogin);
- $this->checkUserHasNotSuperUserAccess($userLogin);
- return $this->model->getSitesAccessFromUser($userLogin);
- }
- /**
- * Returns the user information (login, password md5, alias, email, date_registered, etc.)
- *
- * @param string $userLogin the user login
- *
- * @return array the user information
- */
- public function getUser($userLogin)
- {
- Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
- $this->checkUserExists($userLogin);
- return $this->model->getUser($userLogin);
- }
- /**
- * Returns the user information (login, password md5, alias, email, date_registered, etc.)
- *
- * @param string $userEmail the user email
- *
- * @return array the user information
- */
- public function getUserByEmail($userEmail)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkUserEmailExists($userEmail);
- return $this->model->getUserByEmail($userEmail);
- }
- private function checkLogin($userLogin)
- {
- if ($this->userExists($userLogin)) {
- throw new Exception(Piwik::translate('UsersManager_ExceptionLoginExists', $userLogin));
- }
- Piwik::checkValidLoginString($userLogin);
- }
- private function checkEmail($email)
- {
- if ($this->userEmailExists($email)) {
- throw new Exception(Piwik::translate('UsersManager_ExceptionEmailExists', $email));
- }
- if (!Piwik::isValidEmailString($email)) {
- throw new Exception(Piwik::translate('UsersManager_ExceptionInvalidEmail'));
- }
- }
- private function getCleanAlias($alias, $userLogin)
- {
- if (empty($alias)) {
- $alias = $userLogin;
- }
- return $alias;
- }
- /**
- * Add a user in the database.
- * A user is defined by
- * - a login that has to be unique and valid
- * - a password that has to be valid
- * - an alias
- * - an email that has to be in a correct format
- *
- * @see userExists()
- * @see isValidLoginString()
- * @see isValidPasswordString()
- * @see isValidEmailString()
- *
- * @exception in case of an invalid parameter
- */
- public function addUser($userLogin, $password, $email, $alias = false)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkLogin($userLogin);
- $this->checkEmail($email);
- $password = Common::unsanitizeInputValue($password);
- UsersManager::checkPassword($password);
- $alias = $this->getCleanAlias($alias, $userLogin);
- $passwordTransformed = UsersManager::getPasswordHash($password);
- $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed);
- $this->model->addUser($userLogin, $passwordTransformed, $email, $alias, $token_auth, Date::now()->getDatetime());
- // we reload the access list which doesn't yet take in consideration this new user
- Access::getInstance()->reloadAccess();
- Cache::deleteTrackerCache();
- /**
- * Triggered after a new user is created.
- *
- * @param string $userLogin The new user's login handle.
- */
- Piwik::postEvent('UsersManager.addUser.end', array($userLogin));
- }
- /**
- * Enable or disable Super user access to the given user login. Note: When granting Super User access all previous
- * permissions of the user will be removed as the user gains access to everything.
- *
- * @param string $userLogin the user login.
- * @param bool|int $hasSuperUserAccess true or '1' to grant Super User access, false or '0' to remove Super User
- * access.
- * @throws \Exception
- */
- public function setSuperUserAccess($userLogin, $hasSuperUserAccess)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkUserIsNotAnonymous($userLogin);
- $this->checkUserExists($userLogin);
- if (!$hasSuperUserAccess && $this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
- $message = Piwik::translate("UsersManager_ExceptionRemoveSuperUserAccessOnlySuperUser", $userLogin)
- . " "
- . Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
- throw new Exception($message);
- }
- $this->model->deleteUserAccess($userLogin);
- $this->model->setSuperUserAccess($userLogin, $hasSuperUserAccess);
- }
- /**
- * Detect whether the current user has super user access or not.
- *
- * @return bool
- */
- public function hasSuperUserAccess()
- {
- return Piwik::hasUserSuperUserAccess();
- }
- /**
- * Returns a list of all Super Users containing there userLogin and email address.
- *
- * @return array
- */
- public function getUsersHavingSuperUserAccess()
- {
- Piwik::checkUserIsNotAnonymous();
- return $this->model->getUsersHavingSuperUserAccess();
- }
- /**
- * 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.
- *
- * @see addUser() for all the parameters
- */
- public function updateUser($userLogin, $password = false, $email = false, $alias = false,
- $_isPasswordHashed = false)
- {
- Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
- $this->checkUserIsNotAnonymous($userLogin);
- $userInfo = $this->getUser($userLogin);
- $passwordHasBeenUpdated = false;
- if (empty($password)) {
- $password = $userInfo['password'];
- } else {
- $password = Common::unsanitizeInputValue($password);
- if (!$_isPasswordHashed) {
- UsersManager::checkPassword($password);
- $password = UsersManager::getPasswordHash($password);
- }
- $passwordHasBeenUpdated = true;
- }
- if (empty($alias)) {
- $alias = $userInfo['alias'];
- }
- if (empty($email)) {
- $email = $userInfo['email'];
- }
- if ($email != $userInfo['email']) {
- $this->checkEmail($email);
- }
- $alias = $this->getCleanAlias($alias, $userLogin);
- $token_auth = $this->getTokenAuth($userLogin, $password);
- $this->model->updateUser($userLogin, $password, $email, $alias, $token_auth);
- Cache::deleteTrackerCache();
- /**
- * Triggered after an existing user has been updated.
- * Event notify about password change.
- *
- * @param string $userLogin The user's login handle.
- * @param boolean $passwordHasBeenUpdated Flag containing information about password change.
- */
- Piwik::postEvent('UsersManager.updateUser.end', array($userLogin, $passwordHasBeenUpdated));
- }
- /**
- * Delete a user and all its access, given its login.
- *
- * @param string $userLogin the user login.
- *
- * @throws Exception if the user doesn't exist
- *
- * @return bool true on success
- */
- public function deleteUser($userLogin)
- {
- Piwik::checkUserHasSuperUserAccess();
- $this->checkUserIsNotAnonymous($userLogin);
- if (!$this->userExists($userLogin)) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionDeleteDoesNotExist", $userLogin));
- }
- if ($this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
- $message = Piwik::translate("UsersManager_ExceptionDeleteOnlyUserWithSuperUserAccess", $userLogin)
- . " "
- . Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
- throw new Exception($message);
- }
- $this->model->deleteUserOnly($userLogin);
- $this->model->deleteUserAccess($userLogin);
- Cache::deleteTrackerCache();
- }
- /**
- * Returns true if the given userLogin is known in the database
- *
- * @param string $userLogin
- * @return bool true if the user is known
- */
- public function userExists($userLogin)
- {
- if ($userLogin == 'anonymous') {
- return true;
- }
- Piwik::checkUserIsNotAnonymous();
- Piwik::checkUserHasSomeViewAccess();
- if ($userLogin == Piwik::getCurrentUserLogin()) {
- return true;
- }
- return $this->model->userExists($userLogin);
- }
- /**
- * Returns true if user with given email (userEmail) is known in the database, or the Super User
- *
- * @param string $userEmail
- * @return bool true if the user is known
- */
- public function userEmailExists($userEmail)
- {
- Piwik::checkUserIsNotAnonymous();
- return $this->model->userEmailExists($userEmail);
- }
- /**
- * Set an access level to a given user for a list of websites ID.
- *
- * If access = 'noaccess' the current access (if any) will be deleted.
- * 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 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);
- $this->checkUserExists($userLogin);
- $this->checkUserHasNotSuperUserAccess($userLogin);
- if ($userLogin == 'anonymous'
- && $access == 'admin'
- ) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionAdminAnonymous"));
- }
- // 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();
- } // in case the idSites is an integer we build an array
- else {
- $idSites = Site::getIdSitesFromIdSitesString($idSites);
- }
- 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 he manages
- 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);
- }
- // we reload the access list which doesn't yet take in consideration this new user access
- Access::getInstance()->reloadAccess();
- Cache::deleteTrackerCache();
- }
- /**
- * Throws an exception is the user login doesn't exist
- *
- * @param string $userLogin user login
- * @throws Exception if the user doesn't exist
- */
- private function checkUserExists($userLogin)
- {
- if (!$this->userExists($userLogin)) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionUserDoesNotExist", $userLogin));
- }
- }
- /**
- * Throws an exception is the user email cannot be found
- *
- * @param string $userEmail user email
- * @throws Exception if the user doesn't exist
- */
- private function checkUserEmailExists($userEmail)
- {
- if (!$this->userEmailExists($userEmail)) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionUserDoesNotExist", $userEmail));
- }
- }
- private function checkUserIsNotAnonymous($userLogin)
- {
- if ($userLogin == 'anonymous') {
- throw new Exception(Piwik::translate("UsersManager_ExceptionEditAnonymous"));
- }
- }
- private function checkUserHasNotSuperUserAccess($userLogin)
- {
- if (Piwik::hasTheUserSuperUserAccess($userLogin)) {
- throw new Exception(Piwik::translate("UsersManager_ExceptionSuperUserAccess"));
- }
- }
- 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();
- return 1 >= count($superUsers) && Piwik::hasTheUserSuperUserAccess($userLogin);
- }
- /**
- * Generates a unique MD5 for the given login & password
- *
- * @param string $userLogin Login
- * @param string $md5Password MD5ied string of the password
- * @throws Exception
- * @return string
- */
- public function getTokenAuth($userLogin, $md5Password)
- {
- if (strlen($md5Password) != 32) {
- throw new Exception(Piwik::translate('UsersManager_ExceptionPasswordMD5HashExpected'));
- }
- return md5($userLogin . $md5Password);
- }
- }