Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/mail.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2021-12-03 15:00:18 +0300
committerChristoph Wurst <christoph@winzerhof-wurst.at>2022-03-21 19:11:54 +0300
commit9096248744e858975b30a7279323a793390afd6e (patch)
tree4ceffca46e6b0fa59c607cd6f4c5fd0244ac65ef /tests
parente181724033cd525fc9840594e545f1a2e3aa0f4b (diff)
Add anti abuse detection
* Trigger alert for number of recipients of a single message * Trigger alert for number of messages per time period (15m, 1h, 1d) Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'tests')
-rw-r--r--tests/Unit/Service/AntiAbuseServiceTest.php199
-rw-r--r--tests/psalm-baseline.xml17
2 files changed, 214 insertions, 2 deletions
diff --git a/tests/Unit/Service/AntiAbuseServiceTest.php b/tests/Unit/Service/AntiAbuseServiceTest.php
new file mode 100644
index 000000000..ffbd22f4f
--- /dev/null
+++ b/tests/Unit/Service/AntiAbuseServiceTest.php
@@ -0,0 +1,199 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\Mail\Tests\Unit\Service;
+
+use ChristophWurst\Nextcloud\Testing\ServiceMockObject;
+use ChristophWurst\Nextcloud\Testing\TestCase;
+use OCA\Mail\Account;
+use OCA\Mail\Address;
+use OCA\Mail\AddressList;
+use OCA\Mail\Model\NewMessageData;
+use OCA\Mail\Service\AntiAbuseService;
+use OCP\IMemcache;
+use OCP\IUser;
+use function array_map;
+use function range;
+
+class AntiAbuseServiceTest extends TestCase {
+
+ /** @var AntiAbuseService */
+ private $service;
+
+ /** @var ServiceMockObject */
+ private $serviceMock;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->serviceMock = $this->createServiceMock(AntiAbuseService::class);
+ $this->service = $this->serviceMock->getService();
+ }
+
+ public function testThresholdDisabled(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $account = $this->createMock(Account::class);
+ $messageData = new NewMessageData(
+ $account,
+ new AddressList([]),
+ new AddressList([]),
+ new AddressList([]),
+ 'subject',
+ 'henlo',
+ );
+ $this->serviceMock->getParameter('config')
+ ->expects(self::once())
+ ->method('getAppValue')
+ ->withConsecutive(
+ ['mail', 'abuse_detection', 'off'],
+ )->willReturnOnConsecutiveCalls(
+ 'off',
+ );
+ $this->serviceMock->getParameter('logger')
+ ->expects(self::never())
+ ->method('alert');
+
+ $this->service->onBeforeMessageSent(
+ $user,
+ $messageData,
+ );
+ }
+
+ public function testThresholdReached(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $account = $this->createMock(Account::class);
+ $messageData = new NewMessageData(
+ $account,
+ new AddressList(array_map(static function (int $i) {
+ return Address::fromRaw(
+ "user$i@domain.tld",
+ "user$i@domain.tld",
+ );
+ }, range(1, 50))),
+ new AddressList(array_map(static function (int $i) {
+ return Address::fromRaw(
+ "user$i@domain.tld",
+ "user$i@domain.tld",
+ );
+ }, range(51, 60))),
+ new AddressList(array_map(static function (int $i) {
+ return Address::fromRaw(
+ "user$i@domain.tld",
+ "user$i@domain.tld",
+ );
+ }, range(51, 70))),
+ 'subject',
+ 'henlo',
+ );
+ $this->serviceMock->getParameter('config')
+ ->method('getAppValue')
+ ->withConsecutive(
+ ['mail', 'abuse_detection', 'off'],
+ ['mail', 'abuse_number_of_recipients_per_message_threshold', '0'],
+ )->willReturnOnConsecutiveCalls(
+ 'on',
+ '50',
+ );
+ $this->serviceMock->getParameter('logger')
+ ->expects(self::once())
+ ->method('alert')
+ ->with(self::anything(), [
+ 'user' => 'user123',
+ 'expected' => 50,
+ 'actual' => 80,
+ ]);
+
+ $this->service->onBeforeMessageSent(
+ $user,
+ $messageData,
+ );
+ }
+
+ public function test15mThreshold(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user123');
+ $account = $this->createMock(Account::class);
+ $messageData = new NewMessageData(
+ $account,
+ new AddressList([
+ Address::fromRaw(
+ "user@domain.tld",
+ "user@domain.tld",
+ )
+ ]),
+ new AddressList([]),
+ new AddressList([]),
+ 'subject',
+ 'henlo',
+ );
+ $this->serviceMock->getParameter('config')
+ ->method('getAppValue')
+ ->withConsecutive(
+ ['mail', 'abuse_detection', 'off'],
+ ['mail', 'abuse_number_of_recipients_per_message_threshold', '0'],
+ ['mail', 'abuse_number_of_messages_per_15m', '0']
+ )->willReturnOnConsecutiveCalls(
+ 'on',
+ '0',
+ '5',
+ );
+ $this->serviceMock->getParameter('cacheFactory')
+ ->expects(self::once())
+ ->method('isAvailable')
+ ->willReturn(true);
+ $cache = $this->createMock(IMemcache::class);
+ $this->serviceMock->getParameter('cacheFactory')
+ ->expects(self::once())
+ ->method('createDistributed')
+ ->willReturn($cache);
+ $this->serviceMock->getParameter('timeFactory')
+ ->expects(self::once())
+ ->method('getTime')
+ ->willReturn(123456);
+ $cache->expects(self::once())
+ ->method('add')
+ ->with('counter_15m_123300', 0);
+ $cache->expects(self::once())
+ ->method('inc')
+ ->with('counter_15m_123300')
+ ->willReturn(5);
+ $this->serviceMock->getParameter('logger')
+ ->expects(self::once())
+ ->method('alert')
+ ->with(self::anything(), [
+ 'user' => 'user123',
+ 'period' => '15m',
+ 'expected' => 5,
+ 'actual' => 5,
+ ]);
+
+ $this->service->onBeforeMessageSent(
+ $user,
+ $messageData,
+ );
+ }
+}
diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml
index 16f0951d4..e2909dab4 100644
--- a/tests/psalm-baseline.xml
+++ b/tests/psalm-baseline.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="4.x-dev@">
<file src="lib/AppInfo/Application.php">
- <MissingDependency occurrences="14">
+ <MissingDependency occurrences="15">
+ <code>BeforeMessageSentEvent</code>
<code>DraftSavedEvent</code>
<code>MailboxesSynchronizedEvent</code>
<code>MessageDeletedEvent</code>
@@ -161,6 +162,11 @@
<code>Event</code>
</MissingDependency>
</file>
+ <file src="lib/Events/BeforeMessageSentEvent.php">
+ <MissingDependency occurrences="1">
+ <code>Event</code>
+ </MissingDependency>
+ </file>
<file src="lib/Events/DraftSavedEvent.php">
<MissingDependency occurrences="1">
<code>Event</code>
@@ -218,6 +224,12 @@
<code>MessageSentEvent</code>
</MissingDependency>
</file>
+ <file src="lib/Listener/AntiAbuseListener.php">
+ <MissingDependency occurrences="2">
+ <code>AntiAbuseListener</code>
+ <code>BeforeMessageSentEvent</code>
+ </MissingDependency>
+ </file>
<file src="lib/Listener/DashboardPanelListener.php">
<MissingDependency occurrences="2">
<code>DashboardPanelListener</code>
@@ -313,7 +325,8 @@
</MissingDependency>
</file>
<file src="lib/Service/MailTransmission.php">
- <MissingDependency occurrences="6">
+ <MissingDependency occurrences="7">
+ <code>BeforeMessageSentEvent</code>
<code>DraftSavedEvent</code>
<code>DraftSavedEvent</code>
<code>MessageSentEvent</code>