Browse Source

implement wallet password change.

implement wallet delete (though actual wallet file delete is left out still)
patch-4
Sander van Grieken 3 years ago
parent
commit
9243f3b896
  1. 2
      electrum/gui/qml/components/WalletMainView.qml
  2. 22
      electrum/gui/qml/components/Wallets.qml
  3. 115
      electrum/gui/qml/components/controls/PasswordDialog.qml
  4. 31
      electrum/gui/qml/components/main.qml
  5. 2
      electrum/gui/qml/qeapp.py
  6. 5
      electrum/gui/qml/qedaemon.py
  7. 46
      electrum/gui/qml/qewallet.py

2
electrum/gui/qml/components/WalletMainView.qml

@ -6,7 +6,7 @@ import QtQml 2.6
Item { Item {
id: rootItem id: rootItem
property string title: Daemon.currentWallet.name property string title: Daemon.currentWallet ? Daemon.currentWallet.name : ''
property QtObject menu: Menu { property QtObject menu: Menu {
id: menu id: menu

22
electrum/gui/qml/components/Wallets.qml

@ -41,7 +41,8 @@ Pane {
} }
function changePassword() { function changePassword() {
// TODO: show set password dialog // trigger dialog via wallet (auth then signal)
Daemon.currentWallet.start_change_password()
} }
property QtObject menu: Menu { property QtObject menu: Menu {
@ -58,7 +59,7 @@ Pane {
id: changePasswordComp id: changePasswordComp
MenuItem { MenuItem {
icon.color: 'transparent' icon.color: 'transparent'
enabled: false enabled: Daemon.currentWallet // != null
action: Action { action: Action {
text: qsTr('Change Password'); text: qsTr('Change Password');
onTriggered: rootItem.changePassword() onTriggered: rootItem.changePassword()
@ -308,6 +309,23 @@ Pane {
} }
} }
Connections {
target: Daemon.currentWallet
function onRequestNewPassword() { // new wallet password
var dialog = app.passwordDialog.createObject(app,
{
'confirmPassword': true,
'title': qsTr('Enter new password'),
'infotext': qsTr('If you forget your password, you\'ll need to\
restore from seed. Please make sure you have your seed stored safely')
} )
dialog.accepted.connect(function() {
Daemon.currentWallet.set_password(dialog.password)
})
dialog.open()
}
}
Component { Component {
id: share id: share
GenericShareDialog { GenericShareDialog {

115
electrum/gui/qml/components/controls/PasswordDialog.qml

@ -0,0 +1,115 @@
import QtQuick 2.6
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.3
import QtQuick.Controls.Material 2.0
import org.electrum 1.0
Dialog {
id: passworddialog
title: qsTr("Enter Password")
property bool confirmPassword: false
property string password
property string infotext
parent: Overlay.overlay
modal: true
x: (parent.width - width) / 2
y: (parent.height - height) / 2
Overlay.modal: Rectangle {
color: "#aa000000"
}
header: GridLayout {
columns: 2
rowSpacing: 0
Image {
source: "../../../icons/lock.png"
Layout.preferredWidth: constants.iconSizeXLarge
Layout.preferredHeight: constants.iconSizeXLarge
Layout.leftMargin: constants.paddingMedium
Layout.topMargin: constants.paddingMedium
Layout.bottomMargin: constants.paddingMedium
}
Label {
text: title
elide: Label.ElideRight
Layout.fillWidth: true
topPadding: constants.paddingXLarge
bottomPadding: constants.paddingXLarge
font.bold: true
font.pixelSize: constants.fontSizeMedium
}
Rectangle {
Layout.columnSpan: 2
Layout.fillWidth: true
Layout.leftMargin: constants.paddingXXSmall
Layout.rightMargin: constants.paddingXXSmall
height: 1
color: Qt.rgba(0,0,0,0.5)
}
}
ColumnLayout {
width: parent.width
InfoTextArea {
visible: infotext
text: infotext
Layout.preferredWidth: password_layout.width
}
GridLayout {
id: password_layout
columns: 2
Layout.fillWidth: true
Layout.margins: constants.paddingXXLarge
Label {
text: qsTr('Password')
}
TextField {
id: pw_1
echoMode: TextInput.Password
}
Label {
text: qsTr('Password (again)')
visible: confirmPassword
}
TextField {
id: pw_2
echoMode: TextInput.Password
visible: confirmPassword
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: constants.paddingXXLarge
Button {
text: qsTr("Ok")
enabled: confirmPassword ? pw_1.text == pw_2.text : true
onClicked: {
password = pw_1.text
passworddialog.accept()
}
}
Button {
text: qsTr("Cancel")
onClicked: {
passworddialog.reject()
}
}
}
}
}

31
electrum/gui/qml/components/main.qml

@ -171,6 +171,14 @@ ApplicationWindow
} }
} }
property alias passwordDialog: _passwordDialog
Component {
id: _passwordDialog
PasswordDialog {
onClosed: destroy()
}
}
NotificationPopup { NotificationPopup {
id: notificationPopup id: notificationPopup
} }
@ -225,14 +233,23 @@ ApplicationWindow
Connections { Connections {
target: Daemon.currentWallet target: Daemon.currentWallet
function onAuthRequired() { function onAuthRequired() {
var dialog = app.messageDialog.createObject(app, {'text': 'Auth placeholder', 'yesno': true}) if (Daemon.currentWallet.verify_password('')) {
dialog.yesClicked.connect(function() { // wallet has no password
Daemon.currentWallet.authProceed() Daemon.currentWallet.authProceed()
}) } else {
dialog.noClicked.connect(function() { var dialog = app.passwordDialog.createObject(app, {'title': qsTr('Enter current password')})
Daemon.currentWallet.authCancel() dialog.accepted.connect(function() {
}) if (Daemon.currentWallet.verify_password(dialog.password)) {
dialog.open() Daemon.currentWallet.authProceed()
} else {
Daemon.currentWallet.authCancel()
}
})
dialog.rejected.connect(function() {
Daemon.currentWallet.authCancel()
})
dialog.open()
}
} }
} }

2
electrum/gui/qml/qeapp.py

@ -52,6 +52,8 @@ class QEAppController(QObject):
def on_wallet_loaded(self): def on_wallet_loaded(self):
qewallet = self._qedaemon.currentWallet qewallet = self._qedaemon.currentWallet
if not qewallet:
return
# attach to the wallet user notification events # attach to the wallet user notification events
# connect only once # connect only once
try: try:

5
electrum/gui/qml/qedaemon.py

@ -152,7 +152,12 @@ class QEDaemon(AuthMixin, QObject):
path = wallet.wallet.storage.path path = wallet.wallet.storage.path
self._logger.debug('Ok to delete wallet with path %s' % path) self._logger.debug('Ok to delete wallet with path %s' % path)
# TODO checks, e.g. existing LN channels, unpaid requests, etc # TODO checks, e.g. existing LN channels, unpaid requests, etc
self._logger.debug('Not deleting yet, just unloading for now')
# TODO actually delete
# TODO walletLoaded signal is confusing
self.daemon.stop_wallet(path) self.daemon.stop_wallet(path)
self._current_wallet = None
self.walletLoaded.emit()
@pyqtProperty('QString') @pyqtProperty('QString')
def path(self): def path(self):

46
electrum/gui/qml/qewallet.py

@ -7,9 +7,10 @@ import threading
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import register_callback, Satoshis, format_time, parse_max_spend from electrum.util import register_callback, Satoshis, format_time, parse_max_spend, InvalidPassword
from electrum.logging import get_logger from electrum.logging import get_logger
from electrum.wallet import Wallet, Abstract_Wallet from electrum.wallet import Wallet, Abstract_Wallet
from electrum.storage import StorageEncryptionVersion
from electrum import bitcoin from electrum import bitcoin
from electrum.transaction import PartialTxOutput from electrum.transaction import PartialTxOutput
from electrum.invoices import (Invoice, InvoiceError, from electrum.invoices import (Invoice, InvoiceError,
@ -331,7 +332,7 @@ class QEWallet(AuthMixin, QObject):
self.wallet.init_lightning(password=None) # TODO pass password if needed self.wallet.init_lightning(password=None) # TODO pass password if needed
self.isLightningChanged.emit() self.isLightningChanged.emit()
@pyqtSlot('QString', int, int, bool) @pyqtSlot(str, int, int, bool)
def send_onchain(self, address, amount, fee=None, rbf=False): def send_onchain(self, address, amount, fee=None, rbf=False):
self._logger.info('send_onchain: %s %d' % (address,amount)) self._logger.info('send_onchain: %s %d' % (address,amount))
coins = self.wallet.get_spendable_coins(None) coins = self.wallet.get_spendable_coins(None)
@ -437,9 +438,9 @@ class QEWallet(AuthMixin, QObject):
return req_key, addr return req_key, addr
@pyqtSlot(QEAmount, 'QString', int) @pyqtSlot(QEAmount, str, int)
@pyqtSlot(QEAmount, 'QString', int, bool) @pyqtSlot(QEAmount, str, int, bool)
@pyqtSlot(QEAmount, 'QString', int, bool, bool) @pyqtSlot(QEAmount, str, int, bool, bool)
def create_request(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False): def create_request(self, amount: QEAmount, message: str, expiration: int, is_lightning: bool = False, ignore_gap: bool = False):
try: try:
if is_lightning: if is_lightning:
@ -463,29 +464,52 @@ class QEWallet(AuthMixin, QObject):
self._requestModel.add_invoice(self.wallet.get_request(key)) self._requestModel.add_invoice(self.wallet.get_request(key))
self.requestCreateSuccess.emit() self.requestCreateSuccess.emit()
@pyqtSlot('QString') @pyqtSlot(str)
def delete_request(self, key: str): def delete_request(self, key: str):
self._logger.debug('delete req %s' % key) self._logger.debug('delete req %s' % key)
self.wallet.delete_request(key) self.wallet.delete_request(key)
self._requestModel.delete_invoice(key) self._requestModel.delete_invoice(key)
@pyqtSlot('QString', result='QVariant') @pyqtSlot(str, result='QVariant')
def get_request(self, key: str): def get_request(self, key: str):
return self._requestModel.get_model_invoice(key) return self._requestModel.get_model_invoice(key)
@pyqtSlot('QString') @pyqtSlot(str)
def delete_invoice(self, key: str): def delete_invoice(self, key: str):
self._logger.debug('delete inv %s' % key) self._logger.debug('delete inv %s' % key)
self.wallet.delete_invoice(key) self.wallet.delete_invoice(key)
self._invoiceModel.delete_invoice(key) self._invoiceModel.delete_invoice(key)
@pyqtSlot('QString', result='QVariant') @pyqtSlot(str, result='QVariant')
def get_invoice(self, key: str): def get_invoice(self, key: str):
return self._invoiceModel.get_model_invoice(key) return self._invoiceModel.get_model_invoice(key)
@pyqtSlot(str) @pyqtSlot(str, result=bool)
def verify_password(self, password):
try:
self.wallet.storage.check_password(password)
return True
except InvalidPassword as e:
return False
requestNewPassword = pyqtSignal()
@pyqtSlot()
@auth_protect @auth_protect
def start_change_password(self):
self.requestNewPassword.emit()
@pyqtSlot(str)
def set_password(self, password): def set_password(self, password):
storage = self.wallet.storage storage = self.wallet.storage
# HW wallet not supported yet
if storage.is_encrypted_with_hw_device():
return
self._logger.debug('Ok to set password for wallet with path %s' % storage.path) self._logger.debug('Ok to set password for wallet with path %s' % storage.path)
# TODO if password:
enc_version = StorageEncryptionVersion.USER_PASSWORD
else:
enc_version = StorageEncryptionVersion.PLAINTEXT
storage.set_password(password, enc_version=enc_version)
self.wallet.save_db()

Loading…
Cancel
Save