Add ability to remove local encryption keys through settings
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Fri, 11 Nov 2022 15:01:04 +0000 (16:01 +0100)
committerClaudio Cambra <claudio.cambra@nextcloud.com>
Tue, 24 Jan 2023 16:00:14 +0000 (17:00 +0100)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
src/gui/accountsettings.cpp
src/gui/accountsettings.h
src/libsync/clientsideencryption.cpp
src/libsync/clientsideencryption.h

index 3fa71150b7998d9f9b58a5e4192f4902ec6d7468..b51cc1188629e73ec83fd17565b439f0474b01a6 100644 (file)
@@ -66,6 +66,7 @@ constexpr auto propertyFolder = "folder";
 constexpr auto propertyPath = "path";
 constexpr auto e2eUiActionIdKey = "id";
 constexpr auto e2EeUiActionEnableEncryptionId = "enable_encryption";
+constexpr auto e2EeUiActionDisableEncryptionId = "disable_encryption";
 constexpr auto e2EeUiActionDisplayMnemonicId = "display_mnemonic";
 }
 
@@ -243,7 +244,12 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
 
 void AccountSettings::slotE2eEncryptionMnemonicReady()
 {
-    auto *const actionDisplayMnemonic = addActionToEncryptionMessage(tr("Display mnemonic"), e2EeUiActionDisplayMnemonicId);
+    const auto actionDisableEncryption = addActionToEncryptionMessage(tr("Disable encryption"), e2EeUiActionDisableEncryptionId);
+    connect(actionDisableEncryption, &QAction::triggered, this, [this] {
+        disableEncryptionForAccount(_accountState->account());
+    });
+
+    const auto actionDisplayMnemonic = addActionToEncryptionMessage(tr("Display mnemonic"), e2EeUiActionDisplayMnemonicId);
     connect(actionDisplayMnemonic, &QAction::triggered, this, [this]() {
         displayMnemonic(_accountState->account()->e2e()->_mnemonic);
     });
@@ -1024,6 +1030,31 @@ void AccountSettings::displayMnemonic(const QString &mnemonic)
     widget.exec();
 }
 
+void AccountSettings::disableEncryptionForAccount(const AccountPtr &account) const
+{
+    QMessageBox dialog;
+    dialog.setWindowTitle(tr("Disable end-to-end encryption"));
+    dialog.setText(tr("Disable end-to-end encryption for %1?").arg(account->davUser()));
+    dialog.setInformativeText(tr("Removing end-to-end encryption will remove locally-synced files that are encrypted."
+                                 "<br>"
+                                 "Encrypted files will remain on the server."));
+    dialog.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+    dialog.setDefaultButton(QMessageBox::Ok);
+    dialog.adjustSize();
+
+    const auto ret = dialog.exec();
+    switch(ret) {
+    case QMessageBox::Ok:
+        connect(account->e2e(), &ClientSideEncryption::sensitiveDataForgotten,
+                this, &AccountSettings::resetE2eEncryption);
+        account->e2e()->forgetSensitiveData(account);
+        break;
+    case QMessageBox::Cancel:
+        break;
+    Q_UNREACHABLE();
+    }
+}
+
 void AccountSettings::showConnectionLabel(const QString &message, QStringList errors)
 {
     const auto errStyle = QLatin1String("color:#ffffff; background-color:#bb4d4d;padding:5px;"
@@ -1257,16 +1288,21 @@ void AccountSettings::slotAccountStateChanged()
     refreshSelectiveSyncStatus();
 
     if (state == AccountState::State::Connected) {
-        /* TODO: We should probably do something better here.
-         * Verify if the user has a private key already uploaded to the server,
-         * if it has, do not offer to create one.
-         */
-        qCInfo(lcAccountSettings) << "Account" << accountsState()->account()->displayName()
-            << "Client Side Encryption" << accountsState()->account()->capabilities().clientSideEncryptionAvailable();
-
-        if (_accountState->account()->capabilities().clientSideEncryptionAvailable()) {
-            _ui->encryptionMessage->show();
-        }
+        checkClientSideEncryptionState();
+    }
+}
+
+void AccountSettings::checkClientSideEncryptionState()
+{
+    /* TODO: We should probably do something better here.
+     * Verify if the user has a private key already uploaded to the server,
+     * if it has, do not offer to create one.
+     */
+    qCInfo(lcAccountSettings) << "Account" << accountsState()->account()->displayName()
+        << "Client Side Encryption" << accountsState()->account()->capabilities().clientSideEncryptionAvailable();
+
+    if (_accountState->account()->capabilities().clientSideEncryptionAvailable()) {
+        _ui->encryptionMessage->show();
     }
 }
 
@@ -1569,6 +1605,17 @@ void AccountSettings::initializeE2eEncryption()
     }
 }
 
+void AccountSettings::resetE2eEncryption()
+{
+    for (const auto action : _ui->encryptionMessage->actions()) {
+        _ui->encryptionMessage->removeAction(action);
+    }
+    _ui->encryptionMessage->setText({});
+    _ui->encryptionMessage->setIcon({});
+    initializeE2eEncryption();
+    checkClientSideEncryptionState();
+}
+
 void AccountSettings::removeActionFromEncryptionMessage(const QString &actionId)
 {
     const auto foundEnableEncryptionActionIt = std::find_if(std::cbegin(_ui->encryptionMessage->actions()), std::cend(_ui->encryptionMessage->actions()), [&actionId](const QAction *action) {
index 395a83c5da36deb3d813c4c12d38d33f0ec0f86d..f3384ef76313c458e087bf58c57cea2e8b78678e 100644 (file)
@@ -119,11 +119,14 @@ private slots:
 
 private slots:
     void displayMnemonic(const QString &mnemonic);
+    void disableEncryptionForAccount(const AccountPtr &account) const;
     void showConnectionLabel(const QString &message, QStringList errors = QStringList());
     void openIgnoredFilesDialog(const QString & absFolderPath);
     void customizeStyle();
 
     void initializeE2eEncryption();
+    void resetE2eEncryption();
+    void checkClientSideEncryptionState();
     void removeActionFromEncryptionMessage(const QString &actionId);
 
 private:
index 49b528dc66fedaf18d5d33f95f6f6b43a1586c59..4277af83925d53de2c20cd04840708bfbca1b69b 100644 (file)
@@ -1066,7 +1066,7 @@ void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account)
 {
     _publicKey = QSslKey();
 
-    const auto createDeleteJob = [account](QString user) {
+    const auto createDeleteJob = [account](const QString user) {
         auto *job = new DeletePasswordJob(Theme::instance()->appName());
         job->setInsecureFallback(false);
         job->setKey(AbstractCredentials::keychainKey(account->url().toString(), user, account->id()));
@@ -1086,7 +1086,7 @@ void ClientSideEncryption::forgetSensitiveData(const AccountPtr &account)
     deleteMnemonicJob->start();
 }
 
-void ClientSideEncryption::handlePrivateKeyDeleted(QKeychain::Job *incoming)
+void ClientSideEncryption::handlePrivateKeyDeleted(const QKeychain::Job* const incoming)
 {
     if (incoming->error() != QKeychain::NoError) {
         qCWarning(lcCse) << "Private key could not be deleted:" << incoming->errorString();
@@ -1099,7 +1099,7 @@ void ClientSideEncryption::handlePrivateKeyDeleted(QKeychain::Job *incoming)
     checkAllSensitiveDataDeleted();
 }
 
-void ClientSideEncryption::handleCertificateDeleted(QKeychain::Job *incoming)
+void ClientSideEncryption::handleCertificateDeleted(const QKeychain::Job* const incoming)
 {
     if (incoming->error() != QKeychain::NoError) {
         qCWarning(lcCse) << "Certificate could not be deleted:" << incoming->errorString();
@@ -1112,7 +1112,7 @@ void ClientSideEncryption::handleCertificateDeleted(QKeychain::Job *incoming)
     checkAllSensitiveDataDeleted();
 }
 
-void ClientSideEncryption::handleMnemonicDeleted(QKeychain::Job *incoming)
+void ClientSideEncryption::handleMnemonicDeleted(const QKeychain::Job* const incoming)
 {
     if (incoming->error() != QKeychain::NoError) {
         qCWarning(lcCse) << "Mnemonic could not be deleted:" << incoming->errorString();
index 037d231ea02f0eafc7a2b80902d663937088ff30..335c767465604199d875cfda7de168a7cbb457ba 100644 (file)
@@ -145,9 +145,9 @@ private slots:
     void privateKeyFetched(QKeychain::Job *incoming);
     void mnemonicKeyFetched(QKeychain::Job *incoming);
 
-    void handlePrivateKeyDeleted(QKeychain::Job *incoming);
-    void handleCertificateDeleted(QKeychain::Job *incoming);
-    void handleMnemonicDeleted(QKeychain::Job *incoming);
+    void handlePrivateKeyDeleted(const QKeychain::Job* const incoming);
+    void handleCertificateDeleted(const QKeychain::Job* const incoming);
+    void handleMnemonicDeleted(const QKeychain::Job* const incoming);
     void checkAllSensitiveDataDeleted();
 
     void getPrivateKeyFromServer(const AccountPtr &account);