Completed
Push — master ( 9adfa6...ca9d25 )
by Morris
22:40 queued 11:31
created

Manager::userDeleted()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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 Symfony\Component\EventDispatcher\EventDispatcher;
52
use Symfony\Component\EventDispatcher\GenericEvent;
53
use OCP\Share\IShareProvider;
54
55
/**
56
 * This class is the communication hub for all sharing related operations.
57
 */
58
class Manager implements IManager {
59
60
	/** @var IProviderFactory */
61
	private $factory;
62
	/** @var ILogger */
63
	private $logger;
64
	/** @var IConfig */
65
	private $config;
66
	/** @var ISecureRandom */
67
	private $secureRandom;
68
	/** @var IHasher */
69
	private $hasher;
70
	/** @var IMountManager */
71
	private $mountManager;
72
	/** @var IGroupManager */
73
	private $groupManager;
74
	/** @var IL10N */
75
	private $l;
76
	/** @var IUserManager */
77
	private $userManager;
78
	/** @var IRootFolder */
79
	private $rootFolder;
80
	/** @var CappedMemoryCache */
81
	private $sharingDisabledForUsersCache;
82
	/** @var EventDispatcher */
83
	private $eventDispatcher;
84
	/** @var LegacyHooks */
85
	private $legacyHooks;
86
87
88
	/**
89
	 * Manager constructor.
90
	 *
91
	 * @param ILogger $logger
92
	 * @param IConfig $config
93
	 * @param ISecureRandom $secureRandom
94
	 * @param IHasher $hasher
95
	 * @param IMountManager $mountManager
96
	 * @param IGroupManager $groupManager
97
	 * @param IL10N $l
98
	 * @param IProviderFactory $factory
99
	 * @param IUserManager $userManager
100
	 * @param IRootFolder $rootFolder
101
	 * @param EventDispatcher $eventDispatcher
102
	 */
103
	public function __construct(
104
			ILogger $logger,
105
			IConfig $config,
106
			ISecureRandom $secureRandom,
107
			IHasher $hasher,
108
			IMountManager $mountManager,
109
			IGroupManager $groupManager,
110
			IL10N $l,
111
			IProviderFactory $factory,
112
			IUserManager $userManager,
113
			IRootFolder $rootFolder,
114
			EventDispatcher $eventDispatcher
115
	) {
116
		$this->logger = $logger;
117
		$this->config = $config;
118
		$this->secureRandom = $secureRandom;
119
		$this->hasher = $hasher;
120
		$this->mountManager = $mountManager;
121
		$this->groupManager = $groupManager;
122
		$this->l = $l;
123
		$this->factory = $factory;
124
		$this->userManager = $userManager;
125
		$this->rootFolder = $rootFolder;
126
		$this->eventDispatcher = $eventDispatcher;
127
		$this->sharingDisabledForUsersCache = new CappedMemoryCache();
128
		$this->legacyHooks = new LegacyHooks($this->eventDispatcher);
129
	}
130
131
	/**
132
	 * Convert from a full share id to a tuple (providerId, shareId)
133
	 *
134
	 * @param string $id
135
	 * @return string[]
136
	 */
137
	private function splitFullId($id) {
138
		return explode(':', $id, 2);
139
	}
140
141
	/**
142
	 * Verify if a password meets all requirements
143
	 *
144
	 * @param string $password
145
	 * @throws \Exception
146
	 */
147
	protected function verifyPassword($password) {
148
		if ($password === null) {
149
			// No password is set, check if this is allowed.
150
			if ($this->shareApiLinkEnforcePassword()) {
151
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
152
			}
153
154
			return;
155
		}
156
157
		// Let others verify the password
158
		try {
159
			$event = new GenericEvent($password);
160
			$this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event);
161
		} catch (HintException $e) {
162
			throw new \Exception($e->getHint());
163
		}
164
	}
165
166
	/**
167
	 * Check for generic requirements before creating a share
168
	 *
169
	 * @param \OCP\Share\IShare $share
170
	 * @throws \InvalidArgumentException
171
	 * @throws GenericShareException
172
	 */
173
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
174
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
175
			// We expect a valid user as sharedWith for user shares
176
			if (!$this->userManager->userExists($share->getSharedWith())) {
177
				throw new \InvalidArgumentException('SharedWith is not a valid user');
178
			}
179
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
180
			// We expect a valid group as sharedWith for group shares
181
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
182
				throw new \InvalidArgumentException('SharedWith is not a valid group');
183
			}
184
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
185
			if ($share->getSharedWith() !== null) {
186
				throw new \InvalidArgumentException('SharedWith should be empty');
187
			}
188
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
189
			if ($share->getSharedWith() === null) {
190
				throw new \InvalidArgumentException('SharedWith should not be empty');
191
			}
192
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
193
			if ($share->getSharedWith() === null) {
194
				throw new \InvalidArgumentException('SharedWith should not be empty');
195
			}
196
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) {
197
			$circle = \OCA\Circles\Api\Circles::detailsCircle($share->getSharedWith());
198
			if ($circle === null) {
199
				throw new \InvalidArgumentException('SharedWith is not a valid circle');
200
			}
201
		} else {
202
			// We can't handle other types yet
203
			throw new \InvalidArgumentException('unknown share type');
204
		}
205
206
		// Verify the initiator of the share is set
207
		if ($share->getSharedBy() === null) {
208
			throw new \InvalidArgumentException('SharedBy should be set');
209
		}
210
211
		// Cannot share with yourself
212 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
213
			$share->getSharedWith() === $share->getSharedBy()) {
214
			throw new \InvalidArgumentException('Can\'t share with yourself');
215
		}
216
217
		// The path should be set
218
		if ($share->getNode() === null) {
219
			throw new \InvalidArgumentException('Path should be set');
220
		}
221
222
		// And it should be a file or a folder
223
		if (!($share->getNode() instanceof \OCP\Files\File) &&
224
				!($share->getNode() instanceof \OCP\Files\Folder)) {
225
			throw new \InvalidArgumentException('Path should be either a file or a folder');
226
		}
227
228
		// And you can't share your rootfolder
229
		if ($this->userManager->userExists($share->getSharedBy())) {
230
			$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath();
231
		} else {
232
			$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath();
233
		}
234
		if ($sharedPath === $share->getNode()->getPath()) {
235
			throw new \InvalidArgumentException('You can\'t share your root folder');
236
		}
237
238
		// Check if we actually have share permissions
239 View Code Duplication
		if (!$share->getNode()->isShareable()) {
240
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
241
			throw new GenericShareException($message_t, $message_t, 404);
242
		}
243
244
		// Permissions should be set
245
		if ($share->getPermissions() === null) {
246
			throw new \InvalidArgumentException('A share requires permissions');
247
		}
248
249
		/*
250
		 * Quick fix for #23536
251
		 * Non moveable mount points do not have update and delete permissions
252
		 * while we 'most likely' do have that on the storage.
253
		 */
254
		$permissions = $share->getNode()->getPermissions();
255
		$mount = $share->getNode()->getMountPoint();
256
		if (!($mount instanceof MoveableMount)) {
257
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
258
		}
259
260
		// Check that we do not share with more permissions than we have
261 View Code Duplication
		if ($share->getPermissions() & ~$permissions) {
262
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
263
			throw new GenericShareException($message_t, $message_t, 404);
264
		}
265
266
267
		// Check that read permissions are always set
268
		// Link shares are allowed to have no read permissions to allow upload to hidden folders
269
		$noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK
270
			|| $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL;
271
		if (!$noReadPermissionRequired &&
272
			($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
273
			throw new \InvalidArgumentException('Shares need at least read permissions');
274
		}
275
276
		if ($share->getNode() instanceof \OCP\Files\File) {
277 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
278
				$message_t = $this->l->t('Files can\'t be shared with delete permissions');
279
				throw new GenericShareException($message_t);
280
			}
281 View Code Duplication
			if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
282
				$message_t = $this->l->t('Files can\'t be shared with create permissions');
283
				throw new GenericShareException($message_t);
284
			}
285
		}
286
	}
287
288
	/**
289
	 * Validate if the expiration date fits the system settings
290
	 *
291
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
292
	 * @return \OCP\Share\IShare The modified share object
293
	 * @throws GenericShareException
294
	 * @throws \InvalidArgumentException
295
	 * @throws \Exception
296
	 */
297
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
298
299
		$expirationDate = $share->getExpirationDate();
300
301
		if ($expirationDate !== null) {
302
			//Make sure the expiration date is a date
303
			$expirationDate->setTime(0, 0, 0);
304
305
			$date = new \DateTime();
306
			$date->setTime(0, 0, 0);
307 View Code Duplication
			if ($date >= $expirationDate) {
308
				$message = $this->l->t('Expiration date is in the past');
309
				throw new GenericShareException($message, $message, 404);
310
			}
311
		}
312
313
		// If expiredate is empty set a default one if there is a default
314
		$fullId = null;
315
		try {
316
			$fullId = $share->getFullId();
317
		} catch (\UnexpectedValueException $e) {
318
			// This is a new share
319
		}
320
321
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
322
			$expirationDate = new \DateTime();
323
			$expirationDate->setTime(0,0,0);
324
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
325
		}
326
327
		// If we enforce the expiration date check that is does not exceed
328
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
329
			if ($expirationDate === null) {
330
				throw new \InvalidArgumentException('Expiration date is enforced');
331
			}
332
333
			$date = new \DateTime();
334
			$date->setTime(0, 0, 0);
335
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
336 View Code Duplication
			if ($date < $expirationDate) {
337
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
338
				throw new GenericShareException($message, $message, 404);
339
			}
340
		}
341
342
		$accepted = true;
343
		$message = '';
344
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
345
			'expirationDate' => &$expirationDate,
346
			'accepted' => &$accepted,
347
			'message' => &$message,
348
			'passwordSet' => $share->getPassword() !== null,
349
		]);
350
351
		if (!$accepted) {
352
			throw new \Exception($message);
353
		}
354
355
		$share->setExpirationDate($expirationDate);
356
357
		return $share;
358
	}
359
360
	/**
361
	 * Check for pre share requirements for user shares
362
	 *
363
	 * @param \OCP\Share\IShare $share
364
	 * @throws \Exception
365
	 */
366
	protected function userCreateChecks(\OCP\Share\IShare $share) {
367
		// Check if we can share with group members only
368
		if ($this->shareWithGroupMembersOnly()) {
369
			$sharedBy = $this->userManager->get($share->getSharedBy());
370
			$sharedWith = $this->userManager->get($share->getSharedWith());
371
			// Verify we can share with this user
372
			$groups = array_intersect(
373
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 369 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...
374
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) 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
			);
376
			if (empty($groups)) {
377
				throw new \Exception('Only sharing with group members is allowed');
378
			}
379
		}
380
381
		/*
382
		 * TODO: Could be costly, fix
383
		 *
384
		 * Also this is not what we want in the future.. then we want to squash identical shares.
385
		 */
386
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
387
		$existingShares = $provider->getSharesByPath($share->getNode());
388
		foreach($existingShares as $existingShare) {
389
			// Ignore if it is the same share
390
			try {
391
				if ($existingShare->getFullId() === $share->getFullId()) {
392
					continue;
393
				}
394
			} catch (\UnexpectedValueException $e) {
395
				//Shares are not identical
396
			}
397
398
			// Identical share already existst
399
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
400
				throw new \Exception('Path already shared with this user');
401
			}
402
403
			// The share is already shared with this user via a group share
404
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
405
				$group = $this->groupManager->get($existingShare->getSharedWith());
406
				if (!is_null($group)) {
407
					$user = $this->userManager->get($share->getSharedWith());
408
409
					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 407 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...
410
						throw new \Exception('Path already shared with this user');
411
					}
412
				}
413
			}
414
		}
415
	}
416
417
	/**
418
	 * Check for pre share requirements for group shares
419
	 *
420
	 * @param \OCP\Share\IShare $share
421
	 * @throws \Exception
422
	 */
423
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
424
		// Verify group shares are allowed
425
		if (!$this->allowGroupSharing()) {
426
			throw new \Exception('Group sharing is now allowed');
427
		}
428
429
		// Verify if the user can share with this group
430
		if ($this->shareWithGroupMembersOnly()) {
431
			$sharedBy = $this->userManager->get($share->getSharedBy());
432
			$sharedWith = $this->groupManager->get($share->getSharedWith());
433
			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 431 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...
434
				throw new \Exception('Only sharing within your own groups is allowed');
435
			}
436
		}
437
438
		/*
439
		 * TODO: Could be costly, fix
440
		 *
441
		 * Also this is not what we want in the future.. then we want to squash identical shares.
442
		 */
443
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
444
		$existingShares = $provider->getSharesByPath($share->getNode());
445
		foreach($existingShares as $existingShare) {
446
			try {
447
				if ($existingShare->getFullId() === $share->getFullId()) {
448
					continue;
449
				}
450
			} catch (\UnexpectedValueException $e) {
451
				//It is a new share so just continue
452
			}
453
454
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
455
				throw new \Exception('Path already shared with this group');
456
			}
457
		}
458
	}
459
460
	/**
461
	 * Check for pre share requirements for link shares
462
	 *
463
	 * @param \OCP\Share\IShare $share
464
	 * @throws \Exception
465
	 */
466
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
467
		// Are link shares allowed?
468
		if (!$this->shareApiAllowLinks()) {
469
			throw new \Exception('Link sharing not allowed');
470
		}
471
472
		// Link shares by definition can't have share permissions
473
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
474
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
475
		}
476
477
		// Check if public upload is allowed
478
		if (!$this->shareApiLinkAllowPublicUpload() &&
479
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
480
			throw new \InvalidArgumentException('Public upload not allowed');
481
		}
482
	}
483
484
	/**
485
	 * To make sure we don't get invisible link shares we set the parent
486
	 * of a link if it is a reshare. This is a quick word around
487
	 * until we can properly display multiple link shares in the UI
488
	 *
489
	 * See: https://github.com/owncloud/core/issues/22295
490
	 *
491
	 * FIXME: Remove once multiple link shares can be properly displayed
492
	 *
493
	 * @param \OCP\Share\IShare $share
494
	 */
495
	protected function setLinkParent(\OCP\Share\IShare $share) {
496
497
		// No sense in checking if the method is not there.
498
		if (method_exists($share, 'setParent')) {
499
			$storage = $share->getNode()->getStorage();
500
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
501
				/** @var \OCA\Files_Sharing\SharedStorage $storage */
502
				$share->setParent($storage->getShareId());
503
			}
504
		};
505
	}
506
507
	/**
508
	 * @param File|Folder $path
509
	 */
510
	protected function pathCreateChecks($path) {
511
		// Make sure that we do not share a path that contains a shared mountpoint
512
		if ($path instanceof \OCP\Files\Folder) {
513
			$mounts = $this->mountManager->findIn($path->getPath());
514
			foreach($mounts as $mount) {
515
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
516
					throw new \InvalidArgumentException('Path contains files shared with you');
517
				}
518
			}
519
		}
520
	}
521
522
	/**
523
	 * Check if the user that is sharing can actually share
524
	 *
525
	 * @param \OCP\Share\IShare $share
526
	 * @throws \Exception
527
	 */
528
	protected function canShare(\OCP\Share\IShare $share) {
529
		if (!$this->shareApiEnabled()) {
530
			throw new \Exception('The share API is disabled');
531
		}
532
533
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
534
			throw new \Exception('You are not allowed to share');
535
		}
536
	}
537
538
	/**
539
	 * Share a path
540
	 *
541
	 * @param \OCP\Share\IShare $share
542
	 * @return Share The share object
543
	 * @throws \Exception
544
	 *
545
	 * TODO: handle link share permissions or check them
546
	 */
547
	public function createShare(\OCP\Share\IShare $share) {
548
		$this->canShare($share);
549
550
		$this->generalCreateChecks($share);
551
552
		// Verify if there are any issues with the path
553
		$this->pathCreateChecks($share->getNode());
554
555
		/*
556
		 * On creation of a share the owner is always the owner of the path
557
		 * Except for mounted federated shares.
558
		 */
559
		$storage = $share->getNode()->getStorage();
560
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
561
			$parent = $share->getNode()->getParent();
562
			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
563
				$parent = $parent->getParent();
564
			}
565
			$share->setShareOwner($parent->getOwner()->getUID());
566
		} else {
567
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
568
		}
569
570
		//Verify share type
571
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
572
			$this->userCreateChecks($share);
573
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
574
			$this->groupCreateChecks($share);
575
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
576
			$this->linkCreateChecks($share);
577
			$this->setLinkParent($share);
578
579
			/*
580
			 * For now ignore a set token.
581
			 */
582
			$share->setToken(
583
				$this->secureRandom->generate(
584
					\OC\Share\Constants::TOKEN_LENGTH,
585
					\OCP\Security\ISecureRandom::CHAR_LOWER.
586
					\OCP\Security\ISecureRandom::CHAR_UPPER.
587
					\OCP\Security\ISecureRandom::CHAR_DIGITS
588
				)
589
			);
590
591
			//Verify the expiration date
592
			$this->validateExpirationDate($share);
593
594
			//Verify the password
595
			$this->verifyPassword($share->getPassword());
596
597
			// If a password is set. Hash it!
598
			if ($share->getPassword() !== null) {
599
				$share->setPassword($this->hasher->hash($share->getPassword()));
600
			}
601
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
602
			$share->setToken(
603
				$this->secureRandom->generate(
604
					\OC\Share\Constants::TOKEN_LENGTH,
605
					\OCP\Security\ISecureRandom::CHAR_LOWER.
606
					\OCP\Security\ISecureRandom::CHAR_UPPER.
607
					\OCP\Security\ISecureRandom::CHAR_DIGITS
608
				)
609
			);
610
		}
611
612
		// Cannot share with the owner
613 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
614
			$share->getSharedWith() === $share->getShareOwner()) {
615
			throw new \InvalidArgumentException('Can\'t share with the share owner');
616
		}
617
618
		// Generate the target
619
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
620
		$target = \OC\Files\Filesystem::normalizePath($target);
621
		$share->setTarget($target);
622
623
		// Pre share hook
624
		$run = true;
625
		$error = '';
626
		$preHookData = [
627
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
628
			'itemSource' => $share->getNode()->getId(),
629
			'shareType' => $share->getShareType(),
630
			'uidOwner' => $share->getSharedBy(),
631
			'permissions' => $share->getPermissions(),
632
			'fileSource' => $share->getNode()->getId(),
633
			'expiration' => $share->getExpirationDate(),
634
			'token' => $share->getToken(),
635
			'itemTarget' => $share->getTarget(),
636
			'shareWith' => $share->getSharedWith(),
637
			'run' => &$run,
638
			'error' => &$error,
639
		];
640
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
641
642
		if ($run === false) {
643
			throw new \Exception($error);
644
		}
645
646
		$oldShare = $share;
647
		$provider = $this->factory->getProviderForType($share->getShareType());
648
		$share = $provider->create($share);
649
		//reuse the node we already have
650
		$share->setNode($oldShare->getNode());
651
652
		// Post share hook
653
		$postHookData = [
654
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
655
			'itemSource' => $share->getNode()->getId(),
656
			'shareType' => $share->getShareType(),
657
			'uidOwner' => $share->getSharedBy(),
658
			'permissions' => $share->getPermissions(),
659
			'fileSource' => $share->getNode()->getId(),
660
			'expiration' => $share->getExpirationDate(),
661
			'token' => $share->getToken(),
662
			'id' => $share->getId(),
663
			'shareWith' => $share->getSharedWith(),
664
			'itemTarget' => $share->getTarget(),
665
			'fileTarget' => $share->getTarget(),
666
		];
667
668
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
669
670
		return $share;
671
	}
672
673
	/**
674
	 * Update a share
675
	 *
676
	 * @param \OCP\Share\IShare $share
677
	 * @return \OCP\Share\IShare The share object
678
	 * @throws \InvalidArgumentException
679
	 */
680
	public function updateShare(\OCP\Share\IShare $share) {
681
		$expirationDateUpdated = false;
682
683
		$this->canShare($share);
684
685
		try {
686
			$originalShare = $this->getShareById($share->getFullId());
687
		} catch (\UnexpectedValueException $e) {
688
			throw new \InvalidArgumentException('Share does not have a full id');
689
		}
690
691
		// We can't change the share type!
692
		if ($share->getShareType() !== $originalShare->getShareType()) {
693
			throw new \InvalidArgumentException('Can\'t change share type');
694
		}
695
696
		// We can only change the recipient on user shares
697
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
698
		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
699
			throw new \InvalidArgumentException('Can only update recipient on user shares');
700
		}
701
702
		// Cannot share with the owner
703 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
704
			$share->getSharedWith() === $share->getShareOwner()) {
705
			throw new \InvalidArgumentException('Can\'t share with the share owner');
706
		}
707
708
		$this->generalCreateChecks($share);
709
710
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
711
			$this->userCreateChecks($share);
712
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
713
			$this->groupCreateChecks($share);
714
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
715
			$this->linkCreateChecks($share);
716
717
			// Password updated.
718 View Code Duplication
			if ($share->getPassword() !== $originalShare->getPassword()) {
719
				//Verify the password
720
				$this->verifyPassword($share->getPassword());
721
722
				// If a password is set. Hash it!
723
				if ($share->getPassword() !== null) {
724
					$share->setPassword($this->hasher->hash($share->getPassword()));
725
				}
726
			}
727
728
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
729
				//Verify the expiration date
730
				$this->validateExpirationDate($share);
731
				$expirationDateUpdated = true;
732
			}
733
		}
734
735
		$plainTextPassword = null;
736
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
737
			// Password updated.
738 View Code Duplication
			if ($share->getPassword() !== $originalShare->getPassword()) {
739
				//Verify the password
740
				$this->verifyPassword($share->getPassword());
741
742
				// If a password is set. Hash it!
743
				if ($share->getPassword() !== null) {
744
					$plainTextPassword = $share->getPassword();
745
					$share->setPassword($this->hasher->hash($plainTextPassword));
746
				}
747
			}
748
		}
749
750
		$this->pathCreateChecks($share->getNode());
751
752
		// Now update the share!
753
		$provider = $this->factory->getProviderForType($share->getShareType());
754
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
755
			$share = $provider->update($share, $plainTextPassword);
756
		} else {
757
			$share = $provider->update($share);
758
		}
759
760
		if ($expirationDateUpdated === true) {
761
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
762
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
763
				'itemSource' => $share->getNode()->getId(),
764
				'date' => $share->getExpirationDate(),
765
				'uidOwner' => $share->getSharedBy(),
766
			]);
767
		}
768
769
		if ($share->getPassword() !== $originalShare->getPassword()) {
770
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
771
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
772
				'itemSource' => $share->getNode()->getId(),
773
				'uidOwner' => $share->getSharedBy(),
774
				'token' => $share->getToken(),
775
				'disabled' => is_null($share->getPassword()),
776
			]);
777
		}
778
779
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
780
			if ($this->userManager->userExists($share->getShareOwner())) {
781
				$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
782
			} else {
783
				$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
784
			}
785
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
786
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
787
				'itemSource' => $share->getNode()->getId(),
788
				'shareType' => $share->getShareType(),
789
				'shareWith' => $share->getSharedWith(),
790
				'uidOwner' => $share->getSharedBy(),
791
				'permissions' => $share->getPermissions(),
792
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
793
			));
794
		}
795
796
		return $share;
797
	}
798
799
	/**
800
	 * Delete all the children of this share
801
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
802
	 *
803
	 * @param \OCP\Share\IShare $share
804
	 * @return \OCP\Share\IShare[] List of deleted shares
805
	 */
806
	protected function deleteChildren(\OCP\Share\IShare $share) {
807
		$deletedShares = [];
808
809
		$provider = $this->factory->getProviderForType($share->getShareType());
810
811
		foreach ($provider->getChildren($share) as $child) {
812
			$deletedChildren = $this->deleteChildren($child);
813
			$deletedShares = array_merge($deletedShares, $deletedChildren);
814
815
			$provider->delete($child);
816
			$deletedShares[] = $child;
817
		}
818
819
		return $deletedShares;
820
	}
821
822
	/**
823
	 * Delete a share
824
	 *
825
	 * @param \OCP\Share\IShare $share
826
	 * @throws ShareNotFound
827
	 * @throws \InvalidArgumentException
828
	 */
829
	public function deleteShare(\OCP\Share\IShare $share) {
830
831
		try {
832
			$share->getFullId();
833
		} catch (\UnexpectedValueException $e) {
834
			throw new \InvalidArgumentException('Share does not have a full id');
835
		}
836
837
		$event = new GenericEvent($share);
838
		$this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event);
839
840
		// Get all children and delete them as well
841
		$deletedShares = $this->deleteChildren($share);
842
843
		// Do the actual delete
844
		$provider = $this->factory->getProviderForType($share->getShareType());
845
		$provider->delete($share);
846
847
		// All the deleted shares caused by this delete
848
		$deletedShares[] = $share;
849
850
		// Emit post hook
851
		$event->setArgument('deletedShares', $deletedShares);
852
		$this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event);
853
	}
854
855
856
	/**
857
	 * Unshare a file as the recipient.
858
	 * This can be different from a regular delete for example when one of
859
	 * the users in a groups deletes that share. But the provider should
860
	 * handle this.
861
	 *
862
	 * @param \OCP\Share\IShare $share
863
	 * @param string $recipientId
864
	 */
865
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
866
		list($providerId, ) = $this->splitFullId($share->getFullId());
867
		$provider = $this->factory->getProvider($providerId);
868
869
		$provider->deleteFromSelf($share, $recipientId);
870
	}
871
872
	/**
873
	 * @inheritdoc
874
	 */
875
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
876
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
877
			throw new \InvalidArgumentException('Can\'t change target of link share');
878
		}
879
880
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
881
			throw new \InvalidArgumentException('Invalid recipient');
882
		}
883
884
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
885
			$sharedWith = $this->groupManager->get($share->getSharedWith());
886
			if (is_null($sharedWith)) {
887
				throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist');
888
			}
889
			$recipient = $this->userManager->get($recipientId);
890
			if (!$sharedWith->inGroup($recipient)) {
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by $this->userManager->get($recipientId) on line 889 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...
891
				throw new \InvalidArgumentException('Invalid recipient');
892
			}
893
		}
894
895
		list($providerId, ) = $this->splitFullId($share->getFullId());
896
		$provider = $this->factory->getProvider($providerId);
897
898
		$provider->move($share, $recipientId);
899
	}
900
901
	public function getSharesInFolder($userId, Folder $node, $reshares = false) {
902
		$providers = $this->factory->getAllProviders();
903
904
		return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
905
			$newShares = $provider->getSharesInFolder($userId, $node, $reshares);
906
			foreach ($newShares as $fid => $data) {
907
				if (!isset($shares[$fid])) {
908
					$shares[$fid] = [];
909
				}
910
911
				$shares[$fid] = array_merge($shares[$fid], $data);
912
			}
913
			return $shares;
914
		}, []);
915
	}
916
917
	/**
918
	 * @inheritdoc
919
	 */
920
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
921
		if ($path !== null &&
922
				!($path instanceof \OCP\Files\File) &&
923
				!($path instanceof \OCP\Files\Folder)) {
924
			throw new \InvalidArgumentException('invalid path');
925
		}
926
927
		try {
928
			$provider = $this->factory->getProviderForType($shareType);
929
		} catch (ProviderException $e) {
930
			return [];
931
		}
932
933
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
934
935
		/*
936
		 * Work around so we don't return expired shares but still follow
937
		 * proper pagination.
938
		 */
939
940
		$shares2 = [];
941
942
		while(true) {
943
			$added = 0;
944
			foreach ($shares as $share) {
945
946
				try {
947
					$this->checkExpireDate($share);
948
				} catch (ShareNotFound $e) {
949
					//Ignore since this basically means the share is deleted
950
					continue;
951
				}
952
953
				$added++;
954
				$shares2[] = $share;
955
956
				if (count($shares2) === $limit) {
957
					break;
958
				}
959
			}
960
961
			if (count($shares2) === $limit) {
962
				break;
963
			}
964
965
			// If there was no limit on the select we are done
966
			if ($limit === -1) {
967
				break;
968
			}
969
970
			$offset += $added;
971
972
			// Fetch again $limit shares
973
			$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
974
975
			// No more shares means we are done
976
			if (empty($shares)) {
977
				break;
978
			}
979
		}
980
981
		$shares = $shares2;
982
983
		return $shares;
984
	}
985
986
	/**
987
	 * @inheritdoc
988
	 */
989
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
990
		try {
991
			$provider = $this->factory->getProviderForType($shareType);
992
		} catch (ProviderException $e) {
993
			return [];
994
		}
995
996
		$shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
997
998
		// remove all shares which are already expired
999
		foreach ($shares as $key => $share) {
1000
			try {
1001
				$this->checkExpireDate($share);
1002
			} catch (ShareNotFound $e) {
1003
				unset($shares[$key]);
1004
			}
1005
		}
1006
1007
		return $shares;
1008
	}
1009
1010
	/**
1011
	 * @inheritdoc
1012
	 */
1013
	public function getShareById($id, $recipient = null) {
1014
		if ($id === null) {
1015
			throw new ShareNotFound();
1016
		}
1017
1018
		list($providerId, $id) = $this->splitFullId($id);
1019
1020
		try {
1021
			$provider = $this->factory->getProvider($providerId);
1022
		} catch (ProviderException $e) {
1023
			throw new ShareNotFound();
1024
		}
1025
1026
		$share = $provider->getShareById($id, $recipient);
1027
1028
		$this->checkExpireDate($share);
1029
1030
		return $share;
1031
	}
1032
1033
	/**
1034
	 * Get all the shares for a given path
1035
	 *
1036
	 * @param \OCP\Files\Node $path
1037
	 * @param int $page
1038
	 * @param int $perPage
1039
	 *
1040
	 * @return Share[]
1041
	 */
1042
	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...
1043
		return [];
1044
	}
1045
1046
	/**
1047
	 * Get the share by token possible with password
1048
	 *
1049
	 * @param string $token
1050
	 * @return Share
1051
	 *
1052
	 * @throws ShareNotFound
1053
	 */
1054
	public function getShareByToken($token) {
1055
		$share = null;
1056
		try {
1057
			if($this->shareApiAllowLinks()) {
1058
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
1059
				$share = $provider->getShareByToken($token);
1060
			}
1061
		} catch (ProviderException $e) {
1062
		} catch (ShareNotFound $e) {
1063
		}
1064
1065
1066
		// If it is not a link share try to fetch a federated share by token
1067 View Code Duplication
		if ($share === null) {
1068
			try {
1069
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE);
1070
				$share = $provider->getShareByToken($token);
1071
			} catch (ProviderException $e) {
1072
			} catch (ShareNotFound $e) {
1073
			}
1074
		}
1075
1076
		// If it is not a link share try to fetch a mail share by token
1077 View Code Duplication
		if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
1078
			try {
1079
				$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
1080
				$share = $provider->getShareByToken($token);
1081
			} catch (ProviderException $e) {
1082
			} catch (ShareNotFound $e) {
1083
			}
1084
		}
1085
1086
		if ($share === null) {
1087
			throw new ShareNotFound();
1088
		}
1089
1090
		$this->checkExpireDate($share);
1091
1092
		/*
1093
		 * Reduce the permissions for link shares if public upload is not enabled
1094
		 */
1095
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
1096
			!$this->shareApiLinkAllowPublicUpload()) {
1097
			$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
1098
		}
1099
1100
		return $share;
1101
	}
1102
1103
	protected function checkExpireDate($share) {
1104
		if ($share->getExpirationDate() !== null &&
1105
			$share->getExpirationDate() <= new \DateTime()) {
1106
			$this->deleteShare($share);
1107
			throw new ShareNotFound();
1108
		}
1109
1110
	}
1111
1112
	/**
1113
	 * Verify the password of a public share
1114
	 *
1115
	 * @param \OCP\Share\IShare $share
1116
	 * @param string $password
1117
	 * @return bool
1118
	 */
1119
	public function checkPassword(\OCP\Share\IShare $share, $password) {
1120
		$passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK
1121
			|| $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL;
1122
		if (!$passwordProtected) {
1123
			//TODO maybe exception?
1124
			return false;
1125
		}
1126
1127
		if ($password === null || $share->getPassword() === null) {
1128
			return false;
1129
		}
1130
1131
		$newHash = '';
1132
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1133
			return false;
1134
		}
1135
1136
		if (!empty($newHash)) {
1137
			$share->setPassword($newHash);
1138
			$provider = $this->factory->getProviderForType($share->getShareType());
1139
			$provider->update($share);
1140
		}
1141
1142
		return true;
1143
	}
1144
1145
	/**
1146
	 * @inheritdoc
1147
	 */
1148
	public function userDeleted($uid) {
1149
		$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];
1150
1151
		foreach ($types as $type) {
1152
			try {
1153
				$provider = $this->factory->getProviderForType($type);
1154
			} catch (ProviderException $e) {
1155
				continue;
1156
			}
1157
			$provider->userDeleted($uid, $type);
1158
		}
1159
	}
1160
1161
	/**
1162
	 * @inheritdoc
1163
	 */
1164
	public function groupDeleted($gid) {
1165
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1166
		$provider->groupDeleted($gid);
1167
	}
1168
1169
	/**
1170
	 * @inheritdoc
1171
	 */
1172
	public function userDeletedFromGroup($uid, $gid) {
1173
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
1174
		$provider->userDeletedFromGroup($uid, $gid);
1175
	}
1176
1177
	/**
1178
	 * Get access list to a path. This means
1179
	 * all the users and groups that can access a given path.
1180
	 *
1181
	 * Consider:
1182
	 * -root
1183
	 * |-folder1
1184
	 *  |-folder2
1185
	 *   |-fileA
1186
	 *
1187
	 * fileA is shared with user1
1188
	 * folder2 is shared with group2
1189
	 * folder1 is shared with user2
1190
	 *
1191
	 * Then the access list will to '/folder1/folder2/fileA' is:
1192
	 * [
1193
	 * 	'users' => ['user1', 'user2'],
1194
	 *  'groups' => ['group2']
1195
	 * ]
1196
	 *
1197
	 * This is required for encryption
1198
	 *
1199
	 * @param \OCP\Files\Node $path
1200
	 */
1201
	public function getAccessList(\OCP\Files\Node $path) {
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...
1202
	}
1203
1204
	/**
1205
	 * Create a new share
1206
	 * @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...
1207
	 */
1208
	public function newShare() {
1209
		return new \OC\Share20\Share($this->rootFolder, $this->userManager);
1210
	}
1211
1212
	/**
1213
	 * Is the share API enabled
1214
	 *
1215
	 * @return bool
1216
	 */
1217
	public function shareApiEnabled() {
1218
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1219
	}
1220
1221
	/**
1222
	 * Is public link sharing enabled
1223
	 *
1224
	 * @return bool
1225
	 */
1226
	public function shareApiAllowLinks() {
1227
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1228
	}
1229
1230
	/**
1231
	 * Is password on public link requires
1232
	 *
1233
	 * @return bool
1234
	 */
1235
	public function shareApiLinkEnforcePassword() {
1236
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1237
	}
1238
1239
	/**
1240
	 * Is default expire date enabled
1241
	 *
1242
	 * @return bool
1243
	 */
1244
	public function shareApiLinkDefaultExpireDate() {
1245
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1246
	}
1247
1248
	/**
1249
	 * Is default expire date enforced
1250
	 *`
1251
	 * @return bool
1252
	 */
1253
	public function shareApiLinkDefaultExpireDateEnforced() {
1254
		return $this->shareApiLinkDefaultExpireDate() &&
1255
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1256
	}
1257
1258
	/**
1259
	 * Number of default expire days
1260
	 *shareApiLinkAllowPublicUpload
1261
	 * @return int
1262
	 */
1263
	public function shareApiLinkDefaultExpireDays() {
1264
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1265
	}
1266
1267
	/**
1268
	 * Allow public upload on link shares
1269
	 *
1270
	 * @return bool
1271
	 */
1272
	public function shareApiLinkAllowPublicUpload() {
1273
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1274
	}
1275
1276
	/**
1277
	 * check if user can only share with group members
1278
	 * @return bool
1279
	 */
1280
	public function shareWithGroupMembersOnly() {
1281
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1282
	}
1283
1284
	/**
1285
	 * Check if users can share with groups
1286
	 * @return bool
1287
	 */
1288
	public function allowGroupSharing() {
1289
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1290
	}
1291
1292
	/**
1293
	 * Copied from \OC_Util::isSharingDisabledForUser
1294
	 *
1295
	 * TODO: Deprecate fuction from OC_Util
1296
	 *
1297
	 * @param string $userId
1298
	 * @return bool
1299
	 */
1300
	public function sharingDisabledForUser($userId) {
1301
		if ($userId === null) {
1302
			return false;
1303
		}
1304
1305
		if (isset($this->sharingDisabledForUsersCache[$userId])) {
1306
			return $this->sharingDisabledForUsersCache[$userId];
1307
		}
1308
1309
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1310
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1311
			$excludedGroups = json_decode($groupsList);
1312 View Code Duplication
			if (is_null($excludedGroups)) {
1313
				$excludedGroups = explode(',', $groupsList);
1314
				$newValue = json_encode($excludedGroups);
1315
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1316
			}
1317
			$user = $this->userManager->get($userId);
1318
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1317 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...
1319 View Code Duplication
			if (!empty($usersGroups)) {
1320
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1321
				// if the user is only in groups which are disabled for sharing then
1322
				// sharing is also disabled for the user
1323
				if (empty($remainingGroups)) {
1324
					$this->sharingDisabledForUsersCache[$userId] = true;
1325
					return true;
1326
				}
1327
			}
1328
		}
1329
1330
		$this->sharingDisabledForUsersCache[$userId] = false;
1331
		return false;
1332
	}
1333
1334
	/**
1335
	 * @inheritdoc
1336
	 */
1337
	public function outgoingServer2ServerSharesAllowed() {
1338
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1339
	}
1340
1341
	/**
1342
	 * @inheritdoc
1343
	 */
1344
	public function shareProviderExists($shareType) {
1345
		try {
1346
			$this->factory->getProviderForType($shareType);
1347
		} catch (ProviderException $e) {
1348
			return false;
1349
		}
1350
1351
		return true;
1352
	}
1353
1354
}
1355