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 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"
|
||||||
|
|||||||
@@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user