PageRenderTime 26ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/UsersManager/API.php

https://github.com/CodeYellowBV/piwik
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
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\UsersManager;
  10. use Exception;
  11. use Piwik\Access;
  12. use Piwik\Common;
  13. use Piwik\Config;
  14. use Piwik\Date;
  15. use Piwik\Option;
  16. use Piwik\Piwik;
  17. use Piwik\Site;
  18. use Piwik\Tracker\Cache;
  19. /**
  20. * The UsersManager API lets you Manage Users and their permissions to access specific websites.
  21. *
  22. * You can create users via "addUser", update existing users via "updateUser" and delete users via "deleteUser".
  23. * There are many ways to list users based on their login "getUser" and "getUsers", their email "getUserByEmail",
  24. * or which users have permission (view or admin) to access the specified websites "getUsersWithSiteAccess".
  25. *
  26. * Existing Permissions are listed given a login via "getSitesAccessFromUser", or a website ID via "getUsersAccessFromSite",
  27. * or you can list all users and websites for a given permission via "getUsersSitesFromAccess". Permissions are set and updated
  28. * via the method "setUserAccess".
  29. * See also the documentation about <a href='http://piwik.org/docs/manage-users/' target='_blank'>Managing Users</a> in Piwik.
  30. */
  31. class API extends \Piwik\Plugin\API
  32. {
  33. /**
  34. * @var Model
  35. */
  36. private $model;
  37. const PREFERENCE_DEFAULT_REPORT = 'defaultReport';
  38. const PREFERENCE_DEFAULT_REPORT_DATE = 'defaultReportDate';
  39. static private $instance = null;
  40. protected function __construct()
  41. {
  42. $this->model = new Model();
  43. }
  44. /**
  45. * You can create your own Users Plugin to override this class.
  46. * Example of how you would overwrite the UsersManager_API with your own class:
  47. * Call the following in your plugin __construct() for example:
  48. *
  49. * Registry::set('UsersManager_API', \Piwik\Plugins\MyCustomUsersManager\API::getInstance());
  50. *
  51. * @throws Exception
  52. * @return \Piwik\Plugins\UsersManager\API
  53. */
  54. static public function getInstance()
  55. {
  56. try {
  57. $instance = \Piwik\Registry::get('UsersManager_API');
  58. if (!($instance instanceof API)) {
  59. // Exception is caught below and corrected
  60. throw new Exception('UsersManager_API must inherit API');
  61. }
  62. self::$instance = $instance;
  63. } catch (Exception $e) {
  64. self::$instance = new self;
  65. \Piwik\Registry::set('UsersManager_API', self::$instance);
  66. }
  67. return self::$instance;
  68. }
  69. /**
  70. * Sets a user preference
  71. * @param string $userLogin
  72. * @param string $preferenceName
  73. * @param string $preferenceValue
  74. * @return void
  75. */
  76. public function setUserPreference($userLogin, $preferenceName, $preferenceValue)
  77. {
  78. Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
  79. Option::set($this->getPreferenceId($userLogin, $preferenceName), $preferenceValue);
  80. }
  81. /**
  82. * Gets a user preference
  83. * @param string $userLogin
  84. * @param string $preferenceName
  85. * @return bool|string
  86. */
  87. public function getUserPreference($userLogin, $preferenceName)
  88. {
  89. Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
  90. $optionValue = Option::get($this->getPreferenceId($userLogin, $preferenceName));
  91. if ($optionValue !== false) {
  92. return $optionValue;
  93. }
  94. return $this->getDefaultUserPreference($preferenceName, $userLogin);
  95. }
  96. private function getPreferenceId($login, $preference)
  97. {
  98. return $login . '_' . $preference;
  99. }
  100. private function getDefaultUserPreference($preferenceName, $login)
  101. {
  102. switch ($preferenceName) {
  103. case self::PREFERENCE_DEFAULT_REPORT:
  104. $viewableSiteIds = \Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAtLeastViewAccess($login);
  105. return reset($viewableSiteIds);
  106. case self::PREFERENCE_DEFAULT_REPORT_DATE:
  107. return Config::getInstance()->General['default_day'];
  108. default:
  109. return false;
  110. }
  111. }
  112. /**
  113. * Returns the list of all the users
  114. *
  115. * @param string $userLogins Comma separated list of users to select. If not specified, will return all users
  116. * @return array the list of all the users
  117. */
  118. public function getUsers($userLogins = '')
  119. {
  120. Piwik::checkUserHasSomeAdminAccess();
  121. $logins = array();
  122. if (!empty($userLogins)) {
  123. $logins = explode(',', $userLogins);
  124. }
  125. $users = $this->model->getUsers($logins);
  126. // Non Super user can only access login & alias
  127. if (!Piwik::hasUserSuperUserAccess()) {
  128. foreach ($users as &$user) {
  129. $user = array('login' => $user['login'], 'alias' => $user['alias']);
  130. }
  131. }
  132. return $users;
  133. }
  134. /**
  135. * Returns the list of all the users login
  136. *
  137. * @return array the list of all the users login
  138. */
  139. public function getUsersLogin()
  140. {
  141. Piwik::checkUserHasSomeAdminAccess();
  142. return $this->model->getUsersLogin();
  143. }
  144. /**
  145. * For each user, returns the list of website IDs where the user has the supplied $access level.
  146. * If a user doesn't have the given $access to any website IDs,
  147. * the user will not be in the returned array.
  148. *
  149. * @param string Access can have the following values : 'view' or 'admin'
  150. *
  151. * @return array The returned array has the format
  152. * array(
  153. * login1 => array ( idsite1,idsite2),
  154. * login2 => array(idsite2),
  155. * ...
  156. * )
  157. */
  158. public function getUsersSitesFromAccess($access)
  159. {
  160. Piwik::checkUserHasSuperUserAccess();
  161. $this->checkAccessType($access);
  162. return $this->model->getUsersSitesFromAccess($access);
  163. }
  164. /**
  165. * For each user, returns his access level for the given $idSite.
  166. * If a user doesn't have any access to the $idSite ('noaccess'),
  167. * the user will not be in the returned array.
  168. *
  169. * @param int $idSite website ID
  170. *
  171. * @return array The returned array has the format
  172. * array(
  173. * login1 => 'view',
  174. * login2 => 'admin',
  175. * login3 => 'view',
  176. * ...
  177. * )
  178. */
  179. public function getUsersAccessFromSite($idSite)
  180. {
  181. Piwik::checkUserHasAdminAccess($idSite);
  182. return $this->model->getUsersAccessFromSite($idSite);
  183. }
  184. public function getUsersWithSiteAccess($idSite, $access)
  185. {
  186. Piwik::checkUserHasAdminAccess($idSite);
  187. $this->checkAccessType($access);
  188. $logins = $this->model->getUsersLoginWithSiteAccess($idSite, $access);
  189. if (empty($logins)) {
  190. return array();
  191. }
  192. $logins = implode(',', $logins);
  193. return $this->getUsers($logins);
  194. }
  195. /**
  196. * For each website ID, returns the access level of the given $userLogin.
  197. * If the user doesn't have any access to a website ('noaccess'),
  198. * this website will not be in the returned array.
  199. * If the user doesn't have any access, the returned array will be an empty array.
  200. *
  201. * @param string $userLogin User that has to be valid
  202. *
  203. * @return array The returned array has the format
  204. * array(
  205. * idsite1 => 'view',
  206. * idsite2 => 'admin',
  207. * idsite3 => 'view',
  208. * ...
  209. * )
  210. */
  211. public function getSitesAccessFromUser($userLogin)
  212. {
  213. Piwik::checkUserHasSuperUserAccess();
  214. $this->checkUserExists($userLogin);
  215. $this->checkUserHasNotSuperUserAccess($userLogin);
  216. return $this->model->getSitesAccessFromUser($userLogin);
  217. }
  218. /**
  219. * Returns the user information (login, password md5, alias, email, date_registered, etc.)
  220. *
  221. * @param string $userLogin the user login
  222. *
  223. * @return array the user information
  224. */
  225. public function getUser($userLogin)
  226. {
  227. Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
  228. $this->checkUserExists($userLogin);
  229. return $this->model->getUser($userLogin);
  230. }
  231. /**
  232. * Returns the user information (login, password md5, alias, email, date_registered, etc.)
  233. *
  234. * @param string $userEmail the user email
  235. *
  236. * @return array the user information
  237. */
  238. public function getUserByEmail($userEmail)
  239. {
  240. Piwik::checkUserHasSuperUserAccess();
  241. $this->checkUserEmailExists($userEmail);
  242. return $this->model->getUserByEmail($userEmail);
  243. }
  244. private function checkLogin($userLogin)
  245. {
  246. if ($this->userExists($userLogin)) {
  247. throw new Exception(Piwik::translate('UsersManager_ExceptionLoginExists', $userLogin));
  248. }
  249. Piwik::checkValidLoginString($userLogin);
  250. }
  251. private function checkEmail($email)
  252. {
  253. if ($this->userEmailExists($email)) {
  254. throw new Exception(Piwik::translate('UsersManager_ExceptionEmailExists', $email));
  255. }
  256. if (!Piwik::isValidEmailString($email)) {
  257. throw new Exception(Piwik::translate('UsersManager_ExceptionInvalidEmail'));
  258. }
  259. }
  260. private function getCleanAlias($alias, $userLogin)
  261. {
  262. if (empty($alias)) {
  263. $alias = $userLogin;
  264. }
  265. return $alias;
  266. }
  267. /**
  268. * Add a user in the database.
  269. * A user is defined by
  270. * - a login that has to be unique and valid
  271. * - a password that has to be valid
  272. * - an alias
  273. * - an email that has to be in a correct format
  274. *
  275. * @see userExists()
  276. * @see isValidLoginString()
  277. * @see isValidPasswordString()
  278. * @see isValidEmailString()
  279. *
  280. * @exception in case of an invalid parameter
  281. */
  282. public function addUser($userLogin, $password, $email, $alias = false)
  283. {
  284. Piwik::checkUserHasSuperUserAccess();
  285. $this->checkLogin($userLogin);
  286. $this->checkEmail($email);
  287. $password = Common::unsanitizeInputValue($password);
  288. UsersManager::checkPassword($password);
  289. $alias = $this->getCleanAlias($alias, $userLogin);
  290. $passwordTransformed = UsersManager::getPasswordHash($password);
  291. $token_auth = $this->getTokenAuth($userLogin, $passwordTransformed);
  292. $this->model->addUser($userLogin, $passwordTransformed, $email, $alias, $token_auth, Date::now()->getDatetime());
  293. // we reload the access list which doesn't yet take in consideration this new user
  294. Access::getInstance()->reloadAccess();
  295. Cache::deleteTrackerCache();
  296. /**
  297. * Triggered after a new user is created.
  298. *
  299. * @param string $userLogin The new user's login handle.
  300. */
  301. Piwik::postEvent('UsersManager.addUser.end', array($userLogin));
  302. }
  303. /**
  304. * Enable or disable Super user access to the given user login. Note: When granting Super User access all previous
  305. * permissions of the user will be removed as the user gains access to everything.
  306. *
  307. * @param string $userLogin the user login.
  308. * @param bool|int $hasSuperUserAccess true or '1' to grant Super User access, false or '0' to remove Super User
  309. * access.
  310. * @throws \Exception
  311. */
  312. public function setSuperUserAccess($userLogin, $hasSuperUserAccess)
  313. {
  314. Piwik::checkUserHasSuperUserAccess();
  315. $this->checkUserIsNotAnonymous($userLogin);
  316. $this->checkUserExists($userLogin);
  317. if (!$hasSuperUserAccess && $this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
  318. $message = Piwik::translate("UsersManager_ExceptionRemoveSuperUserAccessOnlySuperUser", $userLogin)
  319. . " "
  320. . Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
  321. throw new Exception($message);
  322. }
  323. $this->model->deleteUserAccess($userLogin);
  324. $this->model->setSuperUserAccess($userLogin, $hasSuperUserAccess);
  325. }
  326. /**
  327. * Detect whether the current user has super user access or not.
  328. *
  329. * @return bool
  330. */
  331. public function hasSuperUserAccess()
  332. {
  333. return Piwik::hasUserSuperUserAccess();
  334. }
  335. /**
  336. * Returns a list of all Super Users containing there userLogin and email address.
  337. *
  338. * @return array
  339. */
  340. public function getUsersHavingSuperUserAccess()
  341. {
  342. Piwik::checkUserIsNotAnonymous();
  343. return $this->model->getUsersHavingSuperUserAccess();
  344. }
  345. /**
  346. * Updates a user in the database.
  347. * Only login and password are required (case when we update the password).
  348. * When the password changes, the key token for this user will change, which could break
  349. * its API calls.
  350. *
  351. * @see addUser() for all the parameters
  352. */
  353. public function updateUser($userLogin, $password = false, $email = false, $alias = false,
  354. $_isPasswordHashed = false)
  355. {
  356. Piwik::checkUserHasSuperUserAccessOrIsTheUser($userLogin);
  357. $this->checkUserIsNotAnonymous($userLogin);
  358. $userInfo = $this->getUser($userLogin);
  359. $passwordHasBeenUpdated = false;
  360. if (empty($password)) {
  361. $password = $userInfo['password'];
  362. } else {
  363. $password = Common::unsanitizeInputValue($password);
  364. if (!$_isPasswordHashed) {
  365. UsersManager::checkPassword($password);
  366. $password = UsersManager::getPasswordHash($password);
  367. }
  368. $passwordHasBeenUpdated = true;
  369. }
  370. if (empty($alias)) {
  371. $alias = $userInfo['alias'];
  372. }
  373. if (empty($email)) {
  374. $email = $userInfo['email'];
  375. }
  376. if ($email != $userInfo['email']) {
  377. $this->checkEmail($email);
  378. }
  379. $alias = $this->getCleanAlias($alias, $userLogin);
  380. $token_auth = $this->getTokenAuth($userLogin, $password);
  381. $this->model->updateUser($userLogin, $password, $email, $alias, $token_auth);
  382. Cache::deleteTrackerCache();
  383. /**
  384. * Triggered after an existing user has been updated.
  385. * Event notify about password change.
  386. *
  387. * @param string $userLogin The user's login handle.
  388. * @param boolean $passwordHasBeenUpdated Flag containing information about password change.
  389. */
  390. Piwik::postEvent('UsersManager.updateUser.end', array($userLogin, $passwordHasBeenUpdated));
  391. }
  392. /**
  393. * Delete a user and all its access, given its login.
  394. *
  395. * @param string $userLogin the user login.
  396. *
  397. * @throws Exception if the user doesn't exist
  398. *
  399. * @return bool true on success
  400. */
  401. public function deleteUser($userLogin)
  402. {
  403. Piwik::checkUserHasSuperUserAccess();
  404. $this->checkUserIsNotAnonymous($userLogin);
  405. if (!$this->userExists($userLogin)) {
  406. throw new Exception(Piwik::translate("UsersManager_ExceptionDeleteDoesNotExist", $userLogin));
  407. }
  408. if ($this->isUserTheOnlyUserHavingSuperUserAccess($userLogin)) {
  409. $message = Piwik::translate("UsersManager_ExceptionDeleteOnlyUserWithSuperUserAccess", $userLogin)
  410. . " "
  411. . Piwik::translate("UsersManager_ExceptionYouMustGrantSuperUserAccessFirst");
  412. throw new Exception($message);
  413. }
  414. $this->model->deleteUserOnly($userLogin);
  415. $this->model->deleteUserAccess($userLogin);
  416. Cache::deleteTrackerCache();
  417. }
  418. /**
  419. * Returns true if the given userLogin is known in the database
  420. *
  421. * @param string $userLogin
  422. * @return bool true if the user is known
  423. */
  424. public function userExists($userLogin)
  425. {
  426. if ($userLogin == 'anonymous') {
  427. return true;
  428. }
  429. Piwik::checkUserIsNotAnonymous();
  430. Piwik::checkUserHasSomeViewAccess();
  431. if ($userLogin == Piwik::getCurrentUserLogin()) {
  432. return true;
  433. }
  434. return $this->model->userExists($userLogin);
  435. }
  436. /**
  437. * Returns true if user with given email (userEmail) is known in the database, or the Super User
  438. *
  439. * @param string $userEmail
  440. * @return bool true if the user is known
  441. */
  442. public function userEmailExists($userEmail)
  443. {
  444. Piwik::checkUserIsNotAnonymous();
  445. return $this->model->userEmailExists($userEmail);
  446. }
  447. /**
  448. * Set an access level to a given user for a list of websites ID.
  449. *
  450. * If access = 'noaccess' the current access (if any) will be deleted.
  451. * If access = 'view' or 'admin' the current access level is deleted and updated with the new value.
  452. *
  453. * @param string $userLogin The user login
  454. * @param string $access Access to grant. Must have one of the following value : noaccess, view, admin
  455. * @param int|array $idSites The array of idSites on which to apply the access level for the user.
  456. * 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.
  457. *
  458. * @throws Exception if the user doesn't exist
  459. * @throws Exception if the access parameter doesn't have a correct value
  460. * @throws Exception if any of the given website ID doesn't exist
  461. *
  462. * @return bool true on success
  463. */
  464. public function setUserAccess($userLogin, $access, $idSites)
  465. {
  466. $this->checkAccessType($access);
  467. $this->checkUserExists($userLogin);
  468. $this->checkUserHasNotSuperUserAccess($userLogin);
  469. if ($userLogin == 'anonymous'
  470. && $access == 'admin'
  471. ) {
  472. throw new Exception(Piwik::translate("UsersManager_ExceptionAdminAnonymous"));
  473. }
  474. // in case idSites is all we grant access to all the websites on which the current connected user has an 'admin' access
  475. if ($idSites === 'all') {
  476. $idSites = \Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAdminAccess();
  477. } // in case the idSites is an integer we build an array
  478. else {
  479. $idSites = Site::getIdSitesFromIdSitesString($idSites);
  480. }
  481. if (empty($idSites)) {
  482. throw new Exception('Specify at least one website ID in &idSites=');
  483. }
  484. // it is possible to set user access on websites only for the websites admin
  485. // basically an admin can give the view or the admin access to any user for the websites he manages
  486. Piwik::checkUserHasAdminAccess($idSites);
  487. $this->model->deleteUserAccess($userLogin, $idSites);
  488. // if the access is noaccess then we don't save it as this is the default value
  489. // when no access are specified
  490. if ($access != 'noaccess') {
  491. $this->model->addUserAccess($userLogin, $access, $idSites);
  492. }
  493. // we reload the access list which doesn't yet take in consideration this new user access
  494. Access::getInstance()->reloadAccess();
  495. Cache::deleteTrackerCache();
  496. }
  497. /**
  498. * Throws an exception is the user login doesn't exist
  499. *
  500. * @param string $userLogin user login
  501. * @throws Exception if the user doesn't exist
  502. */
  503. private function checkUserExists($userLogin)
  504. {
  505. if (!$this->userExists($userLogin)) {
  506. throw new Exception(Piwik::translate("UsersManager_ExceptionUserDoesNotExist", $userLogin));
  507. }
  508. }
  509. /**
  510. * Throws an exception is the user email cannot be found
  511. *
  512. * @param string $userEmail user email
  513. * @throws Exception if the user doesn't exist
  514. */
  515. private function checkUserEmailExists($userEmail)
  516. {
  517. if (!$this->userEmailExists($userEmail)) {
  518. throw new Exception(Piwik::translate("UsersManager_ExceptionUserDoesNotExist", $userEmail));
  519. }
  520. }
  521. private function checkUserIsNotAnonymous($userLogin)
  522. {
  523. if ($userLogin == 'anonymous') {
  524. throw new Exception(Piwik::translate("UsersManager_ExceptionEditAnonymous"));
  525. }
  526. }
  527. private function checkUserHasNotSuperUserAccess($userLogin)
  528. {
  529. if (Piwik::hasTheUserSuperUserAccess($userLogin)) {
  530. throw new Exception(Piwik::translate("UsersManager_ExceptionSuperUserAccess"));
  531. }
  532. }
  533. private function checkAccessType($access)
  534. {
  535. $accessList = Access::getListAccess();
  536. // do not allow to set the superUser access
  537. unset($accessList[array_search("superuser", $accessList)]);
  538. if (!in_array($access, $accessList)) {
  539. throw new Exception(Piwik::translate("UsersManager_ExceptionAccessValues", implode(", ", $accessList)));
  540. }
  541. }
  542. private function isUserTheOnlyUserHavingSuperUserAccess($userLogin)
  543. {
  544. $superUsers = $this->getUsersHavingSuperUserAccess();
  545. return 1 >= count($superUsers) && Piwik::hasTheUserSuperUserAccess($userLogin);
  546. }
  547. /**
  548. * Generates a unique MD5 for the given login & password
  549. *
  550. * @param string $userLogin Login
  551. * @param string $md5Password MD5ied string of the password
  552. * @throws Exception
  553. * @return string
  554. */
  555. public function getTokenAuth($userLogin, $md5Password)
  556. {
  557. if (strlen($md5Password) != 32) {
  558. throw new Exception(Piwik::translate('UsersManager_ExceptionPasswordMD5HashExpected'));
  559. }
  560. return md5($userLogin . $md5Password);
  561. }
  562. }