Add key exporting to account settings

This commit is contained in:
miruka 2019-09-07 19:17:32 -04:00
parent 11486ab51d
commit a87f98fae0
5 changed files with 64 additions and 16 deletions

View File

@ -121,7 +121,6 @@
- Edit/delete own devices - Edit/delete own devices
- Request room keys from own other devices - Request room keys from own other devices
- Auto-trust accounts within the same client - Auto-trust accounts within the same client
- Export keys
- Uploads & proper http thumbnails - Uploads & proper http thumbnails
- Reduce messages ListView cacheBuffer height once http thumbnails - Reduce messages ListView cacheBuffer height once http thumbnails
downloading is implemented downloading is implemented

View File

@ -338,6 +338,17 @@ class MatrixClient(nio.AsyncClient):
await self.retry_decrypting_events() await self.retry_decrypting_events()
async def export_keys(self, outfile: str, passphrase: str) -> None:
path = Path(outfile)
path.parent.mkdir(parents=True, exist_ok=True)
# Remove any existing file
# (the QML dialog asks the user if he wants to overwrite before this)
path.unlink()
await super().export_keys(outfile, passphrase)
async def clear_import_error(self) -> None: async def clear_import_error(self) -> None:
self.models[Account][self.user_id].import_error = ("", "", "") self.models[Account][self.user_id].import_error = ("", "", "")

View File

@ -28,10 +28,10 @@ Popup {
function verifyPassword(pass, callback) { function verifyPassword(pass, callback) {
// Implement this function when using this component. // Can be reimplemented when using this component.
// Return true on success, false on invalid password, or // Pass to the callback true on success, false on invalid password, or
// a [error message, translated] array for any other error. // a [error message, translated] array for any other error.
return ["Verification not implemented", false] callback(true)
} }

View File

@ -14,6 +14,19 @@ HLoader {
property bool importing: false property bool importing: false
function exportKeys(file, passphrase, button=null) {
if (button) button.loading = true
let path = file.toString().replace(/^file:\/\//, "")
py.callClientCoro(
editAccount.userId, "export_keys", [path, passphrase], () => {
// null: user is on another page
if (encryptionUI !== null && button) button.loading = false
}
)
}
function importKeys(file, passphrase, button=null) { function importKeys(file, passphrase, button=null) {
if (button) button.loading = true if (button) button.loading = true
encryptionUI.importing = true encryptionUI.importing = true
@ -22,7 +35,7 @@ HLoader {
py.callClientCoro( py.callClientCoro(
editAccount.userId, "import_keys", [path, passphrase], () => { editAccount.userId, "import_keys", [path, passphrase], () => {
if (encryptionUI !== null) { // null: user is on another page if (encryptionUI !== null) {
encryptionUI.importing = false encryptionUI.importing = false
if (button) button.loading = false if (button) button.loading = false
} }

View File

@ -1,22 +1,27 @@
import QtQuick 2.12 import QtQuick 2.12
import QtQuick.Controls 2.12 import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import Qt.labs.platform 1.1
import "../../Base" import "../../Base"
import "../../utils.js" as Utils import "../../utils.js" as Utils
HBox { HBox {
property var exportButton: null
horizontalSpacing: currentSpacing horizontalSpacing: currentSpacing
verticalSpacing: currentSpacing verticalSpacing: currentSpacing
buttonModel: [ buttonModel: [
{ name: "export", text: qsTr("Export"), iconName: "export-keys", { name: "export", text: qsTr("Export"), iconName: "export-keys"},
enabled: false },
{ name: "import", text: qsTr("Import"), iconName: "import-keys"}, { name: "import", text: qsTr("Import"), iconName: "import-keys"},
] ]
buttonCallbacks: ({ buttonCallbacks: ({
export: button => {}, export: button => {
import: button => { fileDialog.dialog.open() }, exportButton = button
exportFileDialog.dialog.open()
},
import: button => { importFileDialog.dialog.open() },
}) })
@ -24,29 +29,49 @@ HBox {
wrapMode: Text.Wrap wrapMode: Text.Wrap
text: qsTr( text: qsTr(
"The decryption keys for messages you received in encrypted " + "The decryption keys for messages you received in encrypted " +
"rooms can be exported to a passphrase-protected file.%1" + "rooms can be exported to a passphrase-protected file.\n" +
"You will then be able to import this file in another " + "You can then import this file on another Matrix account or " +
"Matrix client." "client, to be able to decrypt these messages again."
).arg(pageLoader.isWide ? "\n" :"\n\n") )
Layout.fillWidth: true Layout.fillWidth: true
} }
HFileDialogOpener { HFileDialogOpener {
id: fileDialog id: exportFileDialog
fill: false fill: false
dialog.title: qsTr("Select a decryption key file to import") dialog.title: qsTr("Save decryption keys file as...")
dialog.fileMode: FileDialog.SaveFile
onFileChanged: {
exportPasswordPopup.file = file
exportPasswordPopup.open()
}
}
HFileDialogOpener {
id: importFileDialog
fill: false
dialog.title: qsTr("Select a decryption keys file to import")
onFileChanged: { onFileChanged: {
importPasswordPopup.file = file importPasswordPopup.file = file
importPasswordPopup.open() importPasswordPopup.open()
} }
} }
HPasswordPopup {
property url file: ""
id: exportPasswordPopup
label.text: qsTr("Please enter a passphrase to protect this file:")
onAcceptedPasswordChanged:
encryptionUI.exportKeys(file, acceptedPassword, exportButton)
}
HPasswordPopup { HPasswordPopup {
property url file: "" property url file: ""
function verifyPassword(pass, callback) { function verifyPassword(pass, callback) {
return py.callCoro( py.callCoro(
"check_exported_keys_passphrase", "check_exported_keys_passphrase",
[file.toString().replace(/^file:\/\//, ""), pass], [file.toString().replace(/^file:\/\//, ""), pass],
callback callback