Browse Source

auth_protect: pass authMessage in the auth_protect decorator,

instead of relying on side-effects

This is probably safer, and also more self-contained.
master
ThomasV 3 years ago
parent
commit
a03f4769ca
  1. 40
      electrum/gui/qml/auth.py
  2. 26
      electrum/gui/qml/components/main.qml
  3. 3
      electrum/gui/qml/qechannelopener.py
  4. 3
      electrum/gui/qml/qedaemon.py
  5. 1
      electrum/gui/qml/qeinvoice.py
  6. 10
      electrum/gui/qml/qeswaphelper.py
  7. 2
      electrum/gui/qml/qewallet.py

40
electrum/gui/qml/auth.py

@ -4,32 +4,28 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty
from electrum.logging import get_logger
def auth_protect(func=None, reject=None, method='pin'):
if func is None:
return partial(auth_protect, reject=reject, method=method)
def auth_protect(message='', method='pin', reject=None):
@wraps(func)
def wrapper(self, *args, **kwargs):
_logger = get_logger(__name__)
_logger.debug(f'{str(self)}.{func.__name__}')
if hasattr(self, '__auth_fcall'):
_logger.debug('object already has a pending authed function call')
raise Exception('object already has a pending authed function call')
setattr(self, '__auth_fcall', (func,args,kwargs,reject))
getattr(self, 'authRequired').emit(method)
def decorator(func=None):
if func is None:
return partial(auth_protect, reject=reject, method=method)
return wrapper
@wraps(func)
def wrapper(self, *args, **kwargs):
_logger = get_logger(__name__)
_logger.debug(f'{str(self)}.{func.__name__}')
if hasattr(self, '__auth_fcall'):
_logger.debug('object already has a pending authed function call')
raise Exception('object already has a pending authed function call')
setattr(self, '__auth_fcall', (func,args,kwargs,reject))
getattr(self, 'authRequired').emit(method, message)
return wrapper
return decorator
class AuthMixin:
_auth_logger = get_logger(__name__)
authRequired = pyqtSignal([str],arguments=['method'])
auth_message = ''
_authMixinMessageChanged = pyqtSignal()
@pyqtProperty(str, notify=_authMixinMessageChanged)
def authMessage(self):
return self.auth_message
authRequired = pyqtSignal([str, str], arguments=['method', 'authMessage'])
@pyqtSlot()
def authProceed(self):
@ -44,7 +40,6 @@ class AuthMixin:
raise e
finally:
delattr(self,'__auth_fcall')
self.auth_message = ''
@pyqtSlot()
def authCancel(self):
@ -64,4 +59,3 @@ class AuthMixin:
raise e
finally:
delattr(self, '__auth_fcall')
self.auth_message = ''

26
electrum/gui/qml/components/main.qml

@ -372,7 +372,7 @@ ApplicationWindow
id: _swaphelper
wallet: Daemon.currentWallet
onAuthRequired: {
app.handleAuthRequired(_swaphelper, method)
app.handleAuthRequired(_swaphelper, method, authMessage)
}
onError: {
var dialog = app.messageDialog.createObject(app, { text: message })
@ -477,8 +477,8 @@ ApplicationWindow
var dialog = app.messageDialog.createObject(app, {'text': error})
dialog.open()
}
function onAuthRequired(method) {
handleAuthRequired(Daemon, method)
function onAuthRequired(method, authMessage) {
handleAuthRequired(Daemon, method, authMessage)
}
function onLoadingChanged() {
if (!Daemon.loading)
@ -509,8 +509,8 @@ ApplicationWindow
Connections {
target: Daemon.currentWallet
function onAuthRequired(method) {
handleAuthRequired(Daemon.currentWallet, method)
function onAuthRequired(method, authMessage) {
handleAuthRequired(Daemon.currentWallet, method, authMessage)
}
// TODO: add to notification queue instead of barging through
function onPaymentSucceeded(key) {
@ -523,12 +523,12 @@ ApplicationWindow
Connections {
target: Config
function onAuthRequired(method) {
handleAuthRequired(Config, method)
function onAuthRequired(method, authMessage) {
handleAuthRequired(Config, method, authMessage)
}
}
function handleAuthRequired(qtobject, method) {
function handleAuthRequired(qtobject, method, authMessage) {
console.log('auth using method ' + method)
if (method == 'wallet') {
if (Daemon.currentWallet.verify_password('')) {
@ -551,12 +551,12 @@ ApplicationWindow
} else if (method == 'pin') {
if (Config.pinCode == '') {
// no PIN configured
handleAuthConfirmationOnly(qtobject)
handleAuthConfirmationOnly(qtobject, authMessage)
} else {
var dialog = app.pinDialog.createObject(app, {
mode: 'check',
pincode: Config.pinCode,
authMessage: qtobject.authMessage
authMessage: authMessage
})
dialog.accepted.connect(function() {
qtobject.authProceed()
@ -573,12 +573,12 @@ ApplicationWindow
}
}
function handleAuthConfirmationOnly(qtobject) {
if (!qtobject.authMessage) {
function handleAuthConfirmationOnly(qtobject, authMessage) {
if (!authMessage) {
qtobject.authProceed()
return
}
var dialog = app.messageDialog.createObject(app, {text: qtobject.authMessage, yesno: true})
var dialog = app.messageDialog.createObject(app, {text: authMessage, yesno: true})
dialog.accepted.connect(function() {
qtobject.authProceed()
})

3
electrum/gui/qml/qechannelopener.py

@ -165,7 +165,6 @@ class QEChannelOpener(QObject, AuthMixin):
node_id=self._node_pubkey,
fee_est=None)
self.auth_message = _('Open Lightning channel?')
acpt = lambda tx: self.do_open_channel(tx, self._connect_str_resolved, self._wallet.password)
self._finalizer = QETxFinalizer(self, make_tx=mktx, accept=acpt)
@ -174,7 +173,7 @@ class QEChannelOpener(QObject, AuthMixin):
self._finalizer.wallet = self._wallet
self.finalizerChanged.emit()
@auth_protect
@auth_protect(message=_('Open Lichtning channel?'))
def do_open_channel(self, funding_tx, conn_str, password):
"""
conn_str: a connection string that extract_nodeid can parse, i.e. cannot be a trampoline name

3
electrum/gui/qml/qedaemon.py

@ -261,10 +261,9 @@ class QEDaemon(AuthMixin, QObject):
self.walletDeleteError.emit('balance', _('There are still coins present in this wallet. Really delete?'))
return
self.auth_message = _('Really delete this wallet?')
self.delete_wallet(wallet)
@auth_protect
@auth_protect(message=_('Really delete this wallet?'))
def delete_wallet(self, wallet):
path = standardize_path(wallet.wallet.storage.path)
self._logger.debug('deleting wallet with path %s' % path)

1
electrum/gui/qml/qeinvoice.py

@ -383,7 +383,6 @@ class QEInvoice(QObject, QtEventListener):
# TODO: is update amount_msat for overrideAmount sufficient?
self._effectiveInvoice.amount_msat = self.amountOverride.satsInt * 1000
self._wallet.auth_message = _('Pay Lightning Invoice?')
self._wallet.pay_lightning_invoice(self._effectiveInvoice)
def get_max_spendable_onchain(self):

10
electrum/gui/qml/qeswaphelper.py

@ -423,17 +423,9 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
if not self._wallet.wallet.network:
self.error.emit(_("You are offline."))
return
if self.isReverse:
self.auth_message = _('Do you want to do a reverse submarine swap?')
else:
self.auth_message = _('Do you want to do a submarine swap? '
'You will need to wait for the swap transaction to confirm.'
)
self._do_execute_swap()
@auth_protect
@auth_protect(message=_('Confirm Lightning swap?'))
def _do_execute_swap(self):
if self.isReverse:
lightning_amount = self._send_amount

2
electrum/gui/qml/qewallet.py

@ -584,7 +584,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
def ln_auth_rejected(self):
self.paymentAuthRejected.emit()
@auth_protect(reject='ln_auth_rejected')
@auth_protect(message=_('Pay lightning invoice?'), reject='ln_auth_rejected')
def pay_lightning_invoice(self, invoice: 'QEInvoice'):
amount_msat = invoice.get_amount_msat()

Loading…
Cancel
Save