Skip to content
This repository has been archived by the owner on Dec 27, 2023. It is now read-only.

Commit

Permalink
fix(Tinebase/WebDav): check sync and read grant for all tree nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
ccheng-dev committed Jun 22, 2022
1 parent 9d8d614 commit 28fe692
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 21 deletions.
199 changes: 198 additions & 1 deletion tests/tine20/Filemanager/Frontend/WebDAVTest.php
Expand Up @@ -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 = [];
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion tine20/Filemanager/Frontend/WebDAV.php
Expand Up @@ -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!
Expand Down
28 changes: 16 additions & 12 deletions tine20/Tinebase/Container.php
Expand Up @@ -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)) {
Expand All @@ -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();
Expand All @@ -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);
}
Expand All @@ -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)) {
Expand Down
5 changes: 4 additions & 1 deletion tine20/Tinebase/Frontend/WebDAV/Directory.php
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions tine20/Tinebase/Model/Filter/GrantsFilterGroup.php
Expand Up @@ -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);
}

Expand All @@ -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()
Expand Down
8 changes: 4 additions & 4 deletions tine20/Tinebase/WebDav/Collection/AbstractContainerTree.php
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
)
);

Expand Down

0 comments on commit 28fe692

Please sign in to comment.