diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index dde5d3c50af3..d673da09f70b 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -2,6 +2,7 @@ // Firefox and Konqueror tries to download application/json for me. --Arthur OCP\JSON::setContentTypeHeader('text/plain'); +OCP\JSON::callCheck(); // If a directory token is sent along check if public upload is permitted. // If not, check the login. @@ -12,46 +13,48 @@ // The standard case, files are uploaded through logged in users :) OCP\JSON::checkLoggedIn(); $dir = isset($_POST['dir']) ? $_POST['dir'] : ""; - if (!$dir || empty($dir) || $dir === false) { - OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Unable to set upload directory.'))))); + if (!$dir) { + OCP\JSON::error(array('data' => array('message' => $l->t('Unable to set upload directory.')))); die(); } } else { - $linkItem = OCP\Share::getShareByToken($_POST['dirToken']); - if ($linkItem === false) { - OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Invalid Token'))))); - die(); - } - - if (!($linkItem['permissions'] & OCP\PERMISSION_CREATE)) { - OCP\JSON::checkLoggedIn(); - } else { - // resolve reshares - $rootLinkItem = OCP\Share::resolveReShare($linkItem); - - // Setup FS with owner - OC_Util::tearDownFS(); - OC_Util::setupFS($rootLinkItem['uid_owner']); - - // The token defines the target directory (security reasons) - $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); - $dir = sprintf( - "/%s/%s", - $path, - isset($_POST['subdir']) ? $_POST['subdir'] : '' + $shareManager = OCP\Share::getShareManager(); + if ($shareManager) { + $filter = array( + 'shareTypeId' => 'link', + 'token' => $_POST['dirToken'], ); - - if (!$dir || empty($dir) || $dir === false) { - OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Unable to set upload directory.'))))); + $share = $shareManager->getShares('file', $filter, 1); + if (empty($share)) { + // Try folder item type instead + $share = $shareManager->getShares('folder', $filter, 1); + } + if (!empty($share)) { + $share = reset($share); + if ($share->isCreatable()) { + OC_Util::tearDownFS(); + OC_Util::setupFS($share->getItemOwner()); + $path = \OC\Files\Filesystem::getPath($share->getItemSource()); + $dir = sprintf( + "/%s/%s", + $path, + isset($_POST['subdir']) ? $_POST['subdir'] : '' + ); + if (!$dir) { + OCP\JSON::error(array('data' => array('message' => $l->t('Unable to set upload directory.')))); + die(); + } + } + } else { + OCP\JSON::error(array('data' => array('message' => $l->t('Invalid Token')))); die(); } + } else { + OCP\JSON::error(array('data' => array('message' => $l->t('Unable to set upload directory.')))); + die(); } } - -OCP\JSON::callCheck(); - - // get array with current storage stats (e.g. max file size) $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index b8d686234939..27dff50f7621 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -1140,7 +1140,7 @@ public function getSharingUsersArray($sharingEnabled, $filePath, $currentUserId if ($sharingEnabled) { // Find out who, if anyone, is sharing the file - $result = \OCP\Share::getUsersSharingFile($ownerPath, $owner, true); + $result = $this->getUsersSharingFile($ownerPath, $owner, true); $userIds = $result['users']; if ($result['public']) { $userIds[] = $this->publicShareKeyId; @@ -1179,6 +1179,47 @@ public function getSharingUsersArray($sharingEnabled, $filePath, $currentUserId } + /** + * @brief Find which users can access a shared item + * @param $path to the file + * @param $user owner of the file + * @param include owner to the list of users with access to the file + * @return array + * @note $path needs to be relative to user data dir, e.g. 'file.txt' + * not '/admin/data/file.txt' + */ + public function getUsersSharingFile($path, $user, $includeOwner = false) { + $users = array(); + $public = false; + $view = new \OC\Files\View('/' . $user . '/files/'); + $meta = $view->getFileInfo(\OC\Files\Filesystem::normalizePath($path)); + if ($meta !== false) { + $fileId = $meta['fileid']; + $shareManager = \OCP\Share::getShareManager(); + if ($shareManager) { + $fetcher = new \OCA\Files\Share\FileShareFetcher($shareManager, + \OC_Group::getManager() + ); + $shares = $fetcher->getById($fileId); + foreach ($shares as $share) { + if ($share->getShareTypeId() === 'link') { + $public = true; + break; + } + } + $users = $fetcher->getUsersSharedWith($shares); + if ($includeOwner) { + if (!in_array($user, $users)) { + $users[] = $user; + } + } else { + $users = array_diff($users, array($user)); + } + } + } + return array('users' => $users, 'public' => $public); + } + private function getUserWithAccessToMountPoint($users, $groups) { $result = array(); if (in_array('all', $users)) { @@ -1605,7 +1646,7 @@ private function recoverFile($file, $privateKey) { // Find out who, if anyone, is sharing the file if ($sharingEnabled) { - $result = \OCP\Share::getUsersSharingFile($file, $this->userId, true); + $result = $this->getUsersSharingFile($file, $this->userId, true); $userIds = $result['users']; $userIds[] = $this->recoveryKeyId; if ($result['public']) { diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php index 6a29d2428dc0..74deb165850b 100755 --- a/apps/files_encryption/tests/share.php +++ b/apps/files_encryption/tests/share.php @@ -32,6 +32,7 @@ require_once __DIR__ . '/util.php'; use OCA\Encryption; +use OCA\Files\Share\FileShare; /** * Class Test_Encryption_Share @@ -55,6 +56,9 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { public $subfolder; public $subsubfolder; + private static $usersSharingPolicy; + private static $linkSharingPolicy; + public static function setUpBeforeClass() { // reset backend \OC_User::clearBackends(); @@ -88,6 +92,11 @@ public static function setUpBeforeClass() { \OC_Group::createGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); \OC_Group::addToGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); \OC_Group::addToGroup(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + + self::$usersSharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + self::$linkSharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'yes'); } function setUp() { @@ -125,6 +134,9 @@ public static function tearDownAfterClass() { \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); \OC_User::deleteUser(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', self::$usersSharingPolicy); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', self::$linkSharingPolicy); } /** @@ -159,7 +171,15 @@ function testShareFile($withTeardown = true) { \OC_FileProxy::$enabled = $proxyStatus; // share the file - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('user'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $share->setPermissions(27); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -186,7 +206,7 @@ function testShareFile($withTeardown = true) { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); // unshare the file - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $shareManager->unshare($share); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -219,7 +239,15 @@ function testReShareFile($withTeardown = true) { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2 . '/files/Shared/' . $this->filename); // share the file with user2 - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('user'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + $share->setPermissions(27); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -245,8 +273,10 @@ function testReShareFile($withTeardown = true) { // login as user1 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $parentShare = reset($shareManager->getParents($share)); + // unshare the file with user2 - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + $shareManager->unshare($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -257,7 +287,7 @@ function testReShareFile($withTeardown = true) { . $this->filename . '.' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3 . '.shareKey')); // unshare the file with user1 - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $shareManager->unshare($parentShare); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -314,7 +344,15 @@ function testShareFolder($withTeardown = true) { \OC_FileProxy::$enabled = $proxyStatus; // share the folder with user1 - \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('folder'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('user'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $share->setPermissions(OCP\PERMISSION_ALL); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -343,7 +381,7 @@ function testShareFolder($withTeardown = true) { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); // unshare the folder with user1 - \OCP\Share::unshare('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $shareManager->unshare($share); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -390,7 +428,15 @@ function testReShareFolder($withTeardown = true) { \OC_FileProxy::$enabled = $proxyStatus; // share the file with user2 - \OCP\Share::shareItem('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('folder'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('user'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + $share->setPermissions(OCP\PERMISSION_ALL); + $shareManager = \OCP\Share::getShareManager(); + $folderShare = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -421,7 +467,15 @@ function testReShareFolder($withTeardown = true) { $this->assertTrue(is_array($fileInfo)); // share the file with user3 - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('user'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + $share->setPermissions(27); + $shareManager = \OCP\Share::getShareManager(); + $fileShare = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -449,7 +503,7 @@ function testReShareFolder($withTeardown = true) { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); // unshare the file with user3 - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4); + $shareManager->unshare($fileShare); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -460,8 +514,10 @@ function testReShareFolder($withTeardown = true) { // login as user1 \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $parentShare = reset($shareManager->getParents($share)); + // unshare the folder with user2 - \OCP\Share::unshare('folder', $fileInfoSubFolder['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER3); + $shareManager->unshare($folderShare); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -473,7 +529,7 @@ function testReShareFolder($withTeardown = true) { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); // unshare the folder1 with user1 - \OCP\Share::unshare('folder', $fileInfoFolder1['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER2); + $shareManager->unshare($parentShare); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -522,7 +578,14 @@ function testPublicShareFile() { \OC_FileProxy::$enabled = $proxyStatus; // share the file - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, false, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('link'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $share->setPermissions(27); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -551,7 +614,7 @@ function testPublicShareFile() { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); // unshare the file - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + $shareManager->unshare($share); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -598,7 +661,15 @@ function testShareFileWithGroup() { \OC_FileProxy::$enabled = $proxyStatus; // share the file - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('group'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + $share->setPermissions(27); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); // login as admin \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); @@ -625,7 +696,7 @@ function testShareFileWithGroup() { \Test_Encryption_Util::loginHelper(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); // unshare the file - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + $this->shareManager->unshare($share); // check if share key not exists $this->assertFalse($this->view->file_exists( @@ -885,7 +956,15 @@ function testFailShareFile() { // share the file try { - \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1, OCP\PERMISSION_ALL); + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource($fileInfo['fileid']); + $share->setShareTypeId('group'); + $share->setShareOwner(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1); + $share->setShareWith(\Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + $share->setPermissions(OCP\PERMISSION_ALL); + $shareManager = \OCP\Share::getShareManager(); + $share = $shareManager->share($share); } catch (Exception $e) { $this->assertEquals(0, strpos($e->getMessage(), "Following users are not set up for encryption")); } @@ -917,7 +996,7 @@ function testFailShareFile() { \OC_FileProxy::$enabled = $proxyStatus; // unshare the file with user1 - \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_GROUP1); + $shareManager->unshare($share); // check if share key not exists $this->assertFalse($this->view->file_exists( diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 895d446a3365..f9572b64546a 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -1,18 +1,73 @@ . + */ -OC::$CLASSPATH['OC_Share_Backend_File'] = 'files_sharing/lib/share/file.php'; -OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'files_sharing/lib/share/folder.php'; +OC::$CLASSPATH['OCA\Files\Share\FileShare'] = 'files_sharing/lib/share/fileshare.php'; +OC::$CLASSPATH['OCA\Files\Share\FileShareBackend'] = 'files_sharing/lib/share/filesharebackend.php'; +OC::$CLASSPATH['OCA\Files\Share\FolderShareBackend'] = 'files_sharing/lib/share/foldersharebackend.php'; +OC::$CLASSPATH['OCA\Files\Share\FileShareFactory'] = 'files_sharing/lib/share/filesharefactory.php'; +OC::$CLASSPATH['OCA\Files\Share\FileTargetMachine'] = 'files_sharing/lib/share/filetargetmachine.php'; +OC::$CLASSPATH['OCA\Files\Share\FileShareFetcher'] = 'files_sharing/lib/share/filesharefetcher.php'; OC::$CLASSPATH['OC\Files\Storage\Shared'] = 'files_sharing/lib/sharedstorage.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'files_sharing/lib/cache.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'files_sharing/lib/permissions.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php'; -OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); -OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); -OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); -OCP\Util::addScript('files_sharing', 'share'); -\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Shared_Updater', 'writeHook'); -\OC_Hook::connect('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook'); -\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook'); -\OC_Hook::connect('OCP\Share', 'post_shared', '\OC\Files\Cache\Shared_Updater', 'shareHook'); -\OC_Hook::connect('OCP\Share', 'pre_unshare', '\OC\Files\Cache\Shared_Updater', 'shareHook'); +OC::$CLASSPATH['OC\Files\Cache\SharedCache'] = 'files_sharing/lib/cache.php'; +OC::$CLASSPATH['OC\Files\Cache\SharedPermissions'] = 'files_sharing/lib/permissions.php'; +OC::$CLASSPATH['OC\Files\Cache\SharedUpdater'] = 'files_sharing/lib/updater.php'; +OC::$CLASSPATH['OC\Files\Cache\SharedWatcher'] = 'files_sharing/lib/watcher.php'; + +$shareManager = \OCP\Share::getShareManager(); +$timeMachine = new \OC\Share\TimeMachine(); +$fileShareFactory = new \OCA\Files\Share\FileShareFactory(); +$fileTargetMachine = new \OCA\Files\Share\FileTargetMachine(); +$userManager = \OC_User::getManager(); +$groupManager = \OC_Group::getManager(); +$tokenMachine = new \OC\Share\ShareType\TokenMachine(); +$hasher = new \PasswordHash(8, (CRYPT_BLOWFISH != 1)); +$fileShareTypes = array( + new \OC\Share\ShareType\User('file', $fileShareFactory, $fileTargetMachine, $userManager, + $groupManager + ), + new \OC\Share\ShareType\Group('file', $fileShareFactory, $fileTargetMachine, $groupManager, + $userManager + ), + new \OC\Share\ShareType\Link('file', $fileShareFactory, $fileTargetMachine, $userManager, + $tokenMachine, $hasher + ), +); +$folderShareTypes = array( + new \OC\Share\ShareType\User('folder', $fileShareFactory, $fileTargetMachine, $userManager, + $groupManager + ), + new \OC\Share\ShareType\Group('folder', $fileShareFactory, $fileTargetMachine, $groupManager, + $userManager + ), + new \OC\Share\ShareType\Link('folder', $fileShareFactory, $fileTargetMachine, $userManager, + $tokenMachine, $hasher + ), +); +$fileShareBackend = new \OCA\Files\Share\FileShareBackend($timeMachine, $fileShareTypes); +$folderShareBackend = new \OCA\Files\Share\FolderShareBackend($timeMachine, $folderShareTypes); +$shareManager->registerShareBackend($fileShareBackend); +$shareManager->registerShareBackend($folderShareBackend); +$sharedUpdater = new \OC\Files\Cache\SharedUpdater($shareManager, + new \OCA\Files\Share\FileShareFetcher($shareManager, $groupManager) +); +// TODO Wait for hook changes so we can use a closure to replace setup and pass in ShareManager +\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); +\OCP\Util::addScript('files_sharing', 'share'); \ No newline at end of file diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 3be89a39fa0c..55a2a948e7f0 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -6,6 +6,7 @@ $(document).ready(function() { $('#fileList').one('fileActionsReady',function(){ OC.Share.loadIcons('file'); + OC.Share.loadIcons('folder'); }); FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 33cd14288998..9fa8f9329a9f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -3,7 +3,7 @@ * ownCloud * * @author Michael Gapczynski - * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * @copyright 2012-2013 Michael Gapczynski mtgap@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -21,51 +21,46 @@ namespace OC\Files\Cache; +use OCA\Files\Share\FileShareFetcher; + /** * Metadata cache for shared files * * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead */ -class Shared_Cache extends Cache { +class SharedCache extends Cache { private $storage; - private $files = array(); + private $storageId; + private $fetcher; - public function __construct($storage) { + /** + * The constructor + * @param \OC\Files\Storage\Shared $storage + * @param \OCA\Files\Share\FileShareFetcher $fetcher + */ + public function __construct($storage, FileShareFetcher $fetcher) { $this->storage = $storage; + $this->fetcher = $fetcher; } /** - * @brief Get the source cache of a shared file or folder + * Get the source cache of a shared file or folder * @param string $target Shared target file path - * @return \OC\Files\Cache\Cache + * @return array Consisting of \OC\Files\Cache\Cache and the internal path */ - private function getSourceCache($target) { - $source = \OC_Share_Backend_File::getSource($target); - if (isset($source['path']) && isset($source['fileOwner'])) { - \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); - if (is_array($mount)) { - $fullPath = $mount[key($mount)]->getMountPoint().$source['path']; - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); - if ($storage) { - $this->files[$target] = $internalPath; - $cache = $storage->getCache(); - $this->storageId = $storage->getId(); - $this->numericId = $cache->getNumericStorageId(); - return $cache; - } - } + protected function getSourceCache($path) { + list ($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + $cache = $storage->getCache(); + $this->storageId = $storage->getId(); + return array($cache, $internalPath); } - return false; + return array(null, null); } public function getNumericStorageId() { - if (isset($this->numericId)) { - return $this->numericId; - } else { - return false; - } + return null; } /** @@ -75,32 +70,84 @@ public function getNumericStorageId() { * @return array */ public function get($file) { - if ($file == '') { - $data = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); - $etag = \OCP\Config::getUserValue(\OCP\User::getUser(), 'files_sharing', 'etag'); + if ($file === '' || $file === -1) { + $files = array(); + $size = 0; + $mtime = 0; + $encrypted = false; + $unencryptedSize = 0; + $shares = $this->fetcher->getAll(); + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + // Ignore duplicate shares + if (!isset($files[$fileId])) { + if ($share->getEncrypted()) { + $encrypted = true; + } + if ($share->getMtime() > $mtime) { + $mtime = $share->getMtime(); + } + $size += $share->getSize(); + $unencryptedSize += $share->getUnencryptedSize(); + $files[$fileId] = true; + } + } + $etag = $this->fetcher->getETag(); if (!isset($etag)) { $etag = $this->storage->getETag(''); - \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $etag); - } - $data['etag'] = $etag; - return $data; - } else if (is_string($file)) { - if ($cache = $this->getSourceCache($file)) { - return $cache->get($this->files[$file]); + $this->fetcher->setETag($etag); } + return array( + 'fileid' => -1, + 'storage' => null, + 'path' => '', + 'parent' => -1, + 'name' => 'Shared', + 'mimetype' => 'httpd/unix-directory', + 'mimepart' => 'httpd', + 'size' => $size, + 'mtime' => $mtime, + 'storage_mtime' => $mtime, + 'encrypted' => $encrypted, + 'unencrypted_size' => $unencryptedSize, + 'etag' => $etag, + ); } else { - $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`,' - .' `size`, `mtime`, `encrypted`' - .' FROM `*PREFIX*filecache` WHERE `fileid` = ?'); - $result = $query->execute(array($file)); - $data = $result->fetchRow(); - $data['fileid'] = (int)$data['fileid']; - $data['size'] = (int)$data['size']; - $data['mtime'] = (int)$data['mtime']; - $data['encrypted'] = (bool)$data['encrypted']; - $data['mimetype'] = $this->getMimetype($data['mimetype']); - $data['mimepart'] = $this->getMimetype($data['mimepart']); + $data = false; + if (is_string($file)) { + $shares = $this->fetcher->getByPath($file); + } else { + $shares = $this->fetcher->getById($file); + } + foreach ($shares as $share) { + // Check if we have an exact share for this path or id + if (is_string($file) && $share->getItemTarget() === $file + || is_int($file) && $share->getItemSource() === $file + ) { + $data = $share->getMetadata(); + break; + } + } + if ($data) { + $data['mimetype'] = $this->getMimetype($data['mimetype']); + $data['mimepart'] = $this->getMimetype($data['mimepart']); + if ($data['storage_mtime'] === 0) { + $data['storage_mtime'] = $data['mtime']; + } + } else if (!empty($shares)) { + $share = reset($shares); + $folder = $share->getItemTarget(); + if (is_string($file)) { + list($cache, $internalPath) = $this->getSourceCache($file); + $data = $cache->get($internalPath); + } else { + list($cache) = $this->getSourceCache($folder); + $data = $cache->get($file); + } + if ($data) { + $data['path'] = $folder.substr($data['path'], strlen($share->getPath())); + } + } return $data; } return false; @@ -113,19 +160,30 @@ public function get($file) { * @return array */ public function getFolderContents($folder) { - if ($folder == '') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); - foreach ($files as &$file) { - $file['mimetype'] = $this->getMimetype($file['mimetype']); - $file['mimepart'] = $this->getMimetype($file['mimepart']); + $files = array(); + if ($folder === '') { + $shares = $this->fetcher->getAll(); + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + // Ignore duplicate shares + if (!isset($files[$fileId])) { + $file = $share->getMetadata(); + $file['mimetype'] = $this->getMimetype($file['mimetype']); + $file['mimepart'] = $this->getMimetype($file['mimepart']); + if ($file['storage_mtime'] === 0) { + $file['storage_mtime'] = $file['mtime']; + } + $files[$fileId] = $file; + } } - return $files; + $files = array_values($files); } else { - if ($cache = $this->getSourceCache($folder)) { - return $cache->getFolderContents($this->files[$folder]); + list($cache, $internalPath) = $this->getSourceCache($folder); + if ($cache && $internalPath) { + $files = $cache->getFolderContents($internalPath); } } - return false; + return $files; } /** @@ -137,12 +195,17 @@ public function getFolderContents($folder) { * @return int file id */ public function put($file, array $data) { - if ($file === '' && isset($data['etag'])) { - return \OCP\Config::setUserValue(\OCP\User::getUser(), 'files_sharing', 'etag', $data['etag']); - } else if ($cache = $this->getSourceCache($file)) { - return $cache->put($this->files[$file], $data); + if ($file === '') { + if (isset($data['etag'])) { + $this->fetcher->setETag($data['etag']); + } + } else { + list($cache, $internalPath) = $this->getSourceCache($file); + if ($cache && $internalPath) { + return $cache->put($internalPath, $data); + } } - return false; + return -1; } /** @@ -152,8 +215,18 @@ public function put($file, array $data) { * @return int */ public function getId($file) { - if ($cache = $this->getSourceCache($file)) { - return $cache->getId($this->files[$file]); + if ($file !== '') { + $shares = $this->fetcher->getByPath($file); + foreach ($shares as $share) { + // Check if we have an exact share for this path + if ($share->getItemTarget() === $file) { + return $share->getItemSource(); + } + } + list($cache, $internalPath) = $this->getSourceCache($file); + if ($cache && $internalPath) { + return $cache->getId($internalPath); + } } return -1; } @@ -165,7 +238,7 @@ public function getId($file) { * @return bool */ public function inCache($file) { - if ($file == '') { + if ($file === '') { return true; } return parent::inCache($file); @@ -177,8 +250,9 @@ public function inCache($file) { * @param string $file */ public function remove($file) { - if ($cache = $this->getSourceCache($file)) { - $cache->remove($this->files[$file]); + list($cache, $internalPath) = $this->getSourceCache($file); + if ($cache && $internalPath) { + return $cache->remove($internalPath); } } @@ -189,10 +263,13 @@ public function remove($file) { * @param string $target */ public function move($source, $target) { - if ($cache = $this->getSourceCache($source)) { - $file = \OC_Share_Backend_File::getSource($target); - if ($file && isset($file['path'])) { - $cache->move($this->files[$source], $file['path']); + list($cache, $oldInternalPath) = $this->getSourceCache($source); + if ($cache && $oldInternalPath) { + // Renaming/moving is only allowed within shared folders + if (dirname($target) !== '') { + list( , $newInternalPath) = $this->fetcher->resolvePath(dirname($target)); + $newInternalPath .= '/'.basename($target); + $cache->move($oldInternalPath, $newInternalPath); } } } @@ -210,11 +287,12 @@ public function clear() { * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { - if ($file == '') { + if ($file === '') { return self::COMPLETE; } - if ($cache = $this->getSourceCache($file)) { - return $cache->getStatus($this->files[$file]); + list($cache, $internalPath) = $this->getSourceCache($file); + if ($cache && $internalPath) { + return $cache->getStatus($internalPath); } return self::NOT_FOUND; } @@ -226,31 +304,35 @@ public function getStatus($file) { * @return array of file data */ public function search($pattern) { - // TODO + $pattern = $this->normalize($pattern); + // Remove the '%' characters that were added for the LIKE query + $trimmedPattern = trim($pattern, '%'); + return $this->searchCommon($pattern, 'search', function($share) use ($trimmedPattern) { + if (stripos($share->getItemTarget(), $trimmedPattern) !== false) { + return true; + } + return false; + }); } /** * search for files by mimetype * - * @param string $part1 - * @param string $part2 + * @param string $mimetype * @return array */ public function searchByMime($mimetype) { - if (strpos($mimetype, '/')) { - $where = '`mimetype` = ?'; - } else { - $where = '`mimepart` = ?'; - } - $mimetype = $this->getMimetypeId($mimetype); - $ids = $this->getAll(); - $placeholders = join(',', array_fill(0, count($ids), '?')); - $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')' + $mimetypeId = (int)$this->getMimetypeId($mimetype); + return $this->searchCommon($mimetype, 'searchByMime', + function($share) use ($mimetypeId) { + if ($share->getMimepart() === $mimetypeId + || $share->getMimetype() === $mimetypeId + ) { + return true; + } + return false; + } ); - $result = $query->execute(array_merge(array($mimetype), $ids)); - return $result->fetchAll(); } /** @@ -260,8 +342,13 @@ public function searchByMime($mimetype) { * @return int */ public function calculateFolderSize($path) { - if ($cache = $this->getSourceCache($path)) { - return $cache->calculateFolderSize($this->files[$path]); + if ($path === '') { + $data = $this->get(''); + return $data['size']; + } + list($cache, $internalPath) = $this->getSourceCache($path); + if ($cache && $internalPath) { + return $cache->calculateFolderSize($internalPath); } return 0; } @@ -272,7 +359,20 @@ public function calculateFolderSize($path) { * @return int[] */ public function getAll() { - return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + $ids = array(); + $shares = $this->fetcher->getAll(); + $folderMimetypeId = (int)$this->getMimetypeId('httpd/unix-directory'); + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + if (!isset($ids[$fileId])) { + $ids[$fileId] = $fileId; + if ($share->getMimetype() === $folderMimetypeId) { + $childrenIds = $this->getChildrenIds($fileId); + $ids = array_merge($ids, array_combine($childrenIds, $childrenIds)); + } + } + } + return array_values($ids); } /** @@ -288,4 +388,85 @@ public function getIncomplete() { return false; } -} + /** + * Search all file shares and their children + * @param mixed $query The search query to pass to the method + * @param string $method The method to call inside a cache - 'search' or 'searchByMime' + * @param callable $callback A callable that returns true if the file share passed as an + * argument matches the search query, elsewise returns false + * @return array + */ + protected function searchCommon($query, $method, $callback) { + $files = array(); + $caches = array(); + $folderMimetypeId = (int)$this->getMimetypeId('httpd/unix-directory'); + $shares = $this->fetcher->getAll(); + // Look through all file shares for matches + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + // Ignore duplicate shares + if (!isset($files[$fileId])) { + if ($callback($share)) { + $file = $share->getMetadata(); + $file['mimetype'] = $this->getMimetype($file['mimetype']); + $file['mimepart'] = $this->getMimetype($file['mimepart']); + if ($file['storage_mtime'] === 0) { + $file['storage_mtime'] = $file['mtime']; + } + $files[$fileId] = $file; + } + $storageId = $share->getStorage(); + if ($share->getMimetype() === $folderMimetypeId && !isset($caches[$storageId])) { + list($cache) = $this->getSourceCache($share->getItemTarget()); + $caches[$storageId] = $cache; + } + } + } + $ids = $this->getAll(); + // Look inside shared folders' caches for more results + foreach ($caches as $cache) { + $result = $cache->$method($query); + foreach ($result as $file) { + $fileId = (int)$file['fileid']; + // Ensure that the search result is a shared file + if (!isset($files[$fileId]) && in_array($fileId, $ids) !== false) { + // Find the shared folder this file is inside + foreach ($shares as $share) { + $path = $share->getPath(); + if ((int)$file['storage'] === $share->getStorage() + && strpos($file['path'], $path) === 0 + ) { + // Rebuild the path relative to the Shared folder + $folder = $share->getItemTarget(); + $file['path'] = $folder.substr($file['path'], strlen($path)); + $files[$fileId] = $file; + } + } + } + } + } + return array_values($files); + } + + /** + * Get all children file ids for the specified file id + * @param int $fileId + * @return int[] + */ + protected function getChildrenIds($fileId) { + $ids = array(); + $folderMimetypeId = (int)$this->getMimetypeId('httpd/unix-directory'); + $sql = 'SELECT `fileid`, `mimetype` FROM `*PREFIX*filecache` WHERE `parent` = ?'; + $result = \OC_DB::executeAudited($sql, array($fileId)); + $rows = $result->fetchAll(); + foreach ($rows as $row) { + $fileId = (int)$row['fileid']; + $ids[] = $fileId; + if ((int)$row['mimetype'] === $folderMimetypeId) { + $ids = array_merge($ids, $this->getChildrenIds($fileId)); + } + } + return $ids; + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index e2978e12bfb1..e7ce00aa825e 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -1,26 +1,41 @@ . -*/ + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2012-2013 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ + namespace OC\Files\Cache; -class Shared_Permissions extends Permissions { +use OCA\Files\Share\FileShareFetcher; + +class SharedPermissions extends Permissions { + + private $fetcher; + + /** + * The constructor + * @param \OC\Files\Storage\Storage|string $storage + * @param \OCA\FilesSharing\Share\FileShareFetcher $fetcher + */ + public function __construct($storage, FileShareFetcher $fetcher) { + parent::__construct($storage); + $this->fetcher = $fetcher; + } /** * get the permissions for a single file @@ -30,16 +45,10 @@ class Shared_Permissions extends Permissions { * @return int (-1 if file no permissions set) */ public function get($fileId, $user) { - if ($fileId == -1) { + if ($fileId === -1) { return \OCP\PERMISSION_READ; } - $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, - null, true); - if ($source) { - return $source['permissions']; - } else { - return -1; - } + return $this->fetcher->getPermissionsById($fileId); } /** @@ -65,7 +74,7 @@ public function getMultiple($fileIds, $user) { return array(); } foreach ($fileIds as $fileId) { - $filePermissions[$fileId] = self::get($fileId, $user); + $filePermissions[$fileId] = $this->get($fileId, $user); } return $filePermissions; } @@ -78,16 +87,17 @@ public function getMultiple($fileIds, $user) { * @return int[] */ public function getDirectoryPermissions($parentId, $user) { + $filePermissions = array(); // Root of the Shared folder if ($parentId === -1) { - return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_PERMISSIONS); - } - $permissions = $this->get($parentId, $user); - $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `parent` = ?'); - $result = $query->execute(array($parentId)); - $filePermissions = array(); - while ($row = $result->fetchRow()) { - $filePermissions[$row['fileid']] = $permissions; + $filePermissions = $this->fetcher->getAllPermissions(); + } else { + $permissions = $this->get($parentId, $user); + $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `parent` = ?'; + $result = \OC_DB::executeAudited($sql, array($parentId)); + while ($row = $result->fetchRow()) { + $filePermissions[$row['fileid']] = $permissions; + } } return $filePermissions; } diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php deleted file mode 100644 index 07e7a4ca0c5c..000000000000 --- a/apps/files_sharing/lib/share/file.php +++ /dev/null @@ -1,179 +0,0 @@ -. -*/ - -class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { - - const FORMAT_SHARED_STORAGE = 0; - const FORMAT_GET_FOLDER_CONTENTS = 1; - const FORMAT_FILE_APP_ROOT = 2; - const FORMAT_OPENDIR = 3; - const FORMAT_GET_ALL = 4; - const FORMAT_PERMISSIONS = 5; - - private $path; - - public function isValidSource($itemSource, $uidOwner) { - $query = \OC_DB::prepare('SELECT `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); - $result = $query->execute(array($itemSource)); - if ($row = $result->fetchRow()) { - $this->path = $row['name']; - return true; - } - return false; - } - - public function getFilePath($itemSource, $uidOwner) { - if (isset($this->path)) { - $path = $this->path; - $this->path = null; - return $path; - } - return false; - } - - public function generateTarget($filePath, $shareWith, $exclude = null) { - $target = '/'.basename($filePath); - if (isset($exclude)) { - if ($pos = strrpos($target, '.')) { - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - } else { - $name = $target; - $ext = ''; - } - $i = 2; - $append = ''; - while (in_array($name.$append.$ext, $exclude)) { - $append = ' ('.$i.')'; - $i++; - } - $target = $name.$append.$ext; - } - return $target; - } - - public function formatItems($items, $format, $parameters = null) { - if ($format == self::FORMAT_SHARED_STORAGE) { - // Only 1 item should come through for this format call - return array( - 'parent' => $items[key($items)]['parent'], - 'path' => $items[key($items)]['path'], - 'storage' => $items[key($items)]['storage'], - 'permissions' => $items[key($items)]['permissions'], - 'uid_owner' => $items[key($items)]['uid_owner'] - ); - } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { - $files = array(); - foreach ($items as $item) { - $file = array(); - $file['fileid'] = $item['file_source']; - $file['storage'] = $item['storage']; - $file['path'] = $item['file_target']; - $file['parent'] = $item['file_parent']; - $file['name'] = basename($item['file_target']); - $file['mimetype'] = $item['mimetype']; - $file['mimepart'] = $item['mimepart']; - $file['size'] = $item['size']; - $file['mtime'] = $item['mtime']; - $file['encrypted'] = $item['encrypted']; - $file['etag'] = $item['etag']; - $files[] = $file; - } - return $files; - } else if ($format == self::FORMAT_FILE_APP_ROOT) { - $mtime = 0; - $size = 0; - foreach ($items as $item) { - if ($item['mtime'] > $mtime) { - $mtime = $item['mtime']; - } - $size += (int)$item['size']; - } - return array( - 'fileid' => -1, - 'name' => 'Shared', - 'mtime' => $mtime, - 'mimetype' => 'httpd/unix-directory', - 'size' => $size - ); - } else if ($format == self::FORMAT_OPENDIR) { - $files = array(); - foreach ($items as $item) { - $files[] = basename($item['file_target']); - } - return $files; - } else if ($format == self::FORMAT_GET_ALL) { - $ids = array(); - foreach ($items as $item) { - $ids[] = $item['file_source']; - } - return $ids; - } else if ($format === self::FORMAT_PERMISSIONS) { - $filePermissions = array(); - foreach ($items as $item) { - $filePermissions[$item['file_source']] = $item['permissions']; - } - return $filePermissions; - } - return array(); - } - - public static function getSource($target) { - if ($target == '') { - return false; - } - $target = '/'.$target; - $target = rtrim($target, '/'); - $pos = strpos($target, '/', 1); - // Get shared folder name - if ($pos !== false) { - $folder = substr($target, 0, $pos); - $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($source) { - $source['path'] = $source['path'].substr($target, strlen($folder)); - } - } else { - $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - } - if ($source) { - if (isset($source['parent'])) { - $parent = $source['parent']; - while (isset($parent)) { - $query = \OC_DB::prepare('SELECT `parent`, `uid_owner` FROM `*PREFIX*share` WHERE `id` = ?', 1); - $item = $query->execute(array($parent))->fetchRow(); - if (isset($item['parent'])) { - $parent = $item['parent']; - } else { - $fileOwner = $item['uid_owner']; - break; - } - } - } else { - $fileOwner = $source['uid_owner']; - } - $source['fileOwner'] = $fileOwner; - return $source; - } - \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); - return false; - } - -} diff --git a/apps/files_sharing/lib/share/fileshare.php b/apps/files_sharing/lib/share/fileshare.php new file mode 100644 index 000000000000..16413019d3de --- /dev/null +++ b/apps/files_sharing/lib/share/fileshare.php @@ -0,0 +1,254 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\Share; + +/** + * Data holder for shared files and folders + */ +class FileShare extends Share { + + protected $storage; + protected $path; + protected $parent; + protected $name; + protected $mimetype; + protected $mimepart; + protected $size; + protected $mtime; + protected $storageMtime; + protected $encrypted; + protected $unencryptedSize; + protected $etag; + + public function __construct() { + $this->addType('itemSource', 'int'); + $this->addType('storage', 'int'); + $this->addType('parent', 'int'); + $this->addType('mimetype', 'int'); + $this->addType('mimepart', 'int'); + $this->addType('size', 'int'); + $this->addType('mtime', 'int'); + $this->addType('encrypted', 'bool'); + $this->addType('unencryptedSize', 'int'); + } + + /** + * Get the numeric storage id + * @return int + */ + public function getStorage() { + return $this->storage; + } + + /** + * Set the numeric storage id + * @param int $storage + */ + public function setStorage($storage) { + $this->storage = $storage; + } + + /** + * Get the source file path + * @return string + */ + public function getPath() { + return $this->path; + } + + /** + * Set the source file path + * @param string $path + */ + public function setPath($path) { + $this->path = $path; + } + + /** + * Get the parent folder id, not to be confused with getParentIds for reshares + * @return int + */ + public function getParent() { + return $this->parent; + } + + /** + * Set the parent folder id, not to be confused with setParentIds for reshares + * @param int $parent + */ + public function setParent($parent) { + $this->parent = $parent; + } + + /** + * Get the mimetype id + * @return int + */ + public function getMimetype() { + return $this->mimetype; + } + + /** + * Set the mimetype id + * @param int $mimetype + */ + public function setMimetype($mimetype) { + $this->mimetype = $mimetype; + } + + /** + * Get the mimepart id + * @return int + */ + public function getMimepart() { + return $this->mimepart; + } + + /** + * Set the mimepart id + * @param int $mimepart + */ + public function setMimepart($mimepart) { + $this->mimepart = $mimepart; + } + + /** + * Get the file size + * @return int + */ + public function getSize() { + return $this->size; + } + + /** + * Set the file size + * @param int $size + */ + public function setSize($size) { + $this->size = $size; + } + + /** + * Get the mtime + * @return int + */ + public function getMtime() { + return $this->mtime; + } + + /** + * Set the mtime + * @param int $mtime + */ + public function setMtime($mtime) { + $this->mtime = $mtime; + } + + /** + * Get the storage mtime + * @return int + */ + public function getStorageMtime() { + return $this->storageMtime; + } + + /** + * Set the storage mtime + * @param int $storageMtime + */ + public function setStorageMtime($storageMtime) { + $this->storageMtime = $storageMtime; + } + + /** + * Get if the file is encrypted + * @return bool + */ + public function getEncrypted() { + return $this->encrypted; + } + + /** + * Set if the file is encrypted + * @param bool $encrypted + */ + public function setEncrypted($encrypted) { + $this->encrypted = $encrypted; + } + + /** + * Get the unencrypted file size + * @return int + */ + public function getUnencryptedSize() { + return $this->unencryptedSize; + } + + /** + * Set the unencrypted file size + * @param int $unencryptedSize + */ + public function setUnencryptedSize($unencryptedSize) { + $this->unencryptedSize = $unencryptedSize; + } + + /** + * Get the ETag + * @return string + */ + public function getEtag() { + return $this->etag; + } + + /** + * Set the ETag + * @param string $etag + */ + public function setEtag($etag) { + $this->etag = $etag; + } + + /** + * Get the metadata + * @return array + */ + public function getMetadata() { + return array( + 'fileid' => $this->getItemSource(), + 'storage' => $this->getStorage(), + 'path' => $this->getItemTarget(), + 'parent' => $this->getParent(), + 'name' => basename($this->getItemTarget()), + 'mimetype' => $this->getMimetype(), + 'mimepart' => $this->getMimepart(), + 'size' => $this->getSize(), + 'mtime' => $this->getMtime(), + 'storage_mtime' => $this->getStorageMtime(), + 'encrypted' => $this->getEncrypted(), + 'unencrypted_size' => $this->getUnencryptedSize(), + 'etag' => $this->getEtag(), + ); + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/filesharebackend.php b/apps/files_sharing/lib/share/filesharebackend.php new file mode 100644 index 000000000000..d8af85be2a76 --- /dev/null +++ b/apps/files_sharing/lib/share/filesharebackend.php @@ -0,0 +1,81 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\Share; +use OC\Share\ShareBackend; +use OC\Share\Exception\InvalidItemException; +use OC\Share\Exception\InvalidPermissionsException; +use OC\Files\Filesystem; + +class FileShareBackend extends ShareBackend { + + /** + * Get the identifier for the item type this backend handles, should be a singular noun + * @return string + */ + public function getItemType() { + return 'file'; + } + + /** + * Get the plural form of getItemType, used for the RESTful API + * @return string + */ + public function getItemTypePlural() { + return 'files'; + } + + /** + * Check if an item is valid for the share owner + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidItemException If the file does not exist or the share + * owner does not have access to the file + * @return bool + */ + protected function isValidItem(Share $share) { + Filesystem::init($share->getShareOwner(), '/'.$share->getShareOwner().'/files'); + $path = Filesystem::getPath((int)$share->getItemSource()); + if (!isset($path)) { + throw new InvalidItemException('The file does not exist in the filesystem'); + } + return true; + } + + /** + * Check if a share's permissions are valid + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidPermissionsException If the permissions are not an + * integer or are not in the range of 1 to 31 or exceed the filesystem permissions + * @return bool + */ + protected function areValidPermissions(Share $share) { + parent::areValidPermissions($share); + Filesystem::init($share->getShareOwner(), '/'.$share->getShareOwner().'/files'); + $path = Filesystem::getPath((int)$share->getItemSource()); + if ($share->getPermissions() & ~Filesystem::getPermissions($path)) { + throw new InvalidPermissionsException('The permissions are not valid for the file'); + } + return true; + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/filesharefactory.php b/apps/files_sharing/lib/share/filesharefactory.php new file mode 100644 index 000000000000..07788e38ed81 --- /dev/null +++ b/apps/files_sharing/lib/share/filesharefactory.php @@ -0,0 +1,79 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\AdvancedShareFactory; + +/** + * Share factory for file shares, used by both file and folder backends + */ +class FileShareFactory extends AdvancedShareFactory { + + protected $table; + + public function __construct() { + $this->table = '`*PREFIX*filecache`'; + } + + /** + * Map a database row to a file share + * @param array $row A key => value array of share properties + * @return \OCA\Files\Share\FileShare + */ + public function mapToShare($row) { + return FileShare::fromRow($row); + } + + /** + * Get JOIN(s) to app table(s) + * @return string + */ + public function getJoins() { + return 'JOIN '.$this->table.' ON '. + '`*PREFIX*shares`.`item_source` = '.$this->table.'.`fileid`'; + } + + /** + * Get app table column(s) + * @return string + */ + public function getColumns() { + $columns = array( + 'storage', + 'path', + 'parent', + 'mimetype', + 'mimepart', + 'size', + 'mtime', + 'storage_mtime', + 'encrypted', + 'unencrypted_size', + 'etag', + ); + foreach ($columns as &$column) { + $column = $this->table.'.`'.$column.'`'; + } + return join(', ', $columns); + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/filesharefetcher.php b/apps/files_sharing/lib/share/filesharefetcher.php new file mode 100644 index 000000000000..0e2838ffc66c --- /dev/null +++ b/apps/files_sharing/lib/share/filesharefetcher.php @@ -0,0 +1,345 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\ShareManager; +use OC\Group\Manager; + +/** + * Class to retrieve file shares from the ShareManager + * + * These are helper methods used by the shared storage, cache, permissions, etc. + */ +class FileShareFetcher { + + protected $shareManager; + protected $groupManager; + protected $uid; + protected $fileItemType; + protected $folderItemType; + + /** + * The constructor + * @param \OC\Share\ShareManager $shareManager + * @param \OC\Group\Manager $groupManager + * @param string $uid (optional) + */ + public function __construct(ShareManager $shareManager, Manager $groupManager, $uid = null) { + $this->shareManager = $shareManager; + $this->groupManager = $groupManager; + $this->setUID($uid); + $this->fileItemType = 'file'; + $this->folderItemType = 'folder'; + } + + /** + * Get the UID of the user to retrieve shares for + * @return string | null + * + * If no UID is set, all file shares can be retrieved + * + */ + public function getUID() { + return $this->uid; + } + + /** + * Set the UID of the user to retrieve shares for + * @param string | null $uid + */ + public function setUID($uid) { + $this->uid = $uid; + } + + /** + * Get all files shares + * @return \OCA\Files\Share\FileShare[] + */ + public function getAll() { + $filter = array(); + $uid = $this->getUID(); + if (isset($uid)) { + $filter['shareWith'] = $uid; + $filter['isShareWithUser'] = true; + } + $fileShares = $this->shareManager->getShares($this->fileItemType, $filter); + $folderShares = $this->shareManager->getShares($this->folderItemType, $filter); + return array_merge($fileShares, $folderShares); + } + + /** + * Get all permissions of all shared files + * @return array With file ids as keys and permissions as values + */ + public function getAllPermissions() { + return $this->getPermissions($this->getAll()); + } + + /** + * Get the ETag of the Shared folder for the user + * @return string + */ + public function getETag() { + $uid = $this->getUID(); + if (isset($uid)) { + return \OCP\Config::getUserValue($uid, 'files_sharing', 'etag'); + } + } + + /** + * Set the ETag of the Shared folder for the user + * @param string $etag + */ + public function setETag($etag) { + $uid = $this->getUID(); + if (isset($uid)) { + \OCP\Config::setUserValue($uid, 'files_sharing', 'etag', $etag); + } + } + + /** + * Get all files shares specified by target path for the user + * @param string $path + * @return \OCA\Files\Share\FileShare[] + * + * The returned file shares may not be exact shares for the path, but parent folders + * Only works if a UID is set + * + */ + public function getByPath($path) { + $shares = array(); + $uid = $this->getUID(); + if (isset($uid)) { + $filter = array( + 'shareWith' => $this->uid, + 'isShareWithUser' => true, + ); + $path = rtrim($path, '/'); + $pos = strpos($path, '/', 1); + // Get shared folder name + if ($pos !== false) { + $filter['itemTarget'] = substr($path, 0, $pos); + $shares = $this->shareManager->getShares($this->folderItemType, $filter); + } else { + $ext = pathinfo($path, PATHINFO_EXTENSION); + if ($ext === 'part') { + $path = substr($path, 0, -5); + } + $filter['itemTarget'] = $path; + // Try to guess file type + if (empty($ext)) { + $shares = $this->shareManager->getShares($this->folderItemType, $filter); + $itemType = $this->fileItemType; + } else { + $shares = $this->shareManager->getShares($this->fileItemType, $filter); + $itemType = $this->folderItemType; + } + if (empty($shares)) { + // Try the other item type + $shares = $this->shareManager->getShares($itemType, $filter); + } + } + } + return $shares; + } + + /** + * Get all files shares specified by file id + * @param int $fileId + * @return \OCA\Files\Share\FileShare[] + * + * The returned file shares may not be exact shares for the file id, but parent folders + * + */ + public function getById($fileId) { + $filter = array( + 'itemSource' => $fileId, + ); + $uid = $this->getUID(); + if (isset($uid)) { + $filter['shareWith'] = $uid; + $filter['isShareWithUser'] = true; + } + $shares = $this->shareManager->getShares($this->fileItemType, $filter); + if (empty($shares)) { + // Try folder item type instead + $shares = $this->shareManager->getShares($this->folderItemType, $filter); + } + return array_merge($shares, $this->getParentFolders($fileId)); + } + + /** + * Resolve a target path for the user to a storage and internal path + * @param string $path + * @return array Consisting of \OC\Files\Storage\Storage and the internal path + * + * Only works if a UID is set + * + */ + public function resolvePath($path) { + $shares = $this->getByPath($path); + if (!empty($shares)) { + $share = reset($shares); + \OC\Files\Filesystem::initMountPoints($share->getItemOwner()); + $mounts = \OC\Files\Filesystem::getMountByNumericId($share->getStorage()); + if (!empty($mounts)) { + $internalPath = $share->getPath(); + $path = rtrim($path, '/'); + $pos = strpos($path, '/', 1); + // Get shared folder name + if ($pos !== false) { + $internalPath .= substr($path, $pos); + } + if (pathinfo($path, PATHINFO_EXTENSION) === 'part') { + $internalPath .= '.part'; + } + $fullPath = reset($mounts)->getMountPoint().$internalPath; + return \OC\Files\Filesystem::resolvePath($fullPath); + } + } + return array(null, null); + } + + /** + * Get permissions for a shared file specified by target path for the user + * @param string $path + * @return int + * + * Only works if a UID is set + * + */ + public function getPermissionsByPath($path) { + $shares = $this->getByPath($path); + $permissions = $this->getPermissions($shares); + if (!empty($permissions)) { + return reset($permissions); + } + return 0; + } + + /** + * Get permissions for a shared file specified by file id + * @param int $fileId + * @return int + */ + public function getPermissionsById($fileId) { + $shares = $this->getById($fileId); + $permissions = $this->getPermissions($shares); + if (!empty($permissions)) { + return reset($permissions); + } + return 0; + } + + /** + * Get all UIDs of files shares specified by target path for the user + * @param string $path + * @return array With UIDs as values + * + * Only works if a UID is set + * + */ + public function getUsersSharedWithByPath($path) { + $shares = $this->getByPath($path); + return $this->getUsersSharedWith($shares); + } + + /** + * Get all UIDs of files shares specified by file id + * @param int $fileId + * @return array With UIDs as values + */ + public function getUsersSharedWithById($fileId) { + $shares = $this->getById($fileId); + return $this->getUsersSharedWith($shares); + } + + /** + * Get all UIDs of an array of shares + * @param \OCA\Files\Share\FileShare[] $shares + * @return array With UIDs as values + */ + public function getUsersSharedWith(array $shares) { + $uids = array(); + foreach ($shares as $share) { + $shareTypeId = $share->getShareTypeId(); + if ($shareTypeId === 'user') { + $uids[] = $share->getShareWith(); + } else if ($shareTypeId === 'group') { + $group = $this->groupManager->get($share->getShareWith()); + if ($group) { + foreach ($group->getUsers() as $user) { + $uids[] = $user->getUID(); + } + } + } + } + return array_unique($uids); + } + + /** + * Get all permissions of an array of shares + * @param \OCA\Files\Share\FileShare[] $shares + * @return array With file ids as keys and permissions as values + */ + protected function getPermissions(array $shares) { + $files = array(); + if (!empty($shares)) { + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + if (!isset($files[$fileId])) { + $files[$fileId] = $share->getPermissions(); + } else { + // Combine permissions for duplicate shared files + $files[$fileId] |= $share->getPermissions(); + } + } + } + return $files; + } + + /** + * Get all shared parent folders + * @param int $fileId + * @return \OCA\Files\Share\FileShare[] + */ + protected function getParentFolders($fileId) { + $shares = array(); + $folderShareBackend = $this->shareManager->getShareBackend($this->folderItemType); + if ($folderShareBackend) { + $uid = $this->getUID(); + if (isset($uid)) { + $filter['shareWith'] = $uid; + $filter['isShareWithUser'] = true; + } + $fileId = $folderShareBackend->getParentFolderId($fileId); + while ($fileId !== -1) { + $filter['itemSource'] = $fileId; + $folderShares = $this->shareManager->getShares($this->folderItemType, $filter); + $shares = array_merge($shares, $folderShares); + $fileId = $folderShareBackend->getParentFolderId($fileId); + } + } + return $shares; + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/filetargetmachine.php b/apps/files_sharing/lib/share/filetargetmachine.php new file mode 100644 index 000000000000..f47b97170f5e --- /dev/null +++ b/apps/files_sharing/lib/share/filetargetmachine.php @@ -0,0 +1,46 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\Share; +use OC\Share\ItemTargetMachine; +use OC\User\User; +use OC\Files\Filesystem; + +class FileTargetMachine extends ItemTargetMachine { + + /** + * Get a unique item target for the specified share and user + * @param \OC\Share\Share $share + * @param \OC\User\User $user + * @return string + * + * If $user is null, any item target is acceptable + * + */ + public function getItemTarget(Share $share, User $user = null) { + Filesystem::init($share->getShareOwner(), '/'.$share->getShareOwner().'/files'); + $path = Filesystem::getPath((int)$share->getItemSource()); + return basename($path); + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php deleted file mode 100644 index 4426beec6368..000000000000 --- a/apps/files_sharing/lib/share/folder.php +++ /dev/null @@ -1,51 +0,0 @@ -. -*/ - -class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share_Backend_Collection { - - public function getChildren($itemSource) { - $children = array(); - $parents = array($itemSource); - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?'); - $result = $query->execute(array('httpd/unix-directory')); - if ($row = $result->fetchRow()) { - $mimetype = $row['id']; - } else { - $mimetype = -1; - } - while (!empty($parents)) { - $parents = "'".implode("','", $parents)."'"; - $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache`' - .' WHERE `parent` IN ('.$parents.')'); - $result = $query->execute(); - $parents = array(); - while ($file = $result->fetchRow()) { - $children[] = array('source' => $file['fileid'], 'file_path' => $file['name']); - // If a child folder is found look inside it - if ($file['mimetype'] == $mimetype) { - $parents[] = $file['fileid']; - } - } - } - return $children; - } - -} diff --git a/apps/files_sharing/lib/share/foldersharebackend.php b/apps/files_sharing/lib/share/foldersharebackend.php new file mode 100644 index 000000000000..a9e2ec73edce --- /dev/null +++ b/apps/files_sharing/lib/share/foldersharebackend.php @@ -0,0 +1,105 @@ +. + */ + +namespace OCA\Files\Share; + +use OC\Share\Share; +use OC\Share\ICollectionShareBackend; +use OC\Share\TimeMachine; + +class FolderShareBackend extends FileShareBackend implements ICollectionShareBackend { + + protected $table; + + /** + * The constructor + * @param \OC\Share\TimeMachine $timeMachine The time() mock + * @param \OC\Share\ShareType\IShareType[] $shareTypes An array of share type objects that + * items can be shared through e.g. User, Group, Link + */ + public function __construct(TimeMachine $timeMachine, array $shareTypes) { + parent::__construct($timeMachine, $shareTypes); + $this->table = '`*PREFIX*filecache`'; + } + + /** + * Get the identifier for the item type this backend handles, should be a singular noun + * @return string + */ + public function getItemType() { + return 'folder'; + } + + /** + * Get the plural form of getItemType, used for the RESTful API + * @return string + */ + public function getItemTypePlural() { + return 'folders'; + } + + /** + * Get the identifiers for the children item types of this backend + * @return array + */ + public function getChildrenItemTypes() { + // Include 'folder' in the return because folders can be inside folders + return array('file', 'folder'); + } + + /** + * Search for shares of this collection item type that contain the child item source and + * shared with the share owner + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + */ + public function searchForParentCollections(Share $share) { + $parents = array(); + $filter = array( + 'shareWith' => $share->getShareOwner(), + 'isShareWithUser' => true, + ); + // Loop through parent folders and check if they are shared + $fileId = $this->getParentFolderId($share->getItemSource()); + while ($fileId !== -1) { + $filter['itemSource'] = $fileId; + $parents = array_merge($parents, $this->getShares($filter)); + $fileId = $this->getParentFolderId($fileId); + } + return $parents; + } + + /** + * Get the file id of the parent folder for the specified file id + * @param int $fileId + * @return int + */ + public function getParentFolderId($fileId) { + $sql = 'SELECT `parent` FROM '.$this->table.' WHERE `fileid` = ?'; + $result = \OC_DB::executeAudited($sql, array($fileId)); + $id = $result->fetchOne(); + if ($id !== false) { + return (int)$id; + } + return -1; + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 7384b094cb04..5250f994912d 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -3,7 +3,7 @@ * ownCloud * * @author Michael Gapczynski - * @copyright 2011 Michael Gapczynski mtgap@owncloud.com + * @copyright 2011-2013 Michael Gapczynski mtgap@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -22,162 +22,153 @@ namespace OC\Files\Storage; +use OC\Files\Cache\SharedCache; +use OC\Files\Cache\SharedPermissions; +use OC\Files\Cache\SharedWatcher; +use OCA\Files\Share\FileShareFetcher; + /** * Convert target path to source path and pass the function call to the correct storage provider */ -class Shared extends \OC\Files\Storage\Common { - - private $sharedFolder; - private $files = array(); - - public function __construct($arguments) { - $this->sharedFolder = $arguments['sharedFolder']; - } +class Shared extends Common { - public function getId(){ - return 'shared::' . $this->sharedFolder; - } + private $fetcher; + private $cache; + private $permissionsCache; + private $watcher; /** - * @brief Get the source file path, permissions, and owner for a shared file - * @param string Shared target file path - * @return Returns array with the keys path, permissions, and owner or false if not found - */ - private function getFile($target) { - if (!isset($this->files[$target])) { - // Check for partial files - if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { - $source = \OC_Share_Backend_File::getSource(substr($target, 0, -5)); - if ($source) { - $source['path'] .= '.part'; - // All partial files have delete permission - $source['permissions'] |= \OCP\PERMISSION_DELETE; - } - } else { - $source = \OC_Share_Backend_File::getSource($target); - } - $this->files[$target] = $source; - } - return $this->files[$target]; + * The constructor + * @param array $params Array with a FileShareFetcher + */ + public function __construct($params) { + $this->fetcher = $params['fetcher']; } /** - * @brief Get the source file path for a shared file - * @param string Shared target file path - * @return string source file path or false if not found - */ - private function getSourcePath($target) { - $source = $this->getFile($target); - if ($source) { - if (!isset($source['fullPath'])) { - \OC\Files\Filesystem::initMountPoints($source['fileOwner']); - $mount = \OC\Files\Filesystem::getMountByNumericId($source['storage']); - if (is_array($mount)) { - $this->files[$target]['fullPath'] = $mount[key($mount)]->getMountPoint().$source['path']; - } else { - $this->files[$target]['fullPath'] = false; - } - } - return $this->files[$target]['fullPath']; + * Mount this shared storage, called by the hook setup in files_sharing/appinfo/app.php + * @param array $params + */ + public static function setup($params) { + if (isset($params['user']) && isset($params['user_dir'])) { + $shareManager = \OCP\Share::getShareManager(); + $fetcher = new FileShareFetcher($shareManager, \OC_Group::getManager(), + $params['user'] + ); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('fetcher' => $fetcher), + $params['user_dir'].'/Shared/' + ); } - return false; } - /** - * @brief Get the permissions granted for a shared file - * @param string Shared target file path - * @return int CRUDS permissions granted or false if not found - */ - public function getPermissions($target) { - $source = $this->getFile($target); - if ($source) { - return $source['permissions']; - } - return false; + public function getId() { + return 'shared'; } public function mkdir($path) { - if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { + if ($path === '' || !$this->isCreatable(dirname($path))) { return false; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->mkdir($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->mkdir($internalPath); + } } return false; } public function rmdir($path) { - if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->rmdir($internalPath); + if ($this->isDeletable($path)) { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->rmdir($internalPath); + } } return false; } public function opendir($path) { - if ($path == '' || $path == '/') { - $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); + if ($path === '') { + $files = array(); + $shares = $this->fetcher->getAll(); + foreach ($shares as $share) { + $fileId = $share->getItemSource(); + if (!isset($files[$fileId])) { + $files[$fileId] = basename($share->getItemTarget()); + } + } + $files = array_values($files); \OC\Files\Stream\Dir::register('shared', $files); return opendir('fakedir://shared'); - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->opendir($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->opendir($internalPath); + } } return false; } public function is_dir($path) { - if ($path == '' || $path == '/') { + if ($path === '') { return true; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->is_dir($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->is_dir($internalPath); + } } return false; } public function is_file($path) { - if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { return $storage->is_file($internalPath); } return false; } public function stat($path) { - if ($path == '' || $path == '/') { + if ($path === '') { $stat['size'] = $this->filesize($path); $stat['mtime'] = $this->filemtime($path); return $stat; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->stat($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->mkdir($internalPath); + } } return false; } public function filetype($path) { - if ($path == '' || $path == '/') { + if ($path === '') { return 'dir'; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->filetype($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->filetype($internalPath); + } } return false; } public function filesize($path) { - if ($path == '' || $path == '/' || $this->is_dir($path)) { + if ($path === '' || $this->is_dir($path)) { return 0; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->filesize($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->filesize($internalPath); + } } return false; } public function isCreatable($path) { - if ($path == '') { + if ($path === '') { return false; } return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE); @@ -188,39 +179,50 @@ public function isReadable($path) { } public function isUpdatable($path) { - if ($path == '') { + if ($path === '') { return false; } return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); } public function isDeletable($path) { - if ($path == '') { + if ($path === '') { return true; } return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE); } public function isSharable($path) { - if ($path == '') { + if ($path === '') { return false; } return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } + public function getPermissions($path) { + $permissions = $this->fetcher->getPermissionsByPath($path); + if (pathinfo($path, PATHINFO_EXTENSION) === 'part') { + // All partial files have delete permission + $permissions |= \OCP\PERMISSION_DELETE; + } + return $permissions; + } + public function file_exists($path) { - if ($path == '' || $path == '/') { + if ($path === '') { return true; - } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->file_exists($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->file_exists($internalPath); + } } return false; } public function filemtime($path) { - if ($path == '' || $path == '/') { - $mtime = 0; + $mtime = 0; + if ($path === '') { if ($dh = $this->opendir($path)) { while (($filename = readdir($dh)) !== false) { $tempmtime = $this->filemtime($filename); @@ -229,62 +231,66 @@ public function filemtime($path) { } } } - return $mtime; } else { - $source = $this->getSourcePath($path); - if ($source) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->filemtime($internalPath); + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + $mtime = $storage->filemtime($internalPath); } } + return $mtime; } public function file_get_contents($path) { - $source = $this->getSourcePath($path); - if ($source) { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { $info = array( - 'target' => $this->sharedFolder.$path, - 'source' => $source, + 'target' => '/Shared'.$path, + 'source' => $internalPath, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->file_get_contents($internalPath); } + return null; } public function file_put_contents($path, $data) { - if ($source = $this->getSourcePath($path)) { - // Check if permission is granted - if (($this->file_exists($path) && !$this->isUpdatable($path)) - || ($this->is_dir($path) && !$this->isCreatable($path))) { - return false; - } + $exists = $this->file_exists($path); + if ($exists && !$this->isUpdatable($path)) { + return false; + } + if (!$exists && !$this->isCreatable(dirname(path))) { + return false; + } + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { $info = array( - 'target' => $this->sharedFolder.$path, - 'source' => $source, - ); + 'target' => '/Shared'.$path, + 'source' => $internalPath, + ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - $result = $storage->file_put_contents($internalPath, $data); - return $result; + return $storage->file_put_contents($internalPath, $data); } return false; } public function unlink($path) { // Delete the file if DELETE permission is granted - if ($source = $this->getSourcePath($path)) { - if ($this->isDeletable($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + if ($this->isDeletable($path)) { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { return $storage->unlink($internalPath); - } else if (dirname($path) == '/' || dirname($path) == '.') { + } else if (dirname($path) === '/' || dirname($path) === '.') { // Unshare the file from the user if in the root of the Shared folder if ($this->is_dir($path)) { $itemType = 'folder'; } else { $itemType = 'file'; } - return \OCP\Share::unshareFromSelf($itemType, $path); + $shareManager =\OCP\Share::getShareManager(); + $shares = $this->fetcher->getByPath($path); + foreach ($shares as $share) { + $shareManager->unshare($share); + } } } return false; @@ -293,8 +299,8 @@ public function unlink($path) { public function rename($path1, $path2) { // Check for partial files if (pathinfo($path1, PATHINFO_EXTENSION) === 'part') { - if ($oldSource = $this->getSourcePath($path1)) { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); + list($storage, $oldInternalPath) = $this->fetcher->resolvePath($path1); + if ($storage && $oldInternalPath) { $newInternalPath = substr($oldInternalPath, 0, -5); return $storage->rename($oldInternalPath, $newInternalPath); } @@ -302,31 +308,31 @@ public function rename($path1, $path2) { // Renaming/moving is only allowed within shared folders $pos1 = strpos($path1, '/', 1); $pos2 = strpos($path2, '/', 1); - if ($pos1 !== false && $pos2 !== false && ($oldSource = $this->getSourcePath($path1))) { - $newSource = $this->getSourcePath(dirname($path2)).'/'.basename($path2); - if (dirname($path1) == dirname($path2)) { - // Rename the file if UPDATE permission is granted - if ($this->isUpdatable($path1)) { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); - return $storage->rename($oldInternalPath, $newInternalPath); - } - } else { - // Move the file if DELETE and CREATE permissions are granted - if ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { - // Get the root shared folder - $folder1 = substr($path1, 0, $pos1); - $folder2 = substr($path2, 0, $pos2); - // Copy and unlink the file if it exists in a different shared folder - if ($folder1 != $folder2) { - if ($this->copy($path1, $path2)) { - return $this->unlink($path1); - } - } else { - list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); - list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); + if ($pos1 !== false && $pos2 !== false) { + list($storage, $oldInternalPath) = $this->fetcher->resolvePath($path1); + list( , $newInternalPath) = $this->fetcher->resolvePath(dirname($path2)); + if ($storage && $oldInternalPath && $newInternalPath) { + $newInternalPath .= '/'.basename($path2); + if (dirname($path1) === dirname($path2)) { + // Rename the file if UPDATE permission is granted + if ($this->isUpdatable($path1)) { return $storage->rename($oldInternalPath, $newInternalPath); } + } else { + // Move the file if DELETE and CREATE permissions are granted + if ($this->isDeletable($path1) && $this->isCreatable(dirname($path2))) { + // Get the root shared folder + $folder1 = substr($path1, 0, $pos1); + $folder2 = substr($path2, 0, $pos2); + // Copy and unlink the file if it exists in a different shared folder + if ($folder1 != $folder2) { + if ($this->copy($path1, $path2)) { + return $this->unlink($path1); + } + } else { + return $storage->rename($oldInternalPath, $newInternalPath); + } + } } } } @@ -335,18 +341,15 @@ public function rename($path1, $path2) { } public function copy($path1, $path2) { - // Copy the file if CREATE permission is granted if ($this->isCreatable(dirname($path2))) { - $source = $this->fopen($path1, 'r'); - $target = $this->fopen($path2, 'w'); - list ($count, $result) = \OC_Helper::streamCopy($source, $target); - return $result; + parent::copy($path1, $path2); } return false; } public function fopen($path, $mode) { - if ($source = $this->getSourcePath($path)) { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { switch ($mode) { case 'r+': case 'rb+': @@ -362,112 +365,118 @@ public function fopen($path, $mode) { case 'xb': case 'a': case 'ab': - if (!$this->isUpdatable($path)) { + $exists = $this->file_exists($path); + if ($exists && !$this->isUpdatable($path)) { + return false; + } + if (!$exists && !$this->isCreatable(dirname(path))) { return false; } } $info = array( - 'target' => $this->sharedFolder.$path, - 'source' => $source, + 'target' => '/Shared'.$path, + 'source' => $internalPath, 'mode' => $mode, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->fopen($internalPath, $mode); } return false; } public function getMimeType($path) { - if ($path == '' || $path == '/') { + if ($path === '') { return 'httpd/unix-directory'; + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->getMimeType($internalPath); + } } - if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->getMimeType($internalPath); - } - return false; + return null; } public function free_space($path) { - if ($path == '') { - return \OC\Files\SPACE_UNKNOWN; - } - $source = $this->getSourcePath($path); - if ($source) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->free_space($internalPath); + if ($path !== '') { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->free_space($internalPath); + } } + return \OC\Files\SPACE_UNKNOWN; } public function getLocalFile($path) { - if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->getLocalFile($internalPath); + if ($path !== '') { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->getLocalFile($internalPath); + } } return false; } + public function touch($path, $mtime = null) { - if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->touch($internalPath, $mtime); + if ($path !== '') { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->touch($internalPath, $mtime); + } } return false; } - public static function setup($options) { - if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] - || \OCP\Share::getItemsSharedWith('file')) { - $user_dir = $options['user_dir']; - \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', - array('sharedFolder' => '/Shared'), - $user_dir.'/Shared/'); - } - } - public function hasUpdated($path, $time) { - if ($path == '') { - return false; + if ($path !== '') { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->hasUpdated($internalPath, $time); + } } - return $this->filemtime($path) > $time; + return false; } public function getCache($path = '') { - return new \OC\Files\Cache\Shared_Cache($this); - } - - public function getScanner($path = '') { - return new \OC\Files\Cache\Scanner($this); + if (!isset($this->cache)) { + $this->cache = new SharedCache($this, $this->fetcher); + } + return $this->cache; } public function getPermissionsCache($path = '') { - return new \OC\Files\Cache\Shared_Permissions($this); + if (!isset($this->permissionsCache)) { + $this->permissionsCache = new SharedPermissions($this, $this->fetcher); + } + return $this->permissionsCache; } public function getWatcher($path = '') { - return new \OC\Files\Cache\Shared_Watcher($this); + if (!isset($this->watcher)) { + $this->watcher = new SharedWatcher($this); + } + return $this->watcher; } public function getOwner($path) { - if ($path == '') { - return false; - } - $source = $this->getFile($path); - if ($source) { - return $source['fileOwner']; + if ($path !== '') { + $shares = $this->fetcher->getByPath($path); + if (!empty($shares)) { + return reset($shares)->getItemOwner(); + } } - return false; + return null; } public function getETag($path) { - if ($path == '') { + if ($path === '') { return parent::getETag($path); - } - if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - return $storage->getETag($internalPath); + } else { + list($storage, $internalPath) = $this->fetcher->resolvePath($path); + if ($storage && $internalPath) { + return $storage->getETag($internalPath); + } } return null; } -} +} \ No newline at end of file diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index a43ab2e2a0a5..329f3a2df645 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -21,86 +21,113 @@ namespace OC\Files\Cache; -class Shared_Updater { +use OC\Share\Share; +use OC\Share\ShareManager; +use OCA\Files\Share\FileShareFetcher; +use OC\Files\Filesystem; +use OC\Files\Cache\Updater; + +/** + * Listens to filesystem hooks and share hooks to update ETags and remove deleted shares + */ +class SharedUpdater { + + protected $shareManager; + protected $fetcher; + protected $fileItemType; + protected $folderItemType; /** - * Correct the parent folders' ETags for all users shared the file at $target - * - * @param string $target - */ - static public function correctFolders($target) { - $uid = \OCP\User::getUser(); - $uidOwner = \OC\Files\Filesystem::getOwner($target); - $info = \OC\Files\Filesystem::getFileInfo($target); - // Correct Shared folders of other users shared with - $users = \OCP\Share::getUsersItemShared('file', $info['fileid'], $uidOwner, true); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); - foreach ($users as $user) { - if ( $user !== $uidOwner ) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $info['fileid'], $user, true)); - } - } - $users = $reshareUsers; - } - // Correct folders of shared file owner - $target = substr($target, 8); - if ($uidOwner !== $uid && $source = \OC_Share_Backend_File::getSource($target)) { - \OC\Files\Filesystem::initMountPoints($uidOwner); - $source = '/'.$uidOwner.'/'.$source['path']; - \OC\Files\Cache\Updater::correctFolder($source, $info['mtime']); - } - } + * The constructor + * @param \OC\Share\ShareManager $shareManager + * @param \OCA\Files\Share\FileShareFetcher $fetcher + */ + public function __construct(ShareManager $shareManager, FileShareFetcher $fetcher) { + $this->shareManager = $shareManager; + $this->fetcher = $fetcher; + $this->fileItemType = 'file'; + $this->folderItemType = 'folder'; + \OCP\Util::connectHook('OC_Filesystem', 'post_write', $this, 'onWrite'); + \OCP\Util::connectHook('OC_Filesystem', 'post_rename', $this, 'onRename'); + \OCP\Util::connectHook('OC_Filesystem', 'post_delete', $this, 'onDelete'); + $this->shareManager->listen('\OC\Share', 'postShare', array($this, 'onShare')); } /** + * Correct the parent folders' ETags * @param array $params */ - static public function writeHook($params) { - self::correctFolders($params['path']); + public function onWrite($params) { + $this->correctFolders($params['path']); } /** + * Correct the parent folders' ETags * @param array $params */ - static public function renameHook($params) { - self::correctFolders($params['newpath']); - self::correctFolders(pathinfo($params['oldpath'], PATHINFO_DIRNAME)); + public function onRename($params) { + $this->correctFolders($params['newpath']); + $this->correctFolders(dirname($params['oldpath'])); } /** + * Correct the parent folders' ETags and unshare all shares of the file * @param array $params */ - static public function deleteHook($params) { - self::correctFolders($params['path']); + public function onDelete($params) { + $this->correctFolders($params['path']); + $data = Filesystem::getFileInfo($path); + if (isset($data['fileid']) && isset($data['mimetype'])) { + $fileId = $data['fileid']; + if ($data['mimetype'] === 'httpd/unix-directory') { + $itemType = $this->folderItemType; + } else { + $itemType = $this->fileItemType; + } + $this->shareManager->unshareItem($itemType, $fileId); + } } /** - * @param array $params + * Correct the parent folders' ETags for all users the specified share was shared with + * @param \OC\Share\Share $share */ - static public function shareHook($params) { - if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { - $uidOwner = \OCP\User::getUser(); - $users = \OCP\Share::getUsersItemShared($params['itemType'], $params['fileSource'], $uidOwner, true); - if (!empty($users)) { - while (!empty($users)) { - $reshareUsers = array(); - foreach ($users as $user) { - if ($user !== $uidOwner) { - $etag = \OC\Files\Filesystem::getETag(''); - \OCP\Config::setUserValue($user, 'files_sharing', 'etag', $etag); - // Look for reshares - $reshareUsers = array_merge($reshareUsers, \OCP\Share::getUsersItemShared('file', $params['fileSource'], $user, true)); - } - } - $users = $reshareUsers; + public function onShare(Share $share) { + $itemType = $share->getItemType(); + if ($itemType === $this->fileItemType || $itemType === $this->folderItemType) { + $eTag = Filesystem::getETag(''); + $uids = $this->fetcher->getUsersSharedWith(array($share)); + foreach ($uids as $uid) { + $this->fetcher->setUID($uid); + $this->fetcher->setETag($eTag); + } + } + } + + /** + * Correct the parent folders' ETags for all users with access to the specified path + * @param string $path + */ + protected function correctFolders($path) { + $data = Filesystem::getFileInfo($path); + if (isset($data['fileid'])) { + $fileId = $data['fileid']; + $itemOwner = Filesystem::getOwner($path); + $eTag = Filesystem::getETag(''); + $this->fetcher->setUID(null); + $uids = $this->fetcher->getUsersSharedWithById($fileId); + foreach ($uids as $uid) { + if ($uid !== $itemOwner) { + $this->fetcher->setUID($uid); + $this->fetcher->setETag($eTag); } } + if ($itemOwner !== \OCP\User::getUser()) { + // Correct folders of file owner + Filesystem::init($itemOwner, '/'.$itemOwner.'/files'); + Updater::correctFolder(Filesystem::getPath($fileId), $data['mtime']); + } } } -} +} \ No newline at end of file diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php index 6fdfc1db36d0..0c07e8a7392b 100644 --- a/apps/files_sharing/lib/watcher.php +++ b/apps/files_sharing/lib/watcher.php @@ -24,7 +24,7 @@ /** * check the storage backends for updates and change the cache accordingly */ -class Shared_Watcher extends Watcher { +class SharedWatcher extends Watcher { /** * check $path for updates @@ -32,7 +32,7 @@ class Shared_Watcher extends Watcher { * @param string $path */ public function checkUpdate($path) { - if ($path != '') { + if ($path !== '') { parent::checkUpdate($path); } } @@ -43,7 +43,7 @@ public function checkUpdate($path) { * @param string $path */ public function cleanFolder($path) { - if ($path != '') { + if ($path !== '') { parent::cleanFolder($path); } } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index e9fdf6e4c951..042b9c5653f4 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -1,245 +1,249 @@ . + */ + // Load other apps for file previews OC_App::loadApps(); - -if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); -} - -function fileCmp($a, $b) { - if ($a['type'] == 'dir' and $b['type'] != 'dir') { - return -1; - } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { - return 1; - } else { - return strnatcasecmp($a['name'], $b['name']); - } -} - -if (isset($_GET['t'])) { +if (isset($_GET['t']) && OCP\Config::getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes') { $token = $_GET['t']; - $linkItem = OCP\Share::getShareByToken($token); - if (is_array($linkItem) && isset($linkItem['uid_owner'])) { - // seems to be a valid share - $type = $linkItem['item_type']; - $fileSource = $linkItem['file_source']; - $shareOwner = $linkItem['uid_owner']; - $path = null; - $rootLinkItem = OCP\Share::resolveReShare($linkItem); - $fileOwner = $rootLinkItem['uid_owner']; - if (isset($fileOwner)) { - OC_Util::tearDownFS(); - OC_Util::setupFS($fileOwner); - $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); + $shareManager = OCP\Share::getShareManager(); + if ($shareManager) { + $filter = array( + 'shareTypeId' => 'link', + 'token' => $token, + ); + $share = $shareManager->getShares('file', $filter, 1); + if (empty($share)) { + // Try folder item type instead + $share = $shareManager->getShares('folder', $filter, 1); } - } -} -if (isset($path)) { - if (!isset($linkItem['item_type'])) { - OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - if (isset($linkItem['share_with'])) { - // Authenticate share_with - $url = OCP\Util::linkToPublic('files') . '&t=' . $token; - if (isset($_GET['file'])) { - $url .= '&file=' . urlencode($_GET['file']); - } else { - if (isset($_GET['dir'])) { - $url .= '&dir=' . urlencode($_GET['dir']); - } - } - if (isset($_POST['password'])) { - $password = $_POST['password']; - if ($linkItem['share_type'] == OCP\Share::SHARE_TYPE_LINK) { - // Check Password - $forcePortable = (CRYPT_BLOWFISH != 1); - $hasher = new PasswordHash(8, $forcePortable); - if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''), - $linkItem['share_with']))) { + if (!empty($share)) { + $share = reset($share); + $password = $share->getPassword(); + if (isset($password)) { + $url = OCP\Util::linkToPublic('files').'&t='.$token; + if (isset($_GET['file'])) { + $url .= '&file='.urlencode($_GET['file']); + } else { + if (isset($_GET['dir'])) { + $url .= '&dir='.urlencode($_GET['dir']); + } + } + if (isset($_POST['password'])) { + $forcePortable = (CRYPT_BLOWFISH != 1); + $hasher = new PasswordHash(8, $forcePortable); + $salt = OCP\Config::getSystemValue('passwordsalt', ''); + if (!$hasher->CheckPassword($_POST['password'].$salt, $password)) { + // Prompt for password again + $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); + $tmpl->assign('URL', $url); + $tmpl->assign('wrongpw', true); + $tmpl->printPage(); + exit(); + } else { + // Save share id in session for future requests + OC::$session->set('public_link_authenticated', $share->getId()); + } + // Check if share id is set in session + } else if (!OC::$session->exists('public_link_authenticated') + || OC::$session->get('public_link_authenticated') !== $share->getId() + ) { + // Prompt for password $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); $tmpl->assign('URL', $url); - $tmpl->assign('wrongpw', true); $tmpl->printPage(); exit(); - } else { - // Save item id in session for future requests - \OC::$session->set('public_link_authenticated', $linkItem['id']); } + } + OC_Util::tearDownFS(); + OC_Util::setupFS($share->getItemOwner()); + $path = \OC\Files\Filesystem::getPath($share->getItemSource()); + if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($path.$_GET['path'])) { + $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']); + $path .= $getPath; } else { - OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type'] - .' for share id '.$linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); + $getPath = ''; } - - } else { - // Check if item id is set in session - if ( ! \OC::$session->exists('public_link_authenticated') - || \OC::$session->get('public_link_authenticated') !== $linkItem['id'] - ) { - // Prompt for password - $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); - $tmpl->assign('URL', $url); - $tmpl->printPage(); + $dir = dirname($path); + $file = basename($path); + if (isset($_GET['download'])) { + if (isset($_GET['files'])) { + $files = urldecode($_GET['files']); + $filesList = json_decode($files); + // in case we get only a single file + if ($filesList === NULL ) { + $filesList = array($files); + } + OC_Files::get($path, $filesList, $_SERVER['REQUEST_METHOD'] === 'HEAD'); + } else { + OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] === 'HEAD'); + } exit(); - } - } - } - $basePath = $path; - if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($basePath . $_GET['path'])) { - $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']); - $path .= $getPath; - } else { - $getPath = ''; - } - $dir = dirname($path); - $file = basename($path); - // Download the file - if (isset($_GET['download'])) { - if (isset($_GET['files'])) { // download selected files - $files = urldecode($_GET['files']); - $files_list = json_decode($files); - // in case we get only a single file - if ($files_list === NULL ) { - $files_list = array($files); - } - OC_Files::get($path, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD'); - } else { - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD'); - } - exit(); - } else { - OCP\Util::addScript('files', 'file-upload'); - OCP\Util::addStyle('files_sharing', 'public'); - OCP\Util::addScript('files_sharing', 'public'); - OCP\Util::addScript('files', 'fileactions'); - OCP\Util::addScript('files', 'jquery.iframe-transport'); - OCP\Util::addScript('files', 'jquery.fileupload'); - $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); - $tmpl = new OCP\Template('files_sharing', 'public', 'base'); - $tmpl->assign('uidOwner', $shareOwner); - $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner)); - $tmpl->assign('filename', $file); - $tmpl->assign('directory_path', $linkItem['file_target']); - $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - $tmpl->assign('fileTarget', basename($linkItem['file_target'])); - $tmpl->assign('dirToken', $linkItem['token']); - $allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE); - if (\OCP\App::isEnabled('files_encryption')) { - $allowPublicUploadEnabled = false; - } - if (OC_Appconfig::getValue('core', 'shareapi_allow_public_upload', 'yes') === 'no') { - $allowPublicUploadEnabled = false; - } - if ($linkItem['item_type'] !== 'folder') { - $allowPublicUploadEnabled = false; - } - $tmpl->assign('allowPublicUploadEnabled', $allowPublicUploadEnabled); - $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); - $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); - - $urlLinkIdentifiers= (isset($token)?'&t='.$token:'') - .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') - .(isset($_GET['file'])?'&file='.$_GET['file']:''); - // Show file list - if (\OC\Files\Filesystem::is_dir($path)) { - $tmpl->assign('dir', $getPath); - - OCP\Util::addStyle('files', 'files'); - OCP\Util::addScript('files', 'files'); - OCP\Util::addScript('files', 'filelist'); - OCP\Util::addscript('files', 'keyboardshortcuts'); - $files = array(); - $rootLength = strlen($basePath) + 1; - $totalSize = 0; - foreach (\OC\Files\Filesystem::getDirectoryContent($path) as $i) { - $totalSize += $i['size']; - $i['date'] = OCP\Util::formatDate($i['mtime']); - if ($i['type'] == 'file') { - $fileinfo = pathinfo($i['name']); - $i['basename'] = $fileinfo['filename']; - if (!empty($fileinfo['extension'])) { - $i['extension'] = '.' . $fileinfo['extension']; + } else { + // Display the file or folder + OCP\Util::addScript('files', 'file-upload'); + OCP\Util::addStyle('files_sharing', 'public'); + OCP\Util::addScript('files_sharing', 'public'); + OCP\Util::addScript('files', 'fileactions'); + OCP\Util::addScript('files', 'jquery.iframe-transport'); + OCP\Util::addScript('files', 'jquery.fileupload'); + $maxUploadFilesize = OCP\Util::maxUploadFilesize($path); + $tmpl = new OCP\Template('files_sharing', 'public', 'base'); + $tmpl->assign('uidOwner', $share->getShareOwner()); + $tmpl->assign('displayName', $share->getShareOwnerDisplayName()); + $tmpl->assign('filename', $file); + $tmpl->assign('directory_path', $share->getItemTarget()); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); + $tmpl->assign('fileTarget', $share->getItemTarget()); + $tmpl->assign('dirToken', $token); + $allowPublicUploadEnabled = $share->isCreatable(); + if (\OCP\App::isEnabled('files_encryption') + || OCP\Config::getAppValue('core', 'shareapi_allow_public_upload', 'yes') + === 'no' || $share->getItemType() !== 'folder' + ) { + $allowPublicUploadEnabled = false; + } + $tmpl->assign('allowPublicUploadEnabled', $allowPublicUploadEnabled); + $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); + $tmpl->assign('uploadMaxHumanFilesize', + OCP\Util::humanFileSize($maxUploadFilesize) + ); + $urlLinkIdentifiers = (isset($token)?'&t='.$token:'') + .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') + .(isset($_GET['file'])?'&file='.$_GET['file']:''); + // Show file list + if (\OC\Files\Filesystem::is_dir($path)) { + $tmpl->assign('dir', $getPath); + OCP\Util::addStyle('files', 'files'); + OCP\Util::addScript('files', 'files'); + OCP\Util::addScript('files', 'filelist'); + OCP\Util::addscript('files', 'keyboardshortcuts'); + $files = array(); + $totalSize = 0; + foreach (\OC\Files\Filesystem::getDirectoryContent($path) as $i) { + $totalSize += $i['size']; + $i['date'] = OCP\Util::formatDate($i['mtime']); + if ($i['type'] == 'file') { + $fileinfo = pathinfo($i['name']); + $i['basename'] = $fileinfo['filename']; + if (!empty($fileinfo['extension'])) { + $i['extension'] = '.' . $fileinfo['extension']; + } else { + $i['extension'] = ''; + } + } + $i['directory'] = $getPath; + $i['permissions'] = $share->getPermissions(); + $files[] = $i; + } + usort($files, 'fileCmp'); + // Make breadcrumb + $breadcrumb = array(); + $pathtohere = ''; + foreach (explode('/', $getPath) as $i) { + if ($i != '') { + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + } + } + $list = new OCP\Template('files', 'part.list', ''); + $list->assign('files', $files); + $list->assign('disableSharing', true); + $list->assign('baseURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=' + ); + $list->assign('downloadURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=' + ); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb); + $breadcrumbNav->assign('baseURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=' + ); + $maxUploadFilesize = OCP\Util::maxUploadFilesize($path); + $folder = new OCP\Template('files', 'index', ''); + $folder->assign('fileList', $list->fetchPage()); + $folder->assign('breadcrumb', $breadcrumbNav->fetchPage()); + $folder->assign('dir', $getPath); + $folder->assign('isCreatable', false); + $folder->assign('permissions', OCP\PERMISSION_READ); + $folder->assign('isPublic',true); + $folder->assign('publicUploadEnabled', 'no'); + $folder->assign('files', $files); + $folder->assign('uploadMaxFilesize', $maxUploadFilesize); + $folder->assign('uploadMaxHumanFilesize', + OCP\Util::humanFileSize($maxUploadFilesize) + ); + $folder->assign('allowZipDownload', + intval(OCP\Config::getSystemValue('allowZipDownload', true)) + ); + $folder->assign('usedSpacePercent', 0); + $folder->assign('encryptedFiles', \OCP\Util::encryptedFiles()); + $tmpl->assign('folder', $folder->fetchPage()); + if (OCP\Config::getSystemValue('allowZipDownload', true) + && $totalSize <= OCP\Config::getSystemValue('maxZipInputSize', + OCP\Util::computerFileSize('800 MB')) + ) { + $allowZip = true; } else { - $i['extension'] = ''; + $allowZip = false; + } + $tmpl->assign('allowZipDownload', intval($allowZip)); + $tmpl->assign('downloadURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path='. + urlencode($getPath) + ); + } else { + $tmpl->assign('dir', $dir); + // Show file preview if viewer is available + if ($share->getItemType() === 'file') { + $tmpl->assign('downloadURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download' + ); + } else { + $tmpl->assign('downloadURL', + OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path='. + urlencode($getPath) + ); } } - $i['directory'] = $getPath; - $i['permissions'] = OCP\PERMISSION_READ; - $files[] = $i; - } - usort($files, "fileCmp"); - - // Make breadcrumb - $breadcrumb = array(); - $pathtohere = ''; - foreach (explode('/', $getPath) as $i) { - if ($i != '') { - $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); - } - } - $list = new OCP\Template('files', 'part.list', ''); - $list->assign('files', $files); - $list->assign('disableSharing', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); - $list->assign('downloadURL', - OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path='); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); - $breadcrumbNav->assign('breadcrumb', $breadcrumb); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); - $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); - $folder = new OCP\Template('files', 'index', ''); - $folder->assign('fileList', $list->fetchPage()); - $folder->assign('breadcrumb', $breadcrumbNav->fetchPage()); - $folder->assign('dir', $getPath); - $folder->assign('isCreatable', false); - $folder->assign('permissions', OCP\PERMISSION_READ); - $folder->assign('isPublic',true); - $folder->assign('publicUploadEnabled', 'no'); - $folder->assign('files', $files); - $folder->assign('uploadMaxFilesize', $maxUploadFilesize); - $folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); - $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $folder->assign('usedSpacePercent', 0); - $tmpl->assign('folder', $folder->fetchPage()); - $allowZip = OCP\Config::getSystemValue('allowZipDownload', true) - && $totalSize <= OCP\Config::getSystemValue('maxZipInputSize', OCP\Util::computerFileSize('800 MB')); - $tmpl->assign('allowZipDownload', intval($allowZip)); - $tmpl->assign('downloadURL', - OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); - } else { - $tmpl->assign('dir', $dir); - - // Show file preview if viewer is available - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); - } else { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') - .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); + $tmpl->printPage(); } + exit(); } - $tmpl->printPage(); } - exit(); -} else { - OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } - $errorTemplate = new OCP\Template('files_sharing', 'part.404', ''); $errorContent = $errorTemplate->fetchPage(); - header('HTTP/1.0 404 Not Found'); OCP\Util::addStyle('files_sharing', '404'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->assign('content', $errorContent); $tmpl->printPage(); + +function fileCmp($a, $b) { + if ($a['type'] == 'dir' and $b['type'] != 'dir') { + return -1; + } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { + return 1; + } else { + return strnatcasecmp($a['name'], $b['name']); + } +} \ No newline at end of file diff --git a/apps/files_sharing/tests/lib/cache.php b/apps/files_sharing/tests/lib/cache.php new file mode 100644 index 000000000000..8684434de796 --- /dev/null +++ b/apps/files_sharing/tests/lib/cache.php @@ -0,0 +1,517 @@ +. + */ + +namespace Test\Files\Cache; + +use OCA\Files\Share\FileShare; + +class TestSharedCache extends \OC\Files\Cache\SharedCache { + + public function getMimetype($id) { + $mimetypes = array( + 1 => 'httpd', + 2 => 'httpd/unix-directory', + 3 => 'text', + 4 => 'text/plain', + ); + if (isset($mimetypes[$id])) { + return $mimetypes[$id]; + } + return null; + } + +} + +class SharedCache extends \PHPUnit_Framework_TestCase { + + protected $storage; + protected $fetcher; + protected $cache; + protected $sourceStorage; + protected $sourceCache; + + protected function setUp() { + $this->storage = $this->getMockBuilder('\OC\Files\Storage\Shared') + ->disableOriginalConstructor() + ->getMock(); + $this->fetcher = $this->getMockBuilder('\OCA\Files\Share\FileShareFetcher') + ->disableOriginalConstructor() + ->getMock(); + $this->cache = new TestSharedCache($this->storage, $this->fetcher); + $this->sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Local') + ->disableOriginalConstructor() + ->getMock(); + $this->sourceCache = $this->getMockBuilder('\OC\Files\Cache\Cache') + ->disableOriginalConstructor() + ->getMock(); + $this->sourceStorage->expects($this->any()) + ->method('getCache') + ->will($this->returnValue($this->sourceCache)); + } + + protected function getTestShareFile() { + $share = new FileShare(); + $share->setItemSource(23); + $share->setStorage(1); + $share->setItemTarget('foo.txt'); + $share->setPath('files/foo.txt'); + $share->setParent(2); + $share->setMimetype(4); + $share->setMimepart(3); + $share->setSize(342); + $share->setMtime(1377389543); + $share->setStorageMtime(1377389543); + $share->setEncrypted(false); + $share->setUnencryptedSize(342); + $share->setEtag('5203de5db60f6'); + return $share; + } + + protected function getTestShareFolder() { + $share = new FileShare(); + $share->setItemSource(13); + $share->setStorage(1); + $share->setItemTarget('bar'); + $share->setPath('files/foobar'); + $share->setParent(2); + $share->setMimetype(2); + $share->setMimepart(1); + $share->setSize(123); + $share->setMtime(1377389567); + $share->setStorageMtime(1377389567); + $share->setEncrypted(false); + $share->setUnencryptedSize(123); + $share->setEtag('521618e9b8a59'); + return $share; + } + + public function testGetNumericStorageId() { + $this->assertNull($this->cache->getNumericStorageId()); + } + public function testGetWithPath() { + $share = $this->getTestShareFile(); + $data = $share->getMetadata(); + $data['mimetype'] = 'text/plain'; + $data['mimepart'] = 'text'; + $data['storage_mtime'] = 1377389543; + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($share))); + $this->assertEquals($data, $this->cache->get('foo.txt')); + } + + public function testGetWithPathAndNotExactShare() { + $share = $this->getTestShareFolder(); + $data = array( + 'fileid' => 26, + 'storage' => 1, + 'path' => 'files/foobar/bar.txt', + 'parent' => 13, + 'name' => 'bar.txt', + 'mimetype' => 'text/plain', + 'mimepart' => 'text', + 'size' => 42, + 'mtime' => 1377389567, + 'storage_mtime' => 1377389567, + 'encrypted' => false, + 'unencrypted_size' => 42, + 'etag' => '52150c666ec70', + ); + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('bar/bar.txt')) + ->will($this->returnValue(array($share))); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar/bar.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar/bar.txt'))); + $this->sourceCache->expects($this->once()) + ->method('get') + ->with($this->equalTo('files/foobar/bar.txt')) + ->will($this->returnValue($data)); + $data['path'] = 'bar/bar.txt'; + $this->assertEquals($data, $this->cache->get('bar/bar.txt')); + } + + public function testGetWithId() { + $share = $this->getTestShareFile(); + $share->setStorageMtime(0); + $data = $share->getMetadata(); + $data['mimetype'] = 'text/plain'; + $data['mimepart'] = 'text'; + $data['storage_mtime'] = $share->getMtime(); + $this->fetcher->expects($this->once()) + ->method('getById') + ->with($this->equalTo(23)) + ->will($this->returnValue(array($share))); + $this->assertEquals($data, $this->cache->get(23)); + } + + public function testGetWithIdAndNotExactShare() { + $share = $this->getTestShareFolder(); + $data = array( + 'fileid' => 26, + 'storage' => 1, + 'path' => 'files/foobar/bar.txt', + 'parent' => 13, + 'name' => 'bar.txt', + 'mimetype' => 'text/plain', + 'mimepart' => 'text', + 'size' => 42, + 'mtime' => 1377389567, + 'storage_mtime' => 1377389567, + 'encrypted' => false, + 'unencrypted_size' => 42, + 'etag' => '52150c666ec70', + ); + $this->fetcher->expects($this->once()) + ->method('getById') + ->with($this->equalTo(26)) + ->will($this->returnValue(array($share))); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar'))); + $this->sourceCache->expects($this->once()) + ->method('get') + ->with($this->equalTo(26)) + ->will($this->returnValue($data)); + $data['path'] = 'bar/bar.txt'; + $this->assertEquals($data, $this->cache->get(26)); + } + + public function testGetWithRoot() { + $share1 = $this->getTestShareFile(); + $share1->setEncrypted(true); + $share2 = $this->getTestShareFolder(); + $data = array( + 'fileid' => -1, + 'storage' => null, + 'path' => '', + 'parent' => -1, + 'name' => 'Shared', + 'mimetype' => 'httpd/unix-directory', + 'mimepart' => 'httpd', + 'size' => 465, + 'mtime' => 1377389567, + 'storage_mtime' => 1377389567, + 'encrypted' => true, + 'unencrypted_size' => 465, + 'etag' => '52150a6bea09e', + ); + $this->fetcher->expects($this->exactly(2)) + ->method('getAll') + ->will($this->returnValue(array($share1, $share2))); + $this->fetcher->expects($this->exactly(2)) + ->method('getETag') + ->will($this->returnValue('52150a6bea09e')); + $this->assertEquals($data, $this->cache->get('')); + $this->assertEquals($data, $this->cache->get(-1)); + } + + public function testGetWithRootAndNoETag() { + $share1 = $this->getTestShareFile(); + $share2 = $this->getTestShareFolder(); + $data = array( + 'fileid' => -1, + 'storage' => null, + 'path' => '', + 'parent' => -1, + 'name' => 'Shared', + 'mimetype' => 'httpd/unix-directory', + 'mimepart' => 'httpd', + 'size' => 465, + 'mtime' => 1377389567, + 'storage_mtime' => 1377389567, + 'encrypted' => false, + 'unencrypted_size' => 465, + 'etag' => '52150a6bea09e', + ); + $this->fetcher->expects($this->once()) + ->method('getAll') + ->will($this->returnValue(array($share1, $share2))); + $this->fetcher->expects($this->once()) + ->method('getETag') + ->will($this->returnValue(null)); + $this->storage->expects($this->once()) + ->method('getETag') + ->with($this->equalTo('')) + ->will($this->returnValue('52150a6bea09e')); + $this->fetcher->expects($this->once()) + ->method('setETag') + ->with($this->equalTo('52150a6bea09e')); + $this->assertEquals($data, $this->cache->get('')); + } + + public function testGetWithNotFound() { + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array())); + $this->assertFalse($this->cache->get('bar.txt')); + } + + public function testGetFolderContents() { + $files = array( + array( + 'fileid' => 26, + 'storage' => 1, + 'path' => 'files/foobar/bar.txt', + 'parent' => 13, + 'name' => 'bar.txt', + 'mimetype' => 'text/plain', + 'mimepart' => 'text', + 'size' => 42, + 'mtime' => 1377389567, + 'storage_mtime' => 1377389567, + 'encrypted' => false, + 'unencrypted_size' => 42, + 'etag' => '52150c666ec70', + ), + ); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar'))); + $this->sourceCache->expects($this->once()) + ->method('getFolderContents') + ->with($this->equalTo('files/foobar')) + ->will($this->returnValue($files)); + $this->assertEquals($files, $this->cache->getFolderContents('bar')); + } + + public function testGetFolderContentsWithRoot() { + $share1 = $this->getTestShareFile(); + $share1->setStorageMtime(0); + $share2 = $this->getTestShareFolder(); + $data1 = $share1->getMetadata(); + $data1['mimetype'] = 'text/plain'; + $data1['mimepart'] = 'text'; + $data1['storage_mtime'] = $share1->getMtime(); + $data2 = $share2->getMetadata(); + $data2['mimetype'] = 'httpd/unix-directory'; + $data2['mimepart'] = 'httpd'; + $files = array($data1, $data2); + $this->fetcher->expects($this->once()) + ->method('getAll') + ->will($this->returnValue(array($share1, $share2))); + $this->assertEquals($files, $this->cache->getFolderContents('')); + } + + public function testPut() { + $data = array( + 'mtime' => 1377389567, + 'size' => 2802, + ); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foo.txt'))); + $this->sourceCache->expects($this->once()) + ->method('put') + ->with($this->equalTo('files/foo.txt'), $data) + ->will($this->returnValue(23)); + $this->assertEquals(23, $this->cache->put('foo.txt', $data)); + } + + public function testPutWithNotFound() { + $data = array( + 'mtime' => 1377389321, + 'size' => 22, + ); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array(null, null))); + $this->sourceCache->expects($this->never()) + ->method('put'); + $this->assertEquals(-1, $this->cache->put('bar.txt', $data)); + } + + public function testPutWithRoot() { + $this->fetcher->expects($this->once()) + ->method('setETag') + ->with($this->equalTo('52150a6bea09e')); + $this->assertEquals(-1, $this->cache->put('', array('etag' => '52150a6bea09e'))); + } + + public function testGetId() { + $share = $this->getTestShareFile(); + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($share))); + $this->assertEquals($share->getItemSource(), $this->cache->getId('foo.txt')); + } + + public function testGetIdWithNotExactShare() { + $share = $this->getTestShareFolder(); + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('bar/bar.txt')) + ->will($this->returnValue(array($share))); + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar/bar.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar/bar.txt'))); + $this->sourceCache->expects($this->once()) + ->method('getId') + ->with($this->equalTo('files/foobar/bar.txt')) + ->will($this->returnValue(26)); + $this->assertEquals(26, $this->cache->getId('bar/bar.txt')); + } + + public function testGetIdWithNotFound() { + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array())); + $this->assertEquals(-1, $this->cache->getId('bar.txt')); + } + + public function testGetIdWithRoot() { + $this->assertEquals(-1, $this->cache->getId('')); + } + + public function testInCache() { + $share = $this->getTestShareFile(); + $this->fetcher->expects($this->once()) + ->method('getByPath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($share))); + $this->assertTrue($this->cache->inCache('foo.txt')); + } + + public function testInCacheWithRoot() { + $this->assertTrue($this->cache->inCache('')); + } + + public function testRemove() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foo.txt'))); + $this->sourceCache->expects($this->once()) + ->method('remove') + ->with($this->equalTo('files/foo.txt')); + $this->cache->remove('foo.txt'); + } + + public function testRemoveNotFound() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array(null, null))); + $this->sourceCache->expects($this->never()) + ->method('remove'); + $this->cache->remove('bar.txt'); + } + + public function testMove() { + $this->fetcher->expects($this->at(0)) + ->method('resolvePath') + ->with($this->equalTo('bar/bar.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar/bar.txt'))); + $this->fetcher->expects($this->at(1)) + ->method('resolvePath') + ->with($this->equalTo('bar')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar'))); + $this->sourceCache->expects($this->once()) + ->method('move') + ->with($this->equalTo('files/foobar/bar.txt'), + $this->equalTo('files/foobar/newbar.txt') + ); + $this->cache->move('bar/bar.txt', 'bar/newbar.txt'); + } + + public function testMoveWithNotFound() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array(null, null))); + $this->sourceCache->expects($this->never()) + ->method('move'); + $this->cache->move('bar.txt', 'newbar.txt'); + } + + public function testGetStatus() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('foo.txt')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foo.txt'))); + $this->sourceCache->expects($this->once()) + ->method('getStatus') + ->with($this->equalTo('files/foo.txt')) + ->will($this->returnValue(\OC\Files\Cache\Cache::SHALLOW)); + $this->assertEquals(\OC\Files\Cache\Cache::SHALLOW, $this->cache->getStatus('foo.txt')); + } + + public function testGetStatusWithNotFound() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar.txt')) + ->will($this->returnValue(array(null, null))); + $this->sourceCache->expects($this->never()) + ->method('getStatus'); + $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->getStatus('bar.txt')); + } + + public function testGetStatusWithRoot() { + $this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('')); + } + + public function testCalculateFolderSize() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('bar')) + ->will($this->returnValue(array($this->sourceStorage, 'files/foobar'))); + $this->sourceCache->expects($this->once()) + ->method('calculateFolderSize') + ->with($this->equalTo('files/foobar')) + ->will($this->returnValue(79)); + $this->assertEquals(79, $this->cache->calculateFolderSize('bar')); + } + + public function testCalculateFolderSizeNotFound() { + $this->fetcher->expects($this->once()) + ->method('resolvePath') + ->with($this->equalTo('foo')) + ->will($this->returnValue(array(null, null))); + $this->sourceCache->expects($this->never()) + ->method('calculateFolderSize'); + $this->assertEquals(0, $this->cache->calculateFolderSize('foo')); + } + + public function testCalculateFolderSizeWithRoot() { + $share1 = $this->getTestShareFile(); + $share2 = $this->getTestShareFolder(); + $this->fetcher->expects($this->once()) + ->method('getAll') + ->will($this->returnValue(array($share1, $share2))); + $this->assertEquals(465, $this->cache->calculateFolderSize('')); + } + + public function testGetIncomplete() { + $this->assertFalse($this->cache->getIncomplete()); + } + +} \ No newline at end of file diff --git a/apps/files_sharing/tests/lib/share/filesharefetcher.php b/apps/files_sharing/tests/lib/share/filesharefetcher.php new file mode 100644 index 000000000000..a75e55bc236e --- /dev/null +++ b/apps/files_sharing/tests/lib/share/filesharefetcher.php @@ -0,0 +1,394 @@ +. + */ + +namespace Test\Share; + +use OCA\Files\Share\FileShare; + +class FileShareFetcher extends \PHPUnit_Framework_TestCase { + + protected $matt; + protected $shareManager; + protected $groupManager; + protected $folderShareBackend; + protected $fetcher; + + protected function setUp() { + $this->matt = 'MTRichards'; + $this->shareManager = $this->getMockBuilder('\OC\Share\ShareManager') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->folderShareBackend = $this->getMockBuilder('\OCA\Files\Share\FolderShareBackend') + ->disableOriginalConstructor() + ->getMock(); + $this->shareManager->expects($this->any()) + ->method('getShareBackend') + ->with($this->equalTo('folder')) + ->will($this->returnValue($this->folderShareBackend)); + $this->fetcher = new \OCA\Files\Share\FileShareFetcher($this->shareManager, + $this->groupManager, $this->matt + ); + } + + public function testGetAll() { + $share1 = new FileShare(); + $share1->setItemType('file'); + $share2 = new FileShare(); + $share2->setItemType('folder'); + $map = array( + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true), null, null, + array($share1) + ), + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true), null, + null, array($share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $shares = $this->fetcher->getAll(); + $this->assertCount(2, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + } + + public function testGetAllPermissions() { + $share1 = new FileShare(); + $share1->setItemType('file'); + $share1->setItemSource(79); + $share1->setPermissions(1); + $share2 = new FileShare(); + $share2->setItemType('folder'); + $share2->setItemSource(80); + $share2->setPermissions(27); + $share3 = new FileShare(); + $share3->setItemType('folder'); + $share3->setItemSource(80); + $share3->setPermissions(5); + $map = array( + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true), null, null, + array($share1) + ), + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true), null, + null, array($share2, $share3) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $permissions = $this->fetcher->getAllPermissions(); + $this->assertCount(2, $permissions); + $this->assertArrayHasKey(79, $permissions); + $this->assertEquals(1, $permissions[79]); + $this->assertArrayHasKey(80, $permissions); + $this->assertEquals(31, $permissions[80]); + } + + public function testGetByPathWithFile() { + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemTarget('secrets.txt'); + $map = array( + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'secrets.txt'), null, null, array($share) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $shares = $this->fetcher->getByPath('secrets.txt'); + $this->assertEquals(array($share), $shares); + } + + public function testGetByPathWithFileWithoutExt() { + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemTarget('secrets'); + $map = array( + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'secrets'), null, null, array() + ), + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'secrets'), null, null, array($share) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $shares = $this->fetcher->getByPath('secrets'); + $this->assertEquals(array($share), $shares); + } + + public function testGetByPathWithFileAndPartExt() { + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemTarget('secrets.txt'); + $map = array( + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'secrets.txt'), null, null, array($share) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $shares = $this->fetcher->getByPath('secrets.txt.part'); + $this->assertEquals(array($share), $shares); + } + + public function testGetByPathWithFolder() { + $share = new FileShare(); + $share->setItemType('folder'); + $share->setItemTarget('reports'); + $map = array( + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'reports'), null, null, array($share) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $shares = $this->fetcher->getByPath('reports/subfolder'); + $this->assertEquals(array($share), $shares); + } + + public function testGetByIdWithFile() { + $share = new FileShare(); + $share->setItemType('file'); + $share->setItemSource(79); + $map = array( + array('file', array('itemSource' => 79, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array($share) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->folderShareBackend->expects($this->once()) + ->method('getParentFolderId') + ->with($this->equalTo(79)) + ->will($this->returnValue(-1)); + $shares = $this->fetcher->getById(79); + $this->assertEquals(array($share), $shares); + } + + public function testGetByIdWithFolder() { + $share = new FileShare(); + $share->setItemType('folder'); + $share->setItemSource(80); + $map = array( + array('file', array('itemSource' => 80, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array() + ), + array('folder', array('itemSource' => 80, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array($share) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->folderShareBackend->expects($this->once()) + ->method('getParentFolderId') + ->with($this->equalTo(80)) + ->will($this->returnValue(-1)); + $shares = $this->fetcher->getById(80); + $this->assertEquals(array($share), $shares); + } + + public function testGetByIdWithParentFolders() { + $share1 = new FileShare(); + $share1->setItemType('file'); + $share1->setItemSource(72); + $share2 = new FileShare(); + $share2->setItemType('folder'); + $share2->setItemSource(21); + $share3 = new FileShare(); + $share3->setItemType('folder'); + $share3->setItemSource(4); + $map = array( + array('file', array('itemSource' => 72, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array($share1) + ), + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemSource' => 21), null, null, array($share2) + ), + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemSource' => 4), null, null, array($share3) + ), + ); + $this->shareManager->expects($this->exactly(3)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->folderShareBackend->expects($this->at(0)) + ->method('getParentFolderId') + ->with($this->equalTo(72)) + ->will($this->returnValue(21)); + $this->folderShareBackend->expects($this->at(1)) + ->method('getParentFolderId') + ->with($this->equalTo(21)) + ->will($this->returnValue(4)); + $this->folderShareBackend->expects($this->at(2)) + ->method('getParentFolderId') + ->with($this->equalTo(4)) + ->will($this->returnValue(-1)); + $shares = $this->fetcher->getById(72); + $this->assertCount(3, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + $this->assertContains($share3, $shares); + } + + public function testGetPermissionsByPath() { + $share1 = new FileShare(); + $share1->setItemType('folder'); + $share1->setItemTarget('reports'); + $share1->setPermissions(27); + $share2 = new FileShare(); + $share2->setItemType('folder'); + $share2->setItemTarget('reports'); + $share2->setPermissions(5); + $map = array( + array('folder', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'reports'), null, null, array($share1, $share2) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertEquals(31, $this->fetcher->getPermissionsByPath('reports/subfolder')); + } + + public function testGetPermissionsByPathWithNoShares() { + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValue(array())); + $this->assertEquals(0, $this->fetcher->getPermissionsByPath('foo')); + } + + public function testGetPermissionsById() { + $share1 = new FileShare(); + $share1->setItemType('folder'); + $share1->setItemSource(80); + $share1->setPermissions(27); + $share2 = new FileShare(); + $share2->setItemType('folder'); + $share2->setItemSource(80); + $share2->setPermissions(5); + $map = array( + array('file', array('itemSource' => 80, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array() + ), + array('folder', array('itemSource' => 80, 'shareWith' => $this->matt, + 'isShareWithUser' => true), null, null, array($share1, $share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->folderShareBackend->expects($this->once()) + ->method('getParentFolderId') + ->with($this->equalTo(80)) + ->will($this->returnValue(-1)); + $this->assertEquals(31, $this->fetcher->getPermissionsById(80)); + } + + public function testGetPermissionsByIdWithNoShares() { + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValue(array())); + $this->folderShareBackend->expects($this->once()) + ->method('getParentFolderId') + ->will($this->returnValue(-1)); + $this->assertEquals(0, $this->fetcher->getPermissionsById(1)); + } + + public function testGetUsersSharedWithByPath() { + $share = new FileShare(); + $share->setShareTypeId('user'); + $share->setShareWith($this->matt); + $share->setItemType('file'); + $share->setItemTarget('secrets.txt'); + $map = array( + array('file', array('shareWith' => $this->matt, 'isShareWithUser' => true, + 'itemTarget' => 'secrets.txt'), null, null, array($share) + ), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $uids = $this->fetcher->getUsersSharedWithByPath('secrets.txt'); + $this->assertCount(1, $uids); + $this->assertContains($this->matt, $uids); + } + + public function testGetUsersSharedWithById() { + $share1 = new FileShare(); + $share1->setShareTypeId('user'); + $share1->setShareWith($this->matt); + $share1->setItemType('file'); + $share1->setItemSource(79); + $share2 = new FileShare(); + $share2->setShareTypeId('group'); + $share2->setShareWith('group'); + $share2->setItemType('file'); + $share2->setItemSource(79); + $map = array( + array('file', array('itemSource' => 79), null, null, array($share1, $share2)), + ); + $this->shareManager->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->folderShareBackend->expects($this->once()) + ->method('getParentFolderId') + ->with($this->equalTo(79)) + ->will($this->returnValue(-1)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->once()) + ->method('getUID') + ->will($this->returnValue($this->matt)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('MTGap')); + $group->expects($this->once()) + ->method('getUsers') + ->will($this->returnValue(array($user1, $user2))); + $this->groupManager->expects($this->once()) + ->method('get') + ->with($this->equalTo('group')) + ->will($this->returnValue($group)); + $this->fetcher->setUID(null); + $uids = $this->fetcher->getUsersSharedWithById(79); + $this->assertCount(2, $uids); + $this->assertContains($this->matt, $uids); + $this->assertContains('MTGap', $uids); + } + +} \ No newline at end of file diff --git a/core/ajax/share.php b/core/ajax/share.php index bdcb61284ecd..468b16e2733c 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -23,26 +23,34 @@ OCP\JSON::callCheck(); OC_App::loadApps(); +$shareManager = OCP\Share::getShareManager(); if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSource'])) { switch ($_POST['action']) { case 'share': if (isset($_POST['shareType']) && isset($_POST['shareWith']) && isset($_POST['permissions'])) { try { - $shareType = (int)$_POST['shareType']; + $share = new \OC\Share\Share(); + $shareType = $_POST['shareType']; + settype($shareType, 'int'); + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { + $share->setShareTypeId('user'); + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $share->setShareTypeId('group'); + } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { + $share->setShareTypeId('link'); + } $shareWith = $_POST['shareWith']; - if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith == '') { + if ($shareType === \OCP\Share::SHARE_TYPE_LINK && $shareWith === '') { $shareWith = null; } - - $token = OCP\Share::shareItem( - $_POST['itemType'], - $_POST['itemSource'], - $shareType, - $shareWith, - $_POST['permissions'] - ); - - if (is_string($token)) { + $share->setShareOwner(\OCP\User::getUser()); + $share->setShareWith($shareWith); + $share->setItemType($_POST['itemType']); + $share->setItemSource($_POST['itemSource']); + $share->setPermissions((int)$_POST['permissions']); + $share = $shareManager->share($share); + $token = $share->getToken(); + if (isset($token)) { OC_JSON::success(array('data' => array('token' => $token))); } else { OC_JSON::success(); @@ -54,31 +62,85 @@ break; case 'unshare': if (isset($_POST['shareType']) && isset($_POST['shareWith'])) { - if ((int)$_POST['shareType'] === OCP\Share::SHARE_TYPE_LINK && $_POST['shareWith'] == '') { - $shareWith = null; - } else { - $shareWith = $_POST['shareWith']; + $shareType = $_POST['shareType']; + settype($shareType, 'int'); + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { + $shareType = 'user'; + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $shareType ='group'; + } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { + $shareType = 'link'; + } + $filter = array( + 'shareOwner' => \OCP\User::getUser(), + 'shareWith' => $_POST['shareWith'], + 'shareTypeId' => $shareType, + 'itemSource' => $_POST['itemSource'], + ); + try { + $share = $shareManager->getShares($_POST['itemType'], $filter, 1); + if (!empty($share)) { + $share = reset($share); + $shareManager->unshare($share); + } + } catch (Exception $exception) { + OC_JSON::error(); } - $return = OCP\Share::unshare($_POST['itemType'], $_POST['itemSource'], $_POST['shareType'], $shareWith); - ($return) ? OC_JSON::success() : OC_JSON::error(); + OC_JSON::success(); } break; case 'setPermissions': if (isset($_POST['shareType']) && isset($_POST['shareWith']) && isset($_POST['permissions'])) { - $return = OCP\Share::setPermissions( - $_POST['itemType'], - $_POST['itemSource'], - $_POST['shareType'], - $_POST['shareWith'], - $_POST['permissions'] + $shareType = $_POST['shareType']; + settype($shareType, 'int'); + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { + $shareType = 'user'; + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { + $shareType ='group'; + } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { + $shareType = 'link'; + } + $filter = array( + 'shareOwner' => \OCP\User::getUser(), + 'shareWith' => $_POST['shareWith'], + 'shareTypeId' => $shareType, + 'itemSource' => $_POST['itemSource'], ); - ($return) ? OC_JSON::success() : OC_JSON::error(); + try { + $share = $shareManager->getShares($_POST['itemType'], $filter, 1); + if (!empty($share)) { + $share = reset($share); + $share->setPermissions((int)$_POST['permissions']); + $shareManager->update($share); + } + } catch (Exception $exception) { + OC_JSON::error(); + } + OC_JSON::success(); } break; case 'setExpirationDate': if (isset($_POST['date'])) { - $return = OCP\Share::setExpirationDate($_POST['itemType'], $_POST['itemSource'], $_POST['date']); - ($return) ? OC_JSON::success() : OC_JSON::error(); + if ($_POST['date'] === '') { + $time = null; + } else { + $date = new \DateTime($_POST['date']); + $time = $date->getTimeStamp(); + } + $filter = array( + 'shareOwner' => \OCP\User::getUser(), + 'itemSource' => $_POST['itemSource'], + ); + try { + $shares = $shareManager->getShares($_POST['itemType'], $filter); + foreach ($shares as $share) { + $share->setExpirationTime($time); + $shareManager->update($share); + } + } catch (Exception $exception) { + OC_JSON::error(); + } + OC_JSON::success(); } break; case 'email': @@ -126,8 +188,27 @@ switch ($_GET['fetch']) { case 'getItemsSharedStatuses': if (isset($_GET['itemType'])) { - $return = OCP\Share::getItemsShared($_GET['itemType'], OCP\Share::FORMAT_STATUSES); - is_array($return) ? OC_JSON::success(array('data' => $return)) : OC_JSON::error(); + $statuses = array(); + $filter = array( + 'shareOwner' => \OCP\User::getUser(), + ); + try { + $shares = $shareManager->getShares($_GET['itemType'], $filter); + foreach ($shares as $share) { + if ($share->getShareTypeId() === 'link') { + $statuses[$share->getItemSource()]['link'] = true; + } else if (!isset($statuses[$share->getItemSource()])) { + $statuses[$share->getItemSource()]['link'] = false; + } + $itemType = $share->getItemType(); + if ($itemType === 'file' || $itemType == 'folder') { + $statuses[$share->getItemSource()]['path'] = \OC\Files\Filesystem::getPath($share->getItemSource()); + } + } + } catch (Exception $exception) { + OC_JSON::error(array('data' => array('message' => OC_Util::sanitizeHTML($exception->getMessage())))); + } + OC_JSON::success(array('data' => $statuses)); } break; case 'getItem': @@ -136,104 +217,110 @@ && isset($_GET['checkReshare']) && isset($_GET['checkShares'])) { if ($_GET['checkReshare'] == 'true') { - $reshare = OCP\Share::getItemSharedWithBySource( - $_GET['itemType'], - $_GET['itemSource'], - OCP\Share::FORMAT_NONE, - null, - true + $reshare = array(); + $filter = array( + 'shareWith' => \OCP\User::getUser(), + 'isShareWithUser' => true, + 'itemSource' => $_GET['itemSource'], ); + $result = $shareManager->getShares($_GET['itemType'], $filter); + $shares = array(); + foreach ($result as $share) { + $shareTypeId = $share->getShareTypeId(); + if ($shareTypeId === 'user') { + $shareTypeId = \OCP\Share::SHARE_TYPE_USER; + } else if ($shareTypeId === 'group') { + $shareTypeId = OCP\Share::SHARE_TYPE_GROUP; + } else if ($shareTypeId === 'link') { + $shareTypeId = OCP\Share::SHARE_TYPE_LINK; + } + $expiration = $share->getExpirationTime(); + if (isset($expiration)) { + $expiration = date('d-m-Y', $share->getExpirationTime()); + } + $reshare[$share->getId()] = array( + 'share_type' => $shareTypeId, + 'uid_owner' => $share->getShareOwner(), + 'share_with' => $share->getShareWith(), + 'permissions' => $share->getPermissions(), + 'share_with_displayname' => $share->getShareWithDisplayName(), + 'displayname_owner' => $share->getShareOwnerDisplayName(), + 'expiration' => $expiration, + ); + } } else { - $reshare = false; + $reshare = array(); } if ($_GET['checkShares'] == 'true') { - $shares = OCP\Share::getItemShared( - $_GET['itemType'], - $_GET['itemSource'], - OCP\Share::FORMAT_NONE, - null, - true + $filter = array( + 'shareOwner' => \OCP\User::getUser(), + 'itemSource' => $_GET['itemSource'], ); + $result = $shareManager->getShares($_GET['itemType'], $filter); + $shares = array(); + foreach ($result as $share) { + $shareTypeId = $share->getShareTypeId(); + if ($shareTypeId === 'user') { + $shareTypeId = \OCP\Share::SHARE_TYPE_USER; + } else if ($shareTypeId === 'group') { + $shareTypeId = OCP\Share::SHARE_TYPE_GROUP; + } else if ($shareTypeId === 'link') { + $shareTypeId = OCP\Share::SHARE_TYPE_LINK; + } + $expiration = $share->getExpirationTime(); + if (isset($expiration)) { + $expiration = date('d-m-Y', $share->getExpirationTime()); + } + $shares[$share->getId()] = array( + 'share_type' => $shareTypeId, + 'uid_owner' => $share->getShareOwner(), + 'share_with' => $share->getShareWith(), + 'permissions' => $share->getPermissions(), + 'share_with_displayname' => $share->getShareWithDisplayName(), + 'displayname_owner' => $share->getShareOwnerDisplayName(), + 'expiration' => $expiration, + ); + } } else { - $shares = false; + $shares = array(); } OC_JSON::success(array('data' => array('reshare' => $reshare, 'shares' => $shares))); } break; case 'getShareWith': if (isset($_GET['search'])) { - $sharePolicy = OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); - $shareWith = array(); -// if (OC_App::isEnabled('contacts')) { -// // TODO Add function to contacts to only get the 'fullname' column to improve performance -// $ids = OC_Contacts_Addressbook::activeIds(); -// foreach ($ids as $id) { -// $vcards = OC_Contacts_VCard::all($id); -// foreach ($vcards as $vcard) { -// $contact = $vcard['fullname']; -// if (stripos($contact, $_GET['search']) !== false -// && (!isset($_GET['itemShares']) -// || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]) -// || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]) -// || !in_array($contact, $_GET['itemShares'][OCP\Share::SHARE_TYPE_CONTACT]))) { -// $shareWith[] = array('label' => $contact, 'value' => array('shareType' => 5, 'shareWith' => $vcard['id'])); -// } -// } -// } -// } - if ($sharePolicy == 'groups_only') { - $groups = OC_Group::getUserGroups(OC_User::getUser()); - } else { - $groups = OC_Group::getGroups(); - } - $count = 0; - $users = array(); - $limit = 0; - $offset = 0; - while ($count < 15 && count($users) == $limit) { - $limit = 15 - $count; - if ($sharePolicy == 'groups_only') { - $users = OC_Group::DisplayNamesInGroups($groups, $_GET['search'], $limit, $offset); - } else { - $users = OC_User::getDisplayNames($_GET['search'], $limit, $offset); + $shareWiths = array(); + $shareOwner = \OCP\User::getUser(); + $shareBackend = $shareManager->getShareBackend('file'); + $shareTypes = $shareBackend->getShareTypes(); + foreach ($shareTypes as $shareType) { + $shareTypeId = $shareType->getId(); + if ($shareTypeId === 'user') { + $shareTypeId = \OCP\Share::SHARE_TYPE_USER; + } else if ($shareTypeId === 'group') { + $shareTypeId = OCP\Share::SHARE_TYPE_GROUP; } - $offset += $limit; - foreach ($users as $uid => $displayName) { - if ((!isset($_GET['itemShares']) - || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) - || !in_array($uid, $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])) - && $uid != OC_User::getUser()) { - $shareWith[] = array( - 'label' => $displayName, - 'value' => array('shareType' => OCP\Share::SHARE_TYPE_USER, - 'shareWith' => $uid) - ); - $count++; + $result = $shareType->searchForPotentialShareWiths($shareOwner, $_GET['search'], 10, null); + foreach ($result as $shareWith) { + $shareWiths[] = array( + 'label' => $shareWith['shareWithDisplayName'], + 'value' => array( + 'shareType' => $shareTypeId, + 'shareWith' => $shareWith['shareWith'], + ), + ); + if (isset($limit)) { + $limit--; + if ($limit === 0) { + break 2; + } } - } - } - $count = 0; - foreach ($groups as $group) { - if ($count < 15) { - if (stripos($group, $_GET['search']) !== false - && (!isset($_GET['itemShares']) - || !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) - || !is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) - || !in_array($group, $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]))) { - $shareWith[] = array( - 'label' => $group.' (group)', - 'value' => array( - 'shareType' => OCP\Share::SHARE_TYPE_GROUP, - 'shareWith' => $group - ) - ); - $count++; + if (isset($offset) && $offset > 0) { + $offset--; } - } else { - break; } } - OC_JSON::success(array('data' => $shareWith)); + OC_JSON::success(array('data' => $shareWiths)); } break; } diff --git a/core/js/share.js b/core/js/share.js index 27c16f38b92d..a26aff86e3d4 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -257,7 +257,7 @@ OC.Share={ var shareWith = selected.item.value.shareWith; $(this).val(shareWith); // Default permissions are Edit (CRUD) and Share - var permissions = OC.PERMISSION_ALL; + var permissions = 17; OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, function() { OC.Share.addShareWith(shareType, shareWith, selected.item.label, permissions, possiblePermissions); $('#shareWith').val(''); diff --git a/db_structure.xml b/db_structure.xml index f926ab44cd40..55f753c5707f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -857,6 +857,199 @@ + + + *dbprefix*shares + + + + + id + integer + 0 + true + 1 + 4 + + + + share_type_id + text + + true + 32 + + + + share_owner + text + + true + 64 + + + + share_with + text + + false + 64 + + + + item_type + text + + true + 64 + + + + item_owner + text + + true + 64 + + + + item_source + text + + false + 64 + + + + item_target + text + + false + 250 + + + + permissions + integer + 0 + true + 1 + + + + share_time + integer + 0 + true + 4 + + + + expiration_time + integer + + false + 4 + + + + +
+ + + + *dbprefix*shares_groups + + + + + id + integer + 0 + true + 4 + + + + uid + text + + true + 64 + + + + item_target + text + + true + 250 + + + + +
+ + + + *dbprefix*shares_links + + + + + id + integer + 0 + true + 4 + + + + token + text + + true + 250 + + + + password + text + + false + 250 + + + + +
+ + + + *dbprefix*shares_parents + + + + + id + integer + 0 + true + 4 + + + + parent_id + integer + 0 + true + 4 + + + + +
+ *dbprefix*jobs diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 39e36684b7ba..9dbe88e90567 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -104,8 +104,8 @@ public function get($file) { $where = 'WHERE `storage` = ? AND `path_hash` = ?'; $params = array($this->getNumericStorageId(), md5($file)); } else { //file id - $where = 'WHERE `fileid` = ?'; - $params = array($file); + $where = 'WHERE `storage` = ? AND `fileid` = ?'; + $params = array($this->getNumericStorageId(), $file); } $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, `encrypted`, `unencrypted_size`, `etag` @@ -119,9 +119,9 @@ public function get($file) { $data = false; } - //merge partial data - if (!$data and is_string($file)) { - if (isset($this->partial[$file])) { + if (!$data) { + //merge partial data + if (is_string($file) && isset($this->partial[$file])) { $data = $this->partial[$file]; } } else { diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 10ec5c41d11e..ad784c56def3 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -588,6 +588,10 @@ static public function isSharable($path) { return self::$defaultInstance->isSharable($path); } + static public function getPermissions($path) { + return self::$defaultInstance->getPermissions($path); + } + static public function file_exists($path) { return self::$defaultInstance->file_exists($path); } diff --git a/lib/files/mount/mount.php b/lib/files/mount/mount.php index 0ce2f5975c71..e1dc7a984df6 100644 --- a/lib/files/mount/mount.php +++ b/lib/files/mount/mount.php @@ -119,7 +119,7 @@ public function getStorageId() { * @return string */ public function getInternalPath($path) { - if ($this->mountPoint === $path or $this->mountPoint . '/' === $path) { + if ($this->mountPoint === $path or $this->mountPoint === $path . '/') { $internalPath = ''; } else { $internalPath = substr($path, strlen($this->mountPoint)); diff --git a/lib/files/view.php b/lib/files/view.php index bb737f19ef82..8e43cdc0068b 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -230,6 +230,10 @@ public function isSharable($path) { return $this->basicOperation('isSharable', $path); } + public function getPermissions($path) { + return $this->basicOperation('getPermissions', $path); + } + public function file_exists($path) { if ($path == '/') { return true; @@ -1026,23 +1030,26 @@ public function getETag($path) { /** * Get the path of a file by id, relative to the view * - * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file + * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file * * @param int $id - * @return string + * @return string | null */ public function getPath($id) { - list($storage, $internalPath) = Cache\Cache::getById($id); - $mounts = Filesystem::getMountByStorageId($storage); + $mountManager = Filesystem::getMountManager(); + $mounts = $mountManager->findIn($this->getRoot()); + $mounts[] = $mountManager->find($this->getRoot()); foreach ($mounts as $mount) { - /** - * @var \OC\Files\Mount $mount - */ - $fullPath = $mount->getMountPoint() . $internalPath; - if (!is_null($path = $this->getRelativePath($fullPath))) { - return $path; + $cache = $mount->getStorage()->getCache(); + $data = $cache->get($id); + if ($data) { + $fullPath = $mount->getMountPoint() . $data['path']; + if (!is_null($path = $this->getRelativePath($fullPath))) { + return $path; + } } } return null; } + } diff --git a/lib/public/share.php b/lib/public/share.php index b38208bc67fc..268d2c725033 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -20,6 +20,9 @@ */ namespace OCP; +use OC\Share\ShareManager; +use OC\Share\Controller\ShareDialogController; + /** * This class provides the ability for apps to share their content between users. * Apps must create a backend class that implements OCP\Share_Backend and register it with this class. @@ -63,7 +66,124 @@ class Share { private static $backendTypes = array(); private static $isResharingAllowed; + private static $shareManager; + + /** + * Get the ShareManager + * @return \OC\Share\ShareManager | null + */ + public static function getShareManager() { + if (!isset(self::$shareManager)) { + if (\OC_Appconfig::getValue('core', 'shareapi_enabled', 'yes') === 'yes') { + self::$shareManager = new ShareManager(); + \OC_Util::addScript('core', 'share'); + \OC_Util::addStyle('core', 'share'); + self::setupHooks(); + } else { + self::$shareManager = null; + } + } + return self::$shareManager; + } + + // /** + // * Get the ShareDialogController + // * @param mixed $params The parameters for the request + // * @return \OC\Share\Controller\ShareDialogController | null + // */ + // public static function getShareDialogController($params) { + // \OC_JSON::callCheck(); + // \OC_JSON::checkLoggedIn(); + // $contents = json_decode(file_get_contents('php://input'), true); + // $contents = is_array($contents) ? $contents: array(); + // $params = array_merge($params, $contents, $_GET, $_POST); + // $shareManager = self::getShareManager(); + // if ($shareManager) { + // return new ShareDialogController($shareManager, new \OC\Log(), $params); + // } + // return null; + // } + + /** + * Emit old hooks for backwards compatibility + */ + public static function setupHooks() { + $shareManager = self::getShareManager(); + if ($shareManager) { + $shareManager->listen('\OC\Share', 'preShare', function($share) { + \OC_Hook::emit('OCP\Share', 'pre_shared', self::getHookArray($share)); + }); + $shareManager->listen('\OC\Share', 'postShare', function($share) { + \OC_Hook::emit('OCP\Share', 'post_shared', self::getHookArray($share)); + }); + $shareManager->listen('\OC\Share', 'preUnshare', function($share) { + $params = self::getHookArray($share); + $params['itemParent'] = $params['parent']; + \OC_Hook::emit('OCP\Share', 'pre_unshare', $params); + }); + $shareManager->listen('\OC\Share', 'postUnshare', function($share) { + $params = self::getHookArray($share); + $params['itemParent'] = $params['parent']; + \OC_Hook::emit('OCP\Share', 'post_unshare', $params); + }); + $shareManager->listen('\OC\Share', 'postUpdate', function($share) { + $properties = $share->getUpdatedProperties(); + if (isset($properties['permissions'])) { + $itemType = $share->getItemType(); + if ($itemType === 'file' || $itemType === 'folder') { + $params = self::getHookArray($share); + $params['path'] = $share->getPath(); + \OC_Hook::emit('OCP\Share', 'post_update_permissions', $params); + } + } + }); + } + } + + /** + * Get the share properties in an array as expected for the old hooks + * @param \OC\Share\Share $share + * @return array + */ + public static function getHookArray($share) { + $itemTarget = $share->getItemTarget(); + if ($share->getShareTypeId() === 'group') { + if (is_array($itemTarget)) { + $itemTarget = reset($itemTarget); + } + } + $shareType = null; + $shareTypeId = $share->getShareTypeId(); + if ($shareTypeId === 'user') { + $shareType = self::SHARE_TYPE_USER; + } else if ($shareTypeId === 'group') { + $shareType = self::SHARE_TYPE_GROUP; + } else if ($shareTypeId === 'link') { + $shareType = self::SHARE_TYPE_LINK; + } + $parent = null; + $parentIds = $share->getParentIds(); + if (is_array($parentIds)) { + $parent = reset($parentIds); + } + return array( + 'itemType' => $share->getItemType(), + 'itemSource' => $share->getItemSource(), + 'itemTarget' => $itemTarget, + 'parent' => $parent, + 'shareType' => $shareType, + 'shareWith' => $share->getShareWith(), + 'uidOwner' => $share->getShareOwner(), + 'permissions' => $share->getPermissions(), + 'fileSource' => $share->getItemSource(), + 'fileTarget' => $itemTarget, + 'id' => $share->getId(), + 'token' => $share->getToken(), + ); + } + /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string Item type * @param string Backend class @@ -94,6 +214,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Check if the Share API is enabled * @return Returns true if enabled or false * @@ -108,6 +229,7 @@ public static function isEnabled() { } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Prepare a path to be passed to DB as file_target * @return string Prepared path */ @@ -125,6 +247,7 @@ public static function prepFileTarget( $path ) { } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Find which users can access a shared item * @param $path to the file * @param $user owner of the file @@ -227,6 +350,7 @@ public static function getUsersSharingFile($path, $user, $includeOwner = false) } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Get the items of item type shared with the current user * @param string Item type * @param int Format (optional) Format type must be defined by the backend @@ -240,6 +364,7 @@ public static function getItemsSharedWith($itemType, $format = self::FORMAT_NONE } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Get the item of item type shared with the current user * @param string Item type * @param string Item target @@ -253,6 +378,7 @@ public static function getItemSharedWith($itemType, $itemTarget, $format = self: } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Get the item of item type shared with the current user by source * @param string Item type * @param string Item source @@ -266,6 +392,7 @@ public static function getItemSharedWithBySource($itemType, $itemSource, $format } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Get the item of item type shared by a link * @param string Item type * @param string Item source @@ -278,6 +405,7 @@ public static function getItemSharedWithByLink($itemType, $itemSource, $uidOwner } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief Get the item shared by a token * @param string token * @return Item @@ -292,6 +420,7 @@ public static function getShareByToken($token) { } /** + * @deprecated As of version 2.0.0, replaced by ShareManager * @brief resolves reshares down to the last real share * @param $linkItem * @return $fileOwner @@ -454,9 +583,6 @@ public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $forcePortable = (CRYPT_BLOWFISH != 1); $hasher = new \PasswordHash(8, $forcePortable); $shareWith = $hasher->HashPassword($shareWith.\OC_Config::getValue('passwordsalt', '')); - } else { - // reuse the already set password - $shareWith = $checkExists['share_with']; } // Generate token @@ -1292,8 +1418,6 @@ private static function put($itemType, $itemSource, $shareType, $shareWith, $uid if ($shareType == self::SHARE_TYPE_GROUP) { $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith['group'], $uidOwner, $suggestedItemTarget); - $run = true; - $error = ''; \OC_Hook::emit('OCP\Share', 'pre_shared', array( 'itemType' => $itemType, 'itemSource' => $itemSource, @@ -1303,9 +1427,7 @@ private static function put($itemType, $itemSource, $shareType, $shareWith, $uid 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error + 'token' => $token )); if ($run === false) { @@ -1387,8 +1509,6 @@ private static function put($itemType, $itemSource, $shareType, $shareWith, $uid } else { $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $suggestedItemTarget); - $run = true; - $error = ''; \OC_Hook::emit('OCP\Share', 'pre_shared', array( 'itemType' => $itemType, 'itemSource' => $itemSource, @@ -1398,9 +1518,7 @@ private static function put($itemType, $itemSource, $shareType, $shareWith, $uid 'uidOwner' => $uidOwner, 'permissions' => $permissions, 'fileSource' => $fileSource, - 'token' => $token, - 'run' => &$run, - 'error' => &$error + 'token' => $token )); if ($run === false) { diff --git a/lib/share/advancedsharefactory.php b/lib/share/advancedsharefactory.php new file mode 100644 index 000000000000..b1692e1f6cc0 --- /dev/null +++ b/lib/share/advancedsharefactory.php @@ -0,0 +1,49 @@ +. + */ + +namespace OC\Share; + +/** + * An alternative to ShareFactory that can reduce the number of queries to create a Share entity + * Setups JOINs in the share queries to retrieve additional properties for the Share entity + * The columns specified in getColumns() will be returned in the $row passed to mapToShare($row) + */ +abstract class AdvancedShareFactory extends ShareFactory { + + /** + * Get JOIN(s) to app table(s) + * @return string + * + * Example: JOIN `*PREFIX*table1` ON `*PREFIX*share`.`item_source` = `*PREFIX*table1`.`id` + * + */ + abstract public function getJoins(); + + /** + * Get app table column(s) + * @return string + * + * Example: `*PREFIX*table1`.`id`, `*PREFIX*table1`.`name` + * + */ + abstract public function getColumns(); + +} \ No newline at end of file diff --git a/lib/share/exception/invalidexpirationtimeexception.php b/lib/share/exception/invalidexpirationtimeexception.php new file mode 100644 index 000000000000..c85538424862 --- /dev/null +++ b/lib/share/exception/invalidexpirationtimeexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidExpirationTimeException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invaliditemexception.php b/lib/share/exception/invaliditemexception.php new file mode 100644 index 000000000000..bb6abba2d73a --- /dev/null +++ b/lib/share/exception/invaliditemexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidItemException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invalidpermissionsexception.php b/lib/share/exception/invalidpermissionsexception.php new file mode 100644 index 000000000000..e8edb6ea5550 --- /dev/null +++ b/lib/share/exception/invalidpermissionsexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidPermissionsException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/invalidshareexception.php b/lib/share/exception/invalidshareexception.php new file mode 100644 index 000000000000..11c2eb424783 --- /dev/null +++ b/lib/share/exception/invalidshareexception.php @@ -0,0 +1,30 @@ +. + */ + +namespace OC\Share\Exception; + +class InvalidShareException extends \Exception { + + public function __construct($message) { + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/multiplesharesreturnedexception.php b/lib/share/exception/multiplesharesreturnedexception.php new file mode 100644 index 000000000000..6b5801e94047 --- /dev/null +++ b/lib/share/exception/multiplesharesreturnedexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class MultipleSharesReturnedException extends \Exception { + + public function __construct($id) { + $message = 'Multiple shares were returned for the id '.$id; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharebackenddoesnotexistexception.php b/lib/share/exception/sharebackenddoesnotexistexception.php new file mode 100644 index 000000000000..afe54634cf60 --- /dev/null +++ b/lib/share/exception/sharebackenddoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareBackendDoesNotExistException extends \Exception { + + public function __construct($itemType) { + $message = 'A share backend does not exist for the item type '.$itemType; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharedoesnotexistexception.php b/lib/share/exception/sharedoesnotexistexception.php new file mode 100644 index 000000000000..a7da075dc27a --- /dev/null +++ b/lib/share/exception/sharedoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareDoesNotExistException extends \Exception { + + public function __construct($id) { + $message = 'A share does not exist with the id '.$id; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/exception/sharetypedoesnotexistexception.php b/lib/share/exception/sharetypedoesnotexistexception.php new file mode 100644 index 000000000000..71e629609a27 --- /dev/null +++ b/lib/share/exception/sharetypedoesnotexistexception.php @@ -0,0 +1,31 @@ +. + */ + +namespace OC\Share\Exception; + +class ShareTypeDoesNotExistException extends \Exception { + + public function __construct($shareTypeId) { + $message = 'A share type does not exist with the id '.$shareTypeId; + parent::__construct($message); + } + +} \ No newline at end of file diff --git a/lib/share/icollectionsharebackend.php b/lib/share/icollectionsharebackend.php new file mode 100644 index 000000000000..874adc90844d --- /dev/null +++ b/lib/share/icollectionsharebackend.php @@ -0,0 +1,56 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; + +/** + * This interface should be implemented if a share backend has content that can have children that + * are also shared using a different backend class e.g. folders + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +interface ICollectionShareBackend { + + /** + * Get the identifiers for the children item types of this backend + * @return array + */ + public function getChildrenItemTypes(); + + /** + * Search for shares of this collection item type that contain the child item source and + * shared with the share owner + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + */ + public function searchForParentCollections(Share $share); + +} \ No newline at end of file diff --git a/lib/share/itemtargetmachine.php b/lib/share/itemtargetmachine.php new file mode 100644 index 000000000000..ace1bf592958 --- /dev/null +++ b/lib/share/itemtargetmachine.php @@ -0,0 +1,39 @@ +. + */ + +namespace OC\Share; + +use OC\User\User; + +abstract class ItemTargetMachine { + + /** + * Get a unique item target for the specified share and user + * @param \OC\Share\Share $share + * @param \OC\User\User $user + * @return string + * + * If $user is null, any item target is acceptable + * + */ + abstract public function getItemTarget(Share $share, User $user = null); + +} \ No newline at end of file diff --git a/lib/share/share.php b/lib/share/share.php new file mode 100644 index 000000000000..510ef073b4e5 --- /dev/null +++ b/lib/share/share.php @@ -0,0 +1,511 @@ +. + */ + +namespace OC\Share; + +/** + * Data holder for shared items + * Extend this class to store additional properties + * + * Adapated from OCA\AppFramework\Db\Entity + * Only setters that call markPropertyUpdated are mutable properties + */ +class Share { + + protected $id; + protected $parentIds = array(); + protected $shareTypeId; + protected $shareOwner; + protected $shareOwnerDisplayName; + protected $shareWith; + protected $shareWithDisplayName; + protected $itemType; + protected $itemSource; + protected $itemTarget; + protected $itemOwner; + protected $permissions = 0; + protected $expirationTime; + protected $shareTime; + protected $token; + protected $password; + + private $updatedProperties = array(); + private $propertyTypes = array( + 'id' => 'int', + 'permissions' => 'int', + 'expirationTime' => 'int', + 'shareTime' => 'int' + ); + + /** + * Get the id + * @return int + */ + public function getId() { + return $this->id; + } + + /** + * Set the id + * @param int $id + */ + public function setId($id) { + $this->id = $id; + } + + /** + * Get the references to parent shares + * @return array + */ + public function getParentIds() { + return $this->parentIds; + } + + /** + * Set the references to parent shares + * @param array $parentIds + */ + public function setParentIds($parentIds) { + $this->parentIds = $parentIds; + $this->markPropertyUpdated('parentIds'); + } + + /** + * Add a reference to a parent share + * @param int $id + */ + public function addParentId($id) { + $parentIds = $this->getParentIds(); + $parentIds[] = $id; + $this->setParentIds($parentIds); + } + + /** + * Remove a reference to a parent share + * @param int $id + */ + public function removeParentId($id) { + $parentIds = $this->getParentIds(); + $parentIds = array_diff($this->getParentIds(), array($id)); + $this->setParentIds($parentIds); + } + + /** + * Get the share type id + * @return string + */ + public function getShareTypeId() { + return $this->shareTypeId; + } + + /** + * Set the share type id + * @param string $shareTypeId + */ + public function setShareTypeId($shareTypeId) { + $this->shareTypeId = $shareTypeId; + } + + /** + * Get the share owner + * @return string + */ + public function getShareOwner() { + return $this->shareOwner; + } + + /** + * Set the share owner + * @param string $shareOwner + */ + public function setShareOwner($shareOwner) { + $this->shareOwner = $shareOwner; + } + + /** + * Get the share owner display name + * @return string + */ + public function getShareOwnerDisplayName() { + return $this->shareOwnerDisplayName; + } + + /** + * Set the share owner display name + * @param string $shareOwnerDisplayName + */ + public function setShareOwnerDisplayName($shareOwnerDisplayName) { + $this->shareOwnerDisplayName = $shareOwnerDisplayName; + } + + /** + * Get the share with + * @return string + */ + public function getShareWith() { + return $this->shareWith; + } + + /** + * Set the share with + * @param string $shareWith + */ + public function setShareWith($shareWith) { + $this->shareWith = $shareWith; + } + + /** + * Get the share with display name + * @return string + */ + public function getShareWithDisplayName() { + return $this->shareWithDisplayName; + } + + /** + * Set the share With display name + * @param string $shareWithDisplayName + */ + public function setShareWithDisplayName($shareWithDisplayName) { + $this->shareWithDisplayName = $shareWithDisplayName; + } + + /** + * Get the item type + * @return string + */ + public function getItemType() { + return $this->itemType; + } + + /** + * Set the item type + * @param string $itemType + */ + public function setItemType($itemType) { + $this->itemType = $itemType; + } + + /** + * Get the item source + * @return mixed + */ + public function getItemSource() { + return $this->itemSource; + } + + /** + * Set the item source + * @param mixed $itemSource + */ + public function setItemSource($itemSource) { + $this->itemSource = $itemSource; + } + + /** + * Get the item target + * @return string + */ + public function getItemTarget() { + return $this->itemTarget; + } + + /** + * Set the item target + * @param string $itemTarget + */ + public function setItemTarget($itemTarget) { + $this->itemTarget = $itemTarget; + $this->markPropertyUpdated('itemTarget'); + } + + /** + * Get the item owner, may differ from share owner if this is a reshare + * @return string + */ + public function getItemOwner() { + return $this->itemOwner; + } + + /** + * Set the item owner + * @param string $itemOwner + */ + public function setItemOwner($itemOwner) { + $this->itemOwner = $itemOwner; + } + + /** + * Get the permissions + * @return int + */ + public function getPermissions() { + return $this->permissions; + } + + /** + * Set the permissions + * @param int $permissions + */ + public function setPermissions($permissions) { + $this->permissions = $permissions; + $this->markPropertyUpdated('permissions'); + } + + /** + * Check if create permission is granted + * @return bool + */ + public function isCreatable() { + return ($this->permissions & \OCP\PERMISSION_CREATE) !== 0; + } + + /** + * Check if read permission is granted + * @return bool + */ + public function isReadable() { + return ($this->permissions & \OCP\PERMISSION_READ) !== 0; + } + + /** + * Check if update permission is granted + * @return bool + */ + public function isUpdatable() { + return ($this->permissions & \OCP\PERMISSION_UPDATE) !== 0; + } + + /** + * Check if delete permission is granted + * @return bool + */ + public function isDeletable() { + return ($this->permissions & \OCP\PERMISSION_DELETE) !== 0; + } + + /** + * Check if share permission is granted + * @return bool + */ + public function isSharable() { + return ($this->permissions & \OCP\PERMISSION_SHARE) !== 0; + } + + /** + * Get the expiration time + * @return int + */ + public function getExpirationTime() { + return $this->expirationTime; + } + + /** + * Set the expiration time + * @param int $expirationTime + */ + public function setExpirationTime($expirationTime) { + $this->expirationTime = $expirationTime; + $this->markPropertyUpdated('expirationTime'); + } + + /** + * Get the share time + * @return int + */ + public function getShareTime() { + return $this->shareTime; + } + + /** + * Set the share time + * @param int $shareTime + */ + public function setShareTime($shareTime) { + $this->shareTime = $shareTime; + } + + /** + * Get the token, only for shares of the link share type + * @return string + */ + public function getToken() { + return $this->token; + } + + /** + * Set the token, only for shares of the link share type + * @param string $token + */ + public function setToken($token) { + $this->token = $token; + } + + /** + * Get the password, only for shares of the link share type + * @return string + */ + public function getPassword() { + return $this->password; + } + + /** + * Set the password, only for shares of the link share type + * @param string $password + */ + public function setPassword($password) { + $this->password = $password; + $this->markPropertyUpdated('password'); + } + + /** + * Get all properties in an array + * @return array + */ + public function toAPI() { + return array( + 'id' => $this->getId(), + 'parentIds' => $this->getParentIds(), + 'shareTypeId' => $this->getShareTypeId(), + 'shareOwner' => $this->getShareOwner(), + 'shareOwnerDisplayName' => $this->getShareOwnerDisplayName(), + 'shareWith' => $this->getShareWith(), + 'shareWithDisplayName' => $this->getShareWithDisplayName(), + 'itemType' => $this->getItemType(), + 'itemSource' => $this->getItemSource(), + 'itemTarget' => $this->getItemTarget(), + 'itemOwner' => $this->getItemOwner(), + 'permissions' => $this->getPermissions(), + 'expirationTime' => $this->getExpirationTime(), + 'shareTime' => $this->getShareTime(), + 'token' => $this->getToken(), + 'password' => $this->getPassword(), + ); + } + + /** + * Simple alternative constructor for building entities from a request + * @param array $params the array which was obtained via $this->params('key') + * in the controller + * @return Share + */ + public static function fromParams(array $params) { + $instance = new static(); + foreach ($params as $property => $value) { + $setter = 'set'.ucfirst($property); + if (method_exists($instance, $setter)) { + $instance->$setter($value); + } + } + return $instance; + } + + /** + * Maps the keys of the row array to the attributes + * @param array $row the row to map onto the entity + */ + public static function fromRow(array $row) { + $instance = new static(); + foreach ($row as $column => $value) { + $property = $instance::columnToProperty($column); + $setter = 'set'.ucfirst($property); + if (method_exists($instance, $setter)) { + if (isset($value) && isset($instance->propertyTypes[$property])) { + settype($value, $instance->propertyTypes[$property]); + } + $instance->$setter($value); + } + } + return $instance; + } + + /** + * Mark a property as updated + * @param string $property the name of the property + */ + protected function markPropertyUpdated($property) { + $this->updatedProperties[$property] = true; + } + + /** + * @return array array of updated fields for update query + */ + public function getUpdatedProperties() { + return $this->updatedProperties; + } + + /** + * Marks the entity as clean + */ + public function resetUpdatedProperties() { + $this->updatedProperties = array(); + } + + /** + * Transform a database column name to a property + * @param string $columnName the name of the column + * @return string the property name + */ + public static function columnToProperty($columnName) { + $columnName = trim($columnName, '`'); + $parts = explode('_', $columnName); + $property = null; + foreach ($parts as $part) { + if ($property === null) { + $property = $part; + } else { + $property .= ucfirst($part); + } + } + return $property; + } + + /** + * Transform a property to a database column name + * @param string $property the name of the property + * @return string the column name + */ + public static function propertyToColumn($property) { + $parts = preg_split('/(?=[A-Z])/', $property); + $column = null; + foreach ($parts as $part) { + if ($column === null) { + $column = $part; + } else { + $column .= '_'.lcfirst($part); + } + } + return '`'.$column.'`'; + } + + /** + * Adds type information for a property so that its automatically casted to + * that value once its being returned from the database + * @param string $property the name of the attribute + * @param string $type the type which will be used to call settype() + */ + protected function addType($property, $type) { + $this->propertyTypes[$property] = $type; + } + +} \ No newline at end of file diff --git a/lib/share/sharebackend.php b/lib/share/sharebackend.php new file mode 100644 index 000000000000..6f39009ba72f --- /dev/null +++ b/lib/share/sharebackend.php @@ -0,0 +1,292 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; +use OC\Share\TimeMachine; +use OC\Hooks\BasicEmitter; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Share\Exception\InvalidPermissionsException; +use OC\Share\Exception\InvalidExpirationTimeException; + +/** + * Backend class that apps extend and register with the ShareManager to share content + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +abstract class ShareBackend extends BasicEmitter { + + protected $timeMachine; + protected $shareTypes; + + /** + * The constructor + * @param \OC\Share\TimeMachine $timeMachine The time() mock + * @param \OC\Share\ShareType\IShareType[] $shareTypes An array of share type objects that + * items can be shared through e.g. User, Group, Link + */ + public function __construct(TimeMachine $timeMachine, array $shareTypes) { + $this->timeMachine = $timeMachine; + foreach ($shareTypes as $shareType) { + $this->shareTypes[$shareType->getId()] = $shareType; + } + } + + /** + * Get the identifier for the item type this backend handles, should be a singular noun + * @return string + */ + abstract public function getItemType(); + + /** + * Get the plural form of getItemType, used for the RESTful API + * @return string + */ + abstract public function getItemTypePlural(); + + /** + * Check if an item is valid for the share owner + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidItemException If the item does not exist or the share + * owner does not have access to the item + * @return bool + */ + abstract protected function isValidItem(Share $share); + + /** + * Get all share types + * @return \OC\Share\ShareType\IShareType[] + */ + public function getShareTypes() { + return $this->shareTypes; + } + + /** + * Get share type by id + * @param string $shareTypeId + * @throws \OC\Share\Exception\ShareTypeDoesNotExistException + * @return \OC\Share\IShareType + */ + public function getShareType($shareTypeId) { + if (isset($this->shareTypes[$shareTypeId])) { + return $this->shareTypes[$shareTypeId]; + } else { + throw new ShareTypeDoesNotExistException($shareTypeId); + } + } + + /** + * Share a share + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidItemException + * @throws \OC\Share\Exception\InvalidShareException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return \OC\Share\Share | bool + */ + public function share(Share $share) { + if ($this->isValidItem($share)) { + // Don't share the same share again + $filter = array( + 'shareTypeId' => $share->getShareTypeId(), + 'shareOwner' => $share->getShareOwner(), + 'shareWith' => $share->getShareWith(), + 'itemSource' => $share->getItemSource(), + ); + $exists = $this->getShares($filter, 1); + if (!empty($exists)) { + throw new InvalidShareException('The share already exists'); + } + $shareType = $this->getShareType($share->getShareTypeId()); + if ($shareType->isValidShare($share) && $this->areValidPermissions($share) + && $this->isValidExpirationTime($share) + ) { + $this->emit('\OC\Share', 'preShare', array($share)); + $share->setShareTime($this->timeMachine->getTime()); + $share = $shareType->share($share); + $this->emit('\OC\Share', 'postShare', array($share)); + return $share; + } + } + return false; + } + + /** + * Unshare a share + * @param \OC\Share\Share $share + */ + public function unshare(Share $share) { + $shareType = $this->getShareType($share->getShareTypeId()); + $this->emit('\OC\Share', 'preUnshare', array($share)); + $shareType->unshare($share); + $this->emit('\OC\Share', 'postUnshare', array($share)); + } + + /** + * Update a share's properties in the database + * @param \OC\Share\Share $share + */ + public function update(Share $share) { + $properties = $share->getUpdatedProperties(); + if (!empty($properties)) { + if (isset($properties['permissions'])) { + $this->areValidPermissions($share); + } + if (isset($properties['expirationTime'])) { + $this->isValidExpirationTime($share); + } + $shareType = $this->getShareType($share->getShareTypeId()); + $this->emit('\OC\Share', 'preUpdate', array($share)); + foreach ($properties as $property => $updated) { + $setter = 'set'.ucfirst($property); + if (method_exists($shareType, $setter)) { + $shareType->$setter($share); + unset($properties[$property]); + } + } + if (!empty($properties)) { + $shareType->update($share); + } + $this->emit('\OC\Share', 'postUpdate', array($share)); + } + } + + /** + * Get the shares with the specified parameters + * @param array $filter (optional) A key => value array of share properties + * @param int $limit (optional) + * @param int $offset (optional) + * @return \OC\Share\Share[] + */ + public function getShares($filter = array(), $limit = null, $offset = null) { + if (isset($filter['shareTypeId'])) { + $shareTypes = array($this->getShareType($filter['shareTypeId'])); + unset($filter['shareTypeId']); + } else { + $shareTypes = $this->shareTypes; + } + $shares = array(); + foreach ($shareTypes as $shareType) { + $result = $shareType->getShares($filter, $limit, $offset); + foreach ($result as $share) { + $shares[] = $share; + if (isset($limit)) { + $limit--; + if ($limit === 0) { + return $shares; + } + } + if (isset($offset) && $offset > 0) { + $offset--; + } + } + } + return $shares; + } + + /** + * Check if a share is expired + * @param \OC\Share\Share $share + * @return bool + */ + public function isExpired(Share $share) { + $time = $share->getExpirationTime(); + $now = $this->timeMachine->getTime(); + if (isset($time)) { + if ($time - $now < 0) { + return true; + } else { + return false; + } + } else { + // Check if the admin has set a default expiration time + $defaultTime = \OC_Appconfig::getValue('core', 'shareapi_expiration_time', 0); + if ($defaultTime > 0 && $defaultTime + $share->getShareTime() - $now < 0) { + return true; + } else { + return false; + } + } + } + + /** + * Check if a share's permissions are valid + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidPermissionsException If the permissions are not an + * integer or are not in the range of 1 to 31 + * @return bool + * + * Permissions are defined by the CRUDS system, see lib/public/constants.php + * General information: wikipedia.org/wiki/Create,_read,_update_and_delete + * + * In ownCloud 'S' is defined as share permission + * + * You can use PHP's bitwise operators to manipulate the permissions + * + */ + protected function areValidPermissions(Share $share) { + $permissions = $share->getPermissions(); + if (!is_int($permissions)) { + throw new InvalidPermissionsException('The permissions are not an integer'); + } + if ($permissions < 1 || $permissions > \OCP\PERMISSION_ALL) { + throw new InvalidPermissionsException( + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + } + return true; + } + + /** + * Check if a share's expiration time is valid + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidExpirationTimeException If the expiration time is set and is + * not an integer or is not at least 1 hour in the future + * @return bool + * + * Expiration time is defined by a unix timestamp + * An expiration time of null implies the share will not expire + * + */ + protected function isValidExpirationTime(Share $share) { + $time = $share->getExpirationTime(); + if (isset($time) && !is_int($time)) { + throw new InvalidExpirationTimeException('The expiration time is not an integer'); + } + if (isset($time) && $time < $this->timeMachine->getTime() + 3600) { + throw new InvalidExpirationTimeException( + 'The expiration time is not at least 1 hour in the future' + ); + } + return true; + } + +} \ No newline at end of file diff --git a/lib/share/sharefactory.php b/lib/share/sharefactory.php new file mode 100644 index 000000000000..7388dc99e41e --- /dev/null +++ b/lib/share/sharefactory.php @@ -0,0 +1,38 @@ +. + */ + +namespace OC\Share; + +/** + * Creates Share entities from the database + */ +class ShareFactory { + + /** + * Map a database row to a Share entity + * @param array $row A key => value array of share properties + * @return Share + */ + public function mapToShare($row) { + return Share::fromRow($row); + } + +} \ No newline at end of file diff --git a/lib/share/sharemanager.php b/lib/share/sharemanager.php new file mode 100644 index 000000000000..aede75fb954d --- /dev/null +++ b/lib/share/sharemanager.php @@ -0,0 +1,553 @@ +. + */ + +namespace OC\Share; + +use OC\Share\Share; +use OC\Share\ShareBackend; +use OC\Share\CollectionShareBackend; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareDoesNotExistException; +use OC\Share\Exception\ShareBackendDoesNotExistException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Share\Exception\InvalidPermissionsException; +use OC\Share\Exception\InvalidExpirationTimeException; +use OC\Share\Exception\MultipleSharesReturnedException; +use OC\Hooks\ForwardingEmitter; + +/** + * This is the gateway for sharing content between users in ownCloud, aka Share API + * Apps must create a share backend class that extends OC\Share\ShareBackend and register it here + * + * The ShareManager's primary purpose is to ensure consistency between shares and their reshares + * + * Hooks available in scope \OC\Share: + * - preShare(\OC\Share\Share $share) + * - postShare(\OC\Share\Share $share) + * - preUnshare(\OC\Share\Share $share) + * - postUnshare(\OC\Share\Share $share) + * - preUpdate(\OC\Share\Share $share) + * - postUpdate(\OC\Share\Share $share) + * + * @version 2.0.0 BETA + */ +class ShareManager extends ForwardingEmitter { + + protected $shareBackends; + + /** + * Register a share backend + * @param \OC\Share\ShareBackend $shareBackend + */ + public function registerShareBackend(ShareBackend $shareBackend) { + $this->shareBackends[$shareBackend->getItemType()] = $shareBackend; + $this->forward($shareBackend); + } + + /** + * Get all registered share backends + * @return \OC\Share\ShareBackend[] + */ + public function getShareBackends() { + return $this->shareBackends; + } + + /** + * Get a share backend by item type + * @param string $itemType + * @throws \OC\Share\Exception\ShareBackendDoesNotExistException + * @return \OC\Share\ShareBackend + */ + public function getShareBackend($itemType) { + if (isset($this->shareBackends[$itemType])) { + return $this->shareBackends[$itemType]; + } else { + throw new ShareBackendDoesNotExistException($itemType); + } + } + + /** + * Share a share in the share backend + * @param Share $share + * @throws \OC\Share\Exception\InvalidItemException + * @throws \OC\Share\Exception\InvalidShareException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return \OC\Share\Share | bool + */ + public function share(Share $share) { + $shareBackend = $this->getShareBackend($share->getItemType()); + $parents = $this->searchForParents($share); + // See if the share is a reshare + if (!empty($parents)) { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + if ($resharing !== 'yes') { + throw new InvalidShareException('The admin has disabled resharing'); + } + foreach ($parents as $parent) { + if ($parent->getShareTypeId() === $share->getShareTypeId()) { + if ($parent->getShareOwner() === $share->getShareWith()) { + throw new InvalidShareException( + 'The share can\'t reshare back to the share owner' + ); + } + if ($parent->getShareWith() === $share->getShareWith()) { + throw new InvalidShareException( + 'The parent share has the same share with' + ); + } + } + if ($parent->isSharable()) { + $share->addParentId($parent->getId()); + } + } + $parentIds = $share->getParentIds(); + if (empty($parentIds)) { + throw new InvalidShareException( + 'The parent shares don\'t allow resharing' + ); + } + $share->setItemOwner(reset($parents)->getItemOwner()); + $this->areValidPermissionsForParents($share); + $this->isValidExpirationTimeForParents($share); + } else { + $share->setItemOwner($share->getShareOwner()); + } + $share = $shareBackend->share($share); + if ($share !== false && $share->isSharable()) { + $id = $share->getId(); + // Add this share to existing reshares' parent ids + $reshares = $this->searchForReshares($share); + foreach ($reshares as $reshare) { + $reshare->addParentId($id); + $this->update($reshare); + } + } + return $share; + } + + /** + * Unshare a share in the share backend + * @param \OC\Share\Share $share + */ + public function unshare(Share $share) { + $shareBackend = $this->getShareBackend($share->getItemType()); + if ($share->isSharable()) { + // Fake removing all permissions so reshares will be unshared or updated correctly + $fakeShare = clone $share; + $fakeShare->setPermissions(0); + $this->updateReshares($fakeShare, $share); + } + $shareBackend->unshare($share); + } + + /** + * Update a share's properties in the share backend + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @throws \OC\Share\Exception\InvalidPermissionsException + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * + * Updating permissions or expiration time will trigger an update of the respective property + * for all reshares to ensure consistency with the parent shares + * + */ + public function update(Share $share) { + $itemType = $share->getItemType(); + $properties = $share->getUpdatedProperties(); + if (isset($properties['permissions'])) { + $this->areValidPermissionsForParents($share); + } + if (isset($properties['expirationTime'])) { + $this->isValidExpirationTimeForParents($share); + } + // Find the share in the backend to compare old/new properties for reshares' updates + $oldShare = $this->getShareById($share->getId(), $itemType, $share->getShareTypeId()); + $shareBackend = $this->getShareBackend($itemType); + $shareBackend->update($share); + if (isset($properties['permissions']) || isset($properties['expirationTime'])) { + $this->updateReshares($share, $oldShare); + } + } + + /** + * Get the shares with the specified parameters in the share backend + * @param string $itemType + * @param array $filter (optional) A key => value array of share properties + * @param int $limit (optional) + * @param int $offset (optional) + * @return \OC\Share\Share[] + */ + public function getShares($itemType, $filter = array(), $limit = null, $offset = null) { + $shares = array(); + $shareBackend = $this->getShareBackend($itemType); + $results = $shareBackend->getShares($filter, $limit, $offset); + $expired = 0; + foreach ($results as $share) { + if ($shareBackend->isExpired($share)) { + $this->unshare($share); + $expired++; + } else { + $shares[] = $share; + } + } + // If shares expired and a limit was requested, attempt to replace the shares + // Don't try if the number of results didn't match the limit since there are no more shares + if ($expired > 0 && isset($limit) && count($results) === $limit) { + if (isset($offset)) { + $offset += $limit - $expired; + } else { + $offset = $limit - $expired; + } + $expiredReplacements = $this->getShares($itemType, $filter, $expired, $offset); + $shares = array_merge($shares, $expiredReplacements); + } + return $shares; + } + + /** + * Get a share by id + * @param int $id + * @param string $itemType (optional) + * @param string $shareTypeId (optional) + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @return \OC\Share\Share + */ + public function getShareById($id, $itemType = null, $shareTypeId = null) { + $share = array(); + $filter = array('id' => $id); + if (isset($shareTypeId)) { + $filter['shareTypeId'] = $shareTypeId; + } + if (isset($itemType)) { + $share = $this->getShares($itemType, $filter, 1); + } else { + foreach ($this->getShareBackends() as $shareBackend) { + try { + $share = $this->getShares($shareBackend->getItemType(), $filter, 1); + if (!empty($share)) { + break; + } + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + } + if (empty($share)) { + throw new ShareDoesNotExistException($id); + } else if (count($share) > 1) { + throw new MultipleSharesReturnedException($id); + } else { + return reset($share); + } + } + + /** + * Unshare all shares of an item + * @param string $itemType + * @param any $itemSource + * + * Call this if an item is deleted by the item owner. However, this should not be called if + * the item owner is deleted because this is handled by the UserWatcher. + * + */ + public function unshareItem($itemType, $itemSource) { + $filter = array( + 'itemSource' => $itemSource, + ); + $shares = $this->getShares($itemType, $filter); + foreach ($shares as $share) { + $this->unshare($share); + } + } + + /** + * Get all reshares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * It is possible for the reshares to be of a different item type if the share's item type + * is a collection + * + */ + public function getReshares(Share $share) { + $itemType = $share->getItemType(); + $filter = array( + 'parentId' => $share->getId(), + ); + $reshares = $this->getShares($itemType, $filter); + $shareBackend = $this->getShareBackend($itemType); + if ($shareBackend instanceof ICollectionShareBackend) { + // Find reshares in children item types + foreach ($shareBackend->getChildrenItemTypes() as $childItemType) { + $childReshares = $this->getShares($childItemType, $filter); + $reshares = array_merge($reshares, $childReshares); + } + } + return $reshares; + } + + /** + * Get all parents of a share + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\ShareDoesNotExistException + * @return \OC\Share\Share[] + * + * It is possible for the parents to be of a different item type if the shares's item type + * is a child item type in a collection + * + */ + public function getParents(Share $share) { + $parents = array(); + $parentIds = $share->getParentIds(); + if (!empty($parentIds)) { + $itemType = $share->getItemType(); + $parentItemTypes = array($itemType); + foreach ($this->getShareBackends() as $shareBackend) { + if ($shareBackend instanceof ICollectionShareBackend + && in_array($itemType, $shareBackend->getChildrenItemTypes()) + && !in_array($shareBackend->getItemType(), $parentItemTypes) + ) { + $parentItemTypes[] = $shareBackend->getItemType(); + } + } + foreach ($parentIds as $parentId) { + foreach ($parentItemTypes as $parentItemType) { + try { + $parents[] = $this->getShareById($parentId, $parentItemType); + break; + } catch (ShareDoesNotExistException $exception) { + if ($parentItemType === end($parentItemTypes)) { + throw $exception; + } + } + } + } + } + return $parents; + } + + /** + * Search for reshares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * Call this to determine if the share has existing reshares because there is a duplicate share + * + * Use getReshares() for all other cases + * + */ + protected function searchForReshares(Share $share) { + $reshares = array(); + $id = $share->getId(); + $filter = array( + 'shareOwner' => $share->getShareOwner(), + 'itemSource' => $share->getItemSource(), + ); + $potentialDuplicates = $this->getShares($share->getItemType(), $filter); + foreach ($potentialDuplicates as $potentialDuplicate) { + if ($potentialDuplicate->getId() !== $id) { + $potentialReshares = $this->getReshares($potentialDuplicate); + foreach ($potentialReshares as $potentialReshare) { + $parents = $this->searchForParents($potentialReshare); + foreach ($parents as $parent) { + if ($parent->getId() === $id) { + $reshares[] = $potentialReshare; + break; + } + } + } + } + } + return $reshares; + } + + /** + * Search for parent shares of a share + * @param \OC\Share\Share $share + * @return \OC\Share\Share[] + * + * Call this to determine if the share is a reshare and needs to set the parent ids property + * + * Use getParents() for all other cases + * + */ + protected function searchForParents(Share $share) { + $itemType = $share->getItemType(); + $filter = array( + 'shareWith' => $share->getShareOwner(), + 'isShareWithUser' => true, + 'itemSource' => $share->getItemSource(), + ); + $parents = $this->getShares($itemType, $filter); + // Search in collections for parents in case children were reshared + foreach ($this->getShareBackends() as $shareBackend) { + if ($shareBackend instanceof ICollectionShareBackend + && in_array($itemType, $shareBackend->getChildrenItemTypes()) + ) { + $collectionParents = $shareBackend->searchForParentCollections($share); + // Check if shares are expired, because this method call doesn't go through + // ShareManager's getShares method + foreach ($collectionParents as $share) { + if ($shareBackend->isExpired($share)) { + $this->unshare($share); + } else { + $parents[] = $share; + } + } + } + } + return $parents; + } + + /** + * Get the total permissions of an array of shares + * @param \OC\Share\Share[] $shares + * @return int + */ + protected function getTotalPermissions(array $shares) { + $totalPermissions = 0; + foreach ($shares as $share) { + $totalPermissions |= $share->getPermissions(); + } + return $totalPermissions; + } + + /** + * Get the latest expiration time of an array of shares + * @param \OC\Share\Share[] $shares + * @return int | null + */ + protected function getLatestExpirationTime(array $shares) { + $latestTime = null; + foreach ($shares as $share) { + $time = $share->getExpirationTime(); + if (!isset($time)) { + return null; + } else if (!isset($latestTime) || $time > $latestTime) { + $latestTime = $time; + } + } + return $latestTime; + } + + /** + * Check if a share's permissions are valid with respect to the parent shares + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidPermissionsException + * @return bool + */ + protected function areValidPermissionsForParents(Share $share) { + $parents = $this->getParents($share); + if (!empty($parents)) { + // Combine parent share's permissions to see if the share exceeds those permissions + $permissions = $share->getPermissions(); + $parentPermissions = $this->getTotalPermissions($parents); + if ($permissions & ~$parentPermissions) { + throw new InvalidPermissionsException( + 'The permissions exceeds the parent shares\' permissions' + ); + } + } + return true; + } + + /** + * Check if a share's expiration time is valid with respect to the parent shares + * @param \OC\Share\Share $share + * @throws \OC\Share\Exception\InvalidExpirationTimeException + * @return bool + */ + protected function isValidExpirationTimeForParents(Share $share) { + $parents = $this->getParents($share); + if (!empty($parents)) { + // Check if time is later than the latest parent share expiration time + $time = $share->getExpirationTime(); + $latestParentTime = $this->getLatestExpirationTime($parents); + if (isset($latestParentTime) && (!isset($time) || $time > $latestParentTime)) { + throw new InvalidExpirationTimeException( + 'The expiration time exceeds the parent shares\' expiration times' + ); + } + } + return true; + } + + /** + * Update the reshares of a share to ensure consistency of permissions and expiration time + * @param \OC\Share\Share $share + * @param \OC\Share\Share $oldShare + * + * The share has to be updated in the share backend before this is called + * + */ + protected function updateReshares(Share $share, Share $oldShare) { + $id = $share->getId(); + // Add this share to reshares' parent ids if share permission is added + if ($share->isSharable() && !$oldShare->isSharable()) { + $reshares = $this->searchForReshares($share); + foreach ($reshares as $reshare) { + $reshare->addParentId($id); + $this->update($reshare); + } + // There is no need to continue the update process if share permission was just added, + // because the reshares (if any) must have a different parent share that already + // dictated the possible permissions and expiration time + } else if ($oldShare->isSharable()) { + $reshares = $this->getReshares($share); + foreach ($reshares as $reshare) { + $parentIds = $reshare->getParentIds(); + if (count($parentIds) === 1) { + if (!$share->isSharable()) { + // If the reshare has no other parents, it has to be unshared + $this->unshare($reshare); + continue; + } + $parents = array($share); + } else { + if (!$share->isSharable()) { + $reshare->removeParentId($id); + } + $parents = $this->getParents($reshare); + } + // Adjust permissions and expiration time as necessary for them to be valid + $parentPermissions = $this->getTotalPermissions($parents); + $latestParentTime = $this->getLatestExpirationTime($parents); + $resharePermissions = $reshare->getPermissions(); + if (~$parentPermissions & $resharePermissions) { + $resharePermissions &= $parentPermissions; + $reshare->setPermissions($resharePermissions); + } + $reshareTime = $reshare->getExpirationTime(); + if (isset($latestParentTime) + && (!isset($reshareTime) || $latestParentTime < $reshareTime) + ) { + $reshare->setExpirationTime($latestParentTime); + } + $properties = $reshare->getUpdatedProperties(); + if (!empty($properties)) { + $this->update($reshare); + } + } + } + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/common.php b/lib/share/sharetype/common.php new file mode 100644 index 000000000000..8e19f788c99a --- /dev/null +++ b/lib/share/sharetype/common.php @@ -0,0 +1,199 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; + +abstract class Common implements IShareType { + + protected $itemType; + protected $shareFactory; + protected $table; + protected $parentTable; + + /** + * The constructor + * @param string $itemType + * @param ShareFactory $shareFactory + */ + public function __construct($itemType, ShareFactory $shareFactory) { + $this->itemType = $itemType; + $this->shareFactory = $shareFactory; + $this->table = '`*PREFIX*shares`'; + $this->parentsTable = '`*PREFIX*shares_parents`'; + } + + public function share(Share $share) { + $properties = array( + 'shareTypeId', + 'shareOwner', + 'shareWith', + 'itemType', + 'itemSource', + 'itemTarget', + 'itemOwner', + 'permissions', + 'expirationTime', + 'shareTime', + ); + $columms = array(); + $params = array(); + // Retrieve share's properties to store in the database + foreach ($properties as $property) { + $columns[] = Share::propertyToColumn($property); + $getter = 'get'.ucfirst($property); + $params[] = $share->$getter(); + } + $placeholders = join(',', array_fill(0, count($columns), '?')); + $columns = join(',', $columns); + $sql = 'INSERT INTO '.$this->table.' ('.$columns.') VALUES ('.$placeholders.')'; + $result = \OC_DB::executeAudited($sql, $params); + $id = (int)\OC_DB::insertid(); + $share->setId($id); + $share->resetUpdatedProperties(); + $this->setParentIds($share); + return $share; + } + + public function unshare(Share $share) { + $sql = 'DELETE FROM '.$this->table.' WHERE `id` = ?'; + $params = array($share->getId()); + \OC_DB::executeAudited($sql, $params); + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, $params); + } + + public function update(Share $share) { + $columns = ''; + $params = array(); + $properties = $share->getUpdatedProperties(); + foreach ($properties as $property => $updated) { + $columns .= Share::propertyToColumn($property).' = ?, '; + $getter = 'get'.ucfirst($property); + $params[] = $share->$getter(); + } + $columns = rtrim($columns, ', '); + $params[] = $share->getId(); + $sql = 'UPDATE '.$this->table.' SET '.$columns.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, $params); + } + + /** + * Update the share's parent references in the database + * @param Share $share + */ + public function setParentIds(Share $share) { + $id = $share->getId(); + $parentIds = $share->getParentIds(); + $cachedParentIds = $this->getParentIds($id); + // Compare current parent ids with those in the database + // to determine which need to be added and removed + $newParentIds = array_diff($parentIds, $cachedParentIds); + $oldParentIds = array_diff($cachedParentIds, $parentIds); + if (!empty($newParentIds)) { + $sql = 'INSERT INTO '.$this->parentsTable.' (`id`, `parent_id`) VALUES (?, ?)'; + foreach ($newParentIds as $parentId) { + \OC_DB::executeAudited($sql, array($id, $parentId)); + } + } + if (!empty($oldParentIds)) { + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE `id` = ? AND `parent_id` = ?'; + foreach ($oldParentIds as $parentId) { + \OC_DB::executeAudited($sql, array($id, $parentId)); + } + } + } + + public function getShares(array $filter, $limit, $offset) { + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + unset($filter['isShareWithUser']); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + $joins = ''; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= 'LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= ' '.$this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + $shares = array(); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + // TODO Come up with an alternative so we don't have to do an additional query + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share->resetUpdatedProperties(); + $shares[] = $share; + } + return $shares; + } + + public function clear() { + $sql = 'DELETE FROM '.$this->table.' WHERE `share_type_id` = ?'; + \OC_DB::executeAudited($sql, array($this->getId())); + $sql = 'DELETE FROM '.$this->parentsTable.' WHERE '.$this->parentsTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + /** + * Get the parent ids for a share based on id + * @param int $id + * @return array + */ + protected function getParentIds($id) { + $sql = 'SELECT `parent_id` FROM '.$this->parentsTable.' WHERE `id` = ?'; + $result = \OC_DB::executeAudited($sql, array($id)); + $parentIds = array(); + while ($row = $result->fetchRow()) { + $parentIds[] = $row['parent_id']; + } + return $parentIds; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/group.php b/lib/share/sharetype/group.php new file mode 100644 index 000000000000..d23f8b9eee60 --- /dev/null +++ b/lib/share/sharetype/group.php @@ -0,0 +1,319 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\Group\Manager as GroupManager; +use OC\User\Manager as UserManager; + +/** + * Controller for shares between a user and a group + */ +class Group extends Common { + + protected $itemTargetMachine; + protected $groupManager; + protected $userManager; + protected $groupTable; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\Group\Manager $groupManager + * @param \OC\User\Manager $userManager + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, GroupManager $groupManager, UserManager $userManager + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->groupManager = $groupManager; + $this->userManager = $userManager; + $this->groupsTable = '`*PREFIX*shares_groups`'; + } + + public function getId() { + return 'group'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + $shareWith = $share->getShareWith(); + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + if (!$this->groupManager->groupExists($shareWith)) { + throw new InvalidShareException('The group shared with does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $group = $this->groupManager->get($shareWith); + $shareOwnerUser = $this->userManager->get($shareOwner); + if (!$group->inGroup($shareOwnerUser)) { + throw new InvalidShareException( + 'The share owner is not in the group shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + } + } + return true; + } + + public function share(Share $share) { + $groupItemTarget = $this->itemTargetMachine->getItemTarget($share); + $share->setItemTarget($groupItemTarget); + $share = parent::share($share); + if ($share) { + $shareOwner = $share->getShareOwner(); + $userItemTargets = array(); + $group = $this->groupManager->get($share->getShareWith()); + foreach ($group->getUsers() as $user) { + $uid = $user->getUID(); + if ($uid !== $shareOwner) { + $userItemTarget = $this->itemTargetMachine->getItemTarget($share, $user); + if ($userItemTarget !== $groupItemTarget) { + $userItemTargets[$uid] = $userItemTarget; + } + } + } + $itemTargets = array($share->getItemTarget()); + $itemTargets['users'] = $userItemTargets; + $share->setItemTarget($itemTargets); + if (!empty($userItemTargets)) { + $this->setItemTarget($share); + } + $share = $this->setShareDisplayNames($share); + } + return $share; + } + + /** + * Update the share's item targets in the database + * @param \OC\Share\Share $share + * + * Group shares can have different item targets for users in the group + * and are stored in a separate table + * + */ + public function setItemTarget(Share $share) { + $id = $share->getId(); + $itemTargets = $share->getItemTarget(); + if (is_array($itemTargets)) { + $groupItemTarget = reset($itemTargets); + } else { + $groupItemTarget = $itemTargets; + } + if (isset($itemTargets['users'])) { + $userItemTargets = $itemTargets['users']; + } else { + $userItemTargets = array(); + } + $sql = 'UPDATE '.$this->table.' SET `item_target` = ? WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($groupItemTarget, $id)); + $cachedItemTargets = $this->getUserItemTargets($id); + // Compare current item targets with those in the database + // to determine which need to be added and removed + $newItemTargets = array_diff_assoc($userItemTargets, $cachedItemTargets); + $oldItemTargets = array_diff_assoc($cachedItemTargets, $userItemTargets); + if (!empty($newItemTargets)) { + $sql = 'INSERT INTO '.$this->groupsTable.' (`id`, `uid`, `item_target`) + VALUES (?, ?, ?)'; + foreach ($newItemTargets as $uid => $itemTarget) { + \OC_DB::executeAudited($sql, array($id, $uid, $itemTarget)); + } + } + if (!empty($oldItemTargets)) { + $sql = 'DELETE FROM '.$this->groupsTable.' WHERE `id` = ? AND `uid` = ? AND '. + '`item_target` = ?'; + foreach ($oldItemTargets as $uid => $itemTarget) { + \OC_DB::executeAudited($sql, array($id, $uid, $itemTarget)); + } + } + } + + public function getShares(array $filter, $limit, $offset) { + $shares = array(); + $isShareWithUser = false; + // The isShareWithUser parameter allows the shareWith parameter to be a user rather than + // a group in the filter + if (!isset($filter['isShareWithUser']) || $filter['isShareWithUser'] === false) { + unset($filter['isShareWithUser']); + $shares = parent::getShares($filter, $limit, $offset); + } else { + unset($filter['isShareWithUser']); + $isShareWithUser = true; + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'shareWith') { + $groups = array(); + $shareWithUser = $this->userManager->get($value); + if ($shareWithUser) { + $groups = $this->groupManager->getUserGroups($shareWithUser); + } + if (empty($groups)) { + // The user has no groups, no group shares are possible + return array(); + } else { + // Find shares with the user's groups, but exclude the shares they own + foreach ($groups as $group) { + $params[] = $group->getGID(); + } + $placeholders = join(',', array_fill(0, count($groups), '?')); + $where .= $column.' IN ('.$placeholders.') AND `share_owner` != ? AND '; + $params[] = $value; + } + } else { + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + $joins = ''; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= 'LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= $this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share->resetUpdatedProperties(); + $shares[] = $share; + } + } + foreach ($shares as &$share) { + $userItemTargets = $this->getUserItemTargets($share->getId()); + if (!empty($userItemTargets)) { + if ($isShareWithUser && isset($userItemTargets[$filter['shareWith']])) { + $share->setItemTarget($userItemTargets[$filter['shareWith']]); + } else { + $itemTargets = array($share->getItemTarget()); + $itemTargets['users'] = $userItemTargets; + $share->setItemTarget($itemTargets); + } + } + $share = $this->setShareDisplayNames($share); + } + return $shares; + } + + public function clear() { + parent::clear(); + $sql = 'DELETE FROM '.$this->groupsTable.' WHERE '.$this->groupsTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + $shareWiths = array(); + $groups = array(); + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + if ($shareOwnerUser) { + $groups = $this->groupManager->search($pattern); + $userGroups = $this->groupManager->getUserGroups($shareOwnerUser); + $groups = array_uintersect($groups, $userGroups, function($group1, $group2) { + return strcmp($group1->getGID(), $group2->getGID()); + }); + $groups = array_slice($groups, $offset, $limit); + } + } else { + $groups = $this->groupManager->search($pattern, $limit, $offset); + } + foreach ($groups as $group) { + $shareWiths[] = array( + 'shareWith' => $group->getGID(), + 'shareWithDisplayName' => $group->getGID().' (group)', + ); + } + return $shareWiths; + } + + /** + * Get the item target machine + * @return \OC\Share\ItemTargetMachine + */ + public function getItemTargetMachine() { + return $this->itemTargetMachine; + } + + /** + * Get the unique item targets for the users of a group share specified by the id + * @param int $id + * @return array + */ + protected function getUserItemTargets($id) { + $sql = 'SELECT `uid`, `item_target` FROM '.$this->groupsTable.' WHERE `id` = ?'; + $result = \OC_DB::executeAudited($sql, array($id)); + $itemTargets = array(); + while ($row = $result->fetchRow()) { + $itemTargets[$row['uid']] = $row['item_target']; + } + return $itemTargets; + } + + /** + * Set the display names for the share owner and share with + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayNames(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $share->setShareWithDisplayName($share->getShareWith(). ' (group)'); + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/groupwatcher.php b/lib/share/sharetype/groupwatcher.php new file mode 100644 index 000000000000..d6f24f2bdd14 --- /dev/null +++ b/lib/share/sharetype/groupwatcher.php @@ -0,0 +1,163 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\ShareManager; +use OC\Share\Exception\InvalidShareException; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\Group\Manager; + +/** + * Listen to group events that require updating shares + */ +class GroupWatcher { + + private $shareManager; + + public function __construct(ShareManager $shareManager, Manager $groupManager) { + $this->shareManager = $shareManager; + $groupManager->listen('\OC\Group', 'postDelete', array($this, 'onGroupDeleted')); + $groupManager->listen('\OC\Group', 'postAddUser', array($this, 'onUserAddedToGroup')); + $groupManager->listen('\OC\Group', 'postRemoveUser', + array($this, 'onUserRemovedFromGroup') + ); + } + + /** + * Unshare all shares to the deleted group + * @param \OC\Group\Group $group + */ + public function onGroupDeleted(\OC\Group\Group $group) { + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $this->shareManager->unshare($share); + } + foreach ($group->getUsers() as $user) { + $this->revalidateShares($user); + } + } + + /** + * Check if the added user requires unique item targets for the group shares + * @param \OC\Group\Group $group + * @param \OC\User\User $user + */ + public function onUserAddedToGroup(\OC\Group\Group $group, \OC\User\User $user) { + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $shareBackend = $this->shareManager->getShareBackend($share->getItemType()); + $shareType = $shareBackend->getShareType($share->getShareTypeId()); + $itemTargetMachine = $shareType->getItemTargetMachine(); + $itemTargets = $share->getItemTarget(); + $groupItemTarget = reset($itemTargets); + $userItemTarget = $itemTargetMachine->getItemTarget($share, $user); + if ($userItemTarget !== $groupItemTarget) { + $itemTargets['users'][$user->getUID()] = $userItemTarget; + $share->setItemTarget($itemTargets); + $this->shareManager->update($share); + } + } + } + + /** + * Unshare all reshares of the group share that were reshared by the removed user + * @param \OC\Group\Group $group + * @param \OC\User\User $user + */ + public function onUserRemovedFromGroup(\OC\Group\Group $group, \OC\User\User $user) { + $uid = $user->getUID(); + $shares = $this->getGroupShares($group); + foreach ($shares as $share) { + $reshares = $this->shareManager->getReshares($share); + foreach ($reshares as $reshare) { + if ($reshare->getShareOwner() === $uid) { + $this->shareManager->unshare($reshare); + } + } + $itemTargets = $share->getItemTarget(); + // Remove unique item target if set for removed user + if (isset($itemTargets['user'][$uid])) { + unset($itemTargets['user'][$uid]); + $share->setItemTarget($itemTargets); + $this->shareManager->update($share); + } + } + $this->revalidateShares($user); + } + + /** + * Get all group shares + * @param \OC\Group\Group $group + * @return \OC\Share\Share[] + */ + protected function getGroupShares(\OC\Group\Group $group) { + $gid = $group->getGID(); + $shares = array(); + $filter = array( + 'shareTypeId' => 'group', + 'shareWith' => $gid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + try { + $itemType = $shareBackend->getItemType(); + $shares = array_merge($shares, $this->shareManager->getShares($itemType, $filter)); + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + return $shares; + } + + /** + * Revalidate the sharing conditions for a user's shares if the groups only policy is set + * @param \OC\User\User $user + */ + protected function revalidateShares(\OC\User\User $user) { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $uid = $user->getUID(); + $filter = array( + 'shareTypeId' => 'user', + 'shareOwner' => $uid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + try { + $shareType = $shareBackend->getShareType('user'); + $itemType = $shareBackend->getItemType(); + $shares = $this->shareManager->getShares($itemType, $filter); + foreach ($shares as $share) { + try { + $shareType->isValidShare($share); + } catch (InvalidShareException $exception) { + $this->shareManager->unshare($share); + } + } + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + } + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/isharetype.php b/lib/share/sharetype/isharetype.php new file mode 100644 index 000000000000..c2d660870725 --- /dev/null +++ b/lib/share/sharetype/isharetype.php @@ -0,0 +1,88 @@ +. +*/ + +namespace OC\Share\ShareType; + +use OC\Share\Share; + +/** + * Interface for a controller of shares + */ +interface IShareType { + + /** + * Get the identifier for this share type + * @return string id + */ + public function getId(); + + /** + * Check if this share is valid for this share type + * @param \OC\Share\Share $share + * @return bool + * @throws \OC\Share\Exception\InvalidShareException + */ + public function isValidShare(Share $share); + + /** + * Insert the share into the database + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + public function share(Share $share); + + /** + * Remove the share from the database + * @param \OC\Share\Share $share + */ + public function unshare(Share $share); + + /** + * Update the share's properties in the database + * @param \OC\Share\Share $share + */ + public function update(Share $share); + + /** + * Get the shares with the specified parameters for this share type + * @param array $filter A key => value array of share properties + * @param int $limit + * @param int $offset + * @return \OC\Share\Share[] + */ + public function getShares(array $filter, $limit, $offset); + + /** + * Remove all shares of this share type in the database + */ + public function clear(); + + /** + * Search for potential people of this share type to share with based on the given pattern + * @param string $shareOwner + * @param string $pattern + * @param int $limit + * @param int $offset + * @return array + */ + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset); + +} \ No newline at end of file diff --git a/lib/share/sharetype/link.php b/lib/share/sharetype/link.php new file mode 100644 index 000000000000..feaf67649331 --- /dev/null +++ b/lib/share/sharetype/link.php @@ -0,0 +1,209 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\AdvancedShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\User\Manager; +use PasswordHash; + +/** + * Controller for shares between a user and the public via a link + */ +class Link extends Common { + + protected $itemTargetMachine; + protected $userManager; + protected $tokenMachine; + protected $hasher; + protected $linkTable; + + const TOKEN_LENGTH = 32; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\User\Manager $userManager + * @param \OC\Share\ShareType\TokenMachine $tokenMachine + * @param \PasswordHash $hasher + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, Manager $userManager, TokenMachine $tokenMachine, + PasswordHash $hasher + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->userManager = $userManager; + $this->tokenMachine = $tokenMachine; + $this->hasher = $hasher; + $this->linksTable = '`*PREFIX*shares_links`'; + } + + public function getId() { + return 'link'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + if ($sharingPolicy !== 'yes') { + throw new InvalidShareException('The admin has disabled sharing via links'); + } + return true; + } + + public function share(Share $share) { + $share->setItemTarget($this->itemTargetMachine->getItemTarget($share)); + $share = parent::share($share); + if ($share) { + $token = $this->tokenMachine->getToken(self::TOKEN_LENGTH); + $password = $this->hashPassword($share->getPassword()); + $sql = 'INSERT INTO '.$this->linksTable.' (`id`, `token`, `password`)'. + 'VALUES (?, ?, ?)'; + \OC_DB::executeAudited($sql, array($share->getId(), $token, $password)); + $share->setToken($token); + $share->setPassword($password); + $share = $this->setShareDisplayName($share); + } + return $share; + } + + public function unshare(Share $share) { + parent::unshare($share); + $sql = 'DELETE FROM '.$this->linksTable.' WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($share->getId())); + } + + /** + * Update the share's password for the link in the database + * @param \OC\Share\Share $share + */ + public function setPassword(Share $share) { + $password = $this->hashPassword($share->getPassword()); + $sql = 'UPDATE '.$this->linksTable.' SET `password` = ? WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($password, $share->getId())); + } + + public function getShares(array $filter, $limit, $offset) { + if (isset($filter['shareWith'])) { + // Links are not associated with a person + return array(); + } else { + $defaults = array( + 'shareTypeId' => $this->getId(), + 'itemType' => $this->itemType, + ); + $filter = array_merge($defaults, $filter); + $where = ''; + $params = array(); + // Build the WHERE clause + foreach ($filter as $property => $value) { + $column = Share::propertyToColumn($property); + if ($property === 'id') { + $column = $this->table.'.'.$column; + } else if ($property === 'parentId') { + $column = $this->parentsTable.'.'.$column; + } else if ($property === 'token' || $property === 'password') { + $column = $this->linksTable.'.'.$column; + } + $where .= $column.' = ? AND '; + $params[] = $value; + } + $where = rtrim($where, ' AND '); + $columns = $this->table.'.*'; + // JOIN with the links table + $joins = 'JOIN '.$this->linksTable.' ON '. + $this->table.'.`id` = '.$this->linksTable.'.`id`'; + $columns .= ', '.$this->linksTable.'.*'; + if (isset($filter['parentId'])) { + // LEFT JOIN with the parents table + $joins .= ' LEFT JOIN '.$this->parentsTable.' ON '. + $this->table.'.`id` = '.$this->parentsTable.'.`id`'; + } + if ($this->shareFactory instanceof AdvancedShareFactory) { + // Add the JOINs for the app + $joins .= ' '.$this->shareFactory->getJoins(); + $columns .= ', '.$this->shareFactory->getColumns(); + } + $sql = 'SELECT '.$columns.' FROM '.$this->table.' '.$joins.' WHERE '.$where; + $query = \OC_DB::prepare($sql, $limit, $offset); + $result = \OC_DB::executeAudited($query, $params); + $shares = array(); + while ($row = $result->fetchRow()) { + $share = $this->shareFactory->mapToShare($row); + $parentIds = $this->getParentIds($share->getId()); + $share->setParentIds($parentIds); + $share = $this->setShareDisplayName($share); + $shares[] = $share; + } + return $shares; + } + } + + public function clear() { + parent::clear(); + $sql = 'DELETE FROM '.$this->linksTable.' WHERE '.$this->linksTable.'.`id` NOT IN '. + ' (SELECT `id` FROM '.$this->table.')'; + \OC_DB::executeAudited($sql); + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + // Links are not associated with a person + return array(); + } + + /** + * Hash the link password to store in the database + * @param string|null $password + * @return string|null + */ + protected function hashPassword($password) { + if (isset($password)) { + $salt = \OC_Config::getValue('passwordsalt', ''); + $password = $this->hasher->HashPassword($password.$salt); + } + return $password; + } + + /** + * Set the display name for the share owner + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayName(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/tokenmachine.php b/lib/share/sharetype/tokenmachine.php new file mode 100644 index 000000000000..2442c1575d40 --- /dev/null +++ b/lib/share/sharetype/tokenmachine.php @@ -0,0 +1,33 @@ +. + */ + +namespace OC\Share\ShareType; + +/** + * Temporary class - waiting for an injectable Util + */ +class TokenMachine { + + public function getToken($length) { + return \OC_Util::generate_random_bytes($length); + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/user.php b/lib/share/sharetype/user.php new file mode 100644 index 000000000000..304d1d27b830 --- /dev/null +++ b/lib/share/sharetype/user.php @@ -0,0 +1,167 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; +use OC\Share\ItemTargetMachine; +use OC\Share\Exception\InvalidShareException; +use OC\User\Manager as UserManager; +use OC\Group\Manager as GroupManager; + +/** + * Controller for shares between two users + */ +class User extends Common { + + protected $itemTargetMachine; + protected $userManager; + protected $groupManager; + + /** + * The constructor + * @param string $itemType + * @param \OC\Share\ShareFactory $shareFactory + * @param \OC\Share\ItemTargetMachine $itemTargetMachine + * @param \OC\User\Manager $userManager + * @param \OC\Group\Manager $groupManager + */ + public function __construct($itemType, ShareFactory $shareFactory, + ItemTargetMachine $itemTargetMachine, UserManager $userManager, GroupManager $groupManager + ) { + parent::__construct($itemType, $shareFactory); + $this->itemTargetMachine = $itemTargetMachine; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + } + + public function getId() { + return 'user'; + } + + public function isValidShare(Share $share) { + $shareOwner = $share->getShareOwner(); + $shareWith = $share->getShareWith(); + if ($shareOwner === $shareWith) { + throw new InvalidShareException('The share owner is the user shared with'); + } + if (!$this->userManager->userExists($shareOwner)) { + throw new InvalidShareException('The share owner does not exist'); + } + if (!$this->userManager->userExists($shareWith)) { + throw new InvalidShareException('The user shared with does not exist'); + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + $shareWithUser = $this->userManager->get($shareWith); + $groups = $this->groupManager->getUserGroups($shareOwnerUser); + foreach ($groups as $group) { + if ($group->inGroup($shareWithUser)) { + return true; + } + } + throw new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + } + return true; + } + + public function share(Share $share) { + $user = $this->userManager->get($share->getShareWith()); + $share->setItemTarget($this->itemTargetMachine->getItemTarget($share, $user)); + $share = parent::share($share); + if ($share) { + $share = $this->setShareDisplayNames($share); + } + return $share; + } + + public function getShares(array $filter, $limit, $offset) { + $shares = parent::getShares($filter, $limit, $offset); + foreach ($shares as &$share) { + $share = $this->setShareDisplayNames($share); + } + return $shares; + } + + public function searchForPotentialShareWiths($shareOwner, $pattern, $limit, $offset) { + $shareWiths = array(); + $users = array(); + $fakeLimit = $limit; + if (isset($fakeLimit)) { + // Just in case the share owner shows up in the list of users + $fakeLimit++; + if (isset($offset)) { + // Using the offset in the user manager and group calls may cause unexpected + // returns because this function filters the users. The limit and offset are + // applied manually after all possible users are retrieved and filtered + $fakeLimit += $offset; + } + } + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + if ($sharingPolicy === 'groups_only') { + $shareOwnerUser = $this->userManager->get($shareOwner); + if ($shareOwnerUser) { + $groups = $this->groupManager->getUserGroups($shareOwnerUser); + foreach ($groups as $group) { + $result = $group->searchDisplayName($pattern, $fakeLimit, null); + $users = array_merge($users, $result); + } + } + } else { + $users = $this->userManager->searchDisplayName($pattern, $fakeLimit, null); + } + foreach ($users as $user) { + $uid = $user->getUID(); + if ($uid !== $shareOwner && !isset($shareWiths[$uid])) { + $shareWiths[$uid] = array( + 'shareWith' => $uid, + 'shareWithDisplayName' => $user->getDisplayName(), + ); + } + } + $shareWiths = array_slice($shareWiths, $offset, $limit); + return array_values($shareWiths); + } + + /** + * Set the display names for the share owner and share with + * @param \OC\Share\Share $share + * @return \OC\Share\Share + */ + protected function setShareDisplayNames(Share $share) { + $shareOwnerUser = $this->userManager->get($share->getShareOwner()); + if ($shareOwnerUser) { + $share->setShareOwnerDisplayName($shareOwnerUser->getDisplayName()); + } + $shareWithUser = $this->userManager->get($share->getShareWith()); + if ($shareWithUser) { + $share->setShareWithDisplayName($shareWithUser->getDisplayName()); + } + $share->resetUpdatedProperties(); + return $share; + } + +} \ No newline at end of file diff --git a/lib/share/sharetype/userwatcher.php b/lib/share/sharetype/userwatcher.php new file mode 100644 index 000000000000..0ef9bc255082 --- /dev/null +++ b/lib/share/sharetype/userwatcher.php @@ -0,0 +1,71 @@ +. + */ + +namespace OC\Share\ShareType; + +use OC\Share\ShareManager; +use OC\Share\Exception\ShareTypeDoesNotExistException; +use OC\User\Manager; + +/** + * Listen to user events that require updating shares + */ +class UserWatcher { + + private $shareManager; + + public function __construct(ShareManager $shareManager, Manager $userManager) { + $this->shareManager = $shareManager; + $userManager->listen('\OC\User', 'postDelete', array($this, 'onUserDeleted')); + } + + /** + * Unshare all shares to and owned by the deleted user + * @param \OC\User\User $user + */ + public function onUserDeleted(\OC\User\User $user) { + $uid = $user->getUID(); + $shares = array(); + $filterShareOwner = array( + 'shareOwner' => $uid, + ); + $filterShareWith = array( + 'shareTypeId' => 'user', + 'shareWith' => $uid, + ); + $shareBackends = $this->shareManager->getShareBackends(); + foreach ($shareBackends as $shareBackend) { + $itemType = $shareBackend->getItemType(); + $shareOwnerShares = $this->shareManager->getShares($itemType, $filterShareOwner); + $shares = array_merge($shares, $shareOwnerShares); + try { + $shareWithShares = $this->shareManager->getShares($itemType, $filterShareWith); + $shares = array_merge($shares, $shareWithShares); + } catch (ShareTypeDoesNotExistException $exception) { + // Do nothing + } + } + foreach ($shares as $share) { + $this->shareManager->unshare($share); + } + } + +} \ No newline at end of file diff --git a/lib/share/timemachine.php b/lib/share/timemachine.php new file mode 100644 index 000000000000..b2d8b4930437 --- /dev/null +++ b/lib/share/timemachine.php @@ -0,0 +1,37 @@ +. + */ + +namespace OC\Share; + +/** + * Mock calls to time() + */ +class TimeMachine { + + /** + * Get time() + * @return int + */ + public function getTime() { + return time(); + } + +} \ No newline at end of file diff --git a/lib/share/updater.php b/lib/share/updater.php new file mode 100644 index 000000000000..56070dfb4eb2 --- /dev/null +++ b/lib/share/updater.php @@ -0,0 +1,230 @@ +. + */ + +namespace OC\Share; + +use OC\Log; +use DateTime; +use Exception; + +/** + * Update old shares to Share API 2.0.0 + */ +class Updater { + + private $shareManager; + private $logger; + private $shareTypeGroupUserUnique = 2; + private $updatedShares; + + /** + * The constructor + * @param \OC\Share\ShareManager $shareManager + * @param \OC\Log $logger + */ + public function __construct(ShareManager $shareManager, Log $logger) { + $this->shareManager = $shareManager; + $this->logger = $logger; + } + + /** + * Update all shares + */ + public function updateAll() { + $version = \OC_Appconfig::getValue('core', 'shareAPIVersion', '1.0.0'); + if ($version === '1.0.0') { + $this->logger->notice('Started update of shares to Share API 2'); + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_type`, '. + '`item_source`, `file_source`, `permissions`, `stime`, `expiration`, `token` '. + 'FROM `*PREFIX*share`'; + $result = \OC_DB::executeAudited($sql); + while ($row = $result->fetchRow()) { + $this->updateShare($row); + } + $this->logger->notice('Finished update of shares to Share API 2'); + \OC_Appconfig::setValue('core', 'shareAPIVersion', '2.0.0'); + } + } + + /** + * Update the share specified by the $row + * @param array $row + * @return \OC\Share\Share | false + */ + protected function updateShare($row) { + $id = $row['id']; + if (!isset($this->updatedShares[$id])) { + $share = $this->getShareFromRow($row); + if ($share) { + // Ensure all parent shares are already updated + $latestParentTime = false; + $parentRows = $this->searchForParents($row); + foreach ($parentRows as $parentRow) { + $parent = $this->updateShare($parentRow); + if ($parent) { + // Find the latest parent expiration time + $time = $parent->getExpirationTime(); + if (!isset($time)) { + $latestParentTime = null; + } else if ($latestParentTime === false + || (isset($latestParentTime) && $time > $latestParentTime) + ) { + $latestParentTime = $time; + } + } + } + if ($latestParentTime !== false) { + $time = $share->getExpirationTime(); + // Truncate expiration time as necessary + if (!isset($time) || $latestParentTime < $time) { + $share->setExpirationTime($latestParentTime); + } + } + try { + $token = $share->getToken(); + $password = $share->getPassword(); + $share = $this->shareManager->share($share); + $this->updatedShares[$id] = $share; + if (isset($token)) { + // Reuse old token and password + $sql = 'UPDATE `*PREFIX*shares_links` SET `token` = ?, `password` = ? '. + 'WHERE `id` = ?'; + \OC_DB::executeAudited($sql, array($token, $password, $share->getId())); + } + } catch (Exception $exception) { + $this->logger->error('Unable to update share with id: '.$id.' '. + 'because of an exception: '.$exception->getMessage(), + array('app' => 'core') + ); + $this->updatedShares[$id] = false; + } + } else { + $this->updatedShares[$id] = false; + } + } + return $this->updatedShares[$id]; + } + + /** + * Translate an old database row to a share + * @param array $row + * @return \OC\Share\Share | null + */ + protected function getShareFromRow($row) { + $id = $row['id']; + unset($row['id']); + unset($row['parent']); + settype($row['share_type'], 'int'); + // The group user unique target shares can be ignored because the unique targets + // are handled by the group share type + if ($row['share_type'] !== $this->shareTypeGroupUserUnique) { + if ($row['share_type'] === \OCP\Share::SHARE_TYPE_USER) { + $row['share_type_id'] = 'user'; + } else if ($row['share_type'] === \OCP\Share::SHARE_TYPE_GROUP) { + $row['share_type_id'] = 'group'; + } else if ($row['share_type'] === \OCP\Share::SHARE_TYPE_LINK) { + $row['share_type_id'] = 'link'; + if (isset($row['share_with'])) { + $row['password'] = $row['share_with']; + unset($row['share_with']); + } + } else { + $this->logger->error('Unable to update share with id: '.$id.' '. + 'because the share type is unknown', array('app' => 'core') + ); + return null; + } + unset($row['share_type']); + $row['share_owner'] = $row['uid_owner']; + unset($row['uid_owner']); + if (isset($row['file_source'])) { + $row['item_source'] = $row['file_source']; + } + unset($row['file_source']); + settype($row['permissions'], 'int'); + if ($row['item_type'] === 'file') { + // Remove Create permission from files + $row['permissions'] &= ~4; + } + $row['share_time'] = $row['stime']; + unset($row['stime']); + settype($row['share_time'], 'int'); + if (isset($row['expiration'])) { + $date = new DateTime($row['expiration']); + $row['expiration_time'] = $date->getTimeStamp(); + } + unset($row['expiration']); + return Share::fromRow($row); + } + return null; + } + + /** + * Search for parent shares of a share + * @param array $row + * @return array + */ + protected function searchForParents($row) { + $parentRows = array(); + if (isset($row['parent'])) { + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, `item_type`, '. + '`item_source`, `file_source`, `permissions`, `stime`, `expiration`, `token` '. + 'FROM `*PREFIX*share` WHERE `id` = ?'; + $parentRow = \OC_DB::executeAudited($sql, array($row['parent']), 1)->fetchRow(); + if ($parentRow) { + $parentRows[] = $parentRow; + // Look for shares similar to parent + $sql = 'SELECT `id`, `parent`, `share_type`, `share_with`, `uid_owner`, '. + '`item_type`, `item_source`, `file_source`, `permissions`, `stime`, '. + '`expiration`, `token` FROM `*PREFIX*share`'; + $params = array(); + if ($parentRow['item_type'] === 'file') { + $sql .= ' WHERE `item_type` IN (?, ?)'; + $params[] = 'file'; + $params[] = 'folder'; + } else { + $sql .= ' WHERE `item_type` = ?'; + $params[] = $parentRow['item_type']; + } + // A trick for reshared files inside shared folders is that the item_source + // is always the file id of the shared folder, thanks to bad programming + $sql .= ' AND `item_source` = ?'; + $params[] = $parentRow['item_source']; + $sql .= ' AND `share_type` IN (?, ?)'; + $params[] = \OCP\Share::SHARE_TYPE_USER; + $params[] = \OCP\Share::SHARE_TYPE_GROUP; + $user = $row['uid_owner']; + $userAndGroups = array_merge(array($user), \OC_Group::getUserGroups($user)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $sql .= ' AND `share_with` IN ('.$placeholders.')'; + $params = array_merge($params, $userAndGroups); + $sql .= ' AND `id` != ?'; + $params[] = $row['parent']; + $result = \OC_DB::executeAudited($sql, $params); + while ($row = $result->fetchRow()) { + $parentRows[] = $row; + } + } + } + return $parentRows; + } + +} \ No newline at end of file diff --git a/lib/updater.php b/lib/updater.php index df7332a96a97..6fb26bb5544c 100644 --- a/lib/updater.php +++ b/lib/updater.php @@ -115,6 +115,15 @@ public function upgrade() { \OC_App::checkAppsRequirements(); // load all apps to also upgrade enabled apps \OC_App::loadApps(); + try { + $shareManager = \OCP\Share::getShareManager(); + if ($shareManager) { + $updater = new \OC\Share\Updater($shareManager, $this->log); + $updater->updateAll(); + } + } catch (\Exception $exception) { + $this->emit('\OC\Updater', 'failure', array($exception->getMessage())); + } \OC_Config::setValue('maintenance', false); $this->emit('\OC\Updater', 'maintenanceEnd'); } diff --git a/lib/util.php b/lib/util.php index e03667b07944..6195178701b5 100755 --- a/lib/util.php +++ b/lib/util.php @@ -98,7 +98,7 @@ public static function tearDownFS() { public static function getVersion() { // hint: We only can count up. Reset minor/patchlevel when // updating major/minor version number. - return array(5, 80, 05); + return array(5, 80, 07); } /** diff --git a/tests/lib/share/backend.php b/tests/lib/share/backend.php deleted file mode 100644 index 2f6c84678ffc..000000000000 --- a/tests/lib/share/backend.php +++ /dev/null @@ -1,71 +0,0 @@ -. -*/ - -OC::$CLASSPATH['OCP\Share_Backend']='lib/public/share.php'; - -class Test_Share_Backend implements OCP\Share_Backend { - - const FORMAT_SOURCE = 0; - const FORMAT_TARGET = 1; - const FORMAT_PERMISSIONS = 2; - - private $testItem1 = 'test.txt'; - private $testItem2 = 'share.txt'; - - public function isValidSource($itemSource, $uidOwner) { - if ($itemSource == $this->testItem1 || $itemSource == $this->testItem2) { - return true; - } - } - - public function generateTarget($itemSource, $shareWith, $exclude = null) { - // Always make target be test.txt to cause conflicts - $target = 'test.txt'; - if (isset($exclude)) { - $pos = strrpos($target, '.'); - $name = substr($target, 0, $pos); - $ext = substr($target, $pos); - $append = ''; - $i = 1; - while (in_array($name.$append.$ext, $exclude)) { - $append = $i; - $i++; - } - $target = $name.$append.$ext; - } - return $target; - } - - public function formatItems($items, $format, $parameters = null) { - $testItems = array(); - foreach ($items as $item) { - if ($format == self::FORMAT_SOURCE) { - $testItems[] = $item['item_source']; - } else if ($format == self::FORMAT_TARGET) { - $testItems[] = $item['item_target']; - } else if ($format == self::FORMAT_PERMISSIONS) { - $testItems[] = $item['permissions']; - } - } - return $testItems; - } - -} diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index e7d441a7e780..57bf79786dbc 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -1,413 +1,208 @@ . + * ownCloud + * + * @author Alessandro Cosentino + * @author Bernhard Posselt + * @author Michael Gapczynski + * @copyright 2012 Alessandro Cosentino cosenal@gmail.com + * @copyright 2012 Bernhard Posselt nukeawhale@gmail.com + * @copyright 2013 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * */ -class Test_Share extends PHPUnit_Framework_TestCase { - - protected $itemType; - protected $userBackend; - protected $user1; - protected $user2; - protected $groupBackend; - protected $group1; - protected $group2; - protected $resharing; - - public function setUp() { - OC_User::clearBackends(); - OC_User::useBackend('dummy'); - $this->user1 = uniqid('user1_'); - $this->user2 = uniqid('user2_'); - $this->user3 = uniqid('user3_'); - $this->user4 = uniqid('user4_'); - OC_User::createUser($this->user1, 'pass'); - OC_User::createUser($this->user2, 'pass'); - OC_User::createUser($this->user3, 'pass'); - OC_User::createUser($this->user4, 'pass'); - OC_User::setUserId($this->user1); - OC_Group::clearBackends(); - OC_Group::useBackend(new OC_Group_Dummy); - $this->group1 = uniqid('group_'); - $this->group2 = uniqid('group_'); - OC_Group::createGroup($this->group1); - OC_Group::createGroup($this->group2); - OC_Group::addToGroup($this->user1, $this->group1); - OC_Group::addToGroup($this->user2, $this->group1); - OC_Group::addToGroup($this->user3, $this->group1); - OC_Group::addToGroup($this->user2, $this->group2); - OC_Group::addToGroup($this->user4, $this->group2); - OCP\Share::registerBackend('test', 'Test_Share_Backend'); - OC_Hook::clear('OCP\\Share'); - OC::registerShareHooks(); - $this->resharing = OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); - OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); +namespace Test\Share; + +use OC\Share\Share; + +class ShareTest extends \PHPUnit_Framework_TestCase { + + public function testToAPI() { + $share = new Share(); + $share->setId(1); + $share->setParentIds(array(2, 3)); + $share->setShareTypeId('link'); + $share->setShareOwner('MTGap'); + $share->setShareOwnerDisplayName('Michael Gapczynski'); + $share->setItemType('test'); + $share->setItemSource(21); + $share->setItemTarget('Test'); + $share->setItemOwner('MTGap'); + $share->setPermissions(31); + $share->setExpirationTime(1370884025); + $share->setShareTime(1370883025); + $share->setToken('3akdsfj32as23kjsdf'); + $share->setPassword('4AJak34jkDajl4aa42Jmkapw'); + $this->assertEquals(array( + 'id' => 1, + 'parentIds' => array(2, 3), + 'shareTypeId' => 'link', + 'shareOwner' => 'MTGap', + 'shareOwnerDisplayName' => 'Michael Gapczynski', + 'shareWith' => null, + 'shareWithDisplayName' => null, + 'itemType' => 'test', + 'itemSource' => 21, + 'itemTarget' => 'Test', + 'itemOwner' => 'MTGap', + 'permissions' => 31, + 'expirationTime' => 1370884025, + 'shareTime' => 1370883025, + 'token' => '3akdsfj32as23kjsdf', + 'password' => '4AJak34jkDajl4aa42Jmkapw', + ), $share->toAPI()); } - public function tearDown() { - $query = OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `item_type` = ?'); - $query->execute(array('test')); - OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $this->resharing); - } - - public function testShareInvalidShareType() { - $message = 'Share type foobar is not valid for test.txt'; - try { - OCP\Share::shareItem('test', 'test.txt', 'foobar', $this->user2, OCP\PERMISSION_READ); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - } + public function testIsCreatable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_CREATE); + $this->assertTrue($share->isCreatable()); - public function testInvalidItemType() { - $message = 'Sharing backend for foobar not found'; - try { - OCP\Share::shareItem('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemsSharedWith('foobar'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemSharedWith('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemSharedWithBySource('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::getItemShared('foobar', 'test.txt'); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::unshare('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - try { - OCP\Share::setPermissions('foobar', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_UPDATE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isCreatable()); } - public function testShareWithUser() { - // Invalid shares - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the item owner'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $message = 'Sharing test.txt failed, because the user foobar does not exist'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, 'foobar', OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $message = 'Sharing foobar failed, because the sharing backend for test could not find its source'; - try { - OCP\Share::shareItem('test', 'foobar', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Valid share - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - - // Attempt to share again - OC_User::setUserId($this->user1); - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->user2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Attempt to share back - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - - // Attempt reshare without share permission - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because resharing is not allowed'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Owner grants share and update permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_SHARE)); - - // Attempt reshare with escalated permissions - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the permissions exceed permissions granted to '.$this->user2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Valid reshare - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - - // Attempt to escalate permissions - OC_User::setUserId($this->user2); - $message = 'Setting permissions for test.txt failed, because the permissions exceed permissions granted to '.$this->user2; - try { - OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - - // Remove update permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_SHARE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertEquals(array(OCP\PERMISSION_READ), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - - // Remove share permission - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); - - // Reshare again, and then have owner unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::setPermissions('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - OC_User::setUserId($this->user2); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); - OC_User::setUserId($this->user3); - $this->assertFalse(OCP\Share::getItemSharedWith('test', 'test.txt')); + public function testIsReadable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertTrue($share->isReadable()); - // Attempt target conflict - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user3); - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ)); - - OC_User::setUserId($this->user2); - $to_test = OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET); - $this->assertEquals(2, count($to_test)); - $this->assertTrue(in_array('test.txt', $to_test)); - $this->assertTrue(in_array('test1.txt', $to_test)); - - // Remove user - OC_User::setUserId($this->user1); - OC_User::deleteUser($this->user1); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test1.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + $share->setPermissions(0); + $this->assertFalse($share->isReadable()); } - public function testShareWithGroup() { - // Invalid shares - $message = 'Sharing test.txt failed, because the group foobar does not exist'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, 'foobar', OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - $policy = OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); - OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); - $message = 'Sharing test.txt failed, because '.$this->user1.' is not a member of the group '.$this->group2; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group2, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } - OC_Appconfig::setValue('core', 'shareapi_share_policy', $policy); + public function testIsUpdatable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_UPDATE); + $this->assertTrue($share->isUpdatable()); - // Valid share - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ)); - $this->assertEquals(array('test.txt'), OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE)); - - // Attempt to share again - OC_User::setUserId($this->user1); - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isUpdatable()); + } - // Attempt to share back to owner of group share - OC_User::setUserId($this->user2); - $message = 'Sharing test.txt failed, because the user '.$this->user1.' is the original sharer'; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + public function testIsDeletable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_DELETE); + $this->assertTrue($share->isDeletable()); - // Attempt to share back to group - $message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isDeletable()); + } - // Attempt to share back to member of group - $message ='Sharing test.txt failed, because this item is already shared with '.$this->user3; - try { - OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user3, OCP\PERMISSION_READ); - $this->fail('Exception was expected: '.$message); - } catch (Exception $exception) { - $this->assertEquals($message, $exception->getMessage()); - } + public function testIsSharable() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_SHARE); + $this->assertTrue($share->isSharable()); - // Unshare - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); + $share->setPermissions(\OCP\PERMISSION_READ); + $this->assertFalse($share->isSharable()); + } - // Valid share with same person - user then group - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE | OCP\PERMISSION_SHARE)); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE | OCP\PERMISSION_SHARE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user3); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testAddParentId() { + $share = new Share(); + $share->addParentId(1); + $this->assertEquals(array(1), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + $share->addParentId(3); + $this->assertEquals(array(1, 3), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Valid reshare - OC_User::setUserId($this->user2); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\PERMISSION_READ)); - OC_User::setUserId($this->user4); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testRemoveParentId() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $share->removeParentId(3); + $this->assertEquals(array(1), $share->getParentIds()); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Unshare from user only - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testFromParams() { + $params = array( + 'shareTypeId' => 'group', + 'shareOwner' => 'MTGap', + 'shareWith' => 'friends', + 'itemType' => 'test', + 'itemSource' => 23, + ); + $share = Share::fromParams($params); + $this->assertEquals($params['shareTypeId'], $share->getShareTypeId()); + $this->assertEquals($params['shareOwner'], $share->getShareOwner()); + $this->assertEquals($params['shareWith'], $share->getShareWith()); + $this->assertEquals($params['itemType'], $share->getItemType()); + $this->assertEquals($params['itemSource'], $share->getItemSource()); + $this->assertTrue($share instanceof Share); + } - // Valid share with same person - group then user - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\PERMISSION_READ | OCP\PERMISSION_DELETE)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testFromRow() { + $row = array( + 'id' => '1', + 'shareTypeId' => 'group', + 'shareOwner' => 'MTGap', + 'shareWith' => 'friends', + 'itemType' => 'test', + 'itemSource' => '23', + ); + $share = Share::fromRow($row); + $this->assertEquals(1, $share->getId()); + $this->assertEquals($row['shareTypeId'], $share->getShareTypeId()); + $this->assertEquals($row['shareOwner'], $share->getShareOwner()); + $this->assertEquals($row['shareWith'], $share->getShareWith()); + $this->assertEquals($row['itemType'], $share->getItemType()); + $this->assertEquals($row['itemSource'], $share->getItemSource()); + $this->assertTrue($share instanceof Share); + } - // Unshare from group only - OC_User::setUserId($this->user1); - $this->assertTrue(OCP\Share::unshare('test', 'test.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1)); - OC_User::setUserId($this->user2); - $this->assertEquals(array(OCP\PERMISSION_READ | OCP\PERMISSION_DELETE), OCP\Share::getItemSharedWith('test', 'test.txt', Test_Share_Backend::FORMAT_PERMISSIONS)); + public function testGetSetId() { + $id = 3; + $share = new Share(); + $share->setId($id); + $this->assertEquals($id, $share->getId()); + } - // Attempt user specific target conflict - OC_User::setUserId($this->user3); - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_GROUP, $this->group1, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user2); - $to_test = OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET); - $this->assertEquals(2, count($to_test)); - $this->assertTrue(in_array('test.txt', $to_test)); - $this->assertTrue(in_array('test1.txt', $to_test)); + public function testSetterMarksPropertyUpdated() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $this->assertContains('parentIds', $share->getUpdatedProperties()); + } - // Valid reshare - $this->assertTrue(OCP\Share::shareItem('test', 'share.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\PERMISSION_READ | OCP\PERMISSION_SHARE)); - OC_User::setUserId($this->user4); - $this->assertEquals(array('test1.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testResetUpdatedProperties() { + $share = new Share(); + $share->setParentIds(array(1, 3)); + $share->resetUpdatedProperties(); + $this->assertEmpty($share->getUpdatedProperties()); + } - // Remove user from group - OC_Group::removeFromGroup($this->user2, $this->group1); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testColumnToPropertyNoReplacement() { + $column = 'my'; + $this->assertEquals('my', Share::columnToProperty($column)); + } - // Add user to group - OC_Group::addToGroup($this->user4, $this->group1); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testColumnToProperty() { + $column = 'my_property'; + $this->assertEquals('myProperty', Share::columnToProperty($column)); + } - // Unshare from self - $this->assertTrue(OCP\Share::unshareFromSelf('test', 'test.txt')); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user2); - $this->assertEquals(array('test.txt'), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); + public function testPropertyToColumnNoReplacement() { + $property = 'my'; + $this->assertEquals('`my`', Share::propertyToColumn($property)); + } - // Remove group - OC_Group::deleteGroup($this->group1); - OC_User::setUserId($this->user4); - $this->assertEquals(array(), OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET)); - OC_User::setUserId($this->user3); - $this->assertEquals(array(), OCP\Share::getItemsShared('test')); + public function testPropertyToColumn() { + $property = 'myProperty'; + $this->assertEquals('`my_property`', Share::propertyToColumn($property)); } -} +} \ No newline at end of file diff --git a/tests/lib/share/sharebackend.php b/tests/lib/share/sharebackend.php new file mode 100644 index 000000000000..0f5a9f6041c3 --- /dev/null +++ b/tests/lib/share/sharebackend.php @@ -0,0 +1,571 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; + +class TestShareBackend extends \OC\Share\ShareBackend { + + private $isValidItem; + private $events; + + public function getItemType() { + return 'test'; + } + + public function getItemTypePlural() { + return 'tests'; + } + + protected function isValidItem(Share $share) { + if ($this->isValidItem === false) { + throw new \OC\Share\Exception\InvalidItemException( + 'The item does not exist' + ); + } else { + return true; + } + } + + public function setIsValidItem($isValidItem) { + $this->isValidItem = $isValidItem; + } + + public function pAreValidPermissions(Share $share) { + return parent::areValidPermissions($share); + } + + public function pIsValidExpirationTime(Share $share) { + return parent::isValidExpirationTime($share); + } + + protected function emit($scope, $method, $arguments = array()) { + // Store the emitted events so they can be retrieved later for assertions + $this->events[] = array($scope, $method, $arguments); + } + + public function getEvents() { + return $this->events; + } + +} + +class ShareBackend extends \PHPUnit_Framework_TestCase { + + protected $timeMachine; + protected $user; + protected $group; + protected $link; + protected $shareBackend; + + protected function setUp() { + $this->timeMachine = $this->getMockBuilder('\OC\Share\TimeMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->timeMachine->expects($this->any()) + ->method('getTime') + ->will($this->returnValue(1370797580)); + $this->user = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user->expects($this->any()) + ->method('getId') + ->will($this->returnValue('user')); + $this->group = $this->getMockBuilder('\OC\Share\ShareType\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->group->expects($this->any()) + ->method('getId') + ->will($this->returnValue('group')); + $this->link = $this->getMockBuilder('\OC\Share\ShareType\Link') + ->disableOriginalConstructor() + ->getMock(); + $this->link->expects($this->any()) + ->method('getId') + ->will($this->returnValue('link')); + $this->shareBackend = new TestShareBackend($this->timeMachine, + array($this->user, $this->group, $this->link) + ); + } + + /** + * Get a fake valid item source for testing isValidItem + * @param Share $share + * @return any + */ + protected function getValidItemSource(Share $share) { + $this->shareBackend->setIsValidItem(true); + return 1; + } + + protected function getExpectedItemTarget(Share $share) {} + + public function testGetShareTypes() { + $shareTypes = $this->shareBackend->getShareTypes(); + $this->assertCount(3, $shareTypes); + $this->assertArrayHasKey('user', $shareTypes); + $this->assertEquals($this->user, $shareTypes['user']); + $this->assertArrayHasKey('group', $shareTypes); + $this->assertEquals($this->group, $shareTypes['group']); + $this->assertArrayHasKey('link', $shareTypes); + $this->assertEquals($this->link, $shareTypes['link']); + } + + public function testGetShareType() { + $this->assertEquals($this->group, $this->shareBackend->getShareType('group')); + } + + public function testGetShareTypeDoesNotExist() { + $this->setExpectedException('\OC\Share\Exception\ShareTypeDoesNotExistException', + 'A share type does not exist with the id foo' + ); + $this->shareBackend->getShareType('foo'); + } + + public function testShare() { + $mtgap = 'MTGap'; + $icewind = 'Icewind'; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($icewind); + $share->setItemOwner($mtgap); + $share->setPermissions(31); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setShareTime(1370797580); + $userMap = array( + array(array('shareOwner' => $mtgap, 'shareWith' => $icewind, 'itemSource' => $item), + 1, null, array() + ) + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->never()) + ->method('getShares'); + $this->user->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share)) + ->will($this->returnValue(true)); + $this->user->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->user->expects($this->never()) + ->method('update'); + $this->user->expects($this->never()) + ->method('setParentIds'); + $this->group->expects($this->never()) + ->method('update'); + $this->group->expects($this->never()) + ->method('setParentIds'); + $this->link->expects($this->never()) + ->method('update'); + $this->link->expects($this->never()) + ->method('setParentIds'); + $share = $this->shareBackend->share($share); + $this->assertEquals($sharedShare, $share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preShare', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postShare', array($sharedShare)), $events); + } + + public function testShareWithInvalidItem() { + $this->shareBackend->setIsValidItem(false); + + $share = new Share(); + $this->setExpectedException('\OC\Share\Exception\InvalidItemException', + 'The item does not exist' + ); + $this->shareBackend->share($share); + } + + public function testShareAgain() { + $butonic = 'butonic'; + $bartv = 'bartv'; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($butonic); + $share->setShareWith($bartv); + $share->setItemOwner($butonic); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $map = array( + array(array('shareOwner' => $butonic, 'shareWith' => $bartv, 'itemSource' => $item), + 1, null, array($share) + ) + ); + $this->user->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share already exists' + ); + $this->shareBackend->share($share); + } + + public function testShareWithInvalidShare() { + $mtgap = 'MTGap'; + $icewind = 'Icewind'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($icewind); + $share->setItemOwner($mtgap); + $share->setPermissions(31); + $item = $this->getValidItemSource($share); + $share->setItemSource($item); + $userMap = array( + array(array('shareOwner' => $mtgap, 'shareWith' => $icewind, 'itemSource' => $item), + 1, null, array() + ), + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->never()) + ->method('getShares'); + $this->user->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share)) + ->will($this->returnValue(false)); + $this->assertFalse($this->shareBackend->share($share)); + } + + public function testUnshare() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('user'); + $this->user->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share)); + $this->shareBackend->unshare($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUnshare', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUnshare', array($share)), $events); + } + + public function testUpdate() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setItemTarget('New Test Item'); + $this->user->expects($this->once()) + ->method('update') + ->with($this->equalTo($share)); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithCustomUpdateMethod() { + $share = new Share(); + $share->setId(3); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setParentIds(array(1, 2)); + $this->user->expects($this->never()) + ->method('update'); + $this->user->expects($this->once()) + ->method('setParentIds') + ->with($this->equalTo($share)); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithNoChanges() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + $this->link->expects($this->never()) + ->method('update'); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testUpdateWithValidPermissionsAndExpirationTime() { + $share = new Share(); + $share->setShareTypeId('user'); + $share->resetUpdatedProperties(); + $share->setPermissions(1); + $share->setExpirationTime(1370801180); + $this->user->expects($this->once()) + ->method('update'); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertContains(array('\OC\Share', 'preUpdate', array($share)), $events); + $this->assertContains(array('\OC\Share', 'postUpdate', array($share)), $events); + } + + public function testUpdateWithInvalidPermissions() { + $share = new Share(); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + $share->setPermissions(0); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testUpdateWithInvalidExpirationTime() { + $share = new Share(); + $share->setShareTypeId('link'); + $share->resetUpdatedProperties(); + // 59 minutes 59 seconds in the future + $share->setExpirationTime(1370801179); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not at least 1 hour in the future' + ); + $this->shareBackend->update($share); + $events = $this->shareBackend->getEvents(); + $this->assertEmpty($events); + } + + public function testGetShares() { + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('user'); + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('group'); + $userMap = array( + array(array(), null, null, array($share1)) + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array(), null, null, array($share2)) + ); + $this->group->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $linkMap = array( + array(array(), null, null, array()) + ); + $this->link->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(); + $this->assertCount(2, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + } + + public function testGetSharesWithFilter() { + $item = 1; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('user'); + $share1->setItemSource($item); + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('group'); + $share2->setItemSource($item); + $userMap = array( + array(array('itemSource' => $item), null, null, array($share1)), + array(array('id' => 2), null, null, array()) + ); + $this->user->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array('itemSource' => $item), null, null, array($share2)), + array(array('id' => 2), null, null, array($share2)) + ); + $this->group->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $linkMap = array( + array(array('itemSource' => $item), null, null, array()), + array(array('id' => 2), null, null, array()) + ); + $this->link->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(array('itemSource' => $item)); + $this->assertCount(2, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share2, $shares); + + $share = $this->shareBackend->getShares(array('id' => 2)); + $this->assertCount(1, $share); + $this->assertContains($share2, $share); + } + + public function testGetSharesWithShareTypeId() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('link'); + $linkMap = array( + array(array(), null, null, array($share)) + ); + $this->user->expects($this->never()) + ->method('getShares'); + $this->group->expects($this->never()) + ->method('getShares'); + $this->link->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($linkMap)); + $shares = $this->shareBackend->getShares(array('shareTypeId' => 'link')); + $this->assertCount(1, $shares); + $this->assertContains($share, $shares); + } + + public function testGetSharesWithLimitOffset() { + $share2 = new Share(); + $share2->setId(2); + $share2->setShareTypeId('user'); + $share3 = new Share(); + $share3->setId(3); + $share3->setShareTypeId('user'); + $share4 = new Share(); + $share4->setId(4); + $share4->setShareTypeId('group'); + $userMap = array( + array(array(), 3, 1, array($share2, $share3)), + ); + $this->user->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($userMap)); + $groupMap = array( + array(array(), 1, 0, array($share4)), + ); + $this->group->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($groupMap)); + $this->link->expects($this->never()) + ->method('getShares'); + $shares = $this->shareBackend->getShares(array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($share2, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + } + + public function testIsExpired() { + // No expiration time + $share = new Share(); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // 1 second in the past + $share->setExpirationTime(1370797579); + $this->assertTrue($this->shareBackend->isExpired($share)); + + // 1 second in the future + $share->setExpirationTime(1370797581); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // Default expiration time set for 2 hours from share time + $share->setExpirationTime(null); + $defaultTime = \OC_Appconfig::getValue('core', 'shareapi_expiration_time', 0); + \OC_Appconfig::setValue('core', 'shareapi_expiration_time', 7200); + // 1 hour 59 minutes 59 seconds in the past + $share->setShareTime(1370790381); + $this->assertFalse($this->shareBackend->isExpired($share)); + + // 2 hours 1 second in the past + $share->setShareTime(1370790379); + $this->assertTrue($this->shareBackend->isExpired($share)); + \OC_Appconfig::setValue('core', 'shareapi_expiration_time', $defaultTime); + } + + public function testAreValidPermissions() { + $share = new Share(); + $share->setPermissions(31); + $this->assertTrue($this->shareBackend->pAreValidPermissions($share)); + } + + public function testAreValidPermissionsWithString() { + $share = new Share(); + $share->setPermissions('31'); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not an integer' + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testAreValidPermissionsWithZero() { + $share = new Share(); + $share->setPermissions(0); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testAreValidPermissionsWithOneMoreThanAll() { + $share = new Share(); + $share->setPermissions(\OCP\PERMISSION_ALL + 1); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions are not in the range of 1 to '.\OCP\PERMISSION_ALL + ); + $this->shareBackend->pAreValidPermissions($share); + } + + public function testIsValidExpirationTime() { + // No expiration time + $share = new Share(); + $this->assertTrue($this->shareBackend->pIsValidExpirationTime($share)); + + // 1 hour in the future + $share->setExpirationTime(1370801180); + $this->assertTrue($this->shareBackend->pIsValidExpirationTime($share)); + } + + public function testIsValidExpirationTimeWithString() { + $share = new Share(); + $share->setExpirationTime('1370797580'); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not an integer' + ); + $this->shareBackend->pIsValidExpirationTime($share); + } + + public function testIsValidExpirationTimeNot1HourInTheFuture() { + $share = new Share(); + // 59 minutes 59 seconds in the future + $share->setExpirationTime(1370801179); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time is not at least 1 hour in the future' + ); + $this->shareBackend->pIsValidExpirationTime($share); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharemanager.php b/tests/lib/share/sharemanager.php new file mode 100644 index 000000000000..8968c51296b1 --- /dev/null +++ b/tests/lib/share/sharemanager.php @@ -0,0 +1,1981 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; +use OC\Share\Exception\ShareTypeDoesNotExistException; + +class TestShareManager extends \OC\Share\ShareManager { + + public function pAreValidPermissionsForParents(Share $share) { + return parent::areValidPermissionsForParents($share); + } + + public function pIsValidExpirationTimeForParents(Share $share) { + return parent::isValidExpirationTimeForParents($share); + } + +} + +abstract class CollectionShareBackend extends \OC\Share\ShareBackend + implements \OC\Share\ICollectionShareBackend { + +} + +class ShareManager extends \PHPUnit_Framework_TestCase { + + private $shareBackend; + private $collectionShareBackend; + private $shareManager; + private $areCollectionsEnabled; + + /** + * In some of the tests we need to clone shares to mock the share stored in the database and to + * confirm that the property was updated correctly + */ + + protected function setUp() { + // Found workaround for mocks of abstract classes with concrete functions here: + // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/95 + $this->shareBackend = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test')); + $this->shareBackend->expects($this->any()) + ->method('getItemTypePlural') + ->will($this->returnValue('tests')); + $this->shareBackend->expects($this->any()) + ->method('isValidItem') + ->will($this->returnValue(true)); + $this->areCollectionsEnabled = false; + $this->collectionShareBackend = $this->getMockBuilder('\Test\Share\CollectionShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\Test\Share\CollectionShareBackend')) + ->getMockForAbstractClass(); + $this->collectionShareBackend->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('testCollection')); + $this->collectionShareBackend->expects($this->any()) + ->method('getItemTypePlural') + ->will($this->returnValue('testCollections')); + $this->collectionShareBackend->expects($this->any()) + ->method('isValidItem') + ->will($this->returnValue(true)); + $this->collectionShareBackend->expects($this->any()) + ->method('getChildrenItemTypes') + ->will($this->returnCallback(array($this, 'getChildrenItemTypesMock'))); + $this->shareManager = new TestShareManager(); + $this->shareManager->registerShareBackend($this->shareBackend); + $this->shareManager->registerShareBackend($this->collectionShareBackend); + } + + public function getChildrenItemTypesMock() { + if ($this->areCollectionsEnabled) { + return array('test'); + } else { + return array(); + } + } + + public function testGetShareBackends() { + $backends = $this->shareManager->getShareBackends(); + $this->assertCount(2, $backends); + $this->assertArrayHasKey('test', $backends); + $this->assertEquals($this->shareBackend, $backends['test']); + $this->assertArrayHasKey('testCollection', $backends); + $this->assertEquals($this->collectionShareBackend, $backends['testCollection']); + } + + public function testGetShareBackend() { + $this->setExpectedException('\OC\Share\Exception\ShareBackendDoesNotExistException', + 'A share backend does not exist for the item type foo' + ); + $this->shareManager->getShareBackend('foo'); + } + + public function testShareWithOneParent() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($jancborchardt); + $sharedShare->addParentId(1); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($jancborchardt); + $parent->setShareWith($danimo); + $parent->setItemOwner($jancborchardt); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + array(array('id' => 1), 1, null, array($parent)), + array(array('shareOwner' => $danimo, 'itemSource' => $item), null, null, + array($share) + ), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndResharingDisabled() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'no'); + + $mtgap = 'MTGap'; + $blizzz = 'Blizzz'; + $schiesbn = 'schiesbn'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($blizzz); + $share->setShareWith($schiesbn); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($mtgap); + $parent->setShareWith($blizzz); + $parent->setItemOwner($mtgap); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $blizzz, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->never()) + ->method('share'); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The admin has disabled resharing' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentWithoutSharePermission() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $blizzz = 'Blizzz'; + $schiesbn = 'schiesbn'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($blizzz); + $share->setShareWith($schiesbn); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($mtgap); + $parent->setShareWith($blizzz); + $parent->setItemOwner($mtgap); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(15); + $map = array( + array(array('shareWith' => $blizzz, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->never()) + ->method('share'); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The parent shares don\'t allow resharing' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndReshareBackToOwner() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setShareOwner($dragotin); + $parent->setShareWith($danimo); + $parent->setItemOwner($dragotin); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share can\'t reshare back to the share owner' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentAndReshareWithSamePeople() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $group = 'group'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('group'); + $share->setShareOwner($danimo); + $share->setShareWith($group); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('group'); + $parent->setShareOwner($jancborchardt); + $parent->setShareWith($group); + $parent->setItemOwner($jancborchardt); + $parent->setItemType('test'); + $parent->setItemSource($item); + $parent->setPermissions(31); + $map = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array($parent) + ), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The parent share has the same share with' + ); + $this->shareManager->share($share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithTwoParents() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $item = 1; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($anybodyelse); + $share->setShareWith($georgehrke); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $sharedShare->setParentIds(array(1, 2)); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($mtgap); + $parent1->setShareWith($anybodyelse); + $parent1->setItemOwner($mtgap); + $parent1->setItemType('test'); + $parent1->setItemSource($item); + $parent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setShareTypeId('group'); + $parent2->setShareOwner($mtgap); + $parent2->setShareWith($group); + $parent2->setItemOwner($mtgap); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(31); + $map = array( + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($parent1, $parent2) + ), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('shareOwner' => $anybodyelse, 'itemSource' => $item), null, null, + array($share) + ), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->shareBackend->expects($this->never()) + ->method('update'); + $share->resetUpdatedProperties(); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithExistingReshares() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $item = 1; + $share = new Share(); + $share->setId(3); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($anybodyelse); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->setExpirationTime(1370884027); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $duplicate = new Share(); + $duplicate->setId(1); + $duplicate->setShareTypeId('group'); + $duplicate->setShareOwner($mtgap); + $duplicate->setShareWith($group); + $duplicate->setItemOwner($mtgap); + $duplicate->setItemType('test'); + $duplicate->setItemSource($item); + $duplicate->setPermissions(31); + $duplicate->setExpirationTime(1370884026); + $reshare = new Share(); + $reshare->setId(2); + $reshare->setShareTypeId('user'); + $reshare->setShareOwner($anybodyelse); + $reshare->setShareWith($georgehrke); + $reshare->setItemType('test'); + $reshare->setItemSource($item); + $reshare->setPermissions(31); + $reshare->setExpirationTime(1370884026); + $reshare->addParentId(1); + $reshare->resetUpdatedProperties(); + $updatedReshare = clone $reshare; + $updatedReshare->addParentId(3); + $map = array( + array(array('shareWith' => $mtgap, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array()), + array(array('shareOwner' => $mtgap, 'itemSource' => $item), null, null, + array($share, $duplicate) + ), + array(array('parentId' => 1), null, null, array($reshare)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($duplicate, $share) + ), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($reshare)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->shareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare)); + $share->resetUpdatedProperties(); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($updatedReshare, $reshare); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareWithOneParentInCollection() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + $this->areCollectionsEnabled = true; + + $jancborchardt = 'jancborchardt'; + $danimo = 'danimo'; + $dragotin = 'dragotin'; + $group = 'group'; + $item = 1; + $collectionItem = 2; + $share = new Share(); + $share->setShareTypeId('user'); + $share->setShareOwner($danimo); + $share->setShareWith($dragotin); + $share->setItemType('test'); + $share->setItemSource($item); + $share->setPermissions(31); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($jancborchardt); + $sharedShare->addParentId(1); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($jancborchardt); + $parent1->setShareWith($danimo); + $parent1->setItemOwner($jancborchardt); + $parent1->setItemType('testCollection'); + $parent1->setItemSource($collectionItem); + $parent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setShareTypeId('user'); + $parent2->setShareOwner($jancborchardt); + $parent2->setShareWith($group); + $parent2->setItemOwner($jancborchardt); + $parent2->setItemType('testCollection'); + $parent2->setItemSource($collectionItem); + $parent2->setPermissions(31); + $sharesMap = array( + array(array('shareWith' => $danimo, 'isShareWithUser' => true, 'itemSource' => $item), + null, null, array()), + array(array('id' => 1), 1, null, array()), + array(array('shareOwner' => $danimo, 'itemSource' => $item), null, null, + array($share) + ), + array(array('parentId' => 2), null, null, array()), + ); + $collectionMap = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('parentId' => 2), null, null, array()), + ); + $childMap = array( + array($share, array($parent1, $parent2)), + ); + $expiredMap = array( + array($parent1, false), + array($parent2, true), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('searchForParentCollections') + ->will($this->returnValueMap($childMap)); + $this->collectionShareBackend->expects($this->any()) + ->method('isExpired') + ->will($this->returnValueMap($expiredMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->shareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testShareCollectionWithExistingReshares() { + $resharing = \OC_Appconfig::getValue('core', 'shareapi_allow_resharing', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', 'yes'); + $this->areCollectionsEnabled = true; + + $mtgap = 'MTGap'; + $group = 'group'; + $anybodyelse = 'AnybodyElse'; + $georgehrke = 'georgehrke'; + $dragotin = 'dragotin'; + $item = 1; + $collectionItem = 2; + $share = new Share(); + $share->setId(5); + $share->setShareTypeId('user'); + $share->setShareOwner($mtgap); + $share->setShareWith($anybodyelse); + $share->setItemType('testCollection'); + $share->setItemSource($collectionItem); + $share->setPermissions(31); + $share->setExpirationTime(1370884027); + $share->resetUpdatedProperties(); + $sharedShare = clone $share; + $sharedShare->setItemOwner($mtgap); + $duplicate = new Share(); + $duplicate->setId(1); + $duplicate->setShareTypeId('group'); + $duplicate->setShareOwner($mtgap); + $duplicate->setShareWith($group); + $duplicate->setItemOwner($mtgap); + $duplicate->setItemType('testCollection'); + $duplicate->setItemSource($collectionItem); + $duplicate->setPermissions(31); + $duplicate->setExpirationTime(1370884026); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setShareOwner($anybodyelse); + $reshare1->setShareWith($georgehrke); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setPermissions(31); + $reshare1->setExpirationTime(1370884026); + $reshare1->addParentId(1); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $updatedReshare1->addParentId(5); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setShareOwner($anybodyelse); + $reshare2->setItemType('testCollection'); + $reshare2->setItemSource($collectionItem); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884020); + $reshare2->addParentId(1); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $updatedReshare2->addParentId(5); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('link'); + $reshare3->setShareOwner($dragotin); + $reshare3->setItemType('testCollection'); + $reshare3->setItemSource($collectionItem); + $reshare3->setPermissions(31); + $reshare3->setExpirationTime(1370884020); + $reshare3->addParentId(1); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $collectionMap = array( + array(array('shareWith' => $mtgap, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array() + ), + array(array('shareOwner' => $mtgap, 'itemSource' => $collectionItem), null, null, + array($share, $duplicate) + ), + array(array('parentId' => 1), null, null, array($reshare2, $reshare3)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array($duplicate, $share) + ), + array(array('shareWith' => $dragotin, 'isShareWithUser' => true, + 'itemSource' => $collectionItem), null, null, array($duplicate) + ), + array(array('id' => 3, 'shareTypeId' => 'link'), 1, null, array($reshare2)), + ); + $sharesMap = array( + array(array('parentId' => 1), null, null, array($reshare1)), + array(array('shareWith' => $anybodyelse, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array() + ), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($reshare1)), + ); + $childMap = array( + array($reshare1, array($duplicate, $share)), + ); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('share') + ->with($this->equalTo($share)) + ->will($this->returnValue($share)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('searchForParentCollections') + ->will($this->returnValueMap($childMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare2)); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->shareBackend->expects($this->once()) + ->method('update') + ->with($this->equalTo($reshare1)); + $share = $this->shareManager->share($share); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + \OC_Appconfig::setValue('core', 'shareapi_allow_resharing', $resharing); + } + + public function testUnshareWithReshares() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('group'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('unshare'); + $this->shareBackend->expects($this->never()) + ->method('update'); + $this->shareManager->unshare($parent); + } + + public function testUnshareWithResharesAndTwoParents() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(2)) + ->method('update'); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($parent1)); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUnshareWithResharesAndTwoParentsAndOneParentDoesNotExpire() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(null); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->resetUpdatedProperties(); + $parent2->setExpirationTime(1370884027); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884027); + $updatedReshare2->removeParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(2)) + ->method('update'); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($parent1)); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUnshareCollectionWithResharesAndDifferentParents() { + $this->areCollectionsEnabled = true; + $item = 1; + $collectionItem = 2; + + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('testCollection'); + $parent1->setItemSource($collectionItem); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('testCollection'); + $parent2->setItemSource($collectionItem); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('user'); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('link'); + $reshare2->setItemType('testCollection'); + $reshare2->setItemSource($collectionItem); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('link'); + $reshare3->setItemType('testCollection'); + $reshare3->setItemSource($collectionItem); + $reshare3->setPermissions(31); + $reshare3->setExpirationTime(1370884020); + $reshare3->addParentId(1); + $reshare3->resetUpdatedProperties(); + $sharesMap = array( + array(array('parentId' => 1), null, null, array($reshare1)), + array(array('id' => 5), 1, null, array()), + array(array('id' => 2, 'shareTypeId' => 'user'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array()), + array(array('parentId' => 4), null, null, array()), + ); + $collectionMap = array( + array(array('parentId' => 1), null, null, array($reshare2, $reshare3)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 3, 'shareTypeId' => 'link'), 1, null, array($oldReshare2)), + array(array('id' => 4, 'shareTypeId' => 'link'), 1, null, array($reshare3)), + array(array('parentId' => 3), null, null, array()), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->collectionShareBackend->expects($this->once()) + ->method('update'); + $this->collectionShareBackend->expects($this->exactly(2)) + ->method('unshare'); + $this->shareManager->unshare($parent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + } + + public function testUpdateWithShareDoesNotExist() { + $share = new Share(); + $share->setId(1); + $share->setShareTypeId('group'); + $share->setItemType('test'); + $share->resetUpdatedProperties(); + $share->setExpirationTime(1370884024); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'group'), 1, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\ShareDoesNotExistException', + 'A share does not exist with the id 1' + ); + $this->shareManager->update($share); + } + + public function testUpdateResharesWithOneParent() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $parent->setExpirationTime(null); + $parent->resetUpdatedProperties(); + $updatedParent = clone $parent; + $updatedParent->setPermissions(19); + $updatedParent->setExpirationTime(1370884025); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(19); + $updatedReshare2->setExpirationTime(1370884025); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884026); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(3); + $updatedReshare3->setExpirationTime(1370884025); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithOneParentAndSharePermissionRemoved() { + $parent = new Share(); + $parent->setId(1); + $parent->setShareTypeId('user'); + $parent->setItemType('test'); + $parent->setPermissions(31); + $parent->resetUpdatedProperties(); + $updatedParent = clone $parent; + $updatedParent->setPermissions(15); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->addParentId(1); + $reshare1->setPermissions(19); + $reshare1->resetUpdatedProperties(); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->addParentId(1); + $reshare2->setPermissions(31); + $reshare2->resetUpdatedProperties(); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->resetUpdatedProperties(); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($reshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->shareBackend->expects($this->exactly(3)) + ->method('unshare'); + $this->shareManager->update($updatedParent); + } + + public function testUpdateResharesWithTwoParents() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(19); + $updatedParent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(23); + $updatedReshare2->setExpirationTime(1370884026); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884026); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(7); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($updatedParent1)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithTwoParentsAndSharePermissionRemoved() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(3); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884026); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $oldReshare1 = clone $reshare1; + $updatedReshare1 = clone $reshare1; + $updatedReshare1->setPermissions(17); + $updatedReshare1->removeParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(1370884027); + $reshare2->resetUpdatedProperties(); + $oldReshare2 = clone $reshare2; + $updatedReshare2 = clone $reshare2; + $updatedReshare2->setPermissions(21); + $updatedReshare2->setExpirationTime(1370884026); + $updatedReshare2->removeParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884027); + $reshare3->resetUpdatedProperties(); + $oldReshare3 = clone $reshare3; + $updatedReshare3 = clone $reshare3; + $updatedReshare3->setPermissions(5); + $updatedReshare3->setExpirationTime(1370884026); + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($updatedParent1)), + array(array('id' => 5), 1, null, array($updatedParent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($oldReshare1)), + array(array('parentId' => 2), null, null, array()), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($oldReshare2)), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 3), 1, null, array($updatedReshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($oldReshare3)), + array(array('parentId' => 4), null, null, array()), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(4)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesWithSharePermissionAdded() { + $tanghus = 'tanghus'; + $DeepDiver = 'DeepDiver'; + $tpn = 'tpn'; + $zimba12 = 'zimba12'; + $group1 = 'group1'; + $group2 = 'group2'; + $item = 1; + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setShareOwner($tanghus); + $parent1->setShareWith($DeepDiver); + $parent1->setItemType('test'); + $parent1->setItemSource($item); + $parent1->setPermissions(15); + $parent1->setExpirationTime(null); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setPermissions(31); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setShareOwner($tanghus); + $parent2->setShareWith($group1); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(21); + $parent2->setExpirationTime(1370884020); + $parent2->resetUpdatedProperties(); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setShareOwner($DeepDiver); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->addParentId(5); + $reshare1->setPermissions(17); + $reshare1->setExpirationTime(1370884019); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $updatedReshare1->addParentId(1); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setShareOwner($DeepDiver); + $reshare2->setShareWith($group2); + $reshare2->setItemType('test'); + $reshare2->setItemSource($item); + $reshare2->addParentId(5); + $reshare2->setPermissions(21); + $reshare2->setExpirationTime(1370884020); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $updatedReshare2->addParentId(1); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setShareOwner($tpn); + $reshare3->setShareWith($zimba12); + $reshare3->setItemType('test'); + $reshare3->setItemSource($item); + $reshare3->addParentId(3); + $reshare3->setPermissions(5); + $reshare3->setExpirationTime(1370884020); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('shareOwner' => $tanghus, 'itemSource' => $item), null, null, + array($parent1, $parent2) + ), + array(array('parentId' => 5), null, null, array($reshare1, $reshare2)), + array(array('shareWith' => $DeepDiver, 'isShareWithUser' => true, + 'itemSource' => $item), null, null, array($parent1, $parent2) + ), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->exactly(3)) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testUpdateResharesTwoParentsAndOneParentDoesNotExpire() { + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setShareTypeId('user'); + $parent1->setItemType('test'); + $parent1->setPermissions(31); + $parent1->setExpirationTime(1370884027); + $parent1->resetUpdatedProperties(); + $updatedParent1 = clone $parent1; + $updatedParent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setPermissions(21); + $parent2->setExpirationTime(null); + $updatedParent2 = clone $parent2; + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(19); + $reshare1->setExpirationTime(1370884024); + $reshare1->resetUpdatedProperties(); + $updatedReshare1 = clone $reshare1; + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(31); + $reshare2->setExpirationTime(null); + $reshare2->resetUpdatedProperties(); + $updatedReshare2 = clone $reshare2; + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->addParentId(3); + $reshare3->setPermissions(15); + $reshare3->setExpirationTime(1370884028); + $reshare3->resetUpdatedProperties(); + $updatedReshare3 = clone $reshare3; + $map = array( + array(array('id' => 1, 'shareTypeId' => 'user'), 1, null, array($parent1)), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 5), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->once()) + ->method('update'); + $this->shareManager->update($updatedParent1); + $this->assertEquals($updatedParent2, $parent2); + $this->assertEquals($updatedReshare1, $reshare1); + $this->assertEquals($updatedReshare2, $reshare2); + $this->assertEquals($updatedReshare3, $reshare3); + } + + public function testGetSharesWithExpiredShare() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $map = array( + array(array(), null, null, array($share1, $share2, $share3)), + array(array('parentId' => 2), null, null, array()), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(2)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test'); + $this->assertEquals(2, count($shares)); + $this->assertContains($share1, $shares); + $this->assertContains($share3, $shares); + } + + public function testGetSharesWithExpiredSharesAndLimit() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $share4 = new Share(); + $share4->setId(4); + $share4->setItemType('test'); + $map = array( + array(array(), 3, null, array($share1, $share2, $share3)), + array(array('parentId' => 2), null, null, array()), + array(array(), 1, 2, array($share4)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(2)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test', array(), 3); + $this->assertCount(3, $shares); + $this->assertContains($share1, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + } + + public function testGetSharesWithExpiredSharesAndLimitOffset() { + $share2 = new Share(); + $share2->setId(2); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setId(3); + $share3->setItemType('test'); + $share4 = new Share(); + $share4->setId(4); + $share4->setItemType('test'); + $share5 = new Share(); + $share5->setId(5); + $share5->setItemType('test'); + $map = array( + array(array(), 3, 1, array($share2, $share3, $share4)), + array(array('parentId' => 2), null, null, array()), + array(array(), 1, 3, array($share5)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->at(1)) + ->method('isExpired') + ->will($this->returnValue(true)); + $this->shareBackend->expects($this->once()) + ->method('unshare') + ->with($this->equalTo($share2)); + $shares = $this->shareManager->getShares('test', array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($share3, $shares); + $this->assertContains($share4, $shares); + $this->assertContains($share5, $shares); + } + + public function testGetShareById() { + $share = new Share(); + $share->setId(1); + $share->setItemType('testCollection'); + $share->setShareTypeId('link'); + $collectionMap = array( + array(array('id' => 1, 'shareTypeId' => 'link'), 1, null, array($share)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->throwException(new ShareTypeDoesNotExistException( + 'No share type found matching id' + ))); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $this->assertEquals($share, $this->shareManager->getShareById(1, null, 'link')); + } + + public function testGetShareByIdWithMultipleSharesReturned() { + $share1 = new Share(); + $share1->setId(1); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setId(1); + $share2->setItemType('test'); + $map = array( + array(array('id' => 1), 1, null, array($share1, $share2)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\MultipleSharesReturnedException', + 'Multiple shares were returned for the id 1' + ); + $this->shareManager->getShareById(1, 'test'); + } + + public function testUnshareItem() { + $item = 1; + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setShareTypeId('user'); + $parent1->setItemSource($item); + $parent1->setPermissions(31); + $parent1->resetUpdatedProperties(); + $parent2 = new Share(); + $parent2->setId(5); + $parent2->setShareTypeId('group'); + $parent2->setItemType('test'); + $parent2->setItemSource($item); + $parent2->setPermissions(21); + $reshare1 = new Share(); + $reshare1->setId(2); + $reshare1->setShareTypeId('link'); + $reshare1->setItemType('test'); + $reshare1->setItemSource($item); + $reshare1->setParentIds(array(1, 5)); + $reshare1->setPermissions(17); + $reshare1->resetUpdatedProperties(); + $reshare2 = new Share(); + $reshare2->setId(3); + $reshare2->setShareTypeId('group'); + $reshare2->setItemType('test'); + $reshare2->setItemSource($item); + $reshare2->setParentIds(array(1, 5)); + $reshare2->setPermissions(21); + $reshare2->resetUpdatedProperties(); + $reshare3 = new Share(); + $reshare3->setId(4); + $reshare3->setShareTypeId('user'); + $reshare3->setItemType('test'); + $reshare3->setItemSource($item); + $reshare3->addParentId(3); + $reshare3->setPermissions(5); + $reshare3->resetUpdatedProperties(); + $map = array( + array(array('itemSource' => $item), null, null, + array($parent1, $parent2, $reshare1, $reshare2, $reshare3) + ), + array(array('parentId' => 1), null, null, array($reshare1, $reshare2)), + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 5), 1, null, array($parent2)), + array(array('id' => 2, 'shareTypeId' => 'link'), 1, null, array($reshare1)), + array(array('id' => 3, 'shareTypeId' => 'group'), 1, null, array($reshare2)), + array(array('parentId' => 5), null, null, array($reshare1, $reshare2)), + array(array('parentId' => 2), null, null, array()), + array(array('parentId' => 3), null, null, array($reshare3)), + array(array('id' => 5), 1, null, array($reshare2)), + array(array('id' => 4, 'shareTypeId' => 'user'), 1, null, array($reshare3)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareBackend->expects($this->any()) + ->method('unshare'); + $this->shareManager->unshareItem('test', $item); + } + + public function testGetReshares() { + $share1 = new Share(); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setItemType('test'); + $share3 = new Share(); + $share3->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $share1->addParentId(1); + $share2->addParentId(1); + $share3->addParentId(1); + $map = array( + array(array('parentId' => 1), null, null, array($share1, $share2, $share3)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $reshares = $this->shareManager->getReshares($parent); + $this->assertCount(3, $reshares); + $this->assertContains($share1, $reshares); + $this->assertContains($share2, $reshares); + $this->assertContains($share3, $reshares); + } + + public function testGetResharesWithNoReshares() { + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $map = array( + array(array('parentId' => 1), null, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertEmpty($this->shareManager->getReshares($parent)); + } + + public function testGetResharesInCollection() { + $this->areCollectionsEnabled = true; + + $share1 = new Share(); + $share1->setItemType('test'); + $share2 = new Share(); + $share2->setItemType('testCollection'); + $share3 = new Share(); + $share3->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('testCollection'); + $share1->addParentId(1); + $share2->addParentId(1); + $share3->addParentId(1); + $shareMap = array( + array(array('parentId' => 1), null, null, array($share1, $share3)), + ); + $collectionMap = array( + array(array('parentId' => 1), null, null, array($share2)), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($shareMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $reshares = $this->shareManager->getReshares($parent); + $this->assertCount(3, $reshares); + $this->assertContains($share1, $reshares); + $this->assertContains($share2, $reshares); + $this->assertContains($share3, $reshares); + } + + public function testGetParents() { + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent3 = new Share(); + $parent3->setId(3); + $parent3->setItemType('test'); + $share->setParentIds(array(1, 2, 3)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + array(array('id' => 3), 1, null, array($parent3)), + ); + $this->shareBackend->expects($this->exactly(3)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $parents = $this->shareManager->getParents($share); + $this->assertCount(3, $parents); + $this->assertContains($parent1, $parents); + $this->assertContains($parent2, $parents); + $this->assertContains($parent3, $parents); + } + + public function testGetParentsInCollection() { + $this->areCollectionsEnabled = true; + + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('testCollection'); + $share->setParentIds(array(1, 2)); + $sharesMap = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array()), + ); + $collectionMap = array( + array(array('id' => 1), 1, null, array()), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($sharesMap)); + $this->collectionShareBackend->expects($this->atLeastOnce()) + ->method('getShares') + ->will($this->returnValueMap($collectionMap)); + $parents = $this->shareManager->getParents($share); + $this->assertCount(2, $parents); + $this->assertContains($parent1, $parents); + $this->assertContains($parent2, $parents); + } + + public function testGetParentsWithNoParents() { + $share = new Share(); + $this->shareBackend->expects($this->never()) + ->method('getShares'); + $this->collectionShareBackend->expects($this->never()) + ->method('getShares'); + $this->assertEmpty($this->shareManager->getParents($share)); + } + + public function testGetParentsWithNotExistingParent() { + $share = new Share(); + $share->setItemType('test'); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array()), + ); + $this->shareBackend->expects($this->once()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\ShareDoesNotExistException', + 'A share does not exist with the id 1' + ); + $this->shareManager->getParents($share); + } + + public function testAreValidPermissionsWithOneParent() { + // Share and parent have all permissions + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setPermissions(31); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + + // Share permissions are only Read and parent permissions are only Read, Update, and Share + $share->setPermissions(1); + $parent->setPermissions(19); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + } + + public function testAreValidPermissionsWithOneParentAndShareExceedsPermissions() { + // Share has all permissions and parent permissions are only Read, Update, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setPermissions(19); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions exceeds the parent shares\' permissions' + ); + $this->shareManager->pAreValidPermissionsForParents($share); + } + + public function testAreValidPermissionsWithTwoParents() { + // Share has all permissions, 1 parent has only Read, Update, and Share + // The other parent has only Read, Create, Delete, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setPermissions(19); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setPermissions(29); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + + // Share and parent remove Create permission + $share->setPermissions(27); + $parent2->setPermissions(25); + $this->assertTrue($this->shareManager->pAreValidPermissionsForParents($share)); + } + + public function testAreValidPermissionsWithTwoParentsAndShareExceedsPermissions() { + // Share has all permissions, 1 parent has only Read, Update, and Share + // The other parent has only Read, Delete, and Share + $share = new Share(); + $share->setItemType('test'); + $share->setPermissions(31); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setPermissions(19); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setPermissions(25); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidPermissionsException', + 'The permissions exceeds the parent shares\' permissions' + ); + $this->shareManager->pAreValidPermissionsForParents($share); + } + + public function testIsValidExpirationTimeWithOneParent() { + // Share and parent have no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second before parent + $share->setExpirationTime(1370884024); + $parent->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + } + + public function testIsValidExpirationTimeWithOneParentExpiresAndShareDoesNotExpire() { + // Parent expires, share has no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setExpirationTime(1370884025); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithOneParentExpiresAndShareExpiresAfter() { + // Share expires 1 second after parent + $share = new Share(); + $share->setItemType('test'); + $share->setExpirationTime(1370884026); + $parent = new Share(); + $parent->setId(1); + $parent->setItemType('test'); + $parent->setExpirationTime(1370884025); + $share->addParentId(1); + $map = array( + array(array('id' => 1), 1, null, array($parent)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithTwoParents() { + // Share and parents have no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // 1 parent expires, the other parent and share have no expiration time + $parent1->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second after 1 parent, the other parent has no expiration time + $share->setExpirationTime(1370884026); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // 1 parent expires 1 second before share, the other parent expires 1 second after share + $parent2->setExpirationTime(1370884027); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + + // Share expires 1 second before both parents + $share->setExpirationTime(1370884024); + $parent2->setExpirationTime(1370884025); + $this->assertTrue($this->shareManager->pIsValidExpirationTimeForParents($share)); + } + + public function testIsValidExpirationTimeWithTwoParentsExpireAndShareDoesNotExpire() { + // Both parents expire, and share has no expiration time + $share = new Share(); + $share->setItemType('test'); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setExpirationTime(1370884025); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setExpirationTime(1370884026); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + + public function testIsValidExpirationTimeWithTwoParentsExpireAndShareExpiresAfter() { + // Both parents expire before share + $share = new Share(); + $share->setItemType('test'); + $share->setExpirationTime(1370884026); + $parent1 = new Share(); + $parent1->setId(1); + $parent1->setItemType('test'); + $parent1->setExpirationTime(1370884024); + $parent2 = new Share(); + $parent2->setId(2); + $parent2->setItemType('test'); + $parent2->setExpirationTime(1370884025); + $share->setParentIds(array(1, 2)); + $map = array( + array(array('id' => 1), 1, null, array($parent1)), + array(array('id' => 2), 1, null, array($parent2)), + ); + $this->shareBackend->expects($this->any()) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidExpirationTimeException', + 'The expiration time exceeds the parent shares\' expiration times' + ); + $this->shareManager->pIsValidExpirationTimeForParents($share); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/group.php b/tests/lib/share/sharetype/group.php new file mode 100644 index 000000000000..17771d45bd26 --- /dev/null +++ b/tests/lib/share/sharetype/group.php @@ -0,0 +1,676 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestGroup extends \OC\Share\ShareType\Group { + + public function getId() { + return 'testgroup'; + } + +} + +class Group extends ShareType { + + private $itemTargetMachine; + private $groupManager; + private $userManager; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + private $group1; + private $group1Display; + private $group2; + private $group2Display; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->group1 = 'group1'; + $this->group2 = 'group2'; + $this->group1Display = 'group1 (group)'; + $this->group2Display = 'group2 (group)'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestGroup('test', new ShareFactory(), $this->itemTargetMachine, + $this->groupManager, $this->userManager + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->mtgap)); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->karlitschek)); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getUID') + ->will($this->returnValue($this->icewind)); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $userMap = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($userMap)); + $group1Group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group1Display)); + $group1Group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($mtgapUser, $karlitschekUser))); + $group2Group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group2Display)); + $group2Group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($mtgapUser, $karlitschekUser, $icewindUser))); + $groupMap = array( + array($this->group1, $group1Group), + array($this->group2, $group2Group), + ); + $this->groupManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($groupMap)); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->group1); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->group2); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($icewindUser)) + ->will($this->returnValue('Robin\'s Target')); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareWith($this->group1); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($mtgapUser)) + ->will($this->returnValue('Michael\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($karlitschekUser)) + ->will($this->returnValue('Frank\'s Target')); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareWith($this->group2); + $this->itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($mtgapUser)) + ->will($this->returnValue('Michael\'s Target')); + $this->itemTargetMachine->expects($this->at(2)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo($icewindUser)) + ->will($this->returnValue('Robin\'s Target')); + break; + } + $this->itemTargetMachine->expects($this->at(0)) + ->method('getItemTarget') + ->with($this->equalTo($share), $this->equalTo(null)) + ->will($this->returnValue('Group Target')); + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->group1); + $share->setShareWithDisplayName($this->group1Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->karlitschek => 'Frank\'s Target' + ) + )); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->group2); + $share->setShareWithDisplayName($this->group2Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->karlitschek => 'Frank\'s Target', + $this->icewind => 'Robin\'s Target', + ) + )); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setShareWith($this->group1); + $share->setShareWithDisplayName($this->group1Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->mtgap => 'Michael\'s Target', + $this->karlitschek => 'Frank\'s Target', + ) + )); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setShareWith($this->group2); + $share->setShareWithDisplayName($this->group2Display); + $share->setItemTarget(array('Group Target', 'users' => array( + $this->mtgap => 'Michael\'s Target', + $this->icewind => 'Robin\'s Target', + ) + )); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $bar = 'bar'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($designers); + $map = array( + array($bar, false), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->any()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareWithDoesNotExist() { + $jakobsack = 'jakobsack'; + $share = new Share(); + $share->setShareOwner($jakobsack); + $share->setShareWith('foo'); + $map = array( + array($jakobsack, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo('foo')) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The group shared with does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('get') + ->with($this->equalTo($designers)) + ->will($this->returnValue($group)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->once()) + ->method('get') + ->with($jancborchardt) + ->will($this->returnValue($shareOwnerUser)); + $group->expects($this->once()) + ->method('inGroup') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerNotInGroupAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $designers = 'designers'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($designers); + $map = array( + array($jancborchardt, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('groupExists') + ->with($this->equalTo($designers)) + ->will($this->returnValue(true)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('get') + ->with($this->equalTo($designers)) + ->will($this->returnValue($group)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->once()) + ->method('get') + ->with($jancborchardt) + ->will($this->returnValue($shareOwnerUser)); + $group->expects($this->once()) + ->method('inGroup') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is not in the group shared with as required by '. + 'the groups only sharing policy set by the admin' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSetItemTarget() { + $share = $this->getTestShare(3); + $share = $this->instance->share($share); + $itemTargets = array( + 'Group Item Target', + 'users' => array( + 'tpn' => 'tpn\'s Target', + 'bartv' => 'bartv\'s Target', + ), + ); + $share->setItemTarget($itemTargets); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + + $itemTargets = array( + 'New Group Item Target', + 'users' => array( + 'tpn' => 'tpn\'s New Target', + ), + ); + $share->setItemTarget($itemTargets); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + + $share->setItemTarget('Group Item Target'); + $this->instance->setItemTarget($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetSharesWithShareWithFilter() { + $this->setupTestShares(); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue($this->group1)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->will($this->returnValue(array($group1))); + $filter = array( + 'shareWith' => $this->icewind, + 'isShareWithUser' => true, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + } + + public function testGetSharesWithShareWithFilterAndNoGroups() { + $this->setupTestShares(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->will($this->returnValue(array())); + $filter = array( + 'shareWith' => $this->mtgap, + 'isShareWithUser' => true, + ); + $this->assertEmpty($this->instance->getShares($filter, null, null)); + } + + public function testSearchForPotentialShareWiths() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(3, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup1', + 'shareWithDisplayName' => 'foogroup1 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup3', + 'shareWithDisplayName' => 'foogroup3 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffset() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $map = array( + array('foo', 3, 1, array($group2, $group3)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup3', + 'shareWithDisplayName' => 'foogroup3 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $group4 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group4->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup4')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3, $group4)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2, $group4))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(3, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup1', + 'shareWithDisplayName' => 'foogroup1 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup4', + 'shareWithDisplayName' => 'foogroup4 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffsetAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup1')); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup2')); + $group3 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group3->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup3')); + $group4 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group4->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('foogroup4')); + $map = array( + array('foo', null, null, array($group1, $group2, $group3, $group4)), + ); + $this->groupManager->expects($this->once()) + ->method('search') + ->will($this->returnValueMap($map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2, $group4))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup2', + 'shareWithDisplayName' => 'foogroup2 (group)'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'foogroup4', + 'shareWithDisplayName' => 'foogroup4 (group)'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testGetItemTargetMachine() { + $this->assertEquals($this->itemTargetMachine, $this->instance->getItemTargetMachine()); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/groupwatcher.php b/tests/lib/share/sharetype/groupwatcher.php new file mode 100644 index 000000000000..3618a4ab8a09 --- /dev/null +++ b/tests/lib/share/sharetype/groupwatcher.php @@ -0,0 +1,431 @@ +. + */ + +namespace Test\Share; + +use OC\Share\Share; +use OC\Share\Exception\InvalidShareException; +use OC\User\User; + +class GroupWatcher extends \PHPUnit_Framework_TestCase { + + private $shareManager; + private $shareBackend1; + private $shareBackend2; + private $user1; + private $user2; + private $group; + + protected function setUp() { + $this->shareManager = $this->getMockBuilder('\OC\Share\ShareManager') + ->disableOriginalConstructor() + ->getMock(); + $this->shareBackend1 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend1->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test1')); + $this->shareBackend2 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $this->shareBackend2->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test2')); + $this->shareManager->expects($this->any()) + ->method('getShareBackends') + ->will($this->returnValue(array( + 'test1' => $this->shareBackend1, + 'test2' => $this->shareBackend2, + ))); + $this->shareManager->expects($this->any()) + ->method('getShareBackend') + ->will($this->returnValueMap(array( + array('test1', $this->shareBackend1), + array('test2', $this->shareBackend2), + ))); + $this->user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('MTGap')); + $this->user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('bantu')); + $this->group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->group->expects($this->any()) + ->method('getGID') + ->will($this->returnValue('friends')); + $this->group->expects($this->any()) + ->method('getUsers') + ->will($this->returnValue(array($this->user1, $this->user2))); + } + + public function testOnGroupDeleted() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $mtgap = 'MTGap'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->exactly(2)) + ->method('unshare'); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testOnGroupDeletedWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($bantu); + $share3->setShareWith($mtgap); + $share3->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $mtgap), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $mtgap), null, null, + array() + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->exactly(6)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->exactly(3)) + ->method('unshare'); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share3)) + ->will($this->throwException(new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ))); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function listenPostDelete($scope, $method, $callback) { + if ($method === 'postDelete') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group)); + } + } + + public function testOnUserAddedToGroup() { + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share1->setItemTarget(array('Group Target')); + $share2 = new Share(); + $share2->setShareTypeId('group'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($group); + $share2->setItemType('test2'); + $share2->setItemTarget(array('Group Target')); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share2) + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $itemTargetMachine->expects($this->at(0)) + ->method('getItemTarget') + ->with($this->equalTo($share1), $this->equalTo($this->user2)) + ->will($this->returnValue('Andreas\' Target 1')); + $itemTargetMachine->expects($this->at(1)) + ->method('getItemTarget') + ->with($this->equalTo($share2), $this->equalTo($this->user2)) + ->will($this->returnValue('Andreas\' Target 2')); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\Group') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->any()) + ->method('getItemTargetMachine') + ->will($this->returnValue($itemTargetMachine)); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('group')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('group')) + ->will($this->returnValue($shareType)); + $updatedShare1 = clone $share1; + $updatedShare1->setItemTarget(array('Group Target', 'users' => array( + $bantu => 'Andreas\' Target 1' + ) + )); + $updatedShare2 = clone $share2; + $updatedShare2->setItemTarget(array('Group Target', 'users' => array( + $bantu => 'Andreas\' Target 2' + ) + )); + // I had to manually counted the indices... this could break easily + $this->shareManager->expects($this->at(4)) + ->method('update') + ->with($this->equalTo($updatedShare1)); + $this->shareManager->expects($this->at(6)) + ->method('update') + ->with($this->equalTo($updatedShare2)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostAddUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + } + + public function listenPostAddUser($scope, $method, $callback) { + if ($method === 'postAddUser') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group, $this->user2)); + } + } + + public function testOnUserRemovedFromGroup() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setId(2); + $share2->addParentId(1); + $share2->setShareTypeId('link'); + $share2->setShareOwner($bantu); + $share2->setItemType('test1'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array() + ), + ); + $this->shareManager->expects($this->exactly(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->once()) + ->method('getReshares') + ->with($this->equalTo($share1)) + ->will($this->returnValue(array($share2))); + $this->shareManager->expects($this->once()) + ->method('unshare'); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostRemoveUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testOnUserRemovedFromGroupWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $mtgap = 'MTGap'; + $bantu = 'bantu'; + $group = 'friends'; + $share1 = new Share(); + $share1->setId(1); + $share1->setShareTypeId('group'); + $share1->setShareOwner($mtgap); + $share1->setShareWith($group); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setId(2); + $share2->addParentId(1); + $share2->setShareTypeId('link'); + $share2->setShareOwner($bantu); + $share2->setItemType('test1'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($bantu); + $share3->setShareWith($mtgap); + $share3->setItemType('test2'); + $map = array( + array('test1', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array($share1) + ), + array('test2', array('shareTypeId' => 'group', 'shareWith' => $group), null, null, + array() + ), + array('test1', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array() + ), + array('test2', array('shareTypeId' => 'user', 'shareOwner' => $bantu), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->exactly(4)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->once()) + ->method('getReshares') + ->with($this->equalTo($share1)) + ->will($this->returnValue(array($share2))); + $this->shareManager->expects($this->exactly(2)) + ->method('unshare'); + $shareType = $this->getMockBuilder('\OC\Share\ShareType\User') + ->disableOriginalConstructor() + ->getMock(); + $shareType->expects($this->once()) + ->method('isValidShare') + ->with($this->equalTo($share3)) + ->will($this->throwException(new InvalidShareException( + 'The share owner is not in any groups of the user shared with as required by '. + 'the groups only sharing policy set by the admin' + ))); + $this->shareBackend1->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $this->shareBackend2->expects($this->any()) + ->method('getShareType') + ->with($this->equalTo('user')) + ->will($this->returnValue($shareType)); + $groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $groupManager->expects($this->atLeastOnce()) + ->method('listen') + ->will($this->returnCallBack(array($this, 'listenPostRemoveUser'))); + $groupWatcher = new \OC\Share\ShareType\GroupWatcher($this->shareManager, $groupManager); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function listenPostRemoveUser($scope, $method, $callback) { + if ($method === 'postRemoveUser') { + $this->assertEquals('\OC\Group', $scope); + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->group, $this->user2)); + } + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/link.php b/tests/lib/share/sharetype/link.php new file mode 100644 index 000000000000..9293517a6f9e --- /dev/null +++ b/tests/lib/share/sharetype/link.php @@ -0,0 +1,297 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestLink extends \OC\Share\ShareType\Link { + + public function getId() { + return 'testlink'; + } + +} + +class Link extends ShareType { + + private $itemTargetMachine; + private $userManager; + private $tokenMachine; + private $counter; + private $hasher; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->itemTargetMachine->expects($this->any()) + ->method('getItemTarget') + ->will($this->returnValue('Test Target')); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->tokenMachine = $this->getMockBuilder('\OC\Share\ShareType\TokenMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->counter = 0; + $this->hasher = $this->getMockBuilder('\PasswordHash') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestLink('test', new ShareFactory(), $this->itemTargetMachine, + $this->userManager, $this->tokenMachine, $this->hasher + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $map = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($map)); + $counter = $this->counter; + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('2kla32ljadsfoj23kjab')); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('auoiu23k2jiapi2kjads')); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('82093jkadp2kjasdf212')); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $this->tokenMachine->expects($this->at($counter)) + ->method('getToken') + ->will($this->returnValue('po2jad2ijajk32i0sads')); + break; + } + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setItemTarget('Test Target'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setToken('2kla32ljadsfoj23kjab'); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setToken('auoiu23k2jiapi2kjads'); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setToken('82093jkadp2kjasdf212'); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setToken('po2jad2ijajk32i0sads'); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'yes'); + + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $map = array( + array($zimba12, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'yes'); + + $tpn = 'tpn'; + $bar = 'bar'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($tpn); + $map = array( + array($bar, false), + array($tpn, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testIsValidShareWithLinkSharingDisabled() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_allow_links', 'no'); + + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $map = array( + array($zimba12, true), + ); + $this->userManager->expects($this->once()) + ->method('userExists') + ->will($this->returnValue(true)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The admin has disabled sharing via links' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_allow_links', $sharingPolicy); + } + + public function testShareWithPassword() { + $share = $this->getTestShare(1); + $share->setPassword('password'); + $this->hasher->expects($this->once()) + ->method('HashPassword') + ->will($this->returnValue('password')); + $result = $this->instance->share($share); + $this->assertNotNull($result->getId()); + $this->assertEquals(array(), $result->getUpdatedProperties()); + $share->setId($result->getId()); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testSetPassword() { + $share = $this->getTestShare(2); + $share = $this->instance->share($share); + $this->hasher->expects($this->once()) + ->method('HashPassword') + ->will($this->returnValue('1234')); + $share->setPassword('1234'); + $this->instance->setPassword($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetSharesWithFilter() { + $this->setupTestShares(); + $filter = array( + 'token' => $this->share2->getToken(), + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share2, $shares, '', false, false); + + $filter = array( + 'shareOwner' => $this->mtgap, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(2, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share2, $shares, '', false, false); + + $filter = array( + 'shareWith' => 'foo', + ); + $this->assertEmpty($this->instance->getShares($filter, null, null)); + } + + public function testSearchForPotentialShareWiths() { + $this->assertEmpty($this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1)); + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/sharetype.php b/tests/lib/share/sharetype/sharetype.php new file mode 100644 index 000000000000..a97f814933e3 --- /dev/null +++ b/tests/lib/share/sharetype/sharetype.php @@ -0,0 +1,175 @@ +. + */ + +namespace Test\Share\ShareType; + +abstract class ShareType extends \PHPUnit_Framework_TestCase { + + protected $instance; + protected $share1; + protected $share2; + protected $share3; + protected $share4; + + /** + * Get a share with fake data, it will be passed into the share method + * @param int $version 1-4 Return a unique share for each version number + * @return \OC\Share\Share + */ + abstract protected function getTestShare($version); + + /** + * Get the same share as getTestShare, but as expected after being shared + * @param int $version 1-4 Return a unique share for each version number + * @return \OC\Share\Share + */ + abstract protected function getSharedTestShare($version); + + /** + * Setup four shares with fake data + */ + protected function setupTestShares() { + $shares = array(); + for ($i = 1; $i < 5; $i++) { + $share = $this->getTestShare($i); + $share = $this->instance->share($share); + $sharedShare = $this->getSharedTestShare($i); + $sharedShare->setId($share->getId()); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $shares[] = $share; + } + list($this->share1, $this->share2, $this->share3, $this->share4) = $shares; + } + + protected function tearDown() { + $this->instance->clear(); + } + + public function testShare() { + $share = $this->getTestShare(1); + $sharedShare = $this->getSharedTestShare(1); + $share = $this->instance->share($share); + $this->assertNotNull($share->getId()); + $this->assertEquals(array(), $share->getUpdatedProperties()); + $sharedShare->setId($share->getId()); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($sharedShare, $this->getShareById($share->getId())); + } + + public function testShareWithParents() { + $share = $this->getTestShare(2); + $share->setParentIds(array(1, 3)); + $sharedShare = $this->getSharedTestShare(2); + $share = $this->instance->share($share); + $this->assertNotNull($share->getId()); + $this->assertEquals(array(), $share->getUpdatedProperties()); + $sharedShare->setId($share->getId()); + $sharedShare->setParentIds(array(1, 3)); + $sharedShare->resetUpdatedProperties(); + $this->assertEquals($sharedShare, $share); + $this->assertEquals($sharedShare, $this->getShareById($share->getId())); + } + + public function testUnshare() { + $share = $this->getTestShare(3); + $share = $this->instance->share($share); + $this->assertEquals($share, $this->getShareById($share->getId())); + $this->instance->unshare($share); + $this->assertFalse($this->getShareById($share->getId())); + } + + public function testUpdate() { + $share = $this->getTestShare(4); + $share = $this->instance->share($share); + $share->setPermissions(1); + $this->instance->update($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testUpdateTwoProperties() { + $share = $this->getTestShare(2); + $share = $this->instance->share($share); + $share->setPermissions(21); + $share->setExpirationTime(1370884027); + $this->instance->update($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testSetParentIds() { + $share = $this->getTestShare(1); + $share->setParentIds(array(1, 2)); + $share = $this->instance->share($share); + $share->setParentIds(array(2, 3, 4)); + $this->instance->setParentIds($share); + $share->resetUpdatedProperties(); + $this->assertEquals($share, $this->getShareById($share->getId())); + } + + public function testGetShares() { + $this->setupTestShares(); + $shares = $this->instance->getShares(array(), null, null); + $this->assertCount(4, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share2, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + $this->assertContains($this->share4, $shares, '', false, false); + } + + public function testGetSharesWithLimitOffset() { + $this->setupTestShares(); + $shares = $this->instance->getShares(array(), 3, 1); + $this->assertCount(3, $shares); + $this->assertContains($this->share2, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + $this->assertContains($this->share4, $shares, '', false, false); + } + + public function testGetSharesWithParentId() { + $this->setupTestShares(); + $this->share4->addParentId($this->share3->getId()); + $this->instance->setParentIds($this->share4); + $this->share4->resetUpdatedProperties(); + $filter = array( + 'parentId' => $this->share3->getId(), + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(1, $shares); + $this->assertContains($this->share4, $shares, '', false, false); + } + + /** + * Get a share from the share type based on id + * @param int $id + * @return \OC\Share\Share | bool + */ + protected function getShareById($id) { + $share = $this->instance->getShares(array('id' => $id), 1, null); + if (is_array($share) && count($share) === 1) { + return reset($share); + } + return false; + } + +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/user.php b/tests/lib/share/sharetype/user.php new file mode 100644 index 000000000000..41e58f9b4be2 --- /dev/null +++ b/tests/lib/share/sharetype/user.php @@ -0,0 +1,580 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\ShareFactory; + +class TestUser extends \OC\Share\ShareType\User { + + public function getId() { + return 'testuser'; + } + +} + +class User extends ShareType { + + private $itemTargetMachine; + private $userManager; + private $groupManager; + private $mtgap; + private $mtgapDisplay; + private $karlitschek; + private $karlitschekDisplay; + private $icewind; + private $icewindDisplay; + + protected function setUp() { + $this->mtgap = 'MTGap'; + $this->mtgapDisplay = 'Michael Gapczynski'; + $this->karlitschek = 'karlitschek'; + $this->karlitschekDisplay = 'Frank Karlitschek'; + $this->icewind = 'Icewind'; + $this->icewindDisplay = 'Robin Appelman'; + $this->itemTargetMachine = $this->getMockBuilder('\OC\Share\ItemTargetMachine') + ->disableOriginalConstructor() + ->getMock(); + $this->itemTargetMachine->expects($this->any()) + ->method('getItemTarget') + ->will($this->returnValue('Test Target')); + $this->userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager = $this->getMockBuilder('\OC\Group\Manager') + ->disableOriginalConstructor() + ->getMock(); + $this->instance = new TestUser('test', new ShareFactory(), $this->itemTargetMachine, + $this->userManager, $this->groupManager + ); + } + + protected function getTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + $mtgapUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $mtgapUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->mtgapDisplay)); + $karlitschekUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $karlitschekUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->karlitschekDisplay)); + $icewindUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $icewindUser->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue($this->icewindDisplay)); + $map = array( + array($this->mtgap, $mtgapUser), + array($this->karlitschek, $karlitschekUser), + array($this->icewind, $icewindUser), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('get') + ->will($this->returnValueMap($map)); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->karlitschek); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareWith($this->icewind); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareWith($this->karlitschek); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareWith($this->mtgap); + break; + } + return $share; + } + + protected function getSharedTestShare($version) { + $share = new Share(); + $share->setShareTypeId($this->instance->getId()); + $share->setItemType('test'); + $share->setItemSource('23'); + $share->setItemTarget('Test Target'); + $share->setPermissions(31); + $share->setShareTime(1370797580); + switch ($version) { + case 1: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->karlitschek); + $share->setShareWithDisplayName($this->karlitschekDisplay); + break; + case 2: + $share->setShareOwner($this->mtgap); + $share->setItemOwner($this->mtgap); + $share->setShareOwnerDisplayName($this->mtgapDisplay); + $share->setShareWith($this->icewind); + $share->setShareWithDisplayName($this->icewindDisplay); + break; + case 3: + $share->setShareOwner($this->icewind); + $share->setItemOwner($this->icewind); + $share->setShareOwnerDisplayName($this->icewindDisplay); + $share->setShareWith($this->karlitschek); + $share->setShareWithDisplayName($this->karlitschekDisplay); + break; + case 4: + $share->setShareOwner($this->karlitschek); + $share->setItemOwner($this->karlitschek); + $share->setShareOwnerDisplayName($this->karlitschekDisplay); + $share->setShareWith($this->mtgap); + $share->setShareWithDisplayName($this->mtgapDisplay); + break; + } + return $share; + } + + public function testIsValidShare() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $tanghus = 'tanghus'; + $DeepDiver = 'DeepDiver'; + $share = new Share(); + $share->setShareOwner($tanghus); + $share->setShareWith($DeepDiver); + $map = array( + array($tanghus, true), + array($DeepDiver, true), + ); + $this->userManager->expects($this->exactly(2)) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithSameUsers() { + $zimba12 = 'zimba12'; + $share = new Share(); + $share->setShareOwner($zimba12); + $share->setShareWith($zimba12); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is the user shared with' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareOwnerDoesNotExist() { + $tpn = 'tpn'; + $bar = 'bar'; + $share = new Share(); + $share->setShareOwner($bar); + $share->setShareWith($tpn); + $map = array( + array($bar, false), + array($tpn, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithShareWithDoesNotExist() { + $raydiation = 'Raydiation'; + $foo = 'foo'; + $share = new Share(); + $share->setShareOwner($raydiation); + $share->setShareWith($foo); + $map = array( + array($raydiation, true), + array($foo, false), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The user shared with does not exist' + ); + $this->instance->isValidShare($share); + } + + public function testIsValidShareWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $raydiation = 'Raydiation'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($raydiation); + $map = array( + array($jancborchardt, true), + array($raydiation, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $shareWithUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $usersMap = array( + array($jancborchardt, $shareOwnerUser), + array($raydiation, $shareWithUser), + ); + $this->userManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($usersMap)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group))); + $group->expects($this->atLeastOnce()) + ->method('inGroup') + ->with($this->equalTo($shareWithUser)) + ->will($this->returnValue(true)); + $this->assertTrue($this->instance->isValidShare($share)); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testIsValidShareWithShareOwnerNotInShareWithGroupsAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $jancborchardt = 'jancborchardt'; + $raydiation = 'Raydiation'; + $share = new Share(); + $share->setShareOwner($jancborchardt); + $share->setShareWith($raydiation); + $map = array( + array($jancborchardt, true), + array($raydiation, true), + ); + $this->userManager->expects($this->atLeastOnce()) + ->method('userExists') + ->will($this->returnValueMap($map)); + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $shareWithUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $usersMap = array( + array($jancborchardt, $shareOwnerUser), + array($raydiation, $shareWithUser), + ); + $this->userManager->expects($this->any()) + ->method('get') + ->will($this->returnValueMap($usersMap)); + $group = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group))); + $group->expects($this->atLeastOnce()) + ->method('inGroup') + ->with($this->equalTo($shareWithUser)) + ->will($this->returnValue(false)); + $this->setExpectedException('\OC\Share\Exception\InvalidShareException', + 'The share owner is not in any groups of the user shared with as required by the '. + 'groups only sharing policy set by the admin' + ); + $this->instance->isValidShare($share); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testGetSharesWithFilter() { + $this->setupTestShares(); + $filter = array( + 'shareWith' => $this->karlitschek, + ); + $shares = $this->instance->getShares($filter, null, null); + $this->assertCount(2, $shares); + $this->assertContains($this->share1, $shares, '', false, false); + $this->assertContains($this->share3, $shares, '', false, false); + } + + public function testSearchForPotentialShareWiths() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $map = array( + array('foo', null, null, array($user1, $user2, $user3)), + ); + $this->userManager->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user1', + 'shareWithDisplayName' => 'foouser1'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffset() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'global'); + + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $map = array( + array('foo', 5, null, array($user1, $user2, $user3)), + ); + $this->userManager->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($map)); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(1, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Map = array( + array('foo', null, null, array($user1, $user2, $user3)), + ); + $group1->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group1Map)); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Map = array( + array('foo', null, null, array($user1, $user2)), + ); + $group2->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group2Map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', null, null); + $this->assertCount(2, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user1', + 'shareWithDisplayName' => 'foouser1'), $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } + + public function testSearchForPotentialShareWithsWithLimitOffsetAndGroupsOnlyPolicy() { + $sharingPolicy = \OC_Appconfig::getValue('core', 'shareapi_share_policy', 'global'); + \OC_Appconfig::setValue('core', 'shareapi_share_policy', 'groups_only'); + + $shareOwnerUser = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->userManager->expects($this->any()) + ->method('get') + ->with($this->equalTo('user2')) + ->will($this->returnValue($shareOwnerUser)); + $user1 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user1->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user1')); + $user1->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser1')); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user2->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user2')); + $user2->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser2')); + $user3 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $user3->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('user3')); + $user3->expects($this->any()) + ->method('getDisplayName') + ->will($this->returnValue('foouser3')); + $group1 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group1Map = array( + array('foo', 5, null, array($user1, $user2, $user3)), + ); + $group1->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group1Map)); + $group2 = $this->getMockBuilder('\OC\Group\Group') + ->disableOriginalConstructor() + ->getMock(); + $group2Map = array( + array('foo', 5, null, array($user1, $user2)), + ); + $group2->expects($this->once()) + ->method('searchDisplayName') + ->will($this->returnValueMap($group2Map)); + $this->groupManager->expects($this->once()) + ->method('getUserGroups') + ->with($this->equalTo($shareOwnerUser)) + ->will($this->returnValue(array($group1, $group2))); + $shareWiths = $this->instance->searchForPotentialShareWiths('user2', 'foo', 3, 1); + $this->assertCount(1, $shareWiths); + $this->assertContains(array( + 'shareWith' => 'user3', + 'shareWithDisplayName' => 'foouser3'), $shareWiths); + + \OC_Appconfig::setValue('core', 'shareapi_share_policy', $sharingPolicy); + } +} \ No newline at end of file diff --git a/tests/lib/share/sharetype/userwatcher.php b/tests/lib/share/sharetype/userwatcher.php new file mode 100644 index 000000000000..65975777f1eb --- /dev/null +++ b/tests/lib/share/sharetype/userwatcher.php @@ -0,0 +1,131 @@ +. + */ + +namespace Test\Share\ShareType; + +use OC\Share\Share; +use OC\Share\Exception\ShareTypeDoesNotExistException; + +class UserWatcher extends \PHPUnit_Framework_TestCase { + + private $shareManager; + private $user; + + protected function setUp() { + $this->shareManager = $this->getMockBuilder('\OC\Share\ShareManager') + ->disableOriginalConstructor() + ->getMock(); + $shareBackend1 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $shareBackend1->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test1')); + $shareBackend2 = $this->getMockBuilder('\OC\Share\ShareBackend') + ->disableOriginalConstructor() + ->setMethods(get_class_methods('\OC\Share\ShareBackend')) + ->getMockForAbstractClass(); + $shareBackend2->expects($this->any()) + ->method('getItemType') + ->will($this->returnValue('test2')); + $this->shareManager->expects($this->any()) + ->method('getShareBackends') + ->will($this->returnValue(array( + 'test1' => $shareBackend1, + 'test2' => $shareBackend2, + ))); + } + + public function testOnUserDeleted() { + $mtgap = 'MTGap'; + $vicdeo = 'VicDeo'; + $share1 = new Share(); + $share1->setShareTypeId('link'); + $share1->setShareOwner($mtgap); + $share1->setItemType('test1'); + $share2 = new Share(); + $share2->setShareTypeId('user'); + $share2->setShareOwner($mtgap); + $share2->setShareWith($vicdeo); + $share2->setItemType('test2'); + $share3 = new Share(); + $share3->setShareTypeId('user'); + $share3->setShareOwner($vicdeo); + $share3->setShareWith($mtgap); + $share3->setItemType('test1'); + $share4 = new Share(); + $share4->setShareTypeId('user'); + $share4->setShareOwner($vicdeo); + $share4->setShareWith($mtgap); + $share4->setItemType('test2'); + $this->user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor() + ->getMock(); + $this->user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue($mtgap)); + $map = array( + array('test1', array('shareOwner' => $mtgap), null, null, array($share1)), + array('test2', array('shareOwner' => $mtgap), null, null, array($share2)), + array('test1', array('shareTypeId' => 'user', 'shareWith' => $mtgap), null, null, + array($share3) + ), + ); + $this->shareManager->expects($this->at(0)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(1)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(2)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(3)) + ->method('getShares') + ->will($this->returnValueMap($map)); + $this->shareManager->expects($this->at(4)) + ->method('getShares') + ->with($this->equalTo('test2'), + $this->equalTo(array('shareTypeId' => 'user', 'shareWith' => $mtgap)), + $this->equalTo(null), $this->equalTo(null) + ) + ->will($this->throwException(new ShareTypeDoesNotExistException( + 'No share type found matching id' + ))); + $this->shareManager->expects($this->exactly(3)) + ->method('unshare'); + $userManager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $userManager->expects($this->once()) + ->method('listen') + ->with($this->equalTo('\OC\User'), $this->equalTo('postDelete')) + ->will($this->returnCallBack(array($this, 'listenPostDelete'))); + $userWatcher = new \OC\Share\ShareType\UserWatcher($this->shareManager, $userManager); + } + + public function listenPostDelete($scope, $method, $callback) { + // Fake PublicEmitter's emit + call_user_func_array($callback, array($this->user)); + } + +} \ No newline at end of file