diff options
-rw-r--r-- | apps/files/command/scan.php | 36 | ||||
-rw-r--r-- | apps/files_encryption/hooks/hooks.php | 51 | ||||
-rwxr-xr-x | apps/files_encryption/lib/helper.php | 26 | ||||
-rwxr-xr-x | apps/files_encryption/lib/keymanager.php | 59 | ||||
-rw-r--r-- | apps/files_encryption/tests/hooks.php | 2 | ||||
-rw-r--r-- | apps/files_encryption/tests/keymanager.php | 69 | ||||
-rw-r--r-- | core/css/share.css | 2 | ||||
-rw-r--r-- | core/js/share.js | 6 | ||||
-rw-r--r-- | lib/private/app.php | 4 | ||||
-rw-r--r-- | lib/private/files/utils/scanner.php | 11 | ||||
-rw-r--r-- | remote.php | 9 |
11 files changed, 149 insertions, 126 deletions
diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php index 25ab70af362..3412cf80dea 100644 --- a/apps/files/command/scan.php +++ b/apps/files/command/scan.php @@ -9,6 +9,7 @@ namespace OCA\Files\Command; +use OC\ForbiddenException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -32,28 +33,32 @@ class Scan extends Command { ->setName('files:scan') ->setDescription('rescan filesystem') ->addArgument( - 'user_id', - InputArgument::OPTIONAL | InputArgument::IS_ARRAY, - 'will rescan all files of the given user(s)' - ) + 'user_id', + InputArgument::OPTIONAL | InputArgument::IS_ARRAY, + 'will rescan all files of the given user(s)' + ) ->addOption( - 'all', - null, - InputOption::VALUE_NONE, - 'will rescan all files of all known users' - ) - ; + 'all', + null, + InputOption::VALUE_NONE, + 'will rescan all files of all known users' + ); } protected function scanFiles($user, OutputInterface $output) { $scanner = new \OC\Files\Utils\Scanner($user); - $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function($path) use ($output) { + $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { $output->writeln("Scanning <info>$path</info>"); }); - $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function($path) use ($output) { + $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { $output->writeln("Scanning <info>$path</info>"); }); - $scanner->scan(''); + try { + $scanner->scan(''); + } catch (ForbiddenException $e) { + $output->writeln("<error>Home storage for user $user not writable</error>"); + $output->writeln("Make sure you're running the scan command only as the user the web server runs as"); + } } protected function execute(InputInterface $input, OutputInterface $output) { @@ -63,6 +68,11 @@ class Scan extends Command { $users = $input->getArgument('user_id'); } + if (count($users) === 0) { + $output->writeln("<error>Please specify the user id to scan or \"--all\" to scan for all users</error>"); + return; + } + foreach ($users as $user) { if (is_object($user)) { $user = $user->getUID(); diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index df101acab9d..3625d5a09f3 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -464,61 +464,44 @@ class Hooks { $newShareKeyPath = $ownerNew . '/files_encryption/share-keys/' . $pathNew;
}
- // add key ext if this is not an folder
+ // create new key folders if it doesn't exists
+ if (!$view->file_exists(dirname($newShareKeyPath))) {
+ $view->mkdir(dirname($newShareKeyPath));
+ }
+ if (!$view->file_exists(dirname($newKeyfilePath))) {
+ $view->mkdir(dirname($newKeyfilePath));
+ }
+
+ // handle share keys
if (!$view->is_dir($oldKeyfilePath)) {
$oldKeyfilePath .= '.key';
$newKeyfilePath .= '.key';
// handle share-keys
- $localKeyPath = $view->getLocalFile($oldShareKeyPath);
- $escapedPath = Helper::escapeGlobPattern($localKeyPath);
- $matches = glob($escapedPath . '*.shareKey');
+ $matches = Helper::findShareKeys($oldShareKeyPath, $view);
foreach ($matches as $src) {
$dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src));
-
- // create destination folder if not exists
- if (!file_exists(dirname($dst))) {
- mkdir(dirname($dst), 0750, true);
- }
-
- rename($src, $dst);
+ $view->rename($src, $dst);
}
} else {
// handle share-keys folders
-
- // create destination folder if not exists
- if (!$view->file_exists(dirname($newShareKeyPath))) {
- mkdir($view->getLocalFile($newShareKeyPath), 0750, true);
- }
-
$view->rename($oldShareKeyPath, $newShareKeyPath);
}
// Rename keyfile so it isn't orphaned
if ($view->file_exists($oldKeyfilePath)) {
-
- // create destination folder if not exists
- if (!$view->file_exists(dirname($newKeyfilePath))) {
- mkdir(dirname($view->getLocalFile($newKeyfilePath)), 0750, true);
- }
-
$view->rename($oldKeyfilePath, $newKeyfilePath);
}
- // build the path to the file
- $newPath = '/' . $ownerNew . '/files' . $pathNew;
+ // update share keys
+ $sharingEnabled = \OCP\Share::isEnabled();
- if ($util->fixFileSize($newPath)) {
- // get sharing app state
- $sharingEnabled = \OCP\Share::isEnabled();
-
- // get users
- $usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew);
+ // get users
+ $usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew);
- // update sharing-keys
- $util->setSharedFileKeyfiles($session, $usersSharing, $pathNew);
- }
+ // update sharing-keys
+ $util->setSharedFileKeyfiles($session, $usersSharing, $pathNew);
\OC_FileProxy::$enabled = $proxyStatus;
}
diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index 2684bf7be33..fed0788028f 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -430,12 +430,28 @@ class Helper { } /** - * glob uses different pattern than regular expressions, escape glob pattern only - * @param string $path unescaped path - * @return string path + * find all share keys for a given file + * @param string $path to the file + * @param \OC\Files\View $view view, relative to data/ + * @return array list of files, path relative to data/ */ - public static function escapeGlobPattern($path) { - return preg_replace('/(\*|\?|\[)/', '[$1]', $path); + public static function findShareKeys($path, $view) { + $result = array(); + $pathinfo = pathinfo($path); + $dirContent = $view->opendir($pathinfo['dirname']); + + if (is_resource($dirContent)) { + while (($file = readdir($dirContent)) !== false) { + if (!\OC\Files\Filesystem::isIgnoredDir($file)) { + if (preg_match("/" . $pathinfo['filename'] . ".(.*).shareKey/", $file)) { + $result[] = $pathinfo['dirname'] . '/' . $file; + } + } + } + closedir($dirContent); + } + + return $result; } /** diff --git a/apps/files_encryption/lib/keymanager.php b/apps/files_encryption/lib/keymanager.php index 98986d1486f..e71fec56854 100755 --- a/apps/files_encryption/lib/keymanager.php +++ b/apps/files_encryption/lib/keymanager.php @@ -133,20 +133,7 @@ class Keymanager { $basePath = '/' . $owner . '/files_encryption/keyfiles'; } - $targetPath = self::keySetPreparation($view, $filename, $basePath, $owner); - - if (!$view->is_dir($basePath . '/' . $targetPath)) { - - // create all parent folders - $info = pathinfo($basePath . '/' . $targetPath); - $keyfileFolderName = $view->getLocalFolder($info['dirname']); - - if (!file_exists($keyfileFolderName)) { - - mkdir($keyfileFolderName, 0750, true); - - } - } + $targetPath = self::keySetPreparation($view, $filename, $basePath); // try reusing key file if part file if (Helper::isPartialFilePath($targetPath)) { @@ -281,8 +268,9 @@ class Keymanager { $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; - if (!$view->file_exists('')) + if (!$view->file_exists('')) { $view->mkdir(''); + } $result = $view->file_put_contents($user . '.private.key', $key); @@ -340,7 +328,7 @@ class Keymanager { $basePath = '/' . $owner . '/files_encryption/share-keys'; } - $shareKeyPath = self::keySetPreparation($view, $filename, $basePath, $owner); + $shareKeyPath = self::keySetPreparation($view, $filename, $basePath); $result = true; @@ -466,8 +454,7 @@ class Keymanager { if ($view->is_dir($shareKeyPath)) { - $localPath = \OC\Files\Filesystem::normalizePath($view->getLocalFolder($shareKeyPath)); - self::recursiveDelShareKeys($localPath, $userIds); + self::recursiveDelShareKeys($shareKeyPath, $userIds, $view); } else { @@ -491,23 +478,25 @@ class Keymanager { * @param string $dir directory * @param array $userIds user ids for which the share keys should be deleted */ - private static function recursiveDelShareKeys($dir, $userIds) { - foreach ($userIds as $userId) { - $extension = '.' . $userId . '.shareKey'; - $escapedDir = Helper::escapeGlobPattern($dir); - $escapedExtension = Helper::escapeGlobPattern($extension); - $matches = glob($escapedDir . '/*' . $escapedExtension); - } - /** @var $matches array */ - foreach ($matches as $ma) { - if (!unlink($ma)) { - \OCP\Util::writeLog('Encryption library', - 'Could not delete shareKey; does not exist: "' . $ma . '"', \OCP\Util::ERROR); + private static function recursiveDelShareKeys($dir, $userIds, $view) { + + $dirContent = $view->opendir($dir); + + if (is_resource($dirContent)) { + while (($file = readdir($dirContent)) !== false) { + if (!\OC\Files\Filesystem::isIgnoredDir($file)) { + if ($view->is_dir($dir . '/' . $file)) { + self::recursiveDelShareKeys($dir . '/' . $file, $userIds, $view); + } else { + foreach ($userIds as $userId) { + if (preg_match("/(.*)." . $userId . ".shareKey/", $file)) { + $view->unlink($dir . '/' . $file); + } + } + } + } } - } - $subdirs = glob($escapedDir . '/*', GLOB_ONLYDIR); - foreach ($subdirs as $subdir) { - self::recursiveDelShareKeys($subdir, $userIds); + closedir($dirContent); } } @@ -516,7 +505,7 @@ class Keymanager { * @param string|boolean $path * @param string $basePath */ - protected static function keySetPreparation(\OC\Files\View $view, $path, $basePath, $userId) { + protected static function keySetPreparation(\OC\Files\View $view, $path, $basePath) { $targetPath = ltrim($path, '/'); diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index 79699a3ef35..5eda8df01b9 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -307,7 +307,7 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { $this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder)); - // move the file out of the shared folder + // move the file to the sub-subfolder $root = $this->rootView->getRoot(); $this->rootView->chroot('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/'); $this->rootView->rename($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename); diff --git a/apps/files_encryption/tests/keymanager.php b/apps/files_encryption/tests/keymanager.php index 0c025443cd6..3339f8c73e6 100644 --- a/apps/files_encryption/tests/keymanager.php +++ b/apps/files_encryption/tests/keymanager.php @@ -206,55 +206,56 @@ class Test_Encryption_Keymanager extends \PHPUnit_Framework_TestCase { */ function testRecursiveDelShareKeys() { - // generate filename - $filename = '/tmp-' . uniqid() . '.txt'; - // create folder structure - $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1'); - $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder'); - $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder/subsubfolder'); - - // enable encryption proxy - $proxyStatus = \OC_FileProxy::$enabled; - \OC_FileProxy::$enabled = true; - - // save file with content - $cryptedFile = file_put_contents('crypt:///'.Test_Encryption_Keymanager::TEST_USER.'/files/folder1/subfolder/subsubfolder' . $filename, $this->dataShort); - - // test that data was successfully written - $this->assertTrue(is_int($cryptedFile)); - - // change encryption proxy to previous state - \OC_FileProxy::$enabled = $proxyStatus; - - // recursive delete keys - Encryption\Keymanager::delShareKey($this->view, array('admin'), '/folder1/'); - - // check if share key not exists + $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1'); + $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder'); + $this->view->mkdir('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder'); + + // create some dummy share keys + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user2.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user3.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file1.user1.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user2.shareKey', 'data'); + $this->view->file_put_contents('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey', 'data'); + + // recursive delete share keys from user1 and user2 + Encryption\Keymanager::delShareKey($this->view, array('user1', 'user2'), '/folder1/'); + + // check if share keys from user1 and user2 are deleted + $this->assertFalse($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file1.user1.shareKey')); + $this->assertFalse($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user2.shareKey')); + $this->assertFalse($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file1.user1.shareKey')); $this->assertFalse($this->view->file_exists( - '/admin/files_encryption/share-keys/folder1/subfolder/subsubfolder/' . $filename . '.admin.shareKey')); + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user2.shareKey')); - // enable encryption proxy - $proxyStatus = \OC_FileProxy::$enabled; - \OC_FileProxy::$enabled = true; + // check if share keys from user3 still exists + $this->assertTrue($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/file2.user3.shareKey')); + $this->assertTrue($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/subsubfolder/file2.user3.shareKey')); + $this->assertTrue($this->view->file_exists( + '/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys/folder1/subfolder/file2.user3.shareKey')); // cleanup - $this->view->deleteAll('/admin/files/folder1'); + $this->view->deleteAll('/'.Test_Encryption_Keymanager::TEST_USER.'/files_encryption/share-keys'); - // change encryption proxy to previous state - \OC_FileProxy::$enabled = $proxyStatus; } function testKeySetPreperation() { $basePath = '/'.Test_Encryption_Keymanager::TEST_USER.'/files'; - $path = '/folder1/subfolder/subsubfolder'; + $path = '/folder1/subfolder/subsubfolder/file.txt'; $this->assertFalse($this->view->is_dir($basePath . '/testKeySetPreperation')); $result = TestProtectedKeymanagerMethods::testKeySetPreperation($this->view, $path, $basePath); // return path without leading slash - $this->assertSame('folder1/subfolder/subsubfolder', $result); + $this->assertSame('folder1/subfolder/subsubfolder/file.txt', $result); // check if directory structure was created $this->assertTrue($this->view->is_dir($basePath . '/folder1/subfolder/subsubfolder')); @@ -283,6 +284,6 @@ class TestProtectedKeymanagerMethods extends \OCA\Encryption\Keymanager { * @param string $basePath */ public static function testKeySetPreperation($view, $path, $basePath) { - return self::keySetPreparation($view, $path, $basePath, ''); + return self::keySetPreparation($view, $path, $basePath); } } diff --git a/core/css/share.css b/core/css/share.css index 0859c195858..314c6140d78 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -35,6 +35,8 @@ #shareWithList label input[type=checkbox]{ margin-left: 0; + top: 3px; + position: relative; } #shareWithList .username{ padding-right: 8px; diff --git a/core/js/share.js b/core/js/share.js index e164602d0ab..1c59524939c 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -569,6 +569,9 @@ OC.Share={ } html += '<label><input type="checkbox" name="mailNotification" class="mailNotification" ' + checked + ' />'+t('core', 'notify by email')+'</label> '; } + if (possiblePermissions & OC.PERMISSION_SHARE) { + html += '<label><input type="checkbox" name="share" class="permissions" '+shareChecked+' data-permissions="'+OC.PERMISSION_SHARE+'" />'+t('core', 'can share')+'</label>'; + } if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) { html += '<label><input type="checkbox" name="edit" class="permissions" '+editChecked+' />'+t('core', 'can edit')+'</label> '; } @@ -583,9 +586,6 @@ OC.Share={ if (possiblePermissions & OC.PERMISSION_DELETE) { html += '<label><input type="checkbox" name="delete" class="permissions" '+deleteChecked+' data-permissions="'+OC.PERMISSION_DELETE+'" />'+t('core', 'delete')+'</label>'; } - if (possiblePermissions & OC.PERMISSION_SHARE) { - html += '<label><input type="checkbox" name="share" class="permissions" '+shareChecked+' data-permissions="'+OC.PERMISSION_SHARE+'" />'+t('core', 'share')+'</label>'; - } html += '</div>'; html += '</li>'; html = $(html).appendTo('#shareWithList'); diff --git a/lib/private/app.php b/lib/private/app.php index 01597b37e77..9fb0ec2e34f 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -509,6 +509,10 @@ class OC_App { * @return string|false */ public static function getAppPath($appid) { + if ($appid === null || trim($appid) === '') { + return false; + } + if (($dir = self::findAppInDirectories($appid)) != false) { return $dir['path'] . '/' . $appid; } diff --git a/lib/private/files/utils/scanner.php b/lib/private/files/utils/scanner.php index 1bb3e694c96..c2fabf51946 100644 --- a/lib/private/files/utils/scanner.php +++ b/lib/private/files/utils/scanner.php @@ -11,6 +11,7 @@ namespace OC\Files\Utils; use OC\Files\View; use OC\Files\Cache\ChangePropagator; use OC\Files\Filesystem; +use OC\ForbiddenException; use OC\Hooks\PublicEmitter; /** @@ -104,6 +105,7 @@ class Scanner extends PublicEmitter { /** * @param string $dir + * @throws \OC\ForbiddenException */ public function scan($dir) { $mounts = $this->getMounts($dir); @@ -111,7 +113,14 @@ class Scanner extends PublicEmitter { if (is_null($mount->getStorage())) { continue; } - $scanner = $mount->getStorage()->getScanner(); + $storage = $mount->getStorage(); + // if the home storage isn't writable then the scanner is run as the wrong user + if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and + (!$storage->isCreatable('') or !$storage->isCreatable('files')) + ) { + throw new ForbiddenException(); + } + $scanner = $storage->getScanner(); $this->attachListener($mount); $scanner->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE, \OC\Files\Cache\Scanner::REUSE_ETAG | \OC\Files\Cache\Scanner::REUSE_SIZE); } diff --git a/remote.php b/remote.php index 232e47ee402..a91742b0475 100644 --- a/remote.php +++ b/remote.php @@ -2,6 +2,15 @@ try { require_once 'lib/base.php'; + + if (\OCP\Util::needUpgrade()) { + // since the behavior of apps or remotes are unpredictable during + // an upgrade, return a 503 directly + OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE); + OC_Template::printErrorPage('Service unavailable'); + exit; + } + $path_info = OC_Request::getPathInfo(); if ($path_info === false || $path_info === '') { OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); |