diff options
Diffstat (limited to 'lib/private/Collaboration/Resources')
-rw-r--r-- | lib/private/Collaboration/Resources/Collection.php | 180 | ||||
-rw-r--r-- | lib/private/Collaboration/Resources/Listener.php | 53 | ||||
-rw-r--r-- | lib/private/Collaboration/Resources/Manager.php | 467 | ||||
-rw-r--r-- | lib/private/Collaboration/Resources/ProviderManager.php | 50 | ||||
-rw-r--r-- | lib/private/Collaboration/Resources/Resource.php | 113 |
5 files changed, 863 insertions, 0 deletions
diff --git a/lib/private/Collaboration/Resources/Collection.php b/lib/private/Collaboration/Resources/Collection.php new file mode 100644 index 00000000000..2481a3e9a09 --- /dev/null +++ b/lib/private/Collaboration/Resources/Collection.php @@ -0,0 +1,180 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Collaboration\Resources; + +use Doctrine\DBAL\Exception\ConstraintViolationException; +use OCP\Collaboration\Resources\ICollection; +use OCP\Collaboration\Resources\IManager; +use OCP\Collaboration\Resources\IResource; +use OCP\Collaboration\Resources\ResourceException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\IUser; + +class Collection implements ICollection { + /** @var IResource[] */ + protected array $resources = []; + + public function __construct( + /** @var Manager $manager */ + protected IManager $manager, + protected IDBConnection $connection, + protected int $id, + protected string $name, + protected ?IUser $userForAccess = null, + protected ?bool $access = null, + ) { + } + + /** + * @since 16.0.0 + */ + public function getId(): int { + return $this->id; + } + + /** + * @since 16.0.0 + */ + public function getName(): string { + return $this->name; + } + + /** + * @since 16.0.0 + */ + public function setName(string $name): void { + $query = $this->connection->getQueryBuilder(); + $query->update(Manager::TABLE_COLLECTIONS) + ->set('name', $query->createNamedParameter($name)) + ->where($query->expr()->eq('id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT))); + $query->executeStatement(); + + $this->name = $name; + } + + /** + * @return IResource[] + * @since 16.0.0 + */ + public function getResources(): array { + if (empty($this->resources)) { + $this->resources = $this->manager->getResourcesByCollectionForUser($this, $this->userForAccess); + } + + return $this->resources; + } + + /** + * Adds a resource to a collection + * + * @throws ResourceException when the resource is already part of the collection + * @since 16.0.0 + */ + public function addResource(IResource $resource): void { + array_map(function (IResource $r) use ($resource) { + if ($this->isSameResource($r, $resource)) { + throw new ResourceException('Already part of the collection'); + } + }, $this->getResources()); + + $this->resources[] = $resource; + + $query = $this->connection->getQueryBuilder(); + $query->insert(Manager::TABLE_RESOURCES) + ->values([ + 'collection_id' => $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT), + 'resource_type' => $query->createNamedParameter($resource->getType()), + 'resource_id' => $query->createNamedParameter($resource->getId()), + ]); + + try { + $query->execute(); + } catch (ConstraintViolationException $e) { + throw new ResourceException('Already part of the collection'); + } + + $this->manager->invalidateAccessCacheForCollection($this); + } + + /** + * Removes a resource from a collection + * + * @since 16.0.0 + */ + public function removeResource(IResource $resource): void { + $this->resources = array_filter($this->getResources(), function (IResource $r) use ($resource) { + return !$this->isSameResource($r, $resource); + }); + + $query = $this->connection->getQueryBuilder(); + $query->delete(Manager::TABLE_RESOURCES) + ->where($query->expr()->eq('collection_id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('resource_type', $query->createNamedParameter($resource->getType()))) + ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId()))); + $query->executeStatement(); + + if (empty($this->resources)) { + $this->removeCollection(); + } else { + $this->manager->invalidateAccessCacheForCollection($this); + } + } + + /** + * Can a user/guest access the collection + * + * @since 16.0.0 + */ + public function canAccess(?IUser $user): bool { + if ($user instanceof IUser) { + return $this->canUserAccess($user); + } + return $this->canGuestAccess(); + } + + protected function canUserAccess(IUser $user): bool { + if (\is_bool($this->access) && $this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) { + return $this->access; + } + + $access = $this->manager->canAccessCollection($this, $user); + if ($this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) { + $this->access = $access; + } + return $access; + } + + protected function canGuestAccess(): bool { + if (\is_bool($this->access) && !$this->userForAccess instanceof IUser) { + return $this->access; + } + + $access = $this->manager->canAccessCollection($this, null); + if (!$this->userForAccess instanceof IUser) { + $this->access = $access; + } + return $access; + } + + protected function isSameResource(IResource $resource1, IResource $resource2): bool { + return $resource1->getType() === $resource2->getType() + && $resource1->getId() === $resource2->getId(); + } + + protected function removeCollection(): void { + $query = $this->connection->getQueryBuilder(); + $query->delete(Manager::TABLE_COLLECTIONS) + ->where($query->expr()->eq('id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT))); + $query->executeStatement(); + + $this->manager->invalidateAccessCacheForCollection($this); + $this->id = 0; + } +} diff --git a/lib/private/Collaboration/Resources/Listener.php b/lib/private/Collaboration/Resources/Listener.php new file mode 100644 index 00000000000..dfdde24d78e --- /dev/null +++ b/lib/private/Collaboration/Resources/Listener.php @@ -0,0 +1,53 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Collaboration\Resources; + +use OCP\Collaboration\Resources\IManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Group\Events\BeforeGroupDeletedEvent; +use OCP\Group\Events\UserAddedEvent; +use OCP\Group\Events\UserRemovedEvent; +use OCP\User\Events\UserDeletedEvent; + +class Listener { + public static function register(IEventDispatcher $eventDispatcher): void { + $eventDispatcher->addListener(UserAddedEvent::class, function (UserAddedEvent $event) { + $user = $event->getUser(); + /** @var IManager $resourceManager */ + $resourceManager = \OCP\Server::get(IManager::class); + + $resourceManager->invalidateAccessCacheForUser($user); + }); + $eventDispatcher->addListener(UserRemovedEvent::class, function (UserRemovedEvent $event) { + $user = $event->getUser(); + /** @var IManager $resourceManager */ + $resourceManager = \OCP\Server::get(IManager::class); + + $resourceManager->invalidateAccessCacheForUser($user); + }); + + $eventDispatcher->addListener(UserDeletedEvent::class, function (UserDeletedEvent $event) { + $user = $event->getUser(); + /** @var IManager $resourceManager */ + $resourceManager = \OCP\Server::get(IManager::class); + + $resourceManager->invalidateAccessCacheForUser($user); + }); + + $eventDispatcher->addListener(BeforeGroupDeletedEvent::class, function (BeforeGroupDeletedEvent $event) { + $group = $event->getGroup(); + /** @var IManager $resourceManager */ + $resourceManager = \OCP\Server::get(IManager::class); + + foreach ($group->getUsers() as $user) { + $resourceManager->invalidateAccessCacheForUser($user); + } + }); + } +} diff --git a/lib/private/Collaboration/Resources/Manager.php b/lib/private/Collaboration/Resources/Manager.php new file mode 100644 index 00000000000..8d1e4b13287 --- /dev/null +++ b/lib/private/Collaboration/Resources/Manager.php @@ -0,0 +1,467 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Collaboration\Resources; + +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OCP\Collaboration\Resources\CollectionException; +use OCP\Collaboration\Resources\ICollection; +use OCP\Collaboration\Resources\IManager; +use OCP\Collaboration\Resources\IProvider; +use OCP\Collaboration\Resources\IProviderManager; +use OCP\Collaboration\Resources\IResource; +use OCP\Collaboration\Resources\ResourceException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\IUser; +use Psr\Log\LoggerInterface; + +class Manager implements IManager { + public const TABLE_COLLECTIONS = 'collres_collections'; + public const TABLE_RESOURCES = 'collres_resources'; + public const TABLE_ACCESS_CACHE = 'collres_accesscache'; + + /** @var string[] */ + protected array $providers = []; + + public function __construct( + protected IDBConnection $connection, + protected IProviderManager $providerManager, + protected LoggerInterface $logger, + ) { + } + + /** + * @throws CollectionException when the collection could not be found + * @since 16.0.0 + */ + public function getCollection(int $id): ICollection { + $query = $this->connection->getQueryBuilder(); + $query->select('*') + ->from(self::TABLE_COLLECTIONS) + ->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + $result = $query->execute(); + $row = $result->fetch(); + $result->closeCursor(); + + if (!$row) { + throw new CollectionException('Collection not found'); + } + + return new Collection($this, $this->connection, (int)$row['id'], (string)$row['name']); + } + + /** + * @throws CollectionException when the collection could not be found + * @since 16.0.0 + */ + public function getCollectionForUser(int $id, ?IUser $user): ICollection { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->select('*') + ->from(self::TABLE_COLLECTIONS, 'c') + ->leftJoin( + 'c', self::TABLE_ACCESS_CACHE, 'a', + $query->expr()->andX( + $query->expr()->eq('c.id', 'a.collection_id'), + $query->expr()->eq('a.user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ) + ) + ->where($query->expr()->eq('c.id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + $result = $query->execute(); + $row = $result->fetch(); + $result->closeCursor(); + + if (!$row) { + throw new CollectionException('Collection not found'); + } + + $access = $row['access'] === null ? null : (bool)$row['access']; + if ($user instanceof IUser) { + return new Collection($this, $this->connection, (int)$row['id'], (string)$row['name'], $user, $access); + } + + return new Collection($this, $this->connection, (int)$row['id'], (string)$row['name'], $user, $access); + } + + /** + * @return ICollection[] + * @since 16.0.0 + */ + public function searchCollections(IUser $user, string $filter, int $limit = 50, int $start = 0): array { + $query = $this->connection->getQueryBuilder(); + $userId = $user->getUID(); + + $query->select('c.*', 'a.access') + ->from(self::TABLE_COLLECTIONS, 'c') + ->leftJoin( + 'c', self::TABLE_ACCESS_CACHE, 'a', + $query->expr()->andX( + $query->expr()->eq('c.id', 'a.collection_id'), + $query->expr()->eq('a.user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ) + ) + ->where($query->expr()->eq('a.access', $query->createNamedParameter(1, IQueryBuilder::PARAM_INT))) + ->orderBy('c.id') + ->setMaxResults($limit) + ->setFirstResult($start); + + if ($filter !== '') { + $query->andWhere($query->expr()->iLike('c.name', $query->createNamedParameter('%' . $this->connection->escapeLikeParameter($filter) . '%'))); + } + + $result = $query->execute(); + $collections = []; + + $foundResults = 0; + while ($row = $result->fetch()) { + $foundResults++; + $access = $row['access'] === null ? null : (bool)$row['access']; + $collection = new Collection($this, $this->connection, (int)$row['id'], (string)$row['name'], $user, $access); + if ($collection->canAccess($user)) { + $collections[] = $collection; + } + } + $result->closeCursor(); + + if (empty($collections) && $foundResults === $limit) { + return $this->searchCollections($user, $filter, $limit, $start + $limit); + } + + return $collections; + } + + /** + * @since 16.0.0 + */ + public function newCollection(string $name): ICollection { + $query = $this->connection->getQueryBuilder(); + $query->insert(self::TABLE_COLLECTIONS) + ->values([ + 'name' => $query->createNamedParameter($name), + ]); + $query->execute(); + + return new Collection($this, $this->connection, $query->getLastInsertId(), $name); + } + + /** + * @since 16.0.0 + */ + public function createResource(string $type, string $id): IResource { + return new Resource($this, $this->connection, $type, $id); + } + + /** + * @throws ResourceException + * @since 16.0.0 + */ + public function getResourceForUser(string $type, string $id, ?IUser $user): IResource { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->select('r.*', 'a.access') + ->from(self::TABLE_RESOURCES, 'r') + ->leftJoin( + 'r', self::TABLE_ACCESS_CACHE, 'a', + $query->expr()->andX( + $query->expr()->eq('r.resource_id', 'a.resource_id'), + $query->expr()->eq('r.resource_type', 'a.resource_type'), + $query->expr()->eq('a.user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ) + ) + ->where($query->expr()->eq('r.resource_type', $query->createNamedParameter($type, IQueryBuilder::PARAM_STR))) + ->andWhere($query->expr()->eq('r.resource_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + $row = $result->fetch(); + $result->closeCursor(); + + if (!$row) { + throw new ResourceException('Resource not found'); + } + + $access = $row['access'] === null ? null : (bool)$row['access']; + if ($user instanceof IUser) { + return new Resource($this, $this->connection, $type, $id, $user, $access); + } + + return new Resource($this, $this->connection, $type, $id, null, $access); + } + + /** + * @return IResource[] + * @since 16.0.0 + */ + public function getResourcesByCollectionForUser(ICollection $collection, ?IUser $user): array { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->select('r.*', 'a.access') + ->from(self::TABLE_RESOURCES, 'r') + ->leftJoin( + 'r', self::TABLE_ACCESS_CACHE, 'a', + $query->expr()->andX( + $query->expr()->eq('r.resource_id', 'a.resource_id'), + $query->expr()->eq('r.resource_type', 'a.resource_type'), + $query->expr()->eq('a.user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR)) + ) + ) + ->where($query->expr()->eq('r.collection_id', $query->createNamedParameter($collection->getId(), IQueryBuilder::PARAM_INT))); + + $resources = []; + $result = $query->execute(); + while ($row = $result->fetch()) { + $access = $row['access'] === null ? null : (bool)$row['access']; + $resources[] = new Resource($this, $this->connection, $row['resource_type'], $row['resource_id'], $user, $access); + } + $result->closeCursor(); + + return $resources; + } + + /** + * Get the rich object data of a resource + * + * @since 16.0.0 + */ + public function getResourceRichObject(IResource $resource): array { + foreach ($this->providerManager->getResourceProviders() as $provider) { + if ($provider->getType() === $resource->getType()) { + try { + return $provider->getResourceRichObject($resource); + } catch (ResourceException $e) { + } + } + } + + return []; + } + + /** + * Can a user/guest access the collection + * + * @since 16.0.0 + */ + public function canAccessResource(IResource $resource, ?IUser $user): bool { + $access = $this->checkAccessCacheForUserByResource($resource, $user); + if (\is_bool($access)) { + return $access; + } + + $access = false; + foreach ($this->providerManager->getResourceProviders() as $provider) { + if ($provider->getType() === $resource->getType()) { + try { + if ($provider->canAccessResource($resource, $user)) { + $access = true; + break; + } + } catch (ResourceException $e) { + } + } + } + + $this->cacheAccessForResource($resource, $user, $access); + return $access; + } + + /** + * Can a user/guest access the collection + * + * @since 16.0.0 + */ + public function canAccessCollection(ICollection $collection, ?IUser $user): bool { + $access = $this->checkAccessCacheForUserByCollection($collection, $user); + if (\is_bool($access)) { + return $access; + } + + $access = null; + // Access is granted when a user can access all resources + foreach ($collection->getResources() as $resource) { + if (!$resource->canAccess($user)) { + $access = false; + break; + } + + $access = true; + } + + $this->cacheAccessForCollection($collection, $user, $access); + return $access; + } + + protected function checkAccessCacheForUserByResource(IResource $resource, ?IUser $user): ?bool { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->select('access') + ->from(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId(), IQueryBuilder::PARAM_STR))) + ->andWhere($query->expr()->eq('resource_type', $query->createNamedParameter($resource->getType(), IQueryBuilder::PARAM_STR))) + ->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR))) + ->setMaxResults(1); + + $hasAccess = null; + $result = $query->execute(); + if ($row = $result->fetch()) { + $hasAccess = (bool)$row['access']; + } + $result->closeCursor(); + + return $hasAccess; + } + + protected function checkAccessCacheForUserByCollection(ICollection $collection, ?IUser $user): ?bool { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->select('access') + ->from(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('collection_id', $query->createNamedParameter($collection->getId(), IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId, IQueryBuilder::PARAM_STR))) + ->setMaxResults(1); + + $hasAccess = null; + $result = $query->execute(); + if ($row = $result->fetch()) { + $hasAccess = (bool)$row['access']; + } + $result->closeCursor(); + + return $hasAccess; + } + + public function cacheAccessForResource(IResource $resource, ?IUser $user, bool $access): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->insert(self::TABLE_ACCESS_CACHE) + ->values([ + 'user_id' => $query->createNamedParameter($userId), + 'resource_id' => $query->createNamedParameter($resource->getId()), + 'resource_type' => $query->createNamedParameter($resource->getType()), + 'access' => $query->createNamedParameter($access, IQueryBuilder::PARAM_BOOL), + ]); + try { + $query->execute(); + } catch (UniqueConstraintViolationException $e) { + } + } + + public function cacheAccessForCollection(ICollection $collection, ?IUser $user, bool $access): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->insert(self::TABLE_ACCESS_CACHE) + ->values([ + 'user_id' => $query->createNamedParameter($userId), + 'collection_id' => $query->createNamedParameter($collection->getId()), + 'access' => $query->createNamedParameter($access, IQueryBuilder::PARAM_BOOL), + ]); + try { + $query->execute(); + } catch (UniqueConstraintViolationException $e) { + } + } + + public function invalidateAccessCacheForUser(?IUser $user): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('user_id', $query->createNamedParameter($userId))); + $query->execute(); + } + + public function invalidateAccessCacheForResource(IResource $resource): void { + $query = $this->connection->getQueryBuilder(); + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId()))) + ->andWhere($query->expr()->eq('resource_type', $query->createNamedParameter($resource->getType(), IQueryBuilder::PARAM_STR))); + $query->execute(); + + foreach ($resource->getCollections() as $collection) { + $this->invalidateAccessCacheForCollection($collection); + } + } + + public function invalidateAccessCacheForAllCollections(): void { + $query = $this->connection->getQueryBuilder(); + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->neq('collection_id', $query->createNamedParameter(0))); + $query->execute(); + } + + public function invalidateAccessCacheForCollection(ICollection $collection): void { + $query = $this->connection->getQueryBuilder(); + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('collection_id', $query->createNamedParameter($collection->getId()))); + $query->execute(); + } + + public function invalidateAccessCacheForProvider(IProvider $provider): void { + $query = $this->connection->getQueryBuilder(); + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('resource_type', $query->createNamedParameter($provider->getType(), IQueryBuilder::PARAM_STR))); + $query->execute(); + } + + public function invalidateAccessCacheForResourceByUser(IResource $resource, ?IUser $user): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId()))) + ->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId))); + $query->execute(); + + foreach ($resource->getCollections() as $collection) { + $this->invalidateAccessCacheForCollectionByUser($collection, $user); + } + } + + protected function invalidateAccessCacheForCollectionByUser(ICollection $collection, ?IUser $user): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('collection_id', $query->createNamedParameter($collection->getId()))) + ->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId))); + $query->execute(); + } + + public function invalidateAccessCacheForProviderByUser(IProvider $provider, ?IUser $user): void { + $query = $this->connection->getQueryBuilder(); + $userId = $user instanceof IUser ? $user->getUID() : ''; + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->eq('resource_type', $query->createNamedParameter($provider->getType(), IQueryBuilder::PARAM_STR))) + ->andWhere($query->expr()->eq('user_id', $query->createNamedParameter($userId))); + $query->execute(); + } + + public function registerResourceProvider(string $provider): void { + $this->logger->debug('\OC\Collaboration\Resources\Manager::registerResourceProvider is deprecated', ['provider' => $provider]); + $this->providerManager->registerResourceProvider($provider); + } + + /** + * Get the resource type of the provider + * + * @since 16.0.0 + */ + public function getType(): string { + return ''; + } +} diff --git a/lib/private/Collaboration/Resources/ProviderManager.php b/lib/private/Collaboration/Resources/ProviderManager.php new file mode 100644 index 00000000000..0ce4ae7155a --- /dev/null +++ b/lib/private/Collaboration/Resources/ProviderManager.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Collaboration\Resources; + +use OCP\AppFramework\QueryException; +use OCP\Collaboration\Resources\IProvider; +use OCP\Collaboration\Resources\IProviderManager; +use OCP\IServerContainer; +use Psr\Log\LoggerInterface; + +class ProviderManager implements IProviderManager { + /** @var string[] */ + protected array $providers = []; + + /** @var IProvider[] */ + protected array $providerInstances = []; + + public function __construct( + protected IServerContainer $serverContainer, + protected LoggerInterface $logger, + ) { + } + + public function getResourceProviders(): array { + if ($this->providers !== []) { + foreach ($this->providers as $provider) { + try { + $this->providerInstances[] = $this->serverContainer->query($provider); + } catch (QueryException $e) { + $this->logger->error("Could not query resource provider $provider: " . $e->getMessage(), [ + 'exception' => $e, + ]); + } + } + $this->providers = []; + } + + return $this->providerInstances; + } + + public function registerResourceProvider(string $provider): void { + $this->providers[] = $provider; + } +} diff --git a/lib/private/Collaboration/Resources/Resource.php b/lib/private/Collaboration/Resources/Resource.php new file mode 100644 index 00000000000..19da3da7e7d --- /dev/null +++ b/lib/private/Collaboration/Resources/Resource.php @@ -0,0 +1,113 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Collaboration\Resources; + +use OCP\Collaboration\Resources\ICollection; +use OCP\Collaboration\Resources\IManager; +use OCP\Collaboration\Resources\IResource; +use OCP\IDBConnection; +use OCP\IUser; + +class Resource implements IResource { + protected ?array $data = null; + + public function __construct( + protected IManager $manager, + protected IDBConnection $connection, + protected string $type, + protected string $id, + protected ?IUser $userForAccess = null, + protected ?bool $access = null, + ) { + } + + /** + * @since 16.0.0 + */ + public function getType(): string { + return $this->type; + } + + /** + * @since 16.0.0 + */ + public function getId(): string { + return $this->id; + } + + /** + * @since 16.0.0 + */ + public function getRichObject(): array { + if ($this->data === null) { + $this->data = $this->manager->getResourceRichObject($this); + } + + return $this->data; + } + + /** + * Can a user/guest access the resource + * + * @since 16.0.0 + */ + public function canAccess(?IUser $user): bool { + if ($user instanceof IUser) { + return $this->canUserAccess($user); + } + return $this->canGuestAccess(); + } + + protected function canUserAccess(IUser $user): bool { + if (\is_bool($this->access) && $this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) { + return $this->access; + } + + $access = $this->manager->canAccessResource($this, $user); + if ($this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) { + $this->access = $access; + } + return $access; + } + + protected function canGuestAccess(): bool { + if (\is_bool($this->access) && !$this->userForAccess instanceof IUser) { + return $this->access; + } + + $access = $this->manager->canAccessResource($this, null); + if (!$this->userForAccess instanceof IUser) { + $this->access = $access; + } + return $access; + } + + /** + * @return ICollection[] + * @since 16.0.0 + */ + public function getCollections(): array { + $collections = []; + + $query = $this->connection->getQueryBuilder(); + + $query->select('collection_id') + ->from('collres_resources') + ->where($query->expr()->eq('resource_type', $query->createNamedParameter($this->getType()))) + ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($this->getId()))); + + $result = $query->execute(); + while ($row = $result->fetch()) { + $collections[] = $this->manager->getCollection((int)$row['collection_id']); + } + $result->closeCursor(); + + return $collections; + } +} |