Browse Source

kivy: request PIN code on startup

3.2.x
ThomasV 7 years ago
parent
commit
b75d82491b
  1. 3
      gui/kivy/main.kv
  2. 69
      gui/kivy/main_window.py
  3. 28
      gui/kivy/uix/dialogs/installwizard.py
  4. 95
      gui/kivy/uix/dialogs/password_dialog.py
  5. 3
      gui/kivy/uix/dialogs/settings.py

3
gui/kivy/main.kv

@ -285,7 +285,8 @@
<KButton@Button>: <KButton@Button>:
size_hint: 1, None size_hint: 1, None
height: '48dp' height: '60dp'
font_size: '30dp'
on_release: on_release:
self.parent.update_amount(self.text) self.parent.update_amount(self.text)

69
gui/kivy/main_window.py

@ -244,6 +244,7 @@ class ElectrumWindow(App):
self.tabs = None self.tabs = None
self.is_exit = False self.is_exit = False
self.wallet = None self.wallet = None
self.pause_time = 0
App.__init__(self)#, **kwargs) App.__init__(self)#, **kwargs)
@ -445,7 +446,6 @@ class ElectrumWindow(App):
#win.softinput_mode = 'below_target' #win.softinput_mode = 'below_target'
self.on_size(win, win.size) self.on_size(win, win.size)
self.init_ui() self.init_ui()
self.load_wallet_by_name(self.electrum_config.get_wallet_path())
# init plugins # init plugins
run_hook('init_kivy', self) run_hook('init_kivy', self)
# fiat currency # fiat currency
@ -467,6 +467,8 @@ class ElectrumWindow(App):
self.network.register_callback(self.on_fee, ['fee']) self.network.register_callback(self.on_fee, ['fee'])
self.network.register_callback(self.on_quotes, ['on_quotes']) self.network.register_callback(self.on_quotes, ['on_quotes'])
self.network.register_callback(self.on_history, ['on_history']) self.network.register_callback(self.on_history, ['on_history'])
# load wallet
self.load_wallet_by_name(self.electrum_config.get_wallet_path())
# URI passed in config # URI passed in config
uri = self.electrum_config.get('url') uri = self.electrum_config.get('url')
if uri: if uri:
@ -484,17 +486,18 @@ class ElectrumWindow(App):
wallet.start_threads(self.daemon.network) wallet.start_threads(self.daemon.network)
self.daemon.add_wallet(wallet) self.daemon.add_wallet(wallet)
self.load_wallet(wallet) self.load_wallet(wallet)
self.on_resume()
def load_wallet_by_name(self, path): def load_wallet_by_name(self, path):
if not path: if not path:
return return
if self.wallet and self.wallet.storage.path == path:
return
wallet = self.daemon.load_wallet(path, None) wallet = self.daemon.load_wallet(path, None)
if wallet: if wallet:
if wallet != self.wallet: if wallet.has_password():
self.stop_wallet() self.password_dialog(wallet, _('Enter PIN code'), lambda x: self.load_wallet(wallet), self.stop)
else:
self.load_wallet(wallet) self.load_wallet(wallet)
self.on_resume()
else: else:
Logger.debug('Electrum: Wallet not found. Launching install wizard') Logger.debug('Electrum: Wallet not found. Launching install wizard')
storage = WalletStorage(path) storage = WalletStorage(path)
@ -504,6 +507,7 @@ class ElectrumWindow(App):
wizard.run(action) wizard.run(action)
def on_stop(self): def on_stop(self):
Logger.info('on_stop')
self.stop_wallet() self.stop_wallet()
def stop_wallet(self): def stop_wallet(self):
@ -617,6 +621,8 @@ class ElectrumWindow(App):
@profiler @profiler
def load_wallet(self, wallet): def load_wallet(self, wallet):
if self.wallet:
self.stop_wallet()
self.wallet = wallet self.wallet = wallet
self.update_wallet() self.update_wallet()
# Once GUI has been initialized check if we want to announce something # Once GUI has been initialized check if we want to announce something
@ -625,6 +631,7 @@ class ElectrumWindow(App):
self.receive_screen.clear() self.receive_screen.clear()
self.update_tabs() self.update_tabs()
run_hook('load_wallet', wallet, self) run_hook('load_wallet', wallet, self)
print('load wallet done', self.wallet)
def update_status(self, *dt): def update_status(self, *dt):
self.num_blocks = self.network.get_local_height() self.num_blocks = self.network.get_local_height()
@ -684,12 +691,16 @@ class ElectrumWindow(App):
Logger.Error('Notification: needs plyer; `sudo pip install plyer`') Logger.Error('Notification: needs plyer; `sudo pip install plyer`')
def on_pause(self): def on_pause(self):
self.pause_time = time.time()
# pause nfc # pause nfc
if self.nfcscanner: if self.nfcscanner:
self.nfcscanner.nfc_disable() self.nfcscanner.nfc_disable()
return True return True
def on_resume(self): def on_resume(self):
now = time.time()
if self.wallet.has_password and now - self.pause_time > 60:
self.password_dialog(self.wallet, _('Enter PIN'), None, self.stop)
if self.nfcscanner: if self.nfcscanner:
self.nfcscanner.nfc_enable() self.nfcscanner.nfc_enable()
@ -875,7 +886,8 @@ class ElectrumWindow(App):
def protected(self, msg, f, args): def protected(self, msg, f, args):
if self.wallet.has_password(): if self.wallet.has_password():
self.password_dialog(msg, f, args) on_success = lambda pw: f(*(args + (pw,)))
self.password_dialog(self.wallet, msg, on_success, lambda: None)
else: else:
f(*(args + (None,))) f(*(args + (None,)))
@ -887,7 +899,7 @@ class ElectrumWindow(App):
def _delete_wallet(self, b): def _delete_wallet(self, b):
if b: if b:
basename = os.path.basename(self.wallet.storage.path) basename = self.wallet.basename()
self.protected(_("Enter your PIN code to confirm deletion of {}").format(basename), self.__delete_wallet, ()) self.protected(_("Enter your PIN code to confirm deletion of {}").format(basename), self.__delete_wallet, ())
def __delete_wallet(self, pw): def __delete_wallet(self, pw):
@ -925,40 +937,23 @@ class ElectrumWindow(App):
if passphrase: if passphrase:
label.text += '\n\n' + _('Passphrase') + ': ' + passphrase label.text += '\n\n' + _('Passphrase') + ': ' + passphrase
def change_password(self, cb): def password_dialog(self, wallet, msg, on_success, on_failure):
if self.wallet.has_password(): from .uix.dialogs.password_dialog import PasswordDialog
self.protected(_("Changing PIN code.") + '\n' + _("Enter your current PIN:"), self._change_password, (cb,)) if self._password_dialog is None:
else: self._password_dialog = PasswordDialog()
self._change_password(cb, None) self._password_dialog.init(self, wallet, msg, on_success, on_failure)
self._password_dialog.open()
def _change_password(self, cb, old_password):
if self.wallet.has_password():
if old_password is None:
return
try:
self.wallet.check_password(old_password)
except InvalidPassword:
self.show_error("Invalid PIN")
return
self.password_dialog(_('Enter new PIN'), self._change_password2, (cb, old_password,))
def _change_password2(self, cb, old_password, new_password):
self.password_dialog(_('Confirm new PIN'), self._change_password3, (cb, old_password, new_password))
def _change_password3(self, cb, old_password, new_password, confirmed_password):
if new_password == confirmed_password:
self.wallet.update_password(old_password, new_password)
cb()
else:
self.show_error("PIN numbers do not match")
def password_dialog(self, msg, f, args): def change_password(self, cb):
from .uix.dialogs.password_dialog import PasswordDialog from .uix.dialogs.password_dialog import PasswordDialog
def callback(pw):
Clock.schedule_once(lambda x: f(*(args + (pw,))), 0.1)
if self._password_dialog is None: if self._password_dialog is None:
self._password_dialog = PasswordDialog() self._password_dialog = PasswordDialog()
self._password_dialog.init(msg, callback) message = _("Changing PIN code.") + '\n' + _("Enter your current PIN:")
def on_success(old_password, new_password):
self.wallet.update_password(old_password, new_password)
self.show_info(_("Your PIN code was updated"))
on_failure = lambda: self.show_error(_("PIN codes do not match"))
self._password_dialog.init(self, self.wallet, message, on_success, on_failure, is_change=1)
self._password_dialog.open() self._password_dialog.open()
def export_private_keys(self, pk_label, addr): def export_private_keys(self, pk_label, addr):

28
gui/kivy/uix/dialogs/installwizard.py

@ -802,28 +802,18 @@ class InstallWizard(BaseWizard, Widget):
app = App.get_running_app() app = App.get_running_app()
Clock.schedule_once(lambda dt: app.show_error(msg)) Clock.schedule_once(lambda dt: app.show_error(msg))
def password_dialog(self, message, callback): def request_password(self, run_next, force_disable_encrypt_cb=False):
def on_success(old_pin, pin):
assert old_pin is None
run_next(pin, False)
def on_failure():
self.show_error(_('PIN mismatch'))
self.run('request_password', run_next)
popup = PasswordDialog() popup = PasswordDialog()
popup.init(message, callback) app = App.get_running_app()
popup.init(app, None, _('Choose PIN code'), on_success, on_failure, is_change=2)
popup.open() popup.open()
def request_password(self, run_next, force_disable_encrypt_cb=False):
def callback(pin):
if pin:
self.run('confirm_password', pin, run_next)
else:
run_next(None, None)
self.password_dialog('Choose a PIN code', callback)
def confirm_password(self, pin, run_next):
def callback(conf):
if conf == pin:
run_next(pin, False)
else:
self.show_error(_('PIN mismatch'))
self.run('request_password', run_next)
self.password_dialog('Confirm your PIN code', callback)
def action_dialog(self, action, run_next): def action_dialog(self, action, run_next):
f = getattr(self, action) f = getattr(self, action)
f() f()

95
gui/kivy/uix/dialogs/password_dialog.py

@ -5,35 +5,42 @@ from kivy.lang import Builder
from decimal import Decimal from decimal import Decimal
from kivy.clock import Clock from kivy.clock import Clock
from electrum.util import InvalidPassword
from electrum_gui.kivy.i18n import _
Builder.load_string(''' Builder.load_string('''
<PasswordDialog@Popup> <PasswordDialog@Popup>
id: popup id: popup
title: _('PIN Code') title: 'Electrum'
message: '' message: ''
size_hint: 0.9, 0.9
BoxLayout: BoxLayout:
size_hint: 1, 1
orientation: 'vertical' orientation: 'vertical'
Widget: Widget:
size_hint: 1, 1 size_hint: 1, 0.05
Label: Label:
font_size: '20dp'
text: root.message text: root.message
text_size: self.width, None text_size: self.width, None
size: self.texture_size size: self.texture_size
Widget: Widget:
size_hint: 1, 1 size_hint: 1, 0.05
Label: Label:
id: a id: a
text: ' * '*len(kb.password) + ' o '*(6-len(kb.password)) font_size: '50dp'
text: '*'*len(kb.password) + '-'*(6-len(kb.password))
size: self.texture_size
Widget: Widget:
size_hint: 1, 1 size_hint: 1, 0.05
GridLayout: GridLayout:
id: kb id: kb
size_hint: 1, None
height: self.minimum_height
update_amount: popup.update_password update_amount: popup.update_password
password: '' password: ''
on_password: popup.on_password(self.password) on_password: popup.on_password(self.password)
size_hint: 1, None spacing: '2dp'
height: '200dp'
cols: 3 cols: 3
KButton: KButton:
text: '1' text: '1'
@ -59,30 +66,44 @@ Builder.load_string('''
text: '0' text: '0'
KButton: KButton:
text: '<' text: '<'
BoxLayout:
size_hint: 1, None
height: '48dp'
Widget:
size_hint: 0.5, None
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Cancel')
on_release:
popup.dismiss()
popup.callback(None)
''') ''')
class PasswordDialog(Factory.Popup): class PasswordDialog(Factory.Popup):
#def __init__(self, message, callback): def init(self, app, wallet, message, on_success, on_failure, is_change=0):
# Factory.Popup.__init__(self) self.app = app
self.wallet = wallet
def init(self, message, callback):
self.message = message self.message = message
self.callback = callback self.on_success = on_success
self.on_failure = on_failure
self.ids.kb.password = '' self.ids.kb.password = ''
self.success = False
self.is_change = is_change
self.pw = None
self.new_password = None
self.title = 'Electrum' + (' - ' + self.wallet.basename() if self.wallet else '')
def check_password(self, password):
if self.is_change > 1:
return True
try:
self.wallet.check_password(password)
return True
except InvalidPassword as e:
return False
def on_dismiss(self):
if not self.success:
if self.on_failure:
self.on_failure()
else:
# keep dialog open
return True
else:
if self.on_success:
args = (self.pw, self.new_password) if self.is_change else (self.pw,)
Clock.schedule_once(lambda dt: self.on_success(*args), 0.1)
def update_password(self, c): def update_password(self, c):
kb = self.ids.kb kb = self.ids.kb
@ -97,5 +118,25 @@ class PasswordDialog(Factory.Popup):
def on_password(self, pw): def on_password(self, pw):
if len(pw) == 6: if len(pw) == 6:
self.dismiss() if self.check_password(pw):
Clock.schedule_once(lambda dt: self.callback(pw), 0.1) if self.is_change == 0:
self.success = True
self.pw = pw
self.message = _('Please wait...')
self.dismiss()
elif self.is_change == 1:
self.pw = pw
self.message = _('Enter new PIN')
self.ids.kb.password = ''
self.is_change = 2
elif self.is_change == 2:
self.new_password = pw
self.message = _('Confirm new PIN')
self.ids.kb.password = ''
self.is_change = 3
elif self.is_change == 3:
self.success = pw == self.new_password
self.dismiss()
else:
self.app.show_error(_('Wrong PIN'))
self.ids.kb.password = ''

3
gui/kivy/uix/dialogs/settings.py

@ -36,9 +36,8 @@ Builder.load_string('''
action: partial(root.language_dialog, self) action: partial(root.language_dialog, self)
CardSeparator CardSeparator
SettingsItem: SettingsItem:
status: '' if root.disable_pin else ('ON' if root.use_encryption else 'OFF')
disabled: root.disable_pin disabled: root.disable_pin
title: _('PIN code') + ': ' + self.status title: _('PIN code')
description: _("Change your PIN code.") description: _("Change your PIN code.")
action: partial(root.change_password, self) action: partial(root.change_password, self)
CardSeparator CardSeparator

Loading…
Cancel
Save