Completed
Push — master ( cb7cab...263a27 )
by
unknown
51:35 queued 26:15
created
apps/federatedfilesharing/lib/OCM/CloudFederationProviderFiles.php 1 patch
Indentation   +765 added lines, -765 removed lines patch added patch discarded remove patch
@@ -46,769 +46,769 @@
 block discarded – undo
46 46
 use SensitiveParameter;
47 47
 
48 48
 class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
49
-	/**
50
-	 * CloudFederationProvider constructor.
51
-	 */
52
-	public function __construct(
53
-		private IAppManager $appManager,
54
-		private FederatedShareProvider $federatedShareProvider,
55
-		private AddressHandler $addressHandler,
56
-		private IUserManager $userManager,
57
-		private IManager $shareManager,
58
-		private ICloudIdManager $cloudIdManager,
59
-		private IActivityManager $activityManager,
60
-		private INotificationManager $notificationManager,
61
-		private IURLGenerator $urlGenerator,
62
-		private ICloudFederationFactory $cloudFederationFactory,
63
-		private ICloudFederationProviderManager $cloudFederationProviderManager,
64
-		private IDBConnection $connection,
65
-		private IGroupManager $groupManager,
66
-		private IConfig $config,
67
-		private Manager $externalShareManager,
68
-		private LoggerInterface $logger,
69
-		private IFilenameValidator $filenameValidator,
70
-		private readonly IProviderFactory $shareProviderFactory,
71
-	) {
72
-	}
73
-
74
-	/**
75
-	 * @return string
76
-	 */
77
-	public function getShareType() {
78
-		return 'file';
79
-	}
80
-
81
-	/**
82
-	 * share received from another server
83
-	 *
84
-	 * @param ICloudFederationShare $share
85
-	 * @return string provider specific unique ID of the share
86
-	 *
87
-	 * @throws ProviderCouldNotAddShareException
88
-	 * @throws QueryException
89
-	 * @throws HintException
90
-	 * @since 14.0.0
91
-	 */
92
-	public function shareReceived(ICloudFederationShare $share) {
93
-		if (!$this->isS2SEnabled(true)) {
94
-			throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE);
95
-		}
96
-
97
-		$protocol = $share->getProtocol();
98
-		if ($protocol['name'] !== 'webdav') {
99
-			throw new ProviderCouldNotAddShareException('Unsupported protocol for data exchange.', '', Http::STATUS_NOT_IMPLEMENTED);
100
-		}
101
-
102
-		[$ownerUid, $remote] = $this->addressHandler->splitUserRemote($share->getOwner());
103
-		// for backward compatibility make sure that the remote url stored in the
104
-		// database ends with a trailing slash
105
-		if (!str_ends_with($remote, '/')) {
106
-			$remote = $remote . '/';
107
-		}
108
-
109
-		$token = $share->getShareSecret();
110
-		$name = $share->getResourceName();
111
-		$owner = $share->getOwnerDisplayName();
112
-		$sharedBy = $share->getSharedByDisplayName();
113
-		$shareWith = $share->getShareWith();
114
-		$remoteId = $share->getProviderId();
115
-		$sharedByFederatedId = $share->getSharedBy();
116
-		$ownerFederatedId = $share->getOwner();
117
-		$shareType = $this->mapShareTypeToNextcloud($share->getShareType());
118
-
119
-		// if no explicit information about the person who created the share was send
120
-		// we assume that the share comes from the owner
121
-		if ($sharedByFederatedId === null) {
122
-			$sharedBy = $owner;
123
-			$sharedByFederatedId = $ownerFederatedId;
124
-		}
125
-
126
-		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
127
-			if (!$this->filenameValidator->isFilenameValid($name)) {
128
-				throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST);
129
-			}
130
-
131
-			// FIXME this should be a method in the user management instead
132
-			if ($shareType === IShare::TYPE_USER) {
133
-				$this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
134
-				Util::emitHook(
135
-					'\OCA\Files_Sharing\API\Server2Server',
136
-					'preLoginNameUsedAsUserName',
137
-					['uid' => &$shareWith]
138
-				);
139
-				$this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
140
-
141
-				if (!$this->userManager->userExists($shareWith)) {
142
-					throw new ProviderCouldNotAddShareException('User does not exists', '', Http::STATUS_BAD_REQUEST);
143
-				}
144
-
145
-				\OC_Util::setupFS($shareWith);
146
-			}
147
-
148
-			if ($shareType === IShare::TYPE_GROUP && !$this->groupManager->groupExists($shareWith)) {
149
-				throw new ProviderCouldNotAddShareException('Group does not exists', '', Http::STATUS_BAD_REQUEST);
150
-			}
151
-
152
-			try {
153
-				$this->externalShareManager->addShare($remote, $token, '', $name, $owner, $shareType, false, $shareWith, $remoteId);
154
-				$shareId = Server::get(IDBConnection::class)->lastInsertId('*PREFIX*share_external');
155
-
156
-				// get DisplayName about the owner of the share
157
-				$ownerDisplayName = $this->getUserDisplayName($ownerFederatedId);
158
-
159
-				$trustedServers = null;
160
-				if ($this->appManager->isEnabledForAnyone('federation')
161
-					&& class_exists(TrustedServers::class)) {
162
-					try {
163
-						$trustedServers = Server::get(TrustedServers::class);
164
-					} catch (\Throwable $e) {
165
-						$this->logger->debug('Failed to create TrustedServers', ['exception' => $e]);
166
-					}
167
-				}
168
-
169
-
170
-				if ($shareType === IShare::TYPE_USER) {
171
-					$event = $this->activityManager->generateEvent();
172
-					$event->setApp('files_sharing')
173
-						->setType('remote_share')
174
-						->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
175
-						->setAffectedUser($shareWith)
176
-						->setObject('remote_share', $shareId, $name);
177
-					Server::get(IActivityManager::class)->publish($event);
178
-					$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
179
-
180
-					// If auto-accept is enabled, accept the share
181
-					if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) {
182
-						$this->externalShareManager->acceptShare($shareId, $shareWith);
183
-					}
184
-				} else {
185
-					$groupMembers = $this->groupManager->get($shareWith)->getUsers();
186
-					foreach ($groupMembers as $user) {
187
-						$event = $this->activityManager->generateEvent();
188
-						$event->setApp('files_sharing')
189
-							->setType('remote_share')
190
-							->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
191
-							->setAffectedUser($user->getUID())
192
-							->setObject('remote_share', $shareId, $name);
193
-						Server::get(IActivityManager::class)->publish($event);
194
-						$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
195
-
196
-						// If auto-accept is enabled, accept the share
197
-						if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) {
198
-							$this->externalShareManager->acceptShare($shareId, $user->getUID());
199
-						}
200
-					}
201
-				}
202
-
203
-				return $shareId;
204
-			} catch (\Exception $e) {
205
-				$this->logger->error('Server can not add remote share.', [
206
-					'app' => 'files_sharing',
207
-					'exception' => $e,
208
-				]);
209
-				throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR);
210
-			}
211
-		}
212
-
213
-		throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST);
214
-	}
215
-
216
-	/**
217
-	 * notification received from another server
218
-	 *
219
-	 * @param string $notificationType (e.g. SHARE_ACCEPTED)
220
-	 * @param string $providerId id of the share
221
-	 * @param array $notification payload of the notification
222
-	 * @return array<string> data send back to the sender
223
-	 *
224
-	 * @throws ActionNotSupportedException
225
-	 * @throws AuthenticationFailedException
226
-	 * @throws BadRequestException
227
-	 * @throws HintException
228
-	 * @since 14.0.0
229
-	 */
230
-	public function notificationReceived($notificationType, $providerId, array $notification) {
231
-		switch ($notificationType) {
232
-			case 'SHARE_ACCEPTED':
233
-				return $this->shareAccepted($providerId, $notification);
234
-			case 'SHARE_DECLINED':
235
-				return $this->shareDeclined($providerId, $notification);
236
-			case 'SHARE_UNSHARED':
237
-				return $this->unshare($providerId, $notification);
238
-			case 'REQUEST_RESHARE':
239
-				return $this->reshareRequested($providerId, $notification);
240
-			case 'RESHARE_UNDO':
241
-				return $this->undoReshare($providerId, $notification);
242
-			case 'RESHARE_CHANGE_PERMISSION':
243
-				return $this->updateResharePermissions($providerId, $notification);
244
-		}
245
-
246
-
247
-		throw new BadRequestException([$notificationType]);
248
-	}
249
-
250
-	/**
251
-	 * map OCM share type (strings) to Nextcloud internal share types (integer)
252
-	 *
253
-	 * @param string $shareType
254
-	 * @return int
255
-	 */
256
-	private function mapShareTypeToNextcloud($shareType) {
257
-		$result = IShare::TYPE_USER;
258
-		if ($shareType === 'group') {
259
-			$result = IShare::TYPE_GROUP;
260
-		}
261
-
262
-		return $result;
263
-	}
264
-
265
-	private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $displayName): void {
266
-		$notification = $this->notificationManager->createNotification();
267
-		$notification->setApp('files_sharing')
268
-			->setUser($shareWith)
269
-			->setDateTime(new \DateTime())
270
-			->setObject('remote_share', $shareId)
271
-			->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/'), $displayName]);
272
-
273
-		$declineAction = $notification->createAction();
274
-		$declineAction->setLabel('decline')
275
-			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
276
-		$notification->addAction($declineAction);
277
-
278
-		$acceptAction = $notification->createAction();
279
-		$acceptAction->setLabel('accept')
280
-			->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
281
-		$notification->addAction($acceptAction);
282
-
283
-		$this->notificationManager->notify($notification);
284
-	}
285
-
286
-	/**
287
-	 * process notification that the recipient accepted a share
288
-	 *
289
-	 * @param string $id
290
-	 * @param array $notification
291
-	 * @return array<string>
292
-	 * @throws ActionNotSupportedException
293
-	 * @throws AuthenticationFailedException
294
-	 * @throws BadRequestException
295
-	 * @throws HintException
296
-	 */
297
-	private function shareAccepted($id, array $notification) {
298
-		if (!$this->isS2SEnabled()) {
299
-			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
300
-		}
301
-
302
-		if (!isset($notification['sharedSecret'])) {
303
-			throw new BadRequestException(['sharedSecret']);
304
-		}
305
-
306
-		$token = $notification['sharedSecret'];
307
-
308
-		$share = $this->federatedShareProvider->getShareById($id);
309
-
310
-		$this->verifyShare($share, $token);
311
-		$this->executeAcceptShare($share);
312
-
313
-		if ($share->getShareOwner() !== $share->getSharedBy()
314
-			&& !$this->userManager->userExists($share->getSharedBy())) {
315
-			// only if share was initiated from another instance
316
-			[, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
317
-			$remoteId = $this->federatedShareProvider->getRemoteId($share);
318
-			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
319
-			$notification->setMessage(
320
-				'SHARE_ACCEPTED',
321
-				'file',
322
-				$remoteId,
323
-				[
324
-					'sharedSecret' => $token,
325
-					'message' => 'Recipient accepted the re-share'
326
-				]
327
-
328
-			);
329
-			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
330
-		}
331
-
332
-		return [];
333
-	}
334
-
335
-	/**
336
-	 * @param IShare $share
337
-	 * @throws ShareNotFound
338
-	 */
339
-	protected function executeAcceptShare(IShare $share) {
340
-		try {
341
-			$fileId = (int)$share->getNode()->getId();
342
-			[$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
343
-		} catch (\Exception $e) {
344
-			throw new ShareNotFound();
345
-		}
346
-
347
-		$event = $this->activityManager->generateEvent();
348
-		$event->setApp('files_sharing')
349
-			->setType('remote_share')
350
-			->setAffectedUser($this->getCorrectUid($share))
351
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
352
-			->setObject('files', $fileId, $file)
353
-			->setLink($link);
354
-		$this->activityManager->publish($event);
355
-	}
356
-
357
-	/**
358
-	 * process notification that the recipient declined a share
359
-	 *
360
-	 * @param string $id
361
-	 * @param array $notification
362
-	 * @return array<string>
363
-	 * @throws ActionNotSupportedException
364
-	 * @throws AuthenticationFailedException
365
-	 * @throws BadRequestException
366
-	 * @throws ShareNotFound
367
-	 * @throws HintException
368
-	 *
369
-	 */
370
-	protected function shareDeclined($id, array $notification) {
371
-		if (!$this->isS2SEnabled()) {
372
-			throw new ActionNotSupportedException('Server does not support federated cloud sharing');
373
-		}
374
-
375
-		if (!isset($notification['sharedSecret'])) {
376
-			throw new BadRequestException(['sharedSecret']);
377
-		}
378
-
379
-		$token = $notification['sharedSecret'];
380
-
381
-		$share = $this->federatedShareProvider->getShareById($id);
382
-
383
-		$this->verifyShare($share, $token);
384
-
385
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
386
-			[, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
387
-			$remoteId = $this->federatedShareProvider->getRemoteId($share);
388
-			$notification = $this->cloudFederationFactory->getCloudFederationNotification();
389
-			$notification->setMessage(
390
-				'SHARE_DECLINED',
391
-				'file',
392
-				$remoteId,
393
-				[
394
-					'sharedSecret' => $token,
395
-					'message' => 'Recipient declined the re-share'
396
-				]
397
-
398
-			);
399
-			$this->cloudFederationProviderManager->sendNotification($remote, $notification);
400
-		}
401
-
402
-		$this->executeDeclineShare($share);
403
-
404
-		return [];
405
-	}
406
-
407
-	/**
408
-	 * delete declined share and create a activity
409
-	 *
410
-	 * @param IShare $share
411
-	 * @throws ShareNotFound
412
-	 */
413
-	protected function executeDeclineShare(IShare $share) {
414
-		$this->federatedShareProvider->removeShareFromTable($share);
415
-
416
-		try {
417
-			$fileId = (int)$share->getNode()->getId();
418
-			[$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
419
-		} catch (\Exception $e) {
420
-			throw new ShareNotFound();
421
-		}
422
-
423
-		$event = $this->activityManager->generateEvent();
424
-		$event->setApp('files_sharing')
425
-			->setType('remote_share')
426
-			->setAffectedUser($this->getCorrectUid($share))
427
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
428
-			->setObject('files', $fileId, $file)
429
-			->setLink($link);
430
-		$this->activityManager->publish($event);
431
-	}
432
-
433
-	/**
434
-	 * received the notification that the owner unshared a file from you
435
-	 *
436
-	 * @param string $id
437
-	 * @param array $notification
438
-	 * @return array<string>
439
-	 * @throws AuthenticationFailedException
440
-	 * @throws BadRequestException
441
-	 */
442
-	private function undoReshare($id, array $notification) {
443
-		if (!isset($notification['sharedSecret'])) {
444
-			throw new BadRequestException(['sharedSecret']);
445
-		}
446
-		$token = $notification['sharedSecret'];
447
-
448
-		$share = $this->federatedShareProvider->getShareById($id);
449
-
450
-		$this->verifyShare($share, $token);
451
-		$this->federatedShareProvider->removeShareFromTable($share);
452
-		return [];
453
-	}
454
-
455
-	/**
456
-	 * unshare file from self
457
-	 *
458
-	 * @param string $id
459
-	 * @param array $notification
460
-	 * @return array<string>
461
-	 * @throws ActionNotSupportedException
462
-	 * @throws BadRequestException
463
-	 */
464
-	private function unshare($id, array $notification) {
465
-		if (!$this->isS2SEnabled(true)) {
466
-			throw new ActionNotSupportedException('incoming shares disabled!');
467
-		}
468
-
469
-		if (!isset($notification['sharedSecret'])) {
470
-			throw new BadRequestException(['sharedSecret']);
471
-		}
472
-		$token = $notification['sharedSecret'];
473
-
474
-		$qb = $this->connection->getQueryBuilder();
475
-		$qb->select('*')
476
-			->from('share_external')
477
-			->where(
478
-				$qb->expr()->andX(
479
-					$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
480
-					$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
481
-				)
482
-			);
483
-
484
-		$result = $qb->executeQuery();
485
-		$share = $result->fetch();
486
-		$result->closeCursor();
487
-
488
-		if ($token && $id && !empty($share)) {
489
-			$remote = $this->cleanupRemote($share['remote']);
490
-
491
-			$owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
492
-			$mountpoint = $share['mountpoint'];
493
-			$user = $share['user'];
494
-
495
-			$qb = $this->connection->getQueryBuilder();
496
-			$qb->delete('share_external')
497
-				->where(
498
-					$qb->expr()->andX(
499
-						$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
500
-						$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
501
-					)
502
-				);
503
-
504
-			$qb->executeStatement();
505
-
506
-			// delete all child in case of a group share
507
-			$qb = $this->connection->getQueryBuilder();
508
-			$qb->delete('share_external')
509
-				->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
510
-			$qb->executeStatement();
511
-
512
-			$ownerDisplayName = $this->getUserDisplayName($owner->getId());
513
-
514
-			if ((int)$share['share_type'] === IShare::TYPE_USER) {
515
-				if ($share['accepted']) {
516
-					$path = trim($mountpoint, '/');
517
-				} else {
518
-					$path = trim($share['name'], '/');
519
-				}
520
-				$notification = $this->notificationManager->createNotification();
521
-				$notification->setApp('files_sharing')
522
-					->setUser($share['user'])
523
-					->setObject('remote_share', (string)$share['id']);
524
-				$this->notificationManager->markProcessed($notification);
525
-
526
-				$event = $this->activityManager->generateEvent();
527
-				$event->setApp('files_sharing')
528
-					->setType('remote_share')
529
-					->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path, $ownerDisplayName])
530
-					->setAffectedUser($user)
531
-					->setObject('remote_share', (int)$share['id'], $path);
532
-				Server::get(IActivityManager::class)->publish($event);
533
-			}
534
-		}
535
-
536
-		return [];
537
-	}
538
-
539
-	private function cleanupRemote($remote) {
540
-		$remote = substr($remote, strpos($remote, '://') + 3);
541
-
542
-		return rtrim($remote, '/');
543
-	}
544
-
545
-	/**
546
-	 * recipient of a share request to re-share the file with another user
547
-	 *
548
-	 * @param string $id
549
-	 * @param array $notification
550
-	 * @return array<string>
551
-	 * @throws AuthenticationFailedException
552
-	 * @throws BadRequestException
553
-	 * @throws ProviderCouldNotAddShareException
554
-	 * @throws ShareNotFound
555
-	 */
556
-	protected function reshareRequested($id, array $notification) {
557
-		if (!isset($notification['sharedSecret'])) {
558
-			throw new BadRequestException(['sharedSecret']);
559
-		}
560
-		$token = $notification['sharedSecret'];
561
-
562
-		if (!isset($notification['shareWith'])) {
563
-			throw new BadRequestException(['shareWith']);
564
-		}
565
-		$shareWith = $notification['shareWith'];
566
-
567
-		if (!isset($notification['senderId'])) {
568
-			throw new BadRequestException(['senderId']);
569
-		}
570
-		$senderId = $notification['senderId'];
571
-
572
-		$share = $this->federatedShareProvider->getShareById($id);
573
-
574
-		// We have to respect the default share permissions
575
-		$permissions = $share->getPermissions() & (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
576
-		$share->setPermissions($permissions);
577
-
578
-		// don't allow to share a file back to the owner
579
-		try {
580
-			[$user, $remote] = $this->addressHandler->splitUserRemote($shareWith);
581
-			$owner = $share->getShareOwner();
582
-			$currentServer = $this->addressHandler->generateRemoteURL();
583
-			if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
584
-				throw new ProviderCouldNotAddShareException('Resharing back to the owner is not allowed: ' . $id);
585
-			}
586
-		} catch (\Exception $e) {
587
-			throw new ProviderCouldNotAddShareException($e->getMessage());
588
-		}
589
-
590
-		$this->verifyShare($share, $token);
591
-
592
-		// check if re-sharing is allowed
593
-		if ($share->getPermissions() & Constants::PERMISSION_SHARE) {
594
-			// the recipient of the initial share is now the initiator for the re-share
595
-			$share->setSharedBy($share->getSharedWith());
596
-			$share->setSharedWith($shareWith);
597
-			$result = $this->federatedShareProvider->create($share);
598
-			$this->federatedShareProvider->storeRemoteId((int)$result->getId(), $senderId);
599
-			return ['token' => $result->getToken(), 'providerId' => $result->getId()];
600
-		} else {
601
-			throw new ProviderCouldNotAddShareException('resharing not allowed for share: ' . $id);
602
-		}
603
-	}
604
-
605
-	/**
606
-	 * update permission of a re-share so that the share dialog shows the right
607
-	 * permission if the owner or the sender changes the permission
608
-	 *
609
-	 * @param string $id
610
-	 * @param array $notification
611
-	 * @return array<string>
612
-	 * @throws AuthenticationFailedException
613
-	 * @throws BadRequestException
614
-	 */
615
-	protected function updateResharePermissions($id, array $notification) {
616
-		throw new HintException('Updating reshares not allowed');
617
-	}
618
-
619
-	/**
620
-	 * translate OCM Permissions to Nextcloud permissions
621
-	 *
622
-	 * @param array $ocmPermissions
623
-	 * @return int
624
-	 * @throws BadRequestException
625
-	 */
626
-	protected function ocmPermissions2ncPermissions(array $ocmPermissions) {
627
-		$ncPermissions = 0;
628
-		foreach ($ocmPermissions as $permission) {
629
-			switch (strtolower($permission)) {
630
-				case 'read':
631
-					$ncPermissions += Constants::PERMISSION_READ;
632
-					break;
633
-				case 'write':
634
-					$ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
635
-					break;
636
-				case 'share':
637
-					$ncPermissions += Constants::PERMISSION_SHARE;
638
-					break;
639
-				default:
640
-					throw new BadRequestException(['permission']);
641
-			}
642
-		}
643
-
644
-		return $ncPermissions;
645
-	}
646
-
647
-	/**
648
-	 * update permissions in database
649
-	 *
650
-	 * @param IShare $share
651
-	 * @param int $permissions
652
-	 */
653
-	protected function updatePermissionsInDatabase(IShare $share, $permissions) {
654
-		$query = $this->connection->getQueryBuilder();
655
-		$query->update('share')
656
-			->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
657
-			->set('permissions', $query->createNamedParameter($permissions))
658
-			->executeStatement();
659
-	}
660
-
661
-
662
-	/**
663
-	 * get file
664
-	 *
665
-	 * @param string $user
666
-	 * @param int $fileSource
667
-	 * @return array with internal path of the file and a absolute link to it
668
-	 */
669
-	private function getFile($user, $fileSource) {
670
-		\OC_Util::setupFS($user);
671
-
672
-		try {
673
-			$file = Filesystem::getPath($fileSource);
674
-		} catch (NotFoundException $e) {
675
-			$file = null;
676
-		}
677
-		$args = Filesystem::is_dir($file) ? ['dir' => $file] : ['dir' => dirname($file), 'scrollto' => $file];
678
-		$link = Util::linkToAbsolute('files', 'index.php', $args);
679
-
680
-		return [$file, $link];
681
-	}
682
-
683
-	/**
684
-	 * check if we are the initiator or the owner of a re-share and return the correct UID
685
-	 *
686
-	 * @param IShare $share
687
-	 * @return string
688
-	 */
689
-	protected function getCorrectUid(IShare $share) {
690
-		if ($this->userManager->userExists($share->getShareOwner())) {
691
-			return $share->getShareOwner();
692
-		}
693
-
694
-		return $share->getSharedBy();
695
-	}
696
-
697
-
698
-
699
-	/**
700
-	 * check if we got the right share
701
-	 *
702
-	 * @param IShare $share
703
-	 * @param string $token
704
-	 * @return bool
705
-	 * @throws AuthenticationFailedException
706
-	 */
707
-	protected function verifyShare(IShare $share, $token) {
708
-		if (
709
-			$share->getShareType() === IShare::TYPE_REMOTE
710
-			&& $share->getToken() === $token
711
-		) {
712
-			return true;
713
-		}
714
-
715
-		if ($share->getShareType() === IShare::TYPE_CIRCLE) {
716
-			try {
717
-				$knownShare = $this->shareManager->getShareByToken($token);
718
-				if ($knownShare->getId() === $share->getId()) {
719
-					return true;
720
-				}
721
-			} catch (ShareNotFound $e) {
722
-			}
723
-		}
724
-
725
-		throw new AuthenticationFailedException();
726
-	}
727
-
728
-
729
-
730
-	/**
731
-	 * check if server-to-server sharing is enabled
732
-	 *
733
-	 * @param bool $incoming
734
-	 * @return bool
735
-	 */
736
-	private function isS2SEnabled($incoming = false) {
737
-		$result = $this->appManager->isEnabledForUser('files_sharing');
738
-
739
-		if ($incoming) {
740
-			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
741
-		} else {
742
-			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
743
-		}
744
-
745
-		return $result;
746
-	}
747
-
748
-
749
-	/**
750
-	 * get the supported share types, e.g. "user", "group", etc.
751
-	 *
752
-	 * @return array
753
-	 *
754
-	 * @since 14.0.0
755
-	 */
756
-	public function getSupportedShareTypes() {
757
-		return ['user', 'group'];
758
-	}
759
-
760
-
761
-	public function getUserDisplayName(string $userId): string {
762
-		// check if gss is enabled and available
763
-		if (!$this->appManager->isEnabledForAnyone('globalsiteselector')
764
-			|| !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
765
-			return '';
766
-		}
767
-
768
-		try {
769
-			$slaveService = Server::get(SlaveService::class);
770
-		} catch (\Throwable $e) {
771
-			Server::get(LoggerInterface::class)->error(
772
-				$e->getMessage(),
773
-				['exception' => $e]
774
-			);
775
-			return '';
776
-		}
777
-
778
-		return $slaveService->getUserDisplayName($this->cloudIdManager->removeProtocolFromUrl($userId), false);
779
-	}
780
-
781
-	/**
782
-	 * @inheritDoc
783
-	 *
784
-	 * @param string $sharedSecret
785
-	 * @param array $payload
786
-	 * @return string
787
-	 */
788
-	public function getFederationIdFromSharedSecret(
789
-		#[SensitiveParameter]
790
-		string $sharedSecret,
791
-		array $payload,
792
-	): string {
793
-		$provider = $this->shareProviderFactory->getProviderForType(IShare::TYPE_REMOTE);
794
-		try {
795
-			$share = $provider->getShareByToken($sharedSecret);
796
-		} catch (ShareNotFound) {
797
-			// Maybe we're dealing with a share federated from another server
798
-			$share = $this->externalShareManager->getShareByToken($sharedSecret);
799
-			if ($share === false) {
800
-				return '';
801
-			}
802
-
803
-			return $share['user'] . '@' . $share['remote'];
804
-		}
805
-
806
-		// if uid_owner is a local account, the request comes from the recipient
807
-		// if not, request comes from the instance that owns the share and recipient is the re-sharer
808
-		if ($this->userManager->get($share->getShareOwner()) !== null) {
809
-			return $share->getSharedWith();
810
-		} else {
811
-			return $share->getShareOwner();
812
-		}
813
-	}
49
+    /**
50
+     * CloudFederationProvider constructor.
51
+     */
52
+    public function __construct(
53
+        private IAppManager $appManager,
54
+        private FederatedShareProvider $federatedShareProvider,
55
+        private AddressHandler $addressHandler,
56
+        private IUserManager $userManager,
57
+        private IManager $shareManager,
58
+        private ICloudIdManager $cloudIdManager,
59
+        private IActivityManager $activityManager,
60
+        private INotificationManager $notificationManager,
61
+        private IURLGenerator $urlGenerator,
62
+        private ICloudFederationFactory $cloudFederationFactory,
63
+        private ICloudFederationProviderManager $cloudFederationProviderManager,
64
+        private IDBConnection $connection,
65
+        private IGroupManager $groupManager,
66
+        private IConfig $config,
67
+        private Manager $externalShareManager,
68
+        private LoggerInterface $logger,
69
+        private IFilenameValidator $filenameValidator,
70
+        private readonly IProviderFactory $shareProviderFactory,
71
+    ) {
72
+    }
73
+
74
+    /**
75
+     * @return string
76
+     */
77
+    public function getShareType() {
78
+        return 'file';
79
+    }
80
+
81
+    /**
82
+     * share received from another server
83
+     *
84
+     * @param ICloudFederationShare $share
85
+     * @return string provider specific unique ID of the share
86
+     *
87
+     * @throws ProviderCouldNotAddShareException
88
+     * @throws QueryException
89
+     * @throws HintException
90
+     * @since 14.0.0
91
+     */
92
+    public function shareReceived(ICloudFederationShare $share) {
93
+        if (!$this->isS2SEnabled(true)) {
94
+            throw new ProviderCouldNotAddShareException('Server does not support federated cloud sharing', '', Http::STATUS_SERVICE_UNAVAILABLE);
95
+        }
96
+
97
+        $protocol = $share->getProtocol();
98
+        if ($protocol['name'] !== 'webdav') {
99
+            throw new ProviderCouldNotAddShareException('Unsupported protocol for data exchange.', '', Http::STATUS_NOT_IMPLEMENTED);
100
+        }
101
+
102
+        [$ownerUid, $remote] = $this->addressHandler->splitUserRemote($share->getOwner());
103
+        // for backward compatibility make sure that the remote url stored in the
104
+        // database ends with a trailing slash
105
+        if (!str_ends_with($remote, '/')) {
106
+            $remote = $remote . '/';
107
+        }
108
+
109
+        $token = $share->getShareSecret();
110
+        $name = $share->getResourceName();
111
+        $owner = $share->getOwnerDisplayName();
112
+        $sharedBy = $share->getSharedByDisplayName();
113
+        $shareWith = $share->getShareWith();
114
+        $remoteId = $share->getProviderId();
115
+        $sharedByFederatedId = $share->getSharedBy();
116
+        $ownerFederatedId = $share->getOwner();
117
+        $shareType = $this->mapShareTypeToNextcloud($share->getShareType());
118
+
119
+        // if no explicit information about the person who created the share was send
120
+        // we assume that the share comes from the owner
121
+        if ($sharedByFederatedId === null) {
122
+            $sharedBy = $owner;
123
+            $sharedByFederatedId = $ownerFederatedId;
124
+        }
125
+
126
+        if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
127
+            if (!$this->filenameValidator->isFilenameValid($name)) {
128
+                throw new ProviderCouldNotAddShareException('The mountpoint name contains invalid characters.', '', Http::STATUS_BAD_REQUEST);
129
+            }
130
+
131
+            // FIXME this should be a method in the user management instead
132
+            if ($shareType === IShare::TYPE_USER) {
133
+                $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
134
+                Util::emitHook(
135
+                    '\OCA\Files_Sharing\API\Server2Server',
136
+                    'preLoginNameUsedAsUserName',
137
+                    ['uid' => &$shareWith]
138
+                );
139
+                $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
140
+
141
+                if (!$this->userManager->userExists($shareWith)) {
142
+                    throw new ProviderCouldNotAddShareException('User does not exists', '', Http::STATUS_BAD_REQUEST);
143
+                }
144
+
145
+                \OC_Util::setupFS($shareWith);
146
+            }
147
+
148
+            if ($shareType === IShare::TYPE_GROUP && !$this->groupManager->groupExists($shareWith)) {
149
+                throw new ProviderCouldNotAddShareException('Group does not exists', '', Http::STATUS_BAD_REQUEST);
150
+            }
151
+
152
+            try {
153
+                $this->externalShareManager->addShare($remote, $token, '', $name, $owner, $shareType, false, $shareWith, $remoteId);
154
+                $shareId = Server::get(IDBConnection::class)->lastInsertId('*PREFIX*share_external');
155
+
156
+                // get DisplayName about the owner of the share
157
+                $ownerDisplayName = $this->getUserDisplayName($ownerFederatedId);
158
+
159
+                $trustedServers = null;
160
+                if ($this->appManager->isEnabledForAnyone('federation')
161
+                    && class_exists(TrustedServers::class)) {
162
+                    try {
163
+                        $trustedServers = Server::get(TrustedServers::class);
164
+                    } catch (\Throwable $e) {
165
+                        $this->logger->debug('Failed to create TrustedServers', ['exception' => $e]);
166
+                    }
167
+                }
168
+
169
+
170
+                if ($shareType === IShare::TYPE_USER) {
171
+                    $event = $this->activityManager->generateEvent();
172
+                    $event->setApp('files_sharing')
173
+                        ->setType('remote_share')
174
+                        ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
175
+                        ->setAffectedUser($shareWith)
176
+                        ->setObject('remote_share', $shareId, $name);
177
+                    Server::get(IActivityManager::class)->publish($event);
178
+                    $this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
179
+
180
+                    // If auto-accept is enabled, accept the share
181
+                    if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) {
182
+                        $this->externalShareManager->acceptShare($shareId, $shareWith);
183
+                    }
184
+                } else {
185
+                    $groupMembers = $this->groupManager->get($shareWith)->getUsers();
186
+                    foreach ($groupMembers as $user) {
187
+                        $event = $this->activityManager->generateEvent();
188
+                        $event->setApp('files_sharing')
189
+                            ->setType('remote_share')
190
+                            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/'), $ownerDisplayName])
191
+                            ->setAffectedUser($user->getUID())
192
+                            ->setObject('remote_share', $shareId, $name);
193
+                        Server::get(IActivityManager::class)->publish($event);
194
+                        $this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
195
+
196
+                        // If auto-accept is enabled, accept the share
197
+                        if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $trustedServers?->isTrustedServer($remote) === true) {
198
+                            $this->externalShareManager->acceptShare($shareId, $user->getUID());
199
+                        }
200
+                    }
201
+                }
202
+
203
+                return $shareId;
204
+            } catch (\Exception $e) {
205
+                $this->logger->error('Server can not add remote share.', [
206
+                    'app' => 'files_sharing',
207
+                    'exception' => $e,
208
+                ]);
209
+                throw new ProviderCouldNotAddShareException('internal server error, was not able to add share from ' . $remote, '', HTTP::STATUS_INTERNAL_SERVER_ERROR);
210
+            }
211
+        }
212
+
213
+        throw new ProviderCouldNotAddShareException('server can not add remote share, missing parameter', '', HTTP::STATUS_BAD_REQUEST);
214
+    }
215
+
216
+    /**
217
+     * notification received from another server
218
+     *
219
+     * @param string $notificationType (e.g. SHARE_ACCEPTED)
220
+     * @param string $providerId id of the share
221
+     * @param array $notification payload of the notification
222
+     * @return array<string> data send back to the sender
223
+     *
224
+     * @throws ActionNotSupportedException
225
+     * @throws AuthenticationFailedException
226
+     * @throws BadRequestException
227
+     * @throws HintException
228
+     * @since 14.0.0
229
+     */
230
+    public function notificationReceived($notificationType, $providerId, array $notification) {
231
+        switch ($notificationType) {
232
+            case 'SHARE_ACCEPTED':
233
+                return $this->shareAccepted($providerId, $notification);
234
+            case 'SHARE_DECLINED':
235
+                return $this->shareDeclined($providerId, $notification);
236
+            case 'SHARE_UNSHARED':
237
+                return $this->unshare($providerId, $notification);
238
+            case 'REQUEST_RESHARE':
239
+                return $this->reshareRequested($providerId, $notification);
240
+            case 'RESHARE_UNDO':
241
+                return $this->undoReshare($providerId, $notification);
242
+            case 'RESHARE_CHANGE_PERMISSION':
243
+                return $this->updateResharePermissions($providerId, $notification);
244
+        }
245
+
246
+
247
+        throw new BadRequestException([$notificationType]);
248
+    }
249
+
250
+    /**
251
+     * map OCM share type (strings) to Nextcloud internal share types (integer)
252
+     *
253
+     * @param string $shareType
254
+     * @return int
255
+     */
256
+    private function mapShareTypeToNextcloud($shareType) {
257
+        $result = IShare::TYPE_USER;
258
+        if ($shareType === 'group') {
259
+            $result = IShare::TYPE_GROUP;
260
+        }
261
+
262
+        return $result;
263
+    }
264
+
265
+    private function notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $displayName): void {
266
+        $notification = $this->notificationManager->createNotification();
267
+        $notification->setApp('files_sharing')
268
+            ->setUser($shareWith)
269
+            ->setDateTime(new \DateTime())
270
+            ->setObject('remote_share', $shareId)
271
+            ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/'), $displayName]);
272
+
273
+        $declineAction = $notification->createAction();
274
+        $declineAction->setLabel('decline')
275
+            ->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
276
+        $notification->addAction($declineAction);
277
+
278
+        $acceptAction = $notification->createAction();
279
+        $acceptAction->setLabel('accept')
280
+            ->setLink($this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
281
+        $notification->addAction($acceptAction);
282
+
283
+        $this->notificationManager->notify($notification);
284
+    }
285
+
286
+    /**
287
+     * process notification that the recipient accepted a share
288
+     *
289
+     * @param string $id
290
+     * @param array $notification
291
+     * @return array<string>
292
+     * @throws ActionNotSupportedException
293
+     * @throws AuthenticationFailedException
294
+     * @throws BadRequestException
295
+     * @throws HintException
296
+     */
297
+    private function shareAccepted($id, array $notification) {
298
+        if (!$this->isS2SEnabled()) {
299
+            throw new ActionNotSupportedException('Server does not support federated cloud sharing');
300
+        }
301
+
302
+        if (!isset($notification['sharedSecret'])) {
303
+            throw new BadRequestException(['sharedSecret']);
304
+        }
305
+
306
+        $token = $notification['sharedSecret'];
307
+
308
+        $share = $this->federatedShareProvider->getShareById($id);
309
+
310
+        $this->verifyShare($share, $token);
311
+        $this->executeAcceptShare($share);
312
+
313
+        if ($share->getShareOwner() !== $share->getSharedBy()
314
+            && !$this->userManager->userExists($share->getSharedBy())) {
315
+            // only if share was initiated from another instance
316
+            [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
317
+            $remoteId = $this->federatedShareProvider->getRemoteId($share);
318
+            $notification = $this->cloudFederationFactory->getCloudFederationNotification();
319
+            $notification->setMessage(
320
+                'SHARE_ACCEPTED',
321
+                'file',
322
+                $remoteId,
323
+                [
324
+                    'sharedSecret' => $token,
325
+                    'message' => 'Recipient accepted the re-share'
326
+                ]
327
+
328
+            );
329
+            $this->cloudFederationProviderManager->sendNotification($remote, $notification);
330
+        }
331
+
332
+        return [];
333
+    }
334
+
335
+    /**
336
+     * @param IShare $share
337
+     * @throws ShareNotFound
338
+     */
339
+    protected function executeAcceptShare(IShare $share) {
340
+        try {
341
+            $fileId = (int)$share->getNode()->getId();
342
+            [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
343
+        } catch (\Exception $e) {
344
+            throw new ShareNotFound();
345
+        }
346
+
347
+        $event = $this->activityManager->generateEvent();
348
+        $event->setApp('files_sharing')
349
+            ->setType('remote_share')
350
+            ->setAffectedUser($this->getCorrectUid($share))
351
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
352
+            ->setObject('files', $fileId, $file)
353
+            ->setLink($link);
354
+        $this->activityManager->publish($event);
355
+    }
356
+
357
+    /**
358
+     * process notification that the recipient declined a share
359
+     *
360
+     * @param string $id
361
+     * @param array $notification
362
+     * @return array<string>
363
+     * @throws ActionNotSupportedException
364
+     * @throws AuthenticationFailedException
365
+     * @throws BadRequestException
366
+     * @throws ShareNotFound
367
+     * @throws HintException
368
+     *
369
+     */
370
+    protected function shareDeclined($id, array $notification) {
371
+        if (!$this->isS2SEnabled()) {
372
+            throw new ActionNotSupportedException('Server does not support federated cloud sharing');
373
+        }
374
+
375
+        if (!isset($notification['sharedSecret'])) {
376
+            throw new BadRequestException(['sharedSecret']);
377
+        }
378
+
379
+        $token = $notification['sharedSecret'];
380
+
381
+        $share = $this->federatedShareProvider->getShareById($id);
382
+
383
+        $this->verifyShare($share, $token);
384
+
385
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
386
+            [, $remote] = $this->addressHandler->splitUserRemote($share->getSharedBy());
387
+            $remoteId = $this->federatedShareProvider->getRemoteId($share);
388
+            $notification = $this->cloudFederationFactory->getCloudFederationNotification();
389
+            $notification->setMessage(
390
+                'SHARE_DECLINED',
391
+                'file',
392
+                $remoteId,
393
+                [
394
+                    'sharedSecret' => $token,
395
+                    'message' => 'Recipient declined the re-share'
396
+                ]
397
+
398
+            );
399
+            $this->cloudFederationProviderManager->sendNotification($remote, $notification);
400
+        }
401
+
402
+        $this->executeDeclineShare($share);
403
+
404
+        return [];
405
+    }
406
+
407
+    /**
408
+     * delete declined share and create a activity
409
+     *
410
+     * @param IShare $share
411
+     * @throws ShareNotFound
412
+     */
413
+    protected function executeDeclineShare(IShare $share) {
414
+        $this->federatedShareProvider->removeShareFromTable($share);
415
+
416
+        try {
417
+            $fileId = (int)$share->getNode()->getId();
418
+            [$file, $link] = $this->getFile($this->getCorrectUid($share), $fileId);
419
+        } catch (\Exception $e) {
420
+            throw new ShareNotFound();
421
+        }
422
+
423
+        $event = $this->activityManager->generateEvent();
424
+        $event->setApp('files_sharing')
425
+            ->setType('remote_share')
426
+            ->setAffectedUser($this->getCorrectUid($share))
427
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
428
+            ->setObject('files', $fileId, $file)
429
+            ->setLink($link);
430
+        $this->activityManager->publish($event);
431
+    }
432
+
433
+    /**
434
+     * received the notification that the owner unshared a file from you
435
+     *
436
+     * @param string $id
437
+     * @param array $notification
438
+     * @return array<string>
439
+     * @throws AuthenticationFailedException
440
+     * @throws BadRequestException
441
+     */
442
+    private function undoReshare($id, array $notification) {
443
+        if (!isset($notification['sharedSecret'])) {
444
+            throw new BadRequestException(['sharedSecret']);
445
+        }
446
+        $token = $notification['sharedSecret'];
447
+
448
+        $share = $this->federatedShareProvider->getShareById($id);
449
+
450
+        $this->verifyShare($share, $token);
451
+        $this->federatedShareProvider->removeShareFromTable($share);
452
+        return [];
453
+    }
454
+
455
+    /**
456
+     * unshare file from self
457
+     *
458
+     * @param string $id
459
+     * @param array $notification
460
+     * @return array<string>
461
+     * @throws ActionNotSupportedException
462
+     * @throws BadRequestException
463
+     */
464
+    private function unshare($id, array $notification) {
465
+        if (!$this->isS2SEnabled(true)) {
466
+            throw new ActionNotSupportedException('incoming shares disabled!');
467
+        }
468
+
469
+        if (!isset($notification['sharedSecret'])) {
470
+            throw new BadRequestException(['sharedSecret']);
471
+        }
472
+        $token = $notification['sharedSecret'];
473
+
474
+        $qb = $this->connection->getQueryBuilder();
475
+        $qb->select('*')
476
+            ->from('share_external')
477
+            ->where(
478
+                $qb->expr()->andX(
479
+                    $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
480
+                    $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
481
+                )
482
+            );
483
+
484
+        $result = $qb->executeQuery();
485
+        $share = $result->fetch();
486
+        $result->closeCursor();
487
+
488
+        if ($token && $id && !empty($share)) {
489
+            $remote = $this->cleanupRemote($share['remote']);
490
+
491
+            $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
492
+            $mountpoint = $share['mountpoint'];
493
+            $user = $share['user'];
494
+
495
+            $qb = $this->connection->getQueryBuilder();
496
+            $qb->delete('share_external')
497
+                ->where(
498
+                    $qb->expr()->andX(
499
+                        $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
500
+                        $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
501
+                    )
502
+                );
503
+
504
+            $qb->executeStatement();
505
+
506
+            // delete all child in case of a group share
507
+            $qb = $this->connection->getQueryBuilder();
508
+            $qb->delete('share_external')
509
+                ->where($qb->expr()->eq('parent', $qb->createNamedParameter((int)$share['id'])));
510
+            $qb->executeStatement();
511
+
512
+            $ownerDisplayName = $this->getUserDisplayName($owner->getId());
513
+
514
+            if ((int)$share['share_type'] === IShare::TYPE_USER) {
515
+                if ($share['accepted']) {
516
+                    $path = trim($mountpoint, '/');
517
+                } else {
518
+                    $path = trim($share['name'], '/');
519
+                }
520
+                $notification = $this->notificationManager->createNotification();
521
+                $notification->setApp('files_sharing')
522
+                    ->setUser($share['user'])
523
+                    ->setObject('remote_share', (string)$share['id']);
524
+                $this->notificationManager->markProcessed($notification);
525
+
526
+                $event = $this->activityManager->generateEvent();
527
+                $event->setApp('files_sharing')
528
+                    ->setType('remote_share')
529
+                    ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path, $ownerDisplayName])
530
+                    ->setAffectedUser($user)
531
+                    ->setObject('remote_share', (int)$share['id'], $path);
532
+                Server::get(IActivityManager::class)->publish($event);
533
+            }
534
+        }
535
+
536
+        return [];
537
+    }
538
+
539
+    private function cleanupRemote($remote) {
540
+        $remote = substr($remote, strpos($remote, '://') + 3);
541
+
542
+        return rtrim($remote, '/');
543
+    }
544
+
545
+    /**
546
+     * recipient of a share request to re-share the file with another user
547
+     *
548
+     * @param string $id
549
+     * @param array $notification
550
+     * @return array<string>
551
+     * @throws AuthenticationFailedException
552
+     * @throws BadRequestException
553
+     * @throws ProviderCouldNotAddShareException
554
+     * @throws ShareNotFound
555
+     */
556
+    protected function reshareRequested($id, array $notification) {
557
+        if (!isset($notification['sharedSecret'])) {
558
+            throw new BadRequestException(['sharedSecret']);
559
+        }
560
+        $token = $notification['sharedSecret'];
561
+
562
+        if (!isset($notification['shareWith'])) {
563
+            throw new BadRequestException(['shareWith']);
564
+        }
565
+        $shareWith = $notification['shareWith'];
566
+
567
+        if (!isset($notification['senderId'])) {
568
+            throw new BadRequestException(['senderId']);
569
+        }
570
+        $senderId = $notification['senderId'];
571
+
572
+        $share = $this->federatedShareProvider->getShareById($id);
573
+
574
+        // We have to respect the default share permissions
575
+        $permissions = $share->getPermissions() & (int)$this->config->getAppValue('core', 'shareapi_default_permissions', (string)Constants::PERMISSION_ALL);
576
+        $share->setPermissions($permissions);
577
+
578
+        // don't allow to share a file back to the owner
579
+        try {
580
+            [$user, $remote] = $this->addressHandler->splitUserRemote($shareWith);
581
+            $owner = $share->getShareOwner();
582
+            $currentServer = $this->addressHandler->generateRemoteURL();
583
+            if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
584
+                throw new ProviderCouldNotAddShareException('Resharing back to the owner is not allowed: ' . $id);
585
+            }
586
+        } catch (\Exception $e) {
587
+            throw new ProviderCouldNotAddShareException($e->getMessage());
588
+        }
589
+
590
+        $this->verifyShare($share, $token);
591
+
592
+        // check if re-sharing is allowed
593
+        if ($share->getPermissions() & Constants::PERMISSION_SHARE) {
594
+            // the recipient of the initial share is now the initiator for the re-share
595
+            $share->setSharedBy($share->getSharedWith());
596
+            $share->setSharedWith($shareWith);
597
+            $result = $this->federatedShareProvider->create($share);
598
+            $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $senderId);
599
+            return ['token' => $result->getToken(), 'providerId' => $result->getId()];
600
+        } else {
601
+            throw new ProviderCouldNotAddShareException('resharing not allowed for share: ' . $id);
602
+        }
603
+    }
604
+
605
+    /**
606
+     * update permission of a re-share so that the share dialog shows the right
607
+     * permission if the owner or the sender changes the permission
608
+     *
609
+     * @param string $id
610
+     * @param array $notification
611
+     * @return array<string>
612
+     * @throws AuthenticationFailedException
613
+     * @throws BadRequestException
614
+     */
615
+    protected function updateResharePermissions($id, array $notification) {
616
+        throw new HintException('Updating reshares not allowed');
617
+    }
618
+
619
+    /**
620
+     * translate OCM Permissions to Nextcloud permissions
621
+     *
622
+     * @param array $ocmPermissions
623
+     * @return int
624
+     * @throws BadRequestException
625
+     */
626
+    protected function ocmPermissions2ncPermissions(array $ocmPermissions) {
627
+        $ncPermissions = 0;
628
+        foreach ($ocmPermissions as $permission) {
629
+            switch (strtolower($permission)) {
630
+                case 'read':
631
+                    $ncPermissions += Constants::PERMISSION_READ;
632
+                    break;
633
+                case 'write':
634
+                    $ncPermissions += Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE;
635
+                    break;
636
+                case 'share':
637
+                    $ncPermissions += Constants::PERMISSION_SHARE;
638
+                    break;
639
+                default:
640
+                    throw new BadRequestException(['permission']);
641
+            }
642
+        }
643
+
644
+        return $ncPermissions;
645
+    }
646
+
647
+    /**
648
+     * update permissions in database
649
+     *
650
+     * @param IShare $share
651
+     * @param int $permissions
652
+     */
653
+    protected function updatePermissionsInDatabase(IShare $share, $permissions) {
654
+        $query = $this->connection->getQueryBuilder();
655
+        $query->update('share')
656
+            ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
657
+            ->set('permissions', $query->createNamedParameter($permissions))
658
+            ->executeStatement();
659
+    }
660
+
661
+
662
+    /**
663
+     * get file
664
+     *
665
+     * @param string $user
666
+     * @param int $fileSource
667
+     * @return array with internal path of the file and a absolute link to it
668
+     */
669
+    private function getFile($user, $fileSource) {
670
+        \OC_Util::setupFS($user);
671
+
672
+        try {
673
+            $file = Filesystem::getPath($fileSource);
674
+        } catch (NotFoundException $e) {
675
+            $file = null;
676
+        }
677
+        $args = Filesystem::is_dir($file) ? ['dir' => $file] : ['dir' => dirname($file), 'scrollto' => $file];
678
+        $link = Util::linkToAbsolute('files', 'index.php', $args);
679
+
680
+        return [$file, $link];
681
+    }
682
+
683
+    /**
684
+     * check if we are the initiator or the owner of a re-share and return the correct UID
685
+     *
686
+     * @param IShare $share
687
+     * @return string
688
+     */
689
+    protected function getCorrectUid(IShare $share) {
690
+        if ($this->userManager->userExists($share->getShareOwner())) {
691
+            return $share->getShareOwner();
692
+        }
693
+
694
+        return $share->getSharedBy();
695
+    }
696
+
697
+
698
+
699
+    /**
700
+     * check if we got the right share
701
+     *
702
+     * @param IShare $share
703
+     * @param string $token
704
+     * @return bool
705
+     * @throws AuthenticationFailedException
706
+     */
707
+    protected function verifyShare(IShare $share, $token) {
708
+        if (
709
+            $share->getShareType() === IShare::TYPE_REMOTE
710
+            && $share->getToken() === $token
711
+        ) {
712
+            return true;
713
+        }
714
+
715
+        if ($share->getShareType() === IShare::TYPE_CIRCLE) {
716
+            try {
717
+                $knownShare = $this->shareManager->getShareByToken($token);
718
+                if ($knownShare->getId() === $share->getId()) {
719
+                    return true;
720
+                }
721
+            } catch (ShareNotFound $e) {
722
+            }
723
+        }
724
+
725
+        throw new AuthenticationFailedException();
726
+    }
727
+
728
+
729
+
730
+    /**
731
+     * check if server-to-server sharing is enabled
732
+     *
733
+     * @param bool $incoming
734
+     * @return bool
735
+     */
736
+    private function isS2SEnabled($incoming = false) {
737
+        $result = $this->appManager->isEnabledForUser('files_sharing');
738
+
739
+        if ($incoming) {
740
+            $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
741
+        } else {
742
+            $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
743
+        }
744
+
745
+        return $result;
746
+    }
747
+
748
+
749
+    /**
750
+     * get the supported share types, e.g. "user", "group", etc.
751
+     *
752
+     * @return array
753
+     *
754
+     * @since 14.0.0
755
+     */
756
+    public function getSupportedShareTypes() {
757
+        return ['user', 'group'];
758
+    }
759
+
760
+
761
+    public function getUserDisplayName(string $userId): string {
762
+        // check if gss is enabled and available
763
+        if (!$this->appManager->isEnabledForAnyone('globalsiteselector')
764
+            || !class_exists('\OCA\GlobalSiteSelector\Service\SlaveService')) {
765
+            return '';
766
+        }
767
+
768
+        try {
769
+            $slaveService = Server::get(SlaveService::class);
770
+        } catch (\Throwable $e) {
771
+            Server::get(LoggerInterface::class)->error(
772
+                $e->getMessage(),
773
+                ['exception' => $e]
774
+            );
775
+            return '';
776
+        }
777
+
778
+        return $slaveService->getUserDisplayName($this->cloudIdManager->removeProtocolFromUrl($userId), false);
779
+    }
780
+
781
+    /**
782
+     * @inheritDoc
783
+     *
784
+     * @param string $sharedSecret
785
+     * @param array $payload
786
+     * @return string
787
+     */
788
+    public function getFederationIdFromSharedSecret(
789
+        #[SensitiveParameter]
790
+        string $sharedSecret,
791
+        array $payload,
792
+    ): string {
793
+        $provider = $this->shareProviderFactory->getProviderForType(IShare::TYPE_REMOTE);
794
+        try {
795
+            $share = $provider->getShareByToken($sharedSecret);
796
+        } catch (ShareNotFound) {
797
+            // Maybe we're dealing with a share federated from another server
798
+            $share = $this->externalShareManager->getShareByToken($sharedSecret);
799
+            if ($share === false) {
800
+                return '';
801
+            }
802
+
803
+            return $share['user'] . '@' . $share['remote'];
804
+        }
805
+
806
+        // if uid_owner is a local account, the request comes from the recipient
807
+        // if not, request comes from the instance that owns the share and recipient is the re-sharer
808
+        if ($this->userManager->get($share->getShareOwner()) !== null) {
809
+            return $share->getSharedWith();
810
+        } else {
811
+            return $share->getShareOwner();
812
+        }
813
+    }
814 814
 }
Please login to merge, or discard this patch.