Completed
Push — master ( a4086a...810fb7 )
by Björn
66:13 queued 48:44
created

Manager::getAccessList()   D

Complexity

Conditions 14
Paths 218

Size

Total Lines 67
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 41
nc 218
nop 3
dl 0
loc 67
rs 4.969
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author Roeland Jago Douma <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\Share20;
29
30
use OC\Cache\CappedMemoryCache;
31
use OC\Files\Mount\MoveableMount;
32
use OC\HintException;
33
use OC\Share20\Exception\ProviderException;
34
use OCP\Files\File;
35
use OCP\Files\Folder;
36
use OCP\Files\IRootFolder;
37
use OCP\Files\Mount\IMountManager;
38
use OCP\Files\Node;
39
use OCP\Files\NotFoundException;
40
use OCP\IConfig;
41
use OCP\IGroupManager;
42
use OCP\IL10N;
43
use OCP\ILogger;
44
use OCP\IUserManager;
45
use OCP\Security\IHasher;
46
use OCP\Security\ISecureRandom;
47
use OCP\Share\Exceptions\GenericShareException;
48
use OCP\Share\Exceptions\ShareNotFound;
49
use OCP\Share\IManager;
50
use OCP\Share\IProviderFactory;
51
use OCP\Share\IShare;
52
use Symfony\Component\EventDispatcher\EventDispatcher;
53
use Symfony\Component\EventDispatcher\GenericEvent;
54
use OCP\Share\IShareProvider;
55
56
/**
57
 * This class is the communication hub for all sharing related operations.
58
 */
59
class Manager implements IManager {
60
61
	/** @var IProviderFactory */
62
	private $factory;
63
	/** @var ILogger */
64
	private $logger;
65
	/** @var IConfig */
66
	private $config;
67
	/** @var ISecureRandom */
68
	private $secureRandom;
69
	/** @var IHasher */
70
	private $hasher;
71
	/** @var IMountManager */
72
	private $mountManager;
73
	/** @var IGroupManager */
74
	private $groupManager;
75
	/** @var IL10N */
76
	private $l;
77
	/** @var IUserManager */
78
	private $userManager;
79
	/** @var IRootFolder */
80
	private $rootFolder;
81
	/** @var CappedMemoryCache */
82
	private $sharingDisabledForUsersCache;
83
	/** @var EventDispatcher */
84
	private $eventDispatcher;
85
	/** @var LegacyHooks */
86
	private $legacyHooks;
87
88
89
	/**
90
	 * Manager constructor.
91
	 *
92
	 * @param ILogger $logger
93
	 * @param IConfig $config
94
	 * @param ISecureRandom $secureRandom
95
	 * @param IHasher $hasher
96
	 * @param IMountManager $mountManager
97
	 * @param IGroupManager $groupManager
98
	 * @param IL10N $l
99
	 * @param IProviderFactory $factory
100
	 * @param IUserManager $userManager
101
	 * @param IRootFolder $rootFolder
102
	 * @param EventDispatcher $eventDispatcher
103
	 */
104
	public function __construct(
105
			ILogger $logger,
106
			IConfig $config,
107
			ISecureRandom $secureRandom,
108
			IHasher $hasher,
109
			IMountManager $mountManager,
110
			IGroupManager $groupManager,
111
			IL10N $l,
112
			IProviderFactory $factory,
113
			IUserManager $userManager,
114
			IRootFolder $rootFolder,
115
			EventDispatcher $eventDispatcher
116
	) {
117
		$this->logger = $logger;
118
		$this->config = $config;
119
		$this->secureRandom = $secureRandom;
120
		$this->hasher = $hasher;
121
		$this->mountManager = $mountManager;
122
		$this->groupManager = $groupManager;
123
		$this->l = $l;
124
		$this->factory = $factory;
125
		$this->userManager = $userManager;
126
		$this->rootFolder = $rootFolder;
127
		$this->eventDispatcher = $eventDispatcher;
128
		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
129
		$this->legacyHooks = new LegacyHooks($this->eventDispatcher);
130
	}
131
132
	/**
133
	 * Convert from a full share id to a tuple (providerId, shareId)
134
	 *
135
	 * @param string $id
136
	 * @return string[]
137
	 */
138
	private function splitFullId($id) {
139
		return explode(':', $id, 2);
140
	}
141
142
	/**
143
	 * Verify if a password meets all requirements
144
	 *
145
	 * @param string $password
146
	 * @throws \Exception
147
	 */
148
	protected function verifyPassword($password) {
149
		if ($password === null) {
150
			// No password is set, check if this is allowed.
151
			if ($this->shareApiLinkEnforcePassword()) {
152
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
153
			}
154
155
			return;
156
		}
157
158
		// Let others verify the password
159
		try {
160
			$event = new GenericEvent($password);
161
			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
162
		} catch (HintException $e) {
163
			throw new \Exception($e->getHint());
164
		}
165
	}
166
167
	/**
168
	 * Check for generic requirements before creating a share
169
	 *
170
	 * @param \OCP\Share\IShare $share
171
	 * @throws \InvalidArgumentException
172
	 * @throws GenericShareException
173
	 */
174
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
175
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
176
			// We expect a valid user as sharedWith for user shares
177
			if (!$this->userManager->userExists($share->getSharedWith())) {
178
				throw new \InvalidArgumentException('SharedWith is not a valid user');
179
			}
180
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
181
			// We expect a valid group as sharedWith for group shares
182
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
183
				throw new \InvalidArgumentException('SharedWith is not a valid group');
184
			}
185
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
186
			if ($share->getSharedWith() !== null) {
187
				throw new \InvalidArgumentException('SharedWith should be empty');
188
			}
189
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
190
			if ($share->getSharedWith() === null) {
191
				throw new \InvalidArgumentException('SharedWith should not be empty');
192
			}
193
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
194
			if ($share->getSharedWith() === null) {
195
				throw new \InvalidArgumentException('SharedWith should not be empty');
196
			}
197
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
198
			$circle = \OCA\Circles\Api\Circles::detailsCircle($share->getSharedWith());
199
			if ($circle === null) {
200
				throw new \InvalidArgumentException('SharedWith is not a valid circle');
201
			}
202
		} else {
203
			// We can't handle other types yet
204
			throw new \InvalidArgumentException('unknown share type');
205
		}
206
207
		// Verify the initiator of the share is set
208
		if ($share->getSharedBy() === null) {
209
			throw new \InvalidArgumentException('SharedBy should be set');
210
		}
211
212
		// Cannot share with yourself
213 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
214
			$share->getSharedWith() === $share->getSharedBy()) {
215
			throw new \InvalidArgumentException('Can\'t share with yourself');
216
		}
217
218
		// The path should be set
219
		if ($share->getNode() === null) {
220
			throw new \InvalidArgumentException('Path should be set');
221
		}
222
223
		// And it should be a file or a folder
224
		if (!($share->getNode() instanceof \OCP\Files\File) &&
225
				!($share->getNode() instanceof \OCP\Files\Folder)) {
226
			throw new \InvalidArgumentException('Path should be either a file or a folder');
227
		}
228
229
		// And you can't share your rootfolder
230
		if ($this->userManager->userExists($share->getSharedBy())) {
231
			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
232
		} else {
233
			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
234
		}
235
		if ($sharedPath === $share->getNode()->getPath()) {
236
			throw new \InvalidArgumentException('You can\'t share your root folder');
237
		}
238
239
		// Check if we actually have share permissions
240 View Code Duplication
		if (!$share->getNode()->isShareable()) {
241
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
242
			throw new GenericShareException($message_t, $message_t, 404);
243
		}
244
245
		// Permissions should be set
246
		if ($share->getPermissions() === null) {
247
			throw new \InvalidArgumentException('A share requires permissions');
248
		}
249
250
		/*
251
		 * Quick fix for #23536
252
		 * Non moveable mount points do not have update and delete permissions
253
		 * while we 'most likely' do have that on the storage.
254
		 */
255
		$permissions = $share->getNode()->getPermissions();
256
		$mount = $share->getNode()->getMountPoint();
257
		if (!($mount instanceof MoveableMount)) {
258
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
259
		}
260
261
		// Check that we do not share with more permissions than we have
262 View Code Duplication
		if ($share->getPermissions() & ~$permissions) {
263
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
264
			throw new GenericShareException($message_t, $message_t, 404);
265
		}
266
267
268
		// Check that read permissions are always set
269
		// Link shares are allowed to have no read permissions to allow upload to hidden folders
270
		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
271
			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
272
		if (!$noReadPermissionRequired &&
273
			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
274
			throw new \InvalidArgumentException('Shares need at least read permissions');
275
		}
276
277
		if ($share->getNode() instanceof \OCP\Files\File) {
278 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
279
				$message_t = $this->l->t('Files can\'t be shared with delete permissions');
280
				throw new GenericShareException($message_t);
281
			}
282 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
283
				$message_t = $this->l->t('Files can\'t be shared with create permissions');
284
				throw new GenericShareException($message_t);
285
			}
286
		}
287
	}
288
289
	/**
290
	 * Validate if the expiration date fits the system settings
291
	 *
292
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
293
	 * @return \OCP\Share\IShare The modified share object
294
	 * @throws GenericShareException
295
	 * @throws \InvalidArgumentException
296
	 * @throws \Exception
297
	 */
298
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
299
300
		$expirationDate = $share->getExpirationDate();
301
302
		if ($expirationDate !== null) {
303
			//Make sure the expiration date is a date
304
			$expirationDate->setTime(0, 0, 0);
305
306
			$date = new \DateTime();
307
			$date->setTime(0, 0, 0);
308 View Code Duplication
			if ($date >= $expirationDate) {
309
				$message = $this->l->t('Expiration date is in the past');
310
				throw new GenericShareException($message, $message, 404);
311
			}
312
		}
313
314
		// If expiredate is empty set a default one if there is a default
315
		$fullId = null;
316
		try {
317
			$fullId = $share->getFullId();
318
		} catch (\UnexpectedValueException $e) {
319
			// This is a new share
320
		}
321
322
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
323
			$expirationDate = new \DateTime();
324
			$expirationDate->setTime(0,0,0);
325
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
326
		}
327
328
		// If we enforce the expiration date check that is does not exceed
329
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
330
			if ($expirationDate === null) {
331
				throw new \InvalidArgumentException('Expiration date is enforced');
332
			}
333
334
			$date = new \DateTime();
335
			$date->setTime(0, 0, 0);
336
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
337 View Code Duplication
			if ($date < $expirationDate) {
338
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
339
				throw new GenericShareException($message, $message, 404);
340
			}
341
		}
342
343
		$accepted = true;
344
		$message = '';
345
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
346
			'expirationDate' => &$expirationDate,
347
			'accepted' => &$accepted,
348
			'message' => &$message,
349
			'passwordSet' => $share->getPassword() !== null,
350
		]);
351
352
		if (!$accepted) {
353
			throw new \Exception($message);
354
		}
355
356
		$share->setExpirationDate($expirationDate);
357
358
		return $share;
359
	}
360
361
	/**
362
	 * Check for pre share requirements for user shares
363
	 *
364
	 * @param \OCP\Share\IShare $share
365
	 * @throws \Exception
366
	 */
367
	protected function userCreateChecks(\OCP\Share\IShare $share) {
368
		// Check if we can share with group members only
369
		if ($this->shareWithGroupMembersOnly()) {
370
			$sharedBy = $this->userManager->get($share->getSharedBy());
371
			$sharedWith = $this->userManager->get($share->getSharedWith());
372
			// Verify we can share with this user
373
			$groups = array_intersect(
374
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 370 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
375
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) on line 371 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
376
			);
377
			if (empty($groups)) {
378
				throw new \Exception('Only sharing with group members is allowed');
379
			}
380
		}
381
382
		/*
383
		 * TODO: Could be costly, fix
384
		 *
385
		 * Also this is not what we want in the future.. then we want to squash identical shares.
386
		 */
387
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
388
		$existingShares = $provider->getSharesByPath($share->getNode());
389
		foreach($existingShares as $existingShare) {
390
			// Ignore if it is the same share
391
			try {
392
				if ($existingShare->getFullId() === $share->getFullId()) {
393
					continue;
394
				}
395
			} catch (\UnexpectedValueException $e) {
396
				//Shares are not identical
397
			}
398
399
			// Identical share already existst
400
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
401
				throw new \Exception('Path already shared with this user');
402
			}
403
404
			// The share is already shared with this user via a group share
405
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
406
				$group = $this->groupManager->get($existingShare->getSharedWith());
407
				if (!is_null($group)) {
408
					$user = $this->userManager->get($share->getSharedWith());
409
410
					if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) {
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($share->getSharedWith()) on line 408 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
411
						throw new \Exception('Path already shared with this user');
412
					}
413
				}
414
			}
415
		}
416
	}
417
418
	/**
419
	 * Check for pre share requirements for group shares
420
	 *
421
	 * @param \OCP\Share\IShare $share
422
	 * @throws \Exception
423
	 */
424
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
425
		// Verify group shares are allowed
426
		if (!$this->allowGroupSharing()) {
427
			throw new \Exception('Group sharing is now allowed');
428
		}
429
430
		// Verify if the user can share with this group
431
		if ($this->shareWithGroupMembersOnly()) {
432
			$sharedBy = $this->userManager->get($share->getSharedBy());
433
			$sharedWith = $this->groupManager->get($share->getSharedWith());
434
			if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) {
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 432 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
435
				throw new \Exception('Only sharing within your own groups is allowed');
436
			}
437
		}
438
439
		/*
440
		 * TODO: Could be costly, fix
441
		 *
442
		 * Also this is not what we want in the future.. then we want to squash identical shares.
443
		 */
444
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
445
		$existingShares = $provider->getSharesByPath($share->getNode());
446
		foreach($existingShares as $existingShare) {
447
			try {
448
				if ($existingShare->getFullId() === $share->getFullId()) {
449
					continue;
450
				}
451
			} catch (\UnexpectedValueException $e) {
452
				//It is a new share so just continue
453
			}
454
455
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
456
				throw new \Exception('Path already shared with this group');
457
			}
458
		}
459
	}
460
461
	/**
462
	 * Check for pre share requirements for link shares
463
	 *
464
	 * @param \OCP\Share\IShare $share
465
	 * @throws \Exception
466
	 */
467
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
468
		// Are link shares allowed?
469
		if (!$this->shareApiAllowLinks()) {
470
			throw new \Exception('Link sharing not allowed');
471
		}
472
473
		// Link shares by definition can't have share permissions
474
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
475
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
476
		}
477
478
		// Check if public upload is allowed
479
		if (!$this->shareApiLinkAllowPublicUpload() &&
480
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
481
			throw new \InvalidArgumentException('Public upload not allowed');
482
		}
483
	}
484
485
	/**
486
	 * To make sure we don't get invisible link shares we set the parent
487
	 * of a link if it is a reshare. This is a quick word around
488
	 * until we can properly display multiple link shares in the UI
489
	 *
490
	 * See: https://github.com/owncloud/core/issues/22295
491
	 *
492
	 * FIXME: Remove once multiple link shares can be properly displayed
493
	 *
494
	 * @param \OCP\Share\IShare $share
495
	 */
496
	protected function setLinkParent(\OCP\Share\IShare $share) {
497
498
		// No sense in checking if the method is not there.
499
		if (method_exists($share, 'setParent')) {
500
			$storage = $share->getNode()->getStorage();
501
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
502
				/** @var \OCA\Files_Sharing\SharedStorage $storage */
503
				$share->setParent($storage->getShareId());
504
			}
505
		};
506
	}
507
508
	/**
509
	 * @param File|Folder $path
510
	 */
511
	protected function pathCreateChecks($path) {
512
		// Make sure that we do not share a path that contains a shared mountpoint
513
		if ($path instanceof \OCP\Files\Folder) {
514
			$mounts = $this->mountManager->findIn($path->getPath());
515
			foreach($mounts as $mount) {
516
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
517
					throw new \InvalidArgumentException('Path contains files shared with you');
518
				}
519
			}
520
		}
521
	}
522
523
	/**
524
	 * Check if the user that is sharing can actually share
525
	 *
526
	 * @param \OCP\Share\IShare $share
527
	 * @throws \Exception
528
	 */
529
	protected function canShare(\OCP\Share\IShare $share) {
530
		if (!$this->shareApiEnabled()) {
531
			throw new \Exception('The share API is disabled');
532
		}
533
534
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
535
			throw new \Exception('You are not allowed to share');
536
		}
537
	}
538
539
	/**
540
	 * Share a path
541
	 *
542
	 * @param \OCP\Share\IShare $share
543
	 * @return Share The share object
544
	 * @throws \Exception
545
	 *
546
	 * TODO: handle link share permissions or check them
547
	 */
548
	public function createShare(\OCP\Share\IShare $share) {
549
		$this->canShare($share);
550
551
		$this->generalCreateChecks($share);
552
553
		// Verify if there are any issues with the path
554
		$this->pathCreateChecks($share->getNode());
555
556
		/*
557
		 * On creation of a share the owner is always the owner of the path
558
		 * Except for mounted federated shares.
559
		 */
560
		$storage = $share->getNode()->getStorage();
561
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
562
			$parent = $share->getNode()->getParent();
563
			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
564
				$parent = $parent->getParent();
565
			}
566
			$share->setShareOwner($parent->getOwner()->getUID());
567
		} else {
568
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
569
		}
570
571
		//Verify share type
572
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
573
			$this->userCreateChecks($share);
574
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
575
			$this->groupCreateChecks($share);
576
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
577
			$this->linkCreateChecks($share);
578
			$this->setLinkParent($share);
579
580
			/*
581
			 * For now ignore a set token.
582
			 */
583
			$share->setToken(
584
				$this->secureRandom->generate(
585
					\OC\Share\Constants::TOKEN_LENGTH,
586
					\OCP\Security\ISecureRandom::CHAR_LOWER.
587
					\OCP\Security\ISecureRandom::CHAR_UPPER.
588
					\OCP\Security\ISecureRandom::CHAR_DIGITS
589
				)
590
			);
591
592
			//Verify the expiration date
593
			$this->validateExpirationDate($share);
594
595
			//Verify the password
596
			$this->verifyPassword($share->getPassword());
597
598
			// If a password is set. Hash it!
599
			if ($share->getPassword() !== null) {
600
				$share->setPassword($this->hasher->hash($share->getPassword()));
601
			}
602
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
603
			$share->setToken(
604
				$this->secureRandom->generate(
605
					\OC\Share\Constants::TOKEN_LENGTH,
606
					\OCP\Security\ISecureRandom::CHAR_LOWER.
607
					\OCP\Security\ISecureRandom::CHAR_UPPER.
608
					\OCP\Security\ISecureRandom::CHAR_DIGITS
609
				)
610
			);
611
		}
612
613
		// Cannot share with the owner
614 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
615
			$share->getSharedWith() === $share->getShareOwner()) {
616
			throw new \InvalidArgumentException('Can\'t share with the share owner');
617
		}
618
619
		// Generate the target
620
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
621
		$target = \OC\Files\Filesystem::normalizePath($target);
622
		$share->setTarget($target);
623
624
		// Pre share hook
625
		$run = true;
626
		$error = '';
627
		$preHookData = [
628
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
629
			'itemSource' => $share->getNode()->getId(),
630
			'shareType' => $share->getShareType(),
631
			'uidOwner' => $share->getSharedBy(),
632
			'permissions' => $share->getPermissions(),
633
			'fileSource' => $share->getNode()->getId(),
634
			'expiration' => $share->getExpirationDate(),
635
			'token' => $share->getToken(),
636
			'itemTarget' => $share->getTarget(),
637
			'shareWith' => $share->getSharedWith(),
638
			'run' => &$run,
639
			'error' => &$error,
640
		];
641
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
642
643
		if ($run === false) {
644
			throw new \Exception($error);
645
		}
646
647
		$oldShare = $share;
648
		$provider = $this->factory->getProviderForType($share->getShareType());
649
		$share = $provider->create($share);
650
		//reuse the node we already have
651
		$share->setNode($oldShare->getNode());
652
653
		// Post share hook
654
		$postHookData = [
655
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
656
			'itemSource' => $share->getNode()->getId(),
657
			'shareType' => $share->getShareType(),
658
			'uidOwner' => $share->getSharedBy(),
659
			'permissions' => $share->getPermissions(),
660
			'fileSource' => $share->getNode()->getId(),
661
			'expiration' => $share->getExpirationDate(),
662
			'token' => $share->getToken(),
663
			'id' => $share->getId(),
664
			'shareWith' => $share->getSharedWith(),
665
			'itemTarget' => $share->getTarget(),
666
			'fileTarget' => $share->getTarget(),
667
		];
668
669
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
670
671
		return $share;
672
	}
673
674
	/**
675
	 * Update a share
676
	 *
677
	 * @param \OCP\Share\IShare $share
678
	 * @return \OCP\Share\IShare The share object
679
	 * @throws \InvalidArgumentException
680
	 */
681
	public function updateShare(\OCP\Share\IShare $share) {
682
		$expirationDateUpdated = false;
683
684
		$this->canShare($share);
685
686
		try {
687
			$originalShare = $this->getShareById($share->getFullId());
688
		} catch (\UnexpectedValueException $e) {
689
			throw new \InvalidArgumentException('Share does not have a full id');
690
		}
691
692
		// We can't change the share type!
693
		if ($share->getShareType() !== $originalShare->getShareType()) {
694
			throw new \InvalidArgumentException('Can\'t change share type');
695
		}
696
697
		// We can only change the recipient on user shares
698
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
699
		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
700
			throw new \InvalidArgumentException('Can only update recipient on user shares');
701
		}
702
703
		// Cannot share with the owner
704 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
705
			$share->getSharedWith() === $share->getShareOwner()) {
706
			throw new \InvalidArgumentException('Can\'t share with the share owner');
707
		}
708
709
		$this->generalCreateChecks($share);
710
711
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
712
			$this->userCreateChecks($share);
713
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
714
			$this->groupCreateChecks($share);
715
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
716
			$this->linkCreateChecks($share);
717
718
			// Password updated.
719 View Code Duplication
			if ($share->getPassword() !== $originalShare->getPassword()) {
720
				//Verify the password
721
				$this->verifyPassword($share->getPassword());
722
723
				// If a password is set. Hash it!
724
				if ($share->getPassword() !== null) {
725
					$share->setPassword($this->hasher->hash($share->getPassword()));
726
				}
727
			}
728
729
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
730
				//Verify the expiration date
731
				$this->validateExpirationDate($share);
732
				$expirationDateUpdated = true;
733
			}
734
		}
735
736
		$plainTextPassword = null;
737
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
738
			// Password updated.
739 View Code Duplication
			if ($share->getPassword() !== $originalShare->getPassword()) {
740
				//Verify the password
741
				$this->verifyPassword($share->getPassword());
742
743
				// If a password is set. Hash it!
744
				if ($share->getPassword() !== null) {
745
					$plainTextPassword = $share->getPassword();
746
					$share->setPassword($this->hasher->hash($plainTextPassword));
747
				}
748
			}
749
		}
750
751
		$this->pathCreateChecks($share->getNode());
752
753
		// Now update the share!
754
		$provider = $this->factory->getProviderForType($share->getShareType());
755
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
756
			$share = $provider->update($share, $plainTextPassword);
757
		} else {
758
			$share = $provider->update($share);
759
		}
760
761
		if ($expirationDateUpdated === true) {
762
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
763
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
764
				'itemSource' => $share->getNode()->getId(),
765
				'date' => $share->getExpirationDate(),
766
				'uidOwner' => $share->getSharedBy(),
767
			]);
768
		}
769
770
		if ($share->getPassword() !== $originalShare->getPassword()) {
771
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
772
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
773
				'itemSource' => $share->getNode()->getId(),
774
				'uidOwner' => $share->getSharedBy(),
775
				'token' => $share->getToken(),
776
				'disabled' => is_null($share->getPassword()),
777
			]);
778
		}
779
780
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
781
			if ($this->userManager->userExists($share->getShareOwner())) {
782
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
783
			} else {
784
				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
785
			}
786
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
787
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
788
				'itemSource' => $share->getNode()->getId(),
789
				'shareType' => $share->getShareType(),
790
				'shareWith' => $share->getSharedWith(),
791
				'uidOwner' => $share->getSharedBy(),
792
				'permissions' => $share->getPermissions(),
793
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
794
			));
795
		}
796
797
		return $share;
798
	}
799
800
	/**
801
	 * Delete all the children of this share
802
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
803
	 *
804
	 * @param \OCP\Share\IShare $share
805
	 * @return \OCP\Share\IShare[] List of deleted shares
806
	 */
807
	protected function deleteChildren(\OCP\Share\IShare $share) {
808
		$deletedShares = [];
809
810
		$provider = $this->factory->getProviderForType($share->getShareType());
811
812
		foreach ($provider->getChildren($share) as $child) {
813
			$deletedChildren = $this->deleteChildren($child);
814
			$deletedShares = array_merge($deletedShares, $deletedChildren);
815
816
			$provider->delete($child);
817
			$deletedShares[] = $child;
818
		}
819
820
		return $deletedShares;
821
	}
822
823
	/**
824
	 * Delete a share
825
	 *
826
	 * @param \OCP\Share\IShare $share
827
	 * @throws ShareNotFound
828
	 * @throws \InvalidArgumentException
829
	 */
830
	public function deleteShare(\OCP\Share\IShare $share) {
831
832
		try {
833
			$share->getFullId();
834
		} catch (\UnexpectedValueException $e) {
835
			throw new \InvalidArgumentException('Share does not have a full id');
836
		}
837
838
		$event = new GenericEvent($share);
839
		$this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
840
841
		// Get all children and delete them as well
842
		$deletedShares = $this->deleteChildren($share);
843
844
		// Do the actual delete
845
		$provider = $this->factory->getProviderForType($share->getShareType());
846
		$provider->delete($share);
847
848
		// All the deleted shares caused by this delete
849
		$deletedShares[] = $share;
850
851
		// Emit post hook
852
		$event->setArgument('deletedShares', $deletedShares);
853
		$this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
854
	}
855
856
857
	/**
858
	 * Unshare a file as the recipient.
859
	 * This can be different from a regular delete for example when one of
860
	 * the users in a groups deletes that share. But the provider should
861
	 * handle this.
862
	 *
863
	 * @param \OCP\Share\IShare $share
864
	 * @param string $recipientId
865
	 */
866
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
867
		list($providerId, ) = $this->splitFullId($share->getFullId());
868
		$provider = $this->factory->getProvider($providerId);
869
870
		$provider->deleteFromSelf($share, $recipientId);
871
	}
872
873
	/**
874
	 * @inheritdoc
875
	 */
876
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
877
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
878
			throw new \InvalidArgumentException('Can\'t change target of link share');
879
		}
880
881
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
882
			throw new \InvalidArgumentException('Invalid recipient');
883
		}
884
885
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
886
			$sharedWith = $this->groupManager->get($share->getSharedWith());
887
			if (is_null($sharedWith)) {
888
				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
889
			}
890
			$recipient = $this->userManager->get($recipientId);
891
			if (!$sharedWith->inGroup($recipient)) {
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by $this->userManager->get($recipientId) on line 890 can be null; however, OCP\IGroup::inGroup() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
892
				throw new \InvalidArgumentException('Invalid recipient');
893
			}
894
		}
895
896
		list($providerId, ) = $this->splitFullId($share->getFullId());
897
		$provider = $this->factory->getProvider($providerId);
898
899
		$provider->move($share, $recipientId);
900
	}
901
902
	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
903
		$providers = $this->factory->getAllProviders();
904
905
		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
906
			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
907
			foreach ($newShares as $fid => $data) {
908
				if (!isset($shares[$fid])) {
909
					$shares[$fid] = [];
910
				}
911
912
				$shares[$fid] = array_merge($shares[$fid], $data);
913
			}
914
			return $shares;
915
		}, []);
916
	}
917
918
	/**
919
	 * @inheritdoc
920
	 */
921
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
922
		if ($path !== null &&
923
				!($path instanceof \OCP\Files\File) &&
924
				!($path instanceof \OCP\Files\Folder)) {
925
			throw new \InvalidArgumentException('invalid path');
926
		}
927
928
		try {
929
			$provider = $this->factory->getProviderForType($shareType);
930
		} catch (ProviderException $e) {
931
			return [];
932
		}
933
934
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
935
936
		/*
937
		 * Work around so we don't return expired shares but still follow
938
		 * proper pagination.
939
		 */
940
941
		$shares2 = [];
942
943
		while(true) {
944
			$added = 0;
945
			foreach ($shares as $share) {
946
947
				try {
948
					$this->checkExpireDate($share);
949
				} catch (ShareNotFound $e) {
950
					//Ignore since this basically means the share is deleted
951
					continue;
952
				}
953
954
				$added++;
955
				$shares2[] = $share;
956
957
				if (count($shares2) === $limit) {
958
					break;
959
				}
960
			}
961
962
			if (count($shares2) === $limit) {
963
				break;
964
			}
965
966
			// If there was no limit on the select we are done
967
			if ($limit === -1) {
968
				break;
969
			}
970
971
			$offset += $added;
972
973
			// Fetch again $limit shares
974
			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
975
976
			// No more shares means we are done
977
			if (empty($shares)) {
978
				break;
979
			}
980
		}
981
982
		$shares = $shares2;
983
984
		return $shares;
985
	}
986
987
	/**
988
	 * @inheritdoc
989
	 */
990
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
991
		try {
992
			$provider = $this->factory->getProviderForType($shareType);
993
		} catch (ProviderException $e) {
994
			return [];
995
		}
996
997
		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
998
999
		// remove all shares which are already expired
1000
		foreach ($shares as $key => $share) {
1001
			try {
1002
				$this->checkExpireDate($share);
1003
			} catch (ShareNotFound $e) {
1004
				unset($shares[$key]);
1005
			}
1006
		}
1007
1008
		return $shares;
1009
	}
1010
1011
	/**
1012
	 * @inheritdoc
1013
	 */
1014
	public function getShareById($id, $recipient = null) {
1015
		if ($id === null) {
1016
			throw new ShareNotFound();
1017
		}
1018
1019
		list($providerId, $id) = $this->splitFullId($id);
1020
1021
		try {
1022
			$provider = $this->factory->getProvider($providerId);
1023
		} catch (ProviderException $e) {
1024
			throw new ShareNotFound();
1025
		}
1026
1027
		$share = $provider->getShareById($id, $recipient);
1028
1029
		$this->checkExpireDate($share);
1030
1031
		return $share;
1032
	}
1033
1034
	/**
1035
	 * Get all the shares for a given path
1036
	 *
1037
	 * @param \OCP\Files\Node $path
1038
	 * @param int $page
1039
	 * @param int $perPage
1040
	 *
1041
	 * @return Share[]
1042
	 */
1043
	public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) {
0 ignored issues
show
Unused Code introduced by
The parameter $path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $page is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $perPage is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1044
		return [];
1045
	}
1046
1047
	/**
1048
	 * Get the share by token possible with password
1049
	 *
1050
	 * @param string $token
1051
	 * @return Share
1052
	 *
1053
	 * @throws ShareNotFound
1054
	 */
1055
	public function getShareByToken($token) {
1056
		$share = null;
1057
		try {
1058
			if($this->shareApiAllowLinks()) {
1059
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1060
				$share = $provider->getShareByToken($token);
1061
			}
1062
		} catch (ProviderException $e) {
1063
		} catch (ShareNotFound $e) {
1064
		}
1065
1066
1067
		// If it is not a link share try to fetch a federated share by token
1068 View Code Duplication
		if ($share === null) {
1069
			try {
1070
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1071
				$share = $provider->getShareByToken($token);
1072
			} catch (ProviderException $e) {
1073
			} catch (ShareNotFound $e) {
1074
			}
1075
		}
1076
1077
		// If it is not a link share try to fetch a mail share by token
1078 View Code Duplication
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1079
			try {
1080
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1081
				$share = $provider->getShareByToken($token);
1082
			} catch (ProviderException $e) {
1083
			} catch (ShareNotFound $e) {
1084
			}
1085
		}
1086
1087
		if ($share === null) {
1088
			throw new ShareNotFound();
1089
		}
1090
1091
		$this->checkExpireDate($share);
1092
1093
		/*
1094
		 * Reduce the permissions for link shares if public upload is not enabled
1095
		 */
1096
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1097
			!$this->shareApiLinkAllowPublicUpload()) {
1098
			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1099
		}
1100
1101
		return $share;
1102
	}
1103
1104
	protected function checkExpireDate($share) {
1105
		if ($share->getExpirationDate() !== null &&
1106
			$share->getExpirationDate() <= new \DateTime()) {
1107
			$this->deleteShare($share);
1108
			throw new ShareNotFound();
1109
		}
1110
1111
	}
1112
1113
	/**
1114
	 * Verify the password of a public share
1115
	 *
1116
	 * @param \OCP\Share\IShare $share
1117
	 * @param string $password
1118
	 * @return bool
1119
	 */
1120
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1121
		$passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1122
			|| $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1123
		if (!$passwordProtected) {
1124
			//TODO maybe exception?
1125
			return false;
1126
		}
1127
1128
		if ($password === null || $share->getPassword() === null) {
1129
			return false;
1130
		}
1131
1132
		$newHash = '';
1133
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1134
			return false;
1135
		}
1136
1137
		if (!empty($newHash)) {
1138
			$share->setPassword($newHash);
1139
			$provider = $this->factory->getProviderForType($share->getShareType());
1140
			$provider->update($share);
1141
		}
1142
1143
		return true;
1144
	}
1145
1146
	/**
1147
	 * @inheritdoc
1148
	 */
1149
	public function userDeleted($uid) {
1150
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL];
1151
1152
		foreach ($types as $type) {
1153
			try {
1154
				$provider = $this->factory->getProviderForType($type);
1155
			} catch (ProviderException $e) {
1156
				continue;
1157
			}
1158
			$provider->userDeleted($uid, $type);
1159
		}
1160
	}
1161
1162
	/**
1163
	 * @inheritdoc
1164
	 */
1165
	public function groupDeleted($gid) {
1166
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1167
		$provider->groupDeleted($gid);
1168
	}
1169
1170
	/**
1171
	 * @inheritdoc
1172
	 */
1173
	public function userDeletedFromGroup($uid, $gid) {
1174
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1175
		$provider->userDeletedFromGroup($uid, $gid);
1176
	}
1177
1178
	/**
1179
	 * Get access list to a path. This means
1180
	 * all the users that can access a given path.
1181
	 *
1182
	 * Consider:
1183
	 * -root
1184
	 * |-folder1 (23)
1185
	 *  |-folder2 (32)
1186
	 *   |-fileA (42)
1187
	 *
1188
	 * fileA is shared with user1 and user1@server1
1189
	 * folder2 is shared with group2 (user4 is a member of group2)
1190
	 * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2
1191
	 *
1192
	 * Then the access list to '/folder1/folder2/fileA' with $currentAccess is:
1193
	 * [
1194
	 *  users  => [
1195
	 *      'user1' => ['node_id' => 42, 'node_path' => '/fileA'],
1196
	 *      'user4' => ['node_id' => 32, 'node_path' => '/folder2'],
1197
	 *      'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'],
1198
	 *  ],
1199
	 *  remote => [
1200
	 *      'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'],
1201
	 *      'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'],
1202
	 *  ],
1203
	 *  public => bool
1204
	 *  mail => bool
1205
	 * ]
1206
	 *
1207
	 * The access list to '/folder1/folder2/fileA' **without** $currentAccess is:
1208
	 * [
1209
	 *  users  => ['user1', 'user2', 'user4'],
1210
	 *  remote => bool,
1211
	 *  public => bool
1212
	 *  mail => bool
1213
	 * ]
1214
	 *
1215
	 * This is required for encryption/activity
1216
	 *
1217
	 * @param \OCP\Files\Node $path
1218
	 * @param bool $recursive Should we check all parent folders as well
1219
	 * @param bool $currentAccess Should the user have currently access to the file
1220
	 * @return array
1221
	 */
1222
	public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) {
1223
		$owner = $path->getOwner()->getUID();
1224
1225
		if ($currentAccess) {
1226
			$al = ['users' => [], 'remote' => [], 'public' => false];
1227
		} else {
1228
			$al = ['users' => [], 'remote' => false, 'public' => false];
1229
		}
1230
		if (!$this->userManager->userExists($owner)) {
1231
			return $al;
1232
		}
1233
1234
		//Get node for the owner
1235
		$userFolder = $this->rootFolder->getUserFolder($owner);
1236
		if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) {
1237
			$path = $userFolder->getById($path->getId())[0];
1238
		}
1239
1240
		$providers = $this->factory->getAllProviders();
1241
1242
		/** @var Node[] $nodes */
1243
		$nodes = [];
1244
1245
1246
		if ($currentAccess) {
1247
			$ownerPath = $path->getPath();
1248
			$ownerPath = explode('/', $ownerPath, 4);
1249
			if (count($ownerPath) < 4) {
1250
				$ownerPath = '';
1251
			} else {
1252
				$ownerPath = $ownerPath[3];
1253
			}
1254
			$al['users'][$owner] = [
1255
				'node_id' => $path->getId(),
1256
				'node_path' => '/' . $ownerPath,
1257
			];
1258
		} else {
1259
			$al['users'][] = $owner;
1260
		}
1261
1262
		// Collect all the shares
1263
		while ($path->getPath() !== $userFolder->getPath()) {
1264
			$nodes[] = $path;
1265
			if (!$recursive) {
1266
				break;
1267
			}
1268
			$path = $path->getParent();
1269
		}
1270
1271
		foreach ($providers as $provider) {
1272
			$tmp = $provider->getAccessList($nodes, $currentAccess);
1273
1274
			foreach ($tmp as $k => $v) {
1275
				if (isset($al[$k])) {
1276
					if (is_array($al[$k])) {
1277
						$al[$k] = array_merge($al[$k], $v);
1278
					} else {
1279
						$al[$k] = $al[$k] || $v;
1280
					}
1281
				} else {
1282
					$al[$k] = $v;
1283
				}
1284
			}
1285
		}
1286
1287
		return $al;
1288
	}
1289
1290
	/**
1291
	 * Create a new share
1292
	 * @return \OCP\Share\IShare;
0 ignored issues
show
Documentation introduced by
The doc-type \OCP\Share\IShare; could not be parsed: Expected "|" or "end of type", but got ";" at position 17. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1293
	 */
1294
	public function newShare() {
1295
		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1296
	}
1297
1298
	/**
1299
	 * Is the share API enabled
1300
	 *
1301
	 * @return bool
1302
	 */
1303
	public function shareApiEnabled() {
1304
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1305
	}
1306
1307
	/**
1308
	 * Is public link sharing enabled
1309
	 *
1310
	 * @return bool
1311
	 */
1312
	public function shareApiAllowLinks() {
1313
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1314
	}
1315
1316
	/**
1317
	 * Is password on public link requires
1318
	 *
1319
	 * @return bool
1320
	 */
1321
	public function shareApiLinkEnforcePassword() {
1322
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1323
	}
1324
1325
	/**
1326
	 * Is default expire date enabled
1327
	 *
1328
	 * @return bool
1329
	 */
1330
	public function shareApiLinkDefaultExpireDate() {
1331
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1332
	}
1333
1334
	/**
1335
	 * Is default expire date enforced
1336
	 *`
1337
	 * @return bool
1338
	 */
1339
	public function shareApiLinkDefaultExpireDateEnforced() {
1340
		return $this->shareApiLinkDefaultExpireDate() &&
1341
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1342
	}
1343
1344
	/**
1345
	 * Number of default expire days
1346
	 *shareApiLinkAllowPublicUpload
1347
	 * @return int
1348
	 */
1349
	public function shareApiLinkDefaultExpireDays() {
1350
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1351
	}
1352
1353
	/**
1354
	 * Allow public upload on link shares
1355
	 *
1356
	 * @return bool
1357
	 */
1358
	public function shareApiLinkAllowPublicUpload() {
1359
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1360
	}
1361
1362
	/**
1363
	 * check if user can only share with group members
1364
	 * @return bool
1365
	 */
1366
	public function shareWithGroupMembersOnly() {
1367
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1368
	}
1369
1370
	/**
1371
	 * Check if users can share with groups
1372
	 * @return bool
1373
	 */
1374
	public function allowGroupSharing() {
1375
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1376
	}
1377
1378
	/**
1379
	 * Copied from \OC_Util::isSharingDisabledForUser
1380
	 *
1381
	 * TODO: Deprecate fuction from OC_Util
1382
	 *
1383
	 * @param string $userId
1384
	 * @return bool
1385
	 */
1386
	public function sharingDisabledForUser($userId) {
1387
		if ($userId === null) {
1388
			return false;
1389
		}
1390
1391
		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1392
			return $this->sharingDisabledForUsersCache[$userId];
1393
		}
1394
1395
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1396
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1397
			$excludedGroups = json_decode($groupsList);
1398 View Code Duplication
			if (is_null($excludedGroups)) {
1399
				$excludedGroups = explode(',', $groupsList);
1400
				$newValue = json_encode($excludedGroups);
1401
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1402
			}
1403
			$user = $this->userManager->get($userId);
1404
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1403 can be null; however, OCP\IGroupManager::getUserGroupIds() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1405 View Code Duplication
			if (!empty($usersGroups)) {
1406
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1407
				// if the user is only in groups which are disabled for sharing then
1408
				// sharing is also disabled for the user
1409
				if (empty($remainingGroups)) {
1410
					$this->sharingDisabledForUsersCache[$userId] = true;
1411
					return true;
1412
				}
1413
			}
1414
		}
1415
1416
		$this->sharingDisabledForUsersCache[$userId] = false;
1417
		return false;
1418
	}
1419
1420
	/**
1421
	 * @inheritdoc
1422
	 */
1423
	public function outgoingServer2ServerSharesAllowed() {
1424
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1425
	}
1426
1427
	/**
1428
	 * @inheritdoc
1429
	 */
1430
	public function shareProviderExists($shareType) {
1431
		try {
1432
			$this->factory->getProviderForType($shareType);
1433
		} catch (ProviderException $e) {
1434
			return false;
1435
		}
1436
1437
		return true;
1438
	}
1439
1440
}
1441