Completed
Push — master ( 8932ec...0de15a )
by Joas
14:56
created

Manager::userDeleted()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
/**
3
 * @author Arthur Schiwon <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Roeland Jago Douma <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OC\Share20;
25
26
use OC\Files\Mount\MoveableMount;
27
use OCP\Files\IRootFolder;
28
use OCP\Files\NotFoundException;
29
use OCP\IUserManager;
30
use OCP\Share\IManager;
31
use OCP\Share\IProviderFactory;
32
use OC\Share20\Exception\BackendError;
33
use OCP\IConfig;
34
use OCP\IL10N;
35
use OCP\ILogger;
36
use OCP\Security\ISecureRandom;
37
use OCP\Security\IHasher;
38
use OCP\Files\Mount\IMountManager;
39
use OCP\IGroupManager;
40
use OCP\Files\File;
41
use OCP\Files\Folder;
42
43
use OCP\Share\Exceptions\ShareNotFound;
44
use OCP\Share\Exceptions\GenericShareException;
45
46
/**
47
 * This class is the communication hub for all sharing related operations.
48
 */
49
class Manager implements IManager {
50
51
	/** @var IProviderFactory */
52
	private $factory;
53
	/** @var ILogger */
54
	private $logger;
55
	/** @var IConfig */
56
	private $config;
57
	/** @var ISecureRandom */
58
	private $secureRandom;
59
	/** @var IHasher */
60
	private $hasher;
61
	/** @var IMountManager */
62
	private $mountManager;
63
	/** @var IGroupManager */
64
	private $groupManager;
65
	/** @var IL10N */
66
	private $l;
67
	/** @var IUserManager */
68
	private $userManager;
69
	/** @var IRootFolder */
70
	private $rootFolder;
71
72
	/**
73
	 * Manager constructor.
74
	 *
75
	 * @param ILogger $logger
76
	 * @param IConfig $config
77
	 * @param ISecureRandom $secureRandom
78
	 * @param IHasher $hasher
79
	 * @param IMountManager $mountManager
80
	 * @param IGroupManager $groupManager
81
	 * @param IL10N $l
82
	 * @param IProviderFactory $factory
83
	 * @param IUserManager $userManager
84
	 * @param IRootFolder $rootFolder
85
	 */
86
	public function __construct(
87
			ILogger $logger,
88
			IConfig $config,
89
			ISecureRandom $secureRandom,
90
			IHasher $hasher,
91
			IMountManager $mountManager,
92
			IGroupManager $groupManager,
93
			IL10N $l,
94
			IProviderFactory $factory,
95
			IUserManager $userManager,
96
			IRootFolder $rootFolder
97
	) {
98
		$this->logger = $logger;
99
		$this->config = $config;
100
		$this->secureRandom = $secureRandom;
101
		$this->hasher = $hasher;
102
		$this->mountManager = $mountManager;
103
		$this->groupManager = $groupManager;
104
		$this->l = $l;
105
		$this->factory = $factory;
106
		$this->userManager = $userManager;
107
		$this->rootFolder = $rootFolder;
108
	}
109
110
	/**
111
	 * Convert from a full share id to a tuple (providerId, shareId)
112
	 *
113
	 * @param string $id
114
	 * @return string[]
115
	 */
116
	private function splitFullId($id) {
117
		return explode(':', $id, 2);
118
	}
119
120
	/**
121
	 * Verify if a password meets all requirements
122
	 *
123
	 * @param string $password
124
	 * @throws \Exception
125
	 */
126
	protected function verifyPassword($password) {
127
		if ($password === null) {
128
			// No password is set, check if this is allowed.
129
			if ($this->shareApiLinkEnforcePassword()) {
130
				throw new \InvalidArgumentException('Passwords are enforced for link shares');
131
			}
132
133
			return;
134
		}
135
136
		// Let others verify the password
137
		$accepted = true;
138
		$message = '';
139
		\OCP\Util::emitHook('\OC\Share', 'verifyPassword', [
140
				'password' => $password,
141
				'accepted' => &$accepted,
142
				'message' => &$message
143
		]);
144
145
		if (!$accepted) {
146
			throw new \Exception($message);
147
		}
148
	}
149
150
	/**
151
	 * Check for generic requirements before creating a share
152
	 *
153
	 * @param \OCP\Share\IShare $share
154
	 * @throws \InvalidArgumentException
155
	 * @throws GenericShareException
156
	 */
157
	protected function generalCreateChecks(\OCP\Share\IShare $share) {
158
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
159
			// We expect a valid user as sharedWith for user shares
160
			if (!$this->userManager->userExists($share->getSharedWith())) {
161
				throw new \InvalidArgumentException('SharedWith is not a valid user');
162
			}
163
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
164
			// We expect a valid group as sharedWith for group shares
165
			if (!$this->groupManager->groupExists($share->getSharedWith())) {
166
				throw new \InvalidArgumentException('SharedWith is not a valid group');
167
			}
168
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
169
			if ($share->getSharedWith() !== null) {
170
				throw new \InvalidArgumentException('SharedWith should be empty');
171
			}
172
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) {
173
			if ($share->getSharedWith() === null) {
174
				throw new \InvalidArgumentException('SharedWith should not be empty');
175
			}
176
		} else {
177
			// We can't handle other types yet
178
			throw new \InvalidArgumentException('unkown share type');
179
		}
180
181
		// Verify the initiator of the share is set
182
		if ($share->getSharedBy() === null) {
183
			throw new \InvalidArgumentException('SharedBy should be set');
184
		}
185
186
		// Cannot share with yourself
187 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
			$share->getSharedWith() === $share->getSharedBy()) {
189
			throw new \InvalidArgumentException('Can\'t share with yourself');
190
		}
191
192
		// The path should be set
193
		if ($share->getNode() === null) {
194
			throw new \InvalidArgumentException('Path should be set');
195
		}
196
197
		// And it should be a file or a folder
198
		if (!($share->getNode() instanceof \OCP\Files\File) &&
199
				!($share->getNode() instanceof \OCP\Files\Folder)) {
200
			throw new \InvalidArgumentException('Path should be either a file or a folder');
201
		}
202
203
		// And you can't share your rootfolder
204
		if ($this->rootFolder->getUserFolder($share->getSharedBy())->getPath() === $share->getNode()->getPath()) {
205
			throw new \InvalidArgumentException('You can\'t share your root folder');
206
		}
207
208
		// Check if we actually have share permissions
209 View Code Duplication
		if (!$share->getNode()->isShareable()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
			$message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]);
211
			throw new GenericShareException($message_t, $message_t, 404);
212
		}
213
214
		// Permissions should be set
215
		if ($share->getPermissions() === null) {
216
			throw new \InvalidArgumentException('A share requires permissions');
217
		}
218
219
		/*
220
		 * Quick fix for #23536
221
		 * Non moveable mount points do not have update and delete permissions
222
		 * while we 'most likely' do have that on the storage.
223
		 */
224
		$permissions = $share->getNode()->getPermissions();
225
		$mount = $share->getNode()->getMountPoint();
226
		if (!($mount instanceof MoveableMount)) {
227
			$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE;
228
		}
229
230
		// Check that we do not share with more permissions than we have
231 View Code Duplication
		if ($share->getPermissions() & ~$permissions) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
			$message_t = $this->l->t('Cannot increase permissions of %s', [$share->getNode()->getPath()]);
233
			throw new GenericShareException($message_t, $message_t, 404);
234
		}
235
236
		// Check that read permissions are always set
237
		if (($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
238
			throw new \InvalidArgumentException('Shares need at least read permissions');
239
		}
240
	}
241
242
	/**
243
	 * Validate if the expiration date fits the system settings
244
	 *
245
	 * @param \OCP\Share\IShare $share The share to validate the expiration date of
246
	 * @return \OCP\Share\IShare The modified share object
247
	 * @throws GenericShareException
248
	 * @throws \InvalidArgumentException
249
	 * @throws \Exception
250
	 */
251
	protected function validateExpirationDate(\OCP\Share\IShare $share) {
252
253
		$expirationDate = $share->getExpirationDate();
254
255
		if ($expirationDate !== null) {
256
			//Make sure the expiration date is a date
257
			$expirationDate->setTime(0, 0, 0);
258
259
			$date = new \DateTime();
260
			$date->setTime(0, 0, 0);
261 View Code Duplication
			if ($date >= $expirationDate) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
262
				$message = $this->l->t('Expiration date is in the past');
263
				throw new GenericShareException($message, $message, 404);
264
			}
265
		}
266
267
		// If expiredate is empty set a default one if there is a default
268
		$fullId = null;
269
		try {
270
			$fullId = $share->getFullId();
271
		} catch (\UnexpectedValueException $e) {
272
			// This is a new share
273
		}
274
275
		if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
276
			$expirationDate = new \DateTime();
277
			$expirationDate->setTime(0,0,0);
278
			$expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
279
		}
280
281
		// If we enforce the expiration date check that is does not exceed
282
		if ($this->shareApiLinkDefaultExpireDateEnforced()) {
283
			if ($expirationDate === null) {
284
				throw new \InvalidArgumentException('Expiration date is enforced');
285
			}
286
287
			$date = new \DateTime();
288
			$date->setTime(0, 0, 0);
289
			$date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D'));
290 View Code Duplication
			if ($date < $expirationDate) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
				$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
292
				throw new GenericShareException($message, $message, 404);
293
			}
294
		}
295
296
		$accepted = true;
297
		$message = '';
298
		\OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
299
			'expirationDate' => &$expirationDate,
300
			'accepted' => &$accepted,
301
			'message' => &$message,
302
			'passwordSet' => $share->getPassword() !== null,
303
		]);
304
305
		if (!$accepted) {
306
			throw new \Exception($message);
307
		}
308
309
		$share->setExpirationDate($expirationDate);
310
311
		return $share;
312
	}
313
314
	/**
315
	 * Check for pre share requirements for user shares
316
	 *
317
	 * @param \OCP\Share\IShare $share
318
	 * @throws \Exception
319
	 */
320
	protected function userCreateChecks(\OCP\Share\IShare $share) {
321
		// Check if we can share with group members only
322
		if ($this->shareWithGroupMembersOnly()) {
323
			$sharedBy = $this->userManager->get($share->getSharedBy());
324
			$sharedWith = $this->userManager->get($share->getSharedWith());
325
			// Verify we can share with this user
326
			$groups = array_intersect(
327
					$this->groupManager->getUserGroupIds($sharedBy),
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 323 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...
328
					$this->groupManager->getUserGroupIds($sharedWith)
0 ignored issues
show
Bug introduced by
It seems like $sharedWith defined by $this->userManager->get($share->getSharedWith()) on line 324 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...
329
			);
330
			if (empty($groups)) {
331
				throw new \Exception('Only sharing with group members is allowed');
332
			}
333
		}
334
335
		/*
336
		 * TODO: Could be costly, fix
337
		 *
338
		 * Also this is not what we want in the future.. then we want to squash identical shares.
339
		 */
340
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER);
341
		$existingShares = $provider->getSharesByPath($share->getNode());
342
		foreach($existingShares as $existingShare) {
343
			// Ignore if it is the same share
344
			try {
345
				if ($existingShare->getFullId() === $share->getFullId()) {
346
					continue;
347
				}
348
			} catch (\UnexpectedValueException $e) {
349
				//Shares are not identical
350
			}
351
352
			// Identical share already existst
353
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
354
				throw new \Exception('Path already shared with this user');
355
			}
356
357
			// The share is already shared with this user via a group share
358
			if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
359
				$group = $this->groupManager->get($existingShare->getSharedWith());
360
				$user = $this->userManager->get($share->getSharedWith());
361
362
				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 360 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...
363
					throw new \Exception('Path already shared with this user');
364
				}
365
			}
366
		}
367
	}
368
369
	/**
370
	 * Check for pre share requirements for group shares
371
	 *
372
	 * @param \OCP\Share\IShare $share
373
	 * @throws \Exception
374
	 */
375
	protected function groupCreateChecks(\OCP\Share\IShare $share) {
376
		// Verify group shares are allowed
377
		if (!$this->allowGroupSharing()) {
378
			throw new \Exception('Group sharing is now allowed');
379
		}
380
381
		// Verify if the user can share with this group
382
		if ($this->shareWithGroupMembersOnly()) {
383
			$sharedBy = $this->userManager->get($share->getSharedBy());
384
			$sharedWith = $this->groupManager->get($share->getSharedWith());
385
			if (!$sharedWith->inGroup($sharedBy)) {
0 ignored issues
show
Bug introduced by
It seems like $sharedBy defined by $this->userManager->get($share->getSharedBy()) on line 383 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...
386
				throw new \Exception('Only sharing within your own groups is allowed');
387
			}
388
		}
389
390
		/*
391
		 * TODO: Could be costly, fix
392
		 *
393
		 * Also this is not what we want in the future.. then we want to squash identical shares.
394
		 */
395
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP);
396
		$existingShares = $provider->getSharesByPath($share->getNode());
397
		foreach($existingShares as $existingShare) {
398
			try {
399
				if ($existingShare->getFullId() === $share->getFullId()) {
400
					continue;
401
				}
402
			} catch (\UnexpectedValueException $e) {
403
				//It is a new share so just continue
404
			}
405
406
			if ($existingShare->getSharedWith() === $share->getSharedWith()) {
407
				throw new \Exception('Path already shared with this group');
408
			}
409
		}
410
	}
411
412
	/**
413
	 * Check for pre share requirements for link shares
414
	 *
415
	 * @param \OCP\Share\IShare $share
416
	 * @throws \Exception
417
	 */
418
	protected function linkCreateChecks(\OCP\Share\IShare $share) {
419
		// Are link shares allowed?
420
		if (!$this->shareApiAllowLinks()) {
421
			throw new \Exception('Link sharing not allowed');
422
		}
423
424
		// Link shares by definition can't have share permissions
425
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) {
426
			throw new \InvalidArgumentException('Link shares can\'t have reshare permissions');
427
		}
428
429
		// We don't allow deletion on link shares
430
		if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
431
			throw new \InvalidArgumentException('Link shares can\'t have delete permissions');
432
		}
433
434
		// Check if public upload is allowed
435
		if (!$this->shareApiLinkAllowPublicUpload() &&
436
			($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE))) {
437
			throw new \InvalidArgumentException('Public upload not allowed');
438
		}
439
	}
440
441
	/**
442
	 * To make sure we don't get invisible link shares we set the parent
443
	 * of a link if it is a reshare. This is a quick word around
444
	 * until we can properly display multiple link shares in the UI
445
	 *
446
	 * See: https://github.com/owncloud/core/issues/22295
447
	 *
448
	 * FIXME: Remove once multiple link shares can be properly displayed
449
	 *
450
	 * @param \OCP\Share\IShare $share
451
	 */
452
	protected function setLinkParent(\OCP\Share\IShare $share) {
453
454
		// No sense in checking if the method is not there.
455
		if (method_exists($share, 'setParent')) {
456
			$storage = $share->getNode()->getStorage();
457
			if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
458
				$share->setParent($storage->getShareId());
459
			}
460
		};
461
	}
462
463
	/**
464
	 * @param File|Folder $path
465
	 */
466
	protected function pathCreateChecks($path) {
467
		// Make sure that we do not share a path that contains a shared mountpoint
468
		if ($path instanceof \OCP\Files\Folder) {
469
			$mounts = $this->mountManager->findIn($path->getPath());
470
			foreach($mounts as $mount) {
471
				if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) {
472
					throw new \InvalidArgumentException('Path contains files shared with you');
473
				}
474
			}
475
		}
476
	}
477
478
	/**
479
	 * Check if the user that is sharing can actually share
480
	 *
481
	 * @param \OCP\Share\IShare $share
482
	 * @throws \Exception
483
	 */
484
	protected function canShare(\OCP\Share\IShare $share) {
485
		if (!$this->shareApiEnabled()) {
486
			throw new \Exception('The share API is disabled');
487
		}
488
489
		if ($this->sharingDisabledForUser($share->getSharedBy())) {
490
			throw new \Exception('You are not allowed to share');
491
		}
492
	}
493
494
	/**
495
	 * Share a path
496
	 *
497
	 * @param \OCP\Share\IShare $share
498
	 * @return Share The share object
499
	 * @throws \Exception
500
	 *
501
	 * TODO: handle link share permissions or check them
502
	 */
503
	public function createShare(\OCP\Share\IShare $share) {
504
		$this->canShare($share);
505
506
		$this->generalCreateChecks($share);
507
508
		//Verify share type
509
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
510
			$this->userCreateChecks($share);
511
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
512
			$this->groupCreateChecks($share);
513
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
514
			$this->linkCreateChecks($share);
515
			$this->setLinkParent($share);
516
517
			/*
518
			 * For now ignore a set token.
519
			 */
520
			$share->setToken(
521
				$this->secureRandom->generate(
522
					\OC\Share\Constants::TOKEN_LENGTH,
523
					\OCP\Security\ISecureRandom::CHAR_LOWER.
524
					\OCP\Security\ISecureRandom::CHAR_UPPER.
525
					\OCP\Security\ISecureRandom::CHAR_DIGITS
526
				)
527
			);
528
529
			//Verify the expiration date
530
			$this->validateExpirationDate($share);
531
532
			//Verify the password
533
			$this->verifyPassword($share->getPassword());
534
535
			// If a password is set. Hash it!
536
			if ($share->getPassword() !== null) {
537
				$share->setPassword($this->hasher->hash($share->getPassword()));
538
			}
539
		}
540
541
		// Verify if there are any issues with the path
542
		$this->pathCreateChecks($share->getNode());
543
544
		/*
545
		 * On creation of a share the owner is always the owner of the path
546
		 * Except for mounted federated shares.
547
		 */
548
		$storage = $share->getNode()->getStorage();
549
		if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
550
			$parent = $share->getNode()->getParent();
551
			while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
552
				$parent = $parent->getParent();
553
			}
554
			$share->setShareOwner($parent->getOwner()->getUID());
555
		} else {
556
			$share->setShareOwner($share->getNode()->getOwner()->getUID());
557
		}
558
559
		// Cannot share with the owner
560 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
561
			$share->getSharedWith() === $share->getShareOwner()) {
562
			throw new \InvalidArgumentException('Can\'t share with the share owner');
563
		}
564
565
		// Generate the target
566
		$target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName();
567
		$target = \OC\Files\Filesystem::normalizePath($target);
568
		$share->setTarget($target);
569
570
		// Pre share hook
571
		$run = true;
572
		$error = '';
573
		$preHookData = [
574
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
575
			'itemSource' => $share->getNode()->getId(),
576
			'shareType' => $share->getShareType(),
577
			'uidOwner' => $share->getSharedBy(),
578
			'permissions' => $share->getPermissions(),
579
			'fileSource' => $share->getNode()->getId(),
580
			'expiration' => $share->getExpirationDate(),
581
			'token' => $share->getToken(),
582
			'itemTarget' => $share->getTarget(),
583
			'shareWith' => $share->getSharedWith(),
584
			'run' => &$run,
585
			'error' => &$error,
586
		];
587
		\OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData);
588
589
		if ($run === false) {
590
			throw new \Exception($error);
591
		}
592
593
		$provider = $this->factory->getProviderForType($share->getShareType());
594
		$share = $provider->create($share);
595
596
		// Post share hook
597
		$postHookData = [
598
			'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
599
			'itemSource' => $share->getNode()->getId(),
600
			'shareType' => $share->getShareType(),
601
			'uidOwner' => $share->getSharedBy(),
602
			'permissions' => $share->getPermissions(),
603
			'fileSource' => $share->getNode()->getId(),
604
			'expiration' => $share->getExpirationDate(),
605
			'token' => $share->getToken(),
606
			'id' => $share->getId(),
607
			'shareWith' => $share->getSharedWith(),
608
			'itemTarget' => $share->getTarget(),
609
			'fileTarget' => $share->getTarget(),
610
		];
611
612
		\OC_Hook::emit('OCP\Share', 'post_shared', $postHookData);
613
614
		return $share;
615
	}
616
617
	/**
618
	 * Update a share
619
	 *
620
	 * @param \OCP\Share\IShare $share
621
	 * @return \OCP\Share\IShare The share object
622
	 * @throws \InvalidArgumentException
623
	 */
624
	public function updateShare(\OCP\Share\IShare $share) {
625
		$expirationDateUpdated = false;
626
627
		$this->canShare($share);
628
629
		try {
630
			$originalShare = $this->getShareById($share->getFullId());
631
		} catch (\UnexpectedValueException $e) {
632
			throw new \InvalidArgumentException('Share does not have a full id');
633
		}
634
635
		// We can't change the share type!
636
		if ($share->getShareType() !== $originalShare->getShareType()) {
637
			throw new \InvalidArgumentException('Can\'t change share type');
638
		}
639
640
		// We can only change the recipient on user shares
641
		if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
642
		    $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) {
643
			throw new \InvalidArgumentException('Can only update recipient on user shares');
644
		}
645
646
		// Cannot share with the owner
647 View Code Duplication
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER &&
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
648
			$share->getSharedWith() === $share->getShareOwner()) {
649
			throw new \InvalidArgumentException('Can\'t share with the share owner');
650
		}
651
652
		$this->generalCreateChecks($share);
653
654
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
655
			$this->userCreateChecks($share);
656
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
657
			$this->groupCreateChecks($share);
658
		} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
659
			$this->linkCreateChecks($share);
660
661
			// Password updated.
662
			if ($share->getPassword() !== $originalShare->getPassword()) {
663
				//Verify the password
664
				$this->verifyPassword($share->getPassword());
665
666
				// If a password is set. Hash it!
667
				if ($share->getPassword() !== null) {
668
					$share->setPassword($this->hasher->hash($share->getPassword()));
669
				}
670
			}
671
672
			if ($share->getExpirationDate() != $originalShare->getExpirationDate()) {
673
				//Verify the expiration date
674
				$this->validateExpirationDate($share);
675
				$expirationDateUpdated = true;
676
			}
677
		}
678
679
		$this->pathCreateChecks($share->getNode());
680
681
		// Now update the share!
682
		$provider = $this->factory->getProviderForType($share->getShareType());
683
		$share = $provider->update($share);
684
685
		if ($expirationDateUpdated === true) {
686
			\OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [
687
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
688
				'itemSource' => $share->getNode()->getId(),
689
				'date' => $share->getExpirationDate(),
690
				'uidOwner' => $share->getSharedBy(),
691
			]);
692
		}
693
694
		if ($share->getPassword() !== $originalShare->getPassword()) {
695
			\OC_Hook::emit('OCP\Share', 'post_update_password', [
696
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
697
				'itemSource' => $share->getNode()->getId(),
698
				'uidOwner' => $share->getSharedBy(),
699
				'token' => $share->getToken(),
700
				'disabled' => is_null($share->getPassword()),
701
			]);
702
		}
703
704
		if ($share->getPermissions() !== $originalShare->getPermissions()) {
705
			$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
706
			\OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
707
				'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
708
				'itemSource' => $share->getNode()->getId(),
709
				'shareType' => $share->getShareType(),
710
				'shareWith' => $share->getSharedWith(),
711
				'uidOwner' => $share->getSharedBy(),
712
				'permissions' => $share->getPermissions(),
713
				'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
714
			));
715
		}
716
717
		return $share;
718
	}
719
720
	/**
721
	 * Delete all the children of this share
722
	 * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
723
	 *
724
	 * @param \OCP\Share\IShare $share
725
	 * @return \OCP\Share\IShare[] List of deleted shares
726
	 */
727
	protected function deleteChildren(\OCP\Share\IShare $share) {
728
		$deletedShares = [];
729
730
		$provider = $this->factory->getProviderForType($share->getShareType());
731
732
		foreach ($provider->getChildren($share) as $child) {
733
			$deletedChildren = $this->deleteChildren($child);
734
			$deletedShares = array_merge($deletedShares, $deletedChildren);
735
736
			$provider->delete($child);
737
			$deletedShares[] = $child;
738
		}
739
740
		return $deletedShares;
741
	}
742
743
	/**
744
	 * Delete a share
745
	 *
746
	 * @param \OCP\Share\IShare $share
747
	 * @throws ShareNotFound
748
	 * @throws \InvalidArgumentException
749
	 */
750
	public function deleteShare(\OCP\Share\IShare $share) {
751
752
		try {
753
			$share->getFullId();
754
		} catch (\UnexpectedValueException $e) {
755
			throw new \InvalidArgumentException('Share does not have a full id');
756
		}
757
758
		$formatHookParams = function(\OCP\Share\IShare $share) {
759
			// Prepare hook
760
			$shareType = $share->getShareType();
761
			$sharedWith = '';
762
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
763
				$sharedWith = $share->getSharedWith();
764
			} else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
765
				$sharedWith = $share->getSharedWith();
766
			} else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) {
767
				$sharedWith = $share->getSharedWith();
768
			}
769
770
			$hookParams = [
771
				'id'         => $share->getId(),
772
				'itemType'   => $share->getNodeType(),
773
				'itemSource' => $share->getNodeId(),
774
				'shareType'  => $shareType,
775
				'shareWith'  => $sharedWith,
776
				'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '',
777
				'uidOwner'   => $share->getSharedBy(),
778
				'fileSource' => $share->getNodeId(),
779
				'fileTarget' => $share->getTarget()
780
			];
781
			return $hookParams;
782
		};
783
784
		$hookParams = $formatHookParams($share);
785
786
		// Emit pre-hook
787
		\OC_Hook::emit('OCP\Share', 'pre_unshare', $hookParams);
788
789
		// Get all children and delete them as well
790
		$deletedShares = $this->deleteChildren($share);
791
792
		// Do the actual delete
793
		$provider = $this->factory->getProviderForType($share->getShareType());
794
		$provider->delete($share);
795
796
		// All the deleted shares caused by this delete
797
		$deletedShares[] = $share;
798
799
		//Format hook info
800
		$formattedDeletedShares = array_map(function($share) use ($formatHookParams) {
801
			return $formatHookParams($share);
802
		}, $deletedShares);
803
804
		$hookParams['deletedShares'] = $formattedDeletedShares;
805
806
		// Emit post hook
807
		\OC_Hook::emit('OCP\Share', 'post_unshare', $hookParams);
808
	}
809
810
811
	/**
812
	 * Unshare a file as the recipient.
813
	 * This can be different from a regular delete for example when one of
814
	 * the users in a groups deletes that share. But the provider should
815
	 * handle this.
816
	 *
817
	 * @param \OCP\Share\IShare $share
818
	 * @param string $recipientId
819
	 */
820
	public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) {
821
		list($providerId, ) = $this->splitFullId($share->getId());
822
		$provider = $this->factory->getProvider($providerId);
823
824
		$provider->deleteFromSelf($share, $recipientId);
825
	}
826
827
	/**
828
	 * @inheritdoc
829
	 */
830
	public function moveShare(\OCP\Share\IShare $share, $recipientId) {
831
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
832
			throw new \InvalidArgumentException('Can\'t change target of link share');
833
		}
834
835
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) {
836
			throw new \InvalidArgumentException('Invalid recipient');
837
		}
838
839
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
840
			$sharedWith = $this->groupManager->get($share->getSharedWith());
841
			$recipient = $this->userManager->get($recipientId);
842
			if (!$sharedWith->inGroup($recipient)) {
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by $this->userManager->get($recipientId) on line 841 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...
843
				throw new \InvalidArgumentException('Invalid recipient');
844
			}
845
		}
846
847
		list($providerId, ) = $this->splitFullId($share->getId());
848
		$provider = $this->factory->getProvider($providerId);
849
850
		$provider->move($share, $recipientId);
851
	}
852
853
	/**
854
	 * @inheritdoc
855
	 */
856
	public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) {
857
		if ($path !== null &&
858
				!($path instanceof \OCP\Files\File) &&
859
				!($path instanceof \OCP\Files\Folder)) {
860
			throw new \InvalidArgumentException('invalid path');
861
		}
862
863
		$provider = $this->factory->getProviderForType($shareType);
864
865
		$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
866
867
		/*
868
		 * Work around so we don't return expired shares but still follow
869
		 * proper pagination.
870
		 */
871
		if ($shareType === \OCP\Share::SHARE_TYPE_LINK) {
872
			$shares2 = [];
873
			$today = new \DateTime();
874
875
			while(true) {
876
				$added = 0;
877
				foreach ($shares as $share) {
878
					// Check if the share is expired and if so delete it
879
					if ($share->getExpirationDate() !== null &&
880
						$share->getExpirationDate() <= $today
881
					) {
882
						try {
883
							$this->deleteShare($share);
884
						} catch (NotFoundException $e) {
885
							//Ignore since this basically means the share is deleted
886
						}
887
						continue;
888
					}
889
					$added++;
890
					$shares2[] = $share;
891
892
					if (count($shares2) === $limit) {
893
						break;
894
					}
895
				}
896
897
				if (count($shares2) === $limit) {
898
					break;
899
				}
900
901
				// If there was no limit on the select we are done
902
				if ($limit === -1) {
903
					break;
904
				}
905
906
				$offset += $added;
907
908
				// Fetch again $limit shares
909
				$shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset);
910
911
				// No more shares means we are done
912
				if (empty($shares)) {
913
					break;
914
				}
915
			}
916
917
			$shares = $shares2;
918
		}
919
920
		return $shares;
921
	}
922
923
	/**
924
	 * @inheritdoc
925
	 */
926
	public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) {
927
		$provider = $this->factory->getProviderForType($shareType);
928
929
		return $provider->getSharedWith($userId, $shareType, $node, $limit, $offset);
930
	}
931
932
	/**
933
	 * @inheritdoc
934
	 */
935
	public function getShareById($id, $recipient = null) {
936
		if ($id === null) {
937
			throw new ShareNotFound();
938
		}
939
940
		list($providerId, $id) = $this->splitFullId($id);
941
		$provider = $this->factory->getProvider($providerId);
942
943
		$share = $provider->getShareById($id, $recipient);
0 ignored issues
show
Bug introduced by
It seems like $recipient defined by parameter $recipient on line 935 can also be of type object<OCP\IUser>; however, OCP\Share\IShareProvider::getShareById() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
944
945
		// Validate link shares expiration date
946
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK &&
947
			$share->getExpirationDate() !== null &&
948
			$share->getExpirationDate() <= new \DateTime()) {
949
			$this->deleteShare($share);
950
			throw new ShareNotFound();
951
		}
952
953
		return $share;
954
	}
955
956
	/**
957
	 * Get all the shares for a given path
958
	 *
959
	 * @param \OCP\Files\Node $path
960
	 * @param int $page
961
	 * @param int $perPage
962
	 *
963
	 * @return Share[]
964
	 */
965
	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...
966
	}
967
968
	/**
969
	 * Get the share by token possible with password
970
	 *
971
	 * @param string $token
972
	 * @return Share
973
	 *
974
	 * @throws ShareNotFound
975
	 */
976
	public function getShareByToken($token) {
977
		$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
978
979
		$share = $provider->getShareByToken($token);
980
981
		if ($share->getExpirationDate() !== null &&
982
			$share->getExpirationDate() <= new \DateTime()) {
983
			$this->deleteShare($share);
984
			throw new ShareNotFound();
985
		}
986
987
		return $share;
988
	}
989
990
	/**
991
	 * Verify the password of a public share
992
	 *
993
	 * @param \OCP\Share\IShare $share
994
	 * @param string $password
995
	 * @return bool
996
	 */
997
	public function checkPassword(\OCP\Share\IShare $share, $password) {
998
		if ($share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK) {
999
			//TODO maybe exception?
1000
			return false;
1001
		}
1002
1003
		if ($password === null || $share->getPassword() === null) {
1004
			return false;
1005
		}
1006
1007
		$newHash = '';
1008
		if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) {
1009
			return false;
1010
		}
1011
1012
		if (!empty($newHash)) {
1013
			$share->setPassword($newHash);
1014
			$provider = $this->factory->getProviderForType($share->getShareType());
1015
			$provider->update($share);
1016
		}
1017
1018
		return true;
1019
	}
1020
1021
	/**
1022
	 * @inheritdoc
1023
	 */
1024
	public function userDeleted($uid) {
1025
		$types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE];
1026
1027
		foreach ($types as $type) {
1028
			$provider = $this->factory->getProviderForType($type);
1029
			$provider->userDeleted($uid, $type);
1030
		}
1031
	}
1032
1033
	/**
1034
	 * Get access list to a path. This means
1035
	 * all the users and groups that can access a given path.
1036
	 *
1037
	 * Consider:
1038
	 * -root
1039
	 * |-folder1
1040
	 *  |-folder2
1041
	 *   |-fileA
1042
	 *
1043
	 * fileA is shared with user1
1044
	 * folder2 is shared with group2
1045
	 * folder1 is shared with user2
1046
	 *
1047
	 * Then the access list will to '/folder1/folder2/fileA' is:
1048
	 * [
1049
	 * 	'users' => ['user1', 'user2'],
1050
	 *  'groups' => ['group2']
1051
	 * ]
1052
	 *
1053
	 * This is required for encryption
1054
	 *
1055
	 * @param \OCP\Files\Node $path
1056
	 */
1057
	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...
1058
	}
1059
1060
	/**
1061
	 * Create a new share
1062
	 * @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...
1063
	 */
1064
	public function newShare() {
1065
		return new \OC\Share20\Share($this->rootFolder);
1066
	}
1067
1068
	/**
1069
	 * Is the share API enabled
1070
	 *
1071
	 * @return bool
1072
	 */
1073
	public function shareApiEnabled() {
1074
		return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes';
1075
	}
1076
1077
	/**
1078
	 * Is public link sharing enabled
1079
	 *
1080
	 * @return bool
1081
	 */
1082
	public function shareApiAllowLinks() {
1083
		return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
1084
	}
1085
1086
	/**
1087
	 * Is password on public link requires
1088
	 *
1089
	 * @return bool
1090
	 */
1091
	public function shareApiLinkEnforcePassword() {
1092
		return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes';
1093
	}
1094
1095
	/**
1096
	 * Is default expire date enabled
1097
	 *
1098
	 * @return bool
1099
	 */
1100
	public function shareApiLinkDefaultExpireDate() {
1101
		return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
1102
	}
1103
1104
	/**
1105
	 * Is default expire date enforced
1106
	 *`
1107
	 * @return bool
1108
	 */
1109
	public function shareApiLinkDefaultExpireDateEnforced() {
1110
		return $this->shareApiLinkDefaultExpireDate() &&
1111
			$this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
1112
	}
1113
1114
	/**
1115
	 * Number of default expire days
1116
	 *shareApiLinkAllowPublicUpload
1117
	 * @return int
1118
	 */
1119
	public function shareApiLinkDefaultExpireDays() {
1120
		return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
1121
	}
1122
1123
	/**
1124
	 * Allow public upload on link shares
1125
	 *
1126
	 * @return bool
1127
	 */
1128
	public function shareApiLinkAllowPublicUpload() {
1129
		return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes';
1130
	}
1131
1132
	/**
1133
	 * check if user can only share with group members
1134
	 * @return bool
1135
	 */
1136
	public function shareWithGroupMembersOnly() {
1137
		return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
1138
	}
1139
1140
	/**
1141
	 * Check if users can share with groups
1142
	 * @return bool
1143
	 */
1144
	public function allowGroupSharing() {
1145
		return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes';
1146
	}
1147
1148
	/**
1149
	 * Copied from \OC_Util::isSharingDisabledForUser
1150
	 *
1151
	 * TODO: Deprecate fuction from OC_Util
1152
	 *
1153
	 * @param string $userId
1154
	 * @return bool
1155
	 */
1156
	public function sharingDisabledForUser($userId) {
1157
		if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') {
1158
			$groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
1159
			$excludedGroups = json_decode($groupsList);
1160 View Code Duplication
			if (is_null($excludedGroups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1161
				$excludedGroups = explode(',', $groupsList);
1162
				$newValue = json_encode($excludedGroups);
1163
				$this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue);
1164
			}
1165
			$user = $this->userManager->get($userId);
1166
			$usersGroups = $this->groupManager->getUserGroupIds($user);
0 ignored issues
show
Bug introduced by
It seems like $user defined by $this->userManager->get($userId) on line 1165 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...
1167 View Code Duplication
			if (!empty($usersGroups)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1168
				$remainingGroups = array_diff($usersGroups, $excludedGroups);
1169
				// if the user is only in groups which are disabled for sharing then
1170
				// sharing is also disabled for the user
1171
				if (empty($remainingGroups)) {
1172
					return true;
1173
				}
1174
			}
1175
		}
1176
		return false;
1177
	}
1178
1179
	/**
1180
	 * @inheritdoc
1181
	 */
1182
	public function outgoingServer2ServerSharesAllowed() {
1183
		return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
1184
	}
1185
1186
}
1187