Better check reshare permissions part2tags/v17.0.0beta1
} | } | ||||
$share->setExpirationDate($expireDate); | $share->setExpirationDate($expireDate); | ||||
} | } | ||||
} | } | ||||
if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) { | |||||
// Get the root mount point for the user and check the share permissions there | |||||
$userFolder = $this->rootFolder->getUserFolder($this->currentUser); | |||||
$userNodes = $userFolder->getById($share->getNodeId()); | |||||
$userNode = array_shift($userNodes); | |||||
$userMountPointId = $userNode->getMountPoint()->getStorageRootId(); | |||||
$userMountPoints = $userFolder->getById($userMountPointId); | |||||
$userMountPoint = array_shift($userMountPoints); | |||||
/* Check if this is an incoming share */ | |||||
$incomingShares = $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_USER, $userMountPoint, -1, 0); | |||||
$incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_GROUP, $userMountPoint, -1, 0)); | |||||
$incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, Share::SHARE_TYPE_ROOM, $userMountPoint, -1, 0)); | |||||
/** @var \OCP\Share\IShare[] $incomingShares */ | |||||
if (!empty($incomingShares)) { | |||||
$maxPermissions = 0; | |||||
foreach ($incomingShares as $incomingShare) { | |||||
$maxPermissions |= $incomingShare->getPermissions(); | |||||
} | |||||
if ($share->getPermissions() & ~$maxPermissions) { | |||||
throw new OCSNotFoundException($this->l->t('Cannot increase permissions')); | |||||
} | |||||
} | |||||
} | |||||
try { | try { | ||||
$share = $this->shareManager->updateShare($share); | $share = $this->shareManager->updateShare($share); | ||||
} catch (GenericShareException $e) { | |||||
$code = $e->getCode() === 0 ? 403 : $e->getCode(); | |||||
throw new OCSException($e->getHint(), $code); | |||||
} catch (\Exception $e) { | } catch (\Exception $e) { | ||||
throw new OCSBadRequestException($e->getMessage(), $e); | throw new OCSBadRequestException($e->getMessage(), $e); | ||||
} | } |
use OCP\App\IAppManager; | use OCP\App\IAppManager; | ||||
use OCP\AppFramework\Http\DataResponse; | use OCP\AppFramework\Http\DataResponse; | ||||
use OCP\AppFramework\OCS\OCSException; | |||||
use OCP\AppFramework\OCS\OCSNotFoundException; | use OCP\AppFramework\OCS\OCSNotFoundException; | ||||
use OCP\Files\File; | use OCP\Files\File; | ||||
use OCP\Files\Folder; | use OCP\Files\Folder; | ||||
use OCP\IUser; | use OCP\IUser; | ||||
use OCP\Files\IRootFolder; | use OCP\Files\IRootFolder; | ||||
use OCP\Lock\LockedException; | use OCP\Lock\LockedException; | ||||
use OCP\Share\Exceptions\GenericShareException; | |||||
use OCP\Share\IManager; | use OCP\Share\IManager; | ||||
use OCP\Share; | use OCP\Share; | ||||
use Test\TestCase; | use Test\TestCase; | ||||
$mountPoint->method('getStorageRootId') | $mountPoint->method('getStorageRootId') | ||||
->willReturn(42); | ->willReturn(42); | ||||
$this->shareManager->expects($this->never())->method('updateShare'); | |||||
try { | |||||
$ocs->updateShare(42, 31); | |||||
$this->fail(); | |||||
} catch (OCSNotFoundException $e) { | |||||
$this->assertEquals('Cannot increase permissions', $e->getMessage()); | |||||
} | |||||
} | |||||
public function testUpdateShareCannotIncreasePermissionsLinkShare() { | |||||
$ocs = $this->mockFormatShare(); | |||||
$folder = $this->createMock(Folder::class); | |||||
$folder->method('getId') | |||||
->willReturn(42); | |||||
$share = \OC::$server->getShareManager()->newShare(); | |||||
$share | |||||
->setId(42) | |||||
->setSharedBy($this->currentUser) | |||||
->setShareOwner('anotheruser') | |||||
->setShareType(\OCP\Share::SHARE_TYPE_LINK) | |||||
->setPermissions(\OCP\Constants::PERMISSION_READ) | |||||
->setNode($folder); | |||||
// note: updateShare will modify the received instance but getSharedWith will reread from the database, | |||||
// so their values will be different | |||||
$incomingShare = \OC::$server->getShareManager()->newShare(); | |||||
$incomingShare | |||||
->setId(42) | |||||
->setSharedBy($this->currentUser) | |||||
->setShareOwner('anotheruser') | |||||
->setShareType(\OCP\Share::SHARE_TYPE_USER) | |||||
->setSharedWith('currentUser') | |||||
->setPermissions(\OCP\Constants::PERMISSION_READ) | |||||
->setNode($folder); | |||||
$this->shareManager->method('getShareById')->with('ocinternal:42')->willReturn($share); | |||||
$this->shareManager->expects($this->any()) | |||||
->method('getSharedWith') | |||||
->will($this->returnValueMap([ | |||||
['currentUser', \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0, [$incomingShare]], | |||||
['currentUser', \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0, []], | |||||
['currentUser', \OCP\Share::SHARE_TYPE_ROOM, $share->getNode(), -1, 0, []] | |||||
])); | |||||
$userFolder = $this->createMock(Folder::class); | |||||
$this->rootFolder->method('getUserFolder') | |||||
->with($this->currentUser) | |||||
->willReturn($userFolder); | |||||
$userFolder->method('getById') | |||||
->with(42) | |||||
->willReturn([$folder]); | |||||
$mountPoint = $this->createMock(IMountPoint::class); | |||||
$folder->method('getMountPoint') | |||||
->willReturn($mountPoint); | |||||
$mountPoint->method('getStorageRootId') | |||||
->willReturn(42); | |||||
$this->shareManager->expects($this->never())->method('updateShare'); | |||||
$this->shareManager->method('shareApiLinkAllowPublicUpload')->willReturn(true); | |||||
try { | |||||
$ocs->updateShare(42, null, null, null, 'true'); | |||||
$this->fail(); | |||||
} catch (OCSNotFoundException $e) { | |||||
$this->assertEquals('Cannot increase permissions', $e->getMessage()); | |||||
} | |||||
} | |||||
public function testUpdateShareCannotIncreasePermissionsRoomShare() { | |||||
$ocs = $this->mockFormatShare(); | |||||
$folder = $this->createMock(Folder::class); | |||||
$folder->method('getId') | |||||
->willReturn(42); | |||||
$share = \OC::$server->getShareManager()->newShare(); | |||||
$share | |||||
->setId(42) | |||||
->setSharedBy($this->currentUser) | |||||
->setShareOwner('anotheruser') | |||||
->setShareType(\OCP\Share::SHARE_TYPE_ROOM) | |||||
->setSharedWith('group1') | |||||
->setPermissions(\OCP\Constants::PERMISSION_READ) | |||||
->setNode($folder); | |||||
// note: updateShare will modify the received instance but getSharedWith will reread from the database, | |||||
// so their values will be different | |||||
$incomingShare = \OC::$server->getShareManager()->newShare(); | |||||
$incomingShare | |||||
->setId(42) | |||||
->setSharedBy($this->currentUser) | |||||
->setShareOwner('anotheruser') | |||||
->setShareType(\OCP\Share::SHARE_TYPE_ROOM) | |||||
->setSharedWith('group1') | |||||
->setPermissions(\OCP\Constants::PERMISSION_READ) | |||||
->setNode($folder); | |||||
$this->request | |||||
->method('getParam') | |||||
->will($this->returnValueMap([ | |||||
['permissions', null, '31'], | |||||
])); | |||||
$this->shareManager | |||||
->method('getShareById') | |||||
->will($this->returnCallback( | |||||
function ($id) use ($share) { | |||||
if ($id !== 'ocRoomShare:42') { | |||||
throw new \OCP\Share\Exceptions\ShareNotFound(); | |||||
} | |||||
return $share; | |||||
} | |||||
)); | |||||
$userFolder = $this->createMock(Folder::class); | |||||
$this->rootFolder->method('getUserFolder') | |||||
->with($this->currentUser) | |||||
->willReturn($userFolder); | |||||
$userFolder->method('getById') | |||||
->with(42) | |||||
->willReturn([$folder]); | |||||
$mountPoint = $this->createMock(IMountPoint::class); | |||||
$folder->method('getMountPoint') | |||||
->willReturn($mountPoint); | |||||
$mountPoint->method('getStorageRootId') | |||||
->willReturn(42); | |||||
$this->shareManager->expects($this->any()) | |||||
->method('getSharedWith') | |||||
->will($this->returnValueMap([ | |||||
['currentUser', \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0, []], | |||||
['currentUser', \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0, []], | |||||
['currentUser', \OCP\Share::SHARE_TYPE_ROOM, $share->getNode(), -1, 0, [$incomingShare]] | |||||
])); | |||||
$this->shareManager->expects($this->never())->method('updateShare'); | |||||
$this->shareManager->expects($this->once()) | |||||
->method('updateShare') | |||||
->with($share) | |||||
->willThrowException(new GenericShareException('Can’t increase permissions of path/file', 'Can’t increase permissions of path/file', 404)); | |||||
try { | try { | ||||
$ocs->updateShare(42, 31); | $ocs->updateShare(42, 31); | ||||
$this->fail(); | $this->fail(); | ||||
} catch (OCSNotFoundException $e) { | |||||
$this->assertEquals('Cannot increase permissions', $e->getMessage()); | |||||
} catch (OCSException $e) { | |||||
$this->assertEquals('Can’t increase permissions of path/file', $e->getMessage()); | |||||
} | } | ||||
} | } | ||||
Then the OCS status code should be "404" | Then the OCS status code should be "404" | ||||
And the HTTP status code should be "200" | And the HTTP status code should be "200" | ||||
Scenario: User is not allowed to reshare file with additional delete permissions | |||||
As an "admin" | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And As an "user0" | |||||
And creating a share with | |||||
| path | /PARENT | | |||||
| shareType | 0 | | |||||
| shareWith | user1 | | |||||
| permissions | 16 | | |||||
And As an "user1" | |||||
When creating a share with | |||||
| path | /PARENT (2) | | |||||
| shareType | 0 | | |||||
| shareWith | user2 | | |||||
| permissions | 25 | | |||||
Then the OCS status code should be "404" | |||||
And the HTTP status code should be "200" | |||||
Scenario: User is not allowed to reshare file with additional delete permissions for files | |||||
As an "admin" | |||||
Given user "user0" exists | |||||
And user "user1" exists | |||||
And user "user2" exists | |||||
And As an "user0" | |||||
And creating a share with | |||||
| path | /textfile0.txt | | |||||
| shareType | 0 | | |||||
| shareWith | user1 | | |||||
| permissions | 16 | | |||||
And As an "user1" | |||||
When creating a share with | |||||
| path | /textfile0 (2).txt | | |||||
| shareType | 0 | | |||||
| shareWith | user2 | | |||||
| permissions | 25 | | |||||
Then the OCS status code should be "100" | |||||
And the HTTP status code should be "200" | |||||
When Getting info of last share | |||||
Then Share fields of last share match with | |||||
| id | A_NUMBER | | |||||
| item_type | file | | |||||
| item_source | A_NUMBER | | |||||
| share_type | 0 | | |||||
| share_with | user2 | | |||||
| file_source | A_NUMBER | | |||||
| file_target | /textfile0 (2).txt | | |||||
| path | /textfile0 (2).txt | | |||||
| permissions | 17 | | |||||
| stime | A_NUMBER | | |||||
| storage | A_NUMBER | | |||||
| mail_send | 0 | | |||||
| uid_owner | user1 | | |||||
| storage_id | shared::/textfile0 (2).txt | | |||||
| file_parent | A_NUMBER | | |||||
| share_with_displayname | user2 | | |||||
| displayname_owner | user1 | | |||||
| mimetype | text/plain | | |||||
Scenario: Get a share with a user which didn't received the share | Scenario: Get a share with a user which didn't received the share | ||||
Given user "user0" exists | Given user "user0" exists | ||||
And user "user1" exists | And user "user1" exists |
// And you can't share your rootfolder | // And you can't share your rootfolder | ||||
if ($this->userManager->userExists($share->getSharedBy())) { | if ($this->userManager->userExists($share->getSharedBy())) { | ||||
$sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath(); | |||||
$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); | |||||
$userFolderPath = $userFolder->getPath(); | |||||
} else { | } else { | ||||
$sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath(); | |||||
$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); | |||||
$userFolderPath = $userFolder->getPath(); | |||||
} | } | ||||
if ($sharedPath === $share->getNode()->getPath()) { | |||||
if ($userFolderPath === $share->getNode()->getPath()) { | |||||
throw new \InvalidArgumentException('You can’t share your root folder'); | throw new \InvalidArgumentException('You can’t share your root folder'); | ||||
} | } | ||||
throw new \InvalidArgumentException('A share requires permissions'); | throw new \InvalidArgumentException('A share requires permissions'); | ||||
} | } | ||||
/* | |||||
* Quick fix for #23536 | |||||
* Non moveable mount points do not have update and delete permissions | |||||
* while we 'most likely' do have that on the storage. | |||||
*/ | |||||
$permissions = $share->getNode()->getPermissions(); | |||||
$mount = $share->getNode()->getMountPoint(); | $mount = $share->getNode()->getMountPoint(); | ||||
if (!($mount instanceof MoveableMount)) { | |||||
$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; | |||||
if ($share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) { | |||||
// When it's a reshare use the parent share permissions as maximum | |||||
$userMountPointId = $mount->getStorageRootId(); | |||||
$userMountPoints = $userFolder->getById($userMountPointId); | |||||
$userMountPoint = array_shift($userMountPoints); | |||||
/* Check if this is an incoming share */ | |||||
$incomingShares = $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_USER, $userMountPoint, -1, 0); | |||||
$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_GROUP, $userMountPoint, -1, 0)); | |||||
$incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_ROOM, $userMountPoint, -1, 0)); | |||||
/** @var \OCP\Share\IShare[] $incomingShares */ | |||||
if (!empty($incomingShares)) { | |||||
$permissions = 0; | |||||
foreach ($incomingShares as $incomingShare) { | |||||
$permissions |= $incomingShare->getPermissions(); | |||||
} | |||||
} | |||||
} else { | |||||
/* | |||||
* Quick fix for #23536 | |||||
* Non moveable mount points do not have update and delete permissions | |||||
* while we 'most likely' do have that on the storage. | |||||
*/ | |||||
$permissions = $share->getNode()->getPermissions(); | |||||
if (!($mount instanceof MoveableMount)) { | |||||
$permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; | |||||
} | |||||
} | } | ||||
// Check that we do not share with more permissions than we have | // Check that we do not share with more permissions than we have |
$user0 = 'user0'; | $user0 = 'user0'; | ||||
$user2 = 'user1'; | $user2 = 'user1'; | ||||
$group0 = 'group0'; | $group0 = 'group0'; | ||||
$owner = $this->createMock(IUser::class); | |||||
$owner->method('getUID') | |||||
->willReturn($user0); | |||||
$file = $this->createMock(File::class); | $file = $this->createMock(File::class); | ||||
$node = $this->createMock(Node::class); | $node = $this->createMock(Node::class); | ||||
$nonShareAble = $this->createMock(Folder::class); | $nonShareAble = $this->createMock(Folder::class); | ||||
$nonShareAble->method('isShareable')->willReturn(false); | $nonShareAble->method('isShareable')->willReturn(false); | ||||
$nonShareAble->method('getPath')->willReturn('path'); | $nonShareAble->method('getPath')->willReturn('path'); | ||||
$nonShareAble->method('getOwner') | |||||
->willReturn($owner); | |||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $nonShareAble, $user2, $user0, $user0, 31, null, null), 'You are not allowed to share path', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $nonShareAble, $user2, $user0, $user0, 31, null, null), 'You are not allowed to share path', true]; | ||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $nonShareAble, $group0, $user0, $user0, 31, null, null), 'You are not allowed to share path', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $nonShareAble, $group0, $user0, $user0, 31, null, null), 'You are not allowed to share path', true]; | ||||
$limitedPermssions->method('isShareable')->willReturn(true); | $limitedPermssions->method('isShareable')->willReturn(true); | ||||
$limitedPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_READ); | $limitedPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_READ); | ||||
$limitedPermssions->method('getPath')->willReturn('path'); | $limitedPermssions->method('getPath')->willReturn('path'); | ||||
$limitedPermssions->method('getOwner') | |||||
->willReturn($owner); | |||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $limitedPermssions, $user2, $user0, $user0, null, null, null), 'A share requires permissions', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $limitedPermssions, $user2, $user0, $user0, null, null, null), 'A share requires permissions', true]; | ||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, null, null, null), 'A share requires permissions', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $limitedPermssions, $group0, $user0, $user0, null, null, null), 'A share requires permissions', true]; | ||||
$nonMoveableMountPermssions->method('isShareable')->willReturn(true); | $nonMoveableMountPermssions->method('isShareable')->willReturn(true); | ||||
$nonMoveableMountPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_READ); | $nonMoveableMountPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_READ); | ||||
$nonMoveableMountPermssions->method('getPath')->willReturn('path'); | $nonMoveableMountPermssions->method('getPath')->willReturn('path'); | ||||
$nonMoveableMountPermssions->method('getOwner') | |||||
->willReturn($owner); | |||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $nonMoveableMountPermssions, $user2, $user0, $user0, 11, null, null), 'Can’t increase permissions of path', false]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $nonMoveableMountPermssions, $user2, $user0, $user0, 11, null, null), 'Can’t increase permissions of path', false]; | ||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $nonMoveableMountPermssions, $group0, $user0, $user0, 11, null, null), 'Can’t increase permissions of path', false]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $nonMoveableMountPermssions, $group0, $user0, $user0, 11, null, null), 'Can’t increase permissions of path', false]; | ||||
$allPermssions = $this->createMock(Folder::class); | $allPermssions = $this->createMock(Folder::class); | ||||
$allPermssions->method('isShareable')->willReturn(true); | $allPermssions->method('isShareable')->willReturn(true); | ||||
$allPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_ALL); | $allPermssions->method('getPermissions')->willReturn(\OCP\Constants::PERMISSION_ALL); | ||||
$allPermssions->method('getOwner') | |||||
->willReturn($owner); | |||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null), 'Shares need at least read permissions', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_USER, $allPermssions, $user2, $user0, $user0, 30, null, null), 'Shares need at least read permissions', true]; | ||||
$data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null), 'Shares need at least read permissions', true]; | $data[] = [$this->createShare(null, \OCP\Share::SHARE_TYPE_GROUP, $allPermssions, $group0, $user0, $user0, 2, null, null), 'Shares need at least read permissions', true]; |