]> source.dussan.org Git - nextcloud-server.git/commitdiff
Add storage and cache wrappers to jail a storage to a subfolder
authorRobin Appelman <icewind@owncloud.com>
Mon, 10 Nov 2014 15:00:08 +0000 (16:00 +0100)
committerRobin Appelman <icewind@owncloud.com>
Thu, 27 Nov 2014 14:25:53 +0000 (15:25 +0100)
lib/private/files/cache/cache.php
lib/private/files/cache/wrapper/cachejail.php [new file with mode: 0644]
lib/private/files/cache/wrapper/cachewrapper.php [new file with mode: 0644]
lib/private/files/storage/wrapper/jail.php [new file with mode: 0644]
lib/private/files/storage/wrapper/permissionsmask.php [new file with mode: 0644]
tests/lib/files/cache/cache.php
tests/lib/files/cache/wrapper/cachejail.php [new file with mode: 0644]
tests/lib/files/storage/wrapper/jail.php [new file with mode: 0644]

index 2c12f834518191cdc6ad15dcd5cd89043e924e28..4157da2281cd7d6d6fb7fcd42f2236bb0d022340 100644 (file)
@@ -585,7 +585,7 @@ class Cache {
        /**
         * find a folder in the cache which has not been fully scanned
         *
-        * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
+        * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
         * use the one with the highest id gives the best result with the background scanner, since that is most
         * likely the folder where we stopped scanning previously
         *
diff --git a/lib/private/files/cache/wrapper/cachejail.php b/lib/private/files/cache/wrapper/cachejail.php
new file mode 100644 (file)
index 0000000..7982293
--- /dev/null
@@ -0,0 +1,255 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Cache\Wrapper;
+
+/**
+ * Jail to a subdirectory of the wrapped cache
+ */
+class CacheJail extends CacheWrapper {
+       /**
+        * @var string
+        */
+       protected $root;
+
+       /**
+        * @param \OC\Files\Cache\Cache $cache
+        * @param string $root
+        */
+       public function __construct($cache, $root) {
+               parent::__construct($cache);
+               $this->root = $root;
+       }
+
+       protected function getSourcePath($path) {
+               if ($path === '') {
+                       return $this->root;
+               } else {
+                       return $this->root . '/' . $path;
+               }
+       }
+
+       /**
+        * @param string $path
+        * @return null|string the jailed path or null if the path is outside the jail
+        */
+       protected function getJailedPath($path) {
+               $rootLength = strlen($this->root) + 1;
+               if ($path === $this->root) {
+                       return '';
+               } else if (substr($path, 0, $rootLength) === $this->root . '/') {
+                       return substr($path, $rootLength);
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * @param array $entry
+        * @return array
+        */
+       protected function formatCacheEntry($entry) {
+               if (isset($entry['path'])) {
+                       $entry['path'] = $this->getJailedPath($entry['path']);
+               }
+               return $entry;
+       }
+
+       protected function filterCacheEntry($entry) {
+               $rootLength = strlen($this->root) + 1;
+               return ($entry['path'] === $this->root) or (substr($entry['path'], 0, $rootLength) === $this->root . '/');
+       }
+
+       /**
+        * get the stored metadata of a file or folder
+        *
+        * @param string /int $file
+        * @return array|false
+        */
+       public function get($file) {
+               if (is_string($file) or $file == '') {
+                       $file = $this->getSourcePath($file);
+               }
+               return parent::get($file);
+       }
+
+       /**
+        * store meta data for a file or folder
+        *
+        * @param string $file
+        * @param array $data
+        *
+        * @return int file id
+        */
+       public function put($file, array $data) {
+               return $this->cache->put($this->getSourcePath($file), $data);
+       }
+
+       /**
+        * update the metadata in the cache
+        *
+        * @param int $id
+        * @param array $data
+        */
+       public function update($id, array $data) {
+               $this->cache->update($this->getSourcePath($id), $data);
+       }
+
+       /**
+        * get the file id for a file
+        *
+        * @param string $file
+        * @return int
+        */
+       public function getId($file) {
+               return $this->cache->getId($this->getSourcePath($file));
+       }
+
+       /**
+        * get the id of the parent folder of a file
+        *
+        * @param string $file
+        * @return int
+        */
+       public function getParentId($file) {
+               if ($file === '') {
+                       return -1;
+               } else {
+                       return $this->cache->getParentId($this->getSourcePath($file));
+               }
+       }
+
+       /**
+        * check if a file is available in the cache
+        *
+        * @param string $file
+        * @return bool
+        */
+       public function inCache($file) {
+               return $this->cache->inCache($this->getSourcePath($file));
+       }
+
+       /**
+        * remove a file or folder from the cache
+        *
+        * @param string $file
+        */
+       public function remove($file) {
+               $this->cache->remove($this->getSourcePath($file));
+       }
+
+       /**
+        * Move a file or folder in the cache
+        *
+        * @param string $source
+        * @param string $target
+        */
+       public function move($source, $target) {
+               $this->cache->move($this->getSourcePath($source), $this->getSourcePath($target));
+       }
+
+       /**
+        * remove all entries for files that are stored on the storage from the cache
+        */
+       public function clear() {
+               $this->cache->remove($this->root);
+       }
+
+       /**
+        * @param string $file
+        *
+        * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
+        */
+       public function getStatus($file) {
+               return $this->cache->getStatus($this->getSourcePath($file));
+       }
+
+       private function formatSearchResults($results) {
+               $results = array_filter($results, array($this, 'filterCacheEntry'));
+               $results = array_values($results);
+               return array_map(array($this, 'formatCacheEntry'), $results);
+       }
+
+       /**
+        * search for files matching $pattern
+        *
+        * @param string $pattern
+        * @return array an array of file data
+        */
+       public function search($pattern) {
+               $results = $this->cache->search($pattern);
+               return $this->formatSearchResults($results);
+       }
+
+       /**
+        * search for files by mimetype
+        *
+        * @param string $mimetype
+        * @return array
+        */
+       public function searchByMime($mimetype) {
+               $results = $this->cache->searchByMime($mimetype);
+               return $this->formatSearchResults($results);
+       }
+
+       /**
+        * update the folder size and the size of all parent folders
+        *
+        * @param string|boolean $path
+        * @param array $data (optional) meta data of the folder
+        */
+       public function correctFolderSize($path, $data = null) {
+               $this->cache->correctFolderSize($this->getSourcePath($path), $data);
+       }
+
+       /**
+        * get the size of a folder and set it in the cache
+        *
+        * @param string $path
+        * @param array $entry (optional) meta data of the folder
+        * @return int
+        */
+       public function calculateFolderSize($path, $entry = null) {
+               return $this->cache->calculateFolderSize($this->getSourcePath($path), $entry);
+       }
+
+       /**
+        * get all file ids on the files on the storage
+        *
+        * @return int[]
+        */
+       public function getAll() {
+               // not supported
+               return array();
+       }
+
+       /**
+        * find a folder in the cache which has not been fully scanned
+        *
+        * If multiply incomplete folders are in the cache, the one with the highest id will be returned,
+        * use the one with the highest id gives the best result with the background scanner, since that is most
+        * likely the folder where we stopped scanning previously
+        *
+        * @return string|bool the path of the folder or false when no folder matched
+        */
+       public function getIncomplete() {
+               // not supported
+               return false;
+       }
+
+       /**
+        * get the path of a file on this storage by it's id
+        *
+        * @param int $id
+        * @return string|null
+        */
+       public function getPathById($id) {
+               $path = $this->cache->getPathById($id);
+               return $this->getJailedPath($path);
+       }
+}
diff --git a/lib/private/files/cache/wrapper/cachewrapper.php b/lib/private/files/cache/wrapper/cachewrapper.php
new file mode 100644 (file)
index 0000000..040358e
--- /dev/null
@@ -0,0 +1,247 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Cache\Wrapper;
+
+use OC\Files\Cache\Cache;
+
+class CacheWrapper extends Cache {
+       /**
+        * @var \OC\Files\Cache\Cache
+        */
+       protected $cache;
+
+       /**
+        * @param \OC\Files\Cache\Cache $cache
+        */
+       public function __construct($cache) {
+               $this->cache = $cache;
+       }
+
+       /**
+        * Make it easy for wrappers to modify every returned cache entry
+        *
+        * @param array $entry
+        * @return array
+        */
+       protected function formatCacheEntry($entry) {
+               return $entry;
+       }
+
+       /**
+        * get the stored metadata of a file or folder
+        *
+        * @param string /int $file
+        * @return array|false
+        */
+       public function get($file) {
+               $result = $this->cache->get($file);
+               if ($result) {
+                       $result = $this->formatCacheEntry($result);
+               }
+               return $result;
+       }
+
+       /**
+        * get the metadata of all files stored in $folder
+        *
+        * @param string $folder
+        * @return array
+        */
+       public function getFolderContents($folder) {
+               // cant do a simple $this->cache->.... call here since getFolderContentsById needs to be called on this
+               // and not the wrapped cache
+               $fileId = $this->getId($folder);
+               return $this->getFolderContentsById($fileId);
+       }
+
+       /**
+        * get the metadata of all files stored in $folder
+        *
+        * @param int $fileId the file id of the folder
+        * @return array
+        */
+       public function getFolderContentsById($fileId) {
+               $results = $this->cache->getFolderContentsById($fileId);
+               return array_map(array($this, 'formatCacheEntry'), $results);
+       }
+
+       /**
+        * store meta data for a file or folder
+        *
+        * @param string $file
+        * @param array $data
+        *
+        * @return int file id
+        */
+       public function put($file, array $data) {
+               return $this->cache->put($file, $data);
+       }
+
+       /**
+        * update the metadata in the cache
+        *
+        * @param int $id
+        * @param array $data
+        */
+       public function update($id, array $data) {
+               $this->cache->update($id, $data);
+       }
+
+       /**
+        * get the file id for a file
+        *
+        * @param string $file
+        * @return int
+        */
+       public function getId($file) {
+               return $this->cache->getId($file);
+       }
+
+       /**
+        * get the id of the parent folder of a file
+        *
+        * @param string $file
+        * @return int
+        */
+       public function getParentId($file) {
+               return $this->cache->getParentId($file);
+       }
+
+       /**
+        * check if a file is available in the cache
+        *
+        * @param string $file
+        * @return bool
+        */
+       public function inCache($file) {
+               return $this->cache->inCache($file);
+       }
+
+       /**
+        * remove a file or folder from the cache
+        *
+        * @param string $file
+        */
+       public function remove($file) {
+               $this->cache->remove($file);
+       }
+
+       /**
+        * Move a file or folder in the cache
+        *
+        * @param string $source
+        * @param string $target
+        */
+       public function move($source, $target) {
+               $this->cache->move($source, $target);
+       }
+
+       /**
+        * remove all entries for files that are stored on the storage from the cache
+        */
+       public function clear() {
+               $this->cache->clear();
+       }
+
+       /**
+        * @param string $file
+        *
+        * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
+        */
+       public function getStatus($file) {
+               return $this->cache->getStatus($file);
+       }
+
+       /**
+        * search for files matching $pattern
+        *
+        * @param string $pattern
+        * @return array an array of file data
+        */
+       public function search($pattern) {
+               $results = $this->cache->search($pattern);
+               return array_map(array($this, 'formatCacheEntry'), $results);
+       }
+
+       /**
+        * search for files by mimetype
+        *
+        * @param string $mimetype
+        * @return array
+        */
+       public function searchByMime($mimetype) {
+               $results = $this->cache->searchByMime($mimetype);
+               return array_map(array($this, 'formatCacheEntry'), $results);
+       }
+
+       /**
+        * update the folder size and the size of all parent folders
+        *
+        * @param string|boolean $path
+        * @param array $data (optional) meta data of the folder
+        */
+       public function correctFolderSize($path, $data = null) {
+               $this->cache->correctFolderSize($path, $data);
+       }
+
+       /**
+        * get the size of a folder and set it in the cache
+        *
+        * @param string $path
+        * @param array $entry (optional) meta data of the folder
+        * @return int
+        */
+       public function calculateFolderSize($path, $entry = null) {
+               return $this->cache->calculateFolderSize($path, $entry);
+       }
+
+       /**
+        * get all file ids on the files on the storage
+        *
+        * @return int[]
+        */
+       public function getAll() {
+               return $this->cache->getAll();
+       }
+
+       /**
+        * find a folder in the cache which has not been fully scanned
+        *
+        * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
+        * use the one with the highest id gives the best result with the background scanner, since that is most
+        * likely the folder where we stopped scanning previously
+        *
+        * @return string|bool the path of the folder or false when no folder matched
+        */
+       public function getIncomplete() {
+               return $this->cache->getIncomplete();
+       }
+
+       /**
+        * get the path of a file on this storage by it's id
+        *
+        * @param int $id
+        * @return string|null
+        */
+       public function getPathById($id) {
+               return $this->cache->getPathById($id);
+       }
+
+       /**
+        * get the storage id of the storage for a file and the internal path of the file
+        * unlike getPathById this does not limit the search to files on this storage and
+        * instead does a global search in the cache table
+        *
+        * @param int $id
+        * @return array, first element holding the storage id, second the path
+        */
+       static public function getById($id) {
+               return parent::getById($id);
+       }
+}
diff --git a/lib/private/files/storage/wrapper/jail.php b/lib/private/files/storage/wrapper/jail.php
new file mode 100644 (file)
index 0000000..22b9676
--- /dev/null
@@ -0,0 +1,413 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Storage\Wrapper;
+
+use OC\Files\Cache\Wrapper\CacheJail;
+
+/**
+ * Jail to a subdirectory of the wrapped storage
+ *
+ * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage
+ */
+class Jail extends Wrapper {
+       /**
+        * @var string
+        */
+       protected $rootPath;
+
+       /**
+        * @param array $arguments ['storage' => $storage, 'mask' => $root]
+        *
+        * $storage: The storage that will be wrapper
+        * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage
+        */
+       public function __construct($arguments) {
+               parent::__construct($arguments);
+               $this->rootPath = $arguments['root'];
+       }
+
+       protected function getSourcePath($path) {
+               if ($path === '') {
+                       return $this->rootPath;
+               } else {
+                       return $this->rootPath . '/' . $path;
+               }
+       }
+
+       public function getId() {
+               return 'link:' . parent::getId() . ':' . $this->rootPath;
+       }
+
+       /**
+        * see http://php.net/manual/en/function.mkdir.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function mkdir($path) {
+               return $this->storage->mkdir($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.rmdir.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function rmdir($path) {
+               return $this->storage->rmdir($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.opendir.php
+        *
+        * @param string $path
+        * @return resource
+        */
+       public function opendir($path) {
+               return $this->storage->opendir($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.is_dir.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function is_dir($path) {
+               return $this->storage->is_dir($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.is_file.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function is_file($path) {
+               return $this->storage->is_file($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.stat.php
+        * only the following keys are required in the result: size and mtime
+        *
+        * @param string $path
+        * @return array
+        */
+       public function stat($path) {
+               return $this->storage->stat($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.filetype.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function filetype($path) {
+               return $this->storage->filetype($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.filesize.php
+        * The result for filesize when called on a folder is required to be 0
+        *
+        * @param string $path
+        * @return int
+        */
+       public function filesize($path) {
+               return $this->storage->filesize($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file can be created in $path
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function isCreatable($path) {
+               return $this->storage->isCreatable($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file can be read
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function isReadable($path) {
+               return $this->storage->isReadable($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file can be written to
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function isUpdatable($path) {
+               return $this->storage->isUpdatable($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file can be deleted
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function isDeletable($path) {
+               return $this->storage->isDeletable($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file can be shared
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function isSharable($path) {
+               return $this->storage->isSharable($this->getSourcePath($path));
+       }
+
+       /**
+        * get the full permissions of a path.
+        * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php
+        *
+        * @param string $path
+        * @return int
+        */
+       public function getPermissions($path) {
+               return $this->storage->getPermissions($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.file_exists.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function file_exists($path) {
+               return $this->storage->file_exists($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.filemtime.php
+        *
+        * @param string $path
+        * @return int
+        */
+       public function filemtime($path) {
+               return $this->storage->filemtime($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.file_get_contents.php
+        *
+        * @param string $path
+        * @return string
+        */
+       public function file_get_contents($path) {
+               return $this->storage->file_get_contents($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.file_put_contents.php
+        *
+        * @param string $path
+        * @param string $data
+        * @return bool
+        */
+       public function file_put_contents($path, $data) {
+               return $this->storage->file_put_contents($this->getSourcePath($path), $data);
+       }
+
+       /**
+        * see http://php.net/manual/en/function.unlink.php
+        *
+        * @param string $path
+        * @return bool
+        */
+       public function unlink($path) {
+               return $this->storage->unlink($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.rename.php
+        *
+        * @param string $path1
+        * @param string $path2
+        * @return bool
+        */
+       public function rename($path1, $path2) {
+               return $this->storage->rename($this->getSourcePath($path1), $this->getSourcePath($path2));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.copy.php
+        *
+        * @param string $path1
+        * @param string $path2
+        * @return bool
+        */
+       public function copy($path1, $path2) {
+               return $this->storage->copy($this->getSourcePath($path1), $this->getSourcePath($path2));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.fopen.php
+        *
+        * @param string $path
+        * @param string $mode
+        * @return resource
+        */
+       public function fopen($path, $mode) {
+               return $this->storage->fopen($this->getSourcePath($path), $mode);
+       }
+
+       /**
+        * get the mimetype for a file or folder
+        * The mimetype for a folder is required to be "httpd/unix-directory"
+        *
+        * @param string $path
+        * @return string
+        */
+       public function getMimeType($path) {
+               return $this->storage->getMimeType($this->getSourcePath($path));
+       }
+
+       /**
+        * see http://php.net/manual/en/function.hash.php
+        *
+        * @param string $type
+        * @param string $path
+        * @param bool $raw
+        * @return string
+        */
+       public function hash($type, $path, $raw = false) {
+               return $this->storage->hash($type, $this->getSourcePath($path), $raw);
+       }
+
+       /**
+        * see http://php.net/manual/en/function.free_space.php
+        *
+        * @param string $path
+        * @return int
+        */
+       public function free_space($path) {
+               return $this->storage->free_space($this->getSourcePath($path));
+       }
+
+       /**
+        * search for occurrences of $query in file names
+        *
+        * @param string $query
+        * @return array
+        */
+       public function search($query) {
+               return $this->storage->search($query);
+       }
+
+       /**
+        * see http://php.net/manual/en/function.touch.php
+        * If the backend does not support the operation, false should be returned
+        *
+        * @param string $path
+        * @param int $mtime
+        * @return bool
+        */
+       public function touch($path, $mtime = null) {
+               return $this->storage->touch($this->getSourcePath($path), $mtime);
+       }
+
+       /**
+        * get the path to a local version of the file.
+        * The local version of the file can be temporary and doesn't have to be persistent across requests
+        *
+        * @param string $path
+        * @return string
+        */
+       public function getLocalFile($path) {
+               return $this->storage->getLocalFile($this->getSourcePath($path));
+       }
+
+       /**
+        * get the path to a local version of the folder.
+        * The local version of the folder can be temporary and doesn't have to be persistent across requests
+        *
+        * @param string $path
+        * @return string
+        */
+       public function getLocalFolder($path) {
+               return $this->storage->getLocalFolder($this->getSourcePath($path));
+       }
+
+       /**
+        * check if a file or folder has been updated since $time
+        *
+        * @param string $path
+        * @param int $time
+        * @return bool
+        *
+        * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed.
+        * returning true for other changes in the folder is optional
+        */
+       public function hasUpdated($path, $time) {
+               return $this->storage->hasUpdated($this->getSourcePath($path), $time);
+       }
+
+       /**
+        * get a cache instance for the storage
+        *
+        * @param string $path
+        * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
+        * @return \OC\Files\Cache\Cache
+        */
+       public function getCache($path = '', $storage = null) {
+               if (!$storage) {
+                       $storage = $this;
+               }
+               $sourceCache = $this->storage->getCache($this->getSourcePath($path), $storage);
+               return new CacheJail($sourceCache, $this->rootPath);
+       }
+
+       /**
+        * get the user id of the owner of a file or folder
+        *
+        * @param string $path
+        * @return string
+        */
+       public function getOwner($path) {
+               return $this->storage->getOwner($this->getSourcePath($path));
+       }
+
+       /**
+        * get a watcher instance for the cache
+        *
+        * @param string $path
+        * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
+        * @return \OC\Files\Cache\Watcher
+        */
+       public function getWatcher($path = '', $storage = null) {
+               if (!$storage) {
+                       $storage = $this;
+               }
+               return $this->storage->getWatcher($this->getSourcePath($path), $storage);
+       }
+
+       /**
+        * get the ETag for a file or folder
+        *
+        * @param string $path
+        * @return string
+        */
+       public function getETag($path) {
+               return $this->storage->getETag($this->getSourcePath($path));
+       }
+}
diff --git a/lib/private/files/storage/wrapper/permissionsmask.php b/lib/private/files/storage/wrapper/permissionsmask.php
new file mode 100644 (file)
index 0000000..be5cb6b
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace OC\Files\Storage\Wrapper;
+
+use OC\Files\Cache\Wrapper\CachePermissionsMask;
+
+/**
+ * Mask the permissions of a storage
+ *
+ * Note that the read permissions cant be masked
+ */
+class PermissionsMask extends Wrapper {
+       /**
+        * @var int
+        */
+       private $mask;
+
+       public function __construct($arguments) {
+               parent::__construct($arguments);
+               $this->mask = $arguments['mask'];
+       }
+
+       private function checkMask($permissions) {
+               return ($this->mask & $permissions) === $permissions;
+       }
+
+       public function isUpdatable($path) {
+               return $this->checkMask(\OCP\PERMISSION_UPDATE) and parent::isUpdatable($path);
+       }
+
+       public function isCreatable($path) {
+               return $this->checkMask(\OCP\PERMISSION_CREATE) and parent::isCreatable($path);
+       }
+
+       public function isDeletable($path) {
+               return $this->checkMask(\OCP\PERMISSION_DELETE) and parent::isDeletable($path);
+       }
+
+       public function getPermissions($path) {
+               return $this->storage->getPermissions($path) & $this->mask;
+       }
+
+       public function rename($path1, $path2) {
+               return $this->checkMask(\OCP\PERMISSION_UPDATE) and parent::rename($path1, $path2);
+       }
+
+       public function copy($path1, $path2) {
+               return $this->checkMask(\OCP\PERMISSION_CREATE) and parent::copy($path1, $path2);
+       }
+
+       public function touch($path, $mtime = null) {
+               $permissions = $this->file_exists($path) ? \OCP\PERMISSION_UPDATE : \OCP\PERMISSION_CREATE;
+               return $this->checkMask($permissions) and parent::touch($path, $mtime);
+       }
+
+       public function mkdir($path) {
+               return $this->checkMask(\OCP\PERMISSION_CREATE) and parent::mkdir($path);
+       }
+
+       public function rmdir($path) {
+               return $this->checkMask(\OCP\PERMISSION_DELETE) and parent::rmdir($path);
+       }
+
+       public function unlink($path) {
+               return $this->checkMask(\OCP\PERMISSION_DELETE) and parent::unlink($path);
+       }
+
+       public function file_put_contents($path, $data) {
+               $permissions = $this->file_exists($path) ? \OCP\PERMISSION_UPDATE : \OCP\PERMISSION_CREATE;
+               return $this->checkMask($permissions) and parent::file_put_contents($path, $data);
+       }
+
+       public function fopen($path, $mode) {
+               if ($mode === 'r' or $mode === 'rb') {
+                       return parent::fopen($path, $mode);
+               } else {
+                       $permissions = $this->file_exists($path) ? \OCP\PERMISSION_UPDATE : \OCP\PERMISSION_CREATE;
+                       return $this->checkMask($permissions) ? parent::fopen($path, $mode) : false;
+               }
+       }
+
+       /**
+        * get a cache instance for the storage
+        *
+        * @param string $path
+        * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache
+        * @return \OC\Files\Cache\Cache
+        */
+       public function getCache($path = '', $storage = null) {
+               if (!$storage) {
+                       $storage = $this;
+               }
+               $sourceCache = parent::getCache($path, $storage);
+               return new CachePermissionsMask($sourceCache, $this->mask);
+       }
+}
index 02c45a165712a7668d0b917c4533f845dad9e505..7360e9885c1a79a6457867538898807454efe15c 100644 (file)
@@ -20,20 +20,20 @@ class Cache extends \Test\TestCase {
        /**
         * @var \OC\Files\Storage\Temporary $storage ;
         */
-       private $storage;
+       protected $storage;
        /**
         * @var \OC\Files\Storage\Temporary $storage2 ;
         */
-       private $storage2;
+       protected $storage2;
 
        /**
         * @var \OC\Files\Cache\Cache $cache
         */
-       private $cache;
+       protected $cache;
        /**
         * @var \OC\Files\Cache\Cache $cache2
         */
-       private $cache2;
+       protected $cache2;
 
        public function testSimple() {
                $file1 = 'foo';
diff --git a/tests/lib/files/cache/wrapper/cachejail.php b/tests/lib/files/cache/wrapper/cachejail.php
new file mode 100644 (file)
index 0000000..13f3dc8
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Cache\Wrapper;
+
+use Test\Files\Cache\Cache;
+
+class CacheJail extends Cache {
+       /**
+        * @var \OC\Files\Cache\Cache $sourceCache
+        */
+       protected $sourceCache;
+
+       public function setUp() {
+               parent::setUp();
+               $this->storage->mkdir('foo');
+               $this->sourceCache = $this->cache;
+               $this->cache = new \OC\Files\Cache\Wrapper\CacheJail($this->sourceCache, 'foo');
+       }
+
+       function testSearchOutsideJail() {
+               $file1 = 'foo/foobar';
+               $file2 = 'folder/foobar';
+               $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder');
+
+               $this->sourceCache->put($file1, $data1);
+               $this->sourceCache->put($file2, $data1);
+
+               $this->assertCount(2, $this->sourceCache->search('%foobar'));
+
+               $result = $this->cache->search('%foobar%');
+               $this->assertCount(1, $result);
+               $this->assertEquals('foobar', $result[0]['path']);
+       }
+
+       function testClearKeepEntriesOutsideJail() {
+               $file1 = 'foo/foobar';
+               $file2 = 'foo/foobar/asd';
+               $file3 = 'folder/foobar';
+               $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory');
+
+               $this->sourceCache->put('foo', $data1);
+               $this->sourceCache->put($file1, $data1);
+               $this->sourceCache->put($file2, $data1);
+               $this->sourceCache->put($file3, $data1);
+
+               $this->cache->clear();
+
+               $this->assertFalse($this->cache->inCache('foobar'));
+               $this->assertTrue($this->sourceCache->inCache('folder/foobar'));
+       }
+
+       function testGetById() {
+               //not supported
+               $this->assertTrue(true);
+       }
+
+       function testGetIncomplete() {
+               //not supported
+               $this->assertTrue(true);
+       }
+}
diff --git a/tests/lib/files/storage/wrapper/jail.php b/tests/lib/files/storage/wrapper/jail.php
new file mode 100644 (file)
index 0000000..270ce75
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+namespace Test\Files\Storage\Wrapper;
+
+class Jail extends \Test\Files\Storage\Storage {
+       /**
+        * @var string tmpDir
+        */
+       private $tmpDir;
+
+       /**
+        * @var \OC\Files\Storage\Temporary
+        */
+       private $sourceStorage;
+
+       public function setUp() {
+               parent::setUp();
+               $this->sourceStorage = new \OC\Files\Storage\Temporary(array());
+               $this->sourceStorage->mkdir('foo');
+               $this->instance = new \OC\Files\Storage\Wrapper\Jail(array(
+                       'storage' => $this->sourceStorage,
+                       'root' => 'foo'
+               ));
+       }
+
+       public function tearDown() {
+               // test that nothing outside our jail is touched
+               $contents = array();
+               $dh = $this->sourceStorage->opendir('');
+               while ($file = readdir($dh)) {
+                       if ($file !== '.' and $file !== '..') {
+                               $contents[] = $file;
+                       }
+               }
+               $this->assertEquals(array('foo'), $contents);
+               $this->sourceStorage->cleanUp();
+               parent::tearDown();
+       }
+
+       public function testMkDirRooted() {
+               $this->instance->mkdir('bar');
+               $this->assertTrue($this->sourceStorage->is_dir('foo/bar'));
+       }
+
+       public function testFilePutContentsRooted() {
+               $this->instance->file_put_contents('bar', 'asd');
+               $this->assertEquals('asd', $this->sourceStorage->file_get_contents('foo/bar'));
+       }
+}