/** * after a file is renamed/copied, rename/copy its keyfile and share-keys also fix the file size and fix also the sharing * * @param array $params array with oldpath and newpath */ public static function postRenameOrCopy($params) { if (\OCP\App::isEnabled('files_encryption') === false) { return true; } // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $view = new \OC_FilesystemView('/'); $userId = \OCP\User::getUser(); $util = new Util($view, $userId); if (isset(self::$renamedFiles[$params['oldpath']]['uid']) && isset(self::$renamedFiles[$params['oldpath']]['path'])) { $ownerOld = self::$renamedFiles[$params['oldpath']]['uid']; $pathOld = self::$renamedFiles[$params['oldpath']]['path']; $type = self::$renamedFiles[$params['oldpath']]['type']; $operation = self::$renamedFiles[$params['oldpath']]['operation']; unset(self::$renamedFiles[$params['oldpath']]); } else { \OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG); \OC_FileProxy::$enabled = $proxyStatus; return false; } list($ownerNew, $pathNew) = $util->getUidAndFilename($params['newpath']); // Format paths to be relative to user files dir if ($util->isSystemWideMountPoint($pathOld)) { $oldKeyfilePath = 'files_encryption/keyfiles/' . $pathOld; $oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld; } else { $oldKeyfilePath = $ownerOld . '/' . 'files_encryption/keyfiles/' . $pathOld; $oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld; } if ($util->isSystemWideMountPoint($pathNew)) { $newKeyfilePath = 'files_encryption/keyfiles/' . $pathNew; $newShareKeyPath = 'files_encryption/share-keys/' . $pathNew; } else { $newKeyfilePath = $ownerNew . '/files_encryption/keyfiles/' . $pathNew; $newShareKeyPath = $ownerNew . '/files_encryption/share-keys/' . $pathNew; } // add key ext if this is not an folder if ($type === 'file') { $oldKeyfilePath .= '.key'; $newKeyfilePath .= '.key'; // create destination folder if not exists $localKeyPath = $view->getLocalFile($oldShareKeyPath); $newLocalKeyPath = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $localKeyPath)); if (!file_exists(dirname($newLocalKeyPath))) { mkdir(dirname($newLocalKeyPath), 0750, true); } // handle share-keys $matches = Helper::findShareKeys($pathOld, $oldShareKeyPath, $view); if (count($matches) === 0) { \OC_Log::write('Encryption library', 'No share keys found for "' . $pathOld . '"', \OC_Log::WARN); } foreach ($matches as $src) { $dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src)); $view->{$operation}($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->{$operation}($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->{$operation}($oldKeyfilePath, $newKeyfilePath); } // build the path to the file $newPath = '/' . $ownerNew . '/files' . $pathNew; // update sharing-keys self::updateKeyfiles($params['newpath'], $type); \OC_FileProxy::$enabled = $proxyStatus; }
/** * Tests whether share keys can be found * * @dataProvider userNamesProvider */ function testFindShareKeys($userName) { self::setUpUsers(); // note: not using dataProvider as we want to make // sure that the correct keys are match and not any // other ones that might happen to have similar names self::setupHooks(); self::loginHelper($userName, true); $testDir = 'testFindShareKeys' . $this->getUniqueID() . '/'; $baseDir = $userName . '/files/' . $testDir; $fileList = array('t est.txt', 't est_.txt', 't est.doc.txt', 't est(.*).txt', 'multiple.dots.can.happen.too.txt', 't est.' . $userName . '.txt', 't est_.' . $userName . '.shareKey.txt', 'who would upload their.shareKey', 'user ones file.txt', 'user ones file.txt.backup', '.t est.txt'); $rootView = new \OC\Files\View('/'); $rootView->mkdir($baseDir); foreach ($fileList as $fileName) { $rootView->file_put_contents($baseDir . $fileName, 'dummy'); } $shareKeysDir = $userName . '/files_encryption/share-keys/' . $testDir; foreach ($fileList as $fileName) { // make sure that every file only gets its correct respective keys $result = Encryption\Helper::findShareKeys($baseDir . $fileName, $shareKeysDir . $fileName, $rootView); $this->assertEquals(array($shareKeysDir . $fileName . '.' . $userName . '.shareKey'), $result); } self::cleanUpUsers(); }
/** * after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing * @param array $params array with oldpath and newpath * * This function is connected to the rename signal of OC_Filesystem and adjust the name and location * of the stored versions along the actual file */ public static function postRename($params) { if (\OCP\App::isEnabled('files_encryption') === false) { return true; } // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; $view = new \OC\Files\View('/'); $session = new \OCA\Encryption\Session($view); $userId = \OCP\User::getUser(); $util = new Util($view, $userId); if (isset(self::$renamedFiles[$params['oldpath']]['uid']) && isset(self::$renamedFiles[$params['oldpath']]['path'])) { $ownerOld = self::$renamedFiles[$params['oldpath']]['uid']; $pathOld = self::$renamedFiles[$params['oldpath']]['path']; } else { \OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG); return false; } list($ownerNew, $pathNew) = $util->getUidAndFilename($params['newpath']); // Format paths to be relative to user files dir if ($util->isSystemWideMountPoint($pathOld)) { $oldKeyfilePath = 'files_encryption/keyfiles/' . $pathOld; $oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld; } else { $oldKeyfilePath = $ownerOld . '/' . 'files_encryption/keyfiles/' . $pathOld; $oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld; } if ($util->isSystemWideMountPoint($pathNew)) { $newKeyfilePath = 'files_encryption/keyfiles/' . $pathNew; $newShareKeyPath = 'files_encryption/share-keys/' . $pathNew; } else { $newKeyfilePath = $ownerNew . '/files_encryption/keyfiles/' . $pathNew; $newShareKeyPath = $ownerNew . '/files_encryption/share-keys/' . $pathNew; } // 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 $matches = Helper::findShareKeys($oldShareKeyPath, $view); foreach ($matches as $src) { $dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src)); $view->rename($src, $dst); } } else { // handle share-keys folders $view->rename($oldShareKeyPath, $newShareKeyPath); } // Rename keyfile so it isn't orphaned if ($view->file_exists($oldKeyfilePath)) { $view->rename($oldKeyfilePath, $newKeyfilePath); } // update share keys $sharingEnabled = \OCP\Share::isEnabled(); // get users $usersSharing = $util->getSharingUsersArray($sharingEnabled, $pathNew); // update sharing-keys $util->setSharedFileKeyfiles($session, $usersSharing, $pathNew); \OC_FileProxy::$enabled = $proxyStatus; }
/** * Move encryption keys to trash so that they can be restored later * * @param $file_path path to original file * @param $filename of deleted file * @param $timestamp when the file was deleted * * @return size of encryption keys */ private static function retainEncryptionKeys($file_path, $filename, $timestamp) { $size = 0; if (\OCP\App::isEnabled('files_encryption')) { $user = \OCP\User::getUser(); $rootView = new \OC\Files\View('/'); list($owner, $ownerPath) = self::getUidAndFilename($file_path); // file has been deleted in between if (empty($ownerPath)) { return 0; } $util = new \OCA\Encryption\Util(new \OC_FilesystemView('/'), $user); // disable proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; \OC_FileProxy::$enabled = false; if ($util->isSystemWideMountPoint($ownerPath)) { $baseDir = '/files_encryption/'; } else { $baseDir = $owner . '/files_encryption/'; } $keyfile = \OC\Files\Filesystem::normalizePath($baseDir . '/keyfiles/' . $ownerPath); if ($rootView->is_dir($keyfile) || $rootView->file_exists($keyfile . '.key')) { // move keyfiles if ($rootView->is_dir($keyfile)) { $size += self::calculateSize(new \OC\Files\View($keyfile)); if ($owner !== $user) { self::copy_recursive($keyfile, $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.d' . $timestamp, $rootView); } $rootView->rename($keyfile, $user . '/files_trashbin/keyfiles/' . $filename . '.d' . $timestamp); } else { $size += $rootView->filesize($keyfile . '.key'); if ($owner !== $user) { $rootView->copy($keyfile . '.key', $owner . '/files_trashbin/keyfiles/' . basename($ownerPath) . '.key.d' . $timestamp); } $rootView->rename($keyfile . '.key', $user . '/files_trashbin/keyfiles/' . $filename . '.key.d' . $timestamp); } } // retain share keys $sharekeys = \OC\Files\Filesystem::normalizePath($baseDir . '/share-keys/' . $ownerPath); if ($rootView->is_dir($sharekeys)) { $size += self::calculateSize(new \OC\Files\View($sharekeys)); if ($owner !== $user) { self::copy_recursive($sharekeys, $owner . '/files_trashbin/share-keys/' . basename($ownerPath) . '.d' . $timestamp, $rootView); } $rootView->rename($sharekeys, $user . '/files_trashbin/share-keys/' . $filename . '.d' . $timestamp); } else { // handle share-keys $matches = \OCA\Encryption\Helper::findShareKeys($ownerPath, $sharekeys, $rootView); foreach ($matches as $src) { // get source file parts $pathinfo = pathinfo($src); // we only want to keep the users key so we can access the private key $userShareKey = $filename . '.' . $user . '.shareKey'; // if we found the share-key for the owner, we need to move it to files_trashbin if ($pathinfo['basename'] == $userShareKey) { // calculate size $size += $rootView->filesize($sharekeys . '.' . $user . '.shareKey'); // move file $rootView->rename($sharekeys . '.' . $user . '.shareKey', $user . '/files_trashbin/share-keys/' . $userShareKey . '.d' . $timestamp); } elseif ($owner !== $user) { $ownerShareKey = basename($ownerPath) . '.' . $owner . '.shareKey'; if ($pathinfo['basename'] == $ownerShareKey) { $rootView->rename($sharekeys . '.' . $owner . '.shareKey', $owner . '/files_trashbin/share-keys/' . $ownerShareKey . '.d' . $timestamp); } } else { // don't keep other share-keys unlink($src); } } } // enable proxy \OC_FileProxy::$enabled = $proxyStatus; } return $size; }
private static function preRenameOrCopy($params, $operation) { $user = \OCP\User::getUser(); $view = new \OC\Files\View('/'); $util = new Util($view, $user); list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']); // we only need to rename the keys if the rename happens on the same mountpoint // otherwise we perform a stream copy, so we get a new set of keys $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']); $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']); $type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file'; if ($mp1 === $mp2) { if ($util->isSystemWideMountPoint($pathOld)) { $oldShareKeyPath = 'files_encryption/share-keys/' . $pathOld; } else { $oldShareKeyPath = $ownerOld . '/' . 'files_encryption/share-keys/' . $pathOld; } // gather share keys here because in postRename() the file will be moved already $oldShareKeys = Helper::findShareKeys($pathOld, $oldShareKeyPath, $view); if (count($oldShareKeys) === 0) { \OC_Log::write('Encryption library', 'No share keys found for "' . $pathOld . '"', \OC_Log::WARN); } self::$renamedFiles[$params['oldpath']] = array('uid' => $ownerOld, 'path' => $pathOld, 'type' => $type, 'operation' => $operation, 'sharekeys' => $oldShareKeys); } }