Teknik is a suite of services with attractive and functional interfaces. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Access.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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;
  10. use Piwik\Db;
  11. /**
  12. * Singleton that manages user access to Piwik resources.
  13. *
  14. * To check whether a user has access to a resource, use one of the {@link Piwik Piwik::checkUser...}
  15. * methods.
  16. *
  17. * In Piwik there are four different access levels:
  18. *
  19. * - **no access**: Users with this access level cannot view the resource.
  20. * - **view access**: Users with this access level can view the resource, but cannot modify it.
  21. * - **admin access**: Users with this access level can view and modify the resource.
  22. * - **Super User access**: Only the Super User has this access level. It means the user can do
  23. * whatever he/she wants.
  24. *
  25. * Super user access is required to set some configuration options.
  26. * All other options are specific to the user or to a website.
  27. *
  28. * Access is granted per website. Uses with access for a website can view all
  29. * data associated with that website.
  30. *
  31. */
  32. class Access
  33. {
  34. private static $instance = null;
  35. /**
  36. * Gets the singleton instance. Creates it if necessary.
  37. */
  38. public static function getInstance()
  39. {
  40. if (self::$instance == null) {
  41. self::$instance = new self;
  42. Piwik::postEvent('Access.createAccessSingleton', array(&self::$instance));
  43. }
  44. return self::$instance;
  45. }
  46. /**
  47. * Sets the singleton instance. For testing purposes.
  48. */
  49. public static function setSingletonInstance($instance)
  50. {
  51. self::$instance = $instance;
  52. }
  53. /**
  54. * Array of idsites available to the current user, indexed by permission level
  55. * @see getSitesIdWith*()
  56. *
  57. * @var array
  58. */
  59. protected $idsitesByAccess = null;
  60. /**
  61. * Login of the current user
  62. *
  63. * @var string
  64. */
  65. protected $login = null;
  66. /**
  67. * token_auth of the current user
  68. *
  69. * @var string
  70. */
  71. protected $token_auth = null;
  72. /**
  73. * Defines if the current user is the Super User
  74. * @see hasSuperUserAccess()
  75. *
  76. * @var bool
  77. */
  78. protected $hasSuperUserAccess = false;
  79. /**
  80. * List of available permissions in Piwik
  81. *
  82. * @var array
  83. */
  84. private static $availableAccess = array('noaccess', 'view', 'admin', 'superuser');
  85. /**
  86. * Authentification object (see Auth)
  87. *
  88. * @var Auth
  89. */
  90. private $auth = null;
  91. /**
  92. * Returns the list of the existing Access level.
  93. * Useful when a given API method requests a given acccess Level.
  94. * We first check that the required access level exists.
  95. *
  96. * @return array
  97. */
  98. public static function getListAccess()
  99. {
  100. return self::$availableAccess;
  101. }
  102. /**
  103. * Constructor
  104. */
  105. public function __construct()
  106. {
  107. $this->idsitesByAccess = array(
  108. 'view' => array(),
  109. 'admin' => array(),
  110. 'superuser' => array()
  111. );
  112. }
  113. /**
  114. * Loads the access levels for the current user.
  115. *
  116. * Calls the authentication method to try to log the user in the system.
  117. * If the user credentials are not correct we don't load anything.
  118. * If the login/password is correct the user is either the SuperUser or a normal user.
  119. * We load the access levels for this user for all the websites.
  120. *
  121. * @param null|Auth $auth Auth adapter
  122. * @return bool true on success, false if reloading access failed (when auth object wasn't specified and user is not enforced to be Super User)
  123. */
  124. public function reloadAccess(Auth $auth = null)
  125. {
  126. if (!is_null($auth)) {
  127. $this->auth = $auth;
  128. }
  129. // if the Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail
  130. if (is_null($this->auth)) {
  131. if ($this->hasSuperUserAccess()) {
  132. return $this->reloadAccessSuperUser();
  133. }
  134. return false;
  135. }
  136. // access = array ( idsite => accessIdSite, idsite2 => accessIdSite2)
  137. $result = $this->auth->authenticate();
  138. if (!$result->wasAuthenticationSuccessful()) {
  139. return false;
  140. }
  141. $this->login = $result->getIdentity();
  142. $this->token_auth = $result->getTokenAuth();
  143. // case the superUser is logged in
  144. if ($result->hasSuperUserAccess()) {
  145. return $this->reloadAccessSuperUser();
  146. }
  147. // in case multiple calls to API using different tokens, we ensure we reset it as not SU
  148. $this->setSuperUserAccess(false);
  149. // we join with site in case there are rows in access for an idsite that doesn't exist anymore
  150. // (backward compatibility ; before we deleted the site without deleting rows in _access table)
  151. $accessRaw = $this->getRawSitesWithSomeViewAccess($this->login);
  152. foreach ($accessRaw as $access) {
  153. $this->idsitesByAccess[$access['access']][] = $access['idsite'];
  154. }
  155. return true;
  156. }
  157. public function getRawSitesWithSomeViewAccess($login)
  158. {
  159. return Db::fetchAll(self::getSqlAccessSite("access, t2.idsite"), $login);
  160. }
  161. /**
  162. * Returns the SQL query joining sites and access table for a given login
  163. *
  164. * @param string $select Columns or expression to SELECT FROM table, eg. "MIN(ts_created)"
  165. * @return string SQL query
  166. */
  167. public static function getSqlAccessSite($select)
  168. {
  169. return "SELECT " . $select . "
  170. FROM " . Common::prefixTable('access') . " as t1
  171. JOIN " . Common::prefixTable('site') . " as t2 USING (idsite) " .
  172. " WHERE login = ?";
  173. }
  174. /**
  175. * Reload Super User access
  176. *
  177. * @return bool
  178. */
  179. protected function reloadAccessSuperUser()
  180. {
  181. $this->hasSuperUserAccess = true;
  182. try {
  183. $allSitesId = Plugins\SitesManager\API::getInstance()->getAllSitesId();
  184. } catch (\Exception $e) {
  185. $allSitesId = array();
  186. }
  187. $this->idsitesByAccess['superuser'] = $allSitesId;
  188. return true;
  189. }
  190. /**
  191. * We bypass the normal auth method and give the current user Super User rights.
  192. * This should be very carefully used.
  193. *
  194. * @param bool $bool
  195. */
  196. public function setSuperUserAccess($bool = true)
  197. {
  198. if ($bool) {
  199. $this->reloadAccessSuperUser();
  200. } else {
  201. $this->hasSuperUserAccess = false;
  202. $this->idsitesByAccess['superuser'] = array();
  203. }
  204. }
  205. /**
  206. * Returns true if the current user is logged in as the Super User
  207. *
  208. * @return bool
  209. */
  210. public function hasSuperUserAccess()
  211. {
  212. return $this->hasSuperUserAccess;
  213. }
  214. /**
  215. * Returns the current user login
  216. *
  217. * @return string|null
  218. */
  219. public function getLogin()
  220. {
  221. return $this->login;
  222. }
  223. /**
  224. * Returns the token_auth used to authenticate this user in the API
  225. *
  226. * @return string|null
  227. */
  228. public function getTokenAuth()
  229. {
  230. return $this->token_auth;
  231. }
  232. /**
  233. * Returns an array of ID sites for which the user has at least a VIEW access.
  234. * Which means VIEW or ADMIN or SUPERUSER.
  235. *
  236. * @return array Example if the user is ADMIN for 4
  237. * and has VIEW access for 1 and 7, it returns array(1, 4, 7);
  238. */
  239. public function getSitesIdWithAtLeastViewAccess()
  240. {
  241. return array_unique(array_merge(
  242. $this->idsitesByAccess['view'],
  243. $this->idsitesByAccess['admin'],
  244. $this->idsitesByAccess['superuser'])
  245. );
  246. }
  247. /**
  248. * Returns an array of ID sites for which the user has an ADMIN access.
  249. *
  250. * @return array Example if the user is ADMIN for 4 and 8
  251. * and has VIEW access for 1 and 7, it returns array(4, 8);
  252. */
  253. public function getSitesIdWithAdminAccess()
  254. {
  255. return array_unique(array_merge(
  256. $this->idsitesByAccess['admin'],
  257. $this->idsitesByAccess['superuser'])
  258. );
  259. }
  260. /**
  261. * Returns an array of ID sites for which the user has a VIEW access only.
  262. *
  263. * @return array Example if the user is ADMIN for 4
  264. * and has VIEW access for 1 and 7, it returns array(1, 7);
  265. * @see getSitesIdWithAtLeastViewAccess()
  266. */
  267. public function getSitesIdWithViewAccess()
  268. {
  269. return $this->idsitesByAccess['view'];
  270. }
  271. /**
  272. * Throws an exception if the user is not the SuperUser
  273. *
  274. * @throws \Piwik\NoAccessException
  275. */
  276. public function checkUserHasSuperUserAccess()
  277. {
  278. if (!$this->hasSuperUserAccess()) {
  279. throw new NoAccessException(Piwik::translate('General_ExceptionPrivilege', array("'superuser'")));
  280. }
  281. }
  282. /**
  283. * If the user doesn't have an ADMIN access for at least one website, throws an exception
  284. *
  285. * @throws \Piwik\NoAccessException
  286. */
  287. public function checkUserHasSomeAdminAccess()
  288. {
  289. if ($this->hasSuperUserAccess()) {
  290. return;
  291. }
  292. $idSitesAccessible = $this->getSitesIdWithAdminAccess();
  293. if (count($idSitesAccessible) == 0) {
  294. throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin')));
  295. }
  296. }
  297. /**
  298. * If the user doesn't have any view permission, throw exception
  299. *
  300. * @throws \Piwik\NoAccessException
  301. */
  302. public function checkUserHasSomeViewAccess()
  303. {
  304. if ($this->hasSuperUserAccess()) {
  305. return;
  306. }
  307. $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
  308. if (count($idSitesAccessible) == 0) {
  309. throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('view')));
  310. }
  311. }
  312. /**
  313. * This method checks that the user has ADMIN access for the given list of websites.
  314. * If the user doesn't have ADMIN access for at least one website of the list, we throw an exception.
  315. *
  316. * @param int|array $idSites List of ID sites to check
  317. * @throws \Piwik\NoAccessException If for any of the websites the user doesn't have an ADMIN access
  318. */
  319. public function checkUserHasAdminAccess($idSites)
  320. {
  321. if ($this->hasSuperUserAccess()) {
  322. return;
  323. }
  324. $idSites = $this->getIdSites($idSites);
  325. $idSitesAccessible = $this->getSitesIdWithAdminAccess();
  326. foreach ($idSites as $idsite) {
  327. if (!in_array($idsite, $idSitesAccessible)) {
  328. throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite)));
  329. }
  330. }
  331. }
  332. /**
  333. * This method checks that the user has VIEW or ADMIN access for the given list of websites.
  334. * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception.
  335. *
  336. * @param int|array|string $idSites List of ID sites to check (integer, array of integers, string comma separated list of integers)
  337. * @throws \Piwik\NoAccessException If for any of the websites the user doesn't have an VIEW or ADMIN access
  338. */
  339. public function checkUserHasViewAccess($idSites)
  340. {
  341. if ($this->hasSuperUserAccess()) {
  342. return;
  343. }
  344. $idSites = $this->getIdSites($idSites);
  345. $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
  346. foreach ($idSites as $idsite) {
  347. if (!in_array($idsite, $idSitesAccessible)) {
  348. throw new NoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite)));
  349. }
  350. }
  351. }
  352. /**
  353. * @param int|array|string $idSites
  354. * @return array
  355. * @throws \Piwik\NoAccessException
  356. */
  357. protected function getIdSites($idSites)
  358. {
  359. if ($idSites === 'all') {
  360. $idSites = $this->getSitesIdWithAtLeastViewAccess();
  361. }
  362. $idSites = Site::getIdSitesFromIdSitesString($idSites);
  363. if (empty($idSites)) {
  364. throw new NoAccessException("The parameter 'idSite=' is missing from the request.");
  365. }
  366. return $idSites;
  367. }
  368. }
  369. /**
  370. * Exception thrown when a user doesn't have sufficient access to a resource.
  371. *
  372. * @api
  373. */
  374. class NoAccessException extends \Exception
  375. {
  376. }