implement wallet password change.
implement wallet delete (though actual wallet file delete is left out still)
This commit is contained in:
@@ -6,7 +6,7 @@ import QtQml 2.6
|
||||
Item {
|
||||
id: rootItem
|
||||
|
||||
property string title: Daemon.currentWallet.name
|
||||
property string title: Daemon.currentWallet ? Daemon.currentWallet.name : ''
|
||||
|
||||
property QtObject menu: Menu {
|
||||
id: menu
|
||||
|
||||
@@ -41,7 +41,8 @@ Pane {
|
||||
}
|
||||
|
||||
function changePassword() {
|
||||
// TODO: show set password dialog
|
||||
// trigger dialog via wallet (auth then signal)
|
||||
Daemon.currentWallet.start_change_password()
|
||||
}
|
||||
|
||||
property QtObject menu: Menu {
|
||||
@@ -58,7 +59,7 @@ Pane {
|
||||
id: changePasswordComp
|
||||
MenuItem {
|
||||
icon.color: 'transparent'
|
||||
enabled: false
|
||||
enabled: Daemon.currentWallet // != null
|
||||
action: Action {
|
||||
text: qsTr('Change Password');
|
||||
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 {
|
||||
id: share
|
||||
GenericShareDialog {
|
||||
|
||||
115
electrum/gui/qml/components/controls/PasswordDialog.qml
Normal file
115
electrum/gui/qml/components/controls/PasswordDialog.qml
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -171,6 +171,14 @@ ApplicationWindow
|
||||
}
|
||||
}
|
||||
|
||||
property alias passwordDialog: _passwordDialog
|
||||
Component {
|
||||
id: _passwordDialog
|
||||
PasswordDialog {
|
||||
onClosed: destroy()
|
||||
}
|
||||
}
|
||||
|
||||
NotificationPopup {
|
||||
id: notificationPopup
|
||||
}
|
||||
@@ -225,14 +233,23 @@ ApplicationWindow
|
||||
Connections {
|
||||
target: Daemon.currentWallet
|
||||
function onAuthRequired() {
|
||||
var dialog = app.messageDialog.createObject(app, {'text': 'Auth placeholder', 'yesno': true})
|
||||
dialog.yesClicked.connect(function() {
|
||||
if (Daemon.currentWallet.verify_password('')) {
|
||||
// wallet has no password
|
||||
Daemon.currentWallet.authProceed()
|
||||
})
|
||||
dialog.noClicked.connect(function() {
|
||||
Daemon.currentWallet.authCancel()
|
||||
})
|
||||
dialog.open()
|
||||
} else {
|
||||
var dialog = app.passwordDialog.createObject(app, {'title': qsTr('Enter current password')})
|
||||
dialog.accepted.connect(function() {
|
||||
if (Daemon.currentWallet.verify_password(dialog.password)) {
|
||||
Daemon.currentWallet.authProceed()
|
||||
} else {
|
||||
Daemon.currentWallet.authCancel()
|
||||
}
|
||||
})
|
||||
dialog.rejected.connect(function() {
|
||||
Daemon.currentWallet.authCancel()
|
||||
})
|
||||
dialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ class QEAppController(QObject):
|
||||
|
||||
def on_wallet_loaded(self):
|
||||
qewallet = self._qedaemon.currentWallet
|
||||
if not qewallet:
|
||||
return
|
||||
# attach to the wallet user notification events
|
||||
# connect only once
|
||||
try:
|
||||
|
||||
@@ -152,7 +152,12 @@ class QEDaemon(AuthMixin, QObject):
|
||||
path = wallet.wallet.storage.path
|
||||
self._logger.debug('Ok to delete wallet with path %s' % path)
|
||||
# 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._current_wallet = None
|
||||
self.walletLoaded.emit()
|
||||
|
||||
@pyqtProperty('QString')
|
||||
def path(self):
|
||||
|
||||
@@ -7,9 +7,10 @@ import threading
|
||||
from PyQt5.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QUrl, QTimer
|
||||
|
||||
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.wallet import Wallet, Abstract_Wallet
|
||||
from electrum.storage import StorageEncryptionVersion
|
||||
from electrum import bitcoin
|
||||
from electrum.transaction import PartialTxOutput
|
||||
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.isLightningChanged.emit()
|
||||
|
||||
@pyqtSlot('QString', int, int, bool)
|
||||
@pyqtSlot(str, int, int, bool)
|
||||
def send_onchain(self, address, amount, fee=None, rbf=False):
|
||||
self._logger.info('send_onchain: %s %d' % (address,amount))
|
||||
coins = self.wallet.get_spendable_coins(None)
|
||||
@@ -437,9 +438,9 @@ class QEWallet(AuthMixin, QObject):
|
||||
|
||||
return req_key, addr
|
||||
|
||||
@pyqtSlot(QEAmount, 'QString', int)
|
||||
@pyqtSlot(QEAmount, 'QString', int, bool)
|
||||
@pyqtSlot(QEAmount, 'QString', int, bool, bool)
|
||||
@pyqtSlot(QEAmount, str, int)
|
||||
@pyqtSlot(QEAmount, str, int, 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):
|
||||
try:
|
||||
if is_lightning:
|
||||
@@ -463,29 +464,52 @@ class QEWallet(AuthMixin, QObject):
|
||||
self._requestModel.add_invoice(self.wallet.get_request(key))
|
||||
self.requestCreateSuccess.emit()
|
||||
|
||||
@pyqtSlot('QString')
|
||||
@pyqtSlot(str)
|
||||
def delete_request(self, key: str):
|
||||
self._logger.debug('delete req %s' % key)
|
||||
self.wallet.delete_request(key)
|
||||
self._requestModel.delete_invoice(key)
|
||||
|
||||
@pyqtSlot('QString', result='QVariant')
|
||||
@pyqtSlot(str, result='QVariant')
|
||||
def get_request(self, key: str):
|
||||
return self._requestModel.get_model_invoice(key)
|
||||
|
||||
@pyqtSlot('QString')
|
||||
@pyqtSlot(str)
|
||||
def delete_invoice(self, key: str):
|
||||
self._logger.debug('delete inv %s' % key)
|
||||
self.wallet.delete_invoice(key)
|
||||
self._invoiceModel.delete_invoice(key)
|
||||
|
||||
@pyqtSlot('QString', result='QVariant')
|
||||
@pyqtSlot(str, result='QVariant')
|
||||
def get_invoice(self, key: str):
|
||||
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
|
||||
def start_change_password(self):
|
||||
self.requestNewPassword.emit()
|
||||
|
||||
@pyqtSlot(str)
|
||||
def set_password(self, password):
|
||||
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)
|
||||
# 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()
|
||||
|
||||
Reference in New Issue
Block a user