%PDF- %PDF-
Direktori : /www/varak.net/nextcloud.varak.net/apps_old/apps/circles/lib/ |
Current File : //www/varak.net/nextcloud.varak.net/apps_old/apps/circles/lib/ShareByCircleProvider.php |
<?php declare(strict_types=1); /** * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\Circles; use Exception; use OC; use OCA\Circles\Exceptions\CircleNotFoundException; use OCA\Circles\Exceptions\ContactAddressBookNotFoundException; use OCA\Circles\Exceptions\ContactFormatException; use OCA\Circles\Exceptions\ContactNotFoundException; use OCA\Circles\Exceptions\FederatedEventException; use OCA\Circles\Exceptions\FederatedItemException; use OCA\Circles\Exceptions\FederatedUserException; use OCA\Circles\Exceptions\FederatedUserNotFoundException; use OCA\Circles\Exceptions\InitiatorNotConfirmedException; use OCA\Circles\Exceptions\InitiatorNotFoundException; use OCA\Circles\Exceptions\InvalidIdException; use OCA\Circles\Exceptions\OwnerNotFoundException; use OCA\Circles\Exceptions\RemoteInstanceException; use OCA\Circles\Exceptions\RemoteNotFoundException; use OCA\Circles\Exceptions\RemoteResourceNotFoundException; use OCA\Circles\Exceptions\RequestBuilderException; use OCA\Circles\Exceptions\ShareWrapperNotFoundException; use OCA\Circles\Exceptions\SingleCircleNotFoundException; use OCA\Circles\Exceptions\UnknownRemoteException; use OCA\Circles\FederatedItems\Files\FileShare; use OCA\Circles\FederatedItems\Files\FileUnshare; use OCA\Circles\Model\Federated\FederatedEvent; use OCA\Circles\Model\Member; use OCA\Circles\Model\Probes\CircleProbe; use OCA\Circles\Model\Probes\DataProbe; use OCA\Circles\Model\ShareWrapper; use OCA\Circles\Service\CircleService; use OCA\Circles\Service\EventService; use OCA\Circles\Service\FederatedEventService; use OCA\Circles\Service\FederatedUserService; use OCA\Circles\Service\ShareTokenService; use OCA\Circles\Service\ShareWrapperService; use OCA\Circles\Tools\Traits\TArrayTools; use OCA\Circles\Tools\Traits\TNCLogger; use OCA\Circles\Tools\Traits\TStringTools; use OCP\Files\Folder; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\IL10N; use OCP\ILogger; use OCP\IURLGenerator; use OCP\IUserManager; use OCP\Security\ISecureRandom; use OCP\Share\Exceptions\AlreadySharedException; use OCP\Share\Exceptions\IllegalIDChangeException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; use Psr\Log\LoggerInterface; /** * Class ShareByCircleProvider * * @package OCA\Circles */ class ShareByCircleProvider implements IShareProvider { use TArrayTools; use TStringTools; use TNCLogger; public const IDENTIFIER = 'ocCircleShare'; private IUserManager $userManager; private IRootFolder $rootFolder; private IL10N $l10n; private LoggerInterface $logger; private IURLGenerator $urlGenerator; private ShareWrapperService $shareWrapperService; private ShareTokenService $shareTokenService; private FederatedUserService $federatedUserService; private FederatedEventService $federatedEventService; private CircleService $circleService; private EventService $eventService; public function __construct( IDBConnection $connection, ISecureRandom $secureRandom, IUserManager $userManager, IRootFolder $rootFolder, IL10N $l10n, ILogger $logger, IURLGenerator $urlGenerator ) { $this->userManager = $userManager; $this->rootFolder = $rootFolder; $this->l10n = $l10n; $this->logger = OC::$server->get(LoggerInterface::class); $this->urlGenerator = $urlGenerator; $this->federatedUserService = OC::$server->get(FederatedUserService::class); $this->federatedEventService = OC::$server->get(FederatedEventService::class); $this->shareWrapperService = OC::$server->get(ShareWrapperService::class); $this->shareTokenService = OC::$server->get(ShareTokenService::class); $this->circleService = OC::$server->get(CircleService::class); $this->eventService = OC::$server->get(EventService::class); } /** * @return string */ public function identifier(): string { return self::IDENTIFIER; } /** * @param IShare $share * * @return IShare * @throws AlreadySharedException * @throws CircleNotFoundException * @throws FederatedEventException * @throws FederatedItemException * @throws InitiatorNotConfirmedException * @throws OwnerNotFoundException * @throws RemoteInstanceException * @throws RemoteNotFoundException * @throws RemoteResourceNotFoundException * @throws UnknownRemoteException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InitiatorNotFoundException * @throws InvalidIdException * @throws InvalidPathException * @throws NotFoundException * @throws RequestBuilderException * @throws ShareNotFound * @throws SingleCircleNotFoundException */ public function create(IShare $share): IShare { if ($share->getShareType() !== IShare::TYPE_CIRCLE) { return $share; } $nodeId = $share->getNode() ->getId(); try { $knowShareWrapper = $this->shareWrapperService->searchShare($share->getSharedWith(), $nodeId); throw new AlreadySharedException( $this->l10n->t('This item is already shared with this team'), $knowShareWrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator) ); } catch (ShareWrapperNotFoundException $e) { } $this->federatedUserService->initCurrentUser(); $circleProbe = new CircleProbe(); $dataProbe = new DataProbe(); $dataProbe->add(DataProbe::OWNER) ->add(DataProbe::INITIATOR, [DataProbe::BASED_ON]); $circle = $this->circleService->probeCircle($share->getSharedWith(), $circleProbe, $dataProbe); $share->setToken($this->token(15)); $owner = $circle->getInitiator(); $this->shareWrapperService->save($share); try { $wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId()); $wrappedShare->setOwner($owner); } catch (ShareWrapperNotFoundException $e) { throw new ShareNotFound(); } $event = new FederatedEvent(FileShare::class); $event->setCircle($circle); $event->getParams()->sObj('wrappedShare', $wrappedShare); $this->federatedEventService->newEvent($event); $this->eventService->localShareCreated($wrappedShare); return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } /** * @param IShare $share * * @return IShare * @throws IllegalIDChangeException * @throws ShareWrapperNotFoundException * @throws RequestBuilderException */ public function update(IShare $share): IShare { $wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId()); $wrappedShare->setPermissions($share->getPermissions()) ->setShareOwner($share->getShareOwner()) ->setAttributes($share->getAttributes()) ->setSharedBy($share->getSharedBy()) ->setExpirationDate($share->getExpirationDate()); $this->shareWrapperService->update($wrappedShare); return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } /** * @param IShare $share * * @throws FederatedEventException * @throws FederatedItemException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws InitiatorNotConfirmedException * @throws InvalidIdException * @throws OwnerNotFoundException * @throws RemoteInstanceException * @throws RemoteNotFoundException * @throws RemoteResourceNotFoundException * @throws RequestBuilderException * @throws SingleCircleNotFoundException * @throws UnknownRemoteException */ public function delete(IShare $share): void { if ($share->getShareType() !== IShare::TYPE_CIRCLE) { return; } $this->federatedUserService->initCurrentUser(); try { $wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId()); } catch (ShareWrapperNotFoundException $e) { return; } $this->shareWrapperService->delete($wrappedShare); try { $circle = $this->circleService->getCircle($share->getSharedWith()); } catch (CircleNotFoundException $e) { return; } catch (InitiatorNotFoundException $e) { // force the unshare ? return; } $event = new FederatedEvent(FileUnshare::class); $event->setCircle($circle) ->getParams()->sObj('wrappedShare', $wrappedShare); $this->federatedEventService->newEvent($event); $this->eventService->localShareDeleted($wrappedShare); } /** * @param IShare $share * @param string $recipient * * @throws ContactAddressBookNotFoundException * @throws ContactFormatException * @throws ContactNotFoundException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws InvalidIdException * @throws NotFoundException * @throws RequestBuilderException * @throws ShareWrapperNotFoundException * @throws SingleCircleNotFoundException */ public function deleteFromSelf(IShare $share, $recipient): void { $federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient); $child = $this->shareWrapperService->getChild($share, $federatedUser); $this->debug('Shares::move()', ['federatedUser' => $federatedUser, 'child' => $child]); if ($child->getPermissions() > 0) { $child->setPermissions(0); $this->shareWrapperService->update($child); } } /** * @param IShare $share * @param string $recipient * * @return IShare * @throws ContactAddressBookNotFoundException * @throws ContactFormatException * @throws ContactNotFoundException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InvalidIdException * @throws NotFoundException * @throws RequestBuilderException * @throws ShareWrapperNotFoundException * @throws SingleCircleNotFoundException */ public function move(IShare $share, $recipient): IShare { $federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient); $child = $this->shareWrapperService->getChild($share, $federatedUser); $this->debug('Shares::move()', ['federatedUser' => $federatedUser, 'child' => $child]); if ($child->getFileTarget() !== $share->getTarget()) { $child->setFileTarget($share->getTarget()); $this->shareWrapperService->update($child); } $wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId(), $federatedUser); return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } /** * @param IShare $share * @param string $recipient * * @return IShare */ public function restore(IShare $share, string $recipient): IShare { $orig = $this->shareWrapperService->getShareById((int)$share->getId()); $federatedUser = $this->federatedUserService->getLocalFederatedUser($recipient); $child = $this->shareWrapperService->getChild($share, $federatedUser); $this->debug('Shares::restore()', ['federatedUser' => $federatedUser, 'child' => $child]); if ($child->getPermissions() !== $orig->getPermissions()) { $child->setPermissions($orig->getPermissions()); $this->shareWrapperService->update($child); } $wrappedShare = $this->shareWrapperService->getShareById((int)$share->getId(), $federatedUser); return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } /** * @param string $userId * @param Folder $node * @param bool $reshares * @param bool $shallow Whether the method should stop at the first level, or look into sub-folders. * * @return array * @throws ContactAddressBookNotFoundException * @throws ContactFormatException * @throws ContactNotFoundException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InvalidIdException * @throws InvalidPathException * @throws NotFoundException * @throws RequestBuilderException * @throws SingleCircleNotFoundException */ public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true): array { $federatedUser = $this->federatedUserService->getLocalFederatedUser($userId); $wrappedShares = $this->shareWrapperService->getSharesInFolder( $federatedUser, $node, $reshares, $shallow ); $result = []; foreach ($wrappedShares as $wrappedShare) { if (!array_key_exists($wrappedShare->getFileSource(), $result)) { $result[$wrappedShare->getFileSource()] = []; } if ($wrappedShare->getFileCache()->isAccessible()) { $result[$wrappedShare->getFileSource()][] = $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } else { $this->logger->debug('shared document is not available anymore', ['wrappedShare' => $wrappedShare]); if ($wrappedShare->getFileCache()->getPath() === '') { $this->logger->notice('share is not available while path is empty. might comes from an unsupported storage.', ['wrappedShare' => $wrappedShare]); } } } return $result; } /** * @param string $userId * @param int $shareType * @param Node|null $node * @param bool $reshares * @param int $limit * @param int $offset * * @return IShare[] * @throws ContactAddressBookNotFoundException * @throws ContactFormatException * @throws ContactNotFoundException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InvalidIdException * @throws InvalidPathException * @throws NotFoundException * @throws RequestBuilderException * @throws SingleCircleNotFoundException */ public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset): array { if ($shareType !== IShare::TYPE_CIRCLE) { return []; } $nodeId = (!is_null($node)) ? $node->getId() : 0; try { $federatedUser = $this->federatedUserService->getLocalFederatedUser($userId); } catch (Exception $e) { $this->e($e, ['userId' => $userId, 'shareType' => $shareType, 'nodeId' => $nodeId]); return []; } $wrappedShares = $this->shareWrapperService->getSharesBy( $federatedUser, $nodeId, $reshares, $limit, $offset, true ); return array_filter( array_map( function (ShareWrapper $wrapper) { return $wrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); }, $wrappedShares ) ); } /** * @param string $shareId * @param string|null $recipientId * * @return IShare * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InvalidIdException * @throws ShareNotFound * @throws SingleCircleNotFoundException * @throws RequestBuilderException */ public function getShareById($shareId, $recipientId = null): IShare { if (is_null($recipientId)) { $federatedUser = null; } else { $federatedUser = $this->federatedUserService->getLocalFederatedUser($recipientId); } try { $wrappedShare = $this->shareWrapperService->getShareById((int)$shareId, $federatedUser); } catch (ShareWrapperNotFoundException $e) { throw new ShareNotFound(); } return $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); } /** * @param Node $path * * @return IShare[] * @throws InvalidPathException * @throws NotFoundException * @throws IllegalIDChangeException * @throws RequestBuilderException */ public function getSharesByPath(Node $path): array { $wrappedShares = $this->shareWrapperService->getSharesByFileId($path->getId()); return array_filter( array_map( function (ShareWrapper $wrapper) { return $wrapper->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); }, $wrappedShares ) ); } /** * @param string $userId * @param int $shareType * @param Node|null $node * @param int $limit * @param int $offset * * @return IShare[] * @throws ContactAddressBookNotFoundException * @throws ContactFormatException * @throws ContactNotFoundException * @throws FederatedUserException * @throws FederatedUserNotFoundException * @throws IllegalIDChangeException * @throws InvalidIdException * @throws InvalidPathException * @throws NotFoundException * @throws RequestBuilderException * @throws SingleCircleNotFoundException */ public function getSharedWith($userId, $shareType, $node, $limit, $offset): array { if ($shareType !== IShare::TYPE_CIRCLE) { return []; } $federatedUser = $this->federatedUserService->getLocalFederatedUser($userId); $probe = new CircleProbe(); $probe->includePersonalCircles() ->includeSystemCircles() ->mustBeMember() ->setItemsLimit((int)$limit) ->setItemsOffset((int)$offset); $wrappedShares = $this->shareWrapperService->getSharedWith( $federatedUser, (!is_null($node)) ? $node->getId() : 0, $probe ); return array_filter( array_map( function (ShareWrapper $wrapper) { return $wrapper->getShare( $this->rootFolder, $this->userManager, $this->urlGenerator, true ); }, $wrappedShares ) ); } /** * @param string $token * * @return IShare * @throws IllegalIDChangeException * @throws RequestBuilderException * @throws ShareNotFound */ public function getShareByToken($token): IShare { if (is_null($token)) { throw new ShareNotFound(); } try { $wrappedShare = $this->shareWrapperService->getShareByToken($token); } catch (ShareWrapperNotFoundException $e) { throw new ShareNotFound(); } $share = $wrappedShare->getShare($this->rootFolder, $this->userManager, $this->urlGenerator); if ($share->getPassword() !== '') { $this->logger->notice('share is protected by a password, hash: ' . $share->getPassword()); } return $share; } public function formatShare(IShare $share): array { $this->federatedUserService->initCurrentUser(); $circleProbe = new CircleProbe(); $dataProbe = new DataProbe(); $result = ['share_with' => $share->getSharedWith()]; try { $circle = $this->circleService->probeCircle($share->getSharedWith(), $circleProbe, $dataProbe); $result['share_with_displayname'] = $circle->getDisplayName(); } catch (Exception $e) { $this->logger->warning( 'Circle not found while probeCircle', [ 'sharedWith' => $share->getSharedWith(), 'exception' => $e ] ); } return $result; } /** * @param string $uid * @param int $shareType */ public function userDeleted($uid, $shareType): void { } /** * @param string $gid */ public function groupDeleted($gid): void { } /** * @param string $uid * @param string $gid */ public function userDeletedFromGroup($uid, $gid): void { } /** * if $currentAccess, returns long version of the access list: * [ * 'users' => [ * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'], * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'], * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'], * ], * 'remote' => [ * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'], * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], * ], * 'public' => bool, * 'mail' => [ * 'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'], * 'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'], * ] * ] * * * * * @param Node[] $nodes * @param bool $currentAccess * * @return array */ public function getAccessList($nodes, $currentAccess): array { $ids = []; foreach ($nodes as $node) { $ids[] = $node->getId(); } if (!$currentAccess) { return $this->getAccessListShort($ids); } $shareIds = $knownIds = $users = $remote = $mails = []; foreach ($this->shareWrapperService->getSharesByFileIds($ids, true, true) as $share) { if (!$share->hasCircle()) { continue; } $shareIds[] = $share->getId(); $circle = $share->getCircle(); foreach ($circle->getInheritedMembers() as $member) { if ($share->getParent() > 0 && in_array($member->getSingleId(), $knownIds[$share->getFileSource()] ?? [])) { continue; } $knownIds[$share->getFileSource()][] = $member->getSingleId(); switch ($member->getUserType()) { case Member::TYPE_USER: if ($member->isLocal()) { $users[$member->getUserId()] = [ 'node_id' => $share->getFileSource(), 'node_path' => $share->getFileTarget() ]; } else { // we only store temp value, as token is unknown at this point $remote[$member->getUserid() . '@' . $member->getInstance()] = [ 'node_id' => $share->getFileSource(), 'shareId' => $share->getId(), 'memberId' => $member->getId(), ]; } break; case Member::TYPE_MAIL: // we only store temp value, as token is unknown at this point $mails[$member->getUserId()] = [ 'node_id' => $share->getFileSource(), 'shareId' => $share->getId(), 'memberId' => $member->getId(), ]; break; } } } // list share tokens in an indexed array and update details for remote/mail entries with the correct token $shareTokens = []; foreach ($this->shareTokenService->getTokensFromShares(array_values(array_unique($shareIds))) as $shareToken) { $shareTokens[$shareToken->getShareId()][$shareToken->getMemberId()] = $shareToken->getToken(); } return [ 'users' => $users, 'remote' => $this->updateAccessListTokens($remote, $shareTokens), 'email' => $this->updateAccessListTokens($mails, $shareTokens) ]; } /** * returns short version of the access list: * [ * 'users' => ['user1', 'user2', 'user4'], * 'remote' => bool, * 'mail' => ['email1@maildomain1', 'email2@maildomain2'] * ] * * @param array $ids * * @return array * @throws FederatedItemException * @throws RemoteInstanceException * @throws RemoteNotFoundException * @throws RemoteResourceNotFoundException * @throws RequestBuilderException * @throws UnknownRemoteException */ private function getAccessListShort(array $ids): array { $users = $mails = []; $remote = false; foreach ($this->shareWrapperService->getSharesByFileIds($ids, true) as $share) { $circle = $share->getCircle(); foreach ($circle->getInheritedMembers() as $member) { switch ($member->getUserType()) { case Member::TYPE_USER: if ($member->isLocal()) { if (!in_array($member->getUserId(), $users)) { $users[] = $member->getUserId(); } } else { $remote = true; } break; case Member::TYPE_MAIL: if (!in_array($member->getUserId(), $mails)) { $mails[] = $member->getUserId(); } break; } } } return [ 'users' => $users, 'remote' => $remote, 'email' => $mails ]; } /** * @param array $list * @param array<int, array<string, string>> $shareTokens * * @return array */ private function updateAccessListTokens(array $list, array $shareTokens): array { $result = []; foreach($list as $id => $data) { $result[$id] = [ 'node_id' => $data['node_id'], 'token' => $shareTokens[$data['shareId']][$data['memberId']] ]; } return $result; } /** * We don't return a thing about children. * The call to this function is deprecated and should be removed in next release of NC. * Also, we get the children in the delete() method. * * @param IShare $parent * * @return array */ public function getChildren(IShare $parent): array { return []; } /** * @return iterable */ public function getAllShares(): iterable { // $qb = $this->dbConnection->getQueryBuilder(); // // $qb->select(' * ') // ->from('share') // ->where( // $qb->expr() // ->orX( // $qb->expr() // ->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE)) // ) // ); // // $cursor = $qb->execute(); // while ($data = $cursor->fetch()) { // try { // yield $this->createShareObject($data); // } catch (IllegalIDChangeException $e) { // }; // } // $cursor->closeCursor(); return []; } }