diff --git a/tests/tine20/Filemanager/Frontend/WebDAVTest.php b/tests/tine20/Filemanager/Frontend/WebDAVTest.php index 56439e162b4..cb67351f6e3 100644 --- a/tests/tine20/Filemanager/Frontend/WebDAVTest.php +++ b/tests/tine20/Filemanager/Frontend/WebDAVTest.php @@ -146,7 +146,7 @@ protected function _testGetNodeForPath_webdav_filemanagerWithOtherUsers($propert $fs->setGrantsForNode($personalFolder, $personalFolder->grants); $children = $node->getChildren(); - + static::assertGreaterThanOrEqual(3, count($children)); static::assertInstanceOf('Filemanager_Frontend_WebDAV', $children[0], 'wrong node class'); $names = []; @@ -159,6 +159,192 @@ protected function _testGetNodeForPath_webdav_filemanagerWithOtherUsers($propert static::assertEquals(1, count(array_intersect($names, [$sclever->{$property}]))); } + /** + * should not change default personal grants + */ + public function testGetNodeForPath_webdav_filemanagerWithoutGrants_currentUser() + { + $nodeFsRootPath = '/webdav/Filemanager'; + + // try to get folder /user , should always sync + // it doesn't check grants in \Tinebase_WebDav_Collection_AbstractContainerTree::_getUser + $children = $this->_getNewWebDAVTreeNode($nodeFsRootPath)->getChildren(); + static::assertCount(2, $children, 'child root nodes should always be sync'); + + // try to get folder /user/personal, should always sync + // check grants in \Tinebase_WebDav_Collection_AbstractContainerTree::_getSharedChildren + $nodeUserRootPath = $nodeFsRootPath . '/'. Tinebase_Core::getUser()->accountDisplayName; + $children = $this->_getNewWebDAVTreeNode($nodeUserRootPath)->getChildren(); + static::assertCount(1, $children, 'child node personal default should be sync'); + + // try to get folder /user/personal/dir1 + // check grants in \Tinebase_Frontend_WebDAV_Directory::getChildren + $nodeUserRoot = $this->_getNewWebDAVTreeNode($nodeUserRootPath); + $nodeUserDefault = current($nodeUserRoot->getChildren()); + $nodeUserDefault->createDirectory('dir1'); + $nodeUserDefaultPath = $nodeUserRootPath . '/' . $nodeUserDefault->getName(); + $children = $this->_getNewWebDAVTreeNode($nodeUserDefaultPath)->getChildren(); + static::assertCount(1, $children, 'child node dir1 should be sync'); + + // try to get folder /user/personal/dir1/dir2 + // check grants in \Tinebase_Frontend_WebDAV_Directory::getChildren + $nodeDir1 = current($nodeUserDefault->getChildren()); + $nodeDir1->createDirectory('dir2'); + $nodeDir1Path = $nodeUserDefaultPath . '/' . $nodeDir1->getName(); + $children = $this->_getNewWebDAVTreeNode($nodeDir1Path)->getChildren(); + static::assertCount(1, $children, 'child node dir2 should be sync'); + } + + /** + * @throws Tinebase_Exception_NotFound + * @throws \Sabre\DAV\Exception\NotFound + */ + public function testGetNodeForPath_webdav_filemanagerWithoutGrants_otherUser() + { + $nodeFsRootPath = '/webdav/Filemanager'; + + // try to get folder /user + // check grants in \Filemanager_Frontend_WebDAV::_getOtherUsersChildren + $treeNodeUserRootPath = Tinebase_FileSystem::getInstance()->getApplicationBasePath('Filemanager', Tinebase_FileSystem::FOLDER_TYPE_PERSONAL) . '/' . + Tinebase_Core::getUser()->getId(); + $treeNodeUserRoot = Tinebase_FileSystem::getInstance()->stat($treeNodeUserRootPath); + $treeNodeUserDefault = Tinebase_FileSystem::getInstance()->getTreeNodeChildren($treeNodeUserRoot)->getFirstRecord(); + $this->_testGrantsHelper($treeNodeUserDefault, $nodeFsRootPath); + + // try to get folder /user/personal , + // check grants in \Tinebase_WebDav_Collection_AbstractContainerTree::_getSharedChildren + $nodeUserRootPath = $nodeFsRootPath . '/'. Tinebase_Core::getUser()->accountDisplayName; + $this->_testGrantsHelper($treeNodeUserDefault, $nodeUserRootPath); + + // try to get folder /user/personal/dir1 + // check grants in \Tinebase_Frontend_WebDAV_Directory::getChildren + $nodeUserRoot = $this->_getNewWebDAVTreeNode($nodeUserRootPath); + $nodeUserDefault = current($nodeUserRoot->getChildren()); + $nodeUserDefaultPath = $nodeUserRootPath . '/' . $nodeUserDefault->getName(); + $nodeUserDefault->createDirectory('dir1'); + $treeNodeDir1 = Tinebase_FileSystem::getInstance()->getTreeNodeChildren($treeNodeUserDefault)->getFirstRecord(); + $this->_testGrantsHelper($treeNodeDir1, $nodeUserDefaultPath); + + // try to get folder /user/personal/dir1/dir2 + // check grants in \Tinebase_Frontend_WebDAV_Directory::getChildren + $nodeDir1 = current($nodeUserDefault->getChildren()); + $nodeDir1->createDirectory('dir2'); + $nodeDir1Path = $nodeUserDefaultPath . '/' . $nodeDir1->getName(); + $treeNodeDir2 = Tinebase_FileSystem::getInstance()->getTreeNodeChildren($treeNodeDir1)->getFirstRecord(); + $this->_testGrantsHelper($treeNodeDir2, $nodeDir1Path); + } + + /** + * @throws Tinebase_Exception_NotFound + * @throws \Sabre\DAV\Exception\NotFound + */ + public function testGetNodeForPath_webdav_filemanagerWithoutGrants_shared() + { + $nodeFsRootPath = '/webdav/Filemanager'; + // try to get folder /shared , should always sync and ignore grants setting + $children = $this->_getNewWebDAVTreeNode($nodeFsRootPath)->getChildren(); + static::assertCount(2, $children, 'child root nodes should always be sync'); + + // try to get folder /shared/dir1 + // check grants in \Tinebase_WebDav_Collection_AbstractContainerTree::_getSharedDirectories + $nodeSharedRootPath = $nodeFsRootPath . '/'. 'shared'; + $nodeSharedRoot = $this->_getNewWebDAVTreeNode($nodeSharedRootPath); + $nodeSharedRoot->createDirectory('dir1'); + $treeNodeSharedRootPath = Tinebase_FileSystem::getInstance()->getApplicationBasePath('Filemanager', Tinebase_FileSystem::FOLDER_TYPE_SHARED); + $treeNodeSharedRoot = Tinebase_FileSystem::getInstance()->stat($treeNodeSharedRootPath); + $treeNodeDir1 = Tinebase_FileSystem::getInstance()->getTreeNodeChildren($treeNodeSharedRoot)->getFirstRecord(); + $this->_testGrantsHelper($treeNodeDir1, $nodeSharedRootPath); + + // try to get folder /shared/dir1/dir2 + // check grants in \Tinebase_Frontend_WebDAV_Directory::getChildren + $nodeDir1 = current($nodeSharedRoot->getChildren()); + $nodeDir1->createDirectory('dir2'); + $nodeDir1Path = $nodeSharedRootPath . '/' . $nodeDir1->getName(); + $treeNodeDir2 = Tinebase_FileSystem::getInstance()->getTreeNodeChildren($treeNodeDir1)->getFirstRecord(); + $this->_testGrantsHelper($treeNodeDir2, $nodeDir1Path); + } + + /** + * node should only be sync when user has both READ_GRANT and SYNC_GRANT + * + * @throws Tinebase_Exception_NotFound + * @throws \Sabre\DAV\Exception\NotFound + */ + protected function _testGrantsHelper($folder, $nodePath, $isForcedSyncNode = false) + { + $testSyncUser = $this->_personas['sclever']; + $hideNodesCount = ! $isForcedSyncNode ? 1 : 0; + + // set default grant for test sync user first + Tinebase_Tree_NodeGrants::getInstance()->getGrantsForRecord($folder); + if (isset($folder['grants'])) { + $folder->grants->addRecord(new Tinebase_Model_Grants([ + 'account_type' => Tinebase_Acl_Rights::ACCOUNT_TYPE_USER, + 'account_id' => $testSyncUser->getId(), + Tinebase_Model_Grants::GRANT_ADMIN => true + ])); + Tinebase_FileSystem::getInstance()->setGrantsForNode($folder, $folder->grants); + } + + // change current user to test the sync ability + Tinebase_Core::setUser($testSyncUser); + $expectChildren = $this->_getNewWebDAVTreeNode($nodePath)->getChildren(); + $expectChildCount = count($expectChildren); + + // assert sync only without read_grant nor sync_grant + foreach ($folder->grants as $grant) { + if ($grant->account_id === $testSyncUser->getId() || $grant->account_type === Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP) { + $grant->adminGrant = false; + $grant->syncGrant = false; + $grant->readGrant = false; + } + } + Tinebase_FileSystem::getInstance()->setGrantsForNode($folder, $folder->grants); + $children = $this->_getNewWebDAVTreeNode($nodePath)->getChildren(); + static::assertCount($expectChildCount - $hideNodesCount, $children, 'children node should not be sync'); + + // assert sync only with read_grant or sync_grant + foreach ($folder->grants as $grant) { + if ($grant->account_id === $testSyncUser->getId()) { + $grant['syncGrant'] = false; + $grant['readGrant'] = true; + } + } + Tinebase_FileSystem::getInstance()->setGrantsForNode($folder, $folder->grants); + $children = $this->_getNewWebDAVTreeNode($nodePath)->getChildren(); + static::assertCount($expectChildCount - $hideNodesCount, $children, 'children node should not be sync'); + + foreach ($folder->grants as $grant) { + if ($grant->account_id === $testSyncUser->getId()) { + $grant['syncGrant'] = true; + $grant['readGrant'] = false; + } + } + Tinebase_FileSystem::getInstance()->setGrantsForNode($folder, $folder->grants); + $children = $this->_getNewWebDAVTreeNode($nodePath)->getChildren(); + static::assertCount($expectChildCount - $hideNodesCount, $children, 'children node should not be sync'); + + // assert sync only with both read_grant and sync_grant + foreach ( $folder->grants as $grant) { + if ($grant->account_id === $testSyncUser->getId()) { + $grant['syncGrant'] = true; + $grant['readGrant'] = true; + } + } + Tinebase_FileSystem::getInstance()->setGrantsForNode($folder, $folder->grants); + $children = $this->_getNewWebDAVTreeNode($nodePath)->getChildren(); + static::assertCount($expectChildCount, $children, 'children node should be sync'); + + // reset current user and folder grants for operating nodes + Tinebase_Core::setUser($this->_originalTestUser); + + foreach ( $folder->grants as $grant) { + if ($grant->account_id === $testSyncUser->getId()) { + $folder->grants->removeRecord($grant); + } + } + } + public function testGetNodeForPath_webdav_filemanagerWithOtherUsersLoginName() { Tinebase_Config::getInstance()->set(Tinebase_Config::USE_LOGINNAME_AS_FOLDERNAME, true); @@ -1014,4 +1200,15 @@ protected function _getWebDAVTree() return $this->_webdavTree; } + + /** + * + * @return \Sabre\DAV\ICollection|\Sabre\DAV\INode|\Sabre\DAV\ObjectTree + */ + protected function _getNewWebDAVTreeNode($path) + { + $node = new Tinebase_WebDav_ObjectTree(new Tinebase_WebDav_Root()); + $node = $node->getNodeForPath($path); + return $node; + } } diff --git a/tine20/Filemanager/Frontend/WebDAV.php b/tine20/Filemanager/Frontend/WebDAV.php index f6e5a550b35..02875b0722f 100644 --- a/tine20/Filemanager/Frontend/WebDAV.php +++ b/tine20/Filemanager/Frontend/WebDAV.php @@ -104,7 +104,7 @@ protected function _getOtherUsersChildren() { $children = array(); foreach (Tinebase_FileSystem::getInstance()->getOtherUsers(Tinebase_Core::getUser(), - $this->_getApplicationName(), [Tinebase_Model_Grants::GRANT_READ, Tinebase_Model_Grants::GRANT_SYNC]) as + $this->_getApplicationName(), [Tinebase_Model_Grants::GRANT_READ, '&' . Tinebase_Model_Grants::GRANT_SYNC]) as $node) { $name = $node->name; // we never use id as name! diff --git a/tine20/Tinebase/Container.php b/tine20/Tinebase/Container.php index 5bf35ea16cb..2649cc50123 100644 --- a/tine20/Tinebase/Container.php +++ b/tine20/Tinebase/Container.php @@ -807,19 +807,20 @@ public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableNa // enforce string for pgsql array_walk($roleMemberships, function(&$item) {$item = (string)$item;}); - $quotedActId = $db->quoteIdentifier("{$_aclTableName}.account_id"); - $quotedActType = $db->quoteIdentifier("{$_aclTableName}.account_type"); - - $accountSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($_select); - $accountSelect - ->orWhere("{$quotedActId} = ? AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_USER), $accountId) - ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships) - ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE), empty($roleMemberships) ? ' ' : $roleMemberships); + $accountSelectFunc = function($quotedActId, $quotedActType) use($_select, $db, $accountId, $groupMemberships, $roleMemberships) { + $accountSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($_select); + $accountSelect + ->orWhere("{$quotedActId} = ? AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_USER), $accountId) + ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_GROUP), empty($groupMemberships) ? ' ' : $groupMemberships) + ->orWhere("{$quotedActId} IN (?) AND {$quotedActType} = " . $db->quote(Tinebase_Acl_Rights::ACCOUNT_TYPE_ROLE), empty($roleMemberships) ? ' ' : $roleMemberships); + + if (!Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) { + $accountSelect->orWhere("{$quotedActType} = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE); + } + $accountSelect->appendWhere(Zend_Db_Select::SQL_AND); + }; - if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ANYONE_ACCOUNT_DISABLED)) { - $accountSelect->orWhere("{$quotedActType} = ?", Tinebase_Acl_Rights::ACCOUNT_TYPE_ANYONE); - } - $accountSelect->appendWhere(Zend_Db_Select::SQL_AND); + $accountSelectFunc($db->quoteIdentifier("{$_aclTableName}.account_id"), $db->quoteIdentifier("{$_aclTableName}.account_type")); // we only need to filter, if the filter does not contain % if (!in_array('*', $grants)) { @@ -833,6 +834,7 @@ public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableNa } $iteration = 0; $grantsSelect = new Tinebase_Backend_Sql_Filter_GroupSelect($_select); + $grantsSelect->openBracket(); foreach ($grants as $grant) { if ($grant[0] === '(') { $grantsSelect->openBracket(); @@ -858,6 +860,7 @@ public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableNa if ($iteration > 0) { $callbackIdentifier = call_user_func($joinCallBack, $_select, $iteration); $grantsSelect->where($db->quoteIdentifier($callbackIdentifier . '.account_grant') . ' LIKE ?', $grant); + $accountSelectFunc($db->quoteIdentifier("{$callbackIdentifier}.account_id"), $db->quoteIdentifier("{$callbackIdentifier}.account_type")); } else { $grantsSelect->where($quotedGrant . ' LIKE ?', $grant); } @@ -869,6 +872,7 @@ public static function addGrantsSql($_select, $_accountId, $_grant, $_aclTableNa $grantsSelect->closeBracket(); } } + $grantsSelect->closeBracket(); // admin grant includes all other grants if (! in_array(Tinebase_Model_Grants::GRANT_ADMIN, $grants)) { diff --git a/tine20/Tinebase/Frontend/WebDAV/Directory.php b/tine20/Tinebase/Frontend/WebDAV/Directory.php index 734fa5e01df..d3352917c54 100644 --- a/tine20/Tinebase/Frontend/WebDAV/Directory.php +++ b/tine20/Tinebase/Frontend/WebDAV/Directory.php @@ -44,7 +44,10 @@ public function getChildren() // Loop through the directory, and create objects for each node try { foreach (Tinebase_FileSystem::getInstance()->scanDir($this->_path) as $node) { - $children[] = $this->getChild($node->name); + if (Tinebase_Core::getUser()->hasGrant($node, Tinebase_Model_Grants::GRANT_READ) + && Tinebase_Core::getUser()->hasGrant($node, Tinebase_Model_Grants::GRANT_SYNC)) { + $children[] = $this->getChild($node->name); + } } } catch (Tinebase_Exception_NotFound $tenf) { throw new Sabre\DAV\Exception\NotFound('path not found: ' . $this->_path); diff --git a/tine20/Tinebase/Model/Filter/GrantsFilterGroup.php b/tine20/Tinebase/Model/Filter/GrantsFilterGroup.php index d45b730b5be..d5bcdd89d67 100644 --- a/tine20/Tinebase/Model/Filter/GrantsFilterGroup.php +++ b/tine20/Tinebase/Model/Filter/GrantsFilterGroup.php @@ -91,7 +91,7 @@ public function appendFilterSql($select, $backend) $this->_tempBackend = null; } - if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ + if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' $select after appending grants sql: ' . $select); } @@ -106,7 +106,7 @@ public function addGrantsSqlCallback($_select, $iteration) { $db = $_select->getAdapter(); $tblAlias = $this->_aclTableName . $iteration; - $_select->join(array( + $_select->joinLeft(array( /* table */ $tblAlias => SQL_TABLE_PREFIX . $this->_aclTableName), /* on */ $db->quoteIdentifier($tblAlias . '.record_id') . ' = ' . $db->quoteIdentifier($this->_tempBackend->getTableName() . '.' . $this->_aclIdColumn), array() diff --git a/tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php b/tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php index 7696b3899ac..d247cf0b9be 100644 --- a/tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php +++ b/tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php @@ -551,7 +551,7 @@ protected function _getOtherUsersChildren() // TODO allow NODES here $otherUsers = Tinebase_Container::getInstance()->getOtherUsers(Tinebase_Core::getUser(), $this->_getApplicationName(), array( Tinebase_Model_Grants::GRANT_READ, - Tinebase_Model_Grants::GRANT_SYNC + '&' . Tinebase_Model_Grants::GRANT_SYNC )); foreach ($otherUsers as $user) { @@ -597,14 +597,14 @@ protected function _getSharedChildren() $accountId, array( Tinebase_Model_Grants::GRANT_READ, - Tinebase_Model_Grants::GRANT_SYNC + '&' . Tinebase_Model_Grants::GRANT_SYNC ) ); } else { // NOTE: seems to be the expected behavior for non-delegation clients $containers = $this->_containerController->getContainerByACL(Tinebase_Core::getUser(), $this->_model, array( Tinebase_Model_Grants::GRANT_READ, - Tinebase_Model_Grants::GRANT_SYNC + '&' . Tinebase_Model_Grants::GRANT_SYNC )); } } catch (Tinebase_Exception_AccessDenied $tead) { @@ -635,7 +635,7 @@ protected function _getSharedDirectories() $this->_model ?: $this->_getApplicationName(), array( Tinebase_Model_Grants::GRANT_READ, - Tinebase_Model_Grants::GRANT_SYNC + '&' . Tinebase_Model_Grants::GRANT_SYNC ) );