Przeglądaj źródła

update share keys if a file is moved to a shared folder

tags/v8.1.0beta1
Bjoern Schiessle 9 lat temu
rodzic
commit
2990b0e07e

+ 2
- 2
lib/private/encryption/file.php Wyświetl plik

@@ -36,7 +36,7 @@ class File implements \OCP\Encryption\IFile {
* get list of users with access to the file
*
* @param string $path to the file
* @return array
* @return array ['users' => $uniqueUserIds, 'public' => $public]
*/
public function getAccessList($path) {

@@ -46,7 +46,7 @@ class File implements \OCP\Encryption\IFile {
// always add owner to the list of users with access to the file
$userIds = array($owner);

if (!$this->util->isFile($ownerPath)) {
if (!$this->util->isFile($owner . '/' . $ownerPath)) {
return array('users' => $userIds, 'public' => false);
}


+ 19
- 1
lib/private/encryption/manager.php Wyświetl plik

@@ -22,6 +22,7 @@

namespace OC\Encryption;

use OC\Files\Filesystem;
use OC\Files\Storage\Shared;
use OC\Files\Storage\Wrapper\Encryption;
use OC\Files\View;
@@ -222,7 +223,24 @@ class Manager implements IManager {
$uid = $user ? $user->getUID() : null;
$fileHelper = \OC::$server->getEncryptionFilesHelper();
$keyStorage = \OC::$server->getEncryptionKeyStorage();
return new Encryption($parameters, $manager, $util, $logger, $fileHelper, $uid, $keyStorage);
$update = new Update(
new View(),
$util,
Filesystem::getMountManager(),
$manager,
$fileHelper,
$uid
);
return new Encryption(
$parameters,
$manager,
$util,
$logger,
$fileHelper,
$uid,
$keyStorage,
$update
);
} else {
return $storage;
}

+ 47
- 19
lib/private/encryption/update.php Wyświetl plik

@@ -22,6 +22,7 @@

namespace OC\Encryption;

use OC\Files\Filesystem;
use \OC\Files\Mount;
use \OC\Files\View;

@@ -74,46 +75,73 @@ class Update {
$this->uid = $uid;
}

/**
* hook after file was shared
*
* @param array $params
*/
public function postShared($params) {
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$this->update($params['fileSource']);
$path = Filesystem::getPath($params['fileSource']);
list($owner, $ownerPath) = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($absPath);
}
}

/**
* hook after file was unshared
*
* @param array $params
*/
public function postUnshared($params) {
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$this->update($params['fileSource']);
$path = Filesystem::getPath($params['fileSource']);
list($owner, $ownerPath) = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($absPath);
}
}

/**
* update keyfiles and share keys recursively
* get owner and path relative to data/<owner>/files
*
* @param int $fileSource file source id
* @param string $path path to file for current user
* @return array ['owner' => $owner, 'path' => $path]
* @throw \InvalidArgumentException
*/
private function update($fileSource) {
$path = \OC\Files\Filesystem::getPath($fileSource);
$info = \OC\Files\Filesystem::getFileInfo($path);
$owner = \OC\Files\Filesystem::getOwner($path);
$view = new \OC\Files\View('/' . $owner . '/files');
$ownerPath = $view->getPath($info->getId());
$absPath = '/' . $owner . '/files' . $ownerPath;
private function getOwnerPath($path) {
$info = Filesystem::getFileInfo($path);
$owner = Filesystem::getOwner($path);
$view = new View('/' . $owner . '/files');
$path = $view->getPath($info->getId());
if ($path === null) {
throw new \InvalidArgumentException('No file found for ' . $info->getId());
}

return array($owner, $path);
}

$mount = $this->mountManager->find($path);
$mountPoint = $mount->getMountPoint();
/**
* notify encryption module about added/removed users from a file/folder
*
* @param string $path relative to data/
* @throws Exceptions\ModuleDoesNotExistsException
*/
public function update($path) {

// if a folder was shared, get a list of all (sub-)folders
if ($this->view->is_dir($absPath)) {
$allFiles = $this->util->getAllFiles($absPath, $mountPoint);
if ($this->view->is_dir($path)) {
$allFiles = $this->util->getAllFiles($path);
} else {
$allFiles = array($absPath);
$allFiles = array($path);
}

$encryptionModule = $this->encryptionManager->getDefaultEncryptionModule();

foreach ($allFiles as $path) {
$usersSharing = $this->file->getAccessList($path);
$encryptionModule->update($path, $this->uid, $usersSharing);
foreach ($allFiles as $file) {
$usersSharing = $this->file->getAccessList($file);
$encryptionModule->update($file, $this->uid, $usersSharing);
}
}


+ 9
- 11
lib/private/encryption/util.php Wyświetl plik

@@ -25,6 +25,7 @@ namespace OC\Encryption;
use OC\Encryption\Exceptions\EncryptionHeaderKeyExistsException;
use OC\Encryption\Exceptions\EncryptionHeaderToLargeException;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Files\Filesystem;
use OC\Files\View;
use OCP\Encryption\IEncryptionModule;
use OCP\IConfig;
@@ -181,10 +182,9 @@ class Util {
* go recursively through a dir and collect all files and sub files.
*
* @param string $dir relative to the users files folder
* @param string $mountPoint
* @return array with list of files relative to the users files folder
*/
public function getAllFiles($dir, $mountPoint = '') {
public function getAllFiles($dir) {
$result = array();
$dirList = array($dir);

@@ -193,13 +193,10 @@ class Util {
$content = $this->view->getDirectoryContent($dir);

foreach ($content as $c) {
// getDirectoryContent() returns the paths relative to the mount points, so we need
// to re-construct the complete path
$path = ($mountPoint !== '') ? $mountPoint . '/' . $c->getPath() : $c->getPath();
if ($c['type'] === 'dir') {
$dirList[] = \OC\Files\Filesystem::normalizePath($path);
if ($c->getType() === 'dir') {
$dirList[] = $c->getPath();
} else {
$result[] = \OC\Files\Filesystem::normalizePath($path);
$result[] = $c->getPath();
}
}

@@ -212,11 +209,12 @@ class Util {
* check if it is a file uploaded by the user stored in data/user/files
* or a metadata file
*
* @param string $path
* @param string $path relative to the data/ folder
* @return boolean
*/
public function isFile($path) {
if (substr($path, 0, strlen('/files/')) === '/files/') {
$parts = explode('/', Filesystem::normalizePath($path), 4);
if (isset($parts[2]) && $parts[2] === 'files') {
return true;
}
return false;
@@ -262,7 +260,7 @@ class Util {

$ownerPath = implode('/', array_slice($parts, 2));

return array($uid, \OC\Files\Filesystem::normalizePath($ownerPath));
return array($uid, Filesystem::normalizePath($ownerPath));

}


+ 21
- 7
lib/private/files/storage/wrapper/encryption.php Wyświetl plik

@@ -24,8 +24,13 @@ namespace OC\Files\Storage\Wrapper;

use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Encryption\File;
use OC\Encryption\Manager;
use OC\Encryption\Update;
use OC\Encryption\Util;
use OC\Files\Filesystem;
use OC\Files\Storage\LocalTempFileTrait;
use OC\Log;
use OCP\Encryption\Keys\IStorage;
use OCP\Files\Mount\IMountPoint;

class Encryption extends Wrapper {
@@ -59,6 +64,9 @@ class Encryption extends Wrapper {
/** @var \OCP\Encryption\Keys\IStorage */
private $keyStorage;

/** @var \OC\Encryption\Update */
private $update;

/**
* @param array $parameters
* @param \OC\Encryption\Manager $encryptionManager
@@ -66,15 +74,18 @@ class Encryption extends Wrapper {
* @param \OC\Log $logger
* @param File $fileHelper
* @param string $uid user who perform the read/write operation (null for public access)
* @param IStorage $keyStorage
* @param Update $update
*/
public function __construct(
$parameters,
\OC\Encryption\Manager $encryptionManager = null,
\OC\Encryption\Util $util = null,
\OC\Log $logger = null,
Manager $encryptionManager = null,
Util $util = null,
Log $logger = null,
File $fileHelper = null,
$uid = null,
$keyStorage = null
IStorage $keyStorage = null,
Update $update = null
) {

$this->mountPoint = $parameters['mountPoint'];
@@ -86,6 +97,7 @@ class Encryption extends Wrapper {
$this->fileHelper = $fileHelper;
$this->keyStorage = $keyStorage;
$this->unencryptedSize = array();
$this->update = $update;
parent::__construct($parameters);
}

@@ -207,12 +219,11 @@ class Encryption extends Wrapper {
* @return bool
*/
public function rename($path1, $path2) {
$fullPath1 = $this->getFullPath($path1);
if ($this->util->isExcluded($fullPath1)) {
$source = $this->getFullPath($path1);
if ($this->util->isExcluded($source)) {
return $this->storage->rename($path1, $path2);
}

$source = $this->getFullPath($path1);
$result = $this->storage->rename($path1, $path2);
if ($result) {
$target = $this->getFullPath($path2);
@@ -220,6 +231,9 @@ class Encryption extends Wrapper {
$this->unencryptedSize[$target] = $this->unencryptedSize[$source];
}
$this->keyStorage->renameKeys($source, $target);
if (dirname($source) !== dirname($target) && $this->util->isFile($target)) {
$this->update->update($target);
}
}

return $result;

+ 129
- 0
tests/lib/encryption/updatetest.php Wyświetl plik

@@ -0,0 +1,129 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/


namespace Test\Encryption;


use OC\Encryption\Update;
use Test\TestCase;

class UpdateTest extends TestCase {

/** @var \OC\Encryption\Update */
private $update;

/** @var string */
private $uid;

/** @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject */
private $view;

/** @var \OC\Encryption\Util | \PHPUnit_Framework_MockObject_MockObject */
private $util;

/** @var \OC\Files\Mount\Manager | \PHPUnit_Framework_MockObject_MockObject */
private $mountManager;

/** @var \OC\Encryption\Manager | \PHPUnit_Framework_MockObject_MockObject */
private $encryptionManager;

/** @var \OCP\Encryption\IEncryptionModule | \PHPUnit_Framework_MockObject_MockObject */
private $encryptionModule;

/** @var \OC\Encryption\File | \PHPUnit_Framework_MockObject_MockObject */
private $fileHelper;

public function setUp() {
parent::setUp();

$this->view = $this->getMockBuilder('\OC\Files\View')
->disableOriginalConstructor()->getMock();
$this->util = $this->getMockBuilder('\OC\Encryption\Util')
->disableOriginalConstructor()->getMock();
$this->mountManager = $this->getMockBuilder('\OC\Files\Mount\Manager')
->disableOriginalConstructor()->getMock();
$this->encryptionManager = $this->getMockBuilder('\OC\Encryption\Manager')
->disableOriginalConstructor()->getMock();
$this->fileHelper = $this->getMockBuilder('\OC\Encryption\File')
->disableOriginalConstructor()->getMock();
$this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
->disableOriginalConstructor()->getMock();

$this->encryptionManager->expects($this->once())
->method('getDefaultEncryptionModule')
->willReturn($this->encryptionModule);

$this->uid = 'testUser1';

$this->update = new Update(
$this->view,
$this->util,
$this->mountManager,
$this->encryptionManager,
$this->fileHelper,
$this->uid);
}

/**
* @dataProvider dataTestUpdate
*
* @param string $path
* @param boolean $isDir
* @param array $allFiles
* @param integer $numberOfFiles
*/
public function testUpdate($path, $isDir, $allFiles, $numberOfFiles) {

$this->view->expects($this->once())
->method('is_dir')
->willReturn($isDir);

if($isDir) {
$this->util->expects($this->once())
->method('getAllFiles')
->willReturn($allFiles);
}

$this->fileHelper->expects($this->exactly($numberOfFiles))
->method('getAccessList')
->willReturn(['users' => [], 'public' => false]);

$this->encryptionModule->expects($this->exactly($numberOfFiles))
->method('update')
->willReturn(true);

$this->update->update($path);
}

/**
* data provider for testUpdate()
*
* @return array
*/
public function dataTestUpdate() {
return array(
array('/user/files/foo', true, ['/user/files/foo/file1.txt', '/user/files/foo/file1.txt'], 2),
array('/user/files/test.txt', false, [], 1),
);
}

}

+ 21
- 0
tests/lib/encryption/utiltest.php Wyświetl plik

@@ -152,4 +152,25 @@ class UtilTest extends TestCase {
return false;
}

/**
* @dataProvider dataTestIsFile
*/
public function testIsFile($path, $expected) {
$this->assertSame($expected,
$this->util->isFile($path)
);
}

public function dataTestIsFile() {
return array(
array('/user/files/test.txt', true),
array('/user/files', true),
array('/user/files_versions/test.txt', false),
array('/user/foo/files/test.txt', false),
array('/files/foo/files/test.txt', false),
array('/user', false),
array('/user/test.txt', false),
);
}

}

+ 53
- 6
tests/lib/files/storage/wrapper/encryption.php Wyświetl plik

@@ -12,11 +12,26 @@ class Encryption extends \Test\Files\Storage\Storage {
*/
private $sourceStorage;

/**
* @var \OC\Files\Storage\Wrapper\Encryption
*/
protected $instance;

/**
* @var \OC\Encryption\Keys\Storage | \PHPUnit_Framework_MockObject_MockObject
*/
private $keyStore;

/**
* @var \OC\Encryption\Util | \PHPUnit_Framework_MockObject_MockObject
*/
private $util;

/**
* @var \OC\Encryption\Update | \PHPUnit_Framework_MockObject_MockObject
*/
private $update;

public function setUp() {

parent::setUp();
@@ -43,8 +58,8 @@ class Encryption extends \Test\Files\Storage\Storage {
->disableOriginalConstructor()
->getMock();

$util = $this->getMock('\OC\Encryption\Util', ['getUidAndFilename'], [new View(), new \OC\User\Manager(), $groupManager, $config]);
$util->expects($this->any())
$this->util = $this->getMock('\OC\Encryption\Util', ['getUidAndFilename', 'isFile'], [new View(), new \OC\User\Manager(), $groupManager, $config]);
$this->util->expects($this->any())
->method('getUidAndFilename')
->willReturnCallback(function ($path) {
return ['user1', $path];
@@ -61,6 +76,8 @@ class Encryption extends \Test\Files\Storage\Storage {
$this->sourceStorage = new Temporary(array());
$this->keyStore = $this->getMockBuilder('\OC\Encryption\Keys\Storage')
->disableOriginalConstructor()->getMock();
$this->update = $this->getMockBuilder('\OC\Encryption\Update')
->disableOriginalConstructor()->getMock();
$mount = $this->getMockBuilder('\OC\Files\Mount\MountPoint')
->disableOriginalConstructor()
->setMethods(['getOption'])
@@ -72,7 +89,7 @@ class Encryption extends \Test\Files\Storage\Storage {
'mountPoint' => '/',
'mount' => $mount
],
$encryptionManager, $util, $logger, $file, null, $this->keyStore
$encryptionManager, $this->util, $logger, $file, null, $this->keyStore, $this->update
);
}

@@ -97,11 +114,41 @@ class Encryption extends \Test\Files\Storage\Storage {
return $encryptionModule;
}

public function testRename() {
/**
* @dataProvider dataTestRename
*
* @param string $source
* @param string $target
* @param boolean $shouldUpdate
*/
public function testRename($source, $target, $shouldUpdate) {
$this->keyStore
->expects($this->once())
->method('renameKeys');
$this->instance->mkdir('folder');
$this->instance->rename('folder', 'flodder');
$this->util->expects($this->any())
->method('isFile')->willReturn(true);
if ($shouldUpdate) {
$this->update->expects($this->once())
->method('update');
} else {
$this->update->expects($this->never())
->method('update');
}

$this->instance->mkdir($source);
$this->instance->mkdir(dirname($target));
$this->instance->rename($source, $target);
}

/**
* data provider for testRename()
*
* @return array
*/
public function dataTestRename() {
return array(
array('source', 'target', false),
array('source', '/subFolder/target', true),
);
}
}

Ładowanie…
Anuluj
Zapisz