123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- <?php
- declare(strict_types=1);
- /**
- * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com>
- *
- * @author Daniel Kesselberg <mail@danielkesselberg.de>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- 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 '';
- }
- }
|