Browse Source

Merge pull request #8678 from accumulator/qml_password_strength

qml: introduce PasswordStrengthIndicator control, and add to Password…
master
ThomasV 2 years ago committed by GitHub
parent
commit
4a3a61c6b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      electrum/gui/qml/components/Constants.qml
  2. 22
      electrum/gui/qml/components/PasswordDialog.qml
  3. 44
      electrum/gui/qml/components/controls/PasswordStrengthIndicator.qml
  4. 38
      electrum/gui/qml/components/wizard/WCWalletPassword.qml
  5. 9
      electrum/gui/qml/qedaemon.py
  6. 21
      electrum/gui/qml/util.py

1
electrum/gui/qml/components/Constants.qml

@ -43,6 +43,7 @@ Item {
property color colorDone: '#ff80ff80' property color colorDone: '#ff80ff80'
property color colorValidBackground: '#ff008000' property color colorValidBackground: '#ff008000'
property color colorInvalidBackground: '#ff800000' property color colorInvalidBackground: '#ff800000'
property color colorAcceptable: '#ff8080ff'
property color colorLightningLocal: "#6060ff" property color colorLightningLocal: "#6060ff"
property color colorLightningLocalReserve: "#0000a0" property color colorLightningLocalReserve: "#0000a0"

22
electrum/gui/qml/components/PasswordDialog.qml

@ -62,7 +62,26 @@ ElDialog {
visible: confirmPassword visible: confirmPassword
showReveal: false showReveal: false
echoMode: pw_1.echoMode echoMode: pw_1.echoMode
enabled: pw_1.text.length >= 6 }
RowLayout {
Layout.fillWidth: true
Layout.rightMargin: constants.paddingXLarge
Layout.topMargin: constants.paddingLarge
Layout.bottomMargin: constants.paddingLarge
visible: confirmPassword
Label {
text: qsTr('Strength')
color: Material.accentColor
font.pixelSize: constants.fontSizeSmall
}
PasswordStrengthIndicator {
Layout.fillWidth: true
password: pw_1.text
}
} }
} }
@ -77,4 +96,5 @@ ElDialog {
} }
} }
} }
} }

44
electrum/gui/qml/components/controls/PasswordStrengthIndicator.qml

@ -0,0 +1,44 @@
import QtQuick 2.6
import QtQuick.Controls 2.1
import QtQuick.Controls.Material 2.0
Rectangle {
property string password
property int strength: 0
property color strengthColor
property string strengthText
onPasswordChanged: checkPasswordStrength(password)
function checkPasswordStrength() {
var _strength = Daemon.passwordStrength(password)
var map = {
0: [constants.colorError, qsTr('Weak')],
1: [constants.colorAcceptable, qsTr('Medium')],
2: [constants.colorDone, qsTr('Strong')],
3: [constants.colorDone, qsTr('Very Strong')]
}
strength = password.length ? _strength + 1 : 0
strengthText = password.length ? map[_strength][1] : ''
strengthColor = map[_strength][0]
}
height: strengthLabel.height
color: 'transparent'
border.color: Material.foreground
Rectangle {
id: strengthBar
x: 1
y: 1
width: (parent.width - 2) * strength / 4
height: parent.height - 2
color: strengthColor
Label {
id: strengthLabel
anchors.centerIn: parent
text: strengthText
color: strength <= 2 ? Material.foreground : '#004000'
}
}
}

38
electrum/gui/qml/components/wizard/WCWalletPassword.qml

@ -1,6 +1,7 @@
import QtQuick import QtQuick
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Controls.Material
import "../controls" import "../controls"
@ -13,7 +14,7 @@ WizardComponent {
} }
ColumnLayout { ColumnLayout {
width: parent.width anchors.fill: parent
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
@ -22,17 +23,50 @@ WizardComponent {
: qsTr('Enter password for %1').arg(wizard_data['wallet_name']) : qsTr('Enter password for %1').arg(wizard_data['wallet_name'])
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
PasswordField { PasswordField {
id: password1 id: password1
} }
Label { Label {
text: qsTr('Enter password (again)') text: qsTr('Enter password (again)')
} }
PasswordField { PasswordField {
id: password2 id: password2
showReveal: false showReveal: false
echoMode: password1.echoMode echoMode: password1.echoMode
enabled: password1.text.length >= 6 }
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: constants.paddingXLarge
Layout.rightMargin: constants.paddingXLarge
Layout.topMargin: constants.paddingXLarge
visible: password1.text != ''
Label {
Layout.rightMargin: constants.paddingLarge
text: qsTr('Strength')
}
PasswordStrengthIndicator {
Layout.fillWidth: true
password: password1.text
}
}
Item {
Layout.preferredWidth: 1
Layout.fillHeight: true
}
InfoTextArea {
Layout.alignment: Qt.AlignCenter
text: qsTr('Passwords don\'t match')
visible: password1.text != password2.text
iconStyle: InfoTextArea.IconStyle.Warn
} }
} }
} }

9
electrum/gui/qml/qedaemon.py

@ -27,6 +27,9 @@ if TYPE_CHECKING:
# wallet list model. supports both wallet basenames (wallet file basenames) # wallet list model. supports both wallet basenames (wallet file basenames)
# and whole Wallet instances (loaded wallets) # and whole Wallet instances (loaded wallets)
from .util import check_password_strength
class QEWalletListModel(QAbstractListModel): class QEWalletListModel(QAbstractListModel):
_logger = get_logger(__name__) _logger = get_logger(__name__)
@ -366,3 +369,9 @@ class QEDaemon(AuthMixin, QObject):
except Exception as e: except Exception as e:
verified = False verified = False
return verified return verified
@pyqtSlot(str, result=int)
def passwordStrength(self, password):
if len(password) == 0:
return 0
return check_password_strength(password)[0]

21
electrum/gui/qml/util.py

@ -1,18 +1,20 @@
import math
import re
import sys import sys
import queue import queue
from functools import wraps from functools import wraps
from time import time from time import time
from typing import Callable, Optional, NamedTuple from typing import Callable, Optional, NamedTuple, Tuple
from PyQt6.QtCore import pyqtSignal, QThread from PyQt6.QtCore import pyqtSignal, QThread
from electrum.i18n import _
from electrum.logging import Logger from electrum.logging import Logger
from electrum.util import EventListener, event_listener from electrum.util import EventListener, event_listener
class QtEventListener(EventListener): class QtEventListener(EventListener):
qt_callback_signal = pyqtSignal(tuple) qt_callback_signal = pyqtSignal(tuple)
def register_callbacks(self): def register_callbacks(self):
@ -58,6 +60,21 @@ def status_update_timer_interval(exp):
return interval return interval
# TODO: copied from qt password_dialog.py, move to common code
def check_password_strength(password: str) -> Tuple[int, str]:
"""Check the strength of the password entered by the user and return back the same
:param password: password entered by user in New Password
:return: password strength Weak or Medium or Strong"""
password = password
n = math.log(len(set(password)))
num = re.search("[0-9]", password) is not None and re.match("^[0-9]*$", password) is None
caps = password != password.upper() and password != password.lower()
extra = re.match("^[a-zA-Z0-9]*$", password) is None
score = len(password)*(n + caps + num + extra)/20
password_strength = {0: _('Weak'), 1: _('Medium'), 2: _('Strong'), 3: _('Very Strong')}
return min(3, int(score)), password_strength[min(3, int(score))]
# TODO: copied from desktop client, this could be moved to a set of common code. # TODO: copied from desktop client, this could be moved to a set of common code.
class TaskThread(QThread, Logger): class TaskThread(QThread, Logger):
"""Thread that runs background tasks. Callbacks are guaranteed """Thread that runs background tasks. Callbacks are guaranteed

Loading…
Cancel
Save