egroupware/api/tests/Vfs/SharingACLTest.php
2020-09-13 10:00:55 +02:00

324 lines
10 KiB
PHP

<?php
/**
* Tests for sharing files and directories
*
* We check the access and permissions, making sure we only have access to what is supposed to be there.
*
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright (c) 2020 Nathan Gray
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
namespace EGroupware\Api\Vfs;
require_once __DIR__ . '/SharingBase.php';
require_once __DIR__ . '/../../../admin/inc/class.admin_cmd_delete_account.inc.php';
require_once __DIR__ . '/../../../admin/inc/class.admin_cmd_edit_user.inc.php';
use EGroupware\Api\LoggedInTest as LoggedInTest;
use EGroupware\Api\Vfs;
class SharingACLTest extends SharingBase
{
// User for testing - we share with this user & log in as them for checking
protected $account_id;
// File that should not be available over the share
protected $no_access;
// Use a completely new user, so we know it's there and "clean"
protected $account = array(
'account_lid' => 'user_test',
'account_firstname' => 'ShareAccess',
'account_lastname' => 'Test',
'account_passwd' => 'passw0rd',
'account_passwd_2' => 'passw0rd'
);
protected function setUp() : void
{
if(($account_id = $GLOBALS['egw']->accounts->name2id($this->account['account_lid'])))
{
// Delete if there in case something went wrong
$GLOBALS['egw']->accounts->delete($account_id);
}
// Execute
$command = new \admin_cmd_edit_user(false, $this->account);
$command->comment = 'Needed for unit test ' . $this->getName();
$command->run();
$this->account_id = $command->account;
}
protected function tearDown() : void
{
parent::tearDown();
if($this->account_id)
{
$GLOBALS['egw']->accounts->delete($this->account_id);
}
}
public function setupShare(&$dir, $extra = array(), $create = 'createShare')
{
// First, create the files to be shared
$this->files[] = $dir = Vfs::get_home_dir() . '/share/';
Vfs::mkdir($dir);
$this->files = $this->addFiles($dir);
// Also create one that should not be accessed
$this->files[] = $this->no_access = Vfs::get_home_dir() . '/not_shared_file.txt';
$this->assertTrue(
file_put_contents(Vfs::PREFIX . $this->no_access, "This file is not shared") !== FALSE,
'Unable to write test file "' . Vfs::PREFIX . $this->no_access . '" - check file permissions for CLI user'
);
// Create and use link
$this->getShareExtra($dir, Sharing::READONLY, $extra);
$share = call_user_func([$this,$create],$dir, Sharing::READONLY, $extra);
$link = Vfs\Sharing::share2link($share);
// Now log out and log in as someone else
Vfs::clearstatcache();
Vfs::init_static();
Vfs\StreamWrapper::init_static();
LoggedInTest::tearDownAfterClass();
return $link;
}
/**
* Create a hidden upload share
*
* @param $path
* @param $mode
* @param array $extra
* @return array
* @throws \EGroupware\Api\Exception\AssertionFailed
*/
protected function createHiddenUploadShare($path, $mode, $extra = array())
{
// Make sure the path is there
if(!Vfs::is_readable($path))
{
$this->assertTrue(
Vfs::is_dir($path) ? Vfs::mkdir($path,0750,true) : Vfs::touch($path),
"Share path $path does not exist"
);
}
// Create share
$this->shares[] = $share = TestHiddenSharing::create('', $path, $mode, $name, $recipients, $extra);
return $share;
}
/**
* Test that a share of a directory only gives access to that directory, and any other
* directories that the sharer has are unavailable
*
* This checks an existing user that is logged in when they follow the share link.
*
* ** CURRENTLY we make a new session anyway, so no changes should be visible on VFS **
*/
public function testShareKeepSession()
{
$dir = '';
$link = $this->setupShare($dir);
LoggedInTest::load_egw($this->account['account_lid'],$this->account['account_passwd']);
Vfs::init_static();
Vfs\StreamWrapper::init_static();
// Check that we can't access the no_access file
$this->assertFalse(Vfs::is_readable($this->no_access), "Could access the not-readable file even before we started.");
// What's our VFS like?
$pre_fstab = Vfs::mount();
$vfs_options = array(
'maxdepth' => 3,
// Exclude a lot of the stuff we're not interested in
'path_preg' => '#^' . Vfs::PREFIX . '\/(?!apps|templates|etemplates|apps-backup).*#'
);
//$pre_files = Vfs::find('/', $vfs_options);
$data = array();
$form = $this->getShare($link, $data, true);
$this->assertNotNull($form, "Could not read the share link");
$rows = array_values($data['data']['content']['nm']['rows']);
$post_mount_vfs = Vfs::mount();
//$post_files = Vfs::find('/', $vfs_options);
// Check that our fstab was not changed
$this->assertEquals(count($pre_fstab), count($post_mount_vfs), "fstab mounts changed");
// Check we can't find the non-shared file in VFS
$this->assertFalse(Vfs::is_readable($this->no_access),
"Could access the not-readable file '$this->no_access' after accessing the share."
);
// Check we can't find the non-shared file in results
$result = array_filter($rows, function($v) {
return $v['name'] == $this->no_access;
});
$this->assertEmpty($result, "Found the file we shouldn't have access to ({$this->no_access})");
// Check that we can find the shared file(s) in the form / nm list
// Don't test the no-access one (done above), and no good way to get the sub-dir file either,
// since nm only has top-level files and we can't switch the filter
$this->checkNextmatch($dir, array_diff($this->files, [$this->no_access, $dir."sub_dir/subdir_test_file.txt"]), $rows);
}
/**
* Test that a share of a directory only gives access to that directory, and any other
* directories that the sharer has are unavailable
*
* This checks from one logged in user to anonymous with a new session
*/
public function testShareNewSession()
{
$dir = '';
$link = $this->setupShare($dir);
// Now follow the link - this _should_ be enough to get it added
//$mimetype = Vfs::mime_content_type($dir);
//$this->checkSharedFile($link, $mimetype);
// Read the etemplate
$data = array();
$form = $this->getShare($link, $data, false);
$this->assertNotNull($form, "Could not read the share link");
$rows = $data['data']['content']['nm']['rows'];
Vfs::clearstatcache();
Vfs::init_static();
Vfs\StreamWrapper::init_static();
// Check we can't find the non-shared file
$result = array_filter($rows, function($v) {
return $v['name'] == $this->no_access;
});
$this->assertEmpty($result, "Found the file we shouldn't have access to ({$this->no_access})");
// Check that we can find the shared file(s) in the form / nm list
// Don't test the no-access one (done above), and no good way to get the sub-dir file either,
// since nm only has top-level files and we can't switch the filter
$this->checkNextmatch($dir, array_diff($this->files, [$this->no_access, $dir."sub_dir/subdir_test_file.txt"]), $rows);
}
/**
* Test that a share of a directory with hidden upload subdirectory only gives access to that directory,
* and the upload directory as well as any other directories that the sharer has are unavailable
*
* This checks from one logged in user to anonymous with a new session
*/
public function testShareHiddenUploadNewSession()
{
$dir = '';
$link = $this->setupShare($dir, [], 'createHiddenUploadShare');
// Now follow the link - this _should_ be enough to get it added
//$mimetype = Vfs::mime_content_type($dir);
//$this->checkSharedFile($link, $mimetype);
// Read the etemplate
$data = array();
$form = $this->getShare($link, $data, false);
$this->assertNotNull($form, "Could not read the share link");
$rows = array_values($data['data']['content']['nm']['rows']);
Vfs::clearstatcache();
Vfs::init_static();
Vfs\StreamWrapper::init_static();
// Check we can't find the non-shared file
$result = array_filter($rows, function($v) {
return $v['name'] == $this->no_access;
});
$this->assertEmpty($result, "Found the file we shouldn't have access to ({$this->no_access})");
// Test that we can't see the hidden upload directory
$result = array_filter($rows, function($v) {
return $v['name'] == 'Upload';
});
$this->assertEmpty($result, "Hidden upload directory is visible");
// Check that we can find the shared file(s) in the form / nm list
// Don't test the no-access one (done above), and no good way to get the sub-dir file either,
// since nm only has top-level files and we can't switch the filter
$this->checkNextmatch($dir, array_diff($this->files, [$this->no_access, $dir."sub_dir/subdir_test_file.txt"]), $rows);
}
/**
* Check the nextmatch rows to see if all the expected files (in the given directory) are present
*
* @param $dir Current working directory, share target
* @param $check_files List of files that should be there
* @param $rows Nextmatch rows
*/
protected function checkNextmatch($dir, $check_files, $rows)
{
foreach($check_files as $file)
{
$relative_file = str_replace($dir,'',$file);
if($relative_file[strlen($relative_file)-1] == '/')
{
$relative_file = substr($relative_file, 0, -1);
}
$result = array_filter($rows, function($v) use ($relative_file) {
return $v['name'] == $relative_file;
});
$this->assertNotEmpty($result, "Couldn't find shared file '$file'");
}
}
/**
* Test that a share of a single file gives the file (uses WebDAV)
*/
public function testSingleFile()
{
$dir = Vfs::get_home_dir().'/';
// Plain text file
$file = $dir.'test_file.txt';
$content = 'Testing that sharing a single (non-editable) file gives us the file.';
$this->assertTrue(
file_put_contents(Vfs::PREFIX.$file, $content) !== FALSE,
'Unable to write test file "' . Vfs::PREFIX . $file .'" - check file permissions for CLI user'
);
$this->files[] = $file;
$mimetype = Vfs::mime_content_type($file);
// Create and use link
$extra = array();
$this->getShareExtra($file, Sharing::READONLY, $extra);
$share = $this->createShare($file, Sharing::READONLY, $extra);
$link = Vfs\Sharing::share2link($share);
// Re-init, since they look at user, fstab, etc.
// Also, further tests that access the filesystem fail if we don't
Vfs::clearstatcache();
Vfs::init_static();
Vfs\StreamWrapper::init_static();
// Log out & clear cache
LoggedInTest::tearDownAfterClass();
$this->checkSharedFile($link, $mimetype);
}
}