Merge pull request #8678 from accumulator/qml_password_strength
qml: introduce PasswordStrengthIndicator control, and add to Password…
This commit is contained in:
@@ -43,6 +43,7 @@ Item {
|
||||
property color colorDone: '#ff80ff80'
|
||||
property color colorValidBackground: '#ff008000'
|
||||
property color colorInvalidBackground: '#ff800000'
|
||||
property color colorAcceptable: '#ff8080ff'
|
||||
|
||||
property color colorLightningLocal: "#6060ff"
|
||||
property color colorLightningLocalReserve: "#0000a0"
|
||||
|
||||
@@ -62,7 +62,26 @@ ElDialog {
|
||||
visible: confirmPassword
|
||||
showReveal: false
|
||||
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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Controls.Material
|
||||
|
||||
import "../controls"
|
||||
|
||||
@@ -13,7 +14,7 @@ WizardComponent {
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
@@ -22,17 +23,50 @@ WizardComponent {
|
||||
: qsTr('Enter password for %1').arg(wizard_data['wallet_name'])
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
PasswordField {
|
||||
id: password1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr('Enter password (again)')
|
||||
}
|
||||
|
||||
PasswordField {
|
||||
id: password2
|
||||
showReveal: false
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,9 @@ if TYPE_CHECKING:
|
||||
|
||||
# wallet list model. supports both wallet basenames (wallet file basenames)
|
||||
# and whole Wallet instances (loaded wallets)
|
||||
from .util import check_password_strength
|
||||
|
||||
|
||||
class QEWalletListModel(QAbstractListModel):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
@@ -366,3 +369,9 @@ class QEDaemon(AuthMixin, QObject):
|
||||
except Exception as e:
|
||||
verified = False
|
||||
return verified
|
||||
|
||||
@pyqtSlot(str, result=int)
|
||||
def passwordStrength(self, password):
|
||||
if len(password) == 0:
|
||||
return 0
|
||||
return check_password_strength(password)[0]
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import math
|
||||
import re
|
||||
import sys
|
||||
import queue
|
||||
|
||||
from functools import wraps
|
||||
from time import time
|
||||
from typing import Callable, Optional, NamedTuple
|
||||
from typing import Callable, Optional, NamedTuple, Tuple
|
||||
|
||||
from PyQt6.QtCore import pyqtSignal, QThread
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.logging import Logger
|
||||
from electrum.util import EventListener, event_listener
|
||||
|
||||
|
||||
class QtEventListener(EventListener):
|
||||
|
||||
qt_callback_signal = pyqtSignal(tuple)
|
||||
|
||||
def register_callbacks(self):
|
||||
@@ -58,6 +60,21 @@ def status_update_timer_interval(exp):
|
||||
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.
|
||||
class TaskThread(QThread, Logger):
|
||||
"""Thread that runs background tasks. Callbacks are guaranteed
|
||||
|
||||
Reference in New Issue
Block a user