Simplify key backup unlocking

Replaces the separate text fields for security keys and backup passphrases with a single on; internally, both methods are then tried.
This commit is contained in:
Tobias Fella
2025-11-10 22:20:47 +01:00
committed by Tobias Fella
parent 52f71d5c55
commit 3c8ca0d421
3 changed files with 56 additions and 54 deletions

View File

@@ -11,6 +11,8 @@ import org.kde.neochat
FormCard.FormCardPage { FormCard.FormCardPage {
id: root id: root
property bool processing: false
title: i18nc("@title:window", "Load your encrypted messages") title: i18nc("@title:window", "Load your encrypted messages")
topPadding: Kirigami.Units.gridUnit topPadding: Kirigami.Units.gridUnit
@@ -25,75 +27,42 @@ FormCard.FormCardPage {
position: Kirigami.InlineMessage.Position.Header position: Kirigami.InlineMessage.Position.Header
} }
property SSSSHandler ssssHandler: SSSSHandler { Connections {
id: ssssHandler target: Controller.activeConnection
function onKeyBackupError(): void {
securityKeyField.clear()
root.processing = false
banner.text = i18nc("@info:status", "The security key or backup passphrase was not correct.")
banner.visible = true
}
property bool processing: false function onKeyBackupUnlocked(): void {
root.processing = false
connection: Controller.activeConnection
onKeyBackupUnlocked: {
ssssHandler.processing = false
banner.text = i18nc("@info:status", "Encryption keys restored.") banner.text = i18nc("@info:status", "Encryption keys restored.")
banner.type = Kirigami.MessageType.Positive banner.type = Kirigami.MessageType.Positive
banner.visible = true banner.visible = true
} }
onError: error => {
if (error !== SSSSHandler.WrongKeyError) {
banner.text = error
banner.visible = true
return;
}
passwordField.clear()
ssssHandler.processing = false
banner.text = i18nc("@info:status", "The security phrase was not correct.")
banner.visible = true
}
} }
FormCard.FormHeader { FormCard.FormHeader {
title: i18nc("@title", "Unlock using Passphrase") title: i18nc("@title", "Unlock using Security Key or Backup Passphrase")
} }
FormCard.FormCard { FormCard.FormCard {
FormCard.FormTextDelegate { FormCard.FormTextDelegate {
description: i18nc("@info", "If you have a backup passphrase for this account, enter it below.") description: i18nc("@info", "If you have a security key or backup passphrase for this account, enter it below or upload it as a file.")
}
FormCard.FormTextFieldDelegate {
id: passwordField
label: i18nc("@label:textbox", "Backup Password:")
echoMode: TextInput.Password
}
FormCard.FormButtonDelegate {
id: unlockButton
text: i18nc("@action:button", "Unlock")
icon.name: "unlock"
enabled: passwordField.text.length > 0 && !ssssHandler.processing
onClicked: {
ssssHandler.processing = true
banner.visible = false
ssssHandler.unlockSSSSWithPassphrase(passwordField.text)
}
}
}
FormCard.FormHeader {
title: i18nc("@title", "Unlock using Security Key")
}
FormCard.FormCard {
FormCard.FormTextDelegate {
description: i18nc("@info", "If you have a security key for this account, enter it below or upload it as a file.")
} }
FormCard.FormTextFieldDelegate { FormCard.FormTextFieldDelegate {
id: securityKeyField id: securityKeyField
label: i18nc("@label:textbox", "Security Key:") label: i18nc("@label:textbox", "Security Key or Backup Passphrase:")
echoMode: TextInput.Password echoMode: TextInput.Password
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
id: uploadSecurityKeyButton id: uploadSecurityKeyButton
text: i18nc("@action:button", "Upload from File") text: i18nc("@action:button", "Upload from File")
icon.name: "cloud-upload" icon.name: "cloud-upload"
enabled: !ssssHandler.processing enabled: !root.processing
onClicked: { onClicked: {
ssssHandler.processing = true root.processing = true
openFileDialog.open() openFileDialog.open()
} }
} }
@@ -101,10 +70,10 @@ FormCard.FormCardPage {
id: unlockSecurityKeyButton id: unlockSecurityKeyButton
text: i18nc("@action:button", "Unlock") text: i18nc("@action:button", "Unlock")
icon.name: "unlock" icon.name: "unlock"
enabled: securityKeyField.text.length > 0 && !ssssHandler.processing enabled: securityKeyField.text.length > 0 && !root.processing
onClicked: { onClicked: {
ssssHandler.processing = true root.processing = true
ssssHandler.unlockSSSSFromSecurityKey(securityKeyField.text) Controller.activeConnection.unlockSSSS(securityKeyField.text)
} }
} }
} }
@@ -120,10 +89,10 @@ FormCard.FormCardPage {
id: unlockCrossSigningButton id: unlockCrossSigningButton
icon.name: "emblem-shared-symbolic" icon.name: "emblem-shared-symbolic"
text: i18nc("@action:button", "Request from other Devices") text: i18nc("@action:button", "Request from other Devices")
enabled: !ssssHandler.processing enabled: !root.processing
onClicked: { onClicked: {
ssssHandler.processing = true root.processing = true
ssssHandler.unlockSSSSFromCrossSigning() Controller.activeConnection.unlockSSSS("")
} }
} }
} }

View File

@@ -19,6 +19,7 @@
#include <Quotient/csapi/profile.h> #include <Quotient/csapi/profile.h>
#include <Quotient/csapi/registration.h> #include <Quotient/csapi/registration.h>
#include <Quotient/csapi/versions.h> #include <Quotient/csapi/versions.h>
#include <Quotient/e2ee/sssshandler.h>
#include <Quotient/jobs/downloadfilejob.h> #include <Quotient/jobs/downloadfilejob.h>
#include <Quotient/qt_connection_util.h> #include <Quotient/qt_connection_util.h>
#include <Quotient/room.h> #include <Quotient/room.h>
@@ -574,4 +575,31 @@ bool NeoChatConnection::isVerifiedSession() const
return isVerifiedDevice(userId(), deviceId()); return isVerifiedDevice(userId(), deviceId());
} }
void NeoChatConnection::unlockSSSS(const QString &secret)
{
auto handler = new SSSSHandler();
handler->setConnection(this);
connect(handler, &SSSSHandler::error, this, [secret, handler, this]() {
disconnect(handler, &SSSSHandler::error, this, nullptr);
if (!secret.isEmpty()) {
connect(handler, &SSSSHandler::error, this, [handler, this]() {
Q_EMIT keyBackupError();
delete handler;
});
handler->unlockSSSSWithPassphrase(secret);
} else {
Q_EMIT keyBackupError();
}
});
connect(handler, &SSSSHandler::keyBackupUnlocked, this, [handler, this]() {
Q_EMIT keyBackupUnlocked();
connect(handler, &SSSSHandler::finished, handler, &SSSSHandler::deleteLater);
});
if (secret.isEmpty()) {
handler->unlockSSSSFromCrossSigning();
} else {
handler->unlockSSSSFromSecurityKey(secret);
}
}
#include "moc_neochatconnection.cpp" #include "moc_neochatconnection.cpp"

View File

@@ -222,6 +222,8 @@ public:
*/ */
bool isVerifiedSession() const; bool isVerifiedSession() const;
Q_INVOKABLE void unlockSSSS(const QString &secret);
Q_SIGNALS: Q_SIGNALS:
void globalUrlPreviewEnabledChanged(); void globalUrlPreviewEnabledChanged();
void labelChanged(); void labelChanged();
@@ -259,6 +261,9 @@ Q_SIGNALS:
*/ */
void ownSessionVerified(); void ownSessionVerified();
void keyBackupUnlocked();
void keyBackupError();
private: private:
static bool m_globalUrlPreviewDefault; static bool m_globalUrlPreviewDefault;
static PushRuleAction::Action m_defaultAction; static PushRuleAction::Action m_defaultAction;