From d9347bd93d522946b7dc600666ccf2c484d93002 Mon Sep 17 00:00:00 2001 From: Brent Honeybone <9443133+brenthoneybone@users.noreply.github.com> Date: Tue, 12 May 2026 10:46:03 +0200 Subject: [PATCH 1/7] initial commit --- ClientSecretExpiryNotifier.php | 277 +++++++++++++++ .../ClientSecretExpiryNotificationEmail.php | 86 +++++ ScheduleReportMicrosoftTeams.php | 32 +- Tasks.php | 33 ++ lang/en.json | 21 +- ...ientSecretExpiryNotificationHtmlEmail.twig | 1 + .../ClientSecretExpiryNotifierTest.php | 331 ++++++++++++++++++ 7 files changed, 749 insertions(+), 32 deletions(-) create mode 100644 ClientSecretExpiryNotifier.php create mode 100644 Emails/ClientSecretExpiryNotificationEmail.php create mode 100644 Tasks.php create mode 100644 templates/_clientSecretExpiryNotificationHtmlEmail.twig create mode 100644 tests/Integration/ClientSecretExpiryNotifierTest.php diff --git a/ClientSecretExpiryNotifier.php b/ClientSecretExpiryNotifier.php new file mode 100644 index 0000000..4664b4f --- /dev/null +++ b/ClientSecretExpiryNotifier.php @@ -0,0 +1,277 @@ +settings = $settings; + $this->scheduledReportsApi = $scheduledReportsApi; + $this->usersManagerApi = $usersManagerApi; + $this->logger = $logger; + } + + /** + * Sends expiry notifications when today matches one of the configured notice days. + */ + public function sendNotificationsIfDue(): void + { + $expiryDate = $this->getExpiryDate(); + if ($expiryDate === null) { + return; + } + + // The task is stateless: only exact notice days result in email. + $daysUntilExpiry = $this->getDaysUntilExpiry($expiryDate); + if (!$this->isNoticeDay($daysUntilExpiry)) { + return; + } + + $superUserRecipients = $this->getSuperUserRecipients(); + $this->sendEmails($superUserRecipients, $expiryDate, $daysUntilExpiry, 'SuperUser'); + + $ownerRecipients = $this->getReportOwnerRecipients($superUserRecipients); + $this->sendEmails($ownerRecipients, $expiryDate, $daysUntilExpiry, 'Owner'); + } + + /** + * Calculates full UTC days remaining until the configured client secret expiry date. + */ + public function getDaysUntilExpiry(Date $expiryDate): int + { + return (int)(($expiryDate->getTimestamp() - Date::today()->getTimestamp()) / Date::NUM_SECONDS_IN_DAY); + } + + /** + * Checks whether the number of days remaining should trigger a notification. + */ + public function isNoticeDay(int $daysUntilExpiry): bool + { + return in_array($daysUntilExpiry, self::NOTICE_DAYS, true); + } + + /** + * Reads and parses the configured expiry date, returning null when it cannot be used. + */ + private function getExpiryDate(): ?Date + { + $expiryDate = $this->settings->clientSecretExpiryDate->getValue(); + if (empty($expiryDate)) { + return null; + } + + try { + return Date::factory($expiryDate); + } catch (\Exception $e) { + return null; + } + } + + /** + * Sends the selected template to each email address in the recipient list. + * + * @param array $emails + */ + private function sendEmails(array $emails, Date $expiryDate, int $daysUntilExpiry, string $recipientType): void + { + foreach ($emails as $email) { + $this->sendEmail($email, $expiryDate, $daysUntilExpiry, $recipientType); + } + } + + /** + * Collects all superuser email addresses, de-duplicated by email address. + * + * @return array + */ + private function getSuperUserRecipients(): array + { + $recipients = []; + + foreach ($this->usersManagerApi->getUsersHavingSuperUserAccess() as $user) { + $this->addRecipient($recipients, $user); + } + + return $recipients; + } + + /** + * Collects non-superuser owners of active Microsoft Teams scheduled reports. + * + * @param array $excludedEmails + * @return array + */ + private function getReportOwnerRecipients(array $excludedEmails): array + { + $recipients = []; + $superUserLogins = $this->getSuperUserLogins(); + + foreach ($this->getMicrosoftTeamsReports() as $report) { + $this->addReportOwnerRecipient($recipients, $report, $superUserLogins, $excludedEmails); + } + + return $recipients; + } + + /** + * Builds a lookup table for logins that already receive the superuser email. + * + * @return array + */ + private function getSuperUserLogins(): array + { + $logins = []; + + foreach ($this->usersManagerApi->getUsersHavingSuperUserAccess() as $user) { + if (!empty($user['login'])) { + $logins[$user['login']] = true; + } + } + + return $logins; + } + + /** + * Returns active scheduled reports that use Microsoft Teams delivery. + * + * @return array> + */ + private function getMicrosoftTeamsReports(): array + { + $reports = []; + + foreach ($this->getScheduledReports() as $report) { + if ($report['type'] === MicrosoftTeams::MS_TEAMS_TYPE) { + $reports[] = $report; + } + } + + return $reports; + } + + /** + * Returns scheduled reports, logging and skipping owner emails if reports cannot be loaded. + * + * @return array> + */ + private function getScheduledReports(): array + { + try { + return $this->scheduledReportsApi->getReports(); + } catch (\Exception $e) { + $this->logger->warning('Could not load scheduled reports for Microsoft Teams expiry emails: {exception}', [ + 'exception' => $e, + ]); + + return []; + } + } + + /** + * Adds a report owner email unless the owner is a superuser or already notified. + * + * @param array $recipients + * @param array $report + * @param array $superUserLogins + * @param array $excludedEmails + */ + private function addReportOwnerRecipient( + array &$recipients, + array $report, + array $superUserLogins, + array $excludedEmails + ): void { + $login = $report['login'] ?? ''; + if (empty($login) || isset($superUserLogins[$login])) { + return; + } + + try { + $this->addRecipient($recipients, $this->usersManagerApi->getUser($login), $excludedEmails); + } catch (\Exception $e) { + return; + } + } + + /** + * Adds one user email to the recipient list unless it is empty or excluded. + * + * @param array $recipients + * @param array $user + * @param array $excludedEmails + */ + private function addRecipient(array &$recipients, array $user, array $excludedEmails = []): void + { + if (empty($user['email']) || isset($excludedEmails[$user['email']])) { + return; + } + + $recipients[$user['email']] = $user['email']; + } + + /** + * Creates and sends a single expiry notification email. + */ + private function sendEmail(string $email, Date $expiryDate, int $daysUntilExpiry, string $recipientType): void + { + try { + /** @var ClientSecretExpiryNotificationEmail $mail */ + $mail = StaticContainer::getContainer()->make(ClientSecretExpiryNotificationEmail::class, [ + 'recipientEmail' => $email, + 'recipientType' => $recipientType, + 'daysUntilExpiry' => $daysUntilExpiry, + 'expiryDate' => $expiryDate->toString(), + ]); + $mail->safeSend(); + } catch (\Exception $e) { + $this->logger->warning('Could not send Microsoft Teams client secret expiry email: {exception}', [ + 'exception' => $e, + ]); + } + } +} diff --git a/Emails/ClientSecretExpiryNotificationEmail.php b/Emails/ClientSecretExpiryNotificationEmail.php new file mode 100644 index 0000000..288b448 --- /dev/null +++ b/Emails/ClientSecretExpiryNotificationEmail.php @@ -0,0 +1,86 @@ + 'FourWeeks', + 21 => 'ThreeWeeks', + 14 => 'TwoWeeks', + 7 => 'OneWeek', + 0 => 'Expired', + ]; + + /** + * @var string + */ + private $recipientType; + + /** + * @var int + */ + private $daysUntilExpiry; + + /** + * @var string + */ + private $expiryDate; + + public function __construct(string $recipientEmail, string $recipientType, int $daysUntilExpiry, string $expiryDate) + { + parent::__construct(); + + $this->recipientType = $recipientType; + $this->daysUntilExpiry = $daysUntilExpiry; + $this->expiryDate = $expiryDate; + + $this->setUpEmail($recipientEmail); + } + + private function setUpEmail(string $recipientEmail): void + { + $this->setDefaultFromPiwik(); + $this->addTo($recipientEmail); + $this->addReplyTo($this->getFrom(), $this->getFromName()); + $this->setSubject($this->getEmailSubject()); + $this->setBodyText($this->getEmailBodyText()); + $this->setWrappedHtmlBody($this->getEmailBodyView()); + } + + private function getEmailSubject(): string + { + return Piwik::translate($this->getTranslationKey('Subject')); + } + + private function getEmailBodyText(): string + { + return Piwik::translate($this->getTranslationKey('Body'), [$this->expiryDate]); + } + + private function getEmailBodyView(): View + { + $view = new View('@MicrosoftTeams/_clientSecretExpiryNotificationHtmlEmail'); + $view->bodyText = $this->getEmailBodyText(); + + return $view; + } + + private function getTranslationKey(string $templatePart): string + { + return 'MicrosoftTeams_ClientSecretExpiryEmail' . $this->recipientType . self::NOTICE_KEYS[$this->daysUntilExpiry] . $templatePart; + } +} diff --git a/ScheduleReportMicrosoftTeams.php b/ScheduleReportMicrosoftTeams.php index 6e014b4..205e49c 100644 --- a/ScheduleReportMicrosoftTeams.php +++ b/ScheduleReportMicrosoftTeams.php @@ -11,10 +11,6 @@ namespace Piwik\Plugins\MicrosoftTeams; -use Piwik\Container\StaticContainer; -use Piwik\Log\LoggerInterface; -use Piwik\Piwik; - class ScheduleReportMicrosoftTeams { /** @@ -60,32 +56,6 @@ public function __construct( public function send(): bool { $microsoftTeamsApi = new MicrosoftTeamsApi($this->webhookUrl); - return $microsoftTeamsApi->uploadFile($this->subject, $this->fileName, $this->fileContents, $this->requiredFields, $this->getTokenExpiryNoteIfNearExpiring()); - } - - private function getTokenExpiryNoteIfNearExpiring(): string - { - $note = ''; - $systemSettings = StaticContainer::get(SystemSettings::class); - $expiryDate = $systemSettings->clientSecretExpiryDate->getValue(); - if (!$expiryDate) { - return $note; - } - $today = new \DateTime(); - $expiry = new \DateTime($expiryDate); - $interval = $today->diff($expiry); - - if ($expiry < $today) { - $logger = StaticContainer::get(LoggerInterface::class); - $logger->error('Client Secret Expired.'); - - return $note; - } - - if ($interval->days <= 31) { - $note = Piwik::translate('MicrosoftTeams_ClientSecretExpiryNote', ['', '', $expiryDate]); - } - - return $note; + return $microsoftTeamsApi->uploadFile($this->subject, $this->fileName, $this->fileContents, $this->requiredFields); } } diff --git a/Tasks.php b/Tasks.php new file mode 100644 index 0000000..652133a --- /dev/null +++ b/Tasks.php @@ -0,0 +1,33 @@ +notifier = $notifier; + } + + public function schedule() + { + $this->daily('sendClientSecretExpiryNotifications'); + } + + public function sendClientSecretExpiryNotifications(): void + { + $this->notifier->sendNotificationsIfDue(); + } +} diff --git a/lang/en.json b/lang/en.json index 56fd494..8cdb921 100644 --- a/lang/en.json +++ b/lang/en.json @@ -10,6 +10,26 @@ "ClientSecretExpiryDateTitle": "Microsoft Client Secret Expiry Date", "ClientSecretExpiryDateDescription": "Optional. Set the expiry date in YYYY-MM-DD format to receive alert on expiry.", "ClientSecretDescription": "Enter your Microsoft Client Secret defined in the client application's Certificates and secrets page. Recommended expiry is 24 months. %1$sLearn more%2$s.", + "ClientSecretExpiryEmailOwnerExpiredBody": "Hi there,\n\nYour scheduled reports sent to Microsoft Teams will no longer be delivered.\n\nThe Microsoft Teams client secret has expired. A Matomo superuser must generate and configure a new client secret to restore the integration.\n\nPlease contact your Matomo superuser.\n\nThe Matomo Team", + "ClientSecretExpiryEmailOwnerExpiredSubject": "Microsoft Teams integration has expired", + "ClientSecretExpiryEmailOwnerFourWeeksBody": "Hi there,\n\nYour scheduled reports sent to Microsoft Teams rely on a client secret that is due to expire on %1$s.\n\nIf this secret is not updated, report delivery to Microsoft Teams will discontinue.\n\nOnly a Matomo superuser can renew this. We recommend contacting your Matomo superuser to confirm that the client secret will be updated before the expiry date.\n\nThe Matomo Team", + "ClientSecretExpiryEmailOwnerFourWeeksSubject": "Microsoft Teams integration expires in 4 weeks", + "ClientSecretExpiryEmailOwnerOneWeekBody": "Hi there,\n\nThe Microsoft Teams client secret used for your scheduled reports will expire in one week (%1$s).\n\nThis will prevent scheduled reports being delivered to Microsoft Teams.\n\nPlease contact your Matomo superuser to confirm the update.\n\nThe Matomo Team", + "ClientSecretExpiryEmailOwnerOneWeekSubject": "Final reminder: Microsoft Teams integration expires soon", + "ClientSecretExpiryEmailOwnerThreeWeeksBody": "Hi there,\n\nThis is a reminder that the Microsoft Teams client secret used for your scheduled reports will expire on %1$s.\n\nThis will prevent scheduled reports being delivered to Microsoft Teams.\n\nPlease check with your Matomo superuser to ensure the client secret is renewed in time.\n\nThe Matomo Team", + "ClientSecretExpiryEmailOwnerThreeWeeksSubject": "Reminder: Microsoft Teams integration may require update", + "ClientSecretExpiryEmailOwnerTwoWeeksBody": "Hi there,\n\nThe Microsoft Teams client secret used for your scheduled reports will expire on %1$s.\n\nTo avoid any interruption, confirm with your Matomo administrator that the client secret will be updated before this date.\n\nThe Matomo Team", + "ClientSecretExpiryEmailOwnerTwoWeeksSubject": "Upcoming expiry: Check Microsoft Teams integration", + "ClientSecretExpiryEmailSuperUserExpiredBody": "Hi there,\n\nYour Microsoft Teams client secret has now expired.\n\nMatomo can no longer send alerts or scheduled reports to Microsoft Teams.\n\nTo restore the integration, generate a new client secret and update your configuration.\n\nThe Matomo Team", + "ClientSecretExpiryEmailSuperUserExpiredSubject": "Action required: Microsoft Teams client secret has expired", + "ClientSecretExpiryEmailSuperUserFourWeeksBody": "Hi there,\n\nYour Microsoft Teams client secret is due to expire on %1$s.\n\nTo maintain the connection between Matomo and Microsoft Teams, generate and configure a new client secret before this date.\n\nIf the secret expires, Matomo will stop sending alerts and scheduled reports to Microsoft Teams.\n\nWe recommend updating the client secret now to avoid disruption.\n\nThe Matomo Team", + "ClientSecretExpiryEmailSuperUserFourWeeksSubject": "Microsoft Teams client secret expires in 4 weeks", + "ClientSecretExpiryEmailSuperUserOneWeekBody": "Hi there,\n\nYour Microsoft Teams client secret will expire in one week (%1$s).\n\nTo prevent disruption to alerts and scheduled reports, update the client secret before the expiry date.\n\nThe Matomo Team", + "ClientSecretExpiryEmailSuperUserOneWeekSubject": "Final reminder: Microsoft Teams client secret expires soon", + "ClientSecretExpiryEmailSuperUserThreeWeeksBody": "Hi there,\n\nThis is a reminder that your Microsoft Teams client secret will expire on %1$s.\n\nUpdate the client secret before this date to ensure Matomo continues sending alerts and scheduled reports.\n\nIf no action is taken, the integration will stop working after expiry.\n\nThe Matomo Team", + "ClientSecretExpiryEmailSuperUserThreeWeeksSubject": "Microsoft Teams client secret expires in 3 weeks", + "ClientSecretExpiryEmailSuperUserTwoWeeksBody": "Hi there,\n\nYour Microsoft Teams client secret will expire on %1$s.\n\nIf you have not updated it yet, generate and configure a new client secret to avoid interruptions to your Microsoft Teams integration.\n\nThe Matomo Team", + "ClientSecretExpiryEmailSuperUserTwoWeeksSubject": "Upcoming expiry: Microsoft Teams client secret", "TenantIdTitle": "Microsoft Tenant ID", "TenantIdDescription": "Enter your Microsoft Tenant ID found in the client application's Overview page. %1$sLearn more%2$s.", "TeamIdTitle": "Microsoft Teams ID", @@ -18,7 +38,6 @@ "RequiredFieldsNotSet": "The required fields to send a file are not set. Please set them %1$shere%2$s.", "PleaseFindYourReport": "Here is your %1$s report for %2$s.", "MicrosoftTeamsAlertContent": "%1$s has been triggered for website %2$s as the metric %3$s in report %4$s %5$s.", - "ClientSecretExpiryNote": "%1$sNote:%2$s The client secret is scheduled to expire on %3$s. Please generate a new token and update it in Matomo to ensure uninterrupted scheduled reports.", "MicrosoftTeamsWebhookUrlDeprecatedNoticeText": "Your Microsoft Teams report is using an outdated webhook URL. Follow this %1$sguide%2$s to create a new one.", "MicrosoftTeamsWebhookUrlDeprecatedNoticeTextCustomAlerts": "Your Microsoft Teams alert is using an outdated webhook URL. Follow this %1$sguide%2$s to create a new one." } diff --git a/templates/_clientSecretExpiryNotificationHtmlEmail.twig b/templates/_clientSecretExpiryNotificationHtmlEmail.twig new file mode 100644 index 0000000..c6344a1 --- /dev/null +++ b/templates/_clientSecretExpiryNotificationHtmlEmail.twig @@ -0,0 +1 @@ +

{{ bodyText|nl2br }}

diff --git a/tests/Integration/ClientSecretExpiryNotifierTest.php b/tests/Integration/ClientSecretExpiryNotifierTest.php new file mode 100644 index 0000000..97e44f8 --- /dev/null +++ b/tests/Integration/ClientSecretExpiryNotifierTest.php @@ -0,0 +1,331 @@ +> + */ + private $sentEmails = []; + + /** + * @var string + */ + private $suffix; + + public function setUp(): void + { + parent::setUp(); + + $this->suffix = substr(md5((string)microtime(true) . (string)mt_rand()), 0, 8); + FakeAccess::$identity = $this->login('root'); + FakeAccess::$superUser = true; + Config::getInstance()->General['emails_enabled'] = 1; + + \Piwik\Plugin\Manager::getInstance()->loadPlugins(['UsersManager', 'ScheduledReports', 'MicrosoftTeams']); + \Piwik\Plugin\Manager::getInstance()->installLoadedPlugins(); + Fixture::loadAllTranslations(); + + SitesManagerApi::getInstance()->addSite('Test', ['http://example.org']); + FakeAccess::setIdSitesView([1]); + FakeAccess::$superUser = true; + + $this->createUsers(); + $this->setRequiredFields(); + + Piwik::addAction('Mail.send', function (Mail $mail): void { + $this->sentEmails[] = [ + 'body' => $mail->getBodyText(), + 'html' => $mail->getBodyHtml(), + 'from' => $mail->getFrom(), + 'recipients' => array_keys($mail->getRecipients()), + 'subject' => $mail->getSubject(), + ]; + }); + } + + public function tearDown(): void + { + Date::$now = null; + ScheduledReportsApi::$cache = []; + + parent::tearDown(); + } + + /** + * @dataProvider getNoticeDays + */ + public function testShouldSendEmailsOnlyOnNoticeDays(int $daysUntilExpiry): void + { + $this->setExpiryDaysFromNow($daysUntilExpiry); + + $this->notifier()->sendNotificationsIfDue(); + + $this->assertCount(2, $this->sentEmails); + } + + public function getNoticeDays(): array + { + return [ + [28], + [21], + [14], + [7], + [0], + ]; + } + + /** + * @dataProvider getNonNoticeDays + */ + public function testShouldSkipEmailsWhenNotOnNoticeDay(int $daysUntilExpiry): void + { + $this->setExpiryDaysFromNow($daysUntilExpiry); + + $this->notifier()->sendNotificationsIfDue(); + + $this->assertSame([], $this->sentEmails); + } + + public function testShouldSkipEmailsWhenExpiryDateIsNotConfigured(): void + { + StaticContainer::get(SystemSettings::class)->clientSecretExpiryDate->setValue(''); + + $this->notifier()->sendNotificationsIfDue(); + + $this->assertSame([], $this->sentEmails); + } + + public function getNonNoticeDays(): array + { + return [ + [31], + [27], + [6], + [-1], + ]; + } + + public function testShouldSendOneEmailPerUserWithRecipientSpecificBody(): void + { + $this->setExpiryDaysFromNow(7); + $this->addTeamsReport($this->login('owner1')); + $this->addTeamsReport($this->login('owner1')); + $this->addTeamsReport($this->login('superuser1')); + $this->addEmailReport($this->login('owner2')); + + $this->notifier()->sendNotificationsIfDue(); + + $emailsByRecipient = $this->indexEmailsByRecipient(); + $this->assertSame([$this->email('owner1'), $this->email('super1'), $this->email('super2')], array_keys($emailsByRecipient)); + $this->assertSame('Final reminder: Microsoft Teams client secret expires soon', $emailsByRecipient[$this->email('super1')]['subject']); + $this->assertSame('Final reminder: Microsoft Teams integration expires soon', $emailsByRecipient[$this->email('owner1')]['subject']); + $this->assertStringContainsString('update the client secret before the expiry date', $emailsByRecipient[$this->email('super1')]['body']); + $this->assertStringContainsString('update the client secret before the expiry date', $emailsByRecipient[$this->email('super2')]['body']); + $this->assertStringContainsString('Please contact your Matomo superuser', $emailsByRecipient[$this->email('owner1')]['body']); + $this->assertStringContainsString('2025-01-08', $emailsByRecipient[$this->email('owner1')]['body']); + $this->assertStringContainsString('

', $emailsByRecipient[$this->email('owner1')]['html']); + $this->assertStringContainsString('
', $emailsByRecipient[$this->email('owner1')]['html']); + $this->assertStringContainsString('Please contact your Matomo superuser', $emailsByRecipient[$this->email('owner1')]['html']); + } + + public function testTeamsReportSenderShouldNotHaveClientSecretExpiryNoteHook(): void + { + $this->assertFalse(method_exists(ScheduleReportMicrosoftTeams::class, 'getTokenExpiryNoteIfNearExpiring')); + } + + public function testTeamsReportSenderShouldUseUploadFileWithoutAdditionalExpiryNote(): void + { + $method = new \ReflectionMethod(MicrosoftTeamsApi::class, 'uploadFile'); + $parameters = $method->getParameters(); + + $this->assertSame('additionalNote', $parameters[4]->getName()); + $this->assertTrue($parameters[4]->isDefaultValueAvailable()); + $this->assertSame('', $parameters[4]->getDefaultValue()); + } + + public function testShouldRegisterDailyClientSecretExpiryTask(): void + { + $tasks = new MicrosoftTeamsTasks($this->notifier()); + + $tasks->schedule(); + + $scheduledTasks = $tasks->getScheduledTasks(); + $this->assertCount(1, $scheduledTasks); + $this->assertSame('sendClientSecretExpiryNotifications', $scheduledTasks[0]->getMethodName()); + $this->assertNull($scheduledTasks[0]->getMethodParameter()); + $this->assertInstanceOf(Daily::class, $scheduledTasks[0]->getScheduledTime()); + } + + public function testScheduledTaskShouldSendDueNotifications(): void + { + $this->setExpiryDaysFromNow(28); + $tasks = new MicrosoftTeamsTasks($this->notifier()); + + $tasks->sendClientSecretExpiryNotifications(); + + $this->assertCount(2, $this->sentEmails); + } + + /** + * @dataProvider getNoticeSubjects + */ + public function testShouldUseNoticeSpecificSubjects( + int $daysUntilExpiry, + string $expectedSuperUserSubject, + string $expectedOwnerSubject + ): void { + $this->setExpiryDaysFromNow($daysUntilExpiry); + $this->addTeamsReport($this->login('owner1')); + + $this->notifier()->sendNotificationsIfDue(); + + $emailsByRecipient = $this->indexEmailsByRecipient(); + $this->assertSame($expectedSuperUserSubject, $emailsByRecipient[$this->email('super1')]['subject']); + $this->assertSame($expectedOwnerSubject, $emailsByRecipient[$this->email('owner1')]['subject']); + } + + public function getNoticeSubjects(): array + { + return [ + [28, 'Microsoft Teams client secret expires in 4 weeks', 'Microsoft Teams integration expires in 4 weeks'], + [21, 'Microsoft Teams client secret expires in 3 weeks', 'Reminder: Microsoft Teams integration may require update'], + [14, 'Upcoming expiry: Microsoft Teams client secret', 'Upcoming expiry: Check Microsoft Teams integration'], + [7, 'Final reminder: Microsoft Teams client secret expires soon', 'Final reminder: Microsoft Teams integration expires soon'], + [0, 'Action required: Microsoft Teams client secret has expired', 'Microsoft Teams integration has expired'], + ]; + } + + private function createUsers(): void + { + $api = UsersManagerApi::getInstance(); + $api->addUser($this->login('superuser1'), 'password1', $this->email('super1'), false); + $api->addUser($this->login('superuser2'), 'password2', $this->email('super2'), false); + $api->addUser($this->login('owner1'), 'password3', $this->email('owner1'), false); + $api->addUser($this->login('owner2'), 'password4', $this->email('owner2'), false); + + $userModel = new UsersManagerModel(); + $userModel->setSuperUserAccess($this->login('superuser1'), true); + $userModel->setSuperUserAccess($this->login('superuser2'), true); + } + + private function setRequiredFields(): void + { + $settings = StaticContainer::get(SystemSettings::class); + $settings->clientID->setValue('clientID'); + $settings->clientSecret->setValue('clientSecret'); + $settings->tenantID->setValue('tenantID'); + $settings->teamID->setValue('teamID'); + } + + private function setExpiryDaysFromNow(int $daysUntilExpiry): void + { + Date::$now = Date::factory('2025-01-01')->getTimestamp(); + StaticContainer::get(SystemSettings::class) + ->clientSecretExpiryDate + ->setValue(Date::today()->addDay($daysUntilExpiry)->toString()); + } + + private function addTeamsReport(string $login): void + { + $this->addReport($login, MicrosoftTeams::MS_TEAMS_TYPE, [ + MicrosoftTeams::MS_TEAMS_INCOMING_WEBHOOK_URL_PARAMETER => 'https://example.org/webhook', + ScheduledReports::DISPLAY_FORMAT_PARAMETER => ScheduledReports::DEFAULT_DISPLAY_FORMAT, + ScheduledReports::EVOLUTION_GRAPH_PARAMETER => ScheduledReports::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE, + ]); + } + + private function addEmailReport(string $login): void + { + $this->addReport($login, ScheduledReports::EMAIL_TYPE, [ + ScheduledReports::EMAIL_ME_PARAMETER => true, + ScheduledReports::ADDITIONAL_EMAILS_PARAMETER => [], + ScheduledReports::DISPLAY_FORMAT_PARAMETER => ScheduledReports::DEFAULT_DISPLAY_FORMAT, + ScheduledReports::EVOLUTION_GRAPH_PARAMETER => ScheduledReports::EVOLUTION_GRAPH_PARAMETER_DEFAULT_VALUE, + ]); + } + + private function addReport(string $login, string $type, array $parameters): void + { + FakeAccess::$identity = $login; + ScheduledReportsApi::$cache = []; + + ScheduledReportsApi::getInstance()->addReport(1, 'description', 'day', 3, $type, 'pdf', [], $parameters); + + FakeAccess::$identity = $this->login('root'); + } + + /** + * @return array> + */ + private function indexEmailsByRecipient(): array + { + $emails = []; + + foreach ($this->sentEmails as $email) { + $emails[$email['recipients'][0]] = $email; + } + + ksort($emails); + + return $emails; + } + + private function notifier(): ClientSecretExpiryNotifier + { + return StaticContainer::get(ClientSecretExpiryNotifier::class); + } + + private function login(string $login): string + { + return $login . '_' . $this->suffix; + } + + private function email(string $login): string + { + return $login . '_' . $this->suffix . '@example.org'; + } + + public function provideContainerConfig(): array + { + return [ + 'Piwik\Access' => new FakeAccess(), + ]; + } +} From 17107c6d4b67f25cd6d20f1f64331e480e796abc Mon Sep 17 00:00:00 2001 From: Brent Honeybone <9443133+brenthoneybone@users.noreply.github.com> Date: Tue, 12 May 2026 15:13:29 +0200 Subject: [PATCH 2/7] sanitize output --- .../_clientSecretExpiryNotificationHtmlEmail.twig | 2 +- .../ClientSecretExpiryNotifierTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/templates/_clientSecretExpiryNotificationHtmlEmail.twig b/templates/_clientSecretExpiryNotificationHtmlEmail.twig index c6344a1..b08927b 100644 --- a/templates/_clientSecretExpiryNotificationHtmlEmail.twig +++ b/templates/_clientSecretExpiryNotificationHtmlEmail.twig @@ -1 +1 @@ -

{{ bodyText|nl2br }}

+

{{ bodyText|e|nl2br }}

diff --git a/tests/Integration/ClientSecretExpiryNotifierTest.php b/tests/Integration/ClientSecretExpiryNotifierTest.php index 97e44f8..5eef242 100644 --- a/tests/Integration/ClientSecretExpiryNotifierTest.php +++ b/tests/Integration/ClientSecretExpiryNotifierTest.php @@ -17,6 +17,7 @@ use Piwik\Mail; use Piwik\Piwik; use Piwik\Plugins\MicrosoftTeams\ClientSecretExpiryNotifier; +use Piwik\Plugins\MicrosoftTeams\Emails\ClientSecretExpiryNotificationEmail; use Piwik\Plugins\MicrosoftTeams\MicrosoftTeams; use Piwik\Plugins\MicrosoftTeams\MicrosoftTeamsApi; use Piwik\Plugins\MicrosoftTeams\ScheduleReportMicrosoftTeams; @@ -165,6 +166,20 @@ public function testShouldSendOneEmailPerUserWithRecipientSpecificBody(): void $this->assertStringContainsString('Please contact your Matomo superuser', $emailsByRecipient[$this->email('owner1')]['html']); } + public function testHtmlEmailEscapesExpiryDatePlaceholder(): void + { + $email = new ClientSecretExpiryNotificationEmail( + $this->email('owner1'), + 'Owner', + 7, + '' + ); + + $this->assertStringContainsString('', $email->getBodyText()); + $this->assertStringNotContainsString('', $email->getBodyHtml()); + $this->assertStringContainsString('<script>alert("xss")</script>', $email->getBodyHtml()); + } + public function testTeamsReportSenderShouldNotHaveClientSecretExpiryNoteHook(): void { $this->assertFalse(method_exists(ScheduleReportMicrosoftTeams::class, 'getTokenExpiryNoteIfNearExpiring')); From 5612bf004a4134f67a98246caa57a3aaaaaca25a Mon Sep 17 00:00:00 2001 From: Brent Honeybone <9443133+brenthoneybone@users.noreply.github.com> Date: Tue, 12 May 2026 15:23:49 +0200 Subject: [PATCH 3/7] update screenshots --- .../CustomAlerts_empty_report.png | Bin 49550 -> 32647 bytes .../CustomAlerts_new_custom_alert.png | Bin 97870 -> 87131 bytes .../CustomAlerts_send_via_teams_new.png | Bin 98456 -> 87575 bytes ...Alerts_teams_alert_report_save_success.png | Bin 59384 -> 43141 bytes ...tomAlerts_teams_report_enabled_default.png | Bin 112424 -> 101625 bytes .../CustomAlerts_teams_report_error.png | Bin 131506 -> 119972 bytes ...rosoftTeamsSystemSetting_settings_page.png | Bin 68143 -> 68395 bytes .../MicrosoftTeams_empty_report.png | Bin 50217 -> 59719 bytes .../MicrosoftTeams_new_scheduled_reports.png | Bin 399504 -> 378760 bytes .../MicrosoftTeams_send_via_teams.png | Bin 394231 -> 375687 bytes .../MicrosoftTeams_send_via_teams_new.png | Bin 398049 -> 377493 bytes .../MicrosoftTeams_teams_report_disabled.png | Bin 394231 -> 375687 bytes .../MicrosoftTeams_teams_report_enabled.png | Bin 399093 -> 378439 bytes .../MicrosoftTeams_teams_report_error.png | Bin 417398 -> 397820 bytes .../MicrosoftTeams_teams_report_pdf_save.png | Bin 78865 -> 95504 bytes .../MicrosoftTeams_teams_report_pdf_view.png | Bin 415076 -> 394387 bytes ...icrosoftTeams_teams_report_save_report.png | Bin 69013 -> 82327 bytes 17 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/UI/expected-ui-screenshots/CustomAlerts_empty_report.png b/tests/UI/expected-ui-screenshots/CustomAlerts_empty_report.png index 526853ec78d6313ddbf03816f01e02c40405917e..34fcdc5ae34cb211685a5a5a39c22f2d16d66b87 100644 GIT binary patch literal 32647 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 X6kn?&UeKY#Ga|@JDoK=!83z0}>3f?~ literal 49550 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