Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a9fe0bf
fix(encryption): clarify home storage encryption setting
joshtrichards Sep 25, 2026
5cdfdb6
fix(encryption): clarify admin recovery key description
joshtrichards Sep 25, 2026
8e05dba
fix(encryption): clarify personal recovery setting
joshtrichards Sep 25, 2026
bd62987
fix(encryption): clarify key initialization warning
joshtrichards Sep 26, 2026
d5dd545
fix(encryption): clarify personal settings with admin wording
joshtrichards Sep 26, 2026
9826a3e
fix(encryption): clarify status messages
joshtrichards Sep 26, 2026
dde18b9
test(encryption): cover admin settings state variations
joshtrichards Sep 26, 2026
6ae86c3
fix(encryption): clarify private key password recovery guidance
joshtrichards Sep 26, 2026
26f0c2d
fix(encryption): pass encryption status in admin initial state
joshtrichards Sep 26, 2026
68cda9d
fix(encryption): distinguish disabled encryption from uninitialized keys
joshtrichards Sep 27, 2026
f3f41c9
fix(encryption): make INIT_EXECUTED message depend on master-key mode
joshtrichards Sep 27, 2026
4b86386
test(encryption): verify serverSideEncryptionEnabled is passed
joshtrichards Sep 27, 2026
eb38efa
test(encryption): check INIT_EXECUTED response messages
joshtrichards Sep 27, 2026
ac02e8b
chore(encryption): inject IAppConfig into StatusController
joshtrichards Sep 27, 2026
e8c94b3
chore(encryption): add serverSideEncryptionEnabled to admin initial s…
joshtrichards Sep 27, 2026
cfb3c4f
fix(encryption): clarify how to enable server-side encryption
joshtrichards Sep 27, 2026
441a68b
chore(encryption): add missing IManager import in Admin.php
joshtrichards Sep 27, 2026
246ddc4
test(encryption): update master-key status expected string
joshtrichards Sep 27, 2026
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
25 changes: 17 additions & 8 deletions apps/encryption/lib/Controller/StatusController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\Encryption\IManager;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\IRequest;

Expand All @@ -31,6 +32,7 @@ public function __construct(
private IL10N $l,
private Session $session,
private IManager $encryptionManager,
private IAppConfig $appConfig,
) {
parent::__construct($appName, $request);
}
Expand All @@ -41,29 +43,36 @@ public function __construct(
#[NoAdminRequired]
public function getStatus() {
$status = 'error';
$message = 'no valid init status';
$message = $this->l->t('Encryption status could not be determined.');

switch ($this->session->getStatus()) {
case Session::INIT_EXECUTED:
$status = 'interactionNeeded';
$message = $this->l->t(
'Invalid private key for encryption app. Please update your private key password in your personal settings to recover access to your encrypted files.'
);
if ($this->appConfig->getValueBool('encryption', 'useMasterKey', true)) {
$message = $this->l->t(
'Server-side encryption could not be initialized. Please contact your administrator for guidance.'
);
} else {
$message = $this->l->t(
'Your private encryption key could not be unlocked. If your login password has changed, update your private key password in Personal settings to restore access to your encrypted files.'
);
}
break;
case Session::NOT_INITIALIZED:
$status = 'interactionNeeded';
if ($this->encryptionManager->isEnabled()) {
if (!$this->encryptionManager->isEnabled()) {
$message = $this->l->t(
'Encryption App is enabled, but your keys are not initialized. Please log-out and log-in again.'
'Server-side encryption is not enabled. Please ask your administrator to enable it in the admin settings.'
);
} else {
$message = $this->l->t(
'Please enable server side encryption in the admin settings in order to use the encryption module.'
'Your encryption keys are not initialized for this session. Please sign out and sign back in.'
);
}
break;
case Session::INIT_SUCCESSFUL:
$status = 'success';
$message = $this->l->t('Encryption app is enabled and ready');
$message = $this->l->t('Encryption is enabled and ready.');
}

return new DataResponse(
Expand Down
3 changes: 3 additions & 0 deletions apps/encryption/lib/Settings/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OC\Files\View;
use OCA\Encryption\AppInfo\Application;
use OCA\Encryption\Crypto\Crypt;
use OCP\Encryption\IManager;
use OCA\Encryption\Session;
use OCA\Encryption\Util;
use OCP\AppFramework\Http\TemplateResponse;
Expand All @@ -37,6 +38,7 @@ public function __construct(
private IInitialState $initialState,
private IAppConfig $appConfig,
private IUserConfig $userConfig,
private IManager $encryptionManager,
) {
}

Expand Down Expand Up @@ -70,6 +72,7 @@ public function getForm() {
'initStatus' => $session->getStatus(),
'encryptHomeStorage' => $encryptHomeStorage,
'masterKeyEnabled' => $util->isMasterKeyEnabled(),
'serverSideEncryptionEnabled' => $this->encryptionManager->isEnabled(),
]);

\OCP\Util::addStyle(Application::APP_ID, 'settings_admin');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ watchDebounced(encryptHomeStorage, async (encryptHomeStorage, oldValue) => {
<NcCheckboxRadioSwitch
v-model="encryptHomeStorage"
:loading="isSavingHomeStorageEncryption"
:description="t('encryption', 'Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted')"
:description="t('encryption', 'Controls whether server-side encryption applies to files within the home storage of accounts. Modifying this setting does not control separately mounted external storage, and does not trigger bulk encryption or decryption of existing files.')"
type="switch">
{{ t('encryption', 'Encrypt the home storage') }}
</NcCheckboxRadioSwitch>
Expand Down
6 changes: 3 additions & 3 deletions apps/encryption/src/components/SettingsAdminRecoveryKey.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ async function onSubmit() {
<template>
<form ref="form" @submit.prevent="onSubmit">
<NcFormGroup
:label="recoveryEnabled ? t('encryption', 'Disable recovery key') : t('encryption', 'Enable recovery key')"
:description="t('encryption', 'The recovery key is an additional encryption key used to encrypt files. It is used to recover files from an account if the password is forgotten.')">
:label="recoveryEnabled ? t('encryption', 'Disable administrator recovery key') : t('encryption', 'Enable administrator recovery key')"
:description="t('encryption', 'Controls whether the administrator recovery key is available to help restore access to encrypted files if an account login password is lost. Each account must also opt in to recovery separately in its personal settings.')">
<NcPasswordField
v-model="password"
required
Expand All @@ -82,7 +82,7 @@ async function onSubmit() {
:label="t('encryption', 'Repeat recovery key password')" />

<NcButton type="submit" :variant="recoveryEnabled ? 'error' : 'primary'">
{{ recoveryEnabled ? t('encryption', 'Disable recovery key') : t('encryption', 'Enable recovery key') }}
{{ recoveryEnabled ? t('encryption', 'Disable administrator recovery key') : t('encryption', 'Enable administrator recovery key') }}
</NcButton>

<NcNoteCard v-if="hasError" type="error">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async function onSubmit() {
<form ref="form" @submit.prevent="onSubmit">
<NcFormGroup
:label="t('encryption', 'Update private key password')"
:description="t('encryption', 'Your private key password no longer matches your log-in password. Set your old private key password to your current log-in password.')">
:description="t('encryption', 'Your private encryption key could not be unlocked. If your login password has changed, try entering your previous login password to unlock your key, then your current password to update your key and restore access.')">
<NcNoteCard v-if="recoveryEnabledForUser">
{{ t('encryption', 'If you do not remember your old password you can ask your administrator to recover your files.') }}
</NcNoteCard>
Expand All @@ -71,12 +71,12 @@ async function onSubmit() {
v-model="oldPrivateKeyPassword"
required
name="oldPassword"
:label="t('encryption', 'Old log-in password')" />
:label="t('encryption', 'Previous login password')" />
<NcPasswordField
v-model="newPrivateKeyPassword"
required
name="newPassword"
:label="t('encryption', 'Current log-in password')" />
:label="t('encryption', 'Current login password')" />

<NcButton
type="submit"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ watchDebounced([userEnableRecovery], async ([newValue], [oldValue]) => {
v-model="userEnableRecovery"
type="switch"
:loading="isLoading"
:description="t('encryption', 'Enabling this option will allow you to reobtain access to your encrypted files in case of password loss')">
{{ t('encryption', 'Enable password recovery') }}
:description="t('encryption', 'Controls whether the administrator can use the recovery key to restore access to your encrypted files if you lose your login password.')">
{{ t('encryption', 'Allow recovery of my encrypted files') }}
</NcCheckboxRadioSwitch>
</template>
9 changes: 6 additions & 3 deletions apps/encryption/src/views/SettingsAdmin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import { InitStatus } from '../utils/types.ts'

const adminSettings = loadState<{
recoveryEnabled: boolean
masterKeyEnabled: boolean
encryptHomeStorage: boolean
initStatus: typeof InitStatus[keyof typeof InitStatus]
encryptHomeStorage: boolean
masterKeyEnabled: boolean
serverSideEncryptionEnabled: boolean
}>('encryption', 'adminSettings')

const encryptHomeStorage = ref(adminSettings.encryptHomeStorage!)
Expand All @@ -27,7 +28,9 @@ const recoveryEnabled = ref(adminSettings.recoveryEnabled!)
<template>
<NcSettingsSection :name="t('encryption', 'Default encryption module')">
<NcNoteCard v-if="adminSettings.initStatus === InitStatus.NotInitialized && !adminSettings.masterKeyEnabled" type="warning">
{{ t('encryption', 'Encryption app is enabled but your keys are not initialized, please log-out and log-in again') }}
{{ adminSettings.serverSideEncryptionEnabled
? t('encryption', 'Your encryption keys are not initialized for this session. Please sign out and sign back in.')
: t('encryption', 'Server-side encryption is disabled. To enable it, open Administration settings, select Security, and turn on Server-side encryption.') }}
</NcNoteCard>

<template v-else>
Expand Down
4 changes: 2 additions & 2 deletions apps/encryption/src/views/SettingsPersonal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ async function reloadStatus() {
</script>

<template>
<NcSettingsSection :name="t('encryption', 'Basic encryption module')">
<NcSettingsSection :name="t('encryption', 'Default encryption module')">
<NcNoteCard v-if="initialized === InitStatus.NotInitialized" type="warning">
{{ t('encryption', 'Encryption app is enabled but your keys are not initialized, please log-out and log-in again') }}
{{ t('encryption', 'Your encryption keys are not initialized for this session. Please sign out and sign back in.') }}
</NcNoteCard>

<SettingsPersonalChangePrivateKey
Expand Down
35 changes: 34 additions & 1 deletion apps/encryption/tests/Controller/StatusControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCA\Encryption\Controller\StatusController;
use OCA\Encryption\Session;
use OCP\Encryption\IManager;
use OCP\IAppConfig;
use OCP\IL10N;
use OCP\IRequest;
use PHPUnit\Framework\MockObject\MockObject;
Expand All @@ -24,6 +25,7 @@ class StatusControllerTest extends TestCase {
protected IL10N&MockObject $l10nMock;
protected Session&MockObject $sessionMock;
protected IManager&MockObject $encryptionManagerMock;
protected IAppConfig&MockObject $appConfigMock;

protected StatusController $controller;

Expand All @@ -42,12 +44,14 @@ protected function setUp(): void {
return $message;
});
$this->encryptionManagerMock = $this->createMock(IManager::class);
$this->appConfigMock = $this->createMock(IAppConfig::class);

$this->controller = new StatusController('encryptionTest',
$this->requestMock,
$this->l10nMock,
$this->sessionMock,
$this->encryptionManagerMock);
$this->encryptionManagerMock,
$this->appConfigMock);
}

/**
Expand All @@ -72,4 +76,33 @@ public static function dataTestGetStatus(): array {
['unknown', 'error'],
];
}

#[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'dataTestGetStatusInitExecutedMessage')]
public function testGetStatusInitExecutedMessage(bool $masterKeyEnabled, string $expectedMessage): void {
$this->sessionMock->expects($this->atLeastOnce())
->method('getStatus')
->willReturn(Session::INIT_EXECUTED);

$this->appConfigMock->expects(self::once())
->method('getValueBool')
->with('encryption', 'useMasterKey', true)
->willReturn($masterKeyEnabled);

$data = $this->controller->getStatus()->getData();

$this->assertSame($expectedMessage, $data['data']['message']);
}

public static function dataTestGetStatusInitExecutedMessage(): array {
return [
'master key enabled' => [
true,
'Server-side encryption could not be initialized. Please contact your administrator for guidance.',
],
'per-user key enabled' => [
false,
'Your private encryption key could not be unlocked. If your login password has changed, update your private key password in Personal settings to restore access to your encrypted files.',
],
];
}
}
45 changes: 44 additions & 1 deletion apps/encryption/tests/Settings/AdminTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\Config\IUserConfig;
use OCP\Encryption\IManager;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
Expand All @@ -36,6 +37,7 @@ class AdminTest extends TestCase {
protected IInitialState&MockObject $initialState;
protected IAppConfig&MockObject $appConfig;
protected IUserConfig&MockObject $userConfig;
protected IManager&MockObject $encryptionManager;

protected function setUp(): void {
parent::setUp();
Expand All @@ -49,6 +51,7 @@ protected function setUp(): void {
$this->initialState = $this->createMock(IInitialState::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->userConfig = $this->createMock(IUserConfig::class);
$this->encryptionManager = $this->createMock(IManager::class);

$this->admin = new Admin(
$this->l,
Expand All @@ -60,10 +63,16 @@ protected function setUp(): void {
$this->initialState,
$this->appConfig,
$this->userConfig,
$this->encryptionManager,
);
}

public function testGetForm(): void {
$this->encryptionManager
->expects(self::once())
->method('isEnabled')
->willReturn(true);

$this->appConfig
->method('getValueBool')
->willReturnMap([
Expand All @@ -79,12 +88,46 @@ public function testGetForm(): void {
'recoveryEnabled' => true,
'initStatus' => '0',
'encryptHomeStorage' => true,
'masterKeyEnabled' => true
'masterKeyEnabled' => true,
'serverSideEncryptionEnabled' => true,
]);
$expected = new TemplateResponse('encryption', 'settings', renderAs: '');
$this->assertEquals($expected, $this->admin->getForm());
}

public function testGetFormPassesDisabledSettingsAndInitializationStatus(): void {
$this->encryptionManager
->expects(self::once())
->method('isEnabled')
->willReturn(false);

$this->appConfig
->method('getValueBool')
->willReturnMap([
['encryption', 'recoveryAdminEnabled', false],
['encryption', 'encryptHomeStorage', true, false],
['encryption', 'useMasterKey', true, false],
]);

$this->session
->method('get')
->with('encryptionInitialized')
->willReturn('2');

$this->initialState
->expects(self::once())
->method('provideInitialState')
->with('adminSettings', [
'recoveryEnabled' => false,
'initStatus' => '2',
'encryptHomeStorage' => false,
'masterKeyEnabled' => false,
'serverSideEncryptionEnabled' => false,
]);

$this->admin->getForm();
}

public function testGetSection(): void {
$this->assertSame('security', $this->admin->getSection());
}
Expand Down
Loading