qml: enforce use of existing password for wallet creation
When creating a new wallet in a Electrum instance with existing wallets this change forces the user to reuse a password of any existing wallet if `SimpleConfig.WALLET_USE_SINGLE_PASSWORD` is True. This prevents the amount of different passwords from increasing and guides the user towards a single wallet password (the intended default).
This commit is contained in:
@@ -5,37 +5,70 @@ import QtQuick.Controls.Material
|
||||
|
||||
import "../controls"
|
||||
|
||||
// We will only end up here if Daemon.singlePasswordEnabled == False.
|
||||
// If there are existing wallets, the user must reuse the password of one of them.
|
||||
// This way they are guided towards password unification.
|
||||
// NOTE: This also needs to be enforced when changing a wallets password.
|
||||
|
||||
WizardComponent {
|
||||
valid: password1.text === password2.text && password1.text.length >= 6
|
||||
id: root
|
||||
valid: isInputValid()
|
||||
property bool enforceExistingPassword: Config.walletShouldUseSinglePassword && Daemon.availableWallets.rowCount() > 0
|
||||
property bool passwordMatchesAnyExisting: false
|
||||
|
||||
function apply() {
|
||||
wizard_data['password'] = password1.text
|
||||
wizard_data['encrypt'] = password1.text != ''
|
||||
}
|
||||
|
||||
function isInputValid() {
|
||||
if (password1.text == "") {
|
||||
return false
|
||||
}
|
||||
if (enforceExistingPassword) {
|
||||
return passwordMatchesAnyExisting
|
||||
}
|
||||
return password1.text === password2.text && password1.text.length >= 6
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: passwordComparisonTimer
|
||||
interval: 500
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
root.passwordMatchesAnyExisting = Daemon.numWalletsWithPassword(password1.text) > 0
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: Daemon.singlePasswordEnabled
|
||||
? qsTr('Enter password')
|
||||
: qsTr('Enter password for %1').arg(wizard_data['wallet_name'])
|
||||
text: !enforceExistingPassword ? qsTr('Enter password') : qsTr('Enter existing password')
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
PasswordField {
|
||||
id: password1
|
||||
onTextChanged: {
|
||||
if (enforceExistingPassword) {
|
||||
root.passwordMatchesAnyExisting = false
|
||||
passwordComparisonTimer.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
text: qsTr('Enter password (again)')
|
||||
visible: !enforceExistingPassword
|
||||
}
|
||||
|
||||
PasswordField {
|
||||
id: password2
|
||||
showReveal: false
|
||||
echoMode: password1.echoMode
|
||||
visible: !enforceExistingPassword
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
@@ -44,7 +77,7 @@ WizardComponent {
|
||||
Layout.rightMargin: constants.paddingXLarge
|
||||
Layout.topMargin: constants.paddingXLarge
|
||||
|
||||
visible: password1.text != ''
|
||||
visible: password1.text != '' && !enforceExistingPassword
|
||||
|
||||
Label {
|
||||
Layout.rightMargin: constants.paddingLarge
|
||||
@@ -65,13 +98,30 @@ WizardComponent {
|
||||
InfoTextArea {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: qsTr('Passwords don\'t match')
|
||||
visible: password1.text != password2.text
|
||||
visible: (password1.text != password2.text) && !enforceExistingPassword
|
||||
iconStyle: InfoTextArea.IconStyle.Warn
|
||||
}
|
||||
InfoTextArea {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
text: qsTr('Password too short')
|
||||
visible: (password1.text == password2.text) && !valid
|
||||
visible: (password1.text == password2.text) && !valid && !enforceExistingPassword
|
||||
iconStyle: InfoTextArea.IconStyle.Warn
|
||||
}
|
||||
InfoTextArea {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.fillWidth: true
|
||||
visible: password1.text == "" && enforceExistingPassword
|
||||
text: [
|
||||
qsTr("Use the password of any existing wallet."),
|
||||
qsTr("Creating new wallets with different passwords is not supported.")
|
||||
].join("\n")
|
||||
iconStyle: InfoTextArea.IconStyle.Info
|
||||
}
|
||||
InfoTextArea {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.fillWidth: true
|
||||
visible: password1.text != "" && !valid && enforceExistingPassword
|
||||
text: qsTr('Password does not match any existing wallets password.')
|
||||
iconStyle: InfoTextArea.IconStyle.Warn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +331,15 @@ class QEConfig(AuthMixin, QObject):
|
||||
self._lnutxoreserve = QEAmount(amount_sat=self.config.LN_UTXO_RESERVE)
|
||||
return self._lnutxoreserve
|
||||
|
||||
walletShouldUseSinglePasswordChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, notify=walletShouldUseSinglePasswordChanged)
|
||||
def walletShouldUseSinglePassword(self):
|
||||
"""
|
||||
NOTE: this only indicates if we even want to use a single password, to check if we
|
||||
actually use a single password the daemon needs to be checked.
|
||||
"""
|
||||
return self.config.WALLET_SHOULD_USE_SINGLE_PASSWORD
|
||||
|
||||
@pyqtSlot('qint64', result=str)
|
||||
@pyqtSlot(QEAmount, result=str)
|
||||
def formatSatsForEditing(self, satoshis):
|
||||
|
||||
@@ -230,7 +230,7 @@ class QEDaemon(AuthMixin, QObject):
|
||||
if wallet is None:
|
||||
return
|
||||
|
||||
if self.daemon.config.WALLET_USE_SINGLE_PASSWORD:
|
||||
if self.daemon.config.WALLET_SHOULD_USE_SINGLE_PASSWORD:
|
||||
self._use_single_password = self.daemon.update_password_for_directory(old_password=local_password, new_password=local_password)
|
||||
self._password = local_password
|
||||
self.singlePasswordChanged.emit()
|
||||
@@ -318,9 +318,40 @@ class QEDaemon(AuthMixin, QObject):
|
||||
def fx(self):
|
||||
return self.qefx
|
||||
|
||||
@pyqtSlot(str, result=list)
|
||||
def getWalletsUnlockableWithPassword(self, password: str) -> list[str]:
|
||||
"""
|
||||
Returns any wallet that can be unlocked with the given password.
|
||||
Can be used as fallback to unlock another wallet the user entered a
|
||||
password that doesn't work for the current wallet but might work for another one.
|
||||
"""
|
||||
wallet_dir = os.path.dirname(self.daemon.config.get_wallet_path())
|
||||
_, _, wallet_paths_can_unlock = self.daemon.check_password_for_directory(
|
||||
old_password=password,
|
||||
new_password=None,
|
||||
wallet_dir=wallet_dir,
|
||||
)
|
||||
if not wallet_paths_can_unlock:
|
||||
return []
|
||||
self._logger.debug(f"getWalletsUnlockableWithPassword: can unlock {len(wallet_paths_can_unlock)} wallets")
|
||||
return [str(path) for path in wallet_paths_can_unlock]
|
||||
|
||||
@pyqtSlot(str, result=int)
|
||||
def numWalletsWithPassword(self, password: str) -> int:
|
||||
"""Returns the number of wallets that can be unlocked with the given password"""
|
||||
wallet_paths_can_unlock = self.getWalletsUnlockableWithPassword(password)
|
||||
return len(wallet_paths_can_unlock)
|
||||
|
||||
singlePasswordChanged = pyqtSignal()
|
||||
@pyqtProperty(bool, notify=singlePasswordChanged)
|
||||
def singlePasswordEnabled(self):
|
||||
"""
|
||||
singlePasswordEnabled is False if:
|
||||
a.) the user has no wallet (and password) yet
|
||||
b.) the user has wallets with different passwords (legacy)
|
||||
c.) all wallets are locked, we couldn't check yet if they all use the same password
|
||||
d.) we are on desktop where different passwords are allowed
|
||||
"""
|
||||
return self._use_single_password
|
||||
|
||||
@pyqtProperty(str, notify=singlePasswordChanged)
|
||||
|
||||
Reference in New Issue
Block a user