aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2024-07-23 08:40:50 +0200
committerGitHub <noreply@github.com>2024-07-23 08:40:50 +0200
commite54d39b0769b5f3a1d29bd367f54f5dcceca5e55 (patch)
tree3760c0f6e21572f92d785f1ca08acc6422df0adb /apps/dav/lib
parenta884d48413c7f130c9deefe7116a3cdf9c9cc441 (diff)
parentcbea7872333974bae8868e7553c06595e5d3d02d (diff)
downloadnextcloud-server-e54d39b0769b5f3a1d29bd367f54f5dcceca5e55.tar.gz
nextcloud-server-e54d39b0769b5f3a1d29bd367f54f5dcceca5e55.zip
Merge pull request #45667 from nextcloud/fix/caldav/strict-default-calendar-checks
fix(caldav): stricter default calendar checks
Diffstat (limited to 'apps/dav/lib')
-rw-r--r--apps/dav/lib/CalDAV/DefaultCalendarValidator.php41
-rw-r--r--apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php3
-rw-r--r--apps/dav/lib/CalDAV/Schedule/Plugin.php19
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php4
-rw-r--r--apps/dav/lib/DAV/CustomPropertiesBackend.php9
-rw-r--r--apps/dav/lib/Server.php6
6 files changed, 73 insertions, 9 deletions
diff --git a/apps/dav/lib/CalDAV/DefaultCalendarValidator.php b/apps/dav/lib/CalDAV/DefaultCalendarValidator.php
new file mode 100644
index 00000000000..266e07ef255
--- /dev/null
+++ b/apps/dav/lib/CalDAV/DefaultCalendarValidator.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\CalDAV;
+
+use Sabre\DAV\Exception as DavException;
+
+class DefaultCalendarValidator {
+ /**
+ * Check if a given Calendar node is suitable to be used as the default calendar for scheduling.
+ *
+ * @throws DavException If the calendar is not suitable to be used as the default calendar
+ */
+ public function validateScheduleDefaultCalendar(Calendar $calendar): void {
+ // Sanity checks for a calendar that should handle invitations
+ if ($calendar->isSubscription()
+ || !$calendar->canWrite()
+ || $calendar->isShared()
+ || $calendar->isDeleted()) {
+ throw new DavException('Calendar is a subscription, not writable, shared or deleted');
+ }
+
+ // Calendar must support VEVENTs
+ $sCCS = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+ $calendarProperties = $calendar->getProperties([$sCCS]);
+ if (isset($calendarProperties[$sCCS])) {
+ $supportedComponents = $calendarProperties[$sCCS]->getValue();
+ } else {
+ $supportedComponents = ['VJOURNAL', 'VTODO', 'VEVENT'];
+ }
+ if (!in_array('VEVENT', $supportedComponents, true)) {
+ throw new DavException('Calendar does not support VEVENT components');
+ }
+ }
+}
diff --git a/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php b/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php
index 94f95652677..e076b2cd1e2 100644
--- a/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php
+++ b/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php
@@ -8,6 +8,7 @@ namespace OCA\DAV\CalDAV\InvitationResponse;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin;
use OCA\DAV\CalDAV\Auth\PublicPrincipalPlugin;
+use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
use OCA\DAV\Connector\Sabre\CachingTree;
@@ -69,7 +70,7 @@ class InvitationResponseServer {
// calendar plugins
$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class)));
+ $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class), \OC::$server->get(DefaultCalendarValidator::class)));
$this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
$this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
//$this->server->addPlugin(new \OCA\DAV\DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php
index 897901a61a4..5496fb23a96 100644
--- a/apps/dav/lib/CalDAV/Schedule/Plugin.php
+++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php
@@ -9,11 +9,13 @@ use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\CalendarHome;
+use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\ICalendar;
use Sabre\CalDAV\ICalendarObject;
use Sabre\CalDAV\Schedule\ISchedulingObject;
+use Sabre\DAV\Exception as DavException;
use Sabre\DAV\INode;
use Sabre\DAV\IProperties;
use Sabre\DAV\PropFind;
@@ -51,13 +53,15 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type';
public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL';
private LoggerInterface $logger;
+ private DefaultCalendarValidator $defaultCalendarValidator;
/**
* @param IConfig $config
*/
- public function __construct(IConfig $config, LoggerInterface $logger) {
+ public function __construct(IConfig $config, LoggerInterface $logger, DefaultCalendarValidator $defaultCalendarValidator) {
$this->config = $config;
$this->logger = $logger;
+ $this->defaultCalendarValidator = $defaultCalendarValidator;
}
/**
@@ -360,11 +364,20 @@ EOF;
* - isn't a calendar subscription
* - user can write to it (no virtual/3rd-party calendars)
* - calendar isn't a share
+ * - calendar supports VEVENTs
*/
foreach ($calendarHome->getChildren() as $node) {
- if ($node instanceof Calendar && !$node->isSubscription() && $node->canWrite() && !$node->isShared() && !$node->isDeleted()) {
- $userCalendars[] = $node;
+ if (!($node instanceof Calendar)) {
+ continue;
}
+
+ try {
+ $this->defaultCalendarValidator->validateScheduleDefaultCalendar($node);
+ } catch (DavException $e) {
+ continue;
+ }
+
+ $userCalendars[] = $node;
}
if (count($userCalendars) > 0) {
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index b07cbc904da..89d9b86becc 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -8,6 +8,7 @@
namespace OCA\DAV\Connector\Sabre;
use OCA\DAV\AppInfo\PluginManager;
+use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Files\BrowserErrorPagePlugin;
use OCP\EventDispatcher\IEventDispatcher;
@@ -167,7 +168,8 @@ class ServerFactory {
$server,
$objectTree,
$this->databaseConnection,
- $this->userSession->getUser()
+ $this->userSession->getUser(),
+ \OC::$server->get(DefaultCalendarValidator::class),
)
)
);
diff --git a/apps/dav/lib/DAV/CustomPropertiesBackend.php b/apps/dav/lib/DAV/CustomPropertiesBackend.php
index f4dd9b2d038..c3a547ab07d 100644
--- a/apps/dav/lib/DAV/CustomPropertiesBackend.php
+++ b/apps/dav/lib/DAV/CustomPropertiesBackend.php
@@ -8,12 +8,13 @@
namespace OCA\DAV\DAV;
use Exception;
+use OCA\DAV\CalDAV\Calendar;
+use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
-use Sabre\CalDAV\ICalendar;
use Sabre\DAV\Exception as DavException;
use Sabre\DAV\PropertyStorage\Backend\BackendInterface;
use Sabre\DAV\PropFind;
@@ -135,6 +136,7 @@ class CustomPropertiesBackend implements BackendInterface {
private Server $server;
private XmlService $xmlService;
+ private DefaultCalendarValidator $defaultCalendarValidator;
/**
* @param Tree $tree node tree
@@ -146,6 +148,7 @@ class CustomPropertiesBackend implements BackendInterface {
Tree $tree,
IDBConnection $connection,
IUser $user,
+ DefaultCalendarValidator $defaultCalendarValidator,
) {
$this->server = $server;
$this->tree = $tree;
@@ -156,6 +159,7 @@ class CustomPropertiesBackend implements BackendInterface {
$this->xmlService->elementMap,
self::COMPLEX_XML_ELEMENT_MAP,
);
+ $this->defaultCalendarValidator = $defaultCalendarValidator;
}
/**
@@ -319,10 +323,11 @@ class CustomPropertiesBackend implements BackendInterface {
// $path is the principal here as this prop is only set on principals
$node = $this->tree->getNodeForPath($href);
- if (!($node instanceof ICalendar) || $node->getOwner() !== $path) {
+ if (!($node instanceof Calendar) || $node->getOwner() !== $path) {
throw new DavException('No such calendar');
}
+ $this->defaultCalendarValidator->validateScheduleDefaultCalendar($node);
break;
}
}
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 415571ac25a..9e6d601649f 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -9,6 +9,7 @@ namespace OCA\DAV;
use OCA\DAV\AppInfo\PluginManager;
use OCA\DAV\BulkUpload\BulkUploadPlugin;
use OCA\DAV\CalDAV\BirthdayService;
+use OCA\DAV\CalDAV\DefaultCalendarValidator;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\CalDAV\Security\RateLimitingPlugin;
use OCA\DAV\CalDAV\Validation\CalDavValidatePlugin;
@@ -153,7 +154,7 @@ class Server {
$this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest(), \OC::$server->getConfig()));
$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
$this->server->addPlugin(new \OCA\DAV\CalDAV\ICSExportPlugin\ICSExportPlugin(\OC::$server->getConfig(), $logger));
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class)));
+ $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin(\OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class), \OC::$server->get(DefaultCalendarValidator::class)));
$this->server->addPlugin(\OC::$server->get(\OCA\DAV\CalDAV\Trashbin\Plugin::class));
$this->server->addPlugin(new \OCA\DAV\CalDAV\WebcalCaching\Plugin($request));
@@ -254,7 +255,8 @@ class Server {
$this->server,
$this->server->tree,
\OC::$server->getDatabaseConnection(),
- \OC::$server->getUserSession()->getUser()
+ \OC::$server->getUserSession()->getUser(),
+ \OC::$server->get(DefaultCalendarValidator::class),
)
)
);