diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 026e31b70a6..1d1c00772c5 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -867,7 +867,7 @@ class FederatedShareProvider implements IShareProvider, IShareProviderSupportsAl $qb = $this->dbConnection->getQueryBuilder(); $qb->delete('share_external') - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USER))) ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) ->executeStatement(); } diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php index 502021c67ee..9121cb1f2a6 100644 --- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php @@ -15,12 +15,12 @@ use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\Federation\TrustedServers; use OCA\Files_Sharing\Activity\Providers\RemoteShares; use OCA\Files_Sharing\External\ExternalShare; +use OCA\Files_Sharing\External\ExternalShareMapper; use OCA\Files_Sharing\External\Manager; use OCA\GlobalSiteSelector\Service\SlaveService; use OCA\Polls\Db\Share; use OCP\Activity\IManager as IActivityManager; use OCP\App\IAppManager; -use OCP\AppFramework\QueryException; use OCP\Constants; use OCP\Federation\Exceptions\ActionNotSupportedException; use OCP\Federation\Exceptions\AuthenticationFailedException; @@ -34,8 +34,6 @@ use OCP\Files\IFilenameValidator; use OCP\Files\NotFoundException; use OCP\HintException; use OCP\IConfig; -use OCP\IDBConnection; -use OCP\IGroup; use OCP\IGroupManager; use OCP\IURLGenerator; use OCP\IUser; @@ -48,52 +46,41 @@ use OCP\Share\IProviderFactory; use OCP\Share\IShare; use OCP\Snowflake\IGenerator; use OCP\Util; +use Override; use Psr\Log\LoggerInterface; use SensitiveParameter; class CloudFederationProviderFiles implements ISignedCloudFederationProvider { - /** - * CloudFederationProvider constructor. - */ public function __construct( - private IAppManager $appManager, - private FederatedShareProvider $federatedShareProvider, - private AddressHandler $addressHandler, - private IUserManager $userManager, - private IManager $shareManager, - private ICloudIdManager $cloudIdManager, - private IActivityManager $activityManager, - private INotificationManager $notificationManager, - private IURLGenerator $urlGenerator, - private ICloudFederationFactory $cloudFederationFactory, - private ICloudFederationProviderManager $cloudFederationProviderManager, - private IDBConnection $connection, - private IGroupManager $groupManager, - private IConfig $config, - private Manager $externalShareManager, - private LoggerInterface $logger, - private IFilenameValidator $filenameValidator, + private readonly IAppManager $appManager, + private readonly FederatedShareProvider $federatedShareProvider, + private readonly AddressHandler $addressHandler, + private readonly IUserManager $userManager, + private readonly IManager $shareManager, + private readonly ICloudIdManager $cloudIdManager, + private readonly IActivityManager $activityManager, + private readonly INotificationManager $notificationManager, + private readonly IURLGenerator $urlGenerator, + private readonly ICloudFederationFactory $cloudFederationFactory, + private readonly ICloudFederationProviderManager $cloudFederationProviderManager, + private readonly IGroupManager $groupManager, + private readonly IConfig $config, + private readonly Manager $externalShareManager, + private readonly LoggerInterface $logger, + private readonly IFilenameValidator $filenameValidator, private readonly IProviderFactory $shareProviderFactory, private readonly SetupManager $setupManager, private readonly IGenerator $snowflakeGenerator, + private readonly ExternalShareMapper $externalShareMapper, ) { } + #[Override] public function getShareType(): string { return 'file'; } - /** - * share received from another server - * - * @param ICloudFederationShare $share - * @return string provider specific unique ID of the share - * - * @throws ProviderCouldNotAddShareException - * @throws QueryException - * @throws HintException - * @since 14.0.0 - */ + #[Override] public function shareReceived(ICloudFederationShare $share): string { if (!$this->isS2SEnabled(true)) { throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE); @@ -132,9 +119,9 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST); } - $userOrGroup = null; + $user = null; + $group = null; - // FIXME this should be a method in the user management instead if ($shareType === IShare::TYPE_USER) { $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']); Util::emitHook( @@ -144,17 +131,15 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { ); $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']); - $userOrGroup = $this->userManager->get($shareWith); - if ($userOrGroup === null) { + $user = $this->userManager->get($shareWith); + if ($user === null) { throw new ProviderCouldNotAddShareException('User does not exists', '', Http::STATUS_BAD_REQUEST); } - $this->setupManager->setupForUser($userOrGroup); - } - - if ($shareType === IShare::TYPE_GROUP) { - $userOrGroup = $this->groupManager->get($shareWith); - if ($userOrGroup === null) { + $this->setupManager->setupForUser($user); + } else { + $group = $this->groupManager->get($shareWith); + if ($group === null) { throw new ProviderCouldNotAddShareException('Group does not exists', '', Http::STATUS_BAD_REQUEST); } } @@ -171,7 +156,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { $externalShare->setAccepted(IShare::STATUS_PENDING); try { - $this->externalShareManager->addShare($externalShare, $userOrGroup); + $this->externalShareManager->addShare($externalShare, $user ?: $group); // get DisplayName about the owner of the share $ownerDisplayName = $this->getUserDisplayName($ownerFederatedId); @@ -186,9 +171,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { } } - if ($shareType === IShare::TYPE_USER) { - /** @var IUser $userOrGroup */ $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') ->setType('remote_share') @@ -200,12 +183,10 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { // If auto-accept is enabled, accept the share if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { - /** @var IUser $userOrGroup */ - $this->externalShareManager->acceptShare($externalShare, $userOrGroup); + $this->externalShareManager->acceptShare($externalShare, $user); } } else { - /** @var IGroup $userOrGroup */ - $groupMembers = $userOrGroup->getUsers(); + $groupMembers = $group->getUsers(); foreach ($groupMembers as $user) { $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') @@ -236,56 +217,28 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST); } - /** - * notification received from another server - * - * @param string $notificationType (e.g. SHARE_ACCEPTED) - * @param string $providerId id of the share - * @param array $notification payload of the notification - * @return array data send back to the sender - * - * @throws ActionNotSupportedException - * @throws AuthenticationFailedException - * @throws BadRequestException - * @throws HintException - * @since 14.0.0 - */ - public function notificationReceived($notificationType, $providerId, array $notification) { - switch ($notificationType) { - case 'SHARE_ACCEPTED': - return $this->shareAccepted($providerId, $notification); - case 'SHARE_DECLINED': - return $this->shareDeclined($providerId, $notification); - case 'SHARE_UNSHARED': - return $this->unshare($providerId, $notification); - case 'REQUEST_RESHARE': - return $this->reshareRequested($providerId, $notification); - case 'RESHARE_UNDO': - return $this->undoReshare($providerId, $notification); - case 'RESHARE_CHANGE_PERMISSION': - return $this->updateResharePermissions($providerId, $notification); - } - - - throw new BadRequestException([$notificationType]); + #[Override] + public function notificationReceived(string $notificationType, string $providerId, array $notification): array { + return match ($notificationType) { + 'SHARE_ACCEPTED' => $this->shareAccepted($providerId, $notification), + 'SHARE_DECLINED' => $this->shareDeclined($providerId, $notification), + 'SHARE_UNSHARED' => $this->unshare($providerId, $notification), + 'REQUEST_RESHARE' => $this->reshareRequested($providerId, $notification), + 'RESHARE_UNDO' => $this->undoReshare($providerId, $notification), + 'RESHARE_CHANGE_PERMISSION' => $this->updateResharePermissions($providerId, $notification), + default => throw new BadRequestException([$notificationType]), + }; } /** - * map OCM share type (strings) to Nextcloud internal share types (integer) - * - * @param string $shareType - * @return int + * Map OCM share type (strings) to Nextcloud internal share types (integer) + * @return IShare::TYPE_GROUP|IShare::TYPE_USER */ - private function mapShareTypeToNextcloud($shareType) { - $result = IShare::TYPE_USER; - if ($shareType === 'group') { - $result = IShare::TYPE_GROUP; - } - - return $result; + private function mapShareTypeToNextcloud(string $shareType): int { + return $shareType === 'group' ? IShare::TYPE_GROUP : IShare::TYPE_USER; } - private function notifyAboutNewShare($shareWith, string $shareId, $ownerFederatedId, $sharedByFederatedId, string $name, string $displayName): void { + private function notifyAboutNewShare(string $shareWith, string $shareId, $ownerFederatedId, $sharedByFederatedId, string $name, string $displayName): void { $notification = $this->notificationManager->createNotification(); $notification->setApp('files_sharing') ->setUser($shareWith) @@ -309,15 +262,14 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * process notification that the recipient accepted a share * - * @param string $id - * @param array $notification + * @param array{sharedSecret?: string} $notification * @return array * @throws ActionNotSupportedException * @throws AuthenticationFailedException * @throws BadRequestException * @throws HintException */ - private function shareAccepted($id, array $notification) { + private function shareAccepted(string $id, array $notification): array { if (!$this->isS2SEnabled()) { throw new ActionNotSupportedException('Server does not support federated cloud sharing'); } @@ -356,21 +308,22 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { } /** - * @param IShare $share * @throws ShareNotFound */ - protected function executeAcceptShare(IShare $share) { + protected function executeAcceptShare(IShare $share): void { + $user = $this->getCorrectUser($share); + try { - $fileId = (int)$share->getNode()->getId(); - [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId); - } catch (\Exception $e) { + $fileId = $share->getNode()->getId(); + [$file, $link] = $this->getFile($user, $fileId); + } catch (\Exception) { throw new ShareNotFound(); } $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') ->setType('remote_share') - ->setAffectedUser($this->getCorrectUid($share)) + ->setAffectedUser($user->getUID()) ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]]) ->setObject('files', $fileId, $file) ->setLink($link); @@ -380,8 +333,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * process notification that the recipient declined a share * - * @param string $id - * @param array $notification + * @param array{sharedSecret?: string} $notification * @return array * @throws ActionNotSupportedException * @throws AuthenticationFailedException @@ -390,7 +342,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { * @throws HintException * */ - protected function shareDeclined($id, array $notification) { + protected function shareDeclined(string $id, array $notification): array { if (!$this->isS2SEnabled()) { throw new ActionNotSupportedException('Server does not support federated cloud sharing'); } @@ -417,7 +369,6 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { 'sharedSecret' => $token, 'message' => 'Recipient declined the re-share' ] - ); $this->cloudFederationProviderManager->sendNotification($remote, $notification); } @@ -430,23 +381,24 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * delete declined share and create a activity * - * @param IShare $share * @throws ShareNotFound */ - protected function executeDeclineShare(IShare $share) { + protected function executeDeclineShare(IShare $share): void { $this->federatedShareProvider->removeShareFromTable($share); + $user = $this->getCorrectUser($share); + try { - $fileId = (int)$share->getNode()->getId(); - [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId); - } catch (\Exception $e) { + $fileId = $share->getNode()->getId(); + [$file, $link] = $this->getFile($user, $fileId); + } catch (\Exception) { throw new ShareNotFound(); } $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') ->setType('remote_share') - ->setAffectedUser($this->getCorrectUid($share)) + ->setAffectedUser($user->getUID()) ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]]) ->setObject('files', $fileId, $file) ->setLink($link); @@ -456,13 +408,12 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * received the notification that the owner unshared a file from you * - * @param string $id - * @param array $notification + * @param array{sharedSecret?: string} $notification * @return array * @throws AuthenticationFailedException * @throws BadRequestException */ - private function undoReshare($id, array $notification) { + private function undoReshare(string $id, array $notification): array { if (!isset($notification['sharedSecret'])) { throw new BadRequestException(['sharedSecret']); } @@ -478,13 +429,12 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * unshare file from self * - * @param string $id - * @param array $notification + * @param array{sharedSecret?: string} $notification * @return array * @throws ActionNotSupportedException * @throws BadRequestException */ - private function unshare($id, array $notification) { + private function unshare(string $id, array $notification): array { if (!$this->isS2SEnabled(true)) { throw new ActionNotSupportedException('incoming shares disabled!'); } @@ -494,56 +444,29 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { } $token = $notification['sharedSecret']; - $qb = $this->connection->getQueryBuilder(); - $qb->select('*') - ->from('share_external') - ->where( - $qb->expr()->andX( - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) - ) - ); + $share = $this->externalShareMapper->getShareByRemoteIdAndToken($id, $token); - $result = $qb->executeQuery(); - $share = $result->fetchAssociative(); - $result->closeCursor(); + if ($token && $id && $share !== null) { + $remote = $this->cleanupRemote($share->getRemote()); - if ($token && $id && !empty($share)) { - $remote = $this->cleanupRemote($share['remote']); + $owner = $this->cloudIdManager->getCloudId($share->getOwner(), $remote); + $mountpoint = $share->getMountpoint(); + $user = $share->getUser(); - $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote); - $mountpoint = $share['mountpoint']; - $user = $share['user']; - - $qb = $this->connection->getQueryBuilder(); - $qb->delete('share_external') - ->where( - $qb->expr()->andX( - $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), - $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) - ) - ); - - $qb->executeStatement(); - - // delete all child in case of a group share - $qb = $this->connection->getQueryBuilder(); - $qb->delete('share_external') - ->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id']))); - $qb->executeStatement(); + $this->externalShareMapper->delete($share); $ownerDisplayName = $this->getUserDisplayName($owner->getId()); - if ((int)$share['share_type'] === IShare::TYPE_USER) { - if ($share['accepted']) { + if ($share->getShareType() === IShare::TYPE_USER) { + if ($share->getAccepted()) { $path = trim($mountpoint, '/'); } else { - $path = trim($share['name'], '/'); + $path = trim($share->getName(), '/'); } $notification = $this->notificationManager->createNotification(); $notification->setApp('files_sharing') - ->setUser($share['user']) - ->setObject('remote_share', (string)$share['id']); + ->setUser($share->getUser()) + ->setObject('remote_share', $share->getId()); $this->notificationManager->markProcessed($notification); $event = $this->activityManager->generateEvent(); @@ -551,7 +474,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { ->setType('remote_share') ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path, $ownerDisplayName]) ->setAffectedUser($user) - ->setObject('remote_share', (int)$share['id'], $path); + ->setObject('remote_share', $share->getId(), $path); Server::get(IActivityManager::class)->publish($event); } } @@ -559,7 +482,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { return []; } - private function cleanupRemote($remote) { + private function cleanupRemote(string $remote): string { $remote = substr($remote, strpos($remote, '://') + 3); return rtrim($remote, '/'); @@ -568,15 +491,14 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * recipient of a share request to re-share the file with another user * - * @param string $id - * @param array $notification + * @param array{sharedSecret?: string, shareWith?: string, senderId?: string} $notification * @return array * @throws AuthenticationFailedException * @throws BadRequestException * @throws ProviderCouldNotAddShareException * @throws ShareNotFound */ - protected function reshareRequested($id, array $notification) { + protected function reshareRequested(string $id, array $notification) { if (!isset($notification['sharedSecret'])) { throw new BadRequestException(['sharedSecret']); } @@ -626,71 +548,22 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { } /** - * update permission of a re-share so that the share dialog shows the right + * Update permission of a re-share so that the share dialog shows the right * permission if the owner or the sender changes the permission * - * @param string $id - * @param array $notification - * @return array + * @return string[] * @throws AuthenticationFailedException * @throws BadRequestException */ - protected function updateResharePermissions($id, array $notification) { + protected function updateResharePermissions(string $id, array $notification): array { throw new HintException('Updating reshares not allowed'); } /** - * translate OCM Permissions to Nextcloud permissions - * - * @param array $ocmPermissions - * @return int - * @throws BadRequestException + * @return list{?string, string} with internal path of the file and a absolute link to it */ - protected function ocmPermissions2ncPermissions(array $ocmPermissions) { - $ncPermissions = 0; - foreach ($ocmPermissions as $permission) { - switch (strtolower($permission)) { - case 'read': - $ncPermissions += Constants::PERMISSION_READ; - break; - case 'write': - $ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE; - break; - case 'share': - $ncPermissions += Constants::PERMISSION_SHARE; - break; - default: - throw new BadRequestException(['permission']); - } - } - - return $ncPermissions; - } - - /** - * update permissions in database - * - * @param IShare $share - * @param int $permissions - */ - protected function updatePermissionsInDatabase(IShare $share, $permissions) { - $query = $this->connection->getQueryBuilder(); - $query->update('share') - ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) - ->set('permissions', $query->createNamedParameter($permissions)) - ->executeStatement(); - } - - - /** - * get file - * - * @param string $user - * @param int $fileSource - * @return array with internal path of the file and a absolute link to it - */ - private function getFile($user, $fileSource) { - \OC_Util::setupFS($user); + private function getFile(IUser $user, int $fileSource): array { + $this->setupManager->setupForUser($user); try { $file = Filesystem::getPath($fileSource); @@ -704,30 +577,26 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { } /** - * check if we are the initiator or the owner of a re-share and return the correct UID - * - * @param IShare $share - * @return string + * Check if we are the initiator or the owner of a re-share and return the correct UID */ - protected function getCorrectUid(IShare $share) { - if ($this->userManager->userExists($share->getShareOwner())) { - return $share->getShareOwner(); + protected function getCorrectUser(IShare $share): IUser { + if ($user = $this->userManager->get($share->getShareOwner())) { + return $user; } - return $share->getSharedBy(); + $user = $this->userManager->get($share->getSharedBy()); + if ($user === null) { + throw new \RuntimeException('Neither the share owner or the share initiator exist'); + } + return $user; } - - /** * check if we got the right share * - * @param IShare $share - * @param string $token - * @return bool * @throws AuthenticationFailedException */ - protected function verifyShare(IShare $share, $token) { + protected function verifyShare(IShare $share, string $token): bool { if ( $share->getShareType() === IShare::TYPE_REMOTE && $share->getToken() === $token @@ -751,12 +620,9 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** - * check if server-to-server sharing is enabled - * - * @param bool $incoming - * @return bool + * Check if server-to-server sharing is enabled */ - private function isS2SEnabled($incoming = false) { + private function isS2SEnabled(bool $incoming = false): bool { $result = $this->appManager->isEnabledForUser('files_sharing'); if ($incoming) { @@ -768,19 +634,11 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { return $result; } - - /** - * get the supported share types, e.g. "user", "group", etc. - * - * @return array - * - * @since 14.0.0 - */ - public function getSupportedShareTypes() { + #[Override] + public function getSupportedShareTypes(): array { return ['user', 'group']; } - public function getUserDisplayName(string $userId): string { // check if gss is enabled and available if (!$this->appManager->isEnabledForAnyone('globalsiteselector') @@ -801,13 +659,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider { return $slaveService->getUserDisplayName($this->cloudIdManager->removeProtocolFromUrl($userId), false); } - /** - * @inheritDoc - * - * @param string $sharedSecret - * @param array $payload - * @return string - */ + #[Override] public function getFederationIdFromSharedSecret( #[SensitiveParameter] string $sharedSecret, diff --git a/apps/files_sharing/lib/Command/CleanupRemoteStorages.php b/apps/files_sharing/lib/Command/CleanupRemoteStorages.php index 034a4961ae0..1200bfde5af 100644 --- a/apps/files_sharing/lib/Command/CleanupRemoteStorages.php +++ b/apps/files_sharing/lib/Command/CleanupRemoteStorages.php @@ -7,7 +7,6 @@ */ namespace OCA\Files_Sharing\Command; -use OCA\Files_Sharing\External\ExternalShareMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Federation\ICloudIdManager; use OCP\IDBConnection; diff --git a/apps/files_sharing/lib/External/ExternalShare.php b/apps/files_sharing/lib/External/ExternalShare.php index 1177a4c0cdc..db2c08f9daf 100644 --- a/apps/files_sharing/lib/External/ExternalShare.php +++ b/apps/files_sharing/lib/External/ExternalShare.php @@ -86,8 +86,8 @@ class ExternalShare extends Entity implements \JsonSerializable { $this->setter('name', [$name]); } - public function setUserOrGroup(IUser|IGroup|null $userOrGroup): void { - $this->setUser($userOrGroup instanceof IGroup ? $userOrGroup->getGID() : $userOrGroup->getUID()); + public function setShareWith(IUser|IGroup $shareWith): void { + $this->setUser($shareWith instanceof IGroup ? $shareWith->getGID() : $shareWith->getUID()); } /** diff --git a/apps/files_sharing/lib/External/ExternalShareMapper.php b/apps/files_sharing/lib/External/ExternalShareMapper.php index be6855a150f..3349981e84d 100644 --- a/apps/files_sharing/lib/External/ExternalShareMapper.php +++ b/apps/files_sharing/lib/External/ExternalShareMapper.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace OCA\Files_Sharing\External; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -19,6 +20,7 @@ use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\Share\IShare; +use Override; /** * @template-extends QBMapper @@ -214,4 +216,37 @@ class ExternalShareMapper extends QBMapper { $qb->delete('share_external') ->executeStatement(); } + + #[Override] + public function delete(Entity $entity): ExternalShare { + /** @var ExternalShare $share */ + $share = $entity; + $qb = $this->db->getQueryBuilder(); + + $qb->delete(self::TABLE_NAME) + // delete the share itself + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + // delete all child in case of a group share + ->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->executeStatement(); + return $share; + } + + public function getShareByRemoteIdAndToken(string $id, mixed $token): ?ExternalShare { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from(self::TABLE_NAME) + ->where( + $qb->expr()->andX( + $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)), + $qb->expr()->eq('share_token', $qb->createNamedParameter($token)) + ) + ); + + try { + return $this->findEntity($qb); + } catch (Exception) { + return null; + } + } } diff --git a/apps/files_sharing/lib/External/Manager.php b/apps/files_sharing/lib/External/Manager.php index 22adac48d1e..0958626b96d 100644 --- a/apps/files_sharing/lib/External/Manager.php +++ b/apps/files_sharing/lib/External/Manager.php @@ -69,26 +69,26 @@ class Manager { * @throws NotPermittedException * @throws NoUserException */ - public function addShare(ExternalShare $shareExternal, IUser|IGroup|null $userOrGroup = null): ?Mount { - $userOrGroup = $userOrGroup ?? $this->user; + public function addShare(ExternalShare $externalShare, IUser|IGroup|null $shareWith = null): ?Mount { + $shareWith = $shareWith ?? $this->user; - if ($shareExternal->getAccepted() !== IShare::STATUS_ACCEPTED) { + if ($externalShare->getAccepted() !== IShare::STATUS_ACCEPTED) { // To avoid conflicts with the mount point generation later, // we only use a temporary mount point name here. The real // mount point name will be generated when accepting the share, // using the original share item name. - $tmpMountPointName = '{{TemporaryMountPointName#' . $shareExternal->getName() . '}}'; - $shareExternal->setMountpoint($tmpMountPointName); - $shareExternal->setUserOrGroup($userOrGroup); + $tmpMountPointName = '{{TemporaryMountPointName#' . $externalShare->getName() . '}}'; + $externalShare->setMountpoint($tmpMountPointName); + $externalShare->setShareWith($shareWith); $i = 1; while (true) { try { - $this->externalShareMapper->insert($shareExternal); + $this->externalShareMapper->insert($externalShare); break; } catch (Exception $e) { if ($e->getReason() === Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { - $shareExternal->setMountpoint($tmpMountPointName . '-' . $i); + $externalShare->setMountpoint($tmpMountPointName . '-' . $i); $i++; } else { throw $e; @@ -99,22 +99,22 @@ class Manager { return null; } - $user = $userOrGroup instanceof IUser ? $userOrGroup : $this->user; + $user = $shareWith instanceof IUser ? $shareWith : $this->user; $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - $mountPoint = $userFolder->getNonExistingName($shareExternal->getName()); + $mountPoint = $userFolder->getNonExistingName($externalShare->getName()); $mountPoint = Filesystem::normalizePath('/' . $mountPoint); - $shareExternal->setMountpoint($mountPoint); - $shareExternal->setUserOrGroup($user); - $this->externalShareMapper->insert($shareExternal); + $externalShare->setMountpoint($mountPoint); + $externalShare->setShareWith($user); + $this->externalShareMapper->insert($externalShare); $options = [ - 'remote' => $shareExternal->getRemote(), - 'token' => $shareExternal->getShareToken(), - 'password' => $shareExternal->getPassword(), - 'mountpoint' => $shareExternal->getMountpoint(), - 'owner' => $shareExternal->getOwner(), + 'remote' => $externalShare->getRemote(), + 'token' => $externalShare->getShareToken(), + 'password' => $externalShare->getPassword(), + 'mountpoint' => $externalShare->getMountpoint(), + 'owner' => $externalShare->getOwner(), ]; return $this->mountShare($options, $user); } @@ -168,7 +168,7 @@ class Manager { public function getShareByToken(string $token): ExternalShare|false { try { return $this->externalShareMapper->getShareByToken($token); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { return false; } } @@ -184,7 +184,7 @@ class Manager { } else { try { $subShare = $this->externalShareMapper->getUserShare($externalShare, $user); - } catch (DoesNotExistException $e) { + } catch (DoesNotExistException) { $subShare = new ExternalShare(); $subShare->setId($this->snowflakeGenerator->nextId()); $subShare->setRemote($externalShare->getRemote()); diff --git a/apps/files_sharing/lib/External/Mount.php b/apps/files_sharing/lib/External/Mount.php index 6741270ec35..49ca047ffb1 100644 --- a/apps/files_sharing/lib/External/Mount.php +++ b/apps/files_sharing/lib/External/Mount.php @@ -10,23 +10,17 @@ namespace OCA\Files_Sharing\External; use OC\Files\Mount\MountPoint; use OC\Files\Mount\MoveableMount; use OC\Files\Storage\Storage; +use OC\Files\Storage\StorageFactory; use OCA\Files_Sharing\ISharedMountPoint; +use Override; class Mount extends MountPoint implements MoveableMount, ISharedMountPoint { - - /** - * @param string|Storage $storage - * @param string $mountpoint - * @param array $options - * @param \OCA\Files_Sharing\External\Manager $manager - * @param \OC\Files\Storage\StorageFactory $loader - */ public function __construct( - $storage, - $mountpoint, - $options, - protected $manager, - $loader = null, + string|Storage $storage, + string $mountpoint, + array $options, + protected Manager $manager, + ?StorageFactory $loader = null, ) { parent::__construct($storage, $mountpoint, $options, $loader, null, null, MountProvider::class); } @@ -50,12 +44,7 @@ class Mount extends MountPoint implements MoveableMount, ISharedMountPoint { return $this->manager->removeShare($this->mountPoint); } - /** - * Get the type of mount point, used to distinguish things like shares and external storage - * in the web interface - * - * @return string - */ + #[Override] public function getMountType(): string { return 'shared'; } diff --git a/apps/files_sharing/lib/External/MountProvider.php b/apps/files_sharing/lib/External/MountProvider.php index 5a0021db6b7..5e132c22903 100644 --- a/apps/files_sharing/lib/External/MountProvider.php +++ b/apps/files_sharing/lib/External/MountProvider.php @@ -1,5 +1,7 @@ managerProvider = $managerProvider; } - public function getMount(IUser $user, $data, IStorageFactory $storageFactory) { + private function getMount(IUser $user, array $data, IStorageFactory $storageFactory): Mount { $managerProvider = $this->managerProvider; $manager = $managerProvider(); $data['manager'] = $manager; $mountPoint = '/' . $user->getUID() . '/files/' . ltrim($data['mountpoint'], '/'); $data['mountpoint'] = $mountPoint; $data['cloudId'] = $this->cloudIdManager->getCloudId($data['owner'], $data['remote']); - $data['certificateManager'] = \OC::$server->getCertificateManager(); + $data['certificateManager'] = Server::get(ICertificateManager::class); $data['HttpClientService'] = Server::get(IClientService::class); + return new Mount(self::STORAGE, $mountPoint, $data, $manager, $storageFactory); } - public function getMountsForUser(IUser $user, IStorageFactory $loader) { + public function getMountsForUser(IUser $user, IStorageFactory $loader): array { $qb = $this->connection->getQueryBuilder(); - $qb->select('remote', 'share_token', 'password', 'mountpoint', 'owner') + $qb->select('id', 'remote', 'share_token', 'password', 'mountpoint', 'owner') ->from('share_external') ->where($qb->expr()->eq('user', $qb->createNamedParameter($user->getUID()))) ->andWhere($qb->expr()->eq('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED, IQueryBuilder::PARAM_INT))); diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 16e3068b15b..fa1ac5b46e0 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -991,7 +991,7 @@ class ShareByMailProvider extends DefaultShareProvider implements IShareProvider */ protected function createShareObject(array $data): IShare { $share = new Share($this->rootFolder, $this->userManager); - $share->setId((int)$data['id']) + $share->setId($data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) ->setTarget($data['file_target']) diff --git a/build/psalm-baseline.xml b/build/psalm-baseline.xml index 470c0b22122..75ce3d3bde5 100644 --- a/build/psalm-baseline.xml +++ b/build/psalm-baseline.xml @@ -1286,9 +1286,6 @@ - - - - - - - - - @@ -2538,12 +2529,6 @@ - - - getId()]]> - - - @@ -4251,17 +4236,7 @@ dbprettyname]]> - - - getId()]]> - getId()]]> - - - - - - diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 5300e6e1baa..bc74002fcbc 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -989,7 +989,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv */ private function createShare($data) { $share = new Share($this->rootFolder, $this->userManager); - $share->setId((int)$data['id']) + $share->setId($data['id']) ->setShareType((int)$data['share_type']) ->setPermissions((int)$data['permissions']) ->setTarget($data['file_target']) diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index e4e12046052..c76040fccc5 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -117,7 +117,7 @@ interface IShareProvider { /** * Get share by id * - * @param int $id + * @param string $id * @param string|null $recipientId * @return \OCP\Share\IShare * @throws ShareNotFound