Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 0 additions & 5 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3661,11 +3661,6 @@
<code><![CDATA[null]]></code>
</NullArgument>
</file>
<file src="lib/private/Encryption/Manager.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[bool]]></code>
</ImplementedReturnTypeMismatch>
</file>
<file src="lib/private/Federation/CloudFederationProviderManager.php">
<ParamNameMismatch>
<code><![CDATA[$providerId]]></code>
Expand Down
6 changes: 1 addition & 5 deletions lib/private/Encryption/EncryptionWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,9 @@ public function __construct(
/**
* Wraps the given storage when it is not a shared storage
*
* @param string $mountPoint
* @param IStorage $storage
* @param IMountPoint $mount
* @param bool $force apply the wrapper even if the storage normally has encryption disabled, helpful for repair steps
* @return Encryption|IStorage
*/
public function wrapStorage(string $mountPoint, IStorage $storage, IMountPoint $mount, bool $force = false) {
public function wrapStorage(string $mountPoint, IStorage $storage, IMountPoint $mount, bool $force = false): Encryption|IStorage {
$parameters = [
'storage' => $storage,
'mountPoint' => $mountPoint,
Expand Down
114 changes: 34 additions & 80 deletions lib/private/Encryption/Manager.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
Expand All @@ -12,66 +14,61 @@
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OC\Encryption\Keys\Storage;
use OC\Files\Filesystem;
use OC\Files\View;
use OC\Files\Storage\Wrapper\Encryption;
use OC\Memcache\ArrayCache;
use OC\ServiceUnavailableException;
use OCP\Encryption\IEncryptionModule;
use OCP\Encryption\IManager;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\Server;
use Psr\Log\LoggerInterface;

class Manager implements IManager {
/**
* @var array<string, array{callback: callable(): IEncryptionModule, displayName: string, id: string}>
*/
protected array $encryptionModules;

public function __construct(
protected IConfig $config,
protected LoggerInterface $logger,
protected IL10N $l,
protected View $rootView,
protected Util $util,
protected ArrayCache $arrayCache,
protected readonly IConfig $config,
protected readonly IAppConfig $appConfig,
protected readonly LoggerInterface $logger,
protected readonly IL10N $l,
protected readonly IRootFolder $rootFolder,
protected readonly Util $util,
protected readonly ArrayCache $arrayCache,
) {
$this->encryptionModules = [];
}

/**
* Check if encryption is enabled
*
* @return bool true if enabled, false if not
*/
#[\Override]
public function isEnabled() {
public function isEnabled(): bool {
$installed = $this->config->getSystemValueBool('installed', false);
if (!$installed) {
return false;
}

return Server::get(IAppConfig::class)->getValueBool('core', 'encryption_enabled');
return $this->appConfig->getValueBool('core', 'encryption_enabled');
}

/**
* check if new encryption is ready
* Check if new encryption is ready
*
* @return bool
* @throws ServiceUnavailableException
*/
public function isReady() {
public function isReady(): bool {
if ($this->isKeyStorageReady() === false) {
throw new ServiceUnavailableException('Key Storage is not ready');
}

return true;
}

/**
* @param string $user
*/
public function isReadyForUser($user) {
public function isReadyForUser(string $user): bool {
if (!$this->isReady()) {
return false;
}
Expand All @@ -87,16 +84,8 @@ public function isReadyForUser($user) {
return true;
}

/**
* Registers an callback function which must return an encryption module instance
*
* @param string $id
* @param string $displayName
* @param callable $callback
* @throws Exceptions\ModuleAlreadyExistsException
*/
#[\Override]
public function registerEncryptionModule($id, $displayName, callable $callback) {
public function registerEncryptionModule(string $id, string $displayName, callable $callback): void {
if (isset($this->encryptionModules[$id])) {
throw new ModuleAlreadyExistsException($id, $displayName);
}
Expand All @@ -114,35 +103,18 @@ public function registerEncryptionModule($id, $displayName, callable $callback)
}
}

/**
* Unregisters an encryption module
*
* @param string $moduleId
*/
#[\Override]
public function unregisterEncryptionModule($moduleId) {
public function unregisterEncryptionModule(string $moduleId): void {
unset($this->encryptionModules[$moduleId]);
}

/**
* get a list of all encryption modules
*
* @return array [id => ['id' => $id, 'displayName' => $displayName, 'callback' => callback]]
*/
#[\Override]
public function getEncryptionModules() {
public function getEncryptionModules(): array {
return $this->encryptionModules;
}

/**
* get a specific encryption module
*
* @param string $moduleId
* @return IEncryptionModule
* @throws Exceptions\ModuleDoesNotExistsException
*/
#[\Override]
public function getEncryptionModule($moduleId = '') {
public function getEncryptionModule(string $moduleId = ''): IEncryptionModule {
if (empty($moduleId)) {
return $this->getDefaultEncryptionModule();
}
Expand All @@ -155,12 +127,11 @@ public function getEncryptionModule($moduleId = '') {
}

/**
* get default encryption module
* Get default encryption module
*
* @return IEncryptionModule
* @throws Exceptions\ModuleDoesNotExistsException
*/
protected function getDefaultEncryptionModule() {
protected function getDefaultEncryptionModule(): IEncryptionModule {
$defaultModuleId = $this->getDefaultEncryptionModuleId();
if (empty($defaultModuleId)) {
$message = 'No default encryption module defined';
Expand All @@ -173,56 +144,43 @@ protected function getDefaultEncryptionModule() {
throw new ModuleDoesNotExistsException($message);
}

/**
* set default encryption module Id
*
* @param string $moduleId
* @return bool
*/
#[\Override]
public function setDefaultEncryptionModule($moduleId) {
public function setDefaultEncryptionModule(string $moduleId): bool {
try {
$this->getEncryptionModule($moduleId);
} catch (\Exception $e) {
return false;
}

$this->config->setAppValue('core', 'default_encryption_module', $moduleId);
$this->appConfig->setValueString('core', 'default_encryption_module', $moduleId);
return true;
}

/**
* get default encryption module Id
*
* @return string
*/
#[\Override]
public function getDefaultEncryptionModuleId() {
return $this->config->getAppValue('core', 'default_encryption_module');
public function getDefaultEncryptionModuleId(): string {
return $this->appConfig->getValueString('core', 'default_encryption_module');
}

/**
* Add storage wrapper
*/
public function setupStorage(): void {
// If encryption is disabled and there are no loaded modules it makes no sense to load the wrapper
if (!empty($this->encryptionModules) || $this->isEnabled()) {
if ($this->encryptionModules !== [] && $this->isEnabled()) {

@artonge artonge Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc. @come-nc as I recall that this is on purpose in case encryption was disabled without decrypting all files.

$encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger);
Filesystem::addStorageWrapper('oc_encryption', $encryptionWrapper->wrapStorage(...), 2);
}
}

public function forceWrapStorage(IMountPoint $mountPoint, IStorage $storage) {
public function forceWrapStorage(IMountPoint $mountPoint, IStorage $storage): Encryption|IStorage {
$encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger);
return $encryptionWrapper->wrapStorage($mountPoint->getMountPoint(), $storage, $mountPoint, true);
}

/**
* check if key storage is ready
*
* @return bool
* Check if key storage is ready
*/
protected function isKeyStorageReady() {
protected function isKeyStorageReady(): bool {
$rootDir = $this->util->getKeyStorageRoot();

// the default root is always valid
Expand All @@ -231,10 +189,6 @@ protected function isKeyStorageReady() {
}

// check if key storage is mounted correctly
if ($this->rootView->file_exists($rootDir . '/' . Storage::KEY_STORAGE_MARKER)) {
return true;
}

return false;
return $this->rootFolder->nodeExists($rootDir . '/' . Storage::KEY_STORAGE_MARKER);
}
}
3 changes: 2 additions & 1 deletion lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,10 @@ public function __construct(
$this->registerService(Encryption\Manager::class, function (Server $c): Encryption\Manager {
return new Encryption\Manager(
$c->get(IConfig::class),
$c->get(IAppConfig::class),
$c->get(LoggerInterface::class),
$c->get(IFactory::class)->get('core'),
$c->get(View::class),
$c->get(IRootFolder::class),
$c->get(Encryption\Util::class),
new ArrayCache()
);
Expand Down
37 changes: 18 additions & 19 deletions lib/public/Encryption/IManager.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
Expand All @@ -10,72 +12,69 @@

use OC\Encryption\Exceptions\ModuleAlreadyExistsException;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
use OCP\AppFramework\Attribute\Consumable;

/**
* This class provides access to files encryption apps.
*
* @since 8.1.0
*/
#[Consumable(since: '8.1.0')]
interface IManager {
/**
* Check if encryption is available (at least one encryption module needs to be enabled)
*
* @return bool true if enabled, false if not
* @since 8.1.0
*/
public function isEnabled();
public function isEnabled(): bool;

/**
* Registers an callback function which must return an encryption module instance
* Registers a callback function which must return an encryption module instance
*
* @param string $id
* @param string $displayName
* @param callable $callback
* @param callable(): IEncryptionModule $callback
* @throws ModuleAlreadyExistsException
* @since 8.1.0
*/
public function registerEncryptionModule($id, $displayName, callable $callback);
public function registerEncryptionModule(string $id, string $displayName, callable $callback): void;

/**
* Unregisters an encryption module
*
* @param string $moduleId
* @since 8.1.0
*/
public function unregisterEncryptionModule($moduleId);
public function unregisterEncryptionModule(string $moduleId): void;

/**
* get a list of all encryption modules
* Get a list of all encryption modules.
*
* @return array [id => ['id' => $id, 'displayName' => $displayName, 'callback' => callback]]
* @return array<string, array{id: string, displayName: string, callback: (callable():IEncryptionModule)}>
* @since 8.1.0
*/
public function getEncryptionModules();
public function getEncryptionModules(): array;

/**
* get a specific encryption module
* Get a specific encryption module.
*
* @param string $moduleId Empty to get the default module
* @return IEncryptionModule
* @throws ModuleDoesNotExistsException
* @since 8.1.0
*/
public function getEncryptionModule($moduleId = '');
public function getEncryptionModule(string $moduleId = ''): IEncryptionModule;

/**
* get default encryption module Id
* Get default encryption module Id
*
* @return string
* @since 8.1.0
*/
public function getDefaultEncryptionModuleId();
public function getDefaultEncryptionModuleId(): string;

/**
* set default encryption module Id
* Set default encryption module Id.
*
* @param string $moduleId
* @return string
* @since 8.1.0
*/
public function setDefaultEncryptionModule($moduleId);
public function setDefaultEncryptionModule(string $moduleId): bool;
}
Loading
Loading