diff options
Diffstat (limited to 'apps/federatedfilesharing/lib')
15 files changed, 369 insertions, 367 deletions
diff --git a/apps/federatedfilesharing/lib/AddressHandler.php b/apps/federatedfilesharing/lib/AddressHandler.php index 320f65c2b42..4588e6da288 100644 --- a/apps/federatedfilesharing/lib/AddressHandler.php +++ b/apps/federatedfilesharing/lib/AddressHandler.php @@ -11,6 +11,7 @@ use OCP\Federation\ICloudIdManager; use OCP\HintException; use OCP\IL10N; use OCP\IURLGenerator; +use OCP\Util; /** * Class AddressHandler - parse, modify and construct federated sharing addresses @@ -19,30 +20,18 @@ use OCP\IURLGenerator; */ class AddressHandler { - /** @var IL10N */ - private $l; - - /** @var IURLGenerator */ - private $urlGenerator; - - /** @var ICloudIdManager */ - private $cloudIdManager; - /** * AddressHandler constructor. * * @param IURLGenerator $urlGenerator - * @param IL10N $il10n + * @param IL10N $l * @param ICloudIdManager $cloudIdManager */ public function __construct( - IURLGenerator $urlGenerator, - IL10N $il10n, - ICloudIdManager $cloudIdManager + private IURLGenerator $urlGenerator, + private IL10N $l, + private ICloudIdManager $cloudIdManager, ) { - $this->l = $il10n; - $this->urlGenerator = $urlGenerator; - $this->cloudIdManager = $cloudIdManager; } /** @@ -86,12 +75,12 @@ class AddressHandler { if (rtrim($normalizedServer1, '/') === rtrim($normalizedServer2, '/')) { // FIXME this should be a method in the user management instead - \OCP\Util::emitHook( + Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', ['uid' => &$user1] ); - \OCP\Util::emitHook( + Util::emitHook( '\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', ['uid' => &$user2] @@ -128,8 +117,8 @@ class AddressHandler { * @return bool */ public function urlContainProtocol($url) { - if (str_starts_with($url, 'https://') || - str_starts_with($url, 'http://')) { + if (str_starts_with($url, 'https://') + || str_starts_with($url, 'http://')) { return true; } diff --git a/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php index f72209642ba..9d66cd71812 100644 --- a/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php +++ b/apps/federatedfilesharing/lib/BackgroundJob/RetryJob.php @@ -22,7 +22,6 @@ use OCP\BackgroundJob\Job; */ class RetryJob extends Job { private bool $retainJob = true; - private Notifications $notifications; /** @var int max number of attempts to send the request */ private int $maxTry = 20; @@ -30,10 +29,11 @@ class RetryJob extends Job { /** @var int how much time should be between two tries (10 minutes) */ private int $interval = 600; - public function __construct(Notifications $notifications, - ITimeFactory $time) { + public function __construct( + private Notifications $notifications, + ITimeFactory $time, + ) { parent::__construct($time); - $this->notifications = $notifications; } /** diff --git a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php index e34ee77a550..b8d2090713b 100644 --- a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php +++ b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2016 ownCloud, Inc. @@ -11,7 +12,11 @@ use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\JSONResponse; use OCP\Constants; use OCP\Federation\ICloudIdManager; @@ -32,7 +37,6 @@ use Psr\Log\LoggerInterface; * * @package OCA\FederatedFileSharing\Controller */ -#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] class MountPublicLinkController extends Controller { /** * MountPublicLinkController constructor. @@ -56,17 +60,18 @@ class MountPublicLinkController extends Controller { /** * send federated share to a user of a public link * - * @NoCSRFRequired - * @PublicPage - * @BruteForceProtection(action=publicLink2FederatedShare) - * * @param string $shareWith Username to share with * @param string $token Token of the share * @param string $password Password of the share * @return JSONResponse<Http::STATUS_OK, array{remoteUrl: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}> + * * 200: Remote URL returned * 400: Creating share is not possible */ + #[NoCSRFRequired] + #[PublicPage] + #[BruteForceProtection(action: 'publicLink2FederatedShare')] + #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] public function createFederatedShare($shareWith, $token, $password = '') { if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) { return new JSONResponse( @@ -86,8 +91,8 @@ class MountPublicLinkController extends Controller { // make sure that user is authenticated in case of a password protected link $storedPassword = $share->getPassword(); - $authenticated = $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId() || - $this->shareManager->checkPassword($share, $password); + $authenticated = $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId() + || $this->shareManager->checkPassword($share, $password); if (!empty($storedPassword) && !$authenticated) { $response = new JSONResponse( ['message' => 'No permission to access the share'], @@ -125,8 +130,6 @@ class MountPublicLinkController extends Controller { /** * ask other server to get a federated share * - * @NoAdminRequired - * * @param string $token * @param string $remote * @param string $password @@ -135,6 +138,7 @@ class MountPublicLinkController extends Controller { * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink()) * @return JSONResponse */ + #[NoAdminRequired] public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') { // check if server admin allows to mount public links from other servers if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) { @@ -148,12 +152,11 @@ class MountPublicLinkController extends Controller { try { $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare', [ - 'body' => - [ - 'token' => $token, - 'shareWith' => rtrim($cloudId->getId(), '/'), - 'password' => $password - ], + 'body' => [ + 'token' => $token, + 'shareWith' => rtrim($cloudId->getId(), '/'), + 'password' => $password + ], 'connect_timeout' => 10, ] ); diff --git a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php index c0fc7123a14..7fdd718cbfe 100644 --- a/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php +++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php @@ -12,7 +12,10 @@ use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\Notifications; use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCSController; @@ -23,10 +26,12 @@ use OCP\Federation\Exceptions\ProviderDoesNotExistsException; use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudIdManager; +use OCP\HintException; use OCP\IDBConnection; use OCP\IRequest; use OCP\IUserManager; use OCP\Log\Audit\CriticalActionPerformedEvent; +use OCP\Server; use OCP\Share; use OCP\Share\Exceptions\ShareNotFound; use Psr\Log\LoggerInterface; @@ -34,75 +39,25 @@ use Psr\Log\LoggerInterface; #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)] class RequestHandlerController extends OCSController { - /** @var FederatedShareProvider */ - private $federatedShareProvider; - - /** @var IDBConnection */ - private $connection; - - /** @var Share\IManager */ - private $shareManager; - - /** @var Notifications */ - private $notifications; - - /** @var AddressHandler */ - private $addressHandler; - - /** @var IUserManager */ - private $userManager; - - /** @var string */ - private $shareTable = 'share'; - - /** @var ICloudIdManager */ - private $cloudIdManager; - - /** @var LoggerInterface */ - private $logger; - - /** @var ICloudFederationFactory */ - private $cloudFederationFactory; - - /** @var ICloudFederationProviderManager */ - private $cloudFederationProviderManager; - - /** @var IEventDispatcher */ - private $eventDispatcher; - - public function __construct(string $appName, + public function __construct( + string $appName, IRequest $request, - FederatedShareProvider $federatedShareProvider, - IDBConnection $connection, - Share\IManager $shareManager, - Notifications $notifications, - AddressHandler $addressHandler, - IUserManager $userManager, - ICloudIdManager $cloudIdManager, - LoggerInterface $logger, - ICloudFederationFactory $cloudFederationFactory, - ICloudFederationProviderManager $cloudFederationProviderManager, - IEventDispatcher $eventDispatcher + private FederatedShareProvider $federatedShareProvider, + private IDBConnection $connection, + private Share\IManager $shareManager, + private Notifications $notifications, + private AddressHandler $addressHandler, + private IUserManager $userManager, + private ICloudIdManager $cloudIdManager, + private LoggerInterface $logger, + private ICloudFederationFactory $cloudFederationFactory, + private ICloudFederationProviderManager $cloudFederationProviderManager, + private IEventDispatcher $eventDispatcher, ) { parent::__construct($appName, $request); - - $this->federatedShareProvider = $federatedShareProvider; - $this->connection = $connection; - $this->shareManager = $shareManager; - $this->notifications = $notifications; - $this->addressHandler = $addressHandler; - $this->userManager = $userManager; - $this->cloudIdManager = $cloudIdManager; - $this->logger = $logger; - $this->cloudFederationFactory = $cloudFederationFactory; - $this->cloudFederationProviderManager = $cloudFederationProviderManager; - $this->eventDispatcher = $eventDispatcher; } /** - * @NoCSRFRequired - * @PublicPage - * * create a new share * * @param string|null $remote Address of the remote @@ -114,11 +69,13 @@ class RequestHandlerController extends OCSController { * @param int|null $remoteId ID of the remote * @param string|null $sharedByFederatedId Federated ID of the sender * @param string|null $ownerFederatedId Federated ID of the receiver - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException * * 200: Share created successfully */ + #[NoCSRFRequired] + #[PublicPage] public function createShare( ?string $remote = null, ?string $token = null, @@ -169,13 +126,10 @@ class RequestHandlerController extends OCSController { throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); } - return new Http\DataResponse(); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * create re-share on behalf of another user * * @param int $id ID of the share @@ -188,10 +142,12 @@ class RequestHandlerController extends OCSController { * * 200: Remote share returned */ + #[NoCSRFRequired] + #[PublicPage] public function reShare(int $id, ?string $token = null, ?string $shareWith = null, ?int $remoteId = 0) { - if ($token === null || - $shareWith === null || - $remoteId === null + if ($token === null + || $shareWith === null + || $remoteId === null ) { throw new OCSBadRequestException(); } @@ -206,7 +162,7 @@ class RequestHandlerController extends OCSController { try { $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); [$newToken, $localId] = $provider->notificationReceived('REQUEST_RESHARE', $id, $notification); - return new Http\DataResponse([ + return new DataResponse([ 'token' => $newToken, 'remoteId' => $localId ]); @@ -223,20 +179,19 @@ class RequestHandlerController extends OCSController { /** - * @NoCSRFRequired - * @PublicPage - * * accept server-to-server share * * @param int $id ID of the remote share * @param string|null $token Shared secret between servers - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException * @throws ShareNotFound - * @throws \OCP\HintException + * @throws HintException * * 200: Share accepted successfully */ + #[NoCSRFRequired] + #[PublicPage] public function acceptShare(int $id, ?string $token = null) { $notification = [ 'sharedSecret' => $token, @@ -255,22 +210,21 @@ class RequestHandlerController extends OCSController { $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]); } - return new Http\DataResponse(); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * decline server-to-server share * * @param int $id ID of the remote share * @param string|null $token Shared secret between servers - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException * * 200: Share declined successfully */ + #[NoCSRFRequired] + #[PublicPage] public function declineShare(int $id, ?string $token = null) { $notification = [ 'sharedSecret' => $token, @@ -289,22 +243,21 @@ class RequestHandlerController extends OCSController { $this->logger->debug('internal server error, can not process notification: ' . $e->getMessage(), ['exception' => $e]); } - return new Http\DataResponse(); + return new DataResponse(); } /** - * @NoCSRFRequired - * @PublicPage - * * remove server-to-server share if it was unshared by the owner * * @param int $id ID of the share * @param string|null $token Shared secret between servers - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSException * * 200: Share unshared successfully */ + #[NoCSRFRequired] + #[PublicPage] public function unshare(int $id, ?string $token = null) { if (!$this->isS2SEnabled()) { throw new OCSException('Server does not support federated cloud sharing', 503); @@ -319,7 +272,7 @@ class RequestHandlerController extends OCSController { $this->logger->debug('processing unshare notification failed: ' . $e->getMessage(), ['exception' => $e]); } - return new Http\DataResponse(); + return new DataResponse(); } private function cleanupRemote($remote) { @@ -330,24 +283,23 @@ class RequestHandlerController extends OCSController { /** - * @NoCSRFRequired - * @PublicPage - * * federated share was revoked, either by the owner or the re-sharer * * @param int $id ID of the share * @param string|null $token Shared secret between servers - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSBadRequestException Revoking the share is not possible * * 200: Share revoked successfully */ + #[NoCSRFRequired] + #[PublicPage] public function revoke(int $id, ?string $token = null) { try { $provider = $this->cloudFederationProviderManager->getCloudFederationProvider('file'); $notification = ['sharedSecret' => $token]; $provider->notificationReceived('RESHARE_UNDO', $id, $notification); - return new Http\DataResponse(); + return new DataResponse(); } catch (\Exception $e) { throw new OCSBadRequestException(); } @@ -360,7 +312,7 @@ class RequestHandlerController extends OCSController { * @return bool */ private function isS2SEnabled($incoming = false) { - $result = \OCP\Server::get(IAppManager::class)->isEnabledForUser('files_sharing'); + $result = Server::get(IAppManager::class)->isEnabledForUser('files_sharing'); if ($incoming) { $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled(); @@ -372,19 +324,18 @@ class RequestHandlerController extends OCSController { } /** - * @NoCSRFRequired - * @PublicPage - * * update share information to keep federated re-shares in sync * * @param int $id ID of the share * @param string|null $token Shared secret between servers * @param int|null $permissions New permissions - * @return Http\DataResponse<Http::STATUS_OK, array<empty>, array{}> + * @return Http\DataResponse<Http::STATUS_OK, list<empty>, array{}> * @throws OCSBadRequestException Updating permissions is not possible * * 200: Permissions updated successfully */ + #[NoCSRFRequired] + #[PublicPage] public function updatePermissions(int $id, ?string $token = null, ?int $permissions = null) { $ncPermissions = $permissions; @@ -399,7 +350,7 @@ class RequestHandlerController extends OCSController { throw new OCSBadRequestException(); } - return new Http\DataResponse(); + return new DataResponse(); } /** @@ -419,8 +370,8 @@ class RequestHandlerController extends OCSController { $ocmPermissions[] = 'read'; } - if (($ncPermissions & Constants::PERMISSION_CREATE) || - ($ncPermissions & Constants::PERMISSION_UPDATE)) { + if (($ncPermissions & Constants::PERMISSION_CREATE) + || ($ncPermissions & Constants::PERMISSION_UPDATE)) { $ocmPermissions[] = 'write'; } @@ -428,9 +379,6 @@ class RequestHandlerController extends OCSController { } /** - * @NoCSRFRequired - * @PublicPage - * * change the owner of a server-to-server share * * @param int $id ID of the share @@ -442,12 +390,14 @@ class RequestHandlerController extends OCSController { * * 200: Share moved successfully */ + #[NoCSRFRequired] + #[PublicPage] public function move(int $id, ?string $token = null, ?string $remote = null, ?string $remote_id = null) { if (!$this->isS2SEnabled()) { throw new OCSException('Server does not support federated cloud sharing', 503); } - $newRemoteId = (string) ($remote_id ?? $id); + $newRemoteId = (string)($remote_id ?? $id); $cloudId = $this->cloudIdManager->resolveCloudId($remote); $qb = $this->connection->getQueryBuilder(); @@ -460,7 +410,7 @@ class RequestHandlerController extends OCSController { $affected = $query->executeStatement(); if ($affected > 0) { - return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); + return new DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]); } else { throw new OCSBadRequestException('Share not found or token invalid'); } diff --git a/apps/federatedfilesharing/lib/Events/FederatedShareAddedEvent.php b/apps/federatedfilesharing/lib/Events/FederatedShareAddedEvent.php index 7654262a57a..2a79f434b8c 100644 --- a/apps/federatedfilesharing/lib/Events/FederatedShareAddedEvent.php +++ b/apps/federatedfilesharing/lib/Events/FederatedShareAddedEvent.php @@ -17,14 +17,12 @@ use OCP\EventDispatcher\Event; */ class FederatedShareAddedEvent extends Event { - /** @var string */ - private $remote; - /** * @since 20.0.0 */ - public function __construct(string $remote) { - $this->remote = $remote; + public function __construct( + private string $remote, + ) { } /** diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index eee60f06068..8a2c12e0ac8 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -17,6 +17,7 @@ use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; +use OCP\HintException; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; @@ -25,6 +26,7 @@ use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; +use OCP\Share\IShareProviderSupportsAllSharesInFolder; use Psr\Log\LoggerInterface; /** @@ -32,7 +34,7 @@ use Psr\Log\LoggerInterface; * * @package OCA\FederatedFileSharing */ -class FederatedShareProvider implements IShareProvider { +class FederatedShareProvider implements IShareProvider, IShareProviderSupportsAllSharesInFolder { public const SHARE_TYPE_REMOTE = 6; /** @var string */ @@ -86,8 +88,8 @@ class FederatedShareProvider implements IShareProvider { $shareType = $share->getShareType(); $expirationDate = $share->getExpirationDate(); - if ($shareType === IShare::TYPE_REMOTE_GROUP && - !$this->isOutgoingServer2serverGroupShareEnabled() + if ($shareType === IShare::TYPE_REMOTE_GROUP + && !$this->isOutgoingServer2serverGroupShareEnabled() ) { $message = 'It is not allowed to send federated group shares from this server.'; $message_t = $this->l->t('It is not allowed to send federated group shares from this server.'); @@ -249,7 +251,8 @@ class FederatedShareProvider implements IShareProvider { $remote, $shareWith, $share->getPermissions(), - $share->getNode()->getName() + $share->getNode()->getName(), + $share->getShareType(), ); return [$token, $remoteId]; @@ -267,7 +270,7 @@ class FederatedShareProvider implements IShareProvider { $query->select('*')->from($this->externalShareTable) ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner()))) ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget()))); - $qResult = $query->execute(); + $qResult = $query->executeQuery(); $result = $qResult->fetchAll(); $qResult->closeCursor(); @@ -303,7 +306,7 @@ class FederatedShareProvider implements IShareProvider { ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) ->setValue('permissions', $qb->createNamedParameter($permissions)) - ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATE)) + ->setValue('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->setValue('token', $qb->createNamedParameter($token)) ->setValue('stime', $qb->createNamedParameter(time())); @@ -313,7 +316,7 @@ class FederatedShareProvider implements IShareProvider { */ $qb->setValue('file_target', $qb->createNamedParameter('')); - $qb->execute(); + $qb->executeStatement(); return $qb->getLastInsertId(); } @@ -329,12 +332,12 @@ class FederatedShareProvider implements IShareProvider { */ $qb = $this->dbConnection->getQueryBuilder(); $qb->update('share') - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) - ->execute(); + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATETIME_MUTABLE)) + ->executeStatement(); // send the updated permission to the owner/initiator, if they are not the same if ($share->getShareOwner() !== $share->getSharedBy()) { @@ -349,7 +352,7 @@ class FederatedShareProvider implements IShareProvider { * * @param IShare $share * @throws ShareNotFound - * @throws \OCP\HintException + * @throws HintException */ protected function sendPermissionUpdate(IShare $share) { $remoteId = $this->getRemoteId($share); @@ -374,7 +377,7 @@ class FederatedShareProvider implements IShareProvider { $query->update('share') ->where($query->expr()->eq('id', $query->createNamedParameter($shareId))) ->set('token', $query->createNamedParameter($token)) - ->execute(); + ->executeStatement(); } /** @@ -392,7 +395,7 @@ class FederatedShareProvider implements IShareProvider { 'remote_id' => $query->createNamedParameter($remoteId), ] ); - $query->execute(); + $query->executeStatement(); } /** @@ -406,7 +409,7 @@ class FederatedShareProvider implements IShareProvider { $query = $this->dbConnection->getQueryBuilder(); $query->select('remote_id')->from('federated_reshares') ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId()))); - $result = $query->execute(); + $result = $query->executeQuery(); $data = $result->fetch(); $result->closeCursor(); @@ -428,13 +431,7 @@ class FederatedShareProvider implements IShareProvider { return $share; } - /** - * Get all children of this share - * - * @param IShare $parent - * @return IShare[] - */ - public function getChildren(IShare $parent) { + public function getChildren(IShare $parent): array { $children = []; $qb = $this->dbConnection->getQueryBuilder(); @@ -444,7 +441,7 @@ class FederatedShareProvider implements IShareProvider { ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) ->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { $children[] = $this->createShareObject($data); } @@ -458,7 +455,7 @@ class FederatedShareProvider implements IShareProvider { * * @param IShare $share * @throws ShareNotFound - * @throws \OCP\HintException + * @throws HintException */ public function delete(IShare $share) { [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedWith()); @@ -485,7 +482,7 @@ class FederatedShareProvider implements IShareProvider { * @param IShare $share * @param bool $isOwner the user can either be the owner or the user who re-sahred it * @throws ShareNotFound - * @throws \OCP\HintException + * @throws HintException */ protected function revokeShare($share, $isOwner) { if ($this->userManager->userExists($share->getShareOwner()) && $this->userManager->userExists($share->getSharedBy())) { @@ -524,12 +521,12 @@ class FederatedShareProvider implements IShareProvider { $qb->delete('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))) ->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE))); - $qb->execute(); + $qb->executeStatement(); $qb = $this->dbConnection->getQueryBuilder(); $qb->delete('federated_reshares') ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId))); - $qb->execute(); + $qb->executeStatement(); } /** @@ -549,32 +546,41 @@ class FederatedShareProvider implements IShareProvider { public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) { if (!$shallow) { - throw new \Exception("non-shallow getSharesInFolder is no longer supported"); + throw new \Exception('non-shallow getSharesInFolder is no longer supported'); } + return $this->getSharesInFolderInternal($userId, $node, $reshares); + } + + public function getAllSharesInFolder(Folder $node): array { + return $this->getSharesInFolderInternal(null, $node, null); + } + /** + * @return array<int, list<IShare>> + */ + private function getSharesInFolderInternal(?string $userId, Folder $node, ?bool $reshares): array { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('*') ->from('share', 's') - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )) + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))) ->andWhere( $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE)) ); - /** - * Reshares for this user are shares where they are the owner. - */ - if ($reshares === false) { - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); - } else { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) - ) - ); + if ($userId !== null) { + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares !== true) { + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } } $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); @@ -583,7 +589,7 @@ class FederatedShareProvider implements IShareProvider { $qb->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[$data['fileid']][] = $this->createShareObject($data); @@ -639,7 +645,7 @@ class FederatedShareProvider implements IShareProvider { $qb->setFirstResult($offset); $qb->orderBy('id'); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); @@ -660,7 +666,7 @@ class FederatedShareProvider implements IShareProvider { ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -680,7 +686,7 @@ class FederatedShareProvider implements IShareProvider { /** * Get shares for a given path * - * @param \OCP\Files\Node $path + * @param Node $path * @return IShare[] */ public function getSharesByPath(Node $path) { @@ -691,7 +697,7 @@ class FederatedShareProvider implements IShareProvider { ->from('share') ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) ->andWhere($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) - ->execute(); + ->executeQuery(); $shares = []; while ($data = $cursor->fetch()) { @@ -731,7 +737,7 @@ class FederatedShareProvider implements IShareProvider { $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); } - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { $shares[] = $this->createShareObject($data); @@ -756,7 +762,7 @@ class FederatedShareProvider implements IShareProvider { ->from('share') ->where($qb->expr()->in('share_type', $qb->createNamedParameter($this->supportedShareType, IQueryBuilder::PARAM_INT_ARRAY))) ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) - ->execute(); + ->executeQuery(); $data = $cursor->fetch(); @@ -787,7 +793,7 @@ class FederatedShareProvider implements IShareProvider { ->from('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); $data = $cursor->fetch(); $cursor->closeCursor(); @@ -813,6 +819,7 @@ class FederatedShareProvider implements IShareProvider { ->setPermissions((int)$data['permissions']) ->setTarget($data['file_target']) ->setMailSend((bool)$data['mail_send']) + ->setStatus((int)$data['accepted']) ->setToken($data['token']); $shareTime = new \DateTime(); @@ -850,7 +857,7 @@ class FederatedShareProvider implements IShareProvider { * * @param string $userId * @param int $id - * @return \OCP\Files\Node + * @return Node * @throws InvalidShare */ private function getNode($userId, $id) { @@ -880,126 +887,151 @@ class FederatedShareProvider implements IShareProvider { //TODO: probably a good idea to send unshare info to remote servers $qb = $this->dbConnection->getQueryBuilder(); - $qb->delete('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) - ->execute(); + ->executeStatement(); + + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete('share_external') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) + ->executeStatement(); } - /** - * This provider does not handle groups - * - * @param string $gid - */ public function groupDeleted($gid) { - // We don't handle groups here + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('id') + ->from('share_external') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) + // This is not a typo, the group ID is really stored in the 'user' column + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); + $cursor = $qb->executeQuery(); + $parentShareIds = $cursor->fetchAll(\PDO::FETCH_COLUMN); + $cursor->closeCursor(); + if ($parentShareIds === []) { + return; + } + + $qb = $this->dbConnection->getQueryBuilder(); + $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); + $qb->delete('share_external') + ->where($qb->expr()->in('id', $parentShareIdsParam)) + ->orWhere($qb->expr()->in('parent', $parentShareIdsParam)) + ->executeStatement(); } - /** - * This provider does not handle groups - * - * @param string $uid - * @param string $gid - */ public function userDeletedFromGroup($uid, $gid) { - // We don't handle groups here + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('id') + ->from('share_external') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))) + // This is not a typo, the group ID is really stored in the 'user' column + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($gid))); + $cursor = $qb->executeQuery(); + $parentShareIds = $cursor->fetchAll(\PDO::FETCH_COLUMN); + $cursor->closeCursor(); + if ($parentShareIds === []) { + return; + } + + $qb = $this->dbConnection->getQueryBuilder(); + $parentShareIdsParam = $qb->createNamedParameter($parentShareIds, IQueryBuilder::PARAM_INT_ARRAY); + $qb->delete('share_external') + ->where($qb->expr()->in('parent', $parentShareIdsParam)) + ->andWhere($qb->expr()->eq('user', $qb->createNamedParameter($uid))) + ->executeStatement(); } /** - * check if users from other Nextcloud instances are allowed to mount public links share by this instance - * - * @return bool + * Check if users from other Nextcloud instances are allowed to mount public links share by this instance */ - public function isOutgoingServer2serverShareEnabled() { + public function isOutgoingServer2serverShareEnabled(): bool { if ($this->gsConfig->onlyInternalFederation()) { return false; } $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes'); - return ($result === 'yes'); + return $result === 'yes'; } /** - * check if users are allowed to mount public links from other Nextclouds - * - * @return bool + * Check if users are allowed to mount public links from other Nextclouds */ - public function isIncomingServer2serverShareEnabled() { + public function isIncomingServer2serverShareEnabled(): bool { if ($this->gsConfig->onlyInternalFederation()) { return false; } $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes'); - return ($result === 'yes'); + return $result === 'yes'; } /** - * check if users from other Nextcloud instances are allowed to send federated group shares - * - * @return bool + * Check if users from other Nextcloud instances are allowed to send federated group shares */ - public function isOutgoingServer2serverGroupShareEnabled() { + public function isOutgoingServer2serverGroupShareEnabled(): bool { if ($this->gsConfig->onlyInternalFederation()) { return false; } $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no'); - return ($result === 'yes'); + return $result === 'yes'; } /** - * check if users are allowed to receive federated group shares - * - * @return bool + * Check if users are allowed to receive federated group shares */ - public function isIncomingServer2serverGroupShareEnabled() { + public function isIncomingServer2serverGroupShareEnabled(): bool { if ($this->gsConfig->onlyInternalFederation()) { return false; } $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no'); - return ($result === 'yes'); + return $result === 'yes'; } /** - * check if federated group sharing is supported, therefore the OCM API need to be enabled - * - * @return bool + * Check if federated group sharing is supported, therefore the OCM API need to be enabled */ - public function isFederatedGroupSharingSupported() { + public function isFederatedGroupSharingSupported(): bool { return $this->cloudFederationProviderManager->isReady(); } /** * Check if querying sharees on the lookup server is enabled - * - * @return bool */ - public function isLookupServerQueriesEnabled() { + public function isLookupServerQueriesEnabled(): bool { // in a global scale setup we should always query the lookup server if ($this->gsConfig->isGlobalScaleEnabled()) { return true; } - $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes'); - return ($result === 'yes'); + $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no') === 'yes'; + // TODO: Reenable if lookup server is used again + // return $result; + return false; } /** * Check if it is allowed to publish user specific data to the lookup server - * - * @return bool */ - public function isLookupServerUploadEnabled() { + public function isLookupServerUploadEnabled(): bool { // in a global scale setup the admin is responsible to keep the lookup server up-to-date if ($this->gsConfig->isGlobalScaleEnabled()) { return false; } - $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes'); - return ($result === 'yes'); + $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'no') === 'yes'; + // TODO: Reenable if lookup server is used again + // return $result; + return false; } /** - * @inheritdoc + * Check if auto accepting incoming shares from trusted servers is enabled */ + public function isFederatedTrustedShareAutoAccept(): bool { + $result = $this->config->getAppValue('files_sharing', 'federatedTrustedShareAutoAccept', 'yes'); + return $result === 'yes'; + } + public function getAccessList($nodes, $currentAccess) { $ids = []; foreach ($nodes as $node) { @@ -1011,11 +1043,8 @@ class FederatedShareProvider implements IShareProvider { ->from('share') ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_REMOTE))) ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); - $cursor = $qb->execute(); + ->andWhere($qb->expr()->in('item_type', $qb->createNamedParameter(['file', 'folder'], IQueryBuilder::PARAM_STR_ARRAY))); + $cursor = $qb->executeQuery(); if ($currentAccess === false) { $remote = $cursor->fetch() !== false; @@ -1041,14 +1070,9 @@ class FederatedShareProvider implements IShareProvider { $qb->select('*') ->from('share') - ->where( - $qb->expr()->orX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_REMOTE)), - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_REMOTE_GROUP)) - ) - ); + ->where($qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_REMOTE_GROUP, IShare::TYPE_REMOTE], IQueryBuilder::PARAM_INT_ARRAY))); - $cursor = $qb->execute(); + $cursor = $qb->executeQuery(); while ($data = $cursor->fetch()) { try { $share = $this->createShareObject($data); diff --git a/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php b/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php index 73f0cd34f3f..34fbd85db5a 100644 --- a/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php +++ b/apps/federatedfilesharing/lib/Listeners/LoadAdditionalScriptsListener.php @@ -14,6 +14,7 @@ use OCP\App\IAppManager; use OCP\AppFramework\Services\IInitialState; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; +use OCP\Util; /** @template-implements IEventListener<LoadAdditionalScriptsEvent> */ class LoadAdditionalScriptsListener implements IEventListener { @@ -34,7 +35,7 @@ class LoadAdditionalScriptsListener implements IEventListener { if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled()) { $this->initialState->provideInitialState('notificationsEnabled', $this->appManager->isEnabledForUser('notifications')); - \OCP\Util::addInitScript('federatedfilesharing', 'external'); + Util::addInitScript('federatedfilesharing', 'external'); } } } diff --git a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php index 78517e3a297..e78c93ec1a5 100644 --- a/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php +++ b/apps/federatedfilesharing/lib/Migration/Version1011Date20201120125158.php @@ -18,11 +18,9 @@ use OCP\Migration\SimpleMigrationStep; class Version1011Date20201120125158 extends SimpleMigrationStep { - /** @var IDBConnection */ - private $connection; - - public function __construct(IDBConnection $connection) { - $this->connection = $connection; + public function __construct( + private IDBConnection $connection, + ) { } public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { diff --git a/apps/federatedfilesharing/lib/Notifications.php b/apps/federatedfilesharing/lib/Notifications.php index 25900ca420d..613c05613ef 100644 --- a/apps/federatedfilesharing/lib/Notifications.php +++ b/apps/federatedfilesharing/lib/Notifications.php @@ -13,6 +13,7 @@ use OCP\BackgroundJob\IJobList; use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; +use OCP\HintException; use OCP\Http\Client\IClientService; use OCP\OCS\IDiscoveryService; use Psr\Log\LoggerInterface; @@ -45,7 +46,7 @@ class Notifications { * @param string $sharedByFederatedId * @param int $shareType (can be a remote user or group share) * @return bool - * @throws \OCP\HintException + * @throws HintException * @throws \OC\ServerNotAvailableException */ public function sendRemoteShare($token, $shareWith, $name, $remoteId, $owner, $ownerFederatedId, $sharedBy, $sharedByFederatedId, $shareType) { @@ -104,15 +105,16 @@ class Notifications { * @param int $permission * @param string $filename * @return array|false - * @throws \OCP\HintException + * @throws HintException * @throws \OC\ServerNotAvailableException */ - public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission, $filename) { + public function requestReShare($token, $id, $shareId, $remote, $shareWith, $permission, $filename, $shareType) { $fields = [ 'shareWith' => $shareWith, 'token' => $token, 'permission' => $permission, 'remoteId' => $shareId, + 'shareType' => $shareType, ]; $ocmFields = $fields; @@ -240,10 +242,10 @@ class Notifications { $result = $this->tryHttpPostToShareEndpoint(rtrim($remote, '/'), '/' . $remoteId . '/' . $action, $fields, $action); $status = json_decode($result['result'], true); - if ($result['success'] && - isset($status['ocs']['meta']['statuscode']) && - ($status['ocs']['meta']['statuscode'] === 100 || - $status['ocs']['meta']['statuscode'] === 200 + if ($result['success'] + && isset($status['ocs']['meta']['statuscode']) + && ($status['ocs']['meta']['statuscode'] === 100 + || $status['ocs']['meta']['statuscode'] === 200 ) ) { return true; @@ -286,7 +288,7 @@ class Notifications { * @return array * @throws \Exception */ - protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields, $action = "share") { + protected function tryHttpPostToShareEndpoint($remoteDomain, $urlSuffix, array $fields, $action = 'share') { if ($this->addressHandler->urlContainProtocol($remoteDomain) === false) { $remoteDomain = 'https://' . $remoteDomain; } @@ -396,7 +398,7 @@ class Notifications { $fields['remoteId'], [ 'sharedSecret' => $fields['token'], - 'messgage' => 'file is no longer shared with you' + 'message' => 'file is no longer shared with you' ] ); return $this->federationProviderManager->sendNotification($remoteDomain, $notification); diff --git a/apps/federatedfilesharing/lib/Notifier.php b/apps/federatedfilesharing/lib/Notifier.php index 4c4380d0875..10b57c578a2 100644 --- a/apps/federatedfilesharing/lib/Notifier.php +++ b/apps/federatedfilesharing/lib/Notifier.php @@ -18,16 +18,8 @@ use OCP\Notification\INotifier; use OCP\Notification\UnknownNotificationException; class Notifier implements INotifier { - /** @var IFactory */ - protected $factory; - /** @var IManager */ - protected $contactsManager; - /** @var IURLGenerator */ - protected $url; /** @var array */ protected $federatedContacts; - /** @var ICloudIdManager */ - protected $cloudIdManager; /** * @param IFactory $factory @@ -35,11 +27,12 @@ class Notifier implements INotifier { * @param IURLGenerator $url * @param ICloudIdManager $cloudIdManager */ - public function __construct(IFactory $factory, IManager $contactsManager, IURLGenerator $url, ICloudIdManager $cloudIdManager) { - $this->factory = $factory; - $this->contactsManager = $contactsManager; - $this->url = $url; - $this->cloudIdManager = $cloudIdManager; + public function __construct( + protected IFactory $factory, + protected IManager $contactsManager, + protected IURLGenerator $url, + protected ICloudIdManager $cloudIdManager, + ) { } /** diff --git a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php index 2f41502d4f6..1ce639532e8 100644 --- a/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php +++ b/apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php @@ -1,28 +1,33 @@ <?php + /** * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\FederatedFileSharing\OCM; +use NCU\Federation\ISignedCloudFederationProvider; use OC\AppFramework\Http; use OC\Files\Filesystem; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; +use OCA\Federation\TrustedServers; use OCA\Files_Sharing\Activity\Providers\RemoteShares; use OCA\Files_Sharing\External\Manager; +use OCA\GlobalSiteSelector\Service\SlaveService; 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; use OCP\Federation\Exceptions\BadRequestException; use OCP\Federation\Exceptions\ProviderCouldNotAddShareException; use OCP\Federation\ICloudFederationFactory; -use OCP\Federation\ICloudFederationProvider; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; +use OCP\Files\IFilenameValidator; use OCP\Files\NotFoundException; use OCP\HintException; use OCP\IConfig; @@ -34,11 +39,13 @@ use OCP\Notification\IManager as INotificationManager; use OCP\Server; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; +use OCP\Share\IProviderFactory; use OCP\Share\IShare; use OCP\Util; use Psr\Log\LoggerInterface; +use SensitiveParameter; -class CloudFederationProviderFiles implements ICloudFederationProvider { +class CloudFederationProviderFiles implements ISignedCloudFederationProvider { /** * CloudFederationProvider constructor. */ @@ -59,6 +66,8 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { private IConfig $config, private Manager $externalShareManager, private LoggerInterface $logger, + private IFilenameValidator $filenameValidator, + private readonly IProviderFactory $shareProviderFactory, ) { } @@ -76,7 +85,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { * @return string provider specific unique ID of the share * * @throws ProviderCouldNotAddShareException - * @throws \OCP\AppFramework\QueryException + * @throws QueryException * @throws HintException * @since 14.0.0 */ @@ -115,7 +124,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { } if ($remote && $token && $name && $owner && $remoteId && $shareWith) { - if (!Util::isValidFileName($name)) { + if (!$this->filenameValidator->isFilenameValid($name)) { throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST); } @@ -142,11 +151,22 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { try { $this->externalShareManager->addShare($remote, $token, '', $name, $owner, $shareType, false, $shareWith, $remoteId); - $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external'); + $shareId = Server::get(IDBConnection::class)->lastInsertId('*PREFIX*share_external'); // get DisplayName about the owner of the share $ownerDisplayName = $this->getUserDisplayName($ownerFederatedId); + $trustedServers = null; + if ($this->appManager->isEnabledForAnyone('federation') + && class_exists(TrustedServers::class)) { + try { + $trustedServers = Server::get(TrustedServers::class); + } catch (\Throwable $e) { + $this->logger->debug('Failed to create TrustedServers', ['exception' => $e]); + } + } + + if ($shareType === IShare::TYPE_USER) { $event = $this->activityManager->generateEvent(); $event->setApp('files_sharing') @@ -154,8 +174,13 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($shareWith) ->setObject('remote_share', $shareId, $name); - \OC::$server->getActivityManager()->publish($event); + Server::get(IActivityManager::class)->publish($event); $this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); + + // If auto-accept is enabled, accept the share + if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { + $this->externalShareManager->acceptShare($shareId, $shareWith); + } } else { $groupMembers = $this->groupManager->get($shareWith)->getUsers(); foreach ($groupMembers as $user) { @@ -165,10 +190,16 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName]) ->setAffectedUser($user->getUID()) ->setObject('remote_share', $shareId, $name); - \OC::$server->getActivityManager()->publish($event); + Server::get(IActivityManager::class)->publish($event); $this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName); + + // If auto-accept is enabled, accept the share + if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) { + $this->externalShareManager->acceptShare($shareId, $user->getUID()); + } } } + return $shareId; } catch (\Exception $e) { $this->logger->error('Server can not add remote share.', [ @@ -278,7 +309,10 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { $this->verifyShare($share, $token); $this->executeAcceptShare($share); - if ($share->getShareOwner() !== $share->getSharedBy()) { + + if ($share->getShareOwner() !== $share->getSharedBy() + && !$this->userManager->userExists($share->getSharedBy())) { + // only if share was initiated from another instance [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy()); $remoteId = $this->federatedShareProvider->getRemoteId($share); $notification = $this->cloudFederationFactory->getCloudFederationNotification(); @@ -429,7 +463,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { */ private function unshare($id, array $notification) { if (!$this->isS2SEnabled(true)) { - throw new ActionNotSupportedException("incoming shares disabled!"); + throw new ActionNotSupportedException('incoming shares disabled!'); } if (!isset($notification['sharedSecret'])) { @@ -447,7 +481,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { ) ); - $result = $qb->execute(); + $result = $qb->executeQuery(); $share = $result->fetch(); $result->closeCursor(); @@ -467,13 +501,13 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { ) ); - $qb->execute(); + $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->execute(); + $qb->executeStatement(); $ownerDisplayName = $this->getUserDisplayName($owner->getId()); @@ -486,7 +520,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { $notification = $this->notificationManager->createNotification(); $notification->setApp('files_sharing') ->setUser($share['user']) - ->setObject('remote_share', (int)$share['id']); + ->setObject('remote_share', (string)$share['id']); $this->notificationManager->markProcessed($notification); $event = $this->activityManager->generateEvent(); @@ -495,7 +529,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path, $ownerDisplayName]) ->setAffectedUser($user) ->setObject('remote_share', (int)$share['id'], $path); - \OC::$server->getActivityManager()->publish($event); + Server::get(IActivityManager::class)->publish($event); } } @@ -621,7 +655,7 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { $query->update('share') ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId()))) ->set('permissions', $query->createNamedParameter($permissions)) - ->execute(); + ->executeStatement(); } @@ -672,8 +706,8 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { */ protected function verifyShare(IShare $share, $token) { if ( - $share->getShareType() === IShare::TYPE_REMOTE && - $share->getToken() === $token + $share->getShareType() === IShare::TYPE_REMOTE + && $share->getToken() === $token ) { return true; } @@ -726,13 +760,13 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { public function getUserDisplayName(string $userId): string { // check if gss is enabled and available - if (!$this->appManager->isInstalled('globalsiteselector') + if (!$this->appManager->isEnabledForAnyone('globalsiteselector') || !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) { return ''; } try { - $slaveService = Server::get(\OCA\GlobalSiteSelector\Service\SlaveService::class); + $slaveService = Server::get(SlaveService::class); } catch (\Throwable $e) { Server::get(LoggerInterface::class)->error( $e->getMessage(), @@ -743,4 +777,38 @@ class CloudFederationProviderFiles implements ICloudFederationProvider { return $slaveService->getUserDisplayName($this->cloudIdManager->removeProtocolFromUrl($userId), false); } + + /** + * @inheritDoc + * + * @param string $sharedSecret + * @param array $payload + * @return string + */ + public function getFederationIdFromSharedSecret( + #[SensitiveParameter] + string $sharedSecret, + array $payload, + ): string { + $provider = $this->shareProviderFactory->getProviderForType(IShare::TYPE_REMOTE); + try { + $share = $provider->getShareByToken($sharedSecret); + } catch (ShareNotFound) { + // Maybe we're dealing with a share federated from another server + $share = $this->externalShareManager->getShareByToken($sharedSecret); + if ($share === false) { + return ''; + } + + return $share['user'] . '@' . $share['remote']; + } + + // if uid_owner is a local account, the request comes from the recipient + // if not, request comes from the instance that owns the share and recipient is the re-sharer + if ($this->userManager->get($share->getShareOwner()) !== null) { + return $share->getSharedWith(); + } else { + return $share->getShareOwner(); + } + } } diff --git a/apps/federatedfilesharing/lib/Settings/Admin.php b/apps/federatedfilesharing/lib/Settings/Admin.php index bb41aede1c8..fc685f952c7 100644 --- a/apps/federatedfilesharing/lib/Settings/Admin.php +++ b/apps/federatedfilesharing/lib/Settings/Admin.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later @@ -14,27 +15,16 @@ use OCP\IURLGenerator; use OCP\Settings\IDelegatedSettings; class Admin implements IDelegatedSettings { - private FederatedShareProvider $fedShareProvider; - private IConfig $gsConfig; - private IL10N $l; - private IURLGenerator $urlGenerator; - private IInitialState $initialState; - /** * Admin constructor. */ public function __construct( - FederatedShareProvider $fedShareProvider, - IConfig $globalScaleConfig, - IL10N $l, - IURLGenerator $urlGenerator, - IInitialState $initialState + private FederatedShareProvider $fedShareProvider, + private IConfig $gsConfig, + private IL10N $l, + private IURLGenerator $urlGenerator, + private IInitialState $initialState, ) { - $this->fedShareProvider = $fedShareProvider; - $this->gsConfig = $globalScaleConfig; - $this->l = $l; - $this->urlGenerator = $urlGenerator; - $this->initialState = $initialState; } /** @@ -51,6 +41,7 @@ class Admin implements IDelegatedSettings { $this->initialState->provideInitialState('incomingServer2serverGroupShareEnabled', $this->fedShareProvider->isIncomingServer2serverGroupShareEnabled()); $this->initialState->provideInitialState('lookupServerEnabled', $this->fedShareProvider->isLookupServerQueriesEnabled()); $this->initialState->provideInitialState('lookupServerUploadEnabled', $this->fedShareProvider->isLookupServerUploadEnabled()); + $this->initialState->provideInitialState('federatedTrustedShareAutoAccept', $this->fedShareProvider->isFederatedTrustedShareAutoAccept()); return new TemplateResponse('federatedfilesharing', 'settings-admin', [], ''); } @@ -64,8 +55,8 @@ class Admin implements IDelegatedSettings { /** * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 */ @@ -87,6 +78,7 @@ class Admin implements IDelegatedSettings { 'incomingServer2serverGroupShareEnabled', 'lookupServerEnabled', 'lookupServerUploadEnabled', + 'federatedTrustedShareAutoAccept', ], ]; } diff --git a/apps/federatedfilesharing/lib/Settings/Personal.php b/apps/federatedfilesharing/lib/Settings/Personal.php index 4eee4064740..2889fb77c1f 100644 --- a/apps/federatedfilesharing/lib/Settings/Personal.php +++ b/apps/federatedfilesharing/lib/Settings/Personal.php @@ -17,24 +17,13 @@ use OCP\IUserSession; use OCP\Settings\ISettings; class Personal implements ISettings { - private FederatedShareProvider $federatedShareProvider; - private IUserSession $userSession; - private Defaults $defaults; - private IInitialState $initialState; - private IURLGenerator $urlGenerator; - public function __construct( - FederatedShareProvider $federatedShareProvider, - IUserSession $userSession, - Defaults $defaults, - IInitialState $initialState, - IURLGenerator $urlGenerator + private FederatedShareProvider $federatedShareProvider, + private IUserSession $userSession, + private Defaults $defaults, + private IInitialState $initialState, + private IURLGenerator $urlGenerator, ) { - $this->federatedShareProvider = $federatedShareProvider; - $this->userSession = $userSession; - $this->defaults = $defaults; - $this->initialState = $initialState; - $this->urlGenerator = $urlGenerator; } /** @@ -60,8 +49,8 @@ class Personal implements ISettings { * @since 9.1 */ public function getSection(): ?string { - if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() || - $this->federatedShareProvider->isIncomingServer2serverGroupShareEnabled()) { + if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() + || $this->federatedShareProvider->isIncomingServer2serverGroupShareEnabled()) { return 'sharing'; } return null; @@ -69,8 +58,8 @@ class Personal implements ISettings { /** * @return int whether the form should be rather on the top or bottom of - * the admin section. The forms are arranged in ascending order of the - * priority values. It is required to return a value between 0 and 100. + * the admin section. The forms are arranged in ascending order of the + * priority values. It is required to return a value between 0 and 100. * * E.g.: 70 * @since 9.1 diff --git a/apps/federatedfilesharing/lib/Settings/PersonalSection.php b/apps/federatedfilesharing/lib/Settings/PersonalSection.php index 90f1f8ddee0..eea10e39393 100644 --- a/apps/federatedfilesharing/lib/Settings/PersonalSection.php +++ b/apps/federatedfilesharing/lib/Settings/PersonalSection.php @@ -1,4 +1,5 @@ <?php + /** * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later @@ -10,14 +11,10 @@ use OCP\IURLGenerator; use OCP\Settings\IIconSection; class PersonalSection implements IIconSection { - /** @var IURLGenerator */ - private $urlGenerator; - /** @var IL10N */ - private $l; - - public function __construct(IURLGenerator $urlGenerator, IL10N $l) { - $this->urlGenerator = $urlGenerator; - $this->l = $l; + public function __construct( + private IURLGenerator $urlGenerator, + private IL10N $l, + ) { } /** @@ -55,8 +52,8 @@ class PersonalSection implements IIconSection { /** * @return int whether the form should be rather on the top or bottom of - * the settings navigation. The sections are arranged in ascending order of - * the priority values. It is required to return a value between 0 and 99. + * the settings navigation. The sections are arranged in ascending order of + * the priority values. It is required to return a value between 0 and 99. * * E.g.: 70 * @since 9.1 diff --git a/apps/federatedfilesharing/lib/TokenHandler.php b/apps/federatedfilesharing/lib/TokenHandler.php index e0a6d2cbe8c..0151d12f5d9 100644 --- a/apps/federatedfilesharing/lib/TokenHandler.php +++ b/apps/federatedfilesharing/lib/TokenHandler.php @@ -17,16 +17,14 @@ use OCP\Security\ISecureRandom; class TokenHandler { public const TOKEN_LENGTH = 15; - /** @var ISecureRandom */ - private $secureRandom; - /** * TokenHandler constructor. * * @param ISecureRandom $secureRandom */ - public function __construct(ISecureRandom $secureRandom) { - $this->secureRandom = $secureRandom; + public function __construct( + private ISecureRandom $secureRandom, + ) { } /** |