diff options
author | Stefan Giehl <stefan@matomo.org> | 2022-04-12 11:01:53 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-12 11:01:53 +0300 |
commit | b12ef7e8fa0bc4f693244f4110c64564613f380c (patch) | |
tree | 637470d65f01c85d2620f9d393cbef770099f008 | |
parent | 341265be9076ce16b96b023029092ddfb0c8b155 (diff) |
Prevent failure in scheduled report task if a user does no longer have access (#19068)
* Prevent failure in scheduled report task if a user does no longer have access
* apply PSR12 code formatting
-rw-r--r-- | plugins/ScheduledReports/API.php | 196 |
1 files changed, 121 insertions, 75 deletions
diff --git a/plugins/ScheduledReports/API.php b/plugins/ScheduledReports/API.php index 2259c0e56b..0d0e9d6be7 100644 --- a/plugins/ScheduledReports/API.php +++ b/plugins/ScheduledReports/API.php @@ -1,4 +1,5 @@ <?php + /** * Matomo - free/libre analytics platform * @@ -6,6 +7,7 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * */ + namespace Piwik\Plugins\ScheduledReports; use Exception; @@ -67,7 +69,7 @@ class API extends \Piwik\Plugin\API private $enableSaveReportOnDisk = false; // static cache storing reports - public static $cache = array(); + public static $cache = []; /** * @var LoggerInterface @@ -98,9 +100,20 @@ class API extends \Piwik\Plugin\API * * @return int idReport generated */ - public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters, $idSegment = false, - $evolutionPeriodFor = 'prev', $evolutionPeriodN = null, $periodParam = null) - { + public function addReport( + $idSite, + $description, + $period, + $hour, + $reportType, + $reportFormat, + $reports, + $parameters, + $idSegment = false, + $evolutionPeriodFor = 'prev', + $evolutionPeriodN = null, + $periodParam = null + ) { Piwik::checkUserIsNotAnonymous(); Piwik::checkUserHasViewAccess($idSite); @@ -115,13 +128,13 @@ class API extends \Piwik\Plugin\API // validation of requested reports $reports = self::validateRequestedReports($idSite, $reportType, $reports); - $idReport = $this->getModel()->createReport(array( + $idReport = $this->getModel()->createReport([ 'idsite' => $idSite, 'login' => $currentUser, 'description' => $description, 'idsegment' => $idSegment, 'period' => $period, - 'period_param'=> $periodParam, + 'period_param' => $periodParam, 'hour' => $hour, 'type' => $reportType, 'format' => $reportFormat, @@ -131,7 +144,7 @@ class API extends \Piwik\Plugin\API 'deleted' => 0, 'evolution_graph_within_period' => $evolutionPeriodFor == 'each', 'evolution_graph_period_n' => $evolutionPeriodN ?: ImageGraph::getDefaultGraphEvolutionLastPeriods(), - )); + ]); return $idReport; } @@ -155,9 +168,21 @@ class API extends \Piwik\Plugin\API * * @see addReport() */ - public function updateReport($idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters, $idSegment = false, - $evolutionPeriodFor = 'prev', $evolutionPeriodN = null, $periodParam = null) - { + public function updateReport( + $idReport, + $idSite, + $description, + $period, + $hour, + $reportType, + $reportFormat, + $reports, + $parameters, + $idSegment = false, + $evolutionPeriodFor = 'prev', + $evolutionPeriodN = null, + $periodParam = null + ) { Piwik::checkUserIsNotAnonymous(); Piwik::checkUserHasViewAccess($idSite); @@ -176,11 +201,11 @@ class API extends \Piwik\Plugin\API // validation of requested reports $reports = self::validateRequestedReports($idSite, $reportType, $reports); - $this->getModel()->updateReport($idReport, array( + $this->getModel()->updateReport($idReport, [ 'description' => $description, 'idsegment' => $idSegment, 'period' => $period, - 'period_param'=> $periodParam, + 'period_param' => $periodParam, 'hour' => $hour, 'type' => $reportType, 'format' => $reportFormat, @@ -188,9 +213,9 @@ class API extends \Piwik\Plugin\API 'reports' => $reports, 'evolution_graph_within_period' => $evolutionPeriodFor == 'each', 'evolution_graph_period_n' => $evolutionPeriodN ?: ImageGraph::getDefaultGraphEvolutionLastPeriods(), - )); + ]); - self::$cache = array(); + self::$cache = []; } /** @@ -204,11 +229,11 @@ class API extends \Piwik\Plugin\API $report = reset($APIScheduledReports); Piwik::checkUserHasSuperUserAccessOrIsTheUser($report['login']); - $this->getModel()->updateReport($idReport, array( + $this->getModel()->updateReport($idReport, [ 'deleted' => 1, - )); + ]); - self::$cache = array(); + self::$cache = []; } /** @@ -232,10 +257,11 @@ class API extends \Piwik\Plugin\API } $sqlWhere = ''; - $bind = array(); + $bind = []; // Super user gets all reports back, other users only their own - if (!Piwik::hasUserSuperUserAccess() + if ( + !Piwik::hasUserSuperUserAccess() || $ifSuperUserReturnOnlySuperUserReports ) { $sqlWhere .= "AND login = ?"; @@ -269,7 +295,8 @@ class API extends \Piwik\Plugin\API WHERE deleted = 0 $sqlWhere", $bind); // When a specific report was requested and not found, throw an error - if ($idReport !== false + if ( + $idReport !== false && empty($reports) ) { throw new Exception("Requested report couldn't be found."); @@ -282,7 +309,10 @@ class API extends \Piwik\Plugin\API // decode report list $report['reports'] = json_decode($report['reports'], true); - if (!empty($report['parameters']['additionalEmails']) && is_array($report['parameters']['additionalEmails'])) { + if ( + !empty($report['parameters']['additionalEmails']) + && is_array($report['parameters']['additionalEmails']) + ) { $report['parameters']['additionalEmails'] = array_values($report['parameters']['additionalEmails']); } @@ -315,8 +345,15 @@ class API extends \Piwik\Plugin\API * @param bool|false|array $parameters array of parameters * @return array|void */ - public function generateReport($idReport, $date, $language = false, $outputType = false, $period = false, $reportFormat = false, $parameters = false) - { + public function generateReport( + $idReport, + $date, + $language = false, + $outputType = false, + $period = false, + $reportFormat = false, + $parameters = false + ) { Piwik::checkUserIsNotAnonymous(); if (!$this->enableSaveReportOnDisk && $outputType == self::OUTPUT_SAVE_ON_DISK) { @@ -373,7 +410,7 @@ class API extends \Piwik\Plugin\API $availableReportMetadata = \Piwik\Plugins\API\API::getInstance()->getReportMetadata($idSite); // we need to lookup which reports metadata are registered in this report - $reportMetadata = array(); + $reportMetadata = []; foreach ($availableReportMetadata as $metadata) { if (in_array($metadata['uniqueId'], $report['reports'])) { $reportMetadata[] = $metadata; @@ -386,12 +423,12 @@ class API extends \Piwik\Plugin\API $_GET['filter_truncate'] = Config::getInstance()->General['scheduled_reports_truncate']; $prettyDate = null; - $processedReports = array(); + $processedReports = []; $segment = self::getSegment($report['idsegment']); foreach ($reportMetadata as $action) { $apiModule = $action['module']; $apiAction = $action['action']; - $apiParameters = array(); + $apiParameters = []; if (isset($action['parameters'])) { $apiParameters = $action['parameters']; } @@ -410,7 +447,8 @@ class API extends \Piwik\Plugin\API // when a view/admin user created a report, workaround the fact that "Super User" // is enforced in Scheduled tasks, and ensure Multisites.getAll only return the websites that this user can access $userLogin = $report['login']; - if (!empty($userLogin) + if ( + !empty($userLogin) && !Piwik::hasTheUserSuperUserAccess($userLogin) ) { $_GET['_restrictSitesToLogin'] = $userLogin; @@ -418,7 +456,7 @@ class API extends \Piwik\Plugin\API } } - $params = array( + $params = [ 'idSite' => $idSite, 'period' => $period, 'date' => $date, @@ -430,7 +468,7 @@ class API extends \Piwik\Plugin\API 'language' => $language, 'serialize' => 0, 'format' => 'original' - ); + ]; if ($segment != null) { $params['segment'] = urlencode($segment['definition']); @@ -442,10 +480,10 @@ class API extends \Piwik\Plugin\API $processedReport = Request::processRequest('API.getProcessedReport', $params); } catch (\Exception $ex) { // NOTE: can't use warning or error because the log message will appear in the UI as a notification - $this->logger->info("Error getting '?{report}' when generating scheduled report: {exception}", array( + $this->logger->info("Error getting '?{report}' when generating scheduled report: {exception}", [ 'report' => Http::buildQuery($params), 'exception' => $ex->getMessage(), - )); + ]); $this->logger->debug($ex); @@ -492,7 +530,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::PROCESS_REPORTS_EVENT, - array(&$processedReports, $reportType, $outputType, $report) + [&$processedReports, $reportType, $outputType, $report] ); $reportRenderer = null; @@ -514,7 +552,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::GET_RENDERER_INSTANCE_EVENT, - array(&$reportRenderer, $reportType, $outputType, $report) + [&$reportRenderer, $reportType, $outputType, $report] ); if (is_null($reportRenderer)) { @@ -527,47 +565,44 @@ class API extends \Piwik\Plugin\API $reportRenderer->setReport($report); // render report - $description = str_replace(array("\r", "\n"), ' ', Common::unsanitizeInputValue($report['description'])); + $description = str_replace(["\r", "\n"], ' ', Common::unsanitizeInputValue($report['description'])); - list($reportSubject, $reportTitle) = self::getReportSubjectAndReportTitle(Common::unsanitizeInputValue(Site::getNameFor($idSite)), $report['reports']); + [$reportSubject, $reportTitle] = self::getReportSubjectAndReportTitle(Common::unsanitizeInputValue(Site::getNameFor($idSite)), $report['reports']); // if reporting for a segment, use the segment's name in the title - if(is_array($segment) && strlen($segment['name'])) { - $reportTitle .= " - ".$segment['name']; + if (is_array($segment) && strlen($segment['name'])) { + $reportTitle .= " - " . $segment['name']; } $filename = "$reportTitle - $prettyDate - $description"; $reportRenderer->renderFrontPage($reportTitle, $prettyDate, $description, $reportMetadata, $segment); - array_walk($processedReports, array($reportRenderer, 'renderReport')); + array_walk($processedReports, [$reportRenderer, 'renderReport']); switch ($outputType) { - case self::OUTPUT_SAVE_ON_DISK: // only used for SendReport $outputFilename = strtoupper($reportFormat) . ' ' . ucfirst($reportType) . ' Report - ' . $idReport . '.' . $date . '.' . $idSite . '.' . $language; - $outputFilename .= ' - ' . Common::getRandomString(40,'abcdefghijklmnoprstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVXYZ_'); + $outputFilename .= ' - ' . Common::getRandomString(40, 'abcdefghijklmnoprstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVXYZ_'); $outputFilename = $reportRenderer->sendToDisk($outputFilename); $additionalFiles = $this->getAttachments($reportRenderer, $report, $processedReports, $prettyDate); - return array( + return [ $outputFilename, $prettyDate, $reportSubject, $reportTitle, $additionalFiles, - ); + ]; break; case self::OUTPUT_INLINE: - $reportRenderer->sendToBrowserInline($filename); break; case self::OUTPUT_RETURN: - return $reportRenderer->getRenderedReport(); break; @@ -600,7 +635,7 @@ class API extends \Piwik\Plugin\API // generate report $this->enableSaveReportOnDisk = true; try { - list($outputFilename, $prettyDate, $reportSubject, $reportTitle, $additionalFiles) = + [$outputFilename, $prettyDate, $reportSubject, $reportTitle, $additionalFiles] = $this->generateReport( $idReport, $date, @@ -608,7 +643,11 @@ class API extends \Piwik\Plugin\API self::OUTPUT_SAVE_ON_DISK, $report['period_param'] ); - + } catch (NoAccessException $e) { + // This might occur if for some reason a report exists where the user does no longer have access to the + // configured site. Normally those reports should be automatically deleted. + Log::info("Skipping report as user does no longer have access to configured site"); + return; } catch (\Throwable $e) { $this->enableSaveReportOnDisk = false; throw new RetryableException($e->getMessage()); @@ -655,7 +694,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::SEND_REPORT_EVENT, - array( + [ &$reportType, $report, $contents, @@ -666,12 +705,12 @@ class API extends \Piwik\Plugin\API $additionalFiles, \Piwik\Period\Factory::build($report['period_param'], $date), $force - ) + ] ); // Update flag in DB $now = Date::now()->getDatetime(); - $this->getModel()->updateReport($report['idreport'], array('ts_last_sent' => $now)); + $this->getModel()->updateReport($report['idreport'], ['ts_last_sent' => $now]); if (!Development::isEnabled()) { @chmod($outputFilename, 0600); @@ -690,20 +729,21 @@ class API extends \Piwik\Plugin\API // if the only report is "All websites", we don't display the site name $reportTitle = $websiteName; $reportSubject = $websiteName; - if (count($reports) == 1 + if ( + count($reports) == 1 && $reports[0] == 'MultiSites_getAll' ) { $reportSubject = Piwik::translate('General_MultiSitesSummary'); $reportTitle = $reportSubject; } - return array($reportSubject, $reportTitle); + return [$reportSubject, $reportTitle]; } private static function validateReportParameters($reportType, $parameters) { // get list of valid parameters - $availableParameters = array(); + $availableParameters = []; /** * Triggered when gathering the available parameters for a scheduled report type. @@ -717,7 +757,7 @@ class API extends \Piwik\Plugin\API * @param string $reportType A string ID describing how the report is sent, eg, * `'sms'` or `'email'`. */ - Piwik::postEvent(self::GET_REPORT_PARAMETERS_EVENT, array(&$availableParameters, $reportType)); + Piwik::postEvent(self::GET_REPORT_PARAMETERS_EVENT, [&$availableParameters, $reportType]); // unset invalid parameters $availableParameterKeys = array_keys($availableParameters); @@ -744,7 +784,7 @@ class API extends \Piwik\Plugin\API * @param string $reportType A string ID describing how the report is sent, eg, * `'sms'` or `'email'`. */ - Piwik::postEvent(self::VALIDATE_PARAMETERS_EVENT, array(&$parameters, $reportType)); + Piwik::postEvent(self::VALIDATE_PARAMETERS_EVENT, [&$parameters, $reportType]); return json_encode($parameters); } @@ -764,7 +804,7 @@ class API extends \Piwik\Plugin\API // retrieve available reports $availableReportMetadata = self::getReportMetadata($idSite, $reportType); - $availableReportIds = array(); + $availableReportIds = []; foreach ($availableReportMetadata as $reportMetadata) { $availableReportIds[] = $reportMetadata['uniqueId']; } @@ -778,8 +818,16 @@ class API extends \Piwik\Plugin\API return json_encode($requestedReports); } - private static function validateCommonReportAttributes($period, $hour, &$description, &$idSegment, $reportType, $reportFormat, $evolutionPeriodFor, $evolutionPeriodN) - { + private static function validateCommonReportAttributes( + $period, + $hour, + &$description, + &$idSegment, + $reportType, + $reportFormat, + $evolutionPeriodFor, + $evolutionPeriodN + ) { self::validateReportPeriod($period); self::validateReportHour($hour); self::validateAndTruncateDescription($description); @@ -791,7 +839,7 @@ class API extends \Piwik\Plugin\API private static function validateReportPeriod($period) { - $availablePeriods = array('day', 'week', 'month', 'never'); + $availablePeriods = ['day', 'week', 'month', 'never']; if (!in_array($period, $availablePeriods)) { throw new Exception('Period schedule must be one of the following: ' . implode(', ', $availablePeriods) . ' (got ' . $period . ')'); } @@ -807,13 +855,10 @@ class API extends \Piwik\Plugin\API private static function validateIdSegment(&$idSegment) { if (empty($idSegment) || (is_numeric($idSegment) && $idSegment == 0)) { - $idSegment = null; } elseif (!is_numeric($idSegment)) { - throw new Exception('Invalid segment identifier. Should be an integer.'); } elseif (self::getSegment($idSegment) == null) { - throw new Exception('Segment with id ' . $idSegment . ' does not exist or SegmentEditor is not activated.'); } } @@ -837,7 +882,7 @@ class API extends \Piwik\Plugin\API throw new Exception( Piwik::translate( 'General_ExceptionInvalidReportRendererFormat', - array($reportFormat, implode(', ', $reportFormats)) + [$reportFormat, implode(', ', $reportFormats)] ) ); } @@ -853,7 +898,8 @@ class API extends \Piwik\Plugin\API throw new \Exception('The evolutionPeriodN param has no effect when evolutionPeriodFor is "each".'); } - if (!empty($evolutionPeriodN) + if ( + !empty($evolutionPeriodN) && (!is_numeric($evolutionPeriodN) || (int)$evolutionPeriodN < 0) ) { throw new \Exception('Evolution period amount must be a positive number (got ' . $evolutionPeriodN . ').'); @@ -865,7 +911,7 @@ class API extends \Piwik\Plugin\API */ public static function getReportMetadata($idSite, $reportType) { - $availableReportMetadata = array(); + $availableReportMetadata = []; /** * TODO: change this event so it returns a list of API methods instead of report metadata arrays. @@ -883,7 +929,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::GET_REPORT_METADATA_EVENT, - array(&$availableReportMetadata, $reportType, $idSite) + [&$availableReportMetadata, $reportType, $idSite] ); return $availableReportMetadata; @@ -911,7 +957,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::ALLOW_MULTIPLE_REPORTS_EVENT, - array(&$allowMultipleReports, $reportType) + [&$allowMultipleReports, $reportType] ); return $allowMultipleReports; } @@ -921,7 +967,7 @@ class API extends \Piwik\Plugin\API */ public static function getReportTypes() { - $reportTypes = array(); + $reportTypes = []; /** * Triggered when gathering all available transport mediums. @@ -932,7 +978,7 @@ class API extends \Piwik\Plugin\API * @param array &$reportTypes An array mapping transport medium IDs with the paths to those * mediums' icons. Add your new backend's ID to this array. */ - Piwik::postEvent(self::GET_REPORT_TYPES_EVENT, array(&$reportTypes)); + Piwik::postEvent(self::GET_REPORT_TYPES_EVENT, [&$reportTypes]); return $reportTypes; } @@ -942,7 +988,7 @@ class API extends \Piwik\Plugin\API */ public static function getReportFormats($reportType) { - $reportFormats = array(); + $reportFormats = []; /** * Triggered when gathering all available scheduled report formats. @@ -958,7 +1004,7 @@ class API extends \Piwik\Plugin\API */ Piwik::postEvent( self::GET_REPORT_FORMATS_EVENT, - array(&$reportFormats, $reportType) + [&$reportFormats, $reportType] ); return $reportFormats; @@ -969,7 +1015,7 @@ class API extends \Piwik\Plugin\API */ public static function getReportRecipients($report) { - $recipients = array(); + $recipients = []; /** * Triggered when getting the list of recipients of a scheduled report. @@ -987,7 +1033,7 @@ class API extends \Piwik\Plugin\API * @param array $report An array describing the scheduled report that is being * generated. */ - Piwik::postEvent(self::GET_REPORT_RECIPIENTS_EVENT, array(&$recipients, $report['type'], $report)); + Piwik::postEvent(self::GET_REPORT_RECIPIENTS_EVENT, [&$recipients, $report['type'], $report]); return $recipients; } @@ -998,7 +1044,6 @@ class API extends \Piwik\Plugin\API public static function getSegment($idSegment) { if (self::isSegmentEditorActivated() && !empty($idSegment)) { - $segment = APISegmentEditor::getInstance()->get($idSegment); if ($segment) { @@ -1030,10 +1075,11 @@ class API extends \Piwik\Plugin\API $idSitesUserHasAccess = SitesManagerApi::getInstance()->getSitesIdWithAtLeastViewAccess($login); - if (empty($idSitesUserHasAccess) + if ( + empty($idSitesUserHasAccess) || !in_array($idSite, $idSitesUserHasAccess) ) { - throw new NoAccessException(Piwik::translate('General_ExceptionPrivilege', array("'view'"))); + throw new NoAccessException(Piwik::translate('General_ExceptionPrivilege', ["'view'"])); } } |