summaryrefslogtreecommitdiffstats
path: root/apps/dav/tests
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2023-12-11 11:02:21 +0100
committerbackportbot-nextcloud[bot] <backportbot-nextcloud[bot]@users.noreply.github.com>2023-12-11 17:28:42 +0000
commit4757d3ed7d9487f04d45d51bac192022084e8fca (patch)
tree476b4dd9a69f56cabc12622156491496464e20fa /apps/dav/tests
parent12bafe16d5635fe340bde43eb47f1d877f5b92c5 (diff)
downloadnextcloud-server-4757d3ed7d9487f04d45d51bac192022084e8fca.tar.gz
nextcloud-server-4757d3ed7d9487f04d45d51bac192022084e8fca.zip
fix(dav): Prevent out-of-office event time drifts
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'apps/dav/tests')
-rw-r--r--apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php88
1 files changed, 82 insertions, 6 deletions
diff --git a/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php b/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php
index 919cf0a9ccf..7d13dd22f69 100644
--- a/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php
+++ b/apps/dav/tests/unit/Listener/OutOfOfficeListenerTest.php
@@ -25,11 +25,14 @@ declare(strict_types=1);
namespace OCA\DAV\Tests\Unit\Listener;
+use DateTimeImmutable;
+use InvalidArgumentException;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\CalendarObject;
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
use OCA\DAV\CalDAV\Plugin;
+use OCA\DAV\CalDAV\TimezoneService;
use OCA\DAV\Connector\Sabre\Server;
use OCA\DAV\Listener\OutOfOfficeListener;
use OCA\DAV\ServerFactory;
@@ -45,6 +48,9 @@ use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Tree;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Reader;
use Test\TestCase;
/**
@@ -55,20 +61,23 @@ class OutOfOfficeListenerTest extends TestCase {
private ServerFactory|MockObject $serverFactory;
private IConfig|MockObject $appConfig;
private LoggerInterface|MockObject $loggerInterface;
- private OutOfOfficeListener $listener;
+ private MockObject|TimezoneService $timezoneService;
private IManager|MockObject $manager;
+ private OutOfOfficeListener $listener;
protected function setUp(): void {
parent::setUp();
$this->serverFactory = $this->createMock(ServerFactory::class);
$this->appConfig = $this->createMock(IConfig::class);
+ $this->timezoneService = $this->createMock(TimezoneService::class);
$this->loggerInterface = $this->createMock(LoggerInterface::class);
$this->manager = $this->createMock(IManager::class);
$this->listener = new OutOfOfficeListener(
$this->serverFactory,
$this->appConfig,
+ $this->timezoneService,
$this->loggerInterface,
$this->manager
);
@@ -166,11 +175,15 @@ class OutOfOfficeListenerTest extends TestCase {
$this->listener->handle($event);
}
- public function testHandleScheduling(): void {
+ public function testHandleSchedulingWithDefaultTimezone(): void {
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('user123');
$data = $this->createMock(IOutOfOfficeData::class);
$data->method('getUser')->willReturn($user);
+ $data->method('getStartDate')
+ ->willReturn((new DateTimeImmutable('2023-12-12T00:00:00Z'))->getTimestamp());
+ $data->method('getEndDate')
+ ->willReturn((new DateTimeImmutable('2023-12-13T00:00:00Z'))->getTimestamp());
$davServer = $this->createMock(Server::class);
$invitationServer = $this->createMock(InvitationResponseServer::class);
$invitationServer->method('getServer')->willReturn($davServer);
@@ -195,12 +208,28 @@ class OutOfOfficeListenerTest extends TestCase {
->with('user123', 'dav', 'defaultCalendar', 'personal')
->willReturn('personal-1');
$calendar = $this->createMock(Calendar::class);
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user123')
+ ->willReturn('Europe/Prague');
$calendarHome->expects(self::once())
->method('getChild')
->with('personal-1')
->willReturn($calendar);
$calendar->expects(self::once())
- ->method('createFile');
+ ->method('createFile')
+ ->willReturnCallback(function ($name, $data) {
+ $vcalendar = Reader::read($data);
+ if (!($vcalendar instanceof VCalendar)) {
+ throw new InvalidArgumentException('Calendar data should be a VCALENDAR');
+ }
+ $vevent = $vcalendar->VEVENT;
+ if ($vevent === null || !($vevent instanceof VEvent)) {
+ throw new InvalidArgumentException('Calendar data should contain a VEVENT');
+ }
+ self::assertSame('Europe/Prague', $vevent->DTSTART['TZID']?->getValue());
+ self::assertSame('Europe/Prague', $vevent->DTEND['TZID']?->getValue());
+ });
$event = new OutOfOfficeScheduledEvent($data);
$this->listener->handle($event);
@@ -295,6 +324,10 @@ class OutOfOfficeListenerTest extends TestCase {
$user->method('getUID')->willReturn('user123');
$data = $this->createMock(IOutOfOfficeData::class);
$data->method('getUser')->willReturn($user);
+ $data->method('getStartDate')
+ ->willReturn((new DateTimeImmutable('2023-12-12T00:00:00Z'))->getTimestamp());
+ $data->method('getEndDate')
+ ->willReturn((new DateTimeImmutable('2023-12-14T00:00:00Z'))->getTimestamp());
$davServer = $this->createMock(Server::class);
$invitationServer = $this->createMock(InvitationResponseServer::class);
$invitationServer->method('getServer')->willReturn($davServer);
@@ -319,6 +352,13 @@ class OutOfOfficeListenerTest extends TestCase {
->with('user123', 'dav', 'defaultCalendar', 'personal')
->willReturn('personal-1');
$calendar = $this->createMock(Calendar::class);
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user123')
+ ->willReturn(null);
+ $this->timezoneService->expects(self::once())
+ ->method('getDefaultTimezone')
+ ->willReturn('Europe/Berlin');
$calendarHome->expects(self::once())
->method('getChild')
->with('personal-1')
@@ -327,17 +367,33 @@ class OutOfOfficeListenerTest extends TestCase {
->method('getChild')
->willThrowException(new NotFound());
$calendar->expects(self::once())
- ->method('createFile');
+ ->method('createFile')
+ ->willReturnCallback(function ($name, $data) {
+ $vcalendar = Reader::read($data);
+ if (!($vcalendar instanceof VCalendar)) {
+ throw new InvalidArgumentException('Calendar data should be a VCALENDAR');
+ }
+ $vevent = $vcalendar->VEVENT;
+ if ($vevent === null || !($vevent instanceof VEvent)) {
+ throw new InvalidArgumentException('Calendar data should contain a VEVENT');
+ }
+ self::assertSame('Europe/Berlin', $vevent->DTSTART['TZID']?->getValue());
+ self::assertSame('Europe/Berlin', $vevent->DTEND['TZID']?->getValue());
+ });
$event = new OutOfOfficeChangedEvent($data);
$this->listener->handle($event);
}
- public function testHandleChange(): void {
+ public function testHandleChangeWithoutTimezone(): void {
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('user123');
$data = $this->createMock(IOutOfOfficeData::class);
$data->method('getUser')->willReturn($user);
+ $data->method('getStartDate')
+ ->willReturn((new DateTimeImmutable('2023-01-12T00:00:00Z'))->getTimestamp());
+ $data->method('getEndDate')
+ ->willReturn((new DateTimeImmutable('2023-12-14T00:00:00Z'))->getTimestamp());
$davServer = $this->createMock(Server::class);
$invitationServer = $this->createMock(InvitationResponseServer::class);
$invitationServer->method('getServer')->willReturn($davServer);
@@ -367,11 +423,31 @@ class OutOfOfficeListenerTest extends TestCase {
->with('personal-1')
->willReturn($calendar);
$eventNode = $this->createMock(CalendarObject::class);
+ $this->timezoneService->expects(self::once())
+ ->method('getUserTimezone')
+ ->with('user123')
+ ->willReturn(null);
+ $this->timezoneService->expects(self::once())
+ ->method('getDefaultTimezone')
+ ->willReturn('UTC');
$calendar->expects(self::once())
->method('getChild')
->willReturn($eventNode);
$eventNode->expects(self::once())
- ->method('put');
+ ->method('put')
+ ->willReturnCallback(function ($data) {
+ $vcalendar = Reader::read($data);
+ if (!($vcalendar instanceof VCalendar)) {
+ throw new InvalidArgumentException('Calendar data should be a VCALENDAR');
+ }
+ $vevent = $vcalendar->VEVENT;
+ if ($vevent === null || !($vevent instanceof VEvent)) {
+ throw new InvalidArgumentException('Calendar data should contain a VEVENT');
+ }
+ // UTC datetimes are stored without a TZID
+ self::assertSame(null, $vevent->DTSTART['TZID']?->getValue());
+ self::assertSame(null, $vevent->DTEND['TZID']?->getValue());
+ });
$calendar->expects(self::never())
->method('createFile');
$event = new OutOfOfficeChangedEvent($data);