PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.6.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.6.0
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Access.php
matomo / app / core Last commit date
API 4 years ago Access 5 years ago Application 4 years ago Archive 4 years ago ArchiveProcessor 4 years ago Archiver 5 years ago AssetManager 4 years ago Auth 5 years ago Category 5 years ago CliMulti 4 years ago Columns 5 years ago Concurrency 4 years ago Config 5 years ago Container 4 years ago CronArchive 4 years ago DataAccess 4 years ago DataFiles 5 years ago DataTable 4 years ago Db 4 years ago DeviceDetector 4 years ago Email 5 years ago Exception 4 years ago Http 5 years ago Intl 4 years ago Mail 5 years ago Measurable 5 years ago Menu 5 years ago Metrics 4 years ago Notification 4 years ago Period 4 years ago Plugin 4 years ago ProfessionalServices 5 years ago Report 5 years ago ReportRenderer 5 years ago Scheduler 4 years ago Segment 5 years ago Session 4 years ago Settings 5 years ago Tracker 4 years ago Translation 4 years ago UpdateCheck 5 years ago Updater 4 years ago Updates 4 years ago Validators 4 years ago View 4 years ago ViewDataTable 5 years ago Visualization 5 years ago Widget 5 years ago .htaccess 6 years ago Access.php 4 years ago Archive.php 4 years ago ArchiveProcessor.php 4 years ago AssetManager.php 5 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 4 years ago Common.php 4 years ago Config.php 4 years ago Console.php 5 years ago Context.php 5 years ago Cookie.php 4 years ago CronArchive.php 4 years ago DataArray.php 4 years ago DataTable.php 4 years ago Date.php 4 years ago Db.php 4 years ago DbHelper.php 4 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 4 years ago ExceptionHandler.php 4 years ago FileIntegrity.php 5 years ago Filechecks.php 5 years ago Filesystem.php 4 years ago FrontController.php 4 years ago Http.php 4 years ago IP.php 4 years ago Log.php 4 years ago LogDeleter.php 4 years ago Mail.php 4 years ago Metrics.php 5 years ago NoAccessException.php 5 years ago Nonce.php 4 years ago Notification.php 5 years ago NumberFormatter.php 5 years ago Option.php 4 years ago Period.php 5 years ago Piwik.php 4 years ago Plugin.php 4 years ago Profiler.php 4 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 4 years ago QuickForm2.php 5 years ago RankingQuery.php 5 years ago ReportRenderer.php 5 years ago Segment.php 5 years ago Sequence.php 5 years ago Session.php 4 years ago SettingsPiwik.php 5 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 4 years ago SupportedBrowser.php 4 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 4 years ago Twig.php 4 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 5 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 4 years ago UrlHelper.php 4 years ago Version.php 4 years ago View.php 4 years ago bootstrap.php 5 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 5 years ago
Access.php
769 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\Access\CapabilitiesProvider;
13 use Piwik\API\Request;
14 use Piwik\Access\RolesProvider;
15 use Piwik\Container\StaticContainer;
16 use Piwik\Plugins\SitesManager\API as SitesManagerApi;
17 use Piwik\Session\SessionAuth;
18
19 /**
20 * Singleton that manages user access to Piwik resources.
21 *
22 * To check whether a user has access to a resource, use one of the {@link Piwik Piwik::checkUser...}
23 * methods.
24 *
25 * In Piwik there are four different access levels:
26 *
27 * - **no access**: Users with this access level cannot view the resource.
28 * - **view access**: Users with this access level can view the resource, but cannot modify it.
29 * - **admin access**: Users with this access level can view and modify the resource.
30 * - **Super User access**: Only the Super User has this access level. It means the user can do
31 * whatever they want.
32 *
33 * Super user access is required to set some configuration options.
34 * All other options are specific to the user or to a website.
35 *
36 * Access is granted per website. Uses with access for a website can view all
37 * data associated with that website.
38 *
39 */
40 class Access
41 {
42 /**
43 * Array of idsites available to the current user, indexed by permission level
44 * @see getSitesIdWith*()
45 *
46 * @var array
47 */
48 protected $idsitesByAccess = null;
49
50 /**
51 * Login of the current user
52 *
53 * @var string
54 */
55 protected $login = null;
56
57 /**
58 * token_auth of the current user
59 *
60 * @var string
61 */
62 protected $token_auth = null;
63
64 /**
65 * Defines if the current user is the Super User
66 * @see hasSuperUserAccess()
67 *
68 * @var bool
69 */
70 protected $hasSuperUserAccess = false;
71
72 /**
73 * Authentification object (see Auth)
74 *
75 * @var Auth
76 */
77 private $auth = null;
78
79 /**
80 * Gets the singleton instance. Creates it if necessary.
81 *
82 * @return self
83 */
84 public static function getInstance()
85 {
86 return StaticContainer::get('Piwik\Access');
87 }
88
89 /**
90 * @var CapabilitiesProvider
91 */
92 protected $capabilityProvider;
93
94 /**
95 * @var RolesProvider
96 */
97 private $roleProvider;
98
99 /**
100 * Constructor
101 */
102 public function __construct(RolesProvider $roleProvider = null, CapabilitiesProvider $capabilityProvider = null)
103 {
104 if (!isset($roleProvider)) {
105 $roleProvider = StaticContainer::get('Piwik\Access\RolesProvider');
106 }
107 if (!isset($capabilityProvider)) {
108 $capabilityProvider = StaticContainer::get('Piwik\Access\CapabilitiesProvider');
109 }
110 $this->roleProvider = $roleProvider;
111 $this->capabilityProvider = $capabilityProvider;
112
113 $this->resetSites();
114 }
115
116 private function resetSites()
117 {
118 $this->idsitesByAccess = array(
119 'view' => array(),
120 'write' => array(),
121 'admin' => array(),
122 'superuser' => array(),
123 );
124 }
125
126 /**
127 * Loads the access levels for the current user.
128 *
129 * Calls the authentication method to try to log the user in the system.
130 * If the user credentials are not correct we don't load anything.
131 * If the login/password is correct the user is either the SuperUser or a normal user.
132 * We load the access levels for this user for all the websites.
133 *
134 * @param null|Auth $auth Auth adapter
135 * @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)
136 */
137 public function reloadAccess(Auth $auth = null)
138 {
139 $this->resetSites();
140
141 if (isset($auth)) {
142 $this->auth = $auth;
143 }
144
145 if ($this->hasSuperUserAccess()) {
146 $this->makeSureLoginNameIsSet();
147 return true;
148 }
149
150 $this->token_auth = null;
151 $this->login = null;
152
153 // if the Auth wasn't set, we may be in the special case of setSuperUser(), otherwise we fail TODO: docs + review
154 if (!isset($this->auth)) {
155 return false;
156 }
157
158 $result = null;
159
160 $forceApiSessionPost = Common::getRequestVar('force_api_session', 0, 'int', $_POST);
161 $forceApiSessionGet = Common::getRequestVar('force_api_session', 0, 'int', $_GET);
162 $isApiRequest = Piwik::getModule() === 'API' && (Piwik::getAction() === 'index' || !Piwik::getAction());
163 $apiMethod = Request::getMethodIfApiRequest(null);
164 $isGetApiRequest = !empty($apiMethod) && 1 === substr_count($apiMethod, '.') && strpos($apiMethod, '.get') > 0;
165
166 if (($forceApiSessionPost && $isApiRequest) || ($forceApiSessionGet && $isApiRequest && $isGetApiRequest)) {
167 $request = ($forceApiSessionGet && $isApiRequest && $isGetApiRequest) ? $_GET : $_POST;
168 $tokenAuth = Common::getRequestVar('token_auth', '', 'string', $request);
169 Session::start();
170 $auth = StaticContainer::get(SessionAuth::class);
171 $auth->setTokenAuth($tokenAuth);
172 $result = $auth->authenticate();
173 // Note: We do not post a failed login event at this point on purpose
174 // If using the SessionAuth doesn't work, the FrontController will try to reload the Auth using
175 // the token_auth only. If that works everything is "fine" and the `force_api_session` parameter was
176 // unneeded. If that fails as well it will trigger the failed login event
177 // See FrontController::init() or Request::reloadAuthUsingTokenAuth()
178 Session::close();
179 // if not successful, we will fallback to regular auth
180 }
181
182 // access = array ( idsite => accessIdSite, idsite2 => accessIdSite2)
183 if (!$result || !$result->wasAuthenticationSuccessful()) {
184 $result = $this->auth->authenticate();
185 }
186
187 if (!$result->wasAuthenticationSuccessful()) {
188 return false;
189 }
190
191 $this->login = $result->getIdentity();
192 $this->token_auth = $result->getTokenAuth();
193
194 // case the superUser is logged in
195 if ($result->hasSuperUserAccess()) {
196 $this->setSuperUserAccess(true);
197 }
198
199 return true;
200 }
201
202 public function getRawSitesWithSomeViewAccess($login)
203 {
204 $sql = self::getSqlAccessSite("access, t2.idsite");
205
206 return Db::fetchAll($sql, $login);
207 }
208
209 /**
210 * Returns the SQL query joining sites and access table for a given login
211 *
212 * @param string $select Columns or expression to SELECT FROM table, eg. "MIN(ts_created)"
213 * @return string SQL query
214 */
215 public static function getSqlAccessSite($select)
216 {
217 $access = Common::prefixTable('access');
218 $siteTable = Common::prefixTable('site');
219
220 return "SELECT " . $select . " FROM " . $access . " as t1
221 JOIN " . $siteTable . " as t2 USING (idsite) WHERE login = ?";
222 }
223
224 /**
225 * Make sure a login name is set
226 *
227 * @return true
228 */
229 protected function makeSureLoginNameIsSet()
230 {
231 if (empty($this->login)) {
232 // flag to force non empty login so Super User is not mistaken for anonymous
233 $this->login = 'super user was set';
234 }
235 }
236
237 protected function loadSitesIfNeeded()
238 {
239 if ($this->hasSuperUserAccess) {
240 if (empty($this->idsitesByAccess['superuser'])) {
241 try {
242 $api = SitesManagerApi::getInstance();
243 $allSitesId = $api->getAllSitesId();
244 } catch (\Exception $e) {
245 $allSitesId = array();
246 }
247 $this->idsitesByAccess['superuser'] = $allSitesId;
248 }
249 } elseif (isset($this->login)) {
250 if (empty($this->idsitesByAccess['view'])
251 && empty($this->idsitesByAccess['write'])
252 && empty($this->idsitesByAccess['admin'])
253 ) {
254 // we join with site in case there are rows in access for an idsite that doesn't exist anymore
255 // (backward compatibility ; before we deleted the site without deleting rows in _access table)
256 $accessRaw = $this->getRawSitesWithSomeViewAccess($this->login);
257
258 foreach ($accessRaw as $access) {
259 $accessType = $access['access'];
260 $this->idsitesByAccess[$accessType][] = $access['idsite'];
261
262 if ($this->roleProvider->isValidRole($accessType)) {
263 foreach ($this->capabilityProvider->getAllCapabilities() as $capability) {
264 if ($capability->hasRoleCapability($accessType)) {
265 // we automatically add this capability
266 if (!isset($this->idsitesByAccess[$capability->getId()])) {
267 $this->idsitesByAccess[$capability->getId()] = array();
268 }
269 $this->idsitesByAccess[$capability->getId()][] = $access['idsite'];
270 }
271 }
272 }
273 }
274
275 /**
276 * Triggered after the initial access levels and permissions for the current user are loaded. Use this
277 * event to modify the current user's permissions (for example, making sure every user has view access
278 * to a specific site).
279 *
280 * **Example**
281 *
282 * function (&$idsitesByAccess, $login) {
283 * if ($login == 'somespecialuser') {
284 * return;
285 * }
286 *
287 * $idsitesByAccess['view'][] = $mySpecialIdSite;
288 * }
289 *
290 * @param array[] &$idsitesByAccess The current user's access levels for individual sites. Maps role and
291 * capability IDs to list of site IDs, eg:
292 *
293 * ```
294 * [
295 * 'view' => [1, 2, 3],
296 * 'write' => [4, 5],
297 * 'admin' => [],
298 * ]
299 * ```
300 * @param string $login The current user's login.
301 */
302 Piwik::postEvent('Access.modifyUserAccess', [&$this->idsitesByAccess, $this->login]);
303 }
304 }
305 }
306
307 /**
308 * We bypass the normal auth method and give the current user Super User rights.
309 * This should be very carefully used.
310 *
311 * @param bool $bool
312 */
313 public function setSuperUserAccess($bool = true)
314 {
315 $this->hasSuperUserAccess = (bool) $bool;
316
317 if ($bool) {
318 $this->makeSureLoginNameIsSet();
319 } else {
320 $this->resetSites();
321 }
322 }
323
324 /**
325 * Returns true if the current user is logged in as the Super User
326 *
327 * @return bool
328 */
329 public function hasSuperUserAccess()
330 {
331 return $this->hasSuperUserAccess;
332 }
333
334 /**
335 * Returns the current user login
336 *
337 * @return string|null
338 */
339 public function getLogin()
340 {
341 return $this->login;
342 }
343
344 /**
345 * Returns the token_auth used to authenticate this user in the API
346 *
347 * @return string|null
348 */
349 public function getTokenAuth()
350 {
351 return $this->token_auth;
352 }
353
354 /**
355 * Returns an array of ID sites for which the user has at least a VIEW access.
356 * Which means VIEW OR WRITE or ADMIN or SUPERUSER.
357 *
358 * @return array Example if the user is ADMIN for 4
359 * and has VIEW access for 1 and 7, it returns array(1, 4, 7);
360 */
361 public function getSitesIdWithAtLeastViewAccess()
362 {
363 $this->loadSitesIfNeeded();
364
365 return array_unique(array_merge(
366 $this->idsitesByAccess['view'],
367 $this->idsitesByAccess['write'],
368 $this->idsitesByAccess['admin'],
369 $this->idsitesByAccess['superuser'])
370 );
371 }
372
373 /**
374 * Returns an array of ID sites for which the user has at least a WRITE access.
375 * Which means WRITE or ADMIN or SUPERUSER.
376 *
377 * @return array Example if the user is WRITE for 4 and 8
378 * and has VIEW access for 1 and 7, it returns array(4, 8);
379 */
380 public function getSitesIdWithAtLeastWriteAccess()
381 {
382 $this->loadSitesIfNeeded();
383
384 return array_unique(array_merge(
385 $this->idsitesByAccess['write'],
386 $this->idsitesByAccess['admin'],
387 $this->idsitesByAccess['superuser'])
388 );
389 }
390
391 /**
392 * Returns an array of ID sites for which the user has an ADMIN access.
393 *
394 * @return array Example if the user is ADMIN for 4 and 8
395 * and has VIEW access for 1 and 7, it returns array(4, 8);
396 */
397 public function getSitesIdWithAdminAccess()
398 {
399 $this->loadSitesIfNeeded();
400
401 return array_unique(array_merge(
402 $this->idsitesByAccess['admin'],
403 $this->idsitesByAccess['superuser'])
404 );
405 }
406
407 /**
408 * Returns an array of ID sites for which the user has a VIEW access only.
409 *
410 * @return array Example if the user is ADMIN for 4
411 * and has VIEW access for 1 and 7, it returns array(1, 7);
412 * @see getSitesIdWithAtLeastViewAccess()
413 */
414 public function getSitesIdWithViewAccess()
415 {
416 $this->loadSitesIfNeeded();
417
418 return $this->idsitesByAccess['view'];
419 }
420
421 /**
422 * Returns an array of ID sites for which the user has a WRITE access only.
423 *
424 * @return array Example if the user is ADMIN for 4
425 * and has WRITE access for 1 and 7, it returns array(1, 7);
426 * @see getSitesIdWithAtLeastWriteAccess()
427 */
428 public function getSitesIdWithWriteAccess()
429 {
430 $this->loadSitesIfNeeded();
431
432 return $this->idsitesByAccess['write'];
433 }
434
435 /**
436 * Throws an exception if the user is not the SuperUser
437 *
438 * @throws \Piwik\NoAccessException
439 */
440 public function checkUserHasSuperUserAccess()
441 {
442 if (!$this->hasSuperUserAccess()) {
443 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilege', array("'superuser'")));
444 }
445 }
446
447 /**
448 * Returns `true` if the current user has admin access to at least one site.
449 *
450 * @return bool
451 */
452 public function isUserHasSomeWriteAccess()
453 {
454 if ($this->hasSuperUserAccess()) {
455 return true;
456 }
457
458 $idSitesAccessible = $this->getSitesIdWithAtLeastWriteAccess();
459
460 return count($idSitesAccessible) > 0;
461 }
462
463 /**
464 * Returns `true` if the current user has admin access to at least one site.
465 *
466 * @return bool
467 */
468 public function isUserHasSomeAdminAccess()
469 {
470 if ($this->hasSuperUserAccess()) {
471 return true;
472 }
473
474 $idSitesAccessible = $this->getSitesIdWithAdminAccess();
475
476 return count($idSitesAccessible) > 0;
477 }
478
479 /**
480 * If the user doesn't have an WRITE access for at least one website, throws an exception
481 *
482 * @throws \Piwik\NoAccessException
483 */
484 public function checkUserHasSomeWriteAccess()
485 {
486 if (!$this->isUserHasSomeWriteAccess()) {
487 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('write')));
488 }
489 }
490
491 /**
492 * If the user doesn't have an ADMIN access for at least one website, throws an exception
493 *
494 * @throws \Piwik\NoAccessException
495 */
496 public function checkUserHasSomeAdminAccess()
497 {
498 if (!$this->isUserHasSomeAdminAccess()) {
499 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('admin')));
500 }
501 }
502
503 /**
504 * If the user doesn't have any view permission, throw exception
505 *
506 * @throws \Piwik\NoAccessException
507 */
508 public function checkUserHasSomeViewAccess()
509 {
510 if ($this->hasSuperUserAccess()) {
511 return;
512 }
513
514 $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
515
516 if (count($idSitesAccessible) == 0) {
517 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAtLeastOneWebsite', array('view')));
518 }
519 }
520
521 /**
522 * This method checks that the user has ADMIN access for the given list of websites.
523 * If the user doesn't have ADMIN access for at least one website of the list, we throw an exception.
524 *
525 * @param int|array $idSites List of ID sites to check
526 * @throws \Piwik\NoAccessException If for any of the websites the user doesn't have an ADMIN access
527 */
528 public function checkUserHasAdminAccess($idSites)
529 {
530 if ($this->hasSuperUserAccess()) {
531 return;
532 }
533
534 $idSites = $this->getIdSites($idSites);
535 $idSitesAccessible = $this->getSitesIdWithAdminAccess();
536
537 foreach ($idSites as $idsite) {
538 if (!in_array($idsite, $idSitesAccessible)) {
539 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'admin'", $idsite)));
540 }
541 }
542 }
543
544 /**
545 * This method checks that the user has VIEW or ADMIN access for the given list of websites.
546 * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception.
547 *
548 * @param int|array|string $idSites List of ID sites to check (integer, array of integers, string comma separated list of integers)
549 * @throws \Piwik\NoAccessException If for any of the websites the user doesn't have an VIEW or ADMIN access
550 */
551 public function checkUserHasViewAccess($idSites)
552 {
553 if ($this->hasSuperUserAccess()) {
554 return;
555 }
556
557 $idSites = $this->getIdSites($idSites);
558 $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess();
559
560 foreach ($idSites as $idsite) {
561 if (!in_array($idsite, $idSitesAccessible)) {
562 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite)));
563 }
564 }
565 }
566
567 /**
568 * This method checks that the user has VIEW or ADMIN access for the given list of websites.
569 * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception.
570 *
571 * @param int|array|string $idSites List of ID sites to check (integer, array of integers, string comma separated list of integers)
572 * @throws \Piwik\NoAccessException If for any of the websites the user doesn't have an VIEW or ADMIN access
573 */
574 public function checkUserHasWriteAccess($idSites)
575 {
576 if ($this->hasSuperUserAccess()) {
577 return;
578 }
579
580 $idSites = $this->getIdSites($idSites);
581 $idSitesAccessible = $this->getSitesIdWithAtLeastWriteAccess();
582
583 foreach ($idSites as $idsite) {
584 if (!in_array($idsite, $idSitesAccessible)) {
585 $this->throwNoAccessException(Piwik::translate('General_ExceptionPrivilegeAccessWebsite', array("'write'", $idsite)));
586 }
587 }
588 }
589
590 public function checkUserIsNotAnonymous()
591 {
592 if ($this->hasSuperUserAccess()) {
593 return;
594 }
595 if (Piwik::isUserIsAnonymous()) {
596 $this->throwNoAccessException(Piwik::translate('General_YouMustBeLoggedIn'));
597 }
598 }
599
600 private function getSitesIdWithCapability($capability)
601 {
602 if (!empty($this->idsitesByAccess[$capability])) {
603 return $this->idsitesByAccess[$capability];
604 }
605 return array();
606 }
607
608 public function checkUserHasCapability($idSites, $capability)
609 {
610 if ($this->hasSuperUserAccess()) {
611 return;
612 }
613
614 $idSites = $this->getIdSites($idSites);
615 $idSitesAccessible = $this->getSitesIdWithCapability($capability);
616
617 foreach ($idSites as $idsite) {
618 if (!in_array($idsite, $idSitesAccessible)) {
619 $this->throwNoAccessException(Piwik::translate('ExceptionCapabilityAccessWebsite', array("'" . $capability ."'", $idsite)));
620 }
621 }
622
623 // a capability applies only when the user also has at least view access
624 $this->checkUserHasViewAccess($idSites);
625 }
626
627 /**
628 * @param int|array|string $idSites
629 * @return array
630 * @throws \Piwik\NoAccessException
631 */
632 protected function getIdSites($idSites)
633 {
634 if ($idSites === 'all') {
635 $idSites = $this->getSitesIdWithAtLeastViewAccess();
636 }
637
638 $idSites = Site::getIdSitesFromIdSitesString($idSites);
639
640 if (empty($idSites)) {
641 $this->throwNoAccessException("The parameter 'idSite=' is missing from the request.");
642 }
643
644 return $idSites;
645 }
646
647 /**
648 * Executes a callback with superuser privileges, making sure those privileges are rescinded
649 * before this method exits. Privileges will be rescinded even if an exception is thrown.
650 *
651 * @param callback $function The callback to execute. Should accept no arguments.
652 * @return mixed The result of `$function`.
653 * @throws Exception rethrows any exceptions thrown by `$function`.
654 * @api
655 */
656 public static function doAsSuperUser($function)
657 {
658 $isSuperUser = self::getInstance()->hasSuperUserAccess();
659
660 if ($isSuperUser) {
661 return $function();
662 }
663
664 $access = self::getInstance();
665 $login = $access->getLogin();
666 $shouldResetLogin = empty($login); // make sure to reset login if a login was set by "makeSureLoginNameIsSet()"
667 $access->setSuperUserAccess(true);
668
669 try {
670 $result = $function();
671 } catch (\Throwable $ex) {
672 $access->setSuperUserAccess($isSuperUser);
673 if ($shouldResetLogin) {
674 $access->login = null;
675 }
676
677 throw $ex;
678 }
679
680 if ($shouldResetLogin) {
681 $access->login = null;
682 }
683 $access->setSuperUserAccess($isSuperUser);
684
685 return $result;
686 }
687
688 /**
689 * Returns the level of access the current user has to the given site.
690 *
691 * @param int $idSite The site to check.
692 * @return string The access level, eg, 'view', 'admin', 'noaccess'.
693 */
694 public function getRoleForSite($idSite)
695 {
696 if ($this->hasSuperUserAccess
697 || in_array($idSite, $this->getSitesIdWithAdminAccess())
698 ) {
699 return 'admin';
700 }
701
702 if (in_array($idSite, $this->getSitesIdWithWriteAccess())) {
703 return 'write';
704 }
705
706 if (in_array($idSite, $this->getSitesIdWithViewAccess())) {
707 return 'view';
708 }
709
710 return 'noaccess';
711 }
712
713 /**
714 * Returns the capabilities the current user has for a given site.
715 *
716 * @param int $idSite The site to check.
717 * @return string[] The capabilities the user has.
718 */
719 public function getCapabilitiesForSite($idSite)
720 {
721 $result = [];
722 foreach ($this->capabilityProvider->getAllCapabilityIds() as $capabilityId) {
723 if (empty($this->idsitesByAccess[$capabilityId])) {
724 continue;
725 }
726
727 if (in_array($idSite, $this->idsitesByAccess[$capabilityId])) {
728 $result[] = $capabilityId;
729 }
730 }
731 return $result;
732 }
733
734 /**
735 * Throw a NoAccessException with the given message, or a more generic 'You need to log in' message if the
736 * user is not currently logged in (e.g. if session has expired).
737 *
738 * @param $message
739 * @throws NoAccessException
740 */
741 private function throwNoAccessException($message)
742 {
743 if (Piwik::isUserIsAnonymous() && !Request::isRootRequestApiRequest()) {
744 $message = Piwik::translate('General_YouMustBeLoggedIn');
745
746 // Try to detect whether user was previously logged in so that we can display a different message
747 $referrer = Url::getReferrer();
748 $matomoUrl = SettingsPiwik::getPiwikUrl();
749 if ($referrer && $matomoUrl && Url::isValidHost(Url::getHostFromUrl($referrer)) &&
750 strpos($referrer, $matomoUrl) === 0
751 ) {
752 $message = Piwik::translate('General_YourSessionHasExpired');
753 }
754 }
755
756 throw new NoAccessException($message);
757 }
758
759 /**
760 * Returns true if the current user is logged in or not.
761 *
762 * @return bool
763 */
764 public function isUserLoggedIn()
765 {
766 return !empty($this->login);
767 }
768 }
769