Skip to content

Commit

Permalink
More improvements
Browse files Browse the repository at this point in the history
- Don't fetch OCP\Files nodes each time but instead use the one in
  $davNode->getNode()

Signed-off-by: Carl Schwan <[email protected]>
  • Loading branch information
CarlSchwan committed Aug 23, 2022
1 parent e374861 commit d135794
Show file tree
Hide file tree
Showing 11 changed files with 128 additions and 232 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm.phar --threads=1",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
"psalm:clear": "psalm.phar --clear-cache && psalm --clear-global-cache",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType",
"test": "phpunit --configuration phpunit.xml --fail-on-warning",
Expand Down
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 2 additions & 22 deletions lib/Connector/Sabre/APlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
*/
namespace OCA\EndToEndEncryption\Connector\Sabre;

use OCP\AppFramework\Http;
use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
use OCA\DAV\Connector\Sabre\File;
use OCA\EndToEndEncryption\E2EEnabledPathCache;
use OCP\Files\IRootFolder;
Expand Down Expand Up @@ -99,29 +97,11 @@ protected function getNodeForPath(string $path): INode {
throw new Conflict();
}

/**
* Get file system node of requested file
* @throws NotFound
*/
protected function getFileNode(string $path): Node {
$user = $this->userSession->getUser();
if ($user === null) {
throw new Forbidden('No user session found');
}
$uid = $user->getUID();
return $this->pathCache->getFileNode($uid, $path, $this->rootFolder);
}

/**
* Checks if the path is an E2E folder or inside an E2E folder
*/
protected function isE2EEnabledPath(string $path): bool {
try {
$node = $this->getFileNode($path);
} catch (NotFound $e) {
return false;
}
return $this->pathCache->isE2EEnabledPath($node, $path);
protected function isE2EEnabledPath(\OCA\DAV\Connector\Sabre\Node $node): bool {
return $this->pathCache->isE2EEnabledPath($node->getNode());
}

/**
Expand Down
8 changes: 4 additions & 4 deletions lib/Connector/Sabre/LockPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,21 @@ public function checkLock(RequestInterface $request): void {
$destNode = $this->getNode($destInfo['destination'], $method);

if ($node instanceof FutureFile) {
if ($this->isE2EEnabledPath($destNode->getPath()) === false) {
if ($this->isE2EEnabledPath($destNode) === false) {
return;
}
} else {
// If neither is an end to end encrypted folders, we don't care
if (!$this->isE2EEnabledPath($node->getPath()) && !$this->isE2EEnabledPath($destNode->getPath())) {
if (!$this->isE2EEnabledPath($node) && !$this->isE2EEnabledPath($destNode)) {
return;
}

// Prevent moving or copying stuff from non-encrypted to encrypted folders
if ($this->isE2EEnabledPath($node->getPath()) xor $this->isE2EEnabledPath($destNode->getPath())) {
if ($this->isE2EEnabledPath($node) xor $this->isE2EEnabledPath($destNode)) {
throw new Forbidden('Cannot copy or move files from non-encrypted folders to end to end encrypted folders or vice versa.');
}
}
} elseif (!$this->isE2EEnabledPath($node->getPath())) {
} elseif (!$this->isE2EEnabledPath($node)) {
return;
}

Expand Down
8 changes: 4 additions & 4 deletions lib/Connector/Sabre/RedirectRequestPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public function httpCopyMove(RequestInterface $request): void {
return;
}
/** @var File|Directory $node */
if (!$this->isE2EEnabledPath($node->getPath())) {
if (!$this->isE2EEnabledPath($node)) {
return;
}

Expand All @@ -114,7 +114,7 @@ public function httpDelete(RequestInterface $request, ResponseInterface $respons
return true;
}
/** @var File|Directory $node */
if (!$this->isE2EEnabledPath($node->getPath())) {
if (!$this->isE2EEnabledPath($node)) {
// If this is no e2e-enabled path, return true to continue up in the event chain
return true;
}
Expand Down Expand Up @@ -149,7 +149,7 @@ public function httpMkColPut(RequestInterface $request): void {
return;
}
/** @var File|Directory $node */
if (!$this->isE2EEnabledPath($node->getPath())) {
if (!$this->isE2EEnabledPath($node)) {
return;
}

Expand Down Expand Up @@ -184,7 +184,7 @@ public function propFind(PropFind $propFind, INode $node): bool {
}

/** @var File|Directory $node */
if (!$this->isE2EEnabledPath($node->getPath())) {
if (!$this->isE2EEnabledPath($node)) {
return true;
}

Expand Down
74 changes: 18 additions & 56 deletions lib/E2EEnabledPathCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,68 +24,37 @@

namespace OCA\EndToEndEncryption;

use Sabre\DAV\INode;
use OCP\Files\Node;
use OCP\Files\Storage\IStorage;
use OCP\Files\IHomeStorage;
use OCP\Files\Cache\ICache;
use OCP\Cache\CappedMemoryCache;
use Sabre\DAV\Exception\NotFound;

class E2EEnabledPathCache {
/**
* @psalm-type EncryptedState=bool
*
* @psalm-type Path=string
*
* @psalm-type StorageId=string|int
* @psalm-type FileId=int
*/

/** @var CappedMemoryCache<StorageId, array<Path, EncryptedState>> */
private CappedMemoryCache $perStorageEncryptedStateCache;

public function __construct() {
$this->perStorageEncryptedStateCache = new CappedMemoryCache();
$this->nodeCache = [];
}

/**
* Checks if the path is an E2E folder or inside an E2E folder
*
* @param INode&Node $node
*/
public function isE2EEnabledPath($node): bool {
public function isE2EEnabledPath(Node $node): bool {
if ($node->isEncrypted()) {
return true;
}
$storage = $node->getStorage();
$cache = $storage->getCache();
return $this->getEncryptedStates($cache, $node, $storage);
}

/**
* Get file system node of requested file
* @throws NotFound
*/
public function getFileNode($uid, string $path, $rootFolder): Node {
if (!isset($this->nodeCache[$uid])) {
$this->nodeCache[$uid] = [];
} else if (isset($this->nodeCache[$uid][$path])) {
$node = $this->nodeCache[$uid][$path];
if ($node instanceof \Exception) {
throw new NotFound('file not found', Http::STATUS_NOT_FOUND, $node);
}
return $node;
}
try {
$node = $rootFolder
->getUserFolder($uid)
->get($path);
$this->nodeCache[$uid][$path] = $node;
return $node;
} catch (Exception $e) {
$this->nodeCache[$uid][$path] = $e;
throw new NotFound('file not found', Http::STATUS_NOT_FOUND, $e);
}
}

/**
* Get the encryption state for the path
*/
Expand All @@ -94,43 +63,36 @@ protected function getEncryptedStates(ICache $cache, $node, IStorage $storage):
return false;
}

$storageId = $cache->getNumericStorageId();
if (isset($this->perStorageEncryptedStateCache[$storageId][$node->getPath()])) {
return $this->perStorageEncryptedStateCache[$storageId][$node->getPath()];
$storageId = (string)$cache->getNumericStorageId();
if (!isset($this->perStorageEncryptedStateCache[$storageId])) {
$this->perStorageEncryptedStateCache[$storageId] = [];
}
if (isset($this->perStorageEncryptedStateCache[$storageId][$node->getId()])) {
return $this->perStorageEncryptedStateCache[$storageId][$node->getId()];
}

$parentIds = [];
if ($node->getPath() === '/' || $node->getPath() === '') {
if ($node->getPath() === '/') {
// root is never encrypted
$this->perStorageEncryptedStateCache[$storageId][$node->getPath()] = false;
$this->perStorageEncryptedStateCache[$storageId][$node->getId()] = false;
return false;
}

if ($node->isEncrypted()) {
// no need to go further down in the tree
$this->perStorageEncryptedStateCache[$storageId][$node->getPath()] = true;
$this->perStorageEncryptedStateCache[$storageId][$node->getId()] = true;
return true;
}

// go down more, but try first just with the parent path to spare a lot of
// queries if already cached
$parentPath = $this->dirname($node->getPath());
if (isset($this->perStorageEncryptedStateCache[$storageId][$parentPath])) {
return $this->perStorageEncryptedStateCache[$storageId][$parentPath];
}

if ($parentPath === '/' || $parentPath === '.') {
$this->perStorageEncryptedStateCache[$storageId][$node->getPath()] = false;
return false;
$parentId = $node->getFileInfo()['parent'];
if (isset($this->perStorageEncryptedStateCache[$storageId][$parentId])) {
return $this->perStorageEncryptedStateCache[$storageId][$parentId];
}

$encrypted = $this->getEncryptedStates($cache, $node->getParent(), $storage);
$this->perStorageEncryptedStateCache[$storageId][$node->getPath()] = $encrypted;
$this->perStorageEncryptedStateCache[$storageId][$node->getId()] = $encrypted;
return $encrypted;
}

protected function dirname(string $path): string {
$dir = dirname($path);
return $dir === '.' ? '' : $dir;
}
}
Loading

0 comments on commit d135794

Please sign in to comment.