Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
536ccf1
feat(encryption): Migrate from hooks to events
come-nc Oct 3, 2024
38f341c
fix(tests): Unregister encryption modules in ViewTest to avoid side e…
come-nc Nov 5, 2024
21233b7
fix(tests): Remove Encryption disabling in ViewTest to avoid side eff…
come-nc Nov 26, 2024
8779ae3
fix(encryption): Fix filesize for part files in Encryption wrapper
come-nc Nov 26, 2024
9bb0721
fix: Fix mtime preservation when moving a directory across storages w…
come-nc Nov 26, 2024
e35a8ed
fix(tests): Disable encryption wrapper when it makes sense
come-nc Nov 26, 2024
14872c8
chore(files_versions): Only mock getSystemValue method to avoid probl…
come-nc Nov 26, 2024
561b590
chore(trashbin): Fix configuration mocking in trashbin tests
come-nc Nov 26, 2024
f6f8343
chore: Update psalm baseline to remove fixed issue
come-nc Nov 26, 2024
367c877
fix(tests): Avoid user login before a private key is setup
come-nc Nov 28, 2024
08bff4c
fix(admin_audit): Survive if file change id after rename (it should not)
come-nc Nov 28, 2024
27599ef
fix(encryption): Fix a PHP error in Encryption Util in specific situa…
come-nc Jan 13, 2025
e6275f8
fix: Preserve file id when moving from object store even if encryptio…
come-nc Jan 13, 2025
2d8f6b3
chore: Assert rename success in versionning tests
come-nc Jan 16, 2025
a79b5de
fix(encryption): Improve Update class and event listenening
come-nc Feb 11, 2025
a86d917
fix(encryption): Only prevent cache deletion if target is not object …
come-nc May 13, 2025
43418ee
fix(tests): Set encryption configuration even earlier so that all use…
come-nc May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(encryption): Improve Update class and event listenening
 to avoid back&forth between path and Node object

Signed-off-by: Côme Chilliet <[email protected]>
  • Loading branch information
come-nc committed May 13, 2025
commit a79b5dea7c813e2fd45b93d47d0cf120288fe28c
15 changes: 8 additions & 7 deletions lib/private/Encryption/EncryptionEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

namespace OC\Encryption;

use OC\Files\Filesystem;
use OC\Files\SetupManager;
use OC\Files\View;
use OCA\Files_Trashbin\Events\NodeRestoredEvent;
Expand All @@ -18,7 +17,6 @@
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeRenamedEvent;
use OCP\Files\Folder;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Share\Events\ShareCreatedEvent;
Expand All @@ -32,6 +30,7 @@ class EncryptionEventListener implements IEventListener {
public function __construct(
private IUserSession $userSession,
private SetupManager $setupManager,
private Manager $encryptionManager,
) {
}

Expand All @@ -43,17 +42,20 @@ public static function register(IEventDispatcher $dispatcher): void {
}

public function handle(Event $event): void {
if (!$this->encryptionManager->isEnabled()) {
return;
}
if ($event instanceof NodeRenamedEvent) {
$this->getUpdate()->postRename($event->getSource() instanceof Folder, $event->getSource()->getPath(), $event->getTarget()->getPath());
$this->getUpdate()->postRename($event->getSource(), $event->getTarget());
} elseif ($event instanceof ShareCreatedEvent) {
$this->getUpdate()->postShared($event->getShare()->getNodeType(), $event->getShare()->getNodeId());
$this->getUpdate()->postShared($event->getShare()->getNode());
} elseif ($event instanceof ShareDeletedEvent) {
// In case the unsharing happens in a background job, we don't have
// a session and we load instead the user from the UserManager
$owner = $event->getShare()->getNode()->getOwner();
$this->getUpdate($owner)->postUnshared($event->getShare()->getNodeType(), $event->getShare()->getNodeId());
$this->getUpdate($owner)->postUnshared($event->getShare()->getNode());
} elseif ($event instanceof NodeRestoredEvent) {
$this->getUpdate()->postRestore($event->getTarget() instanceof Folder, $event->getTarget()->getPath());
$this->getUpdate()->postRestore($event->getTarget());
}
}

Expand All @@ -79,7 +81,6 @@ private function getUpdate(?IUser $owner = null): Update {
\OC::$server->getUserManager(),
\OC::$server->getGroupManager(),
\OC::$server->getConfig()),
Filesystem::getMountManager(),
\OC::$server->getEncryptionManager(),
\OC::$server->get(IFile::class),
\OC::$server->get(LoggerInterface::class),
Expand Down
9 changes: 0 additions & 9 deletions lib/private/Encryption/EncryptionWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ public function wrapStorage(string $mountPoint, IStorage $storage, IMountPoint $
\OC::$server->getGroupManager(),
\OC::$server->getConfig()
);
$update = new Update(
$util,
Filesystem::getMountManager(),
$this->manager,
$fileHelper,
$this->logger,
$uid
);
return new Encryption(
$parameters,
$this->manager,
Expand All @@ -91,7 +83,6 @@ public function wrapStorage(string $mountPoint, IStorage $storage, IMountPoint $
$fileHelper,
$uid,
$keyStorage,
$update,
$mountManager,
$this->arrayCache
);
Expand Down
115 changes: 34 additions & 81 deletions lib/private/Encryption/Update.php
Original file line number Diff line number Diff line change
@@ -1,131 +1,85 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/

namespace OC\Encryption;

use InvalidArgumentException;
use OC\Files\Filesystem;
use OC\Files\Mount;
use OC\Files\View;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use OCP\Files\File as OCPFile;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use Psr\Log\LoggerInterface;

/**
* update encrypted files, e.g. because a file was shared
*/
class Update {
/** @var Util */
protected $util;

/** @var \OC\Files\Mount\Manager */
protected $mountManager;

/** @var Manager */
protected $encryptionManager;

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

/** @var File */
protected $file;

/** @var LoggerInterface */
protected $logger;

/**
* @param string $uid
*/
public function __construct(
Util $util,
Mount\Manager $mountManager,
Manager $encryptionManager,
File $file,
LoggerInterface $logger,
$uid,
protected Util $util,
protected Manager $encryptionManager,
protected File $file,
protected LoggerInterface $logger,
protected string $uid,
) {
$this->util = $util;
$this->mountManager = $mountManager;
$this->encryptionManager = $encryptionManager;
$this->file = $file;
$this->logger = $logger;
$this->uid = $uid;
}

/**
* hook after file was shared
*/
public function postShared(string $nodeType, int $nodeId): void {
if ($this->encryptionManager->isEnabled()) {
if ($nodeType === 'file' || $nodeType === 'folder') {
$path = Filesystem::getPath($nodeId);
[$owner, $ownerPath] = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($nodeType === 'folder', $absPath);
}
}
public function postShared(OCPFile|Folder $node): void {
$this->update($node);
}

/**
* hook after file was unshared
*/
public function postUnshared(string $nodeType, int $nodeId): void {
if ($this->encryptionManager->isEnabled()) {
if ($nodeType === 'file' || $nodeType === 'folder') {
$path = Filesystem::getPath($nodeId);
[$owner, $ownerPath] = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($nodeType === 'folder', $absPath);
}
}
public function postUnshared(OCPFile|Folder $node): void {
$this->update($node);
}

/**
* inform encryption module that a file was restored from the trash bin,
* e.g. to update the encryption keys
*/
public function postRestore(bool $directory, string $filePath): void {
if ($this->encryptionManager->isEnabled()) {
$path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $filePath);
$this->update($directory, $path);
}
public function postRestore(OCPFile|Folder $node): void {
$this->update($node);
}

/**
* inform encryption module that a file was renamed,
* e.g. to update the encryption keys
*/
public function postRename(bool $directory, string $source, string $target): void {
if (
$this->encryptionManager->isEnabled() &&
dirname($source) !== dirname($target)
) {
[$owner, $ownerPath] = $this->getOwnerPath($target);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($directory, $absPath);
public function postRename(OCPFile|Folder $source, OCPFile|Folder $target): void {
if (dirname($source->getPath()) !== dirname($target->getPath())) {
$this->update($target);
}
}

/**
* get owner and path relative to data/<owner>/files
* get owner and path relative to data/
*
* @param string $path path to file for current user
* @return array ['owner' => $owner, 'path' => $path]
* @throws \InvalidArgumentException
*/
protected function getOwnerPath($path) {
$info = Filesystem::getFileInfo($path);
$owner = Filesystem::getOwner($path);
protected function getOwnerPath(OCPFile|Folder $node): string {
$owner = $node->getOwner()?->getUID();
if ($owner === null) {
throw new InvalidArgumentException('No owner found for ' . $node->getId());
}
$view = new View('/' . $owner . '/files');
$path = $view->getPath($info->getId());
if ($path === null) {
throw new InvalidArgumentException('No file found for ' . $info->getId());
try {
$path = $view->getPath($node->getId());
} catch (NotFoundException $e) {
throw new InvalidArgumentException('No file found for ' . $node->getId(), previous:$e);
}

return [$owner, $path];
return '/' . $owner . '/files/' . $path;
}

/**
Expand All @@ -134,7 +88,7 @@ protected function getOwnerPath($path) {
* @param string $path relative to data/
* @throws Exceptions\ModuleDoesNotExistsException
*/
public function update(bool $directory, string $path): void {
public function update(OCPFile|Folder $node): void {
$encryptionModule = $this->encryptionManager->getEncryptionModule();

// if the encryption module doesn't encrypt the files on a per-user basis
Expand All @@ -143,15 +97,14 @@ public function update(bool $directory, string $path): void {
return;
}

$path = $this->getOwnerPath($node);
// if a folder was shared, get a list of all (sub-)folders
if ($directory) {
if ($node instanceof Folder) {
$allFiles = $this->util->getAllFiles($path);
} else {
$allFiles = [$path];
}



foreach ($allFiles as $file) {
$usersSharing = $this->file->getAccessList($file);
try {
Expand Down
2 changes: 0 additions & 2 deletions lib/private/Files/Storage/Wrapper/Encryption.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
namespace OC\Files\Storage\Wrapper;

use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Encryption\Update;
use OC\Encryption\Util;
use OC\Files\Cache\CacheEntry;
use OC\Files\Filesystem;
Expand Down Expand Up @@ -50,7 +49,6 @@ public function __construct(
private IFile $fileHelper,
private ?string $uid,
private IStorage $keyStorage,
private Update $update,
private Manager $mountManager,
private ArrayCache $arrayCache,
) {
Expand Down
Loading