diff options
-rw-r--r-- | apps/files_sharing/api/ocssharewrapper.php | 3 | ||||
-rw-r--r-- | apps/files_sharing/api/share20ocs.php | 66 | ||||
-rw-r--r-- | lib/private/share20/defaultshareprovider.php | 63 | ||||
-rw-r--r-- | lib/private/share20/manager.php | 112 | ||||
-rw-r--r-- | tests/lib/share20/defaultshareprovidertest.php | 354 | ||||
-rw-r--r-- | tests/lib/share20/managertest.php | 376 |
6 files changed, 888 insertions, 86 deletions
diff --git a/apps/files_sharing/api/ocssharewrapper.php b/apps/files_sharing/api/ocssharewrapper.php index cc52d478615..9f63e864cec 100644 --- a/apps/files_sharing/api/ocssharewrapper.php +++ b/apps/files_sharing/api/ocssharewrapper.php @@ -50,7 +50,8 @@ class OCSShareWrapper { } public function updateShare($params) { - return \OCA\Files_Sharing\API\Local::updateShare($params); + $id = $params['id']; + return $this->getShare20OCS()->updateShare($id); } public function deleteShare($params) { diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php index c2ff94db790..c5e7dcff824 100644 --- a/apps/files_sharing/api/share20ocs.php +++ b/apps/files_sharing/api/share20ocs.php @@ -426,6 +426,72 @@ class Share20OCS { } /** + * @param int $id + * @return \OC_OCS_Result + */ + public function updateShare($id) { + // Try both our default and our federated provider + $share = null; + + try { + $share = $this->shareManager->getShareById('ocinternal:' . $id); + } catch (\OC\Share20\Exception\ShareNotFound $e) { + //Ignore for now + //return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + } + + // Could not find the share as internal share... maybe it is a federated share + if ($share === null) { + return \OCA\Files_Sharing\API\Local::updateShare(['id' => $id]); + } + + if (!$this->canAccessShare($share)) { + return new \OC_OCS_Result(null, 404, "wrong share Id, share doesn't exist."); + } + + $permissions = $this->request->getParam('permissions', null); + $password = $this->request->getParam('password', null); + $publicUpload = $this->request->getParam('publicUpload', null); + $expireDate = $this->request->getParam('expireDate', null); + + if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) { + return new \OC_OCS_Result(null, 400, 'Wrong or no update parameter given'); + } + + if ($expireDate !== null) { + try { + $expireDate = $this->parseDate($expireDate); + } catch (\Exception $e) { + return new \OC_OCS_Result(null, 400, $e->getMessage()); + } + $share->setExpirationDate($expireDate); + } + + if ($permissions !== null) { + $permissions = (int)$permissions; + $share->setPermissions($permissions); + } + + if ($password !== null) { + $share->setPassword($password); + } + + if ($publicUpload === 'true') { + $share->setPermissions(\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE); + } else if ($publicUpload === 'false') { + $share->setPermissions(\OCP\Constants::PERMISSION_READ); + } + + try { + $share = $this->shareManager->updateShare($share); + } catch (\Exception $e) { + return new \OC_OCS_Result(null, 400, $e->getMessage()); + } + + return new \OC_OCS_Result($this->formatShare($share)); + } + + /** * @param IShare $share * @return bool */ diff --git a/lib/private/share20/defaultshareprovider.php b/lib/private/share20/defaultshareprovider.php index f2072fe21cd..fb2acb56a73 100644 --- a/lib/private/share20/defaultshareprovider.php +++ b/lib/private/share20/defaultshareprovider.php @@ -183,6 +183,69 @@ class DefaultShareProvider implements IShareProvider { * @return IShare The share object */ public function update(IShare $share) { + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + /* + * We allow updating the recipient on user shares. + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('share_with', $qb->createNamedParameter($share->getSharedWith()->getUID())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()->getUID())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()->getUID())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getPath()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getPath()->getId())) + ->execute(); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()->getUID())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()->getUID())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getPath()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getPath()->getId())) + ->execute(); + + /* + * Update all user defined group shares + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()->getUID())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()->getUID())) + ->set('item_source', $qb->createNamedParameter($share->getPath()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getPath()->getId())) + ->execute(); + + /* + * Now update the permissions for all children that have not set it to 0 + */ + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0))) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->execute(); + + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('share_with', $qb->createNamedParameter($share->getPassword())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()->getUID())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()->getUID())) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('item_source', $qb->createNamedParameter($share->getPath()->getId())) + ->set('file_source', $qb->createNamedParameter($share->getPath()->getId())) + ->set('token', $qb->createNamedParameter($share->getToken())) + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) + ->execute(); + } + + return $share; } /** diff --git a/lib/private/share20/manager.php b/lib/private/share20/manager.php index ea6463c745c..673cea9b946 100644 --- a/lib/private/share20/manager.php +++ b/lib/private/share20/manager.php @@ -170,7 +170,7 @@ class Manager { throw new \InvalidArgumentException('unkown share type'); } - // Verify the initiator of the share is et + // Verify the initiator of the share is set if ($share->getSharedBy() === null) { throw new \InvalidArgumentException('SharedBy should be set'); } @@ -184,6 +184,7 @@ class Manager { if ($share->getPath() === null) { throw new \InvalidArgumentException('Path should be set'); } + // And it should be a file or a folder if (!($share->getPath() instanceof \OCP\Files\File) && !($share->getPath() instanceof \OCP\Files\Folder)) { @@ -216,19 +217,19 @@ class Manager { /** * Validate if the expiration date fits the system settings * - * @param \DateTime $expireDate The current expiration date (can be null) + * @param \DateTime $expirationDate The current expiration date (can be null) * @return \DateTime|null The expiration date or null if $expireDate was null and it is not required * @throws \OC\HintException */ - protected function validateExpiredate($expireDate) { + protected function validateExpirationDate($expirationDate) { - if ($expireDate !== null) { + if ($expirationDate !== null) { //Make sure the expiration date is a date - $expireDate->setTime(0, 0, 0); + $expirationDate->setTime(0, 0, 0); $date = new \DateTime(); $date->setTime(0, 0, 0); - if ($date >= $expireDate) { + if ($date >= $expirationDate) { $message = $this->l->t('Expiration date is in the past'); throw new \OC\HintException($message, $message, 404); } @@ -236,30 +237,30 @@ class Manager { // If we enforce the expiration date check that is does not exceed if ($this->shareApiLinkDefaultExpireDateEnforced()) { - if ($expireDate === null) { + if ($expirationDate === null) { throw new \InvalidArgumentException('Expiration date is enforced'); } $date = new \DateTime(); $date->setTime(0, 0, 0); $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); - if ($date < $expireDate) { + if ($date < $expirationDate) { $message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); throw new \OC\HintException($message, $message, 404); } - return $expireDate; + return $expirationDate; } // If expiredate is empty set a default one if there is a default - if ($expireDate === null && $this->shareApiLinkDefaultExpireDate()) { + if ($expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { $date = new \DateTime(); $date->setTime(0,0,0); $date->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); return $date; } - return $expireDate; + return $expirationDate; } /** @@ -289,6 +290,11 @@ class Manager { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER); $existingShares = $provider->getSharesByPath($share->getPath()); foreach($existingShares as $existingShare) { + // Ignore if it is the same share + if ($existingShare->getFullId() === $share->getFullId()) { + continue; + } + // Identical share already existst if ($existingShare->getSharedWith() === $share->getSharedWith()) { throw new \Exception('Path already shared with this user'); @@ -325,6 +331,10 @@ class Manager { $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); $existingShares = $provider->getSharesByPath($share->getPath()); foreach($existingShares as $existingShare) { + if ($existingShare->getFullId() === $share->getFullId()) { + continue; + } + if ($existingShare->getSharedWith() === $share->getSharedWith()) { throw new \Exception('Path already shared with this group'); } @@ -430,7 +440,7 @@ class Manager { ); //Verify the expiration date - $share->setExpirationDate($this->validateExpiredate($share->getExpirationDate())); + $share->setExpirationDate($this->validateExpirationDate($share->getExpirationDate())); //Verify the password $this->verifyPassword($share->getPassword()); @@ -447,6 +457,11 @@ class Manager { // On creation of a share the owner is always the owner of the path $share->setShareOwner($share->getPath()->getOwner()); + // Cannot share with the owner + if ($share->getSharedWith() === $share->getShareOwner()) { + throw new \InvalidArgumentException('Can\'t share with the share owner'); + } + // Generate the target $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getPath()->getName(); $target = \OC\Files\Filesystem::normalizePath($target); @@ -513,10 +528,77 @@ class Manager { /** * Update a share * - * @param Share $share - * @return Share The share object + * @param IShare $share + * @return IShare The share object */ - public function updateShare(Share $share) { + public function updateShare(IShare $share) { + $expirationDateUpdated = false; + + if (!$this->canShare($share)) { + throw new \Exception('The Share API is disabled'); + } + + $originalShare = $this->getShareById($share->getFullId()); + + // We can't change the share type! + if ($share->getShareType() !== $originalShare->getShareType()) { + throw new \InvalidArgumentException('Can\'t change share type'); + } + + // We can only change the recipient on user shares + if ($share->getSharedWith() !== $originalShare->getSharedWith() && + $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) { + throw new \InvalidArgumentException('Can only update recipient on user shares'); + } + + // Cannot share with the owner + if ($share->getSharedWith() === $share->getShareOwner()) { + throw new \InvalidArgumentException('Can\'t share with the share owner'); + } + + $this->generalCreateChecks($share); + + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + $this->userCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + $this->groupCreateChecks($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { + $this->linkCreateChecks($share); + + // Password updated. + if ($share->getPassword() !== $originalShare->getPassword()) { + //Verify the password + $this->verifyPassword($share->getPassword()); + + // If a password is set. Hash it! + if ($share->getPassword() !== null) { + $share->setPassword($this->hasher->hash($share->getPassword())); + } + } + + if ($share->getExpirationDate() !== $originalShare->getExpirationDate()) { + //Verify the expiration date + $share->setExpirationDate($this->validateExpirationDate($share->getExpirationDate())); + $expirationDateUpdated = true; + } + } + + $this->pathCreateChecks($share->getPath()); + + // Now update the share! + $provider = $this->factory->getProviderForType($share->getShareType()); + $share = $provider->update($share); + + if ($expirationDateUpdated === true) { + \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [ + 'itemType' => $share->getPath() instanceof \OCP\Files\File ? 'file' : 'folder', + 'itemSource' => $share->getPath()->getId(), + 'date' => $share->getExpirationDate(), + 'uidOwner' => $share->getSharedBy()->getUID(), + ]); + } + + return $share; } /** diff --git a/tests/lib/share20/defaultshareprovidertest.php b/tests/lib/share20/defaultshareprovidertest.php index 8a82be4161b..79cc92116be 100644 --- a/tests/lib/share20/defaultshareprovidertest.php +++ b/tests/lib/share20/defaultshareprovidertest.php @@ -21,6 +21,7 @@ namespace Test\Share20; use OC\Share20\Exception\ProviderException; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUserManager; use OCP\IGroupManager; @@ -72,6 +73,43 @@ class DefaultShareProviderTest extends \Test\TestCase { } /** + * @param int $shareType + * @param string $sharedWith + * @param string $sharedBy + * @param string $shareOwner + * @param string $itemType + * @param int $fileSource + * @param string $fileTarget + * @param int $permissions + * @param $token + * @param $expiration + * @return int + */ + private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner, + $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration, + $parent = null) { + $qb = $this->dbConn->getQueryBuilder(); + $qb->insert('share'); + + if ($shareType) $qb->setValue('share_type', $qb->expr()->literal($shareType)); + if ($sharedWith) $qb->setValue('share_with', $qb->expr()->literal($sharedWith)); + if ($sharedBy) $qb->setValue('uid_initiator', $qb->expr()->literal($sharedBy)); + if ($shareOwner) $qb->setValue('uid_owner', $qb->expr()->literal($shareOwner)); + if ($itemType) $qb->setValue('item_type', $qb->expr()->literal($itemType)); + if ($fileSource) $qb->setValue('file_source', $qb->expr()->literal($fileSource)); + if ($fileTarget) $qb->setValue('file_target', $qb->expr()->literal($fileTarget)); + if ($permissions) $qb->setValue('permissions', $qb->expr()->literal($permissions)); + if ($token) $qb->setValue('token', $qb->expr()->literal($token)); + if ($expiration) $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATE)); + if ($parent) $qb->setValue('parent', $qb->expr()->literal($parent)); + + $this->assertEquals(1, $qb->execute()); + return$qb->getLastInsertId(); + } + + + + /** * @expectedException \OC\Share20\Exception\ShareNotFound */ public function testGetShareByIdNotExist() { @@ -1414,14 +1452,14 @@ class DefaultShareProviderTest extends \Test\TestCase { $qb = $this->dbConn->getQueryBuilder(); $stmt = $qb->insert('share') ->values([ - 'share_type' => $qb->expr()->literal(\OCP\Share::SHARE_TYPE_LINK), - 'uid_owner' => $qb->expr()->literal('user1'), + 'share_type' => $qb->expr()->literal(\OCP\Share::SHARE_TYPE_LINK), + 'uid_owner' => $qb->expr()->literal('user1'), 'uid_initiator' => $qb->expr()->literal('user1'), - 'item_type' => $qb->expr()->literal('file'), - 'file_source' => $qb->expr()->literal(1), - 'file_target' => $qb->expr()->literal('myTarget1'), - 'permissions' => $qb->expr()->literal(2), - 'token' => $qb->expr()->literal('token'), + 'item_type' => $qb->expr()->literal('file'), + 'file_source' => $qb->expr()->literal(1), + 'file_target' => $qb->expr()->literal('myTarget1'), + 'permissions' => $qb->expr()->literal(2), + 'token' => $qb->expr()->literal('token'), ])->execute(); $this->assertEquals(1, $stmt); $id = $qb->getLastInsertId(); @@ -1442,4 +1480,306 @@ class DefaultShareProviderTest extends \Test\TestCase { $this->provider->deleteFromSelf($share, $user1); } + + public function testUpdateUser() { + $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_USER, 'user0', 'user1', 'user2', + 'file', 42, 'target', 31, null, null); + + $users = []; + for($i = 0; $i < 6; $i++) { + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('user'.$i); + $users['user'.$i] = $user; + } + + $this->userManager->method('get')->will( + $this->returnCallback(function($userId) use ($users) { + return $users[$userId]; + }) + ); + + $file1 = $this->getMock('\OCP\Files\File'); + $file1->method('getId')->willReturn(42); + $file2 = $this->getMock('\OCP\Files\File'); + $file2->method('getId')->willReturn(43); + + $folder1 = $this->getMock('\OCP\Files\Folder'); + $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder2 = $this->getMock('\OCP\Files\Folder'); + $folder2->method('getById')->with(43)->willReturn([$file2]); + + $this->rootFolder->method('getUserFolder')->will($this->returnValueMap([ + ['user2', $folder1], + ['user5', $folder2], + ])); + + $share = $this->provider->getShareById($id); + + $share->setSharedWith($users['user3']); + $share->setSharedBy($users['user4']); + $share->setShareOwner($users['user5']); + $share->setPath($file2); + $share->setPermissions(1); + + $share2 = $this->provider->update($share); + + $this->assertEquals($id, $share2->getId()); + $this->assertSame($users['user3'], $share2->getSharedWith()); + $this->assertSame($users['user4'], $share2->getSharedBy()); + $this->assertSame($users['user5'], $share2->getShareOwner()); + $this->assertSame(1, $share2->getPermissions()); + } + + public function testUpdateLink() { + $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_LINK, null, 'user1', 'user2', + 'file', 42, 'target', 31, null, null); + + $users = []; + for($i = 0; $i < 6; $i++) { + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('user'.$i); + $users['user'.$i] = $user; + } + + $this->userManager->method('get')->will( + $this->returnCallback(function($userId) use ($users) { + return $users[$userId]; + }) + ); + + $file1 = $this->getMock('\OCP\Files\File'); + $file1->method('getId')->willReturn(42); + $file2 = $this->getMock('\OCP\Files\File'); + $file2->method('getId')->willReturn(43); + + $folder1 = $this->getMock('\OCP\Files\Folder'); + $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder2 = $this->getMock('\OCP\Files\Folder'); + $folder2->method('getById')->with(43)->willReturn([$file2]); + + $this->rootFolder->method('getUserFolder')->will($this->returnValueMap([ + ['user2', $folder1], + ['user5', $folder2], + ])); + + $share = $this->provider->getShareById($id); + + $share->setPassword('password'); + $share->setSharedBy($users['user4']); + $share->setShareOwner($users['user5']); + $share->setPath($file2); + $share->setPermissions(1); + + $share2 = $this->provider->update($share); + + $this->assertEquals($id, $share2->getId()); + $this->assertEquals('password', $share->getPassword()); + $this->assertSame($users['user4'], $share2->getSharedBy()); + $this->assertSame($users['user5'], $share2->getShareOwner()); + $this->assertSame(1, $share2->getPermissions()); + } + + public function testUpdateLinkRemovePassword() { + $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_LINK, 'foo', 'user1', 'user2', + 'file', 42, 'target', 31, null, null); + + $users = []; + for($i = 0; $i < 6; $i++) { + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('user'.$i); + $users['user'.$i] = $user; + } + + $this->userManager->method('get')->will( + $this->returnCallback(function($userId) use ($users) { + return $users[$userId]; + }) + ); + + $file1 = $this->getMock('\OCP\Files\File'); + $file1->method('getId')->willReturn(42); + $file2 = $this->getMock('\OCP\Files\File'); + $file2->method('getId')->willReturn(43); + + $folder1 = $this->getMock('\OCP\Files\Folder'); + $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder2 = $this->getMock('\OCP\Files\Folder'); + $folder2->method('getById')->with(43)->willReturn([$file2]); + + $this->rootFolder->method('getUserFolder')->will($this->returnValueMap([ + ['user2', $folder1], + ['user5', $folder2], + ])); + + $share = $this->provider->getShareById($id); + + $share->setPassword(null); + $share->setSharedBy($users['user4']); + $share->setShareOwner($users['user5']); + $share->setPath($file2); + $share->setPermissions(1); + + $share2 = $this->provider->update($share); + + $this->assertEquals($id, $share2->getId()); + $this->assertEquals(null, $share->getPassword()); + $this->assertSame($users['user4'], $share2->getSharedBy()); + $this->assertSame($users['user5'], $share2->getShareOwner()); + $this->assertSame(1, $share2->getPermissions()); + } + + public function testUpdateGroupNoSub() { + $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user2', + 'file', 42, 'target', 31, null, null); + + $users = []; + for($i = 0; $i < 6; $i++) { + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('user'.$i); + $users['user'.$i] = $user; + } + + $this->userManager->method('get')->will( + $this->returnCallback(function($userId) use ($users) { + return $users[$userId]; + }) + ); + + $groups = []; + for($i = 0; $i < 2; $i++) { + $group = $this->getMock('\OCP\IGroup'); + $group->method('getGID')->willReturn('group'.$i); + $groups['group'.$i] = $group; + } + + $this->groupManager->method('get')->will( + $this->returnCallback(function($groupId) use ($groups) { + return $groups[$groupId]; + }) + ); + + $file1 = $this->getMock('\OCP\Files\File'); + $file1->method('getId')->willReturn(42); + $file2 = $this->getMock('\OCP\Files\File'); + $file2->method('getId')->willReturn(43); + + $folder1 = $this->getMock('\OCP\Files\Folder'); + $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder2 = $this->getMock('\OCP\Files\Folder'); + $folder2->method('getById')->with(43)->willReturn([$file2]); + + $this->rootFolder->method('getUserFolder')->will($this->returnValueMap([ + ['user2', $folder1], + ['user5', $folder2], + ])); + + $share = $this->provider->getShareById($id); + + $share->setSharedWith($groups['group0']); + $share->setSharedBy($users['user4']); + $share->setShareOwner($users['user5']); + $share->setPath($file2); + $share->setPermissions(1); + + $share2 = $this->provider->update($share); + + $this->assertEquals($id, $share2->getId()); + // Group shares do not allow updating the recipient + $this->assertSame($groups['group0'], $share2->getSharedWith()); + $this->assertSame($users['user4'], $share2->getSharedBy()); + $this->assertSame($users['user5'], $share2->getShareOwner()); + $this->assertSame(1, $share2->getPermissions()); + } + + public function testUpdateGroupSubShares() { + $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user2', + 'file', 42, 'target', 31, null, null); + + $id2 = $this->addShareToDB(2, 'user0', 'user1', 'user2', + 'file', 42, 'mytarget', 31, null, null, $id); + + $id3 = $this->addShareToDB(2, 'user3', 'user1', 'user2', + 'file', 42, 'mytarget2', 0, null, null, $id); + + $users = []; + for($i = 0; $i < 6; $i++) { + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('user'.$i); + $users['user'.$i] = $user; + } + + $this->userManager->method('get')->will( + $this->returnCallback(function($userId) use ($users) { + return $users[$userId]; + }) + ); + + $groups = []; + for($i = 0; $i < 2; $i++) { + $group = $this->getMock('\OCP\IGroup'); + $group->method('getGID')->willReturn('group'.$i); + $groups['group'.$i] = $group; + } + + $this->groupManager->method('get')->will( + $this->returnCallback(function($groupId) use ($groups) { + return $groups[$groupId]; + }) + ); + + $file1 = $this->getMock('\OCP\Files\File'); + $file1->method('getId')->willReturn(42); + $file2 = $this->getMock('\OCP\Files\File'); + $file2->method('getId')->willReturn(43); + + $folder1 = $this->getMock('\OCP\Files\Folder'); + $folder1->method('getById')->with(42)->willReturn([$file1]); + $folder2 = $this->getMock('\OCP\Files\Folder'); + $folder2->method('getById')->with(43)->willReturn([$file2]); + + $this->rootFolder->method('getUserFolder')->will($this->returnValueMap([ + ['user2', $folder1], + ['user5', $folder2], + ])); + + $share = $this->provider->getShareById($id); + + $share->setSharedWith($groups['group0']); + $share->setSharedBy($users['user4']); + $share->setShareOwner($users['user5']); + $share->setPath($file2); + $share->setPermissions(1); + + $share2 = $this->provider->update($share); + + $this->assertEquals($id, $share2->getId()); + // Group shares do not allow updating the recipient + $this->assertSame($groups['group0'], $share2->getSharedWith()); + $this->assertSame($users['user4'], $share2->getSharedBy()); + $this->assertSame($users['user5'], $share2->getShareOwner()); + $this->assertSame(1, $share2->getPermissions()); + + $qb = $this->dbConn->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($id))) + ->orderBy('id') + ->execute(); + + $shares = $stmt->fetchAll(); + + $this->assertSame('user0', $shares[0]['share_with']); + $this->assertSame('user4', $shares[0]['uid_initiator']); + $this->assertSame('user5', $shares[0]['uid_owner']); + $this->assertSame(1, (int)$shares[0]['permissions']); + + $this->assertSame('user3', $shares[1]['share_with']); + $this->assertSame('user4', $shares[1]['uid_initiator']); + $this->assertSame('user5', $shares[1]['uid_owner']); + $this->assertSame(0, (int)$shares[1]['permissions']); + + + $stmt->closeCursor(); + + } } diff --git a/tests/lib/share20/managertest.php b/tests/lib/share20/managertest.php index 28303d3152f..ef602fc8e46 100644 --- a/tests/lib/share20/managertest.php +++ b/tests/lib/share20/managertest.php @@ -21,9 +21,11 @@ namespace Test\Share20; use OC\Share20\IProviderFactory; +use OC\Share20\IShare; use OC\Share20\Manager; use OC\Share20\Exception; +use OC\Share20\Share; use OCP\IL10N; use OCP\ILogger; use OCP\IConfig; @@ -32,6 +34,7 @@ use OCP\Security\ISecureRandom; use OCP\Security\IHasher; use OCP\Files\Mount\IMountManager; use OCP\IGroupManager; +use Sabre\VObject\Property\VCard\DateTime; /** * Class ManagerTest @@ -56,7 +59,7 @@ class ManagerTest extends \Test\TestCase { /** @var IHasher */ protected $hasher; - /** @var IShareProvider */ + /** @var IShareProvider | \PHPUnit_Framework_MockObject_MockObject */ protected $defaultProvider; /** @var IMountManager */ @@ -596,29 +599,29 @@ class ManagerTest extends \Test\TestCase { * @expectedException \OC\HintException * @expectedExceptionMessage Expiration date is in the past */ - public function testValidateExpiredateInPast() { + public function testvalidateExpirationDateInPast() { // Expire date in the past $past = new \DateTime(); $past->sub(new \DateInterval('P1D')); - $this->invokePrivate($this->manager, 'validateExpiredate', [$past]); + $this->invokePrivate($this->manager, 'validateExpirationDate', [$past]); } /** * @expectedException InvalidArgumentException * @expectedExceptionMessage Expiration date is enforced */ - public function testValidateExpiredateEnforceButNotSet() { + public function testvalidateExpirationDateEnforceButNotSet() { $this->config->method('getAppValue') ->will($this->returnValueMap([ ['core', 'shareapi_enforce_expire_date', 'no', 'yes'], ])); - $this->invokePrivate($this->manager, 'validateExpiredate', [null]); + $this->invokePrivate($this->manager, 'validateExpirationDate', [null]); } - public function testValidateExpiredateEnforceToFarIntoFuture() { + public function testvalidateExpirationDateEnforceToFarIntoFuture() { // Expire date in the past $future = new \DateTime(); $future->add(new \DateInterval('P7D')); @@ -630,7 +633,7 @@ class ManagerTest extends \Test\TestCase { ])); try { - $this->invokePrivate($this->manager, 'validateExpiredate', [$future]); + $this->invokePrivate($this->manager, 'validateExpirationDate', [$future]); } catch (\OC\HintException $e) { $this->assertEquals('Cannot set expiration date more than 3 days in the future', $e->getMessage()); $this->assertEquals('Cannot set expiration date more than 3 days in the future', $e->getHint()); @@ -638,7 +641,7 @@ class ManagerTest extends \Test\TestCase { } } - public function testValidateExpiredateEnforceValid() { + public function testvalidateExpirationDateEnforceValid() { // Expire date in the past $future = new \DateTime(); $future->add(new \DateInterval('P2D')); @@ -652,27 +655,27 @@ class ManagerTest extends \Test\TestCase { ['core', 'shareapi_expire_after_n_days', '7', '3'], ])); - $future = $this->invokePrivate($this->manager, 'validateExpiredate', [$future]); + $future = $this->invokePrivate($this->manager, 'validateExpirationDate', [$future]); $this->assertEquals($expected, $future->format(\DateTime::ISO8601)); } - public function testValidateExpiredateNoDateNoDefaultNull() { + public function testvalidateExpirationDateNoDateNoDefaultNull() { $date = new \DateTime(); $date->add(new \DateInterval('P5D')); - $res = $this->invokePrivate($this->manager, 'validateExpiredate', [$date]); + $res = $this->invokePrivate($this->manager, 'validateExpirationDate', [$date]); $this->assertEquals($date, $res); } - public function testValidateExpiredateNoDateNoDefault() { - $date = $this->invokePrivate($this->manager, 'validateExpiredate', [null]); + public function testvalidateExpirationDateNoDateNoDefault() { + $date = $this->invokePrivate($this->manager, 'validateExpirationDate', [null]); $this->assertNull($date); } - public function testValidateExpiredateNoDateDefault() { + public function testvalidateExpirationDateNoDateDefault() { $future = new \DateTime(); $future->add(new \DateInterval('P3D')); $future->setTime(0,0,0); @@ -683,7 +686,7 @@ class ManagerTest extends \Test\TestCase { ['core', 'shareapi_expire_after_n_days', '7', '3'], ])); - $date = $this->invokePrivate($this->manager, 'validateExpiredate', [null]); + $date = $this->invokePrivate($this->manager, 'validateExpirationDate', [null]); $this->assertEquals($future->format(\DateTime::ISO8601), $date->format(\DateTime::ISO8601)); } @@ -755,18 +758,22 @@ class ManagerTest extends \Test\TestCase { * @expectedExceptionMessage Path already shared with this user */ public function testUserCreateChecksIdenticalShareExists() { - $share = new \OC\Share20\Share(); + $share = new \OC\Share20\Share(); + $share2 = new \OC\Share20\Share(); $sharedWith = $this->getMock('\OCP\IUser'); - $share->setSharedWith($sharedWith); - $path = $this->getMock('\OCP\Files\Node'); - $share->setPath($path); + + $share->setSharedWith($sharedWith)->setPath($path) + ->setProviderId('foo')->setId('bar'); + + $share2->setSharedWith($sharedWith)->setPath($path) + ->setProviderId('foo')->setId('baz'); $this->defaultProvider ->method('getSharesByPath') ->with($path) - ->willReturn([$share]); + ->willReturn([$share2]); $this->invokePrivate($this->manager, 'userCreateChecks', [$share]); } @@ -776,18 +783,24 @@ class ManagerTest extends \Test\TestCase { * @expectedExceptionMessage Path already shared with this user */ public function testUserCreateChecksIdenticalPathSharedViaGroup() { - $share = new \OC\Share20\Share(); + $share = new \OC\Share20\Share(); + $sharedWith = $this->getMock('\OCP\IUser'); $owner = $this->getMock('\OCP\IUser'); $path = $this->getMock('\OCP\Files\Node'); + $share->setSharedWith($sharedWith) ->setPath($path) - ->setShareOwner($owner); + ->setShareOwner($owner) + ->setProviderId('foo') + ->setId('bar'); $share2 = new \OC\Share20\Share(); $owner2 = $this->getMock('\OCP\IUser'); $share2->setShareType(\OCP\Share::SHARE_TYPE_GROUP) - ->setShareOwner($owner2); + ->setShareOwner($owner2) + ->setProviderId('foo') + ->setId('baz'); $group = $this->getMock('\OCP\IGroup'); $group->method('inGroup') @@ -804,19 +817,23 @@ class ManagerTest extends \Test\TestCase { $this->invokePrivate($this->manager, 'userCreateChecks', [$share]); } - public function xtestUserCreateChecksIdenticalPathNotSharedWithUser() { + public function testUserCreateChecksIdenticalPathNotSharedWithUser() { $share = new \OC\Share20\Share(); $sharedWith = $this->getMock('\OCP\IUser'); $owner = $this->getMock('\OCP\IUser'); $path = $this->getMock('\OCP\Files\Node'); $share->setSharedWith($sharedWith) ->setPath($path) - ->setShareOwner($owner); + ->setShareOwner($owner) + ->setProviderId('foo') + ->setId('bar'); $share2 = new \OC\Share20\Share(); $owner2 = $this->getMock('\OCP\IUser'); $share2->setShareType(\OCP\Share::SHARE_TYPE_GROUP) - ->setShareOwner($owner2); + ->setShareOwner($owner2) + ->setProviderId('foo') + ->setId('baz'); $group = $this->getMock('\OCP\IGroup'); $group->method('inGroup') @@ -888,13 +905,16 @@ class ManagerTest extends \Test\TestCase { $share = new \OC\Share20\Share(); $sharedWith = $this->getMock('\OCP\IGroup'); - $share->setSharedWith($sharedWith); - $path = $this->getMock('\OCP\Files\Node'); - $share->setPath($path); + $share->setSharedWith($sharedWith) + ->setPath($path) + ->setProviderId('foo') + ->setId('bar'); $share2 = new \OC\Share20\Share(); - $share2->setSharedWith($sharedWith); + $share2->setSharedWith($sharedWith) + ->setProviderId('foo') + ->setId('baz'); $this->defaultProvider->method('getSharesByPath') ->with($path) @@ -1294,7 +1314,7 @@ class ManagerTest extends \Test\TestCase { 'generalCreateChecks', 'linkCreateChecks', 'pathCreateChecks', - 'validateExpiredate', + 'validateExpirationDate', 'verifyPassword', ]) ->getMock(); @@ -1310,16 +1330,13 @@ class ManagerTest extends \Test\TestCase { $date = new \DateTime(); - $share = $this->createShare( - null, - \OCP\Share::SHARE_TYPE_LINK, - $path, - null, - $sharedBy, - null, - \OCP\Constants::PERMISSION_ALL, - $date, - 'password'); + $share = new \OC\Share20\Share(); + $share->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setPath($path) + ->setSharedBy($sharedBy) + ->setPermissions(\OCP\Constants::PERMISSION_ALL) + ->setExpirationDate($date) + ->setPassword('password'); $manager->expects($this->once()) ->method('canShare') @@ -1335,7 +1352,7 @@ class ManagerTest extends \Test\TestCase { ->method('pathCreateChecks') ->with($path); $manager->expects($this->once()) - ->method('validateExpiredate') + ->method('validateExpirationDate') ->with($date) ->will($this->returnArgument(0)); $manager->expects($this->once()) @@ -1356,24 +1373,9 @@ class ManagerTest extends \Test\TestCase { ->expects($this->once()) ->method('create') ->with($share) - ->will($this->returnArgument(0)); - - $share->expects($this->once()) - ->method('setShareOwner') - ->with($shareOwner); - $share->expects($this->once()) - ->method('setTarget') - ->with('/target'); - $share->method('getTarget') - ->willReturn('/target'); - $share->expects($this->once()) - ->method('setExpirationDate') - ->with($date); - $share->expects($this->once()) - ->method('setPassword') - ->with('hashed'); - $share->method('getToken') - ->willReturn('token'); + ->will($this->returnCallback(function(Share $share) { + return $share->setId(42); + })); $hookListner = $this->getMockBuilder('Dummy')->setMethods(['pre', 'post'])->getMock(); \OCP\Util::connectHook('OCP\Share', 'pre_shared', $hookListner, 'pre'); @@ -1409,8 +1411,6 @@ class ManagerTest extends \Test\TestCase { 'shareWith' => null, ]; - $share->method('getId')->willReturn(42); - $hookListner->expects($this->once()) ->method('pre') ->with($this->equalTo($hookListnerExpectsPre)); @@ -1418,7 +1418,14 @@ class ManagerTest extends \Test\TestCase { ->method('post') ->with($this->equalTo($hookListnerExpectsPost)); - $manager->createShare($share); + /** @var IShare $share */ + $share = $manager->createShare($share); + + $this->assertSame($shareOwner, $share->getShareOwner()); + $this->assertEquals('/target', $share->getTarget()); + $this->assertSame($date, $share->getExpirationDate()); + $this->assertEquals('token', $share->getToken()); + $this->assertEquals('hashed', $share->getPassword()); } /** @@ -1543,6 +1550,249 @@ class ManagerTest extends \Test\TestCase { $this->assertTrue($this->manager->checkPassword($share, 'password')); } + + /** + * @expectedException Exception + * @expectedExceptionMessage The Share API is disabled + */ + public function testUpdateShareCantShare() { + $manager = $this->createManagerMock() + ->setMethods(['canShare']) + ->getMock(); + + $manager->expects($this->once())->method('canShare')->willReturn(false); + $share = new \OC\Share20\Share(); + $manager->updateShare($share); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Can't change share type + */ + public function testUpdateShareCantChangeShareType() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById' + ]) + ->getMock(); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_GROUP); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_USER); + + $manager->updateShare($share); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Can only update recipient on user shares + */ + public function testUpdateShareCantChangeRecipientForGroupShare() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById' + ]) + ->getMock(); + + $origGroup = $this->getMock('\OCP\IGroup'); + $newGroup = $this->getMock('\OCP\IGroup'); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setSharedWith($origGroup); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setSharedWith($newGroup); + + $manager->updateShare($share); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Can't share with the share owner + */ + public function testUpdateShareCantShareWithOwner() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById' + ]) + ->getMock(); + + $origUser = $this->getMock('\OCP\IUser'); + $newUser = $this->getMock('\OCP\IUser'); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith($origUser); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith($newUser) + ->setShareOwner($newUser); + + $manager->updateShare($share); + } + + public function testUpdateShareUser() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById', + 'generalCreateChecks', + 'userCreateChecks', + 'pathCreateChecks', + ]) + ->getMock(); + + $origUser = $this->getMock('\OCP\IUser'); + $newUser = $this->getMock('\OCP\IUser'); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith($origUser); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith($origUser) + ->setShareOwner($newUser); + + $this->defaultProvider->expects($this->once()) + ->method('update') + ->with($share) + ->willReturn($share); + + $hookListner = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock(); + \OCP\Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListner, 'post'); + $hookListner->expects($this->never())->method('post'); + + + $manager->updateShare($share); + } + + public function testUpdateShareGroup() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById', + 'generalCreateChecks', + 'groupCreateChecks', + 'pathCreateChecks', + ]) + ->getMock(); + + $origGroup = $this->getMock('\OCP\IGroup'); + $user = $this->getMock('\OCP\IUser'); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setSharedWith($origGroup); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setSharedWith($origGroup) + ->setShareOwner($user); + + $this->defaultProvider->expects($this->once()) + ->method('update') + ->with($share) + ->willReturn($share); + + $hookListner = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock(); + \OCP\Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListner, 'post'); + $hookListner->expects($this->never())->method('post'); + + + $manager->updateShare($share); + } + + public function testUpdateShareLink() { + $manager = $this->createManagerMock() + ->setMethods([ + 'canShare', + 'getShareById', + 'generalCreateChecks', + 'linkCreateChecks', + 'pathCreateChecks', + 'verifyPassword', + 'validateExpirationDate', + ]) + ->getMock(); + + $user = $this->getMock('\OCP\IUser'); + $user->method('getUID')->willReturn('owner'); + + $originalShare = new \OC\Share20\Share(); + $originalShare->setShareType(\OCP\Share::SHARE_TYPE_LINK); + + $tomorrow = new \DateTime(); + $tomorrow->setTime(0,0,0); + $tomorrow->add(new \DateInterval('P1D')); + + $manager->expects($this->once())->method('canShare')->willReturn(true); + $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare); + $manager->expects($this->once())->method('validateExpirationDate')->with($tomorrow)->willReturn($tomorrow); + + $file = $this->getMock('OCP\Files\File', [], [], 'File'); + $file->method('getId')->willReturn(100); + + $share = new \OC\Share20\Share(); + $share->setProviderId('foo') + ->setId('42') + ->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setSharedBy($user) + ->setShareOwner($user) + ->setPassword('password') + ->setExpirationDate($tomorrow) + ->setPath($file); + + $this->defaultProvider->expects($this->once()) + ->method('update') + ->with($share) + ->willReturn($share); + + $hookListner = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock(); + \OCP\Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListner, 'post'); + $hookListner->expects($this->once())->method('post')->with([ + 'itemType' => 'file', + 'itemSource' => 100, + 'date' => $tomorrow, + 'uidOwner' => 'owner', + ]); + + + $manager->updateShare($share); + } } class DummyPassword { |