@@ -25,81 +25,81 @@ |
||
| 25 | 25 | * @template-implements IEventListener<Event|CalendarObjectCreatedEvent|CalendarObjectUpdatedEvent|CalendarObjectDeletedEvent> |
| 26 | 26 | */ |
| 27 | 27 | class CalendarFederationNotificationListener implements IEventListener { |
| 28 | - public function __construct( |
|
| 29 | - private readonly ICloudIdManager $cloudIdManager, |
|
| 30 | - private readonly CalendarFederationNotifier $calendarFederationNotifier, |
|
| 31 | - private readonly LoggerInterface $logger, |
|
| 32 | - private readonly SharingMapper $sharingMapper, |
|
| 33 | - ) { |
|
| 34 | - } |
|
| 28 | + public function __construct( |
|
| 29 | + private readonly ICloudIdManager $cloudIdManager, |
|
| 30 | + private readonly CalendarFederationNotifier $calendarFederationNotifier, |
|
| 31 | + private readonly LoggerInterface $logger, |
|
| 32 | + private readonly SharingMapper $sharingMapper, |
|
| 33 | + ) { |
|
| 34 | + } |
|
| 35 | 35 | |
| 36 | - public function handle(Event $event): void { |
|
| 37 | - if (!($event instanceof CalendarObjectCreatedEvent) |
|
| 38 | - && !($event instanceof CalendarObjectUpdatedEvent) |
|
| 39 | - && !($event instanceof CalendarObjectDeletedEvent) |
|
| 40 | - ) { |
|
| 41 | - return; |
|
| 42 | - } |
|
| 36 | + public function handle(Event $event): void { |
|
| 37 | + if (!($event instanceof CalendarObjectCreatedEvent) |
|
| 38 | + && !($event instanceof CalendarObjectUpdatedEvent) |
|
| 39 | + && !($event instanceof CalendarObjectDeletedEvent) |
|
| 40 | + ) { |
|
| 41 | + return; |
|
| 42 | + } |
|
| 43 | 43 | |
| 44 | - $remoteUserShares = array_filter($event->getShares(), function (array $share): bool { |
|
| 45 | - $sharedWithPrincipal = $share['{http://owncloud.org/ns}principal'] ?? ''; |
|
| 46 | - [$prefix] = \Sabre\Uri\split($sharedWithPrincipal); |
|
| 47 | - return $prefix === RemoteUserPrincipalBackend::PRINCIPAL_PREFIX; |
|
| 48 | - }); |
|
| 49 | - if (empty($remoteUserShares)) { |
|
| 50 | - // Not shared with any remote user |
|
| 51 | - return; |
|
| 52 | - } |
|
| 44 | + $remoteUserShares = array_filter($event->getShares(), function (array $share): bool { |
|
| 45 | + $sharedWithPrincipal = $share['{http://owncloud.org/ns}principal'] ?? ''; |
|
| 46 | + [$prefix] = \Sabre\Uri\split($sharedWithPrincipal); |
|
| 47 | + return $prefix === RemoteUserPrincipalBackend::PRINCIPAL_PREFIX; |
|
| 48 | + }); |
|
| 49 | + if (empty($remoteUserShares)) { |
|
| 50 | + // Not shared with any remote user |
|
| 51 | + return; |
|
| 52 | + } |
|
| 53 | 53 | |
| 54 | - $calendarInfo = $event->getCalendarData(); |
|
| 55 | - $remoteUserPrincipals = array_map( |
|
| 56 | - static fn (array $share) => $share['{http://owncloud.org/ns}principal'], |
|
| 57 | - $remoteUserShares, |
|
| 58 | - ); |
|
| 59 | - $remoteShares = $this->sharingMapper->getSharesByPrincipalsAndResource( |
|
| 60 | - $remoteUserPrincipals, |
|
| 61 | - (int)$calendarInfo['id'], |
|
| 62 | - 'calendar', |
|
| 63 | - ); |
|
| 54 | + $calendarInfo = $event->getCalendarData(); |
|
| 55 | + $remoteUserPrincipals = array_map( |
|
| 56 | + static fn (array $share) => $share['{http://owncloud.org/ns}principal'], |
|
| 57 | + $remoteUserShares, |
|
| 58 | + ); |
|
| 59 | + $remoteShares = $this->sharingMapper->getSharesByPrincipalsAndResource( |
|
| 60 | + $remoteUserPrincipals, |
|
| 61 | + (int)$calendarInfo['id'], |
|
| 62 | + 'calendar', |
|
| 63 | + ); |
|
| 64 | 64 | |
| 65 | - foreach ($remoteShares as $share) { |
|
| 66 | - [, $name] = \Sabre\Uri\split($share['principaluri']); |
|
| 67 | - $shareWithRaw = base64_decode($name); |
|
| 68 | - try { |
|
| 69 | - $shareWith = $this->cloudIdManager->resolveCloudId($shareWithRaw); |
|
| 70 | - } catch (\InvalidArgumentException $e) { |
|
| 71 | - // Not a valid remote user principal |
|
| 72 | - continue; |
|
| 73 | - } |
|
| 65 | + foreach ($remoteShares as $share) { |
|
| 66 | + [, $name] = \Sabre\Uri\split($share['principaluri']); |
|
| 67 | + $shareWithRaw = base64_decode($name); |
|
| 68 | + try { |
|
| 69 | + $shareWith = $this->cloudIdManager->resolveCloudId($shareWithRaw); |
|
| 70 | + } catch (\InvalidArgumentException $e) { |
|
| 71 | + // Not a valid remote user principal |
|
| 72 | + continue; |
|
| 73 | + } |
|
| 74 | 74 | |
| 75 | - [, $sharedByUid] = \Sabre\Uri\split($calendarInfo['principaluri']); |
|
| 75 | + [, $sharedByUid] = \Sabre\Uri\split($calendarInfo['principaluri']); |
|
| 76 | 76 | |
| 77 | - $remoteUrl = $shareWith->getRemote(); |
|
| 78 | - try { |
|
| 79 | - $response = $this->calendarFederationNotifier->notifySyncCalendar( |
|
| 80 | - $shareWith, |
|
| 81 | - $sharedByUid, |
|
| 82 | - $calendarInfo['uri'], |
|
| 83 | - $share['token'], |
|
| 84 | - ); |
|
| 85 | - } catch (OCMProviderException $e) { |
|
| 86 | - $this->logger->error("Failed to send SYNC_CALENDAR notification to remote $remoteUrl", [ |
|
| 87 | - 'exception' => $e, |
|
| 88 | - 'shareWith' => $shareWith->getId(), |
|
| 89 | - 'calendarName' => $calendarInfo['uri'], |
|
| 90 | - 'calendarOwner' => $sharedByUid, |
|
| 91 | - ]); |
|
| 92 | - continue; |
|
| 93 | - } |
|
| 77 | + $remoteUrl = $shareWith->getRemote(); |
|
| 78 | + try { |
|
| 79 | + $response = $this->calendarFederationNotifier->notifySyncCalendar( |
|
| 80 | + $shareWith, |
|
| 81 | + $sharedByUid, |
|
| 82 | + $calendarInfo['uri'], |
|
| 83 | + $share['token'], |
|
| 84 | + ); |
|
| 85 | + } catch (OCMProviderException $e) { |
|
| 86 | + $this->logger->error("Failed to send SYNC_CALENDAR notification to remote $remoteUrl", [ |
|
| 87 | + 'exception' => $e, |
|
| 88 | + 'shareWith' => $shareWith->getId(), |
|
| 89 | + 'calendarName' => $calendarInfo['uri'], |
|
| 90 | + 'calendarOwner' => $sharedByUid, |
|
| 91 | + ]); |
|
| 92 | + continue; |
|
| 93 | + } |
|
| 94 | 94 | |
| 95 | - if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { |
|
| 96 | - $this->logger->error("Remote $remoteUrl rejected SYNC_CALENDAR notification", [ |
|
| 97 | - 'statusCode' => $response->getStatusCode(), |
|
| 98 | - 'shareWith' => $shareWith->getId(), |
|
| 99 | - 'calendarName' => $calendarInfo['uri'], |
|
| 100 | - 'calendarOwner' => $sharedByUid, |
|
| 101 | - ]); |
|
| 102 | - } |
|
| 103 | - } |
|
| 104 | - } |
|
| 95 | + if ($response->getStatusCode() < 200 || $response->getStatusCode() >= 300) { |
|
| 96 | + $this->logger->error("Remote $remoteUrl rejected SYNC_CALENDAR notification", [ |
|
| 97 | + 'statusCode' => $response->getStatusCode(), |
|
| 98 | + 'shareWith' => $shareWith->getId(), |
|
| 99 | + 'calendarName' => $calendarInfo['uri'], |
|
| 100 | + 'calendarOwner' => $sharedByUid, |
|
| 101 | + ]); |
|
| 102 | + } |
|
| 103 | + } |
|
| 104 | + } |
|
| 105 | 105 | } |
@@ -20,16 +20,16 @@ |
||
| 20 | 20 | * @template-implements IEventListener<Event|SabrePluginAuthInitEvent> |
| 21 | 21 | */ |
| 22 | 22 | class SabrePluginAuthInitListener implements IEventListener { |
| 23 | - public function handle(Event $event): void { |
|
| 24 | - if (!($event instanceof SabrePluginAuthInitEvent)) { |
|
| 25 | - return; |
|
| 26 | - } |
|
| 23 | + public function handle(Event $event): void { |
|
| 24 | + if (!($event instanceof SabrePluginAuthInitEvent)) { |
|
| 25 | + return; |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - $server = $event->getServer(); |
|
| 29 | - $authPlugin = $server->getPlugin('auth'); |
|
| 30 | - if ($authPlugin instanceof Plugin) { |
|
| 31 | - $authBackend = Server::get(FederatedCalendarAuth::class); |
|
| 32 | - $authPlugin->addBackend($authBackend); |
|
| 33 | - } |
|
| 34 | - } |
|
| 28 | + $server = $event->getServer(); |
|
| 29 | + $authPlugin = $server->getPlugin('auth'); |
|
| 30 | + if ($authPlugin instanceof Plugin) { |
|
| 31 | + $authBackend = Server::get(FederatedCalendarAuth::class); |
|
| 32 | + $authPlugin->addBackend($authBackend); |
|
| 33 | + } |
|
| 34 | + } |
|
| 35 | 35 | } |
@@ -14,116 +14,116 @@ |
||
| 14 | 14 | use Sabre\DAVACL\PrincipalBackend\BackendInterface; |
| 15 | 15 | |
| 16 | 16 | class RemoteUserPrincipalBackend implements BackendInterface { |
| 17 | - public const PRINCIPAL_PREFIX = 'principals/remote-users'; |
|
| 18 | - |
|
| 19 | - private bool $hasCachedAllChildren = false; |
|
| 20 | - |
|
| 21 | - /** @var array<string, mixed>[] */ |
|
| 22 | - private array $principals = []; |
|
| 23 | - |
|
| 24 | - /** @var array<string, array<string, mixed>|null> */ |
|
| 25 | - private array $principalsByPath = []; |
|
| 26 | - |
|
| 27 | - public function __construct( |
|
| 28 | - private readonly ICloudIdManager $cloudIdManager, |
|
| 29 | - private readonly SharingMapper $sharingMapper, |
|
| 30 | - ) { |
|
| 31 | - } |
|
| 32 | - |
|
| 33 | - public function getPrincipalsByPrefix($prefixPath) { |
|
| 34 | - if ($prefixPath !== self::PRINCIPAL_PREFIX) { |
|
| 35 | - return []; |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - if (!$this->hasCachedAllChildren) { |
|
| 39 | - $this->loadChildren(); |
|
| 40 | - $this->hasCachedAllChildren = true; |
|
| 41 | - } |
|
| 42 | - |
|
| 43 | - return $this->principals; |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - public function getPrincipalByPath($path) { |
|
| 47 | - [$prefix] = \Sabre\Uri\split($path); |
|
| 48 | - if ($prefix !== self::PRINCIPAL_PREFIX) { |
|
| 49 | - return null; |
|
| 50 | - } |
|
| 51 | - |
|
| 52 | - if (isset($this->principalsByPath[$path])) { |
|
| 53 | - return $this->principalsByPath[$path]; |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - try { |
|
| 57 | - $principal = $this->principalUriToPrincipal($path); |
|
| 58 | - } catch (\Exception $e) { |
|
| 59 | - $principal = null; |
|
| 60 | - } |
|
| 61 | - $this->principalsByPath[$path] = $principal; |
|
| 62 | - return $principal; |
|
| 63 | - } |
|
| 64 | - |
|
| 65 | - public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { |
|
| 66 | - throw new \Sabre\DAV\Exception('Updating remote user principal is not supported'); |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { |
|
| 70 | - // Searching is not supported |
|
| 71 | - return []; |
|
| 72 | - } |
|
| 73 | - |
|
| 74 | - public function findByUri($uri, $principalPrefix) { |
|
| 75 | - if (str_starts_with($uri, 'principal:')) { |
|
| 76 | - $principal = substr($uri, strlen('principal:')); |
|
| 77 | - $principal = $this->getPrincipalByPath($principal); |
|
| 78 | - if ($principal !== null) { |
|
| 79 | - return $principal['uri']; |
|
| 80 | - } |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - return null; |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - public function getGroupMemberSet($principal) { |
|
| 87 | - return []; |
|
| 88 | - } |
|
| 89 | - |
|
| 90 | - public function getGroupMembership($principal) { |
|
| 91 | - // TODO: for now the group principal has only one member, the user itself |
|
| 92 | - $principal = $this->getPrincipalByPath($principal); |
|
| 93 | - if (!$principal) { |
|
| 94 | - throw new \Sabre\DAV\Exception('Principal not found'); |
|
| 95 | - } |
|
| 96 | - |
|
| 97 | - return [$principal['uri']]; |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - public function setGroupMemberSet($principal, array $members) { |
|
| 101 | - throw new \Sabre\DAV\Exception('Adding members to remote user is not supported'); |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - /** |
|
| 105 | - * @return array{'{DAV:}displayname': string, '{http://nextcloud.com/ns}cloud-id': \OCP\Federation\ICloudId, uri: string} |
|
| 106 | - */ |
|
| 107 | - private function principalUriToPrincipal(string $principalUri): array { |
|
| 108 | - [, $name] = \Sabre\Uri\split($principalUri); |
|
| 109 | - $cloudId = $this->cloudIdManager->resolveCloudId(base64_decode($name)); |
|
| 110 | - return [ |
|
| 111 | - 'uri' => $principalUri, |
|
| 112 | - '{DAV:}displayname' => $cloudId->getDisplayId(), |
|
| 113 | - '{http://nextcloud.com/ns}cloud-id' => $cloudId, |
|
| 114 | - ]; |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - private function loadChildren(): void { |
|
| 118 | - $rows = $this->sharingMapper->getPrincipalUrisByPrefix('calendar', self::PRINCIPAL_PREFIX); |
|
| 119 | - $this->principals = array_map( |
|
| 120 | - fn (array $row) => $this->principalUriToPrincipal($row['principaluri']), |
|
| 121 | - $rows, |
|
| 122 | - ); |
|
| 123 | - |
|
| 124 | - $this->principalsByPath = []; |
|
| 125 | - foreach ($this->principals as $child) { |
|
| 126 | - $this->principalsByPath[$child['uri']] = $child; |
|
| 127 | - } |
|
| 128 | - } |
|
| 17 | + public const PRINCIPAL_PREFIX = 'principals/remote-users'; |
|
| 18 | + |
|
| 19 | + private bool $hasCachedAllChildren = false; |
|
| 20 | + |
|
| 21 | + /** @var array<string, mixed>[] */ |
|
| 22 | + private array $principals = []; |
|
| 23 | + |
|
| 24 | + /** @var array<string, array<string, mixed>|null> */ |
|
| 25 | + private array $principalsByPath = []; |
|
| 26 | + |
|
| 27 | + public function __construct( |
|
| 28 | + private readonly ICloudIdManager $cloudIdManager, |
|
| 29 | + private readonly SharingMapper $sharingMapper, |
|
| 30 | + ) { |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + public function getPrincipalsByPrefix($prefixPath) { |
|
| 34 | + if ($prefixPath !== self::PRINCIPAL_PREFIX) { |
|
| 35 | + return []; |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + if (!$this->hasCachedAllChildren) { |
|
| 39 | + $this->loadChildren(); |
|
| 40 | + $this->hasCachedAllChildren = true; |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + return $this->principals; |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + public function getPrincipalByPath($path) { |
|
| 47 | + [$prefix] = \Sabre\Uri\split($path); |
|
| 48 | + if ($prefix !== self::PRINCIPAL_PREFIX) { |
|
| 49 | + return null; |
|
| 50 | + } |
|
| 51 | + |
|
| 52 | + if (isset($this->principalsByPath[$path])) { |
|
| 53 | + return $this->principalsByPath[$path]; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + try { |
|
| 57 | + $principal = $this->principalUriToPrincipal($path); |
|
| 58 | + } catch (\Exception $e) { |
|
| 59 | + $principal = null; |
|
| 60 | + } |
|
| 61 | + $this->principalsByPath[$path] = $principal; |
|
| 62 | + return $principal; |
|
| 63 | + } |
|
| 64 | + |
|
| 65 | + public function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) { |
|
| 66 | + throw new \Sabre\DAV\Exception('Updating remote user principal is not supported'); |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + public function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') { |
|
| 70 | + // Searching is not supported |
|
| 71 | + return []; |
|
| 72 | + } |
|
| 73 | + |
|
| 74 | + public function findByUri($uri, $principalPrefix) { |
|
| 75 | + if (str_starts_with($uri, 'principal:')) { |
|
| 76 | + $principal = substr($uri, strlen('principal:')); |
|
| 77 | + $principal = $this->getPrincipalByPath($principal); |
|
| 78 | + if ($principal !== null) { |
|
| 79 | + return $principal['uri']; |
|
| 80 | + } |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + return null; |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + public function getGroupMemberSet($principal) { |
|
| 87 | + return []; |
|
| 88 | + } |
|
| 89 | + |
|
| 90 | + public function getGroupMembership($principal) { |
|
| 91 | + // TODO: for now the group principal has only one member, the user itself |
|
| 92 | + $principal = $this->getPrincipalByPath($principal); |
|
| 93 | + if (!$principal) { |
|
| 94 | + throw new \Sabre\DAV\Exception('Principal not found'); |
|
| 95 | + } |
|
| 96 | + |
|
| 97 | + return [$principal['uri']]; |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + public function setGroupMemberSet($principal, array $members) { |
|
| 101 | + throw new \Sabre\DAV\Exception('Adding members to remote user is not supported'); |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + /** |
|
| 105 | + * @return array{'{DAV:}displayname': string, '{http://nextcloud.com/ns}cloud-id': \OCP\Federation\ICloudId, uri: string} |
|
| 106 | + */ |
|
| 107 | + private function principalUriToPrincipal(string $principalUri): array { |
|
| 108 | + [, $name] = \Sabre\Uri\split($principalUri); |
|
| 109 | + $cloudId = $this->cloudIdManager->resolveCloudId(base64_decode($name)); |
|
| 110 | + return [ |
|
| 111 | + 'uri' => $principalUri, |
|
| 112 | + '{DAV:}displayname' => $cloudId->getDisplayId(), |
|
| 113 | + '{http://nextcloud.com/ns}cloud-id' => $cloudId, |
|
| 114 | + ]; |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + private function loadChildren(): void { |
|
| 118 | + $rows = $this->sharingMapper->getPrincipalUrisByPrefix('calendar', self::PRINCIPAL_PREFIX); |
|
| 119 | + $this->principals = array_map( |
|
| 120 | + fn (array $row) => $this->principalUriToPrincipal($row['principaluri']), |
|
| 121 | + $rows, |
|
| 122 | + ); |
|
| 123 | + |
|
| 124 | + $this->principalsByPath = []; |
|
| 125 | + foreach ($this->principals as $child) { |
|
| 126 | + $this->principalsByPath[$child['uri']] = $child; |
|
| 127 | + } |
|
| 128 | + } |
|
| 129 | 129 | } |
@@ -11,239 +11,239 @@ |
||
| 11 | 11 | use OCP\IDBConnection; |
| 12 | 12 | |
| 13 | 13 | class SharingMapper { |
| 14 | - public function __construct( |
|
| 15 | - private IDBConnection $db, |
|
| 16 | - ) { |
|
| 17 | - } |
|
| 18 | - |
|
| 19 | - protected function getSharesForIdByAccess(int $resourceId, string $resourceType, bool $sharesWithAccess): array { |
|
| 20 | - $query = $this->db->getQueryBuilder(); |
|
| 21 | - $query->select(['principaluri', 'access']) |
|
| 22 | - ->from('dav_shares') |
|
| 23 | - ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT))) |
|
| 24 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR))) |
|
| 25 | - ->groupBy(['principaluri', 'access']); |
|
| 26 | - |
|
| 27 | - if ($sharesWithAccess) { |
|
| 28 | - $query->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))); |
|
| 29 | - } else { |
|
| 30 | - $query->andWhere($query->expr()->eq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))); |
|
| 31 | - } |
|
| 32 | - |
|
| 33 | - $result = $query->executeQuery(); |
|
| 34 | - $rows = $result->fetchAll(); |
|
| 35 | - $result->closeCursor(); |
|
| 36 | - return $rows; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - public function getSharesForId(int $resourceId, string $resourceType): array { |
|
| 40 | - return $this->getSharesForIdByAccess($resourceId, $resourceType, true); |
|
| 41 | - } |
|
| 42 | - |
|
| 43 | - public function getUnsharesForId(int $resourceId, string $resourceType): array { |
|
| 44 | - return $this->getSharesForIdByAccess($resourceId, $resourceType, false); |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - public function getSharesForIds(array $resourceIds, string $resourceType): array { |
|
| 48 | - $query = $this->db->getQueryBuilder(); |
|
| 49 | - $result = $query->select(['resourceid', 'principaluri', 'access']) |
|
| 50 | - ->from('dav_shares') |
|
| 51 | - ->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 52 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 53 | - ->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))) |
|
| 54 | - ->groupBy(['principaluri', 'access', 'resourceid']) |
|
| 55 | - ->executeQuery(); |
|
| 56 | - |
|
| 57 | - $rows = $result->fetchAll(); |
|
| 58 | - $result->closeCursor(); |
|
| 59 | - return $rows; |
|
| 60 | - } |
|
| 61 | - |
|
| 62 | - public function unshare(int $resourceId, string $resourceType, string $principal): void { |
|
| 63 | - $query = $this->db->getQueryBuilder(); |
|
| 64 | - $query->insert('dav_shares') |
|
| 65 | - ->values([ |
|
| 66 | - 'principaluri' => $query->createNamedParameter($principal), |
|
| 67 | - 'type' => $query->createNamedParameter($resourceType), |
|
| 68 | - 'access' => $query->createNamedParameter(Backend::ACCESS_UNSHARED), |
|
| 69 | - 'resourceid' => $query->createNamedParameter($resourceId) |
|
| 70 | - ]); |
|
| 71 | - $query->executeStatement(); |
|
| 72 | - } |
|
| 73 | - |
|
| 74 | - public function share(int $resourceId, string $resourceType, int $access, string $principal): void { |
|
| 75 | - $query = $this->db->getQueryBuilder(); |
|
| 76 | - $query->insert('dav_shares') |
|
| 77 | - ->values([ |
|
| 78 | - 'principaluri' => $query->createNamedParameter($principal), |
|
| 79 | - 'type' => $query->createNamedParameter($resourceType), |
|
| 80 | - 'access' => $query->createNamedParameter($access), |
|
| 81 | - 'resourceid' => $query->createNamedParameter($resourceId) |
|
| 82 | - ]); |
|
| 83 | - $query->executeStatement(); |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - public function shareWithToken(int $resourceId, string $resourceType, int $access, string $principal, string $token): void { |
|
| 87 | - $query = $this->db->getQueryBuilder(); |
|
| 88 | - $query->insert('dav_shares') |
|
| 89 | - ->values([ |
|
| 90 | - 'principaluri' => $query->createNamedParameter($principal), |
|
| 91 | - 'type' => $query->createNamedParameter($resourceType), |
|
| 92 | - 'access' => $query->createNamedParameter($access), |
|
| 93 | - 'resourceid' => $query->createNamedParameter($resourceId), |
|
| 94 | - 'token' => $query->createNamedParameter($token), |
|
| 95 | - ]); |
|
| 96 | - $query->executeStatement(); |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - public function deleteShare(int $resourceId, string $resourceType, string $principal): void { |
|
| 100 | - $query = $this->db->getQueryBuilder(); |
|
| 101 | - $query->delete('dav_shares'); |
|
| 102 | - $query->where( |
|
| 103 | - $query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT)), |
|
| 104 | - $query->expr()->eq('type', $query->createNamedParameter($resourceType)), |
|
| 105 | - $query->expr()->eq('principaluri', $query->createNamedParameter($principal)) |
|
| 106 | - ); |
|
| 107 | - $query->executeStatement(); |
|
| 108 | - |
|
| 109 | - } |
|
| 110 | - |
|
| 111 | - public function deleteAllShares(int $resourceId, string $resourceType): void { |
|
| 112 | - $query = $this->db->getQueryBuilder(); |
|
| 113 | - $query->delete('dav_shares') |
|
| 114 | - ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId))) |
|
| 115 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 116 | - ->executeStatement(); |
|
| 117 | - } |
|
| 118 | - |
|
| 119 | - public function deleteAllSharesByUser(string $principaluri, string $resourceType): void { |
|
| 120 | - $query = $this->db->getQueryBuilder(); |
|
| 121 | - $query->delete('dav_shares') |
|
| 122 | - ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri))) |
|
| 123 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 124 | - ->executeStatement(); |
|
| 125 | - } |
|
| 126 | - |
|
| 127 | - public function getSharesByPrincipals(array $principals, string $resourceType): array { |
|
| 128 | - $query = $this->db->getQueryBuilder(); |
|
| 129 | - $result = $query->select(['id', 'principaluri', 'type', 'access', 'resourceid']) |
|
| 130 | - ->from('dav_shares') |
|
| 131 | - ->where($query->expr()->in('principaluri', $query->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY)) |
|
| 132 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 133 | - ->orderBy('id') |
|
| 134 | - ->executeQuery(); |
|
| 135 | - |
|
| 136 | - $rows = $result->fetchAll(); |
|
| 137 | - $result->closeCursor(); |
|
| 138 | - |
|
| 139 | - return $rows; |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - public function deleteUnsharesByPrincipal(string $principal, string $resourceType): void { |
|
| 143 | - $query = $this->db->getQueryBuilder(); |
|
| 144 | - $query->delete('dav_shares') |
|
| 145 | - ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) |
|
| 146 | - ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 147 | - ->andWhere($query->expr()->eq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))) |
|
| 148 | - ->executeStatement(); |
|
| 149 | - } |
|
| 150 | - |
|
| 151 | - /** |
|
| 152 | - * @return array{principaluri: string}[] |
|
| 153 | - * @throws \OCP\DB\Exception |
|
| 154 | - */ |
|
| 155 | - public function getPrincipalUrisByPrefix(string $resourceType, string $prefix): array { |
|
| 156 | - $query = $this->db->getQueryBuilder(); |
|
| 157 | - $result = $query->selectDistinct('principaluri') |
|
| 158 | - ->from('dav_shares') |
|
| 159 | - ->where($query->expr()->like( |
|
| 160 | - 'principaluri', |
|
| 161 | - $query->createNamedParameter("$prefix/%", IQueryBuilder::PARAM_STR), |
|
| 162 | - IQueryBuilder::PARAM_STR, |
|
| 163 | - )) |
|
| 164 | - ->andWhere($query->expr()->eq( |
|
| 165 | - 'type', |
|
| 166 | - $query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR)), |
|
| 167 | - IQueryBuilder::PARAM_STR, |
|
| 168 | - ) |
|
| 169 | - ->executeQuery(); |
|
| 170 | - |
|
| 171 | - $rows = $result->fetchAll(); |
|
| 172 | - $result->closeCursor(); |
|
| 173 | - |
|
| 174 | - return $rows; |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - /** |
|
| 178 | - * @psalm-return array{uri: string, principaluri: string}[] |
|
| 179 | - * @throws \OCP\DB\Exception |
|
| 180 | - */ |
|
| 181 | - public function getSharedCalendarsForRemoteUser( |
|
| 182 | - string $remoteUserPrincipalUri, |
|
| 183 | - string $token, |
|
| 184 | - ): array { |
|
| 185 | - $qb = $this->db->getQueryBuilder(); |
|
| 186 | - $qb->select('c.uri', 'c.principaluri') |
|
| 187 | - ->from('dav_shares', 'ds') |
|
| 188 | - ->join('ds', 'calendars', 'c', $qb->expr()->eq( |
|
| 189 | - 'ds.resourceid', |
|
| 190 | - 'c.id', |
|
| 191 | - IQueryBuilder::PARAM_INT, |
|
| 192 | - )) |
|
| 193 | - ->where($qb->expr()->eq( |
|
| 194 | - 'ds.type', |
|
| 195 | - $qb->createNamedParameter('calendar', IQueryBuilder::PARAM_STR), |
|
| 196 | - IQueryBuilder::PARAM_STR, |
|
| 197 | - )) |
|
| 198 | - ->andWhere($qb->expr()->eq( |
|
| 199 | - 'ds.principaluri', |
|
| 200 | - $qb->createNamedParameter($remoteUserPrincipalUri, IQueryBuilder::PARAM_STR), |
|
| 201 | - IQueryBuilder::PARAM_STR, |
|
| 202 | - )) |
|
| 203 | - ->andWhere($qb->expr()->eq( |
|
| 204 | - 'ds.token', |
|
| 205 | - $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR), |
|
| 206 | - IQueryBuilder::PARAM_STR, |
|
| 207 | - )); |
|
| 208 | - $result = $qb->executeQuery(); |
|
| 209 | - $rows = $result->fetchAll(); |
|
| 210 | - $result->closeCursor(); |
|
| 211 | - |
|
| 212 | - return $rows; |
|
| 213 | - } |
|
| 214 | - |
|
| 215 | - /** |
|
| 216 | - * @param string[] $principalUris |
|
| 217 | - * |
|
| 218 | - * @throws \OCP\DB\Exception |
|
| 219 | - */ |
|
| 220 | - public function getSharesByPrincipalsAndResource( |
|
| 221 | - array $principalUris, |
|
| 222 | - int $resourceId, |
|
| 223 | - string $resourceType, |
|
| 224 | - ): array { |
|
| 225 | - $qb = $this->db->getQueryBuilder(); |
|
| 226 | - $qb->select('*') |
|
| 227 | - ->from('dav_shares') |
|
| 228 | - ->where($qb->expr()->in( |
|
| 229 | - 'principaluri', |
|
| 230 | - $qb->createNamedParameter($principalUris, IQueryBuilder::PARAM_STR_ARRAY), |
|
| 231 | - IQueryBuilder::PARAM_STR_ARRAY, |
|
| 232 | - )) |
|
| 233 | - ->andWhere($qb->expr()->eq( |
|
| 234 | - 'resourceid', |
|
| 235 | - $qb->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT), |
|
| 236 | - IQueryBuilder::PARAM_INT, |
|
| 237 | - )) |
|
| 238 | - ->andWhere($qb->expr()->eq( |
|
| 239 | - 'type', |
|
| 240 | - $qb->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR), |
|
| 241 | - IQueryBuilder::PARAM_STR, |
|
| 242 | - )); |
|
| 243 | - $result = $qb->executeQuery(); |
|
| 244 | - $rows = $result->fetchAll(); |
|
| 245 | - $result->closeCursor(); |
|
| 246 | - |
|
| 247 | - return $rows; |
|
| 248 | - } |
|
| 14 | + public function __construct( |
|
| 15 | + private IDBConnection $db, |
|
| 16 | + ) { |
|
| 17 | + } |
|
| 18 | + |
|
| 19 | + protected function getSharesForIdByAccess(int $resourceId, string $resourceType, bool $sharesWithAccess): array { |
|
| 20 | + $query = $this->db->getQueryBuilder(); |
|
| 21 | + $query->select(['principaluri', 'access']) |
|
| 22 | + ->from('dav_shares') |
|
| 23 | + ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT))) |
|
| 24 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR))) |
|
| 25 | + ->groupBy(['principaluri', 'access']); |
|
| 26 | + |
|
| 27 | + if ($sharesWithAccess) { |
|
| 28 | + $query->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))); |
|
| 29 | + } else { |
|
| 30 | + $query->andWhere($query->expr()->eq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + $result = $query->executeQuery(); |
|
| 34 | + $rows = $result->fetchAll(); |
|
| 35 | + $result->closeCursor(); |
|
| 36 | + return $rows; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + public function getSharesForId(int $resourceId, string $resourceType): array { |
|
| 40 | + return $this->getSharesForIdByAccess($resourceId, $resourceType, true); |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + public function getUnsharesForId(int $resourceId, string $resourceType): array { |
|
| 44 | + return $this->getSharesForIdByAccess($resourceId, $resourceType, false); |
|
| 45 | + } |
|
| 46 | + |
|
| 47 | + public function getSharesForIds(array $resourceIds, string $resourceType): array { |
|
| 48 | + $query = $this->db->getQueryBuilder(); |
|
| 49 | + $result = $query->select(['resourceid', 'principaluri', 'access']) |
|
| 50 | + ->from('dav_shares') |
|
| 51 | + ->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 52 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 53 | + ->andWhere($query->expr()->neq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))) |
|
| 54 | + ->groupBy(['principaluri', 'access', 'resourceid']) |
|
| 55 | + ->executeQuery(); |
|
| 56 | + |
|
| 57 | + $rows = $result->fetchAll(); |
|
| 58 | + $result->closeCursor(); |
|
| 59 | + return $rows; |
|
| 60 | + } |
|
| 61 | + |
|
| 62 | + public function unshare(int $resourceId, string $resourceType, string $principal): void { |
|
| 63 | + $query = $this->db->getQueryBuilder(); |
|
| 64 | + $query->insert('dav_shares') |
|
| 65 | + ->values([ |
|
| 66 | + 'principaluri' => $query->createNamedParameter($principal), |
|
| 67 | + 'type' => $query->createNamedParameter($resourceType), |
|
| 68 | + 'access' => $query->createNamedParameter(Backend::ACCESS_UNSHARED), |
|
| 69 | + 'resourceid' => $query->createNamedParameter($resourceId) |
|
| 70 | + ]); |
|
| 71 | + $query->executeStatement(); |
|
| 72 | + } |
|
| 73 | + |
|
| 74 | + public function share(int $resourceId, string $resourceType, int $access, string $principal): void { |
|
| 75 | + $query = $this->db->getQueryBuilder(); |
|
| 76 | + $query->insert('dav_shares') |
|
| 77 | + ->values([ |
|
| 78 | + 'principaluri' => $query->createNamedParameter($principal), |
|
| 79 | + 'type' => $query->createNamedParameter($resourceType), |
|
| 80 | + 'access' => $query->createNamedParameter($access), |
|
| 81 | + 'resourceid' => $query->createNamedParameter($resourceId) |
|
| 82 | + ]); |
|
| 83 | + $query->executeStatement(); |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + public function shareWithToken(int $resourceId, string $resourceType, int $access, string $principal, string $token): void { |
|
| 87 | + $query = $this->db->getQueryBuilder(); |
|
| 88 | + $query->insert('dav_shares') |
|
| 89 | + ->values([ |
|
| 90 | + 'principaluri' => $query->createNamedParameter($principal), |
|
| 91 | + 'type' => $query->createNamedParameter($resourceType), |
|
| 92 | + 'access' => $query->createNamedParameter($access), |
|
| 93 | + 'resourceid' => $query->createNamedParameter($resourceId), |
|
| 94 | + 'token' => $query->createNamedParameter($token), |
|
| 95 | + ]); |
|
| 96 | + $query->executeStatement(); |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + public function deleteShare(int $resourceId, string $resourceType, string $principal): void { |
|
| 100 | + $query = $this->db->getQueryBuilder(); |
|
| 101 | + $query->delete('dav_shares'); |
|
| 102 | + $query->where( |
|
| 103 | + $query->expr()->eq('resourceid', $query->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT)), |
|
| 104 | + $query->expr()->eq('type', $query->createNamedParameter($resourceType)), |
|
| 105 | + $query->expr()->eq('principaluri', $query->createNamedParameter($principal)) |
|
| 106 | + ); |
|
| 107 | + $query->executeStatement(); |
|
| 108 | + |
|
| 109 | + } |
|
| 110 | + |
|
| 111 | + public function deleteAllShares(int $resourceId, string $resourceType): void { |
|
| 112 | + $query = $this->db->getQueryBuilder(); |
|
| 113 | + $query->delete('dav_shares') |
|
| 114 | + ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId))) |
|
| 115 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 116 | + ->executeStatement(); |
|
| 117 | + } |
|
| 118 | + |
|
| 119 | + public function deleteAllSharesByUser(string $principaluri, string $resourceType): void { |
|
| 120 | + $query = $this->db->getQueryBuilder(); |
|
| 121 | + $query->delete('dav_shares') |
|
| 122 | + ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri))) |
|
| 123 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 124 | + ->executeStatement(); |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + public function getSharesByPrincipals(array $principals, string $resourceType): array { |
|
| 128 | + $query = $this->db->getQueryBuilder(); |
|
| 129 | + $result = $query->select(['id', 'principaluri', 'type', 'access', 'resourceid']) |
|
| 130 | + ->from('dav_shares') |
|
| 131 | + ->where($query->expr()->in('principaluri', $query->createNamedParameter($principals, IQueryBuilder::PARAM_STR_ARRAY), IQueryBuilder::PARAM_STR_ARRAY)) |
|
| 132 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 133 | + ->orderBy('id') |
|
| 134 | + ->executeQuery(); |
|
| 135 | + |
|
| 136 | + $rows = $result->fetchAll(); |
|
| 137 | + $result->closeCursor(); |
|
| 138 | + |
|
| 139 | + return $rows; |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + public function deleteUnsharesByPrincipal(string $principal, string $resourceType): void { |
|
| 143 | + $query = $this->db->getQueryBuilder(); |
|
| 144 | + $query->delete('dav_shares') |
|
| 145 | + ->where($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) |
|
| 146 | + ->andWhere($query->expr()->eq('type', $query->createNamedParameter($resourceType))) |
|
| 147 | + ->andWhere($query->expr()->eq('access', $query->createNamedParameter(Backend::ACCESS_UNSHARED, IQueryBuilder::PARAM_INT))) |
|
| 148 | + ->executeStatement(); |
|
| 149 | + } |
|
| 150 | + |
|
| 151 | + /** |
|
| 152 | + * @return array{principaluri: string}[] |
|
| 153 | + * @throws \OCP\DB\Exception |
|
| 154 | + */ |
|
| 155 | + public function getPrincipalUrisByPrefix(string $resourceType, string $prefix): array { |
|
| 156 | + $query = $this->db->getQueryBuilder(); |
|
| 157 | + $result = $query->selectDistinct('principaluri') |
|
| 158 | + ->from('dav_shares') |
|
| 159 | + ->where($query->expr()->like( |
|
| 160 | + 'principaluri', |
|
| 161 | + $query->createNamedParameter("$prefix/%", IQueryBuilder::PARAM_STR), |
|
| 162 | + IQueryBuilder::PARAM_STR, |
|
| 163 | + )) |
|
| 164 | + ->andWhere($query->expr()->eq( |
|
| 165 | + 'type', |
|
| 166 | + $query->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR)), |
|
| 167 | + IQueryBuilder::PARAM_STR, |
|
| 168 | + ) |
|
| 169 | + ->executeQuery(); |
|
| 170 | + |
|
| 171 | + $rows = $result->fetchAll(); |
|
| 172 | + $result->closeCursor(); |
|
| 173 | + |
|
| 174 | + return $rows; |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + /** |
|
| 178 | + * @psalm-return array{uri: string, principaluri: string}[] |
|
| 179 | + * @throws \OCP\DB\Exception |
|
| 180 | + */ |
|
| 181 | + public function getSharedCalendarsForRemoteUser( |
|
| 182 | + string $remoteUserPrincipalUri, |
|
| 183 | + string $token, |
|
| 184 | + ): array { |
|
| 185 | + $qb = $this->db->getQueryBuilder(); |
|
| 186 | + $qb->select('c.uri', 'c.principaluri') |
|
| 187 | + ->from('dav_shares', 'ds') |
|
| 188 | + ->join('ds', 'calendars', 'c', $qb->expr()->eq( |
|
| 189 | + 'ds.resourceid', |
|
| 190 | + 'c.id', |
|
| 191 | + IQueryBuilder::PARAM_INT, |
|
| 192 | + )) |
|
| 193 | + ->where($qb->expr()->eq( |
|
| 194 | + 'ds.type', |
|
| 195 | + $qb->createNamedParameter('calendar', IQueryBuilder::PARAM_STR), |
|
| 196 | + IQueryBuilder::PARAM_STR, |
|
| 197 | + )) |
|
| 198 | + ->andWhere($qb->expr()->eq( |
|
| 199 | + 'ds.principaluri', |
|
| 200 | + $qb->createNamedParameter($remoteUserPrincipalUri, IQueryBuilder::PARAM_STR), |
|
| 201 | + IQueryBuilder::PARAM_STR, |
|
| 202 | + )) |
|
| 203 | + ->andWhere($qb->expr()->eq( |
|
| 204 | + 'ds.token', |
|
| 205 | + $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR), |
|
| 206 | + IQueryBuilder::PARAM_STR, |
|
| 207 | + )); |
|
| 208 | + $result = $qb->executeQuery(); |
|
| 209 | + $rows = $result->fetchAll(); |
|
| 210 | + $result->closeCursor(); |
|
| 211 | + |
|
| 212 | + return $rows; |
|
| 213 | + } |
|
| 214 | + |
|
| 215 | + /** |
|
| 216 | + * @param string[] $principalUris |
|
| 217 | + * |
|
| 218 | + * @throws \OCP\DB\Exception |
|
| 219 | + */ |
|
| 220 | + public function getSharesByPrincipalsAndResource( |
|
| 221 | + array $principalUris, |
|
| 222 | + int $resourceId, |
|
| 223 | + string $resourceType, |
|
| 224 | + ): array { |
|
| 225 | + $qb = $this->db->getQueryBuilder(); |
|
| 226 | + $qb->select('*') |
|
| 227 | + ->from('dav_shares') |
|
| 228 | + ->where($qb->expr()->in( |
|
| 229 | + 'principaluri', |
|
| 230 | + $qb->createNamedParameter($principalUris, IQueryBuilder::PARAM_STR_ARRAY), |
|
| 231 | + IQueryBuilder::PARAM_STR_ARRAY, |
|
| 232 | + )) |
|
| 233 | + ->andWhere($qb->expr()->eq( |
|
| 234 | + 'resourceid', |
|
| 235 | + $qb->createNamedParameter($resourceId, IQueryBuilder::PARAM_INT), |
|
| 236 | + IQueryBuilder::PARAM_INT, |
|
| 237 | + )) |
|
| 238 | + ->andWhere($qb->expr()->eq( |
|
| 239 | + 'type', |
|
| 240 | + $qb->createNamedParameter($resourceType, IQueryBuilder::PARAM_STR), |
|
| 241 | + IQueryBuilder::PARAM_STR, |
|
| 242 | + )); |
|
| 243 | + $result = $qb->executeQuery(); |
|
| 244 | + $rows = $result->fetchAll(); |
|
| 245 | + $result->closeCursor(); |
|
| 246 | + |
|
| 247 | + return $rows; |
|
| 248 | + } |
|
| 249 | 249 | } |
@@ -8,53 +8,53 @@ |
||
| 8 | 8 | namespace OCA\DAV\DAV\Sharing; |
| 9 | 9 | |
| 10 | 10 | abstract class SharingService { |
| 11 | - protected string $resourceType = ''; |
|
| 12 | - public function __construct( |
|
| 13 | - protected SharingMapper $mapper, |
|
| 14 | - ) { |
|
| 15 | - } |
|
| 16 | - |
|
| 17 | - public function getResourceType(): string { |
|
| 18 | - return $this->resourceType; |
|
| 19 | - } |
|
| 20 | - public function shareWith(int $resourceId, string $principal, int $access): void { |
|
| 21 | - // remove the share if it already exists |
|
| 22 | - $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal); |
|
| 23 | - $this->mapper->share($resourceId, $this->getResourceType(), $access, $principal); |
|
| 24 | - } |
|
| 25 | - |
|
| 26 | - public function unshare(int $resourceId, string $principal): void { |
|
| 27 | - $this->mapper->unshare($resourceId, $this->getResourceType(), $principal); |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - public function deleteShare(int $resourceId, string $principal): void { |
|
| 31 | - $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal); |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - public function deleteAllShares(int $resourceId): void { |
|
| 35 | - $this->mapper->deleteAllShares($resourceId, $this->getResourceType()); |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - public function deleteAllSharesByUser(string $principaluri): void { |
|
| 39 | - $this->mapper->deleteAllSharesByUser($principaluri, $this->getResourceType()); |
|
| 40 | - } |
|
| 41 | - |
|
| 42 | - public function getShares(int $resourceId): array { |
|
| 43 | - return $this->mapper->getSharesForId($resourceId, $this->getResourceType()); |
|
| 44 | - } |
|
| 45 | - |
|
| 46 | - public function getUnshares(int $resourceId): array { |
|
| 47 | - return $this->mapper->getUnsharesForId($resourceId, $this->getResourceType()); |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - public function getSharesForIds(array $resourceIds): array { |
|
| 51 | - return $this->mapper->getSharesForIds($resourceIds, $this->getResourceType()); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - /** |
|
| 55 | - * @param string[] $principals |
|
| 56 | - */ |
|
| 57 | - public function getSharesByPrincipals(array $principals): array { |
|
| 58 | - return $this->mapper->getSharesByPrincipals($principals, $this->getResourceType()); |
|
| 59 | - } |
|
| 11 | + protected string $resourceType = ''; |
|
| 12 | + public function __construct( |
|
| 13 | + protected SharingMapper $mapper, |
|
| 14 | + ) { |
|
| 15 | + } |
|
| 16 | + |
|
| 17 | + public function getResourceType(): string { |
|
| 18 | + return $this->resourceType; |
|
| 19 | + } |
|
| 20 | + public function shareWith(int $resourceId, string $principal, int $access): void { |
|
| 21 | + // remove the share if it already exists |
|
| 22 | + $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal); |
|
| 23 | + $this->mapper->share($resourceId, $this->getResourceType(), $access, $principal); |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public function unshare(int $resourceId, string $principal): void { |
|
| 27 | + $this->mapper->unshare($resourceId, $this->getResourceType(), $principal); |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + public function deleteShare(int $resourceId, string $principal): void { |
|
| 31 | + $this->mapper->deleteShare($resourceId, $this->getResourceType(), $principal); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + public function deleteAllShares(int $resourceId): void { |
|
| 35 | + $this->mapper->deleteAllShares($resourceId, $this->getResourceType()); |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + public function deleteAllSharesByUser(string $principaluri): void { |
|
| 39 | + $this->mapper->deleteAllSharesByUser($principaluri, $this->getResourceType()); |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + public function getShares(int $resourceId): array { |
|
| 43 | + return $this->mapper->getSharesForId($resourceId, $this->getResourceType()); |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + public function getUnshares(int $resourceId): array { |
|
| 47 | + return $this->mapper->getUnsharesForId($resourceId, $this->getResourceType()); |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + public function getSharesForIds(array $resourceIds): array { |
|
| 51 | + return $this->mapper->getSharesForIds($resourceIds, $this->getResourceType()); |
|
| 52 | + } |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * @param string[] $principals |
|
| 56 | + */ |
|
| 57 | + public function getSharesByPrincipals(array $principals): array { |
|
| 58 | + return $this->mapper->getSharesByPrincipals($principals, $this->getResourceType()); |
|
| 59 | + } |
|
| 60 | 60 | } |
@@ -19,250 +19,250 @@ |
||
| 19 | 19 | use Psr\Log\LoggerInterface; |
| 20 | 20 | |
| 21 | 21 | abstract class Backend { |
| 22 | - use TTransactional; |
|
| 23 | - public const ACCESS_OWNER = 1; |
|
| 24 | - |
|
| 25 | - public const ACCESS_READ_WRITE = 2; |
|
| 26 | - public const ACCESS_READ = 3; |
|
| 27 | - // 4 is already in use for public calendars |
|
| 28 | - public const ACCESS_UNSHARED = 5; |
|
| 29 | - |
|
| 30 | - private ICache $shareCache; |
|
| 31 | - |
|
| 32 | - public function __construct( |
|
| 33 | - private IUserManager $userManager, |
|
| 34 | - private IGroupManager $groupManager, |
|
| 35 | - private Principal $principalBackend, |
|
| 36 | - private RemoteUserPrincipalBackend $remoteUserPrincipalBackend, |
|
| 37 | - private ICacheFactory $cacheFactory, |
|
| 38 | - private SharingService $service, |
|
| 39 | - // TODO: Make `FederationSharingService` abstract once we support federated address book |
|
| 40 | - // sharing. The abstract sharing backend should not take a service scoped to calendars |
|
| 41 | - // by default. |
|
| 42 | - private FederationSharingService $federationSharingService, |
|
| 43 | - private LoggerInterface $logger, |
|
| 44 | - ) { |
|
| 45 | - $this->shareCache = $this->cacheFactory->createInMemory(); |
|
| 46 | - } |
|
| 47 | - |
|
| 48 | - /** |
|
| 49 | - * @param list<array{href: string, commonName: string, readOnly: bool}> $add |
|
| 50 | - * @param list<string> $remove |
|
| 51 | - */ |
|
| 52 | - public function updateShares(IShareable $shareable, array $add, array $remove, array $oldShares = []): void { |
|
| 53 | - $this->shareCache->clear(); |
|
| 54 | - foreach ($add as $element) { |
|
| 55 | - // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 56 | - $principal = $this->principalBackend->findByUri($element['href'], '') |
|
| 57 | - ?? $this->remoteUserPrincipalBackend->findByUri($element['href'], ''); |
|
| 58 | - if (empty($principal)) { |
|
| 59 | - continue; |
|
| 60 | - } |
|
| 61 | - |
|
| 62 | - // We need to validate manually because some principals are only virtual |
|
| 63 | - // i.e. Group principals |
|
| 64 | - $principalparts = explode('/', $principal, 3); |
|
| 65 | - if (count($principalparts) !== 3 || $principalparts[0] !== 'principals' || !in_array($principalparts[1], ['users', 'groups', 'circles', 'remote-users'], true)) { |
|
| 66 | - // Invalid principal |
|
| 67 | - continue; |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - // Don't add share for owner |
|
| 71 | - if ($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) { |
|
| 72 | - continue; |
|
| 73 | - } |
|
| 74 | - |
|
| 75 | - $principalparts[2] = urldecode($principalparts[2]); |
|
| 76 | - if (($principalparts[1] === 'users' && !$this->userManager->userExists($principalparts[2])) |
|
| 77 | - || ($principalparts[1] === 'groups' && !$this->groupManager->groupExists($principalparts[2]))) { |
|
| 78 | - // User or group does not exist |
|
| 79 | - continue; |
|
| 80 | - } |
|
| 81 | - |
|
| 82 | - $access = Backend::ACCESS_READ; |
|
| 83 | - if (isset($element['readOnly'])) { |
|
| 84 | - $access = $element['readOnly'] ? Backend::ACCESS_READ : Backend::ACCESS_READ_WRITE; |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - if ($principalparts[1] === 'remote-users') { |
|
| 88 | - $this->federationSharingService->shareWith($shareable, $principal, $access); |
|
| 89 | - } else { |
|
| 90 | - $this->service->shareWith($shareable->getResourceId(), $principal, $access); |
|
| 91 | - } |
|
| 92 | - } |
|
| 93 | - foreach ($remove as $element) { |
|
| 94 | - // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 95 | - $principal = $this->principalBackend->findByUri($element, '') |
|
| 96 | - ?? $this->remoteUserPrincipalBackend->findByUri($element, ''); |
|
| 97 | - if (empty($principal)) { |
|
| 98 | - continue; |
|
| 99 | - } |
|
| 100 | - |
|
| 101 | - // Don't add unshare for owner |
|
| 102 | - if ($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) { |
|
| 103 | - continue; |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - // Delete any possible direct shares (since the frontend does not separate between them) |
|
| 107 | - $this->service->deleteShare($shareable->getResourceId(), $principal); |
|
| 108 | - } |
|
| 109 | - } |
|
| 110 | - |
|
| 111 | - public function deleteAllShares(int $resourceId): void { |
|
| 112 | - $this->shareCache->clear(); |
|
| 113 | - $this->service->deleteAllShares($resourceId); |
|
| 114 | - } |
|
| 115 | - |
|
| 116 | - public function deleteAllSharesByUser(string $principaluri): void { |
|
| 117 | - $this->shareCache->clear(); |
|
| 118 | - $this->service->deleteAllSharesByUser($principaluri); |
|
| 119 | - } |
|
| 120 | - |
|
| 121 | - /** |
|
| 122 | - * Returns the list of people whom this resource is shared with. |
|
| 123 | - * |
|
| 124 | - * Every element in this array should have the following properties: |
|
| 125 | - * * href - Often a mailto: address |
|
| 126 | - * * commonName - Optional, for example a first + last name |
|
| 127 | - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. |
|
| 128 | - * * readOnly - boolean |
|
| 129 | - * |
|
| 130 | - * @param int $resourceId |
|
| 131 | - * @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> |
|
| 132 | - */ |
|
| 133 | - public function getShares(int $resourceId): array { |
|
| 134 | - $cached = $this->shareCache->get((string)$resourceId); |
|
| 135 | - if ($cached) { |
|
| 136 | - return $cached; |
|
| 137 | - } |
|
| 138 | - |
|
| 139 | - $rows = $this->service->getShares($resourceId); |
|
| 140 | - $shares = []; |
|
| 141 | - foreach ($rows as $row) { |
|
| 142 | - $p = $this->getPrincipalByPath($row['principaluri']); |
|
| 143 | - $shares[] = [ |
|
| 144 | - 'href' => "principal:{$row['principaluri']}", |
|
| 145 | - 'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '', |
|
| 146 | - 'status' => 1, |
|
| 147 | - 'readOnly' => (int)$row['access'] === Backend::ACCESS_READ, |
|
| 148 | - '{http://owncloud.org/ns}principal' => (string)$row['principaluri'], |
|
| 149 | - '{http://owncloud.org/ns}group-share' => isset($p['uri']) && (str_starts_with($p['uri'], 'principals/groups') || str_starts_with($p['uri'], 'principals/circles')), |
|
| 150 | - ]; |
|
| 151 | - } |
|
| 152 | - $this->shareCache->set((string)$resourceId, $shares); |
|
| 153 | - return $shares; |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - public function preloadShares(array $resourceIds): void { |
|
| 157 | - $resourceIds = array_filter($resourceIds, function (int $resourceId) { |
|
| 158 | - return empty($this->shareCache->get((string)$resourceId)); |
|
| 159 | - }); |
|
| 160 | - if (empty($resourceIds)) { |
|
| 161 | - return; |
|
| 162 | - } |
|
| 163 | - |
|
| 164 | - $rows = $this->service->getSharesForIds($resourceIds); |
|
| 165 | - $sharesByResource = array_fill_keys($resourceIds, []); |
|
| 166 | - foreach ($rows as $row) { |
|
| 167 | - $resourceId = (int)$row['resourceid']; |
|
| 168 | - $p = $this->getPrincipalByPath($row['principaluri']); |
|
| 169 | - $sharesByResource[$resourceId][] = [ |
|
| 170 | - 'href' => "principal:{$row['principaluri']}", |
|
| 171 | - 'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '', |
|
| 172 | - 'status' => 1, |
|
| 173 | - 'readOnly' => (int)$row['access'] === self::ACCESS_READ, |
|
| 174 | - '{http://owncloud.org/ns}principal' => (string)$row['principaluri'], |
|
| 175 | - '{http://owncloud.org/ns}group-share' => isset($p['uri']) && str_starts_with($p['uri'], 'principals/groups') |
|
| 176 | - ]; |
|
| 177 | - $this->shareCache->set((string)$resourceId, $sharesByResource[$resourceId]); |
|
| 178 | - } |
|
| 179 | - } |
|
| 180 | - |
|
| 181 | - /** |
|
| 182 | - * For shared resources the sharee is set in the ACL of the resource |
|
| 183 | - * |
|
| 184 | - * @param int $resourceId |
|
| 185 | - * @param list<array{privilege: string, principal: string, protected: bool}> $acl |
|
| 186 | - * @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 |
|
| 187 | - * @return list<array{principal: string, privilege: string, protected: bool}> |
|
| 188 | - */ |
|
| 189 | - public function applyShareAcl(array $shares, array $acl): array { |
|
| 190 | - foreach ($shares as $share) { |
|
| 191 | - $acl[] = [ |
|
| 192 | - 'privilege' => '{DAV:}read', |
|
| 193 | - 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 194 | - 'protected' => true, |
|
| 195 | - ]; |
|
| 196 | - if (!$share['readOnly']) { |
|
| 197 | - $acl[] = [ |
|
| 198 | - 'privilege' => '{DAV:}write', |
|
| 199 | - 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 200 | - 'protected' => true, |
|
| 201 | - ]; |
|
| 202 | - } elseif (in_array($this->service->getResourceType(), ['calendar','addressbook'])) { |
|
| 203 | - // Allow changing the properties of read only calendars, |
|
| 204 | - // so users can change the visibility. |
|
| 205 | - $acl[] = [ |
|
| 206 | - 'privilege' => '{DAV:}write-properties', |
|
| 207 | - 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 208 | - 'protected' => true, |
|
| 209 | - ]; |
|
| 210 | - } |
|
| 211 | - } |
|
| 212 | - return $acl; |
|
| 213 | - } |
|
| 214 | - |
|
| 215 | - public function unshare(IShareable $shareable, string $principalUri): bool { |
|
| 216 | - $this->shareCache->clear(); |
|
| 217 | - |
|
| 218 | - $principal = $this->principalBackend->findByUri($principalUri, ''); |
|
| 219 | - if (empty($principal)) { |
|
| 220 | - return false; |
|
| 221 | - } |
|
| 222 | - |
|
| 223 | - if ($shareable->getOwner() === $principal) { |
|
| 224 | - return false; |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - // Delete any possible direct shares (since the frontend does not separate between them) |
|
| 228 | - $this->service->deleteShare($shareable->getResourceId(), $principal); |
|
| 229 | - |
|
| 230 | - $needsUnshare = $this->hasAccessByGroupOrCirclesMembership( |
|
| 231 | - $shareable->getResourceId(), |
|
| 232 | - $principal |
|
| 233 | - ); |
|
| 234 | - |
|
| 235 | - if ($needsUnshare) { |
|
| 236 | - $this->service->unshare($shareable->getResourceId(), $principal); |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - return true; |
|
| 240 | - } |
|
| 241 | - |
|
| 242 | - private function hasAccessByGroupOrCirclesMembership(int $resourceId, string $principal) { |
|
| 243 | - $memberships = array_merge( |
|
| 244 | - $this->principalBackend->getGroupMembership($principal, true), |
|
| 245 | - $this->principalBackend->getCircleMembership($principal) |
|
| 246 | - ); |
|
| 247 | - |
|
| 248 | - $shares = array_column( |
|
| 249 | - $this->service->getShares($resourceId), |
|
| 250 | - 'principaluri' |
|
| 251 | - ); |
|
| 252 | - |
|
| 253 | - return count(array_intersect($memberships, $shares)) > 0; |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - public function getSharesByShareePrincipal(string $principal): array { |
|
| 257 | - return $this->service->getSharesByPrincipals([$principal]); |
|
| 258 | - } |
|
| 259 | - |
|
| 260 | - private function getPrincipalByPath(string $principalUri): ?array { |
|
| 261 | - // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 262 | - if (str_starts_with($principalUri, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX)) { |
|
| 263 | - return $this->remoteUserPrincipalBackend->getPrincipalByPath($principalUri); |
|
| 264 | - } |
|
| 265 | - |
|
| 266 | - return $this->principalBackend->getPrincipalByPath($principalUri); |
|
| 267 | - } |
|
| 22 | + use TTransactional; |
|
| 23 | + public const ACCESS_OWNER = 1; |
|
| 24 | + |
|
| 25 | + public const ACCESS_READ_WRITE = 2; |
|
| 26 | + public const ACCESS_READ = 3; |
|
| 27 | + // 4 is already in use for public calendars |
|
| 28 | + public const ACCESS_UNSHARED = 5; |
|
| 29 | + |
|
| 30 | + private ICache $shareCache; |
|
| 31 | + |
|
| 32 | + public function __construct( |
|
| 33 | + private IUserManager $userManager, |
|
| 34 | + private IGroupManager $groupManager, |
|
| 35 | + private Principal $principalBackend, |
|
| 36 | + private RemoteUserPrincipalBackend $remoteUserPrincipalBackend, |
|
| 37 | + private ICacheFactory $cacheFactory, |
|
| 38 | + private SharingService $service, |
|
| 39 | + // TODO: Make `FederationSharingService` abstract once we support federated address book |
|
| 40 | + // sharing. The abstract sharing backend should not take a service scoped to calendars |
|
| 41 | + // by default. |
|
| 42 | + private FederationSharingService $federationSharingService, |
|
| 43 | + private LoggerInterface $logger, |
|
| 44 | + ) { |
|
| 45 | + $this->shareCache = $this->cacheFactory->createInMemory(); |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | + /** |
|
| 49 | + * @param list<array{href: string, commonName: string, readOnly: bool}> $add |
|
| 50 | + * @param list<string> $remove |
|
| 51 | + */ |
|
| 52 | + public function updateShares(IShareable $shareable, array $add, array $remove, array $oldShares = []): void { |
|
| 53 | + $this->shareCache->clear(); |
|
| 54 | + foreach ($add as $element) { |
|
| 55 | + // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 56 | + $principal = $this->principalBackend->findByUri($element['href'], '') |
|
| 57 | + ?? $this->remoteUserPrincipalBackend->findByUri($element['href'], ''); |
|
| 58 | + if (empty($principal)) { |
|
| 59 | + continue; |
|
| 60 | + } |
|
| 61 | + |
|
| 62 | + // We need to validate manually because some principals are only virtual |
|
| 63 | + // i.e. Group principals |
|
| 64 | + $principalparts = explode('/', $principal, 3); |
|
| 65 | + if (count($principalparts) !== 3 || $principalparts[0] !== 'principals' || !in_array($principalparts[1], ['users', 'groups', 'circles', 'remote-users'], true)) { |
|
| 66 | + // Invalid principal |
|
| 67 | + continue; |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + // Don't add share for owner |
|
| 71 | + if ($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) { |
|
| 72 | + continue; |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + $principalparts[2] = urldecode($principalparts[2]); |
|
| 76 | + if (($principalparts[1] === 'users' && !$this->userManager->userExists($principalparts[2])) |
|
| 77 | + || ($principalparts[1] === 'groups' && !$this->groupManager->groupExists($principalparts[2]))) { |
|
| 78 | + // User or group does not exist |
|
| 79 | + continue; |
|
| 80 | + } |
|
| 81 | + |
|
| 82 | + $access = Backend::ACCESS_READ; |
|
| 83 | + if (isset($element['readOnly'])) { |
|
| 84 | + $access = $element['readOnly'] ? Backend::ACCESS_READ : Backend::ACCESS_READ_WRITE; |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + if ($principalparts[1] === 'remote-users') { |
|
| 88 | + $this->federationSharingService->shareWith($shareable, $principal, $access); |
|
| 89 | + } else { |
|
| 90 | + $this->service->shareWith($shareable->getResourceId(), $principal, $access); |
|
| 91 | + } |
|
| 92 | + } |
|
| 93 | + foreach ($remove as $element) { |
|
| 94 | + // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 95 | + $principal = $this->principalBackend->findByUri($element, '') |
|
| 96 | + ?? $this->remoteUserPrincipalBackend->findByUri($element, ''); |
|
| 97 | + if (empty($principal)) { |
|
| 98 | + continue; |
|
| 99 | + } |
|
| 100 | + |
|
| 101 | + // Don't add unshare for owner |
|
| 102 | + if ($shareable->getOwner() !== null && strcasecmp($shareable->getOwner(), $principal) === 0) { |
|
| 103 | + continue; |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + // Delete any possible direct shares (since the frontend does not separate between them) |
|
| 107 | + $this->service->deleteShare($shareable->getResourceId(), $principal); |
|
| 108 | + } |
|
| 109 | + } |
|
| 110 | + |
|
| 111 | + public function deleteAllShares(int $resourceId): void { |
|
| 112 | + $this->shareCache->clear(); |
|
| 113 | + $this->service->deleteAllShares($resourceId); |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + public function deleteAllSharesByUser(string $principaluri): void { |
|
| 117 | + $this->shareCache->clear(); |
|
| 118 | + $this->service->deleteAllSharesByUser($principaluri); |
|
| 119 | + } |
|
| 120 | + |
|
| 121 | + /** |
|
| 122 | + * Returns the list of people whom this resource is shared with. |
|
| 123 | + * |
|
| 124 | + * Every element in this array should have the following properties: |
|
| 125 | + * * href - Often a mailto: address |
|
| 126 | + * * commonName - Optional, for example a first + last name |
|
| 127 | + * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. |
|
| 128 | + * * readOnly - boolean |
|
| 129 | + * |
|
| 130 | + * @param int $resourceId |
|
| 131 | + * @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}> |
|
| 132 | + */ |
|
| 133 | + public function getShares(int $resourceId): array { |
|
| 134 | + $cached = $this->shareCache->get((string)$resourceId); |
|
| 135 | + if ($cached) { |
|
| 136 | + return $cached; |
|
| 137 | + } |
|
| 138 | + |
|
| 139 | + $rows = $this->service->getShares($resourceId); |
|
| 140 | + $shares = []; |
|
| 141 | + foreach ($rows as $row) { |
|
| 142 | + $p = $this->getPrincipalByPath($row['principaluri']); |
|
| 143 | + $shares[] = [ |
|
| 144 | + 'href' => "principal:{$row['principaluri']}", |
|
| 145 | + 'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '', |
|
| 146 | + 'status' => 1, |
|
| 147 | + 'readOnly' => (int)$row['access'] === Backend::ACCESS_READ, |
|
| 148 | + '{http://owncloud.org/ns}principal' => (string)$row['principaluri'], |
|
| 149 | + '{http://owncloud.org/ns}group-share' => isset($p['uri']) && (str_starts_with($p['uri'], 'principals/groups') || str_starts_with($p['uri'], 'principals/circles')), |
|
| 150 | + ]; |
|
| 151 | + } |
|
| 152 | + $this->shareCache->set((string)$resourceId, $shares); |
|
| 153 | + return $shares; |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + public function preloadShares(array $resourceIds): void { |
|
| 157 | + $resourceIds = array_filter($resourceIds, function (int $resourceId) { |
|
| 158 | + return empty($this->shareCache->get((string)$resourceId)); |
|
| 159 | + }); |
|
| 160 | + if (empty($resourceIds)) { |
|
| 161 | + return; |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + $rows = $this->service->getSharesForIds($resourceIds); |
|
| 165 | + $sharesByResource = array_fill_keys($resourceIds, []); |
|
| 166 | + foreach ($rows as $row) { |
|
| 167 | + $resourceId = (int)$row['resourceid']; |
|
| 168 | + $p = $this->getPrincipalByPath($row['principaluri']); |
|
| 169 | + $sharesByResource[$resourceId][] = [ |
|
| 170 | + 'href' => "principal:{$row['principaluri']}", |
|
| 171 | + 'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '', |
|
| 172 | + 'status' => 1, |
|
| 173 | + 'readOnly' => (int)$row['access'] === self::ACCESS_READ, |
|
| 174 | + '{http://owncloud.org/ns}principal' => (string)$row['principaluri'], |
|
| 175 | + '{http://owncloud.org/ns}group-share' => isset($p['uri']) && str_starts_with($p['uri'], 'principals/groups') |
|
| 176 | + ]; |
|
| 177 | + $this->shareCache->set((string)$resourceId, $sharesByResource[$resourceId]); |
|
| 178 | + } |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + /** |
|
| 182 | + * For shared resources the sharee is set in the ACL of the resource |
|
| 183 | + * |
|
| 184 | + * @param int $resourceId |
|
| 185 | + * @param list<array{privilege: string, principal: string, protected: bool}> $acl |
|
| 186 | + * @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 |
|
| 187 | + * @return list<array{principal: string, privilege: string, protected: bool}> |
|
| 188 | + */ |
|
| 189 | + public function applyShareAcl(array $shares, array $acl): array { |
|
| 190 | + foreach ($shares as $share) { |
|
| 191 | + $acl[] = [ |
|
| 192 | + 'privilege' => '{DAV:}read', |
|
| 193 | + 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 194 | + 'protected' => true, |
|
| 195 | + ]; |
|
| 196 | + if (!$share['readOnly']) { |
|
| 197 | + $acl[] = [ |
|
| 198 | + 'privilege' => '{DAV:}write', |
|
| 199 | + 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 200 | + 'protected' => true, |
|
| 201 | + ]; |
|
| 202 | + } elseif (in_array($this->service->getResourceType(), ['calendar','addressbook'])) { |
|
| 203 | + // Allow changing the properties of read only calendars, |
|
| 204 | + // so users can change the visibility. |
|
| 205 | + $acl[] = [ |
|
| 206 | + 'privilege' => '{DAV:}write-properties', |
|
| 207 | + 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'], |
|
| 208 | + 'protected' => true, |
|
| 209 | + ]; |
|
| 210 | + } |
|
| 211 | + } |
|
| 212 | + return $acl; |
|
| 213 | + } |
|
| 214 | + |
|
| 215 | + public function unshare(IShareable $shareable, string $principalUri): bool { |
|
| 216 | + $this->shareCache->clear(); |
|
| 217 | + |
|
| 218 | + $principal = $this->principalBackend->findByUri($principalUri, ''); |
|
| 219 | + if (empty($principal)) { |
|
| 220 | + return false; |
|
| 221 | + } |
|
| 222 | + |
|
| 223 | + if ($shareable->getOwner() === $principal) { |
|
| 224 | + return false; |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + // Delete any possible direct shares (since the frontend does not separate between them) |
|
| 228 | + $this->service->deleteShare($shareable->getResourceId(), $principal); |
|
| 229 | + |
|
| 230 | + $needsUnshare = $this->hasAccessByGroupOrCirclesMembership( |
|
| 231 | + $shareable->getResourceId(), |
|
| 232 | + $principal |
|
| 233 | + ); |
|
| 234 | + |
|
| 235 | + if ($needsUnshare) { |
|
| 236 | + $this->service->unshare($shareable->getResourceId(), $principal); |
|
| 237 | + } |
|
| 238 | + |
|
| 239 | + return true; |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + private function hasAccessByGroupOrCirclesMembership(int $resourceId, string $principal) { |
|
| 243 | + $memberships = array_merge( |
|
| 244 | + $this->principalBackend->getGroupMembership($principal, true), |
|
| 245 | + $this->principalBackend->getCircleMembership($principal) |
|
| 246 | + ); |
|
| 247 | + |
|
| 248 | + $shares = array_column( |
|
| 249 | + $this->service->getShares($resourceId), |
|
| 250 | + 'principaluri' |
|
| 251 | + ); |
|
| 252 | + |
|
| 253 | + return count(array_intersect($memberships, $shares)) > 0; |
|
| 254 | + } |
|
| 255 | + |
|
| 256 | + public function getSharesByShareePrincipal(string $principal): array { |
|
| 257 | + return $this->service->getSharesByPrincipals([$principal]); |
|
| 258 | + } |
|
| 259 | + |
|
| 260 | + private function getPrincipalByPath(string $principalUri): ?array { |
|
| 261 | + // Hacky code below ... shouldn't we check the whole (principal) root collection instead? |
|
| 262 | + if (str_starts_with($principalUri, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX)) { |
|
| 263 | + return $this->remoteUserPrincipalBackend->getPrincipalByPath($principalUri); |
|
| 264 | + } |
|
| 265 | + |
|
| 266 | + return $this->principalBackend->getPrincipalByPath($principalUri); |
|
| 267 | + } |
|
| 268 | 268 | } |
@@ -18,16 +18,16 @@ |
||
| 18 | 18 | use Psr\Log\LoggerInterface; |
| 19 | 19 | |
| 20 | 20 | class Backend extends SharingBackend { |
| 21 | - public function __construct( |
|
| 22 | - private IUserManager $userManager, |
|
| 23 | - private IGroupManager $groupManager, |
|
| 24 | - private Principal $principalBackend, |
|
| 25 | - private RemoteUserPrincipalBackend $remoteUserPrincipalBackend, |
|
| 26 | - private ICacheFactory $cacheFactory, |
|
| 27 | - private Service $service, |
|
| 28 | - private FederationSharingService $federationSharingService, |
|
| 29 | - private LoggerInterface $logger, |
|
| 30 | - ) { |
|
| 31 | - parent::__construct($this->userManager, $this->groupManager, $this->principalBackend, $this->remoteUserPrincipalBackend, $this->cacheFactory, $this->service, $this->federationSharingService, $this->logger); |
|
| 32 | - } |
|
| 21 | + public function __construct( |
|
| 22 | + private IUserManager $userManager, |
|
| 23 | + private IGroupManager $groupManager, |
|
| 24 | + private Principal $principalBackend, |
|
| 25 | + private RemoteUserPrincipalBackend $remoteUserPrincipalBackend, |
|
| 26 | + private ICacheFactory $cacheFactory, |
|
| 27 | + private Service $service, |
|
| 28 | + private FederationSharingService $federationSharingService, |
|
| 29 | + private LoggerInterface $logger, |
|
| 30 | + ) { |
|
| 31 | + parent::__construct($this->userManager, $this->groupManager, $this->principalBackend, $this->remoteUserPrincipalBackend, $this->cacheFactory, $this->service, $this->federationSharingService, $this->logger); |
|
| 32 | + } |
|
| 33 | 33 | } |
@@ -24,194 +24,194 @@ |
||
| 24 | 24 | |
| 25 | 25 | class SyncService extends ASyncService { |
| 26 | 26 | |
| 27 | - use TTransactional; |
|
| 28 | - private ?array $localSystemAddressBook = null; |
|
| 29 | - protected string $certPath; |
|
| 30 | - |
|
| 31 | - public function __construct( |
|
| 32 | - IClientService $clientService, |
|
| 33 | - IConfig $config, |
|
| 34 | - private CardDavBackend $backend, |
|
| 35 | - private IUserManager $userManager, |
|
| 36 | - private IDBConnection $dbConnection, |
|
| 37 | - private LoggerInterface $logger, |
|
| 38 | - private Converter $converter, |
|
| 39 | - ) { |
|
| 40 | - parent::__construct($clientService, $config); |
|
| 41 | - |
|
| 42 | - $this->certPath = ''; |
|
| 43 | - } |
|
| 44 | - |
|
| 45 | - /** |
|
| 46 | - * @psalm-return list{0: ?string, 1: boolean} |
|
| 47 | - * @throws \Exception |
|
| 48 | - */ |
|
| 49 | - public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): array { |
|
| 50 | - // 1. create addressbook |
|
| 51 | - $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties); |
|
| 52 | - $addressBookId = $book['id']; |
|
| 53 | - |
|
| 54 | - // 2. query changes |
|
| 55 | - try { |
|
| 56 | - $absoluteUri = $this->prepareUri($url, $addressBookUrl); |
|
| 57 | - $response = $this->requestSyncReport($absoluteUri, $userName, $sharedSecret, $syncToken); |
|
| 58 | - } catch (ClientExceptionInterface $ex) { |
|
| 59 | - if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) { |
|
| 60 | - // remote server revoked access to the address book, remove it |
|
| 61 | - $this->backend->deleteAddressBook($addressBookId); |
|
| 62 | - $this->logger->error('Authorization failed, remove address book: ' . $url, ['app' => 'dav']); |
|
| 63 | - throw $ex; |
|
| 64 | - } |
|
| 65 | - $this->logger->error('Client exception:', ['app' => 'dav', 'exception' => $ex]); |
|
| 66 | - throw $ex; |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - // 3. apply changes |
|
| 70 | - // TODO: use multi-get for download |
|
| 71 | - foreach ($response['response'] as $resource => $status) { |
|
| 72 | - $cardUri = basename($resource); |
|
| 73 | - if (isset($status[200])) { |
|
| 74 | - $absoluteUrl = $this->prepareUri($url, $resource); |
|
| 75 | - $vCard = $this->download($absoluteUrl, $userName, $sharedSecret); |
|
| 76 | - $this->atomic(function () use ($addressBookId, $cardUri, $vCard): void { |
|
| 77 | - $existingCard = $this->backend->getCard($addressBookId, $cardUri); |
|
| 78 | - if ($existingCard === false) { |
|
| 79 | - $this->backend->createCard($addressBookId, $cardUri, $vCard); |
|
| 80 | - } else { |
|
| 81 | - $this->backend->updateCard($addressBookId, $cardUri, $vCard); |
|
| 82 | - } |
|
| 83 | - }, $this->dbConnection); |
|
| 84 | - } else { |
|
| 85 | - $this->backend->deleteCard($addressBookId, $cardUri); |
|
| 86 | - } |
|
| 87 | - } |
|
| 88 | - |
|
| 89 | - return [ |
|
| 90 | - $response['token'], |
|
| 91 | - $response['truncated'], |
|
| 92 | - ]; |
|
| 93 | - } |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * @throws \Sabre\DAV\Exception\BadRequest |
|
| 97 | - */ |
|
| 98 | - public function ensureSystemAddressBookExists(string $principal, string $uri, array $properties): ?array { |
|
| 99 | - try { |
|
| 100 | - return $this->atomic(function () use ($principal, $uri, $properties) { |
|
| 101 | - $book = $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 102 | - if (!is_null($book)) { |
|
| 103 | - return $book; |
|
| 104 | - } |
|
| 105 | - $this->backend->createAddressBook($principal, $uri, $properties); |
|
| 106 | - |
|
| 107 | - return $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 108 | - }, $this->dbConnection); |
|
| 109 | - } catch (Exception $e) { |
|
| 110 | - // READ COMMITTED doesn't prevent a nonrepeatable read above, so |
|
| 111 | - // two processes might create an address book here. Ignore our |
|
| 112 | - // failure and continue loading the entry written by the other process |
|
| 113 | - if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { |
|
| 114 | - throw $e; |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - // If this fails we might have hit a replication node that does not |
|
| 118 | - // have the row written in the other process. |
|
| 119 | - // TODO: find an elegant way to handle this |
|
| 120 | - $ab = $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 121 | - if ($ab === null) { |
|
| 122 | - throw new Exception('Could not create system address book', $e->getCode(), $e); |
|
| 123 | - } |
|
| 124 | - return $ab; |
|
| 125 | - } |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - public function ensureLocalSystemAddressBookExists(): ?array { |
|
| 129 | - return $this->ensureSystemAddressBookExists('principals/system/system', 'system', [ |
|
| 130 | - '{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance' |
|
| 131 | - ]); |
|
| 132 | - } |
|
| 133 | - |
|
| 134 | - /** |
|
| 135 | - * @param IUser $user |
|
| 136 | - */ |
|
| 137 | - public function updateUser(IUser $user): void { |
|
| 138 | - $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 139 | - $addressBookId = $systemAddressBook['id']; |
|
| 140 | - |
|
| 141 | - $cardId = self::getCardUri($user); |
|
| 142 | - if ($user->isEnabled()) { |
|
| 143 | - $this->atomic(function () use ($addressBookId, $cardId, $user): void { |
|
| 144 | - $card = $this->backend->getCard($addressBookId, $cardId); |
|
| 145 | - if ($card === false) { |
|
| 146 | - $vCard = $this->converter->createCardFromUser($user); |
|
| 147 | - if ($vCard !== null) { |
|
| 148 | - $this->backend->createCard($addressBookId, $cardId, $vCard->serialize(), false); |
|
| 149 | - } |
|
| 150 | - } else { |
|
| 151 | - $vCard = $this->converter->createCardFromUser($user); |
|
| 152 | - if (is_null($vCard)) { |
|
| 153 | - $this->backend->deleteCard($addressBookId, $cardId); |
|
| 154 | - } else { |
|
| 155 | - $this->backend->updateCard($addressBookId, $cardId, $vCard->serialize()); |
|
| 156 | - } |
|
| 157 | - } |
|
| 158 | - }, $this->dbConnection); |
|
| 159 | - } else { |
|
| 160 | - $this->backend->deleteCard($addressBookId, $cardId); |
|
| 161 | - } |
|
| 162 | - } |
|
| 163 | - |
|
| 164 | - /** |
|
| 165 | - * @param IUser|string $userOrCardId |
|
| 166 | - */ |
|
| 167 | - public function deleteUser($userOrCardId) { |
|
| 168 | - $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 169 | - if ($userOrCardId instanceof IUser) { |
|
| 170 | - $userOrCardId = self::getCardUri($userOrCardId); |
|
| 171 | - } |
|
| 172 | - $this->backend->deleteCard($systemAddressBook['id'], $userOrCardId); |
|
| 173 | - } |
|
| 174 | - |
|
| 175 | - /** |
|
| 176 | - * @return array|null |
|
| 177 | - */ |
|
| 178 | - public function getLocalSystemAddressBook() { |
|
| 179 | - if (is_null($this->localSystemAddressBook)) { |
|
| 180 | - $this->localSystemAddressBook = $this->ensureLocalSystemAddressBookExists(); |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - return $this->localSystemAddressBook; |
|
| 184 | - } |
|
| 185 | - |
|
| 186 | - /** |
|
| 187 | - * @return void |
|
| 188 | - */ |
|
| 189 | - public function syncInstance(?\Closure $progressCallback = null) { |
|
| 190 | - $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 191 | - $this->userManager->callForAllUsers(function ($user) use ($systemAddressBook, $progressCallback): void { |
|
| 192 | - $this->updateUser($user); |
|
| 193 | - if (!is_null($progressCallback)) { |
|
| 194 | - $progressCallback(); |
|
| 195 | - } |
|
| 196 | - }); |
|
| 197 | - |
|
| 198 | - // remove no longer existing |
|
| 199 | - $allCards = $this->backend->getCards($systemAddressBook['id']); |
|
| 200 | - foreach ($allCards as $card) { |
|
| 201 | - $vCard = Reader::read($card['carddata']); |
|
| 202 | - $uid = $vCard->UID->getValue(); |
|
| 203 | - // load backend and see if user exists |
|
| 204 | - if (!$this->userManager->userExists($uid)) { |
|
| 205 | - $this->deleteUser($card['uri']); |
|
| 206 | - } |
|
| 207 | - } |
|
| 208 | - } |
|
| 209 | - |
|
| 210 | - /** |
|
| 211 | - * @param IUser $user |
|
| 212 | - * @return string |
|
| 213 | - */ |
|
| 214 | - public static function getCardUri(IUser $user): string { |
|
| 215 | - return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf'; |
|
| 216 | - } |
|
| 27 | + use TTransactional; |
|
| 28 | + private ?array $localSystemAddressBook = null; |
|
| 29 | + protected string $certPath; |
|
| 30 | + |
|
| 31 | + public function __construct( |
|
| 32 | + IClientService $clientService, |
|
| 33 | + IConfig $config, |
|
| 34 | + private CardDavBackend $backend, |
|
| 35 | + private IUserManager $userManager, |
|
| 36 | + private IDBConnection $dbConnection, |
|
| 37 | + private LoggerInterface $logger, |
|
| 38 | + private Converter $converter, |
|
| 39 | + ) { |
|
| 40 | + parent::__construct($clientService, $config); |
|
| 41 | + |
|
| 42 | + $this->certPath = ''; |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + /** |
|
| 46 | + * @psalm-return list{0: ?string, 1: boolean} |
|
| 47 | + * @throws \Exception |
|
| 48 | + */ |
|
| 49 | + public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): array { |
|
| 50 | + // 1. create addressbook |
|
| 51 | + $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties); |
|
| 52 | + $addressBookId = $book['id']; |
|
| 53 | + |
|
| 54 | + // 2. query changes |
|
| 55 | + try { |
|
| 56 | + $absoluteUri = $this->prepareUri($url, $addressBookUrl); |
|
| 57 | + $response = $this->requestSyncReport($absoluteUri, $userName, $sharedSecret, $syncToken); |
|
| 58 | + } catch (ClientExceptionInterface $ex) { |
|
| 59 | + if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) { |
|
| 60 | + // remote server revoked access to the address book, remove it |
|
| 61 | + $this->backend->deleteAddressBook($addressBookId); |
|
| 62 | + $this->logger->error('Authorization failed, remove address book: ' . $url, ['app' => 'dav']); |
|
| 63 | + throw $ex; |
|
| 64 | + } |
|
| 65 | + $this->logger->error('Client exception:', ['app' => 'dav', 'exception' => $ex]); |
|
| 66 | + throw $ex; |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + // 3. apply changes |
|
| 70 | + // TODO: use multi-get for download |
|
| 71 | + foreach ($response['response'] as $resource => $status) { |
|
| 72 | + $cardUri = basename($resource); |
|
| 73 | + if (isset($status[200])) { |
|
| 74 | + $absoluteUrl = $this->prepareUri($url, $resource); |
|
| 75 | + $vCard = $this->download($absoluteUrl, $userName, $sharedSecret); |
|
| 76 | + $this->atomic(function () use ($addressBookId, $cardUri, $vCard): void { |
|
| 77 | + $existingCard = $this->backend->getCard($addressBookId, $cardUri); |
|
| 78 | + if ($existingCard === false) { |
|
| 79 | + $this->backend->createCard($addressBookId, $cardUri, $vCard); |
|
| 80 | + } else { |
|
| 81 | + $this->backend->updateCard($addressBookId, $cardUri, $vCard); |
|
| 82 | + } |
|
| 83 | + }, $this->dbConnection); |
|
| 84 | + } else { |
|
| 85 | + $this->backend->deleteCard($addressBookId, $cardUri); |
|
| 86 | + } |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + return [ |
|
| 90 | + $response['token'], |
|
| 91 | + $response['truncated'], |
|
| 92 | + ]; |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * @throws \Sabre\DAV\Exception\BadRequest |
|
| 97 | + */ |
|
| 98 | + public function ensureSystemAddressBookExists(string $principal, string $uri, array $properties): ?array { |
|
| 99 | + try { |
|
| 100 | + return $this->atomic(function () use ($principal, $uri, $properties) { |
|
| 101 | + $book = $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 102 | + if (!is_null($book)) { |
|
| 103 | + return $book; |
|
| 104 | + } |
|
| 105 | + $this->backend->createAddressBook($principal, $uri, $properties); |
|
| 106 | + |
|
| 107 | + return $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 108 | + }, $this->dbConnection); |
|
| 109 | + } catch (Exception $e) { |
|
| 110 | + // READ COMMITTED doesn't prevent a nonrepeatable read above, so |
|
| 111 | + // two processes might create an address book here. Ignore our |
|
| 112 | + // failure and continue loading the entry written by the other process |
|
| 113 | + if ($e->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { |
|
| 114 | + throw $e; |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + // If this fails we might have hit a replication node that does not |
|
| 118 | + // have the row written in the other process. |
|
| 119 | + // TODO: find an elegant way to handle this |
|
| 120 | + $ab = $this->backend->getAddressBooksByUri($principal, $uri); |
|
| 121 | + if ($ab === null) { |
|
| 122 | + throw new Exception('Could not create system address book', $e->getCode(), $e); |
|
| 123 | + } |
|
| 124 | + return $ab; |
|
| 125 | + } |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + public function ensureLocalSystemAddressBookExists(): ?array { |
|
| 129 | + return $this->ensureSystemAddressBookExists('principals/system/system', 'system', [ |
|
| 130 | + '{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance' |
|
| 131 | + ]); |
|
| 132 | + } |
|
| 133 | + |
|
| 134 | + /** |
|
| 135 | + * @param IUser $user |
|
| 136 | + */ |
|
| 137 | + public function updateUser(IUser $user): void { |
|
| 138 | + $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 139 | + $addressBookId = $systemAddressBook['id']; |
|
| 140 | + |
|
| 141 | + $cardId = self::getCardUri($user); |
|
| 142 | + if ($user->isEnabled()) { |
|
| 143 | + $this->atomic(function () use ($addressBookId, $cardId, $user): void { |
|
| 144 | + $card = $this->backend->getCard($addressBookId, $cardId); |
|
| 145 | + if ($card === false) { |
|
| 146 | + $vCard = $this->converter->createCardFromUser($user); |
|
| 147 | + if ($vCard !== null) { |
|
| 148 | + $this->backend->createCard($addressBookId, $cardId, $vCard->serialize(), false); |
|
| 149 | + } |
|
| 150 | + } else { |
|
| 151 | + $vCard = $this->converter->createCardFromUser($user); |
|
| 152 | + if (is_null($vCard)) { |
|
| 153 | + $this->backend->deleteCard($addressBookId, $cardId); |
|
| 154 | + } else { |
|
| 155 | + $this->backend->updateCard($addressBookId, $cardId, $vCard->serialize()); |
|
| 156 | + } |
|
| 157 | + } |
|
| 158 | + }, $this->dbConnection); |
|
| 159 | + } else { |
|
| 160 | + $this->backend->deleteCard($addressBookId, $cardId); |
|
| 161 | + } |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + /** |
|
| 165 | + * @param IUser|string $userOrCardId |
|
| 166 | + */ |
|
| 167 | + public function deleteUser($userOrCardId) { |
|
| 168 | + $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 169 | + if ($userOrCardId instanceof IUser) { |
|
| 170 | + $userOrCardId = self::getCardUri($userOrCardId); |
|
| 171 | + } |
|
| 172 | + $this->backend->deleteCard($systemAddressBook['id'], $userOrCardId); |
|
| 173 | + } |
|
| 174 | + |
|
| 175 | + /** |
|
| 176 | + * @return array|null |
|
| 177 | + */ |
|
| 178 | + public function getLocalSystemAddressBook() { |
|
| 179 | + if (is_null($this->localSystemAddressBook)) { |
|
| 180 | + $this->localSystemAddressBook = $this->ensureLocalSystemAddressBookExists(); |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + return $this->localSystemAddressBook; |
|
| 184 | + } |
|
| 185 | + |
|
| 186 | + /** |
|
| 187 | + * @return void |
|
| 188 | + */ |
|
| 189 | + public function syncInstance(?\Closure $progressCallback = null) { |
|
| 190 | + $systemAddressBook = $this->getLocalSystemAddressBook(); |
|
| 191 | + $this->userManager->callForAllUsers(function ($user) use ($systemAddressBook, $progressCallback): void { |
|
| 192 | + $this->updateUser($user); |
|
| 193 | + if (!is_null($progressCallback)) { |
|
| 194 | + $progressCallback(); |
|
| 195 | + } |
|
| 196 | + }); |
|
| 197 | + |
|
| 198 | + // remove no longer existing |
|
| 199 | + $allCards = $this->backend->getCards($systemAddressBook['id']); |
|
| 200 | + foreach ($allCards as $card) { |
|
| 201 | + $vCard = Reader::read($card['carddata']); |
|
| 202 | + $uid = $vCard->UID->getValue(); |
|
| 203 | + // load backend and see if user exists |
|
| 204 | + if (!$this->userManager->userExists($uid)) { |
|
| 205 | + $this->deleteUser($card['uri']); |
|
| 206 | + } |
|
| 207 | + } |
|
| 208 | + } |
|
| 209 | + |
|
| 210 | + /** |
|
| 211 | + * @param IUser $user |
|
| 212 | + * @return string |
|
| 213 | + */ |
|
| 214 | + public static function getCardUri(IUser $user): string { |
|
| 215 | + return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf'; |
|
| 216 | + } |
|
| 217 | 217 | } |
@@ -49,170 +49,170 @@ |
||
| 49 | 49 | use Sabre\DAV\SimpleCollection; |
| 50 | 50 | |
| 51 | 51 | class RootCollection extends SimpleCollection { |
| 52 | - public function __construct() { |
|
| 53 | - $l10n = \OC::$server->getL10N('dav'); |
|
| 54 | - $random = Server::get(ISecureRandom::class); |
|
| 55 | - $logger = Server::get(LoggerInterface::class); |
|
| 56 | - $userManager = Server::get(IUserManager::class); |
|
| 57 | - $userSession = Server::get(IUserSession::class); |
|
| 58 | - $groupManager = Server::get(IGroupManager::class); |
|
| 59 | - $shareManager = Server::get(\OCP\Share\IManager::class); |
|
| 60 | - $db = Server::get(IDBConnection::class); |
|
| 61 | - $dispatcher = Server::get(IEventDispatcher::class); |
|
| 62 | - $config = Server::get(IConfig::class); |
|
| 63 | - $proxyMapper = Server::get(ProxyMapper::class); |
|
| 64 | - $rootFolder = Server::get(IRootFolder::class); |
|
| 65 | - $federatedCalendarFactory = Server::get(FederatedCalendarFactory::class); |
|
| 66 | - |
|
| 67 | - $userPrincipalBackend = new Principal( |
|
| 68 | - $userManager, |
|
| 69 | - $groupManager, |
|
| 70 | - Server::get(IAccountManager::class), |
|
| 71 | - $shareManager, |
|
| 72 | - Server::get(IUserSession::class), |
|
| 73 | - Server::get(IAppManager::class), |
|
| 74 | - $proxyMapper, |
|
| 75 | - Server::get(KnownUserService::class), |
|
| 76 | - Server::get(IConfig::class), |
|
| 77 | - \OC::$server->getL10NFactory() |
|
| 78 | - ); |
|
| 79 | - |
|
| 80 | - $groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config); |
|
| 81 | - $calendarResourcePrincipalBackend = new ResourcePrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper); |
|
| 82 | - $calendarRoomPrincipalBackend = new RoomPrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper); |
|
| 83 | - $remoteUserPrincipalBackend = Server::get(RemoteUserPrincipalBackend::class); |
|
| 84 | - // as soon as debug mode is enabled we allow listing of principals |
|
| 85 | - $disableListing = !$config->getSystemValue('debug', false); |
|
| 86 | - |
|
| 87 | - // setup the first level of the dav tree |
|
| 88 | - $userPrincipals = new Collection($userPrincipalBackend, 'principals/users'); |
|
| 89 | - $userPrincipals->disableListing = $disableListing; |
|
| 90 | - $groupPrincipals = new Collection($groupPrincipalBackend, 'principals/groups'); |
|
| 91 | - $groupPrincipals->disableListing = $disableListing; |
|
| 92 | - $systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system'); |
|
| 93 | - $systemPrincipals->disableListing = $disableListing; |
|
| 94 | - $calendarResourcePrincipals = new Collection($calendarResourcePrincipalBackend, 'principals/calendar-resources'); |
|
| 95 | - $calendarRoomPrincipals = new Collection($calendarRoomPrincipalBackend, 'principals/calendar-rooms'); |
|
| 96 | - $remoteUserPrincipals = new Collection($remoteUserPrincipalBackend, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX); |
|
| 97 | - $calendarSharingBackend = Server::get(Backend::class); |
|
| 98 | - |
|
| 99 | - $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); |
|
| 100 | - $filesCollection->disableListing = $disableListing; |
|
| 101 | - $caldavBackend = new CalDavBackend( |
|
| 102 | - $db, |
|
| 103 | - $userPrincipalBackend, |
|
| 104 | - $userManager, |
|
| 105 | - $random, |
|
| 106 | - $logger, |
|
| 107 | - $dispatcher, |
|
| 108 | - $config, |
|
| 109 | - $calendarSharingBackend, |
|
| 110 | - Server::get(FederatedCalendarMapper::class), |
|
| 111 | - false, |
|
| 112 | - ); |
|
| 113 | - $userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 114 | - $userCalendarRoot->disableListing = $disableListing; |
|
| 115 | - |
|
| 116 | - $remoteUserCalendarRoot = new CalendarRoot($remoteUserPrincipalBackend, $caldavBackend, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX, $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 117 | - $remoteUserCalendarRoot->disableListing = $disableListing; |
|
| 118 | - |
|
| 119 | - $resourceCalendarRoot = new CalendarRoot($calendarResourcePrincipalBackend, $caldavBackend, 'principals/calendar-resources', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 120 | - $resourceCalendarRoot->disableListing = $disableListing; |
|
| 121 | - $roomCalendarRoot = new CalendarRoot($calendarRoomPrincipalBackend, $caldavBackend, 'principals/calendar-rooms', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 122 | - $roomCalendarRoot->disableListing = $disableListing; |
|
| 123 | - |
|
| 124 | - $publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger); |
|
| 125 | - |
|
| 126 | - $systemTagCollection = Server::get(SystemTagsByIdCollection::class); |
|
| 127 | - $systemTagRelationsCollection = new SystemTagsRelationsCollection( |
|
| 128 | - Server::get(ISystemTagManager::class), |
|
| 129 | - Server::get(ISystemTagObjectMapper::class), |
|
| 130 | - Server::get(IUserSession::class), |
|
| 131 | - $groupManager, |
|
| 132 | - $dispatcher, |
|
| 133 | - $rootFolder, |
|
| 134 | - ); |
|
| 135 | - $systemTagInUseCollection = Server::get(SystemTagsInUseCollection::class); |
|
| 136 | - $commentsCollection = new Comments\RootCollection( |
|
| 137 | - Server::get(ICommentsManager::class), |
|
| 138 | - $userManager, |
|
| 139 | - Server::get(IUserSession::class), |
|
| 140 | - $dispatcher, |
|
| 141 | - $logger |
|
| 142 | - ); |
|
| 143 | - |
|
| 144 | - $contactsSharingBackend = Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class); |
|
| 145 | - $config = Server::get(IConfig::class); |
|
| 146 | - |
|
| 147 | - $pluginManager = new PluginManager(\OC::$server, Server::get(IAppManager::class)); |
|
| 148 | - $usersCardDavBackend = new CardDavBackend( |
|
| 149 | - $db, |
|
| 150 | - $userPrincipalBackend, |
|
| 151 | - $userManager, |
|
| 152 | - $dispatcher, |
|
| 153 | - $contactsSharingBackend, |
|
| 154 | - $config |
|
| 155 | - ); |
|
| 156 | - $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users'); |
|
| 157 | - $usersAddressBookRoot->disableListing = $disableListing; |
|
| 158 | - |
|
| 159 | - $systemCardDavBackend = new CardDavBackend( |
|
| 160 | - $db, |
|
| 161 | - $userPrincipalBackend, |
|
| 162 | - $userManager, |
|
| 163 | - $dispatcher, |
|
| 164 | - $contactsSharingBackend, |
|
| 165 | - $config |
|
| 166 | - ); |
|
| 167 | - $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system'); |
|
| 168 | - $systemAddressBookRoot->disableListing = $disableListing; |
|
| 169 | - |
|
| 170 | - $uploadCollection = new Upload\RootCollection( |
|
| 171 | - $userPrincipalBackend, |
|
| 172 | - 'principals/users', |
|
| 173 | - Server::get(CleanupService::class), |
|
| 174 | - $rootFolder, |
|
| 175 | - $userSession, |
|
| 176 | - $shareManager, |
|
| 177 | - ); |
|
| 178 | - $uploadCollection->disableListing = $disableListing; |
|
| 179 | - |
|
| 180 | - $avatarCollection = new Avatars\RootCollection($userPrincipalBackend, 'principals/users'); |
|
| 181 | - $avatarCollection->disableListing = $disableListing; |
|
| 182 | - |
|
| 183 | - $appleProvisioning = new AppleProvisioningNode( |
|
| 184 | - Server::get(ITimeFactory::class)); |
|
| 185 | - |
|
| 186 | - $children = [ |
|
| 187 | - new SimpleCollection('principals', [ |
|
| 188 | - $userPrincipals, |
|
| 189 | - $groupPrincipals, |
|
| 190 | - $systemPrincipals, |
|
| 191 | - $calendarResourcePrincipals, |
|
| 192 | - $calendarRoomPrincipals, |
|
| 193 | - $remoteUserPrincipals]), |
|
| 194 | - $filesCollection, |
|
| 195 | - $userCalendarRoot, |
|
| 196 | - $remoteUserCalendarRoot, |
|
| 197 | - new SimpleCollection('system-calendars', [ |
|
| 198 | - $resourceCalendarRoot, |
|
| 199 | - $roomCalendarRoot, |
|
| 200 | - ]), |
|
| 201 | - $publicCalendarRoot, |
|
| 202 | - new SimpleCollection('addressbooks', [ |
|
| 203 | - $usersAddressBookRoot, |
|
| 204 | - $systemAddressBookRoot]), |
|
| 205 | - $systemTagCollection, |
|
| 206 | - $systemTagRelationsCollection, |
|
| 207 | - $systemTagInUseCollection, |
|
| 208 | - $commentsCollection, |
|
| 209 | - $uploadCollection, |
|
| 210 | - $avatarCollection, |
|
| 211 | - new SimpleCollection('provisioning', [ |
|
| 212 | - $appleProvisioning |
|
| 213 | - ]) |
|
| 214 | - ]; |
|
| 215 | - |
|
| 216 | - parent::__construct('root', $children); |
|
| 217 | - } |
|
| 52 | + public function __construct() { |
|
| 53 | + $l10n = \OC::$server->getL10N('dav'); |
|
| 54 | + $random = Server::get(ISecureRandom::class); |
|
| 55 | + $logger = Server::get(LoggerInterface::class); |
|
| 56 | + $userManager = Server::get(IUserManager::class); |
|
| 57 | + $userSession = Server::get(IUserSession::class); |
|
| 58 | + $groupManager = Server::get(IGroupManager::class); |
|
| 59 | + $shareManager = Server::get(\OCP\Share\IManager::class); |
|
| 60 | + $db = Server::get(IDBConnection::class); |
|
| 61 | + $dispatcher = Server::get(IEventDispatcher::class); |
|
| 62 | + $config = Server::get(IConfig::class); |
|
| 63 | + $proxyMapper = Server::get(ProxyMapper::class); |
|
| 64 | + $rootFolder = Server::get(IRootFolder::class); |
|
| 65 | + $federatedCalendarFactory = Server::get(FederatedCalendarFactory::class); |
|
| 66 | + |
|
| 67 | + $userPrincipalBackend = new Principal( |
|
| 68 | + $userManager, |
|
| 69 | + $groupManager, |
|
| 70 | + Server::get(IAccountManager::class), |
|
| 71 | + $shareManager, |
|
| 72 | + Server::get(IUserSession::class), |
|
| 73 | + Server::get(IAppManager::class), |
|
| 74 | + $proxyMapper, |
|
| 75 | + Server::get(KnownUserService::class), |
|
| 76 | + Server::get(IConfig::class), |
|
| 77 | + \OC::$server->getL10NFactory() |
|
| 78 | + ); |
|
| 79 | + |
|
| 80 | + $groupPrincipalBackend = new GroupPrincipalBackend($groupManager, $userSession, $shareManager, $config); |
|
| 81 | + $calendarResourcePrincipalBackend = new ResourcePrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper); |
|
| 82 | + $calendarRoomPrincipalBackend = new RoomPrincipalBackend($db, $userSession, $groupManager, $logger, $proxyMapper); |
|
| 83 | + $remoteUserPrincipalBackend = Server::get(RemoteUserPrincipalBackend::class); |
|
| 84 | + // as soon as debug mode is enabled we allow listing of principals |
|
| 85 | + $disableListing = !$config->getSystemValue('debug', false); |
|
| 86 | + |
|
| 87 | + // setup the first level of the dav tree |
|
| 88 | + $userPrincipals = new Collection($userPrincipalBackend, 'principals/users'); |
|
| 89 | + $userPrincipals->disableListing = $disableListing; |
|
| 90 | + $groupPrincipals = new Collection($groupPrincipalBackend, 'principals/groups'); |
|
| 91 | + $groupPrincipals->disableListing = $disableListing; |
|
| 92 | + $systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system'); |
|
| 93 | + $systemPrincipals->disableListing = $disableListing; |
|
| 94 | + $calendarResourcePrincipals = new Collection($calendarResourcePrincipalBackend, 'principals/calendar-resources'); |
|
| 95 | + $calendarRoomPrincipals = new Collection($calendarRoomPrincipalBackend, 'principals/calendar-rooms'); |
|
| 96 | + $remoteUserPrincipals = new Collection($remoteUserPrincipalBackend, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX); |
|
| 97 | + $calendarSharingBackend = Server::get(Backend::class); |
|
| 98 | + |
|
| 99 | + $filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users'); |
|
| 100 | + $filesCollection->disableListing = $disableListing; |
|
| 101 | + $caldavBackend = new CalDavBackend( |
|
| 102 | + $db, |
|
| 103 | + $userPrincipalBackend, |
|
| 104 | + $userManager, |
|
| 105 | + $random, |
|
| 106 | + $logger, |
|
| 107 | + $dispatcher, |
|
| 108 | + $config, |
|
| 109 | + $calendarSharingBackend, |
|
| 110 | + Server::get(FederatedCalendarMapper::class), |
|
| 111 | + false, |
|
| 112 | + ); |
|
| 113 | + $userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 114 | + $userCalendarRoot->disableListing = $disableListing; |
|
| 115 | + |
|
| 116 | + $remoteUserCalendarRoot = new CalendarRoot($remoteUserPrincipalBackend, $caldavBackend, RemoteUserPrincipalBackend::PRINCIPAL_PREFIX, $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 117 | + $remoteUserCalendarRoot->disableListing = $disableListing; |
|
| 118 | + |
|
| 119 | + $resourceCalendarRoot = new CalendarRoot($calendarResourcePrincipalBackend, $caldavBackend, 'principals/calendar-resources', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 120 | + $resourceCalendarRoot->disableListing = $disableListing; |
|
| 121 | + $roomCalendarRoot = new CalendarRoot($calendarRoomPrincipalBackend, $caldavBackend, 'principals/calendar-rooms', $logger, $l10n, $config, $federatedCalendarFactory); |
|
| 122 | + $roomCalendarRoot->disableListing = $disableListing; |
|
| 123 | + |
|
| 124 | + $publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger); |
|
| 125 | + |
|
| 126 | + $systemTagCollection = Server::get(SystemTagsByIdCollection::class); |
|
| 127 | + $systemTagRelationsCollection = new SystemTagsRelationsCollection( |
|
| 128 | + Server::get(ISystemTagManager::class), |
|
| 129 | + Server::get(ISystemTagObjectMapper::class), |
|
| 130 | + Server::get(IUserSession::class), |
|
| 131 | + $groupManager, |
|
| 132 | + $dispatcher, |
|
| 133 | + $rootFolder, |
|
| 134 | + ); |
|
| 135 | + $systemTagInUseCollection = Server::get(SystemTagsInUseCollection::class); |
|
| 136 | + $commentsCollection = new Comments\RootCollection( |
|
| 137 | + Server::get(ICommentsManager::class), |
|
| 138 | + $userManager, |
|
| 139 | + Server::get(IUserSession::class), |
|
| 140 | + $dispatcher, |
|
| 141 | + $logger |
|
| 142 | + ); |
|
| 143 | + |
|
| 144 | + $contactsSharingBackend = Server::get(\OCA\DAV\CardDAV\Sharing\Backend::class); |
|
| 145 | + $config = Server::get(IConfig::class); |
|
| 146 | + |
|
| 147 | + $pluginManager = new PluginManager(\OC::$server, Server::get(IAppManager::class)); |
|
| 148 | + $usersCardDavBackend = new CardDavBackend( |
|
| 149 | + $db, |
|
| 150 | + $userPrincipalBackend, |
|
| 151 | + $userManager, |
|
| 152 | + $dispatcher, |
|
| 153 | + $contactsSharingBackend, |
|
| 154 | + $config |
|
| 155 | + ); |
|
| 156 | + $usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/users'); |
|
| 157 | + $usersAddressBookRoot->disableListing = $disableListing; |
|
| 158 | + |
|
| 159 | + $systemCardDavBackend = new CardDavBackend( |
|
| 160 | + $db, |
|
| 161 | + $userPrincipalBackend, |
|
| 162 | + $userManager, |
|
| 163 | + $dispatcher, |
|
| 164 | + $contactsSharingBackend, |
|
| 165 | + $config |
|
| 166 | + ); |
|
| 167 | + $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, $pluginManager, $userSession->getUser(), $groupManager, 'principals/system'); |
|
| 168 | + $systemAddressBookRoot->disableListing = $disableListing; |
|
| 169 | + |
|
| 170 | + $uploadCollection = new Upload\RootCollection( |
|
| 171 | + $userPrincipalBackend, |
|
| 172 | + 'principals/users', |
|
| 173 | + Server::get(CleanupService::class), |
|
| 174 | + $rootFolder, |
|
| 175 | + $userSession, |
|
| 176 | + $shareManager, |
|
| 177 | + ); |
|
| 178 | + $uploadCollection->disableListing = $disableListing; |
|
| 179 | + |
|
| 180 | + $avatarCollection = new Avatars\RootCollection($userPrincipalBackend, 'principals/users'); |
|
| 181 | + $avatarCollection->disableListing = $disableListing; |
|
| 182 | + |
|
| 183 | + $appleProvisioning = new AppleProvisioningNode( |
|
| 184 | + Server::get(ITimeFactory::class)); |
|
| 185 | + |
|
| 186 | + $children = [ |
|
| 187 | + new SimpleCollection('principals', [ |
|
| 188 | + $userPrincipals, |
|
| 189 | + $groupPrincipals, |
|
| 190 | + $systemPrincipals, |
|
| 191 | + $calendarResourcePrincipals, |
|
| 192 | + $calendarRoomPrincipals, |
|
| 193 | + $remoteUserPrincipals]), |
|
| 194 | + $filesCollection, |
|
| 195 | + $userCalendarRoot, |
|
| 196 | + $remoteUserCalendarRoot, |
|
| 197 | + new SimpleCollection('system-calendars', [ |
|
| 198 | + $resourceCalendarRoot, |
|
| 199 | + $roomCalendarRoot, |
|
| 200 | + ]), |
|
| 201 | + $publicCalendarRoot, |
|
| 202 | + new SimpleCollection('addressbooks', [ |
|
| 203 | + $usersAddressBookRoot, |
|
| 204 | + $systemAddressBookRoot]), |
|
| 205 | + $systemTagCollection, |
|
| 206 | + $systemTagRelationsCollection, |
|
| 207 | + $systemTagInUseCollection, |
|
| 208 | + $commentsCollection, |
|
| 209 | + $uploadCollection, |
|
| 210 | + $avatarCollection, |
|
| 211 | + new SimpleCollection('provisioning', [ |
|
| 212 | + $appleProvisioning |
|
| 213 | + ]) |
|
| 214 | + ]; |
|
| 215 | + |
|
| 216 | + parent::__construct('root', $children); |
|
| 217 | + } |
|
| 218 | 218 | } |