Browse Source
Merge pull request #43117 from nextcloud/enh/1653/enable-unsharing-for-dav-shares
Merge pull request #43117 from nextcloud/enh/1653/enable-unsharing-for-dav-shares
enh(sharing): enable unsharing for sharees for DAV sharespull/43608/head
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1219 additions and 264 deletions
-
2apps/dav/appinfo/v1/caldav.php
-
8apps/dav/appinfo/v1/carddav.php
-
6apps/dav/composer/composer/autoload_classmap.php
-
6apps/dav/composer/composer/autoload_static.php
-
43apps/dav/lib/CalDAV/CalDavBackend.php
-
8apps/dav/lib/CalDAV/Calendar.php
-
43apps/dav/lib/CalDAV/Sharing/Backend.php
-
33apps/dav/lib/CalDAV/Sharing/Service.php
-
69apps/dav/lib/CardDAV/CardDavBackend.php
-
42apps/dav/lib/CardDAV/Sharing/Backend.php
-
33apps/dav/lib/CardDAV/Sharing/Service.php
-
6apps/dav/lib/Command/CreateCalendar.php
-
4apps/dav/lib/Connector/Sabre/DavAclPlugin.php
-
221apps/dav/lib/DAV/Sharing/Backend.php
-
113apps/dav/lib/DAV/Sharing/SharingMapper.php
-
71apps/dav/lib/DAV/Sharing/SharingService.php
-
27apps/dav/lib/RootCollection.php
-
109apps/dav/tests/integration/DAV/Sharing/SharingMapperTest.php
-
46apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
-
31apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
-
4apps/dav/tests/unit/CalDAV/CalendarTest.php
-
6apps/dav/tests/unit/CalDAV/PublicCalendarRootTest.php
-
49apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
-
427apps/dav/tests/unit/DAV/Sharing/BackendTest.php
-
72apps/dav/tests/unit/DAV/Sharing/SharingServiceTest.php
-
4build/integration/dav_features/caldav.feature
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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; |
|||
})); |
|||
} |
|||
} |
|||
@ -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); |
|||
} |
|||
|
|||
} |
|||
@ -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); |
|||
} |
|||
} |
|||
@ -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)); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue