Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Changelog

* 5.3.2 - 2026-06-29 - Added code to improve the access check before sending an alert
* 5.3.1 - 2026-06-08 - Added restrict access check for MultiSites.getAll report for non superusers
* 5.3.0 - 2026-05-11 - Added alert description and helptexts
* 5.2.6 - 2026-04-27 - Updated API documentation
Expand Down
16 changes: 16 additions & 0 deletions Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Piwik\Piwik;
use Piwik\Plugins\API\ProcessedReport;
use Piwik\Scheduler\RetryableException;
use Piwik\Plugins\SitesManager\API as SitesManagerApi;
use Piwik\Site;

/**
Expand Down Expand Up @@ -185,6 +186,10 @@ private function shouldBeProcessed($alert, $idSite)
return false;
}

if (!$this->hasViewPermissionForAlertOwner($alert, $idSite)) {
return false;
}

if (!$this->validator->isValidComparableDate($alert['period'], $alert['compared_to'])) {
// actually it would be nice to log or send a notification or whatever that we have skipped an alert
return false;
Expand All @@ -198,6 +203,17 @@ private function shouldBeProcessed($alert, $idSite)
return true;
}

protected function hasViewPermissionForAlertOwner(array $alert, int $idSite): bool
{
if (empty($alert['login'])) {
return false;
}

$idSitesUserHasAccess = SitesManagerApi::getInstance()->getSitesIdWithAtLeastViewAccess($alert['login']);

return !empty($idSitesUserHasAccess) && in_array($idSite, $idSitesUserHasAccess);
}

private function reportExists($idSite, $report, $metric)
{
try {
Expand Down
2 changes: 1 addition & 1 deletion plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "CustomAlerts",
"description": "Create custom Alerts to be notified of important changes on your website or app! ",
"version": "5.3.1",
"version": "5.3.2",
"require": {
"matomo": ">=5.0.0-b1,<6.0.0-b1"
},
Expand Down
28 changes: 28 additions & 0 deletions tests/Integration/ProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public function restrictMultiSitesReportToAlertOwner(array $params, array $repor
{
return parent::restrictMultiSitesReportToAlertOwner($params, $report, $alert);
}

public function hasViewPermissionForAlertOwner(array $alert, int $idSite): bool
{
return parent::hasViewPermissionForAlertOwner($alert, $idSite);
}
}

/**
Expand Down Expand Up @@ -544,6 +549,7 @@ private function buildAlert(
) {
return array(
'idalert' => 1,
'login' => 'superUserLogin',
'period' => $period,
'id_sites' => $idSites,
'metric_condition' => 'increase_more_than',
Expand Down Expand Up @@ -600,6 +606,28 @@ public function test_processAlert_shouldNotRun_IfWebsiteDoesNotMatch()
$this->assertProcessNotRun($alert, array(99, 85));
}

public function test_processAlert_shouldNotRun_IfAlertOwnerNoLongerHasViewAccess()
{
$alert = $this->buildAlert([1]);

$processorMock = $this->getMockBuilder('Piwik\Plugins\CustomAlerts\tests\Integration\CustomProcessor')
->setMethods(['hasViewPermissionForAlertOwner', 'getValueForAlertInPast', 'triggerAlert'])
->getMock();

$processorMock->expects($this->once())
->method('hasViewPermissionForAlertOwner')
->with($this->equalTo($alert), $this->equalTo(1))
->will($this->returnValue(false));

$processorMock->expects($this->never())
->method('getValueForAlertInPast');

$processorMock->expects($this->never())
->method('triggerAlert');

$processorMock->processAlert($alert, 1);
}

public function test_processAlert_shouldOnlyBeTriggeredIfAlertMatches()
{
$alert = $this->buildAlert(array(1), 'MultiSites_getAll', 'nb_visits', '5', 'day', $comparedTo = 7);
Expand Down
Loading