mirror of
https://github.com/nextcloud/server.git
synced 2026-06-08 08:16:43 -04:00
Merge pull request #43117 from nextcloud/enh/1653/enable-unsharing-for-dav-shares
enh(sharing): enable unsharing for sharees for DAV shares
This commit is contained in:
commit
57787f23e8
26 changed files with 1220 additions and 265 deletions
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
->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);
|
||||
|
||||
$result = $query->executeQuery();
|
||||
$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', $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));
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
]);
|
||||
|
|
|
|||
43
apps/dav/lib/CalDAV/Sharing/Backend.php
Normal file
43
apps/dav/lib/CalDAV/Sharing/Backend.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
33
apps/dav/lib/CalDAV/Sharing/Service.php
Normal file
33
apps/dav/lib/CalDAV/Sharing/Service.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
42
apps/dav/lib/CardDAV/Sharing/Backend.php
Normal file
42
apps/dav/lib/CardDAV/Sharing/Backend.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
33
apps/dav/lib/CardDAV/Sharing/Service.php
Normal file
33
apps/dav/lib/CardDAV/Sharing/Service.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
// 4 is already in use for public calendars
|
||||
public const ACCESS_UNSHARED = 5;
|
||||
|
||||
private CappedMemoryCache $shareCache;
|
||||
private ICache $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();
|
||||
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;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
$access = Backend::ACCESS_READ;
|
||||
if (isset($element['readOnly'])) {
|
||||
$access = $element['readOnly'] ? Backend::ACCESS_READ : Backend::ACCESS_READ_WRITE;
|
||||
}
|
||||
|
||||
$this->service->shareWith($shareable->getResourceId(), $principal, $access);
|
||||
}
|
||||
foreach ($remove as $element) {
|
||||
$principal = $this->principalBackend->findByUri($element, '');
|
||||
if (empty($principal)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't share with owner
|
||||
if ($shareable->getOwner() === $parts[1]) {
|
||||
return;
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
$principal = explode('/', $parts[1], 3);
|
||||
if (count($principal) !== 3 || $principal[0] !== 'principals' || !in_array($principal[1], ['users', 'groups', 'circles'], true)) {
|
||||
// Invalid principal
|
||||
return;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
$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();
|
||||
}
|
||||
|
||||
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[] = [
|
||||
|
|
|
|||
113
apps/dav/lib/DAV/Sharing/SharingMapper.php
Normal file
113
apps/dav/lib/DAV/Sharing/SharingMapper.php
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
71
apps/dav/lib/DAV/Sharing/SharingService.php
Normal file
71
apps/dav/lib/DAV/Sharing/SharingService.php
Normal file
|
|
@ -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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
109
apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php
Normal file
109
apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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']
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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->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->groupManager, $this->dispatcher);
|
||||
$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);
|
||||
|
|
|
|||
427
apps/dav/tests/unit/DAV/Sharing/BackendTest.php
Normal file
427
apps/dav/tests/unit/DAV/Sharing/BackendTest.php
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
72
apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php
Normal file
72
apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php
Normal file
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue