Skip to content

Commit 516ea14

Browse files
committed
refactor(files_sharing,Share20): Use Interaction API to apply restrictions
Signed-off-by: provokateurin <kate@provokateurin.de>
1 parent 527bad7 commit 516ea14

3 files changed

Lines changed: 40 additions & 141 deletions

File tree

apps/files_sharing/lib/Controller/ShareAPIController.php

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -718,10 +718,6 @@ public function createShare(
718718
$share->setSharedWith($shareWith);
719719
$share->setPermissions($permissions);
720720
} elseif ($shareType === IShare::TYPE_GROUP) {
721-
if (!$this->shareManager->allowGroupSharing()) {
722-
throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator'));
723-
}
724-
725721
// Valid group is required to share
726722
if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) {
727723
throw new OCSNotFoundException($this->l->t('Please specify a valid group'));
@@ -731,11 +727,6 @@ public function createShare(
731727
} elseif ($shareType === IShare::TYPE_LINK
732728
|| $shareType === IShare::TYPE_EMAIL) {
733729

734-
// Can we even share links?
735-
if (!$this->shareManager->shareApiAllowLinks()) {
736-
throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator'));
737-
}
738-
739730
$this->validateLinkSharePermissions($node, $permissions, $hasPublicUpload);
740731
$share->setPermissions($permissions);
741732

@@ -769,10 +760,6 @@ public function createShare(
769760
$share->setSendPasswordByTalk(true);
770761
}
771762
} elseif ($shareType === IShare::TYPE_REMOTE) {
772-
if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) {
773-
throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
774-
}
775-
776763
if ($shareWith === null) {
777764
throw new OCSNotFoundException($this->l->t('Please specify a valid federated account ID'));
778765
}
@@ -781,10 +768,6 @@ public function createShare(
781768
$share->setPermissions($permissions);
782769
$share->setSharedWithDisplayName($this->getCachedFederatedDisplayName($shareWith, false));
783770
} elseif ($shareType === IShare::TYPE_REMOTE_GROUP) {
784-
if (!$this->shareManager->outgoingServer2ServerGroupSharesAllowed()) {
785-
throw new OCSForbiddenException($this->l->t('Sharing %1$s failed because the back end does not allow shares from type %2$s', [$node->getPath(), $shareType]));
786-
}
787-
788771
if ($shareWith === null) {
789772
throw new OCSNotFoundException($this->l->t('Please specify a valid federated group ID'));
790773
}
@@ -1043,12 +1026,6 @@ private function validateLinkSharePermissions(Node $node, int $permissions, ?boo
10431026
&& ($this->hasPermission($permissions, Constants::PERMISSION_UPDATE) || $this->hasPermission($permissions, Constants::PERMISSION_DELETE))) {
10441027
throw new OCSBadRequestException($this->l->t('Share must have READ permission if UPDATE or DELETE permission is set'));
10451028
}
1046-
1047-
// Check if public uploading was disabled
1048-
if ($this->hasPermission($permissions, Constants::PERMISSION_CREATE)
1049-
&& !$this->shareManager->shareApiLinkAllowPublicUpload()) {
1050-
throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator'));
1051-
}
10521029
}
10531030

10541031
/**

lib/private/Share20/Manager.php

Lines changed: 39 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
use OCP\Files\Folder;
2929
use OCP\Files\IRootFolder;
3030
use OCP\Files\Mount\IMountManager;
31-
use OCP\Files\Mount\IMovableMount;
3231
use OCP\Files\Mount\IShareOwnerlessMount;
3332
use OCP\Files\Node;
3433
use OCP\Files\NotFoundException;
@@ -39,6 +38,18 @@
3938
use OCP\IDBConnection;
4039
use OCP\IGroupManager;
4140
use OCP\IL10N;
41+
use OCP\Interaction\Actions\ShareAction;
42+
use OCP\Interaction\Receivers\CircleReceiver;
43+
use OCP\Interaction\Receivers\DeckReceiver;
44+
use OCP\Interaction\Receivers\EmailReceiver;
45+
use OCP\Interaction\Receivers\GroupReceiver;
46+
use OCP\Interaction\Receivers\LinkReceiver;
47+
use OCP\Interaction\Receivers\RemoteGroupReceiver;
48+
use OCP\Interaction\Receivers\RemoteUserReceiver;
49+
use OCP\Interaction\Receivers\RoomReceiver;
50+
use OCP\Interaction\Receivers\UserReceiver;
51+
use OCP\Interaction\Resources\NodeResource;
52+
use OCP\Interaction\RestrictInteractionEvent;
4253
use OCP\IUser;
4354
use OCP\IUserManager;
4455
use OCP\IUserSession;
@@ -149,7 +160,7 @@ protected function verifyPassword(?string $password): void {
149160
*
150161
* @suppress PhanUndeclaredClassMethod
151162
*/
152-
protected function generalCreateChecks(IShare $share, bool $isUpdate = false): void {
163+
protected function generalChecks(IShare $share): void {
153164
if ($share->getShareType() === IShare::TYPE_USER) {
154165
// We expect a valid user as sharedWith for user shares
155166
if (!$this->userManager->userExists($share->getSharedWith())) {
@@ -212,21 +223,6 @@ protected function generalCreateChecks(IShare $share, bool $isUpdate = false): v
212223
throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
213224
}
214225

215-
// And you cannot share your rootfolder
216-
if ($this->userManager->userExists($share->getSharedBy())) {
217-
$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
218-
} else {
219-
$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
220-
}
221-
if ($userFolder->getId() === $share->getNode()->getId()) {
222-
throw new \InvalidArgumentException($this->l->t('You cannot share your root folder'));
223-
}
224-
225-
// Check if we actually have share permissions
226-
if (!$share->getNode()->isShareable()) {
227-
throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 404);
228-
}
229-
230226
// Permissions should be set
231227
if ($share->getPermissions() === null) {
232228
throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
@@ -237,45 +233,32 @@ protected function generalCreateChecks(IShare $share, bool $isUpdate = false): v
237233
throw new \InvalidArgumentException($this->l->t('Valid permissions are required for sharing'));
238234
}
239235

240-
// Single file shares should never have delete or create permissions
241-
if (($share->getNode() instanceof File)
242-
&& (($share->getPermissions() & (Constants::PERMISSION_CREATE | Constants::PERMISSION_DELETE)) !== 0)) {
243-
throw new \InvalidArgumentException($this->l->t('File shares cannot have create or delete permissions'));
244-
}
236+
$action = new ShareAction($share->getPermissions());
237+
$receiver = match ($share->getShareType()) {
238+
IShare::TYPE_USER => new UserReceiver($share->getSharedWith()),
239+
IShare::TYPE_GROUP => new GroupReceiver($share->getSharedWith()),
240+
IShare::TYPE_LINK => new LinkReceiver(),
241+
IShare::TYPE_EMAIL => new EmailReceiver($share->getSharedWith()),
242+
IShare::TYPE_REMOTE => new RemoteUserReceiver($share->getSharedWith()),
243+
IShare::TYPE_CIRCLE => new CircleReceiver($share->getSharedWith()),
244+
IShare::TYPE_REMOTE_GROUP => new RemoteGroupReceiver($share->getSharedWith()),
245+
IShare::TYPE_ROOM => new RoomReceiver($share->getSharedWith()),
246+
IShare::TYPE_DECK => new DeckReceiver((int)$share->getSharedWith()),
247+
default => throw new \InvalidArgumentException('Unknown share type.'),
248+
};
245249

246-
$permissions = 0;
247-
$nodesForUser = $userFolder->getById($share->getNodeId());
248-
foreach ($nodesForUser as $node) {
249-
if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof IMovableMount) {
250-
// for the root of non-movable mount, the permissions we see if limited by the mount itself,
251-
// so we instead use the "raw" permissions from the storage
252-
$permissions |= $node->getStorage()->getPermissions('');
253-
} else {
254-
$permissions |= $node->getPermissions();
255-
}
256-
}
257-
258-
// Check that we do not share with more permissions than we have
259-
if ($share->getPermissions() & ~$permissions) {
260-
$path = $userFolder->getRelativePath($share->getNode()->getPath());
261-
throw new GenericShareException($this->l->t('Cannot increase permissions of %s', [$path]), code: 404);
250+
$userIds = [];
251+
if ($share->getShareOwner() !== null && $this->userManager->userExists($share->getShareOwner())) {
252+
$userIds[] = $share->getShareOwner();
262253
}
263-
264-
// Check that read permissions are always set
265-
// Link shares are allowed to have no read permissions to allow upload to hidden folders
266-
$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
267-
|| $share->getShareType() === IShare::TYPE_EMAIL;
268-
if (!$noReadPermissionRequired
269-
&& ($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
270-
throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
254+
if ($share->getSharedBy() !== $share->getShareOwner() && $this->userManager->userExists($share->getSharedBy())) {
255+
$userIds[] = $share->getSharedBy();
271256
}
272257

273-
if ($share->getNode() instanceof File) {
274-
if ($share->getPermissions() & Constants::PERMISSION_DELETE) {
275-
throw new GenericShareException($this->l->t('Files cannot be shared with delete permissions'));
276-
}
277-
if ($share->getPermissions() & Constants::PERMISSION_CREATE) {
278-
throw new GenericShareException($this->l->t('Files cannot be shared with create permissions'));
258+
foreach ($userIds as $userId) {
259+
$resource = new NodeResource($share->getNodeId(), $userId);
260+
if ((new RestrictInteractionEvent($userId, null, $resource, $action, $receiver))->isInteractionRestricted()) {
261+
throw new GenericShareException($this->l->t('You are not allowed to share %s', [$share->getNode()->getName()]), code: 403);
279262
}
280263
}
281264
}
@@ -432,25 +415,6 @@ protected function validateExpirationDateLink(IShare $share): IShare {
432415
* @throws \Exception
433416
*/
434417
protected function userCreateChecks(IShare $share): void {
435-
// Check if we can share with group members only
436-
if ($this->shareWithGroupMembersOnly()) {
437-
$sharedBy = $this->userManager->get($share->getSharedBy());
438-
$sharedWith = $this->userManager->get($share->getSharedWith());
439-
// Verify we can share with this user
440-
$groups = array_intersect(
441-
$this->groupManager->getUserGroupIds($sharedBy),
442-
$this->groupManager->getUserGroupIds($sharedWith)
443-
);
444-
445-
// optional excluded groups
446-
$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
447-
$groups = array_diff($groups, $excludedGroups);
448-
449-
if (empty($groups)) {
450-
throw new \Exception($this->l->t('Sharing is only allowed with group members'));
451-
}
452-
}
453-
454418
/*
455419
* TODO: Could be costly, fix
456420
*
@@ -493,23 +457,6 @@ protected function userCreateChecks(IShare $share): void {
493457
* @throws \Exception
494458
*/
495459
protected function groupCreateChecks(IShare $share): void {
496-
// Verify group shares are allowed
497-
if (!$this->allowGroupSharing()) {
498-
throw new \Exception($this->l->t('Group sharing is now allowed'));
499-
}
500-
501-
// Verify if the user can share with this group
502-
if ($this->shareWithGroupMembersOnly()) {
503-
$sharedBy = $this->userManager->get($share->getSharedBy());
504-
$sharedWith = $this->groupManager->get($share->getSharedWith());
505-
506-
// optional excluded groups
507-
$excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList();
508-
if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) {
509-
throw new \Exception($this->l->t('Sharing is only allowed within your own groups'));
510-
}
511-
}
512-
513460
/*
514461
* TODO: Could be costly, fix
515462
*
@@ -532,24 +479,6 @@ protected function groupCreateChecks(IShare $share): void {
532479
}
533480
}
534481

535-
/**
536-
* Check for pre share requirements for link shares
537-
*
538-
* @throws \Exception
539-
*/
540-
protected function linkCreateChecks(IShare $share): void {
541-
// Are link shares allowed?
542-
if (!$this->shareApiAllowLinks()) {
543-
throw new \Exception($this->l->t('Link sharing is not allowed'));
544-
}
545-
546-
// Check if public upload is allowed
547-
if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
548-
&& ($share->getPermissions() & (Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE))) {
549-
throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
550-
}
551-
}
552-
553482
/**
554483
* To make sure we don't get invisible link shares we set the parent
555484
* of a link if it is a reshare. This is a quick word around
@@ -600,10 +529,7 @@ protected function canShare(IShare $share): void {
600529

601530
#[Override]
602531
public function createShare(IShare $share): IShare {
603-
// TODO: handle link share permissions or check them
604-
$this->canShare($share);
605-
606-
$this->generalCreateChecks($share);
532+
$this->generalChecks($share);
607533

608534
// Verify if there are any issues with the path
609535
$this->pathCreateChecks($share->getNode());
@@ -644,7 +570,6 @@ public function createShare(IShare $share): IShare {
644570
$share = $this->validateExpirationDateInternal($share);
645571
} elseif ($share->getShareType() === IShare::TYPE_LINK
646572
|| $share->getShareType() === IShare::TYPE_EMAIL) {
647-
$this->linkCreateChecks($share);
648573
$this->setLinkParent($share);
649574

650575
$token = $this->generateToken();
@@ -737,8 +662,6 @@ public function createShare(IShare $share): IShare {
737662
public function updateShare(IShare $share, bool $onlyValid = true): IShare {
738663
$expirationDateUpdated = false;
739664

740-
$this->canShare($share);
741-
742665
try {
743666
$originalShare = $this->getShareById($share->getFullId(), onlyValid: $onlyValid);
744667
} catch (\UnexpectedValueException $e) {
@@ -762,7 +685,7 @@ public function updateShare(IShare $share, bool $onlyValid = true): IShare {
762685
throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
763686
}
764687

765-
$this->generalCreateChecks($share, true);
688+
$this->generalChecks($share);
766689

767690
if ($share->getShareType() === IShare::TYPE_USER) {
768691
$this->userCreateChecks($share);
@@ -782,7 +705,6 @@ public function updateShare(IShare $share, bool $onlyValid = true): IShare {
782705
}
783706
} elseif ($share->getShareType() === IShare::TYPE_LINK
784707
|| $share->getShareType() === IShare::TYPE_EMAIL) {
785-
$this->linkCreateChecks($share);
786708

787709
// The new password is not set again if it is the same as the old
788710
// one, unless when switching from sending by Talk to sending by
@@ -1096,7 +1018,7 @@ protected function promoteReshares(IShare $share): void {
10961018
foreach ($reshareRecords as $child) {
10971019
try {
10981020
/* Check if the share is still valid (means the resharer still has access to the file through another mean) */
1099-
$this->generalCreateChecks($child);
1021+
$this->generalChecks($child);
11001022
} catch (GenericShareException $e) {
11011023
/* The check is invalid, promote it to a direct share from the sharer of parent share */
11021024
$this->logger->debug('Promote reshare because of exception ' . $e->getMessage(), ['exception' => $e, 'fullId' => $child->getFullId()]);
@@ -1486,7 +1408,7 @@ private function checkShare(IShare $share, int &$added = 1): void {
14861408
throw new ShareNotFound($this->l->t('The requested share does not exist anymore'));
14871409
}
14881410
if ($this->config->getAppValue('files_sharing', 'hide_disabled_user_shares', 'no') === 'yes') {
1489-
$uids = array_unique([$share->getShareOwner(),$share->getSharedBy()]);
1411+
$uids = array_unique([$share->getShareOwner(), $share->getSharedBy()]);
14901412
foreach ($uids as $uid) {
14911413
$user = $this->userManager->get($uid);
14921414
if ($user?->isEnabled() === false) {

tests/lib/Share20/ManagerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2625,7 +2625,7 @@ public function testCreateShareUser(): void {
26252625
->method('canShare')
26262626
->with($share);
26272627
$manager->expects($this->once())
2628-
->method('generalCreateChecks')
2628+
->method('generalChecks')
26292629
->with($share);
26302630
;
26312631
$manager->expects($this->once())

0 commit comments

Comments
 (0)