Browse Source

qt: 2fa implement OTP check

master
Sander van Grieken 2 years ago
parent
commit
fd28c66670
  1. 88
      electrum/plugins/trustedcoin/qt.py
  2. 13
      electrum/plugins/trustedcoin/qt_common.py

88
electrum/plugins/trustedcoin/qt.py

@ -25,26 +25,22 @@
from functools import partial
import threading
import sys
import os
from typing import TYPE_CHECKING
from PyQt5.QtGui import QPixmap
from PyQt5.QtCore import QObject, pyqtSignal, QTimer
from PyQt5.QtGui import QPixmap, QMovie, QColor
from PyQt5.QtCore import QObject, pyqtSignal, QSize, Qt
from PyQt5.QtWidgets import (QTextEdit, QVBoxLayout, QLabel, QGridLayout, QHBoxLayout,
QRadioButton, QCheckBox, QLineEdit, QPushButton, QWidget)
from electrum.i18n import _
from electrum import keystore
from electrum.bip32 import xpub_type
from electrum.plugin import hook
from electrum.util import is_valid_email
from electrum.logging import Logger, get_logger
from electrum.base_wizard import GoBack, UserCancelled
from .qt_common import QSignalObject
from electrum.gui.qt.util import (read_QIcon, WindowModalDialog, WaitingDialog, OkButton,
CancelButton, Buttons, icon_path, WWLabel, CloseButton, ChoicesLayout)
CancelButton, Buttons, icon_path, WWLabel, CloseButton, ChoicesLayout, ColorScheme)
from electrum.gui.qt.qrcodewidget import QRCodeWidget
from electrum.gui.qt.amountedit import AmountEdit
from electrum.gui.qt.main_window import StatusBarButton
@ -52,10 +48,8 @@ from electrum.gui.qt.installwizard import InstallWizard
from electrum.gui.qt.wizard.wallet import WCCreateSeed, WCConfirmSeed, WCHaveSeed, WCEnterExt, WCConfirmExt
from electrum.gui.qt.wizard.wizard import WizardComponent
# from .trustedcoin import TrustedCoinPlugin, server, DISCLAIMER, make_xpub, get_signing_xpub, get_user_id
from .trustedcoin import (TrustedCoinPlugin, server, ErrorConnectingServer,
DISCLAIMER, get_user_id, get_signing_xpub,
TrustedCoinException, make_xpub)
from .qt_common import QSignalObject
from .trustedcoin import TrustedCoinPlugin, server, DISCLAIMER
if TYPE_CHECKING:
@ -505,6 +499,7 @@ class WCShowConfirmOTP(WizardComponent):
def __init__(self, parent, wizard):
WizardComponent.__init__(self, parent, wizard, title=_('Authenticator secret'))
self.plugin = wizard.plugins.get_plugin('trustedcoin')
self._otp_verified = False
self.new_otp = QWidget()
new_otp_layout = QVBoxLayout()
@ -527,6 +522,18 @@ class WCShowConfirmOTP(WizardComponent):
self.authlabelnew = WWLabel(_('Then, enter your Google Authenticator code:'))
self.authlabelexist = WWLabel(_('Google Authenticator code:'))
self.spinner = QMovie(icon_path('spinner.gif'))
self.spinner.setScaledSize(QSize(24, 24))
self.spinner.setBackgroundColor(QColor('black'))
self.spinner_l = QLabel()
self.spinner_l.setMargin(5)
self.spinner_l.setVisible(False)
self.spinner_l.setMovie(self.spinner)
self.otp_status_l = QLabel()
self.otp_status_l.setAlignment(Qt.AlignHCenter)
self.otp_status_l.setVisible(False)
self.resetlabel = WWLabel(_('If you have lost your OTP secret, click the button below to request a new secret from the server.'))
self.button = QPushButton('Request OTP secret')
self.button.clicked.connect(self.on_request_otp)
@ -534,15 +541,18 @@ class WCShowConfirmOTP(WizardComponent):
hbox = QHBoxLayout()
hbox.addWidget(self.authlabelnew)
hbox.addWidget(self.authlabelexist)
pw = AmountEdit(None, is_int = True)
pw.setFocus(True)
pw.setMaximumWidth(150)
hbox.addWidget(pw)
# hbox.addStretch(1)
hbox.addStretch(1)
hbox.addWidget(self.spinner_l)
self.otp_e = AmountEdit(None, is_int=True)
self.otp_e.setFocus(True)
self.otp_e.setMaximumWidth(150)
self.otp_e.textEdited.connect(self.on_otp_edited)
hbox.addWidget(self.otp_e)
self.layout().addWidget(self.new_otp)
self.layout().addWidget(self.exist_otp)
self.layout().addLayout(hbox)
self.layout().addWidget(self.otp_status_l)
self.layout().addWidget(self.resetlabel)
self.layout().addWidget(self.button)
self.layout().addStretch(1)
@ -550,6 +560,10 @@ class WCShowConfirmOTP(WizardComponent):
def on_ready(self):
self.plugin.so.busyChanged.connect(self.on_busy_changed)
self.plugin.so.remoteKeyError.connect(self.on_remote_key_error)
self.plugin.so.otpSuccess.connect(self.on_otp_success)
self.plugin.so.otpError.connect(self.on_otp_error)
self.plugin.so.remoteKeyError.connect(self.on_remote_key_error)
self.plugin.so.createKeystore(self.wizard_data['2fa_email'])
def update(self):
@ -558,8 +572,8 @@ class WCShowConfirmOTP(WizardComponent):
self.exist_otp.setVisible(not is_new)
self.authlabelnew.setVisible(is_new)
self.authlabelexist.setVisible(not is_new)
self.resetlabel.setVisible(not is_new)
self.button.setVisible(not is_new)
self.resetlabel.setVisible(not is_new and not self._otp_verified)
self.button.setVisible(not is_new and not self._otp_verified)
if self.plugin.so.otpSecret:
self.secretlabel.setText(self.plugin.so.otpSecret)
@ -568,18 +582,50 @@ class WCShowConfirmOTP(WizardComponent):
self.qr.setData(uri)
def on_busy_changed(self):
self.busy = self.plugin.so.busy
if not self.busy:
self.update()
if not self.plugin.so._verifyingOtp:
self.busy = self.plugin.so.busy
if not self.busy:
self.update()
def on_remote_key_error(self, text):
self._logger.error(text)
self.error = text
def on_request_otp(self):
self.otp_status_l.setVisible(False)
self.plugin.so.resetOtpSecret()
self.update()
def on_otp_success(self):
self._otp_verified = True
self.otp_status_l.setText('Valid!')
self.otp_status_l.setVisible(True)
self.otp_status_l.setStyleSheet(ColorScheme.GREEN.as_stylesheet(False))
self.setEnabled(True)
self.spinner_l.setVisible(False)
self.spinner.stop()
self.valid = True
def on_otp_error(self, message):
self.otp_status_l.setText(message)
self.otp_status_l.setVisible(True)
self.otp_status_l.setStyleSheet(ColorScheme.RED.as_stylesheet(False))
self.setEnabled(True)
self.spinner_l.setVisible(False)
self.spinner.stop()
def on_otp_edited(self):
self.otp_status_l.setVisible(False)
text = self.otp_e.text()
if len(text) == 6:
# verify otp
self.plugin.so.checkOtp(self.plugin.so.shortId, int(text))
self.setEnabled(False)
self.spinner_l.setVisible(True)
self.spinner.start()
self.otp_e.setText('')
def apply(self):
pass

13
electrum/plugins/trustedcoin/qt_common.py

@ -13,7 +13,6 @@ from electrum.gui.qt_common.plugins import PluginQObject
class QSignalObject(PluginQObject):
canSignWithoutServerChanged = pyqtSignal()
_canSignWithoutServer = False
termsAndConditionsRetrieved = pyqtSignal([str], arguments=['message'])
termsAndConditionsError = pyqtSignal([str], arguments=['message'])
otpError = pyqtSignal([str], arguments=['message'])
@ -21,13 +20,9 @@ class QSignalObject(PluginQObject):
disclaimerChanged = pyqtSignal()
keystoreChanged = pyqtSignal()
otpSecretChanged = pyqtSignal()
_otpSecret = ''
shortIdChanged = pyqtSignal()
_shortId = ''
billingModelChanged = pyqtSignal()
_billingModel = []
_remoteKeyState = ''
remoteKeyStateChanged = pyqtSignal()
remoteKeyError = pyqtSignal([str], arguments=['message'])
@ -36,6 +31,12 @@ class QSignalObject(PluginQObject):
def __init__(self, plugin, wizard, parent):
super().__init__(plugin, parent)
self.wizard = wizard
self._canSignWithoutServer = False
self._otpSecret = ''
self._shortId = ''
self._billingModel = []
self._remoteKeyState = ''
self._verifyingOtp = False
@pyqtProperty(str, notify=disclaimerChanged)
def disclaimer(self):
@ -241,7 +242,9 @@ class QSignalObject(PluginQObject):
finally:
self._busy = False
self.busyChanged.emit()
self._verifyingOtp = False
self._verifyingOtp = True
self._busy = True
self.busyChanged.emit()
t = threading.Thread(target=check_otp_task, daemon=True)

Loading…
Cancel
Save