feat(onboarding / privacy): Integrate password validation (zxcvbn lib) in new password screens
Use new `PrivacyStore` method getPasswordStrengthScore and link it to the new password strength bar value. Used backend/general to call to `status-go` method and services/general to define the common `GetPasswordStrengthScore` service. Added onboarding chain to get password strength score information from `OnboardingStore` to `status-go` call. Closes #5096
This commit is contained in:
parent
d05bd6ce08
commit
8f996992b2
|
@ -185,7 +185,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
|||
result,
|
||||
statusFoundation.events,
|
||||
result.keychainService,
|
||||
result.accountsService
|
||||
result.accountsService,
|
||||
result.generalService
|
||||
)
|
||||
result.mainModule = main_module.newModule[AppController](
|
||||
result,
|
||||
|
@ -219,6 +220,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
|||
result.gifService,
|
||||
result.ensService,
|
||||
result.networkService,
|
||||
result.generalService
|
||||
)
|
||||
|
||||
# Do connections
|
||||
|
|
|
@ -49,6 +49,7 @@ import ../../../app_service/service/mailservers/service as mailservers_service
|
|||
import ../../../app_service/service/gif/service as gif_service
|
||||
import ../../../app_service/service/ens/service as ens_service
|
||||
import ../../../app_service/service/network/service as network_service
|
||||
import ../../../app_service/service/general/service as general_service
|
||||
|
||||
import ../../core/notifications/details
|
||||
import ../../core/eventemitter
|
||||
|
@ -108,6 +109,7 @@ proc newModule*[T](
|
|||
gifService: gif_service.Service,
|
||||
ensService: ens_service.Service,
|
||||
networkService: network_service.Service,
|
||||
generalService: general_service.Service
|
||||
): Module[T] =
|
||||
result = Module[T]()
|
||||
result.delegate = delegate
|
||||
|
@ -144,7 +146,7 @@ proc newModule*[T](
|
|||
result.profileSectionModule = profile_section_module.newModule(
|
||||
result, events, accountsService, settingsService, stickersService,
|
||||
profileService, contactsService, aboutService, languageService, privacyService, nodeConfigurationService,
|
||||
devicesService, mailserversService, chatService, ensService, walletAccountService,
|
||||
devicesService, mailserversService, chatService, ensService, walletAccountService, generalService
|
||||
)
|
||||
result.stickersModule = stickers_module.newModule(result, events, stickersService, settingsService, walletAccountService)
|
||||
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
|
||||
|
|
|
@ -17,6 +17,7 @@ import ../../../../app_service/service/chat/service as chat_service
|
|||
import ../../../../app_service/service/stickers/service as stickersService
|
||||
import ../../../../app_service/service/ens/service as ens_service
|
||||
import ../../../../app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../../../app_service/service/general/service as general_service
|
||||
|
||||
import ./profile/module as profile_module
|
||||
import ./contacts/module as contacts_module
|
||||
|
@ -66,6 +67,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
|
|||
chatService: chat_service.Service,
|
||||
ensService: ens_service.Service,
|
||||
walletAccountService: wallet_account_service.Service,
|
||||
generalService: general_service.Service
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
|
@ -77,7 +79,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
|
|||
result.profileModule = profile_module.newModule(result, profileService)
|
||||
result.contactsModule = contacts_module.newModule(result, events, contactsService)
|
||||
result.languageModule = language_module.newModule(result, languageService)
|
||||
result.privacyModule = privacy_module.newModule(result, events, settingsService, privacyService)
|
||||
result.privacyModule = privacy_module.newModule(result, events, settingsService, privacyService, generalService)
|
||||
result.aboutModule = about_module.newModule(result, events, aboutService)
|
||||
result.advancedModule = advanced_module.newModule(result, events, settingsService, stickersService, nodeConfigurationService)
|
||||
result.devicesModule = devices_module.newModule(result, events, settingsService, devicesService)
|
||||
|
|
|
@ -3,6 +3,7 @@ import io_interface
|
|||
import ../../../../core/eventemitter
|
||||
import ../../../../../app_service/service/settings/service as settings_service
|
||||
import ../../../../../app_service/service/privacy/service as privacy_service
|
||||
import ../../../../../app_service/service/general/service as general_service
|
||||
|
||||
|
||||
type
|
||||
|
@ -11,15 +12,19 @@ type
|
|||
events: EventEmitter
|
||||
settingsService: settings_service.Service
|
||||
privacyService: privacy_service.Service
|
||||
generalService: general_service.Service
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter,
|
||||
|
||||
settingsService: settings_service.Service,
|
||||
privacyService: privacy_service.Service): Controller =
|
||||
privacyService: privacy_service.Service,
|
||||
generalService: general_service.Service): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.settingsService = settingsService
|
||||
result.privacyService = privacyService
|
||||
result.generalService = generalService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
@ -71,5 +76,5 @@ proc getProfilePicturesVisibility*(self: Controller): int =
|
|||
proc setProfilePicturesVisibility*(self: Controller, value: int): bool =
|
||||
self.settingsService.saveProfilePicturesVisibility(value)
|
||||
|
||||
proc getPasswordStrengthScore*(self: Controller, password: string): int =
|
||||
return self.privacyService.getPasswordStrengthScore(password)
|
||||
method getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
||||
return self.generalService.getPasswordStrengthScore(password, userName)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import NimQml, chronicles
|
||||
|
||||
import ../../../../../app/global/global_singleton
|
||||
|
||||
import ./io_interface, ./view, ./controller
|
||||
import ../io_interface as delegate_interface
|
||||
|
||||
import ../../../../core/eventemitter
|
||||
import ../../../../../app_service/service/settings/service as settings_service
|
||||
import ../../../../../app_service/service/privacy/service as privacy_service
|
||||
import ../../../../../app_service/service/general/service as general_service
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -19,13 +22,14 @@ type
|
|||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter,
|
||||
settingsService: settings_service.Service,
|
||||
privacyService: privacy_service.Service):
|
||||
privacyService: privacy_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, settingsService, privacyService)
|
||||
result.controller = controller.newController(result, events, settingsService, privacyService, generalService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
|
@ -94,4 +98,4 @@ method setProfilePicturesVisibility*(self: Module, value: int) =
|
|||
self.view.profilePicturesVisibilityChanged()
|
||||
|
||||
method getPasswordStrengthScore*(self: Module, password: string): int =
|
||||
return self.controller.getPasswordStrengthScore(password)
|
||||
return self.controller.getPasswordStrengthScore(password, singletonInstance.userProfile.getUsername())
|
||||
|
|
|
@ -9,6 +9,7 @@ import login/module as login_module
|
|||
|
||||
import ../../../app_service/service/keychain/service as keychain_service
|
||||
import ../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../app_service/service/general/service as general_service
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -24,7 +25,8 @@ type
|
|||
proc newModule*[T](delegate: T,
|
||||
events: EventEmitter,
|
||||
keychainService: keychain_service.Service,
|
||||
accountsService: accounts_service.Service):
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Module[T] =
|
||||
result = Module[T]()
|
||||
result.delegate = delegate
|
||||
|
@ -33,7 +35,7 @@ proc newModule*[T](delegate: T,
|
|||
result.controller = controller.newController(result, events, accountsService)
|
||||
|
||||
# Submodules
|
||||
result.onboardingModule = onboarding_module.newModule(result, events, accountsService)
|
||||
result.onboardingModule = onboarding_module.newModule(result, events, accountsService, generalService)
|
||||
result.loginModule = login_module.newModule(result, events, keychainService,
|
||||
accountsService)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import io_interface
|
|||
import ../../../core/signals/types
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../app_service/service/general/service as general_service
|
||||
|
||||
logScope:
|
||||
topics = "onboarding-controller"
|
||||
|
@ -14,17 +15,20 @@ type
|
|||
delegate: io_interface.AccessInterface
|
||||
events: EventEmitter
|
||||
accountsService: accounts_service.Service
|
||||
generalService: general_service.Service
|
||||
selectedAccountId: string
|
||||
displayName: string
|
||||
|
||||
proc newController*(delegate: io_interface.AccessInterface,
|
||||
events: EventEmitter,
|
||||
accountsService: accounts_service.Service):
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
result.events = events
|
||||
result.accountsService = accountsService
|
||||
result.generalService = generalService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
@ -62,3 +66,6 @@ proc importMnemonic*(self: Controller, mnemonic: string) =
|
|||
else:
|
||||
self.delegate.importAccountError()
|
||||
|
||||
method getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
||||
return self.generalService.getPasswordStrengthScore(password, userName)
|
||||
|
||||
|
|
|
@ -43,3 +43,6 @@ method importMnemonic*(self: AccessInterface, mnemonic: string) {.base.} =
|
|||
|
||||
method setDisplayName*(self: AccessInterface, displayName: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -5,6 +5,7 @@ import view, controller, item
|
|||
import ../../../global/global_singleton
|
||||
import ../../../core/eventemitter
|
||||
import ../../../../app_service/service/accounts/service as accounts_service
|
||||
import ../../../../app_service/service/general/service as general_service
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -17,13 +18,14 @@ type
|
|||
moduleLoaded: bool
|
||||
|
||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter,
|
||||
accountsService: accounts_service.Service):
|
||||
accountsService: accounts_service.Service,
|
||||
generalService: general_service.Service):
|
||||
Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
result.view = view.newView(result)
|
||||
result.viewVariant = newQVariant(result.view)
|
||||
result.controller = controller.newController(result, events, accountsService)
|
||||
result.controller = controller.newController(result, events, accountsService, generalService)
|
||||
result.moduleLoaded = false
|
||||
|
||||
method delete*(self: Module) =
|
||||
|
@ -76,3 +78,6 @@ method importAccountError*(self: Module) =
|
|||
|
||||
method importAccountSuccess*(self: Module) =
|
||||
self.view.importAccountSuccess()
|
||||
|
||||
method getPasswordStrengthScore*(self: Module, password, userName: string): int =
|
||||
return self.controller.getPasswordStrengthScore(password, userName)
|
||||
|
|
|
@ -89,3 +89,6 @@ QtObject:
|
|||
|
||||
proc importAccountSuccess*(self: View) =
|
||||
self.importedAccountChanged()
|
||||
|
||||
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
|
||||
return self.delegate.getPasswordStrengthScore(password, userName)
|
||||
|
|
|
@ -39,3 +39,15 @@ proc startMessenger*(self: Service) =
|
|||
let errDesription = e.msg
|
||||
error "error: ", errDesription
|
||||
return
|
||||
|
||||
method getPasswordStrengthScore*(self: Service, password, userName: string): int =
|
||||
try:
|
||||
let response = status_general.getPasswordStrengthScore(password, @[userName])
|
||||
if(response.result.contains("error")):
|
||||
let errMsg = response.result["error"].getStr()
|
||||
error "error: ", methodName="getPasswordStrengthScore", errDesription = errMsg
|
||||
return
|
||||
|
||||
return response.result["score"].getInt()
|
||||
except Exception as e:
|
||||
error "error: ", methodName="getPasswordStrengthScore", errName = e.name, errDesription = e.msg
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import NimQml, json, strutils, chronicles
|
||||
|
||||
import ../../../app/global/global_singleton
|
||||
|
||||
import ../settings/service as settings_service
|
||||
import ../accounts/service as accounts_service
|
||||
|
@ -150,16 +149,3 @@ QtObject:
|
|||
except Exception as e:
|
||||
error "error: ", procName="validatePassword", errName = e.name, errDesription = e.msg
|
||||
return false
|
||||
|
||||
proc getPasswordStrengthScore*(self: Service, password: string): int =
|
||||
try:
|
||||
let userName = singletonInstance.userProfile.getUsername()
|
||||
let response = status_privacy.getPasswordStrength(password, @[userName])
|
||||
if(response.result.contains("error")):
|
||||
let errMsg = response.result["error"].getStr()
|
||||
error "error: ", procName="getPasswordStrengthScore", errDesription = errMsg
|
||||
return
|
||||
|
||||
return response.result["Score"].getInt()
|
||||
except Exception as e:
|
||||
error "error: ", procName="getPasswordStrengthScore", errName = e.name, errDesription = e.msg
|
|
@ -45,3 +45,12 @@ proc dropPeerByID*(peer: string): RpcResponse[JsonNode] {.raises: [Exception].}
|
|||
proc removePeer*(peer: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* [peer]
|
||||
result = core.callPrivateRPC("admin_removePeer", payload)
|
||||
|
||||
proc getPasswordStrengthScore*(password: string, userInputs: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let params = %* {"password": password, "userInputs": userInputs}
|
||||
try:
|
||||
let response = status_go.getPasswordStrengthScore($(params))
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
except RpcException as e:
|
||||
error "error", methodName = "getPasswordStrengthScore", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
||||
|
|
|
@ -23,12 +23,3 @@ proc changeDatabasePassword*(keyUID: string, password: string, newPassword: stri
|
|||
proc getLinkPreviewWhitelist*(): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let payload = %* []
|
||||
result = callPrivateRPC("getLinkPreviewWhitelist".prefix, payload)
|
||||
|
||||
proc getPasswordStrength*(password: string, userInputs: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let params = %* {"password": password, "userInputs": userInputs}
|
||||
try:
|
||||
let response = status_go.getPasswordStrength($(params))
|
||||
result.result = Json.decode(response, JsonNode)
|
||||
except RpcException as e:
|
||||
error "error", methodName = "getPasswordStrength", exception=e.msg
|
||||
raise newException(RpcException, e.msg)
|
|
@ -246,6 +246,7 @@ QtObject {
|
|||
property var createPasswordComponent: Component {
|
||||
id: createPassword
|
||||
CreatePasswordView {
|
||||
store: OnboardingStore
|
||||
newPassword: d.newPassword
|
||||
confirmationPassword: d.confirmationPassword
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ QtObject {
|
|||
onBoardingModul.setSelectedAccountByIndex(selectedAccountIdx)
|
||||
}
|
||||
|
||||
function getPasswordStrengthScore(password) {
|
||||
let userName = onBoardingModul.importedAccountAlias
|
||||
return onBoardingModul.getPasswordStrengthScore(password, userName)
|
||||
}
|
||||
|
||||
property ListModel accountsSampleData: ListModel {
|
||||
ListElement {
|
||||
username: "Ferocious Herringbone Sinewave2"
|
||||
|
|
|
@ -77,7 +77,7 @@ Page {
|
|||
qsTr("Connecting...") :
|
||||
qsTr("Confirm you password (again)")
|
||||
textField.echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
textField.validator: RegExpValidator { regExp: /^[!-~]+$/ } // That incudes NOT extended ASCII printable characters less space
|
||||
textField.validator: RegExpValidator { regExp: /^[!-~]{0,64}$/ } // That incudes NOT extended ASCII printable characters less space and a maximum of 64 characters allowed
|
||||
keepHeight: true
|
||||
textField.rightPadding: showHideCurrentIcon.width + showHideCurrentIcon.anchors.rightMargin + Style.current.padding / 2
|
||||
|
||||
|
|
|
@ -12,22 +12,24 @@ import "../../Profile/views"
|
|||
Page {
|
||||
id: root
|
||||
|
||||
property var store
|
||||
property string newPassword
|
||||
property string confirmationPassword
|
||||
|
||||
signal passwordCreated(string newPassword, string confirmationPassword)
|
||||
signal backClicked()
|
||||
|
||||
Component.onCompleted: { view.forceNewPswInputFocus() }
|
||||
anchors.fill: parent
|
||||
background: null
|
||||
|
||||
Component.onCompleted: { view.forceNewPswInputFocus() }
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
readonly property int zBehind: 1
|
||||
readonly property int zFront: 100
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Column {
|
||||
spacing: 4 * Style.current.padding
|
||||
anchors.centerIn: parent
|
||||
|
@ -35,6 +37,7 @@ Page {
|
|||
|
||||
PasswordView {
|
||||
id: view
|
||||
store: root.store
|
||||
newPswText: root.newPassword
|
||||
confirmationPswText: root.confirmationPassword
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ StatusModal {
|
|||
|
||||
PasswordView {
|
||||
id: view
|
||||
store: root.privacyStore
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.centerIn: parent
|
||||
titleVisible: false
|
||||
|
|
|
@ -13,6 +13,7 @@ import StatusQ.Components 0.1
|
|||
Column {
|
||||
id: root
|
||||
|
||||
property var store
|
||||
property bool ready: newPswInput.text.length >= root.minPswLen && newPswInput.text === confirmPswInput.text && errorTxt.text === ""
|
||||
property int minPswLen: 6
|
||||
property bool createNewPsw: true
|
||||
|
@ -21,6 +22,7 @@ Column {
|
|||
property string introText: qsTr("Create a password to unlock Status on this device & sign transactions.")
|
||||
property string recoverText: qsTr("You will not be able to recover this password if it is lost.")
|
||||
property string strengthenText: qsTr("Minimum 6 characers. To strengthen your password consider including:")
|
||||
|
||||
readonly property int zBehind: 1
|
||||
readonly property int zFront: 100
|
||||
|
||||
|
@ -62,7 +64,7 @@ Column {
|
|||
property bool containsNumbers: false
|
||||
property bool containsSymbols: false
|
||||
|
||||
readonly property var validator: RegExpValidator { regExp: /^[!-~]+$/ } // That incudes NOT extended ASCII printable characters less space
|
||||
readonly property var validator: RegExpValidator { regExp: /^[!-~]{0,64}$/ } // That incudes NOT extended ASCII printable characters less space and a maximum of 64 characters allowed
|
||||
|
||||
// Password strength categorization / validation
|
||||
function lowerCaseValidator(text) { return (/[a-z]/.test(text)) }
|
||||
|
@ -70,46 +72,20 @@ Column {
|
|||
function numbersValidator(text) { return (/\d/.test(text)) }
|
||||
// That incudes NOT extended ASCII printable symbols less space:
|
||||
function symbolsValidator(text) { return (/[!-\/:-@[-`{-~]/.test(text)) }
|
||||
function findUniqueChars(text) {
|
||||
// The variable that contains the unique values
|
||||
let uniq = "";
|
||||
|
||||
for(let i = 0; i < text.length; i++) {
|
||||
// Checking if the uniq contains the character
|
||||
if(uniq.includes(text[i]) === false) {
|
||||
// If the character not present in uniq
|
||||
// Concatenate the character with uniq
|
||||
uniq += text[i]
|
||||
// Used to convert strength from a given score to a specific category
|
||||
function convertStrength(score) {
|
||||
var strength = StatusPasswordStrengthIndicator.Strength.None
|
||||
switch(score) {
|
||||
case 0: strength = StatusPasswordStrengthIndicator.Strength.VeryWeak; break
|
||||
case 1: strength = StatusPasswordStrengthIndicator.Strength.Weak; break
|
||||
case 2: strength = StatusPasswordStrengthIndicator.Strength.SoSo; break
|
||||
case 3: strength = StatusPasswordStrengthIndicator.Strength.Good; break
|
||||
case 4: strength = StatusPasswordStrengthIndicator.Strength.Great; break
|
||||
}
|
||||
}
|
||||
return uniq
|
||||
}
|
||||
|
||||
// Algorithm defined in functional requirements / Password categorization
|
||||
function getPswStrength() {
|
||||
let rules = 0
|
||||
let points = 0
|
||||
let strengthType = StatusPasswordStrengthIndicator.Strength.None
|
||||
|
||||
if(newPswInput.text.length >= root.minPswLen) { points += 10; rules++ }
|
||||
if(d.containsLower) { points += 5; rules++ }
|
||||
if(d.containsUpper) { points += 5; rules++ }
|
||||
if(d.containsNumbers) { points += 5; rules++ }
|
||||
if(d.containsSymbols) { points += 10; rules++ }
|
||||
|
||||
let uniq = d.findUniqueChars(newPswInput.text)
|
||||
if(uniq.length >= 5) { points += 5; rules++ }
|
||||
|
||||
// Update points according to rules used:
|
||||
points += rules * 10/*factor*/
|
||||
|
||||
// Strength decision taken:
|
||||
if(points > 0 && points < 40) strengthType = StatusPasswordStrengthIndicator.Strength.VeryWeak
|
||||
else if(points >= 40 && points < 60) strengthType = StatusPasswordStrengthIndicator.Strength.Weak
|
||||
else if(points >= 60 && points < 80) strengthType = StatusPasswordStrengthIndicator.Strength.SoSo
|
||||
else if(points >= 80 && points < 100) strengthType = StatusPasswordStrengthIndicator.Strength.Good
|
||||
else if(points >= 100) strengthType = StatusPasswordStrengthIndicator.Strength.Great
|
||||
return strengthType
|
||||
if(strength > 4)
|
||||
strength = StatusPasswordStrengthIndicator.Strength.Great
|
||||
return strength
|
||||
}
|
||||
|
||||
// Password validation / error message selection:
|
||||
|
@ -232,7 +208,7 @@ Column {
|
|||
d.containsSymbols = d.symbolsValidator(text)
|
||||
|
||||
// Update strength indicator:
|
||||
strengthInditactor.strength = d.getPswStrength()
|
||||
strengthInditactor.strength = d.convertStrength(root.store.getPasswordStrengthScore(newPswInput.text))
|
||||
}
|
||||
|
||||
StatusFlatRoundButton {
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ed2b3f656732e8670a21150559705933fdc8342a
|
||||
Subproject commit a46e18940fbd8dd338f248429d2e3b02dd387cc2
|
|
@ -1 +1 @@
|
|||
Subproject commit cbe2560d928603057a21adebd6f5540195247ee6
|
||||
Subproject commit e67592d556aa7320ee660ea64e2bfccfbc5166e9
|
Loading…
Reference in New Issue