aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/appinfo/v1/caldav.php2
-rw-r--r--apps/dav/appinfo/v1/carddav.php8
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php6
-rw-r--r--apps/dav/composer/composer/autoload_static.php6
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php43
-rw-r--r--apps/dav/lib/CalDAV/Calendar.php8
-rw-r--r--apps/dav/lib/CalDAV/Sharing/Backend.php43
-rw-r--r--apps/dav/lib/CalDAV/Sharing/Service.php33
-rw-r--r--apps/dav/lib/CardDAV/CardDavBackend.php69
-rw-r--r--apps/dav/lib/CardDAV/Sharing/Backend.php42
-rw-r--r--apps/dav/lib/CardDAV/Sharing/Service.php33
-rw-r--r--apps/dav/lib/Command/CreateCalendar.php6
-rw-r--r--apps/dav/lib/Connector/Sabre/DavAclPlugin.php4
-rw-r--r--apps/dav/lib/DAV/Sharing/Backend.php221
-rw-r--r--apps/dav/lib/DAV/Sharing/SharingMapper.php113
-rw-r--r--apps/dav/lib/DAV/Sharing/SharingService.php71
-rw-r--r--apps/dav/lib/RootCollection.php27
-rw-r--r--apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php109
-rw-r--r--apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php46
-rw-r--r--apps/dav/tests/unit/CalDAV/CalDavBackendTest.php31
-rw-r--r--apps/dav/tests/unit/CalDAV/CalendarTest.php4
-rw-r--r--apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php6
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php49
-rw-r--r--apps/dav/tests/unit/DAV/Sharing/BackendTest.php427
-rw-r--r--apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php72
-rw-r--r--build/integration/dav_features/caldav.feature4
26 files changed, 1219 insertions, 264 deletions
diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php
index 8ea9c5dcb4e..d8b4feb4252 100644
--- a/apps/dav/appinfo/v1/caldav.php
+++ b/apps/dav/appinfo/v1/caldav.php
@@ -69,11 +69,11 @@ $calDavBackend = new CalDavBackend(
$db,
$principalBackend,
$userManager,
- \OC::$server->getGroupManager(),
$random,
$logger,
$dispatcher,
$config,
+ OC::$server->get(\OCA\DAV\CalDAV\Sharing\Backend::class),
true
);
diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php
index e7faa9314e2..70e5de1b481 100644
--- a/apps/dav/appinfo/v1/carddav.php
+++ b/apps/dav/appinfo/v1/carddav.php
@@ -64,7 +64,13 @@ $principalBackend = new Principal(
'principals/'
);
$db = \OC::$server->getDatabaseConnection();
-$cardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getUserManager(), \OC::$server->getGroupManager(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class));
+$cardDavBackend = new CardDavBackend(
+ $db,
+ $principalBackend,
+ \OC::$server->getUserManager(),
+ \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class),
+ \OC::$server->get(\OCA\DAV\CardDAV\Sharing\Backend::class),
+);
$debugging = \OC::$server->getConfig()->getSystemValue('debug', false);
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index d4305195d46..b7d7fd38a9a 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -99,6 +99,8 @@ return array(
'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter' => $baseDir . '/../lib/CalDAV/Search/Xml/Filter/PropFilter.php',
'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter' => $baseDir . '/../lib/CalDAV/Search/Xml/Filter/SearchTermFilter.php',
'OCA\\DAV\\CalDAV\\Search\\Xml\\Request\\CalendarSearchReport' => $baseDir . '/../lib/CalDAV/Search/Xml/Request/CalendarSearchReport.php',
+ 'OCA\\DAV\\CalDAV\\Sharing\\Backend' => $baseDir . '/../lib/CalDAV/Sharing/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Sharing\\Service' => $baseDir . '/../lib/CalDAV/Sharing/Service.php',
'OCA\\DAV\\CalDAV\\Status\\StatusService' => $baseDir . '/../lib/CalDAV/Status/StatusService.php',
'OCA\\DAV\\CalDAV\\TimezoneService' => $baseDir . '/../lib/CalDAV/TimezoneService.php',
'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObject' => $baseDir . '/../lib/CalDAV/Trashbin/DeletedCalendarObject.php',
@@ -129,6 +131,8 @@ return array(
'OCA\\DAV\\CardDAV\\MultiGetExportPlugin' => $baseDir . '/../lib/CardDAV/MultiGetExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => $baseDir . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => $baseDir . '/../lib/CardDAV/Plugin.php',
+ 'OCA\\DAV\\CardDAV\\Sharing\\Backend' => $baseDir . '/../lib/CardDAV/Sharing/Backend.php',
+ 'OCA\\DAV\\CardDAV\\Sharing\\Service' => $baseDir . '/../lib/CardDAV/Sharing/Service.php',
'OCA\\DAV\\CardDAV\\SyncService' => $baseDir . '/../lib/CardDAV/SyncService.php',
'OCA\\DAV\\CardDAV\\SystemAddressbook' => $baseDir . '/../lib/CardDAV/SystemAddressbook.php',
'OCA\\DAV\\CardDAV\\UserAddressBooks' => $baseDir . '/../lib/CardDAV/UserAddressBooks.php',
@@ -203,6 +207,8 @@ return array(
'OCA\\DAV\\DAV\\Sharing\\Backend' => $baseDir . '/../lib/DAV/Sharing/Backend.php',
'OCA\\DAV\\DAV\\Sharing\\IShareable' => $baseDir . '/../lib/DAV/Sharing/IShareable.php',
'OCA\\DAV\\DAV\\Sharing\\Plugin' => $baseDir . '/../lib/DAV/Sharing/Plugin.php',
+ 'OCA\\DAV\\DAV\\Sharing\\SharingMapper' => $baseDir . '/../lib/DAV/Sharing/SharingMapper.php',
+ 'OCA\\DAV\\DAV\\Sharing\\SharingService' => $baseDir . '/../lib/DAV/Sharing/SharingService.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => $baseDir . '/../lib/DAV/Sharing/Xml/Invite.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => $baseDir . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => $baseDir . '/../lib/DAV/SystemPrincipalBackend.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 9afd73635ff..627213786a1 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -114,6 +114,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Filter/PropFilter.php',
'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Filter/SearchTermFilter.php',
'OCA\\DAV\\CalDAV\\Search\\Xml\\Request\\CalendarSearchReport' => __DIR__ . '/..' . '/../lib/CalDAV/Search/Xml/Request/CalendarSearchReport.php',
+ 'OCA\\DAV\\CalDAV\\Sharing\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Sharing/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Sharing\\Service' => __DIR__ . '/..' . '/../lib/CalDAV/Sharing/Service.php',
'OCA\\DAV\\CalDAV\\Status\\StatusService' => __DIR__ . '/..' . '/../lib/CalDAV/Status/StatusService.php',
'OCA\\DAV\\CalDAV\\TimezoneService' => __DIR__ . '/..' . '/../lib/CalDAV/TimezoneService.php',
'OCA\\DAV\\CalDAV\\Trashbin\\DeletedCalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/Trashbin/DeletedCalendarObject.php',
@@ -144,6 +146,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CardDAV\\MultiGetExportPlugin' => __DIR__ . '/..' . '/../lib/CardDAV/MultiGetExportPlugin.php',
'OCA\\DAV\\CardDAV\\PhotoCache' => __DIR__ . '/..' . '/../lib/CardDAV/PhotoCache.php',
'OCA\\DAV\\CardDAV\\Plugin' => __DIR__ . '/..' . '/../lib/CardDAV/Plugin.php',
+ 'OCA\\DAV\\CardDAV\\Sharing\\Backend' => __DIR__ . '/..' . '/../lib/CardDAV/Sharing/Backend.php',
+ 'OCA\\DAV\\CardDAV\\Sharing\\Service' => __DIR__ . '/..' . '/../lib/CardDAV/Sharing/Service.php',
'OCA\\DAV\\CardDAV\\SyncService' => __DIR__ . '/..' . '/../lib/CardDAV/SyncService.php',
'OCA\\DAV\\CardDAV\\SystemAddressbook' => __DIR__ . '/..' . '/../lib/CardDAV/SystemAddressbook.php',
'OCA\\DAV\\CardDAV\\UserAddressBooks' => __DIR__ . '/..' . '/../lib/CardDAV/UserAddressBooks.php',
@@ -218,6 +222,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\DAV\\Sharing\\Backend' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Backend.php',
'OCA\\DAV\\DAV\\Sharing\\IShareable' => __DIR__ . '/..' . '/../lib/DAV/Sharing/IShareable.php',
'OCA\\DAV\\DAV\\Sharing\\Plugin' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Plugin.php',
+ 'OCA\\DAV\\DAV\\Sharing\\SharingMapper' => __DIR__ . '/..' . '/../lib/DAV/Sharing/SharingMapper.php',
+ 'OCA\\DAV\\DAV\\Sharing\\SharingService' => __DIR__ . '/..' . '/../lib/DAV/Sharing/SharingService.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/Invite.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/SystemPrincipalBackend.php',
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 034dccba1e0..c694892089e 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -42,8 +42,8 @@ namespace OCA\DAV\CalDAV;
use DateTime;
use DateTimeInterface;
use OCA\DAV\AppInfo\Application;
+use OCA\DAV\CalDAV\Sharing\Backend;
use OCA\DAV\Connector\Sabre\Principal;
-use OCA\DAV\DAV\Sharing\Backend;
use OCA\DAV\DAV\Sharing\IShareable;
use OCA\DAV\Events\CachedCalendarObjectCreatedEvent;
use OCA\DAV\Events\CachedCalendarObjectDeletedEvent;
@@ -72,7 +72,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IDBConnection;
-use OCP\IGroupManager;
use OCP\IUserManager;
use OCP\Security\ISecureRandom;
use Psr\Log\LoggerInterface;
@@ -208,7 +207,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
protected array $userDisplayNames;
- private Backend $calendarSharingBackend;
private string $dbObjectPropertiesTable = 'calendarobjects_props';
private array $cachedObjects = [];
@@ -216,14 +214,13 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
private IDBConnection $db,
private Principal $principalBackend,
private IUserManager $userManager,
- IGroupManager $groupManager,
private ISecureRandom $random,
private LoggerInterface $logger,
private IEventDispatcher $dispatcher,
private IConfig $config,
+ private Sharing\Backend $calendarSharingBackend,
private bool $legacyEndpoint = false,
) {
- $this->calendarSharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'calendar');
}
/**
@@ -361,10 +358,12 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
// query for shared calendars
$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
$principals = array_merge($principals, $this->principalBackend->getCircleMembership($principalUriOriginal));
-
$principals[] = $principalUri;
$fields = array_column($this->propertyMap, 0);
+ $fields = array_map(function (string $field) {
+ return 'a.'.$field;
+ }, $fields);
$fields[] = 'a.id';
$fields[] = 'a.uri';
$fields[] = 'a.synctoken';
@@ -372,19 +371,26 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$fields[] = 'a.principaluri';
$fields[] = 'a.transparent';
$fields[] = 's.access';
- $query = $this->db->getQueryBuilder();
- $query->select($fields)
+
+ $select = $this->db->getQueryBuilder();
+ $subSelect = $this->db->getQueryBuilder();
+
+ $subSelect->select('resourceid')
+ ->from('dav_shares', 'd')
+ ->where($subSelect->expr()->eq('d.access', $select->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT))
+ ->andWhere($subSelect->expr()->in('d.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY));
+
+ $select->select($fields)
->from('dav_shares', 's')
- ->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
- ->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
- ->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
- ->setParameter('type', 'calendar')
- ->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);
+ ->join('s', 'calendars', 'a', $select->expr()->eq('s.resourceid', 'a.id', IQueryBuilder::PARAM_INT))
+ ->where($select->expr()->in('s.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY))
+ ->andWhere($select->expr()->eq('s.type', $select->createNamedParameter('calendar', IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR))
+ ->andWhere($select->expr()->notIn('a.id', $select->createFunction($subSelect->getSQL()), IQueryBuilder::PARAM_INT_ARRAY));
- $result = $query->executeQuery();
+ $results = $select->executeQuery();
$readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
- while ($row = $result->fetch()) {
+ while ($row = $results->fetch()) {
$row['principaluri'] = (string) $row['principaluri'];
if ($row['principaluri'] === $principalUri) {
continue;
@@ -393,7 +399,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$readOnly = (int) $row['access'] === Backend::ACCESS_READ;
if (isset($calendars[$row['id']])) {
if ($readOnly) {
- // New share can not have more permissions then the old one.
+ // New share can not have more permissions than the old one.
continue;
}
if (isset($calendars[$row['id']][$readOnlyPropertyName]) &&
@@ -2891,7 +2897,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
$oldShares = $this->getShares($calendarId);
- $this->calendarSharingBackend->updateShares($shareable, $add, $remove);
+ $this->calendarSharingBackend->updateShares($shareable, $add, $remove, $oldShares);
$this->dispatcher->dispatchTyped(new CalendarShareUpdatedEvent($calendarId, $calendarRow, $oldShares, $add, $remove));
}, $this->db);
@@ -2967,7 +2973,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return list<array{privilege: string, principal: string, protected: bool}>
*/
public function applyShareAcl(int $resourceId, array $acl): array {
- return $this->calendarSharingBackend->applyShareAcl($resourceId, $acl);
+ $shares = $this->calendarSharingBackend->getShares($resourceId);
+ return $this->calendarSharingBackend->applyShareAcl($shares, $acl);
}
/**
diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php
index 92ad3242d78..fbfbdf652ec 100644
--- a/apps/dav/lib/CalDAV/Calendar.php
+++ b/apps/dav/lib/CalDAV/Calendar.php
@@ -236,14 +236,6 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IRestorable, IShareable
if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) &&
$this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']) {
$principal = 'principal:' . parent::getOwner();
- $shares = $this->caldavBackend->getShares($this->getResourceId());
- $shares = array_filter($shares, function ($share) use ($principal) {
- return $share['href'] === $principal;
- });
- if (empty($shares)) {
- throw new Forbidden();
- }
-
$this->caldavBackend->updateShares($this, [], [
$principal
]);
diff --git a/apps/dav/lib/CalDAV/Sharing/Backend.php b/apps/dav/lib/CalDAV/Sharing/Backend.php
new file mode 100644
index 00000000000..7a87f0353e7
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Sharing/Backend.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\CalDAV\Sharing;
+
+use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\Backend as SharingBackend;
+use OCP\ICacheFactory;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+use Psr\Log\LoggerInterface;
+
+class Backend extends SharingBackend {
+
+ public function __construct(private IUserManager $userManager,
+ private IGroupManager $groupManager,
+ private Principal $principalBackend,
+ private ICacheFactory $cacheFactory,
+ private Service $service,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct($this->userManager, $this->groupManager, $this->principalBackend, $this->cacheFactory, $this->service, $this->logger);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Sharing/Service.php b/apps/dav/lib/CalDAV/Sharing/Service.php
new file mode 100644
index 00000000000..cdf8c892ab5
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Sharing/Service.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\CalDAV\Sharing;
+
+use OCA\DAV\DAV\Sharing\SharingMapper;
+use OCA\DAV\DAV\Sharing\SharingService;
+
+class Service extends SharingService {
+ protected string $resourceType = 'calendar';
+ public function __construct(protected SharingMapper $mapper) {
+ parent::__construct($mapper);
+ }
+}
diff --git a/apps/dav/lib/CardDAV/CardDavBackend.php b/apps/dav/lib/CardDAV/CardDavBackend.php
index c1f0fe0c93c..bb7031caeab 100644
--- a/apps/dav/lib/CardDAV/CardDavBackend.php
+++ b/apps/dav/lib/CardDAV/CardDavBackend.php
@@ -52,7 +52,6 @@ use OCP\DB\Exception;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
-use OCP\IGroupManager;
use OCP\IUserManager;
use PDO;
use Sabre\CardDAV\Backend\BackendInterface;
@@ -64,15 +63,11 @@ use Sabre\VObject\Reader;
class CardDavBackend implements BackendInterface, SyncSupport {
use TTransactional;
-
public const PERSONAL_ADDRESSBOOK_URI = 'contacts';
public const PERSONAL_ADDRESSBOOK_NAME = 'Contacts';
- private Principal $principalBackend;
private string $dbCardsTable = 'cards';
private string $dbCardsPropertiesTable = 'cards_properties';
- private IDBConnection $db;
- private Backend $sharingBackend;
/** @var array properties to index */
public static array $indexProperties = [
@@ -84,29 +79,15 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @var string[] Map of uid => display name
*/
protected array $userDisplayNames;
- private IUserManager $userManager;
- private IEventDispatcher $dispatcher;
private array $etagCache = [];
- /**
- * CardDavBackend constructor.
- *
- * @param IDBConnection $db
- * @param Principal $principalBackend
- * @param IUserManager $userManager
- * @param IGroupManager $groupManager
- * @param IEventDispatcher $dispatcher
- */
- public function __construct(IDBConnection $db,
- Principal $principalBackend,
- IUserManager $userManager,
- IGroupManager $groupManager,
- IEventDispatcher $dispatcher) {
- $this->db = $db;
- $this->principalBackend = $principalBackend;
- $this->userManager = $userManager;
- $this->dispatcher = $dispatcher;
- $this->sharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'addressbook');
+ public function __construct(
+ private IDBConnection $db,
+ private Principal $principalBackend,
+ private IUserManager $userManager,
+ private IEventDispatcher $dispatcher,
+ private Sharing\Backend $sharingBackend,
+ ) {
}
/**
@@ -149,14 +130,14 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $this->atomic(function () use ($principalUri) {
$principalUriOriginal = $principalUri;
$principalUri = $this->convertPrincipal($principalUri, true);
- $query = $this->db->getQueryBuilder();
- $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
+ $select = $this->db->getQueryBuilder();
+ $select->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
->from('addressbooks')
- ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)));
+ ->where($select->expr()->eq('principaluri', $select->createNamedParameter($principalUri)));
$addressBooks = [];
- $result = $query->execute();
+ $result = $select->executeQuery();
while ($row = $result->fetch()) {
$addressBooks[$row['id']] = [
'id' => $row['id'],
@@ -178,15 +159,22 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$principals[] = $principalUri;
- $query = $this->db->getQueryBuilder();
- $result = $query->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access'])
+ $select = $this->db->getQueryBuilder();
+ $subSelect = $this->db->getQueryBuilder();
+
+ $subSelect->select('id')
+ ->from('dav_shares', 'd')
+ ->where($subSelect->expr()->eq('d.access', $select->createNamedParameter(\OCA\DAV\CardDAV\Sharing\Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT))
+ ->andWhere($subSelect->expr()->in('d.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY));
+
+
+ $select->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access'])
->from('dav_shares', 's')
- ->join('s', 'addressbooks', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
- ->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
- ->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
- ->setParameter('type', 'addressbook')
- ->setParameter('principaluri', $principals, IQueryBuilder::PARAM_STR_ARRAY)
- ->execute();
+ ->join('s', 'addressbooks', 'a', $select->expr()->eq('s.resourceid', 'a.id'))
+ ->where($select->expr()->in('s.principaluri', $select->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY)))
+ ->andWhere($select->expr()->eq('s.type', $select->createNamedParameter('addressbook', IQueryBuilder::PARAM_STR)))
+ ->andWhere($select->expr()->notIn('s.id', $select->createFunction($subSelect->getSQL()), IQueryBuilder::PARAM_INT_ARRAY));
+ $result = $select->executeQuery();
$readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
while ($row = $result->fetch()) {
@@ -1056,7 +1044,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
$addressBookData = $this->getAddressBookById($addressBookId);
$oldShares = $this->getShares($addressBookId);
- $this->sharingBackend->updateShares($shareable, $add, $remove);
+ $this->sharingBackend->updateShares($shareable, $add, $remove, $oldShares);
$this->dispatcher->dispatchTyped(new AddressBookShareUpdatedEvent($addressBookId, $addressBookData, $oldShares, $add, $remove));
}, $this->db);
@@ -1418,7 +1406,8 @@ class CardDavBackend implements BackendInterface, SyncSupport {
* @return list<array{privilege: string, principal: string, protected: bool}>
*/
public function applyShareAcl(int $addressBookId, array $acl): array {
- return $this->sharingBackend->applyShareAcl($addressBookId, $acl);
+ $shares = $this->sharingBackend->getShares($addressBookId);
+ return $this->sharingBackend->applyShareAcl($shares, $acl);
}
/**
diff --git a/apps/dav/lib/CardDAV/Sharing/Backend.php b/apps/dav/lib/CardDAV/Sharing/Backend.php
new file mode 100644
index 00000000000..f0f53ba9cfa
--- /dev/null
+++ b/apps/dav/lib/CardDAV/Sharing/Backend.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\CardDAV\Sharing;
+
+use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\Backend as SharingBackend;
+use OCP\ICacheFactory;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+use Psr\Log\LoggerInterface;
+
+class Backend extends SharingBackend {
+ public function __construct(private IUserManager $userManager,
+ private IGroupManager $groupManager,
+ private Principal $principalBackend,
+ private ICacheFactory $cacheFactory,
+ private Service $service,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct($this->userManager, $this->groupManager, $this->principalBackend, $this->cacheFactory, $this->service, $this->logger);
+ }
+}
diff --git a/apps/dav/lib/CardDAV/Sharing/Service.php b/apps/dav/lib/CardDAV/Sharing/Service.php
new file mode 100644
index 00000000000..5da71defb5e
--- /dev/null
+++ b/apps/dav/lib/CardDAV/Sharing/Service.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\CardDAV\Sharing;
+
+use OCA\DAV\DAV\Sharing\SharingMapper;
+use OCA\DAV\DAV\Sharing\SharingService;
+
+class Service extends SharingService {
+ protected string $resourceType = 'addressbook';
+ public function __construct(protected SharingMapper $mapper) {
+ parent::__construct($mapper);
+ }
+}
diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php
index d571f113177..9acc1e147f8 100644
--- a/apps/dav/lib/Command/CreateCalendar.php
+++ b/apps/dav/lib/Command/CreateCalendar.php
@@ -28,6 +28,7 @@ namespace OCA\DAV\Command;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
+use OCA\DAV\CalDAV\Sharing\Backend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\Accounts\IAccountManager;
use OCP\EventDispatcher\IEventDispatcher;
@@ -83,17 +84,16 @@ class CreateCalendar extends Command {
$logger = \OC::$server->get(LoggerInterface::class);
$dispatcher = \OC::$server->get(IEventDispatcher::class);
$config = \OC::$server->get(IConfig::class);
-
$name = $input->getArgument('name');
$caldav = new CalDavBackend(
$this->dbConnection,
$principalBackend,
$this->userManager,
- $this->groupManager,
$random,
$logger,
$dispatcher,
- $config
+ $config,
+ \OC::$server->get(Backend::class),
);
$caldav->createCalendar("principals/users/$user", $name, []);
return self::SUCCESS;
diff --git a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
index f574cec00c6..e643304ecec 100644
--- a/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/DavAclPlugin.php
@@ -26,6 +26,7 @@
*/
namespace OCA\DAV\Connector\Sabre;
+use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CardDAV\AddressBook;
use Sabre\CalDAV\Principal\User;
use Sabre\DAV\Exception\NotFound;
@@ -58,6 +59,9 @@ class DavAclPlugin extends \Sabre\DAVACL\Plugin {
case AddressBook::class:
$type = 'Addressbook';
break;
+ case Calendar::class:
+ $type = 'Calendar';
+ break;
default:
$type = 'Node';
break;
diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php
index b115ef61313..f77be9211bf 100644
--- a/apps/dav/lib/DAV/Sharing/Backend.php
+++ b/apps/dav/lib/DAV/Sharing/Backend.php
@@ -1,4 +1,6 @@
<?php
+
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
@@ -10,6 +12,7 @@
* @author Roeland Jago Douma <roeland@famdouma.nl>
* @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Anna Larch <anna.larch@gmx.net>
*
* @license AGPL-3.0
*
@@ -30,142 +33,104 @@ namespace OCA\DAV\DAV\Sharing;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Db\TTransactional;
-use OCP\Cache\CappedMemoryCache;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
+use OCP\ICache;
+use OCP\ICacheFactory;
use OCP\IGroupManager;
use OCP\IUserManager;
+use Psr\Log\LoggerInterface;
-class Backend {
+abstract class Backend {
use TTransactional;
-
- private IDBConnection $db;
- private IUserManager $userManager;
- private IGroupManager $groupManager;
- private Principal $principalBackend;
- private string $resourceType;
-
public const ACCESS_OWNER = 1;
+
public const ACCESS_READ_WRITE = 2;
public const ACCESS_READ = 3;
-
- private CappedMemoryCache $shareCache;
-
- public function __construct(IDBConnection $db, IUserManager $userManager, IGroupManager $groupManager, Principal $principalBackend, string $resourceType) {
- $this->db = $db;
- $this->userManager = $userManager;
- $this->groupManager = $groupManager;
- $this->principalBackend = $principalBackend;
- $this->resourceType = $resourceType;
- $this->shareCache = new CappedMemoryCache();
+ // 4 is already in use for public calendars
+ public const ACCESS_UNSHARED = 5;
+
+ private ICache $shareCache;
+
+ public function __construct(private IUserManager $userManager,
+ private IGroupManager $groupManager,
+ private Principal $principalBackend,
+ private ICacheFactory $cacheFactory,
+ private SharingService $service,
+ private LoggerInterface $logger,
+ ) {
+ $this->shareCache = $this->cacheFactory->createInMemory();
}
/**
* @param list<array{href: string, commonName: string, readOnly: bool}> $add
* @param list<string> $remove
*/
- public function updateShares(IShareable $shareable, array $add, array $remove): void {
+ public function updateShares(IShareable $shareable, array $add, array $remove, array $oldShares = []): void {
$this->shareCache->clear();
- $this->atomic(function () use ($shareable, $add, $remove) {
- foreach ($add as $element) {
- $principal = $this->principalBackend->findByUri($element['href'], '');
- if ($principal !== '') {
- $this->shareWith($shareable, $element);
- }
+ foreach ($add as $element) {
+ $principal = $this->principalBackend->findByUri($element['href'], '');
+ if (empty($principal)) {
+ continue;
}
- foreach ($remove as $element) {
- $principal = $this->principalBackend->findByUri($element, '');
- if ($principal !== '') {
- $this->unshare($shareable, $element);
- }
+
+ // We need to validate manually because some principals are only virtual
+ // i.e. Group principals
+ $principalparts = explode('/', $principal, 3);
+ if (count($principalparts) !== 3 || $principalparts[0] !== 'principals' || !in_array($principalparts[1], ['users', 'groups', 'circles'], true)) {
+ // Invalid principal
+ continue;
}
- }, $this->db);
- }
- /**
- * @param array{href: string, commonName: string, readOnly: bool} $element
- */
- private function shareWith(IShareable $shareable, array $element): void {
- $this->shareCache->clear();
- $user = $element['href'];
- $parts = explode(':', $user, 2);
- if ($parts[0] !== 'principal') {
- return;
- }
+ // Don't add share for owner
+ if($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) {
+ continue;
+ }
- // don't share with owner
- if ($shareable->getOwner() === $parts[1]) {
- return;
- }
+ $principalparts[2] = urldecode($principalparts[2]);
+ if (($principalparts[1] === 'users' && !$this->userManager->userExists($principalparts[2])) ||
+ ($principalparts[1] === 'groups' && !$this->groupManager->groupExists($principalparts[2]))) {
+ // User or group does not exist
+ continue;
+ }
- $principal = explode('/', $parts[1], 3);
- if (count($principal) !== 3 || $principal[0] !== 'principals' || !in_array($principal[1], ['users', 'groups', 'circles'], true)) {
- // Invalid principal
- return;
- }
+ $access = Backend::ACCESS_READ;
+ if (isset($element['readOnly'])) {
+ $access = $element['readOnly'] ? Backend::ACCESS_READ : Backend::ACCESS_READ_WRITE;
+ }
- $principal[2] = urldecode($principal[2]);
- if (($principal[1] === 'users' && !$this->userManager->userExists($principal[2])) ||
- ($principal[1] === 'groups' && !$this->groupManager->groupExists($principal[2]))) {
- // User or group does not exist
- return;
+ $this->service->shareWith($shareable->getResourceId(), $principal, $access);
}
+ foreach ($remove as $element) {
+ $principal = $this->principalBackend->findByUri($element, '');
+ if (empty($principal)) {
+ continue;
+ }
- // remove the share if it already exists
- $this->unshare($shareable, $element['href']);
- $access = self::ACCESS_READ;
- if (isset($element['readOnly'])) {
- $access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE;
- }
+ // Don't add unshare for owner
+ if($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) {
+ continue;
+ }
+
+ // Delete any possible direct shares (since the frontend does not separate between them)
+ $this->service->deleteShare($shareable->getResourceId(), $principal);
- $query = $this->db->getQueryBuilder();
- $query->insert('dav_shares')
- ->values([
- 'principaluri' => $query->createNamedParameter($parts[1]),
- 'type' => $query->createNamedParameter($this->resourceType),
- 'access' => $query->createNamedParameter($access),
- 'resourceid' => $query->createNamedParameter($shareable->getResourceId())
- ]);
- $query->executeStatement();
+ // Check if a user has a groupshare that they're trying to free themselves from
+ // If so we need to add a self::ACCESS_UNSHARED row
+ if(!str_contains($principal, 'group')
+ && $this->service->hasGroupShare($oldShares)
+ ) {
+ $this->service->unshare($shareable->getResourceId(), $principal);
+ }
+ }
}
public function deleteAllShares(int $resourceId): void {
$this->shareCache->clear();
- $query = $this->db->getQueryBuilder();
- $query->delete('dav_shares')
- ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
- ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
- ->executeStatement();
+ $this->service->deleteAllShares($resourceId);
}
public function deleteAllSharesByUser(string $principaluri): void {
$this->shareCache->clear();
- $query = $this->db->getQueryBuilder();
- $query->delete('dav_shares')
- ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri)))
- ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
- ->executeStatement();
- }
-
- private function unshare(IShareable $shareable, string $element): void {
- $this->shareCache->clear();
- $parts = explode(':', $element, 2);
- if ($parts[0] !== 'principal') {
- return;
- }
-
- // don't share with owner
- if ($shareable->getOwner() === $parts[1]) {
- return;
- }
-
- $query = $this->db->getQueryBuilder();
- $query->delete('dav_shares')
- ->where($query->expr()->eq('resourceid', $query->createNamedParameter($shareable->getResourceId())))
- ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
- ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1])))
- ;
- $query->executeStatement();
+ $this->service->deleteAllSharesByUser($principaluri);
}
/**
@@ -181,52 +146,39 @@ class Backend {
* @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}>
*/
public function getShares(int $resourceId): array {
- $cached = $this->shareCache->get($resourceId);
+ $cached = $this->shareCache->get((string)$resourceId);
if ($cached) {
return $cached;
}
- $query = $this->db->getQueryBuilder();
- $result = $query->select(['principaluri', 'access'])
- ->from('dav_shares')
- ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT)))
- ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
- ->groupBy(['principaluri', 'access'])
- ->executeQuery();
+ $rows = $this->service->getShares($resourceId);
$shares = [];
- while ($row = $result->fetch()) {
+ foreach($rows as $row) {
$p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
$shares[] = [
'href' => "principal:{$row['principaluri']}",
'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
'status' => 1,
- 'readOnly' => (int) $row['access'] === self::ACCESS_READ,
+ 'readOnly' => (int) $row['access'] === Backend::ACCESS_READ,
'{http://owncloud.org/ns}principal' => (string)$row['principaluri'],
- '{http://owncloud.org/ns}group-share' => isset($p['uri']) ? str_starts_with($p['uri'], 'principals/groups') : false
+ '{http://owncloud.org/ns}group-share' => isset($p['uri']) && str_starts_with($p['uri'], 'principals/groups')
];
}
-
$this->shareCache->set((string)$resourceId, $shares);
return $shares;
}
public function preloadShares(array $resourceIds): void {
$resourceIds = array_filter($resourceIds, function (int $resourceId) {
- return !isset($this->shareCache[$resourceId]);
+ return empty($this->shareCache->get((string)$resourceId));
});
- if (count($resourceIds) === 0) {
+ if (empty($resourceIds)) {
return;
}
- $query = $this->db->getQueryBuilder();
- $result = $query->select(['resourceid', 'principaluri', 'access'])
- ->from('dav_shares')
- ->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY)))
- ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
- ->groupBy(['principaluri', 'access', 'resourceid'])
- ->executeQuery();
+ $rows = $this->service->getSharesForIds($resourceIds);
$sharesByResource = array_fill_keys($resourceIds, []);
- while ($row = $result->fetch()) {
+ foreach($rows as $row) {
$resourceId = (int)$row['resourceid'];
$p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
$sharesByResource[$resourceId][] = [
@@ -235,12 +187,9 @@ class Backend {
'status' => 1,
'readOnly' => (int) $row['access'] === self::ACCESS_READ,
'{http://owncloud.org/ns}principal' => (string)$row['principaluri'],
- '{http://owncloud.org/ns}group-share' => isset($p['uri']) ? str_starts_with($p['uri'], 'principals/groups') : false
+ '{http://owncloud.org/ns}group-share' => isset($p['uri']) && str_starts_with($p['uri'], 'principals/groups')
];
- }
-
- foreach ($resourceIds as $resourceId) {
- $this->shareCache->set($resourceId, $sharesByResource[$resourceId]);
+ $this->shareCache->set((string)$resourceId, $sharesByResource[$resourceId]);
}
}
@@ -249,10 +198,10 @@ class Backend {
*
* @param int $resourceId
* @param list<array{privilege: string, principal: string, protected: bool}> $acl
- * @return list<array{privilege: string, principal: string, protected: bool}>
+ * @param list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> $shares
+ * @return list<array{principal: string, privilege: string, protected: bool}>
*/
- public function applyShareAcl(int $resourceId, array $acl): array {
- $shares = $this->getShares($resourceId);
+ public function applyShareAcl(array $shares, array $acl): array {
foreach ($shares as $share) {
$acl[] = [
'privilege' => '{DAV:}read',
@@ -265,7 +214,7 @@ class Backend {
'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
'protected' => true,
];
- } elseif ($this->resourceType === 'calendar') {
+ } elseif ($this->service->getResourceType() === 'calendar') {
// Allow changing the properties of read only calendars,
// so users can change the visibility.
$acl[] = [
diff --git a/apps/dav/lib/DAV/Sharing/SharingMapper.php b/apps/dav/lib/DAV/Sharing/SharingMapper.php
new file mode 100644
index 00000000000..c0c939c7a5e
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/SharingMapper.php
@@ -0,0 +1,113 @@
+<?php
+
+declare(strict_types=1);
+/*
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\DAV\DAV\Sharing;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+
+class SharingMapper {
+ public function __construct(private IDBConnection $db) {
+ }
+
+ public function getSharesForId(int $resourceId, string $resourceType): array {
+ $query = $this->db->getQueryBuilder();
+ $result = $query->select(['principaluri', 'access'])
+ ->from('dav_shares')
+ ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR)))
+ ->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT)))
+ ->groupBy(['principaluri', 'access'])
+ ->executeQuery();
+
+ $rows = $result->fetchAll();
+ $result->closeCursor();
+ return $rows;
+ }
+
+ public function getSharesForIds(array $resourceIds, string $resourceType): array {
+ $query = $this->db->getQueryBuilder();
+ $result = $query->select(['resourceid', 'principaluri', 'access'])
+ ->from('dav_shares')
+ ->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType)))
+ ->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT)))
+ ->groupBy(['principaluri', 'access', 'resourceid'])
+ ->executeQuery();
+
+ $rows = $result->fetchAll();
+ $result->closeCursor();
+ return $rows;
+ }
+
+ public function unshare(int $resourceId, string $resourceType, string $principal): void {
+ $query = $this->db->getQueryBuilder();
+ $query->insert('dav_shares')
+ ->values([
+ 'principaluri' => $query->createNamedParameter($principal),
+ 'type' => $query->createNamedParameter($resourceType),
+ 'access' => $query->createNamedParameter(Backend::ACCESS_UNSHARED),
+ 'resourceid' => $query->createNamedParameter($resourceId)
+ ]);
+ $query->executeStatement();
+ }
+
+ public function share(int $resourceId, string $resourceType, int $access, string $principal): void {
+ $query = $this->db->getQueryBuilder();
+ $query->insert('dav_shares')
+ ->values([
+ 'principaluri' => $query->createNamedParameter($principal),
+ 'type' => $query->createNamedParameter($resourceType),
+ 'access' => $query->createNamedParameter($access),
+ 'resourceid' => $query->createNamedParameter($resourceId)
+ ]);
+ $query->executeStatement();
+ }
+
+ public function deleteShare(int $resourceId, string $resourceType, string $principal): void {
+ $query = $this->db->getQueryBuilder();
+ $query->delete('dav_shares');
+ $query->where(
+ $query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT)),
+ $query->expr()->eq('type', $query->createNamedParameter($resourceType)),
+ $query->expr()->eq('principaluri', $query->createNamedParameter($principal))
+ );
+ $query->executeStatement();
+
+ }
+
+ public function deleteAllShares(int $resourceId, string $resourceType): void {
+ $query = $this->db->getQueryBuilder();
+ $query->delete('dav_shares')
+ ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType)))
+ ->executeStatement();
+ }
+
+ public function deleteAllSharesByUser(string $principaluri, string $resourceType): void {
+ $query = $this->db->getQueryBuilder();
+ $query->delete('dav_shares')
+ ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType)))
+ ->executeStatement();
+ }
+}
diff --git a/apps/dav/lib/DAV/Sharing/SharingService.php b/apps/dav/lib/DAV/Sharing/SharingService.php
new file mode 100644
index 00000000000..4b2a0beed1c
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/SharingService.php
@@ -0,0 +1,71 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+namespace OCA\DAV\DAV\Sharing;
+
+abstract class SharingService {
+ protected string $resourceType = '';
+ public function __construct(protected SharingMapper $mapper) {
+ }
+
+ public function getResourceType(): string {
+ return $this->resourceType;
+ }
+ public function shareWith(int $resourceId, string $principal, int $access): void {
+ // remove the share if it already exists
+ $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal);
+ $this->mapper->share($resourceId, $this->getResourceType(), $access, $principal);
+ }
+
+ public function unshare(int $resourceId, string $principal): void {
+ $this->mapper->unshare($resourceId, $this->getResourceType(), $principal);
+ }
+
+ public function deleteShare(int $resourceId, string $principal): void {
+ $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal);
+ }
+
+ public function deleteAllShares(int $resourceId): void {
+ $this->mapper->deleteAllShares($resourceId, $this->getResourceType());
+ }
+
+ public function deleteAllSharesByUser(string $principaluri): void {
+ $this->mapper->deleteAllSharesByUser($principaluri, $this->getResourceType());
+ }
+
+ public function getShares(int $resourceId): array {
+ return $this->mapper->getSharesForId($resourceId, $this->getResourceType());
+ }
+
+ public function getSharesForIds(array $resourceIds): array {
+ return $this->mapper->getSharesForIds($resourceIds, $this->getResourceType());
+ }
+
+ /**
+ * @param array $oldShares
+ * @return bool
+ */
+ public function hasGroupShare(array $oldShares): bool {
+ return !empty(array_filter($oldShares, function (array $share) {
+ return $share['{http://owncloud.org/ns}group-share'] === true;
+ }));
+ }
+}
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index c751a4babf5..0e9af0a4276 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -37,6 +37,7 @@ use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\CalDAV\PublicCalendarRoot;
use OCA\DAV\CalDAV\ResourceBooking\ResourcePrincipalBackend;
use OCA\DAV\CalDAV\ResourceBooking\RoomPrincipalBackend;
+use OCA\DAV\CalDAV\Sharing\Backend;
use OCA\DAV\CardDAV\AddressBookRoot;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
@@ -80,6 +81,7 @@ class RootCollection extends SimpleCollection {
\OC::$server->getConfig(),
\OC::$server->getL10NFactory()
);
+
$groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config);
$calendarResourcePrincipalBackend = new ResourcePrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper);
$calendarRoomPrincipalBackend = new RoomPrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper);
@@ -97,7 +99,7 @@ class RootCollection extends SimpleCollection {
$calendarResourcePrincipals->disableListing = $disableListing;
$calendarRoomPrincipals = new Collection($calendarRoomPrincipalBackend, 'principals/calendar-rooms');
$calendarRoomPrincipals->disableListing = $disableListing;
-
+ $calendarSharingBackend = \OC::$server->get(Backend::class);
$filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users');
$filesCollection->disableListing = $disableListing;
@@ -105,11 +107,12 @@ class RootCollection extends SimpleCollection {
$db,
$userPrincipalBackend,
$userManager,
- $groupManager,
$random,
$logger,
$dispatcher,
- $config
+ $config,
+ $calendarSharingBackend,
+ false,
);
$userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users', $logger);
$userCalendarRoot->disableListing = $disableListing;
@@ -142,12 +145,26 @@ class RootCollection extends SimpleCollection {
$logger
);
+ $contactsSharingBackend = \OC::$server->get(\OCA\DAV\CardDAV\Sharing\Backend::class);
+
$pluginManager = new PluginManager(\OC::$server, \OC::$server->query(IAppManager::class));
- $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
+ $usersCardDavBackend = new CardDavBackend(
+ $db,
+ $userPrincipalBackend,
+ $userManager,
+ $dispatcher,
+ $contactsSharingBackend,
+ );
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users');
$usersAddressBookRoot->disableListing = $disableListing;
- $systemCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $dispatcher);
+ $systemCardDavBackend = new CardDavBackend(
+ $db,
+ $userPrincipalBackend,
+ $userManager,
+ $dispatcher,
+ $contactsSharingBackend,
+ );
$systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system');
$systemAddressBookRoot->disableListing = $disableListing;
diff --git a/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php b/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php
new file mode 100644
index 00000000000..249f11c3780
--- /dev/null
+++ b/apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * @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/>.
+ */
+
+use OCA\DAV\DAV\Sharing\SharingMapper;
+use OCP\IDBConnection;
+use OCP\Server;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class SharingMapperTest extends TestCase {
+
+ private SharingMapper $mapper;
+ private IDBConnection $db;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->db = Server::get(IDBConnection::class);
+ $this->mapper = new SharingMapper($this->db);
+ $qb = $this->db->getQueryBuilder();
+ $qb->delete('dav_shares')->executeStatement();
+ }
+
+ public function testShareAndGet(): void {
+ $resourceId = 42;
+ $resourceType = 'calendar';
+ $access = 3;
+ $principal = 'principals/users/bob';
+ $this->mapper->share($resourceId, $resourceType, $access, $principal);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertCount(1, $shares);
+ }
+
+ public function testShareDelete(): void {
+ $resourceId = 42;
+ $resourceType = 'calendar';
+ $access = 3;
+ $principal = 'principals/users/bob';
+ $this->mapper->share($resourceId, $resourceType, $access, $principal);
+ $this->mapper->deleteShare($resourceId, $resourceType, $principal);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertEmpty($shares);
+ }
+
+ public function testShareUnshare(): void {
+ $resourceId = 42;
+ $resourceType = 'calendar';
+ $access = 3;
+ $principal = 'principals/groups/alicegroup';
+ $userPrincipal = 'principals/users/alice';
+ $this->mapper->share($resourceId, $resourceType, $access, $principal);
+ $this->mapper->unshare($resourceId, $resourceType, $userPrincipal);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertCount(1, $shares);
+ }
+
+ public function testShareDeleteAll(): void {
+ $resourceId = 42;
+ $resourceType = 'calendar';
+ $access = 3;
+ $principal = 'principals/groups/alicegroup';
+ $userPrincipal = 'principals/users/alice';
+ $this->mapper->share($resourceId, $resourceType, $access, $principal);
+ $this->mapper->unshare($resourceId, $resourceType, $userPrincipal);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertCount(1, $shares);
+ $this->mapper->deleteAllShares($resourceId, $resourceType);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertEmpty($shares);
+ }
+
+ public function testShareDeleteAllForUser(): void {
+ $resourceId = 42;
+ $resourceType = 'calendar';
+ $access = 3;
+ $principal = 'principals/groups/alicegroup';
+ $this->mapper->share($resourceId, $resourceType, $access, $principal);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertCount(1, $shares);
+ $this->mapper->deleteAllSharesByUser($principal, $resourceType);
+ $shares = $this->mapper->getSharesForId($resourceId, $resourceType);
+ $this->assertEmpty($shares);
+ }
+
+}
diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
index 4dd61c6e307..dbed804ea41 100644
--- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
+++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
@@ -30,10 +30,14 @@ namespace OCA\DAV\Tests\unit\CalDAV;
use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
+use OCA\DAV\CalDAV\Sharing\Backend as SharingBackend;
+use OCA\DAV\CalDAV\Sharing\Service;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\SharingMapper;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUserManager;
@@ -56,26 +60,16 @@ use Test\TestCase;
*/
abstract class AbstractCalDavBackend extends TestCase {
- /** @var CalDavBackend */
- protected $backend;
-
- /** @var Principal | MockObject */
- protected $principal;
- /** @var IUserManager|MockObject */
- protected $userManager;
- /** @var IGroupManager|MockObject */
- protected $groupManager;
- /** @var IEventDispatcher|MockObject */
- protected $dispatcher;
-
-
- /** @var IConfig | MockObject */
- private $config;
- /** @var ISecureRandom */
- private $random;
- /** @var LoggerInterface*/
- private $logger;
+ protected CalDavBackend $backend;
+ protected Principal|MockObject $principal;
+ protected IUserManager|MockObject $userManager;
+ protected IGroupManager|MockObject $groupManager;
+ protected IEventDispatcher|MockObject $dispatcher;
+ private LoggerInterface|MockObject $logger;
+ private IConfig|MockObject $config;
+ private ISecureRandom $random;
+ protected SharingBackend $sharingBackend;
public const UNIT_TEST_USER = 'principals/users/caldav-unit-test';
public const UNIT_TEST_USER1 = 'principals/users/caldav-unit-test1';
public const UNIT_TEST_GROUP = 'principals/groups/caldav-unit-test-group';
@@ -100,7 +94,7 @@ abstract class AbstractCalDavBackend extends TestCase {
$this->createMock(IConfig::class),
$this->createMock(IFactory::class)
])
- ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
+ ->setMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
->getMock();
$this->principal->expects($this->any())->method('getPrincipalByPath')
->willReturn([
@@ -115,15 +109,23 @@ abstract class AbstractCalDavBackend extends TestCase {
$this->random = \OC::$server->getSecureRandom();
$this->logger = $this->createMock(LoggerInterface::class);
$this->config = $this->createMock(IConfig::class);
+ $this->sharingBackend = new SharingBackend(
+ $this->userManager,
+ $this->groupManager,
+ $this->principal,
+ $this->createMock(ICacheFactory::class),
+ new Service(new SharingMapper($db)),
+ $this->logger);
$this->backend = new CalDavBackend(
$db,
$this->principal,
$this->userManager,
- $this->groupManager,
$this->random,
$this->logger,
$this->dispatcher,
- $this->config
+ $this->config,
+ $this->sharingBackend,
+ false,
);
$this->cleanUpBackend();
diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
index b23091275ef..112786eb987 100644
--- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
@@ -93,6 +93,9 @@ class CalDavBackendTest extends AbstractCalDavBackend {
'href' => 'principal:' . self::UNIT_TEST_GROUP,
'readOnly' => true
]
+ ], [
+ self::UNIT_TEST_USER1,
+ self::UNIT_TEST_GROUP,
]],
[true, true, true, false, [
[
@@ -103,6 +106,9 @@ class CalDavBackendTest extends AbstractCalDavBackend {
'href' => 'principal:' . self::UNIT_TEST_GROUP2,
'readOnly' => false,
],
+ ], [
+ self::UNIT_TEST_GROUP,
+ self::UNIT_TEST_GROUP2,
]],
[true, true, true, true, [
[
@@ -113,12 +119,17 @@ class CalDavBackendTest extends AbstractCalDavBackend {
'href' => 'principal:' . self::UNIT_TEST_GROUP2,
'readOnly' => true,
],
+ ], [
+ self::UNIT_TEST_GROUP,
+ self::UNIT_TEST_GROUP2,
]],
[true, false, false, false, [
[
'href' => 'principal:' . self::UNIT_TEST_USER1,
'readOnly' => true
],
+ ], [
+ self::UNIT_TEST_USER1,
]],
];
@@ -127,27 +138,26 @@ class CalDavBackendTest extends AbstractCalDavBackend {
/**
* @dataProvider providesSharingData
*/
- public function testCalendarSharing($userCanRead, $userCanWrite, $groupCanRead, $groupCanWrite, $add): void {
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject $l10n */
+ public function testCalendarSharing($userCanRead, $userCanWrite, $groupCanRead, $groupCanWrite, $add, $principals): void {
+ $logger = $this->createMock(\Psr\Log\LoggerInterface::class);
+ $config = $this->createMock(IConfig::class);
+ /** @var IL10N|MockObject $l10n */
$l10n = $this->createMock(IL10N::class);
- $l10n
- ->expects($this->any())
+ $l10n->expects($this->any())
->method('t')
->willReturnCallback(function ($text, $parameters = []) {
return vsprintf($text, $parameters);
});
- $logger = $this->createMock(\Psr\Log\LoggerInterface::class);
-
- $config = $this->createMock(IConfig::class);
-
$this->userManager->expects($this->any())
->method('userExists')
->willReturn(true);
-
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::atLeastOnce())
+ ->method('findByUri')
+ ->willReturnOnConsecutiveCalls(...$principals);
$calendarId = $this->createTestCalendar();
$calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
@@ -1250,6 +1260,9 @@ EOD;
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::atLeastOnce())
+ ->method('findByUri')
+ ->willReturn(self::UNIT_TEST_USER);
$me = self::UNIT_TEST_USER;
$sharer = self::UNIT_TEST_USER1;
diff --git a/apps/dav/tests/unit/CalDAV/CalendarTest.php b/apps/dav/tests/unit/CalDAV/CalendarTest.php
index d61e4b58478..68e6e5ef251 100644
--- a/apps/dav/tests/unit/CalDAV/CalendarTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalendarTest.php
@@ -83,11 +83,9 @@ class CalendarTest extends TestCase {
public function testDeleteFromGroup(): void {
- $this->expectException(\Sabre\DAV\Exception\Forbidden::class);
-
/** @var MockObject | CalDavBackend $backend */
$backend = $this->getMockBuilder(CalDavBackend::class)->disableOriginalConstructor()->getMock();
- $backend->expects($this->never())->method('updateShares');
+ $backend->expects($this->once())->method('updateShares');
$backend->expects($this->any())->method('getShares')->willReturn([
['href' => 'principal:group2']
]);
diff --git a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php
index 6634a602e74..3460c548de4 100644
--- a/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php
+++ b/apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php
@@ -84,6 +84,7 @@ class PublicCalendarRootTest extends TestCase {
$this->logger = $this->createMock(LoggerInterface::class);
$dispatcher = $this->createMock(IEventDispatcher::class);
$config = $this->createMock(IConfig::class);
+ $sharingBackend = $this->createMock(\OCA\DAV\CalDAV\Sharing\Backend::class);
$this->principal->expects($this->any())->method('getGroupMembership')
->withAnyParameters()
@@ -97,11 +98,12 @@ class PublicCalendarRootTest extends TestCase {
$db,
$this->principal,
$this->userManager,
- $this->groupManager,
$this->random,
$this->logger,
$dispatcher,
- $config
+ $config,
+ $sharingBackend,
+ false,
);
$this->l10n = $this->getMockBuilder(IL10N::class)
->disableOriginalConstructor()->getMock();
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index 425e7c44ba7..ea80187f554 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -37,11 +37,15 @@ use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\Sharing\Backend;
+use OCA\DAV\CardDAV\Sharing\Service;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\SharingMapper;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
@@ -50,6 +54,7 @@ use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
use OCP\Share\IManager as ShareManager;
+use Psr\Log\LoggerInterface;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\PropPatch;
use Sabre\VObject\Component\VCard;
@@ -78,7 +83,7 @@ class CardDavBackendTest extends TestCase {
/** @var IEventDispatcher|MockObject */
private $dispatcher;
-
+ private Backend $sharingBackend;
/** @var IDBConnection */
private $db;
@@ -141,7 +146,7 @@ class CardDavBackendTest extends TestCase {
$this->createMock(IConfig::class),
$this->createMock(IFactory::class)
])
- ->setMethods(['getPrincipalByPath', 'getGroupMembership'])
+ ->setMethods(['getPrincipalByPath', 'getGroupMembership', 'findByUri'])
->getMock();
$this->principal->method('getPrincipalByPath')
->willReturn([
@@ -154,8 +159,20 @@ class CardDavBackendTest extends TestCase {
$this->dispatcher = $this->createMock(IEventDispatcher::class);
$this->db = \OC::$server->getDatabaseConnection();
-
- $this->backend = new CardDavBackend($this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher);
+ $this->sharingBackend = new Backend($this->userManager,
+ $this->groupManager,
+ $this->principal,
+ $this->createMock(ICacheFactory::class),
+ new Service(new SharingMapper($this->db)),
+ $this->createMock(LoggerInterface::class)
+ );
+
+ $this->backend = new CardDavBackend($this->db,
+ $this->principal,
+ $this->userManager,
+ $this->dispatcher,
+ $this->sharingBackend,
+ );
// start every test with a empty cards_properties and cards table
$query = $this->db->getQueryBuilder();
$query->delete('cards_properties')->execute();
@@ -213,10 +230,12 @@ class CardDavBackendTest extends TestCase {
$this->userManager->expects($this->any())
->method('userExists')
->willReturn(true);
-
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::atLeastOnce())
+ ->method('findByUri')
+ ->willReturnOnConsecutiveCalls(self::UNIT_TEST_USER1, self::UNIT_TEST_GROUP);
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@@ -243,7 +262,7 @@ class CardDavBackendTest extends TestCase {
public function testCardOperations(): void {
/** @var CardDavBackend | \PHPUnit\Framework\MockObject\MockObject $backend */
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->onlyMethods(['updateProperties', 'purgeProperties'])->getMock();
// create a new address book
@@ -298,7 +317,7 @@ class CardDavBackendTest extends TestCase {
public function testMultiCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setMethods(['updateProperties'])->getMock();
// create a new address book
@@ -351,7 +370,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDOnDifferentAddressbooks(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->onlyMethods(['updateProperties'])->getMock();
// create 2 new address books
@@ -373,7 +392,7 @@ class CardDavBackendTest extends TestCase {
public function testMultipleUIDDenied(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setMethods(['updateProperties'])->getMock();
// create a new address book
@@ -394,7 +413,7 @@ class CardDavBackendTest extends TestCase {
public function testNoValidUID(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setMethods(['updateProperties'])->getMock();
// create a new address book
@@ -411,7 +430,7 @@ class CardDavBackendTest extends TestCase {
public function testDeleteWithoutCard(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->onlyMethods([
'getCardId',
'addChange',
@@ -451,7 +470,7 @@ class CardDavBackendTest extends TestCase {
public function testSyncSupport(): void {
$this->backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->setMethods(['updateProperties'])->getMock();
// create a new address book
@@ -477,10 +496,12 @@ class CardDavBackendTest extends TestCase {
$this->userManager->expects($this->any())
->method('userExists')
->willReturn(true);
-
$this->groupManager->expects($this->any())
->method('groupExists')
->willReturn(true);
+ $this->principal->expects(self::any())
+ ->method('findByUri')
+ ->willReturn(self::UNIT_TEST_USER1);
$this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []);
$books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER);
@@ -517,7 +538,7 @@ class CardDavBackendTest extends TestCase {
$cardId = 2;
$backend = $this->getMockBuilder(CardDavBackend::class)
- ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->groupManager, $this->dispatcher])
+ ->setConstructorArgs([$this->db, $this->principal, $this->userManager, $this->dispatcher, $this->sharingBackend])
->onlyMethods(['getCardId'])->getMock();
$backend->expects($this->any())->method('getCardId')->willReturn($cardId);
diff --git a/apps/dav/tests/unit/DAV/Sharing/BackendTest.php b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php
new file mode 100644
index 00000000000..eaa8f3805a1
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/Sharing/BackendTest.php
@@ -0,0 +1,427 @@
+<?php
+
+declare(strict_types=1);
+/*
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+namespace OCA\DAV\Tests\unit\DAV\Sharing;
+
+use OCA\DAV\CalDAV\Sharing\Backend as CalendarSharingBackend;
+use OCA\DAV\CalDAV\Sharing\Service;
+use OCA\DAV\CardDAV\Sharing\Backend as ContactsSharingBackend;
+use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\Sharing\Backend;
+use OCA\DAV\DAV\Sharing\IShareable;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class BackendTest extends TestCase {
+
+ private IDBConnection|MockObject $db;
+ private IUserManager|MockObject $userManager;
+ private IGroupManager|MockObject $groupManager;
+ private MockObject|Principal $principalBackend;
+ private MockObject|ICache $shareCache;
+ private LoggerInterface|MockObject $logger;
+ private MockObject|ICacheFactory $cacheFactory;
+ private Service|MockObject $calendarService;
+ private CalendarSharingBackend $backend;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->db = $this->createMock(IDBConnection::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->principalBackend = $this->createMock(Principal::class);
+ $this->cacheFactory = $this->createMock(ICacheFactory::class);
+ $this->shareCache = $this->createMock(ICache::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+ $this->calendarService = $this->createMock(Service::class);
+ $this->cacheFactory->expects(self::any())
+ ->method('createInMemory')
+ ->willReturn($this->shareCache);
+
+ $this->backend = new CalendarSharingBackend(
+ $this->userManager,
+ $this->groupManager,
+ $this->principalBackend,
+ $this->cacheFactory,
+ $this->calendarService,
+ $this->logger,
+ );
+ }
+
+ public function testUpdateShareCalendarBob(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $add = [
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/users/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->userManager->expects(self::once())
+ ->method('userExists')
+ ->willReturn(true);
+ $this->groupManager->expects(self::never())
+ ->method('groupExists');
+ $this->calendarService->expects(self::once())
+ ->method('shareWith')
+ ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ);
+
+ $this->backend->updateShares($shareable, $add, []);
+ }
+
+ public function testUpdateShareCalendarGroup(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $add = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/groups/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->userManager->expects(self::never())
+ ->method('userExists');
+ $this->groupManager->expects(self::once())
+ ->method('groupExists')
+ ->willReturn(true);
+ $this->calendarService->expects(self::once())
+ ->method('shareWith')
+ ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ);
+
+ $this->backend->updateShares($shareable, $add, []);
+ }
+
+ public function testUpdateShareContactsBob(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $add = [
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/users/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->userManager->expects(self::once())
+ ->method('userExists')
+ ->willReturn(true);
+ $this->groupManager->expects(self::never())
+ ->method('groupExists');
+ $this->calendarService->expects(self::once())
+ ->method('shareWith')
+ ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ);
+
+ $this->backend->updateShares($shareable, $add, []);
+ }
+
+ public function testUpdateShareContactsGroup(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $add = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/groups/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->userManager->expects(self::never())
+ ->method('userExists');
+ $this->groupManager->expects(self::once())
+ ->method('groupExists')
+ ->willReturn(true);
+ $this->calendarService->expects(self::once())
+ ->method('shareWith')
+ ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ);
+
+ $this->backend->updateShares($shareable, $add, []);
+ }
+
+ public function testUpdateShareCircle(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $add = [
+ [
+ 'href' => 'principal:principals/circles/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/groups/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->userManager->expects(self::never())
+ ->method('userExists');
+ $this->groupManager->expects(self::once())
+ ->method('groupExists')
+ ->willReturn(true);
+ $this->calendarService->expects(self::once())
+ ->method('shareWith')
+ ->with($shareable->getResourceId(), $principal, Backend::ACCESS_READ);
+
+ $this->backend->updateShares($shareable, $add, []);
+ }
+
+ public function testUnshareBob(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $remove = [
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $principal = 'principals/users/bob';
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn($principal);
+ $this->calendarService->expects(self::once())
+ ->method('deleteShare')
+ ->with($shareable->getResourceId(), $principal);
+ $this->calendarService->expects(self::once())
+ ->method('hasGroupShare')
+ ->willReturn(false);
+ $this->calendarService->expects(self::never())
+ ->method('unshare');
+
+ $this->backend->updateShares($shareable, [], $remove);
+ }
+
+ public function testUnshareWithBobGroup(): void {
+ $shareable = $this->createConfiguredMock(IShareable::class, [
+ 'getOwner' => 'principals/users/alice',
+ 'getResourceId' => 42,
+ ]);
+ $remove = [
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'readOnly' => true,
+ ]
+ ];
+ $oldShares = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => 'principals/groups/bob',
+ '{http://owncloud.org/ns}group-share' => true,
+ ]
+ ];
+
+
+ $this->shareCache->expects(self::once())
+ ->method('clear');
+ $this->principalBackend->expects(self::once())
+ ->method('findByUri')
+ ->willReturn('principals/users/bob');
+ $this->calendarService->expects(self::once())
+ ->method('deleteShare')
+ ->with($shareable->getResourceId(), 'principals/users/bob');
+ $this->calendarService->expects(self::once())
+ ->method('hasGroupShare')
+ ->with($oldShares)
+ ->willReturn(true);
+ $this->calendarService->expects(self::once())
+ ->method('unshare')
+ ->with($shareable->getResourceId(), 'principals/users/bob');
+
+ $this->backend->updateShares($shareable, [], $remove, $oldShares);
+ }
+
+ public function testGetShares(): void {
+ $resourceId = 42;
+ $principal = 'principals/groups/bob';
+ $rows = [
+ [
+ 'principaluri' => $principal,
+ 'access' => Backend::ACCESS_READ,
+ ]
+ ];
+ $expected = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => $principal,
+ '{http://owncloud.org/ns}group-share' => true,
+ ]
+ ];
+
+
+ $this->shareCache->expects(self::once())
+ ->method('get')
+ ->with((string)$resourceId)
+ ->willReturn(null);
+ $this->calendarService->expects(self::once())
+ ->method('getShares')
+ ->with($resourceId)
+ ->willReturn($rows);
+ $this->principalBackend->expects(self::once())
+ ->method('getPrincipalByPath')
+ ->with($principal)
+ ->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']);
+ $this->shareCache->expects(self::once())
+ ->method('set')
+ ->with((string)$resourceId, $expected);
+
+ $result = $this->backend->getShares($resourceId);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testGetSharesAddressbooks(): void {
+ $service = $this->createMock(\OCA\DAV\CardDAV\Sharing\Service::class);
+ $backend = new ContactsSharingBackend(
+ $this->userManager,
+ $this->groupManager,
+ $this->principalBackend,
+ $this->cacheFactory,
+ $service,
+ $this->logger);
+ $resourceId = 42;
+ $principal = 'principals/groups/bob';
+ $rows = [
+ [
+ 'principaluri' => $principal,
+ 'access' => Backend::ACCESS_READ,
+ ]
+ ];
+ $expected = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => $principal,
+ '{http://owncloud.org/ns}group-share' => true,
+ ]
+ ];
+
+ $this->shareCache->expects(self::once())
+ ->method('get')
+ ->with((string)$resourceId)
+ ->willReturn(null);
+ $service->expects(self::once())
+ ->method('getShares')
+ ->with($resourceId)
+ ->willReturn($rows);
+ $this->principalBackend->expects(self::once())
+ ->method('getPrincipalByPath')
+ ->with($principal)
+ ->willReturn(['uri' => $principal, '{DAV:}displayname' => 'bob']);
+ $this->shareCache->expects(self::once())
+ ->method('set')
+ ->with((string)$resourceId, $expected);
+
+ $result = $backend->getShares($resourceId);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testPreloadShares(): void {
+ $resourceIds = [42, 99];
+ $rows = [
+ [
+ 'resourceid' => 42,
+ 'principaluri' => 'principals/groups/bob',
+ 'access' => Backend::ACCESS_READ,
+ ],
+ [
+ 'resourceid' => 99,
+ 'principaluri' => 'principals/users/carlos',
+ 'access' => Backend::ACCESS_READ_WRITE,
+ ]
+ ];
+ $principalResults = [
+ ['uri' => 'principals/groups/bob', '{DAV:}displayname' => 'bob'],
+ ['uri' => 'principals/users/carlos', '{DAV:}displayname' => 'carlos'],
+ ];
+
+ $this->shareCache->expects(self::exactly(2))
+ ->method('get')
+ ->willReturn(null);
+ $this->calendarService->expects(self::once())
+ ->method('getSharesForIds')
+ ->with($resourceIds)
+ ->willReturn($rows);
+ $this->principalBackend->expects(self::exactly(2))
+ ->method('getPrincipalByPath')
+ ->willReturnCallback(function (string $principal) use ($principalResults) {
+ switch ($principal) {
+ case 'principals/groups/bob':
+ return $principalResults[0];
+ default:
+ return $principalResults[1];
+ }
+ });
+ $this->shareCache->expects(self::exactly(2))
+ ->method('set');
+
+ $this->backend->preloadShares($resourceIds);
+ }
+}
diff --git a/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php b/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php
new file mode 100644
index 00000000000..97cd3176d3b
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+/*
+ * @copyright 2024 Anna Larch <anna.larch@gmx.net>
+ *
+ * @author Anna Larch <anna.larch@gmx.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library 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 library. If not, see <http://www.gnu.org/licenses/>.
+ */
+namespace OCA\DAV\Tests\unit\DAV\Sharing;
+
+use OCA\DAV\CalDAV\Sharing\Service;
+use OCA\DAV\DAV\Sharing\SharingMapper;
+use OCA\DAV\DAV\Sharing\SharingService;
+use Test\TestCase;
+
+class SharingServiceTest extends TestCase {
+
+ private SharingService $service;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->service = new Service($this->createMock(SharingMapper::class));
+ }
+
+ public function testHasGroupShare(): void {
+ $oldShares = [
+ [
+ 'href' => 'principal:principals/groups/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => 'principals/groups/bob',
+ '{http://owncloud.org/ns}group-share' => true,
+ ],
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => 'principals/users/bob',
+ '{http://owncloud.org/ns}group-share' => false,
+ ]
+ ];
+
+ $this->assertTrue($this->service->hasGroupShare($oldShares));
+
+ $oldShares = [
+ [
+ 'href' => 'principal:principals/users/bob',
+ 'commonName' => 'bob',
+ 'status' => 1,
+ 'readOnly' => true,
+ '{http://owncloud.org/ns}principal' => 'principals/users/bob',
+ '{http://owncloud.org/ns}group-share' => false,
+ ]
+ ];
+ $this->assertFalse($this->service->hasGroupShare($oldShares));
+ }
+}
diff --git a/build/integration/dav_features/caldav.feature b/build/integration/dav_features/caldav.feature
index e2cb4f8dc92..fffdd89d367 100644
--- a/build/integration/dav_features/caldav.feature
+++ b/build/integration/dav_features/caldav.feature
@@ -13,7 +13,7 @@ Feature: caldav
When "user0" requests calendar "admin/MyCalendar" on the endpoint "/remote.php/dav/calendars/"
Then The CalDAV HTTP status code should be "404"
And The exception is "Sabre\DAV\Exception\NotFound"
- And The error message is "Node with name 'MyCalendar' could not be found"
+ And The error message is "Calendar with name 'MyCalendar' could not be found"
Scenario: Accessing a not shared calendar of another user via the legacy endpoint
Given user "user0" exists
@@ -22,7 +22,7 @@ Feature: caldav
When "user0" requests calendar "admin/MyCalendar" on the endpoint "/remote.php/caldav/calendars/"
Then The CalDAV HTTP status code should be "404"
And The exception is "Sabre\DAV\Exception\NotFound"
- And The error message is "Node with name 'MyCalendar' could not be found"
+ And The error message is "Calendar with name 'MyCalendar' could not be found"
Scenario: Accessing a not existing calendar of another user
Given user "user0" exists