Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(settings): Clarify peculiarities of enabling encryption #50424

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,30 @@
<NcSettingsSection :name="t('settings', 'Server-side encryption')"
:description="t('settings', 'Server-side encryption makes it possible to encrypt files which are uploaded to this server. This comes with limitations like a performance penalty, so enable this only if needed.')"
:doc-url="encryptionAdminDoc">
<NcCheckboxRadioSwitch :checked="encryptionEnabled || shouldDisplayWarning"
:disabled="encryptionEnabled"
<NcNoteCard v-if="encryptionEnabled" type="info">
<p>
{{ textExistingFilesNotEncrypted }}
{{ t('settings', 'To encrypt all existing files run this OCC command:') }}
</p>
<code>
<pre>occ encryption:encrypt-all</pre>
</code>
</NcNoteCard>

<NcCheckboxRadioSwitch :class="{ disabled: encryptionEnabled }"
:checked="encryptionEnabled"
:aria-disabled="encryptionEnabled ? 'true' : undefined"
:aria-describedby="encryptionEnabled ? 'server-side-encryption-disable-hint' : undefined"
:loading="loadingEncryptionState"
type="switch"
@update:checked="displayWarning">
{{ t('settings', 'Enable server-side encryption') }}
</NcCheckboxRadioSwitch>
<p v-if="encryptionEnabled" id="server-side-encryption-disable-hint" class="disable-hint">
{{ t('settings', 'Disabling server side encryption is only possible using OCC, please refer to the documentation.') }}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we have a link to the documentation?

</p>

<div v-if="shouldDisplayWarning && !encryptionEnabled" class="notecard warning" role="alert">
<p>{{ t('settings', 'Please read carefully before activating server-side encryption:') }}</p>
<ul>
<li>{{ t('settings', 'Once encryption is enabled, all files uploaded to the server from that point forward will be encrypted at rest on the server. It will only be possible to disable encryption at a later date if the active encryption module supports that function, and all pre-conditions (e.g. setting a recover key) are met.') }}</li>
<li>{{ t('settings', 'Encryption alone does not guarantee security of the system. Please see documentation for more information about how the encryption app works, and the supported use cases.') }}</li>
<li>{{ t('settings', 'Be aware that encryption always increases the file size.') }}</li>
<li>{{ t('settings', 'It is always good to create regular backups of your data, in case of encryption make sure to backup the encryption keys along with your data.') }}</li>
</ul>

<p class="margin-bottom">
{{ t('settings', 'This is the final warning: Do you really want to enable encryption?') }}
</p>
<NcButton type="primary"
@click="enableEncryption()">
{{ t('settings', "Enable encryption") }}
</NcButton>
</div>

<div v-if="encryptionEnabled">
<template v-if="encryptionEnabled">
<div v-if="encryptionReady">
<p v-if="encryptionModules.length === 0">
{{ t('settings', 'No encryption module loaded, please enable an encryption module in the app menu.') }}
Expand Down Expand Up @@ -62,31 +60,42 @@
)
}}
</div>
</div>
</template>
</NcSettingsSection>
</template>

<script>
import { showError } from '@nextcloud/dialogs'
import { showError, spawnDialog } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
import { t } from '@nextcloud/l10n'
import { generateOcsUrl } from '@nextcloud/router'
import { confirmPassword } from '@nextcloud/password-confirmation'
import { textExistingFilesNotEncrypted } from './sharedTexts'

import axios from '@nextcloud/axios'
import logger from '../../logger.ts'

import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
import NcSettingsSection from '@nextcloud/vue/dist/Components/NcSettingsSection.js'

import logger from '../logger'

import '@nextcloud/password-confirmation/dist/style.css'
import EncryptionWarningDialog from './EncryptionWarningDialog.vue'

export default {
name: 'Encryption',
name: 'EncryptionSettings',

components: {
NcCheckboxRadioSwitch,
NcNoteCard,
NcSettingsSection,
NcButton,
},

setup() {
return {
t,
textExistingFilesNotEncrypted,
}
},

data() {
const encryptionModules = loadState('settings', 'encryption-modules')
return {
Expand All @@ -95,20 +104,31 @@ export default {
externalBackendsEnabled: loadState('settings', 'external-backends-enabled'),
encryptionAdminDoc: loadState('settings', 'encryption-admin-doc'),
encryptionModules,
shouldDisplayWarning: false,
migrating: false,
defaultCheckedModule: Object.entries(encryptionModules).find((module) => module[1].default)?.[0],

loadingEncryptionState: false,
}
},

methods: {
displayWarning() {
if (!this.encryptionEnabled) {
this.shouldDisplayWarning = !this.shouldDisplayWarning
} else {
this.encryptionEnabled = false
this.shouldDisplayWarning = false
displayWarning(checked) {
if (this.loadingEncryptionState || checked === false) {
return
}

this.loadingEncryptionState = true
spawnDialog(EncryptionWarningDialog, {}, async (confirmed) => {
try {
if (confirmed) {
await this.enableEncryption()
}
} finally {
this.loadingEncryptionState = false
}
})
},

async update(key, value) {
await confirmPassword()

Expand All @@ -129,14 +149,15 @@ export default {
errorMessage: t('settings', 'Unable to update server side encryption config'),
error: e,
})
return false
}
return true
},
async checkDefaultModule() {
await this.update('default_encryption_module', this.defaultCheckedModule)
},
async enableEncryption() {
this.encryptionEnabled = true
await this.update('encryption_enabled', 'yes')
this.encryptionEnabled = await this.update('encryption_enabled', 'yes')
},
async handleResponse({ status, errorMessage, error }) {
if (status !== 'ok') {
Expand All @@ -148,42 +169,27 @@ export default {
}
</script>

<style lang="scss" scoped>

.notecard.success {
--note-background: rgba(var(--color-success-rgb), 0.2);
--note-theme: var(--color-success);
}

.notecard.error {
--note-background: rgba(var(--color-error-rgb), 0.2);
--note-theme: var(--color-error);
}
<style scoped>
code {
background-color: var(--color-background-dark);
color: var(--color-main-text);

.notecard.warning {
--note-background: rgba(var(--color-warning-rgb), 0.2);
--note-theme: var(--color-warning);
display: block;
margin-block-start: 0.5rem;
padding: .25lh .5lh;
width: fit-content;
}

#body-settings .notecard {
color: var(--color-text-light);
background-color: var(--note-background);
border: 1px solid var(--color-border);
border-inline-start: 4px solid var(--note-theme);
border-radius: var(--border-radius);
box-shadow: rgba(43, 42, 51, 0.05) 0px 1px 2px 0px;
margin: 1rem 0;
margin-top: 1rem;
padding: 1rem;
.disabled {
opacity: .75;
}

li {
list-style-type: initial;
margin-inline-start: 1rem;
padding: 0.25rem 0;
.disabled :deep(*) {
cursor: not-allowed !important;
}

.margin-bottom {
margin-bottom: 0.75rem;
.disable-hint {
color: var(--color-text-maxcontrast);
padding-inline-start: 10px;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import type { IDialogButton } from '@nextcloud/dialogs'

import { t } from '@nextcloud/l10n'
import { textExistingFilesNotEncrypted } from './sharedTexts.ts'
import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js'
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'

const emit = defineEmits<{
(e: 'close', encrypt: boolean): void
}>()

const buttons: IDialogButton[] = [
{
label: t('settings', 'Cancel encryption'),
// @ts-expect-error Needs to be fixed in the dialogs library - value is allowed but missing from the types
type: 'tertiary',
callback: () => emit('close', false),
},
{
label: t('settings', 'Enable encryption'),
type: 'error',
callback: () => emit('close', true),
},
]

/**
* When closed we need to emit the close event
* @param isOpen open state of the dialog
*/
function onUpdateOpen(isOpen: boolean) {
if (!isOpen) {
emit('close', false)
}
}
</script>

<template>
<NcDialog :buttons="buttons"
:name="t('settings', 'Confirm enabling encryption')"
size="normal"
@update:open="onUpdateOpen">
<NcNoteCard type="warning">
<p>
{{ t('settings', 'Please read carefully before activating server-side encryption:') }}
<ul>
<li>
{{ t('settings', 'Once encryption is enabled, all files uploaded to the server from that point forward will be encrypted at rest on the server. It will only be possible to disable encryption at a later date if the active encryption module supports that function, and all pre-conditions (e.g. setting a recover key) are met.') }}
</li>
<li>
{{ t('settings', 'Encryption alone does not guarantee security of the system. Please see documentation for more information about how the encryption app works, and the supported use cases.') }}
</li>
<li>
{{ t('settings', 'Be aware that encryption always increases the file size.') }}
</li>
<li>
{{ t('settings', 'It is always good to create regular backups of your data, in case of encryption make sure to backup the encryption keys along with your data.') }}
</li>
<li>
{{ textExistingFilesNotEncrypted }}
{{ t('settings', 'Refer to the admin documentation on how to manually also encrypt existing files.') }}
</li>
</ul>
</p>
</NcNoteCard>
<p>
{{ t('settings', 'This is the final warning: Do you really want to enable encryption?') }}
</p>
</NcDialog>
</template>

<style scoped>
li {
list-style-type: initial;
margin-inline-start: 1rem;
padding: 0.25rem 0;
}

p + p,
div + p {
margin-block: 0.75rem;
}
</style>
7 changes: 7 additions & 0 deletions apps/settings/src/components/Encryption/sharedTexts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*!
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { t } from '@nextcloud/l10n'

export const textExistingFilesNotEncrypted = t('settings', 'For performance reasons, when you enable encryption on a Nextcloud server only new and changed files are encrypted.')
4 changes: 2 additions & 2 deletions apps/settings/src/main-admin-security.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { loadState } from '@nextcloud/initial-state'
import Vue from 'vue'

import AdminTwoFactor from './components/AdminTwoFactor.vue'
import Encryption from './components/Encryption.vue'
import EncryptionSettings from './components/Encryption/EncryptionSettings.vue'
import store from './store/admin-security.js'

// eslint-disable-next-line camelcase
Expand All @@ -28,5 +28,5 @@ new View({
store,
}).$mount('#two-factor-auth-settings')

const EncryptionView = Vue.extend(Encryption)
const EncryptionView = Vue.extend(EncryptionSettings)
new EncryptionView().$mount('#vue-admin-encryption')
Loading