feat(@desktop/wallet): account interaction - stop using a keycard for profile keypair - handling on paired devices

Part 4 of: #11737
This commit is contained in:
Sale Djenic 2023-09-04 18:06:51 +02:00 committed by saledjenic
parent f11b2b1e86
commit b22b632b2d
21 changed files with 250 additions and 67 deletions

View File

@ -478,6 +478,8 @@ proc finishAppLoading*(self: AppController) =
self.startupModule.delete
self.startupModule = nil
self.mainModule.checkAndPerformProfileMigrationIfNeeded()
proc logout*(self: AppController) =
self.generalService.logout()

View File

@ -1,29 +1,29 @@
import chronicles, stint, tables
import ../../global/app_sections_config as conf
import ../../global/global_singleton
import ../../global/app_signals
import ../../core/signals/types as signal_types
import ../../core/eventemitter
import ../../core/notifications/notifications_manager
import ../../../app_service/common/types
import ../../../app_service/service/settings/service as settings_service
import ../../../app_service/service/node_configuration/service as node_configuration_service
import ../../../app_service/service/accounts/service as accounts_service
import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import ../../../app_service/service/contacts/service as contacts_service
import ../../../app_service/service/message/service as message_service
import ../../../app_service/service/gif/service as gif_service
import ../../../app_service/service/mailservers/service as mailservers_service
import ../../../app_service/service/privacy/service as privacy_service
import ../../../app_service/service/node/service as node_service
import ../../../app_service/service/community_tokens/service as community_tokens_service
import ../../../app_service/service/wallet_account/service as wallet_account_service
import ../../../app_service/service/token/service as token_service
import ../../../app_service/service/network/service as networks_service
import ../../../app_service/service/visual_identity/service as procs_from_visual_identity_service
import app/global/app_sections_config as conf
import app/global/global_singleton
import app/global/app_signals
import app/core/signals/types as signal_types
import app/core/eventemitter
import app/core/notifications/notifications_manager
import app_service/common/types
import app_service/service/settings/service as settings_service
import app_service/service/node_configuration/service as node_configuration_service
import app_service/service/accounts/service as accounts_service
import app_service/service/chat/service as chat_service
import app_service/service/community/service as community_service
import app_service/service/contacts/service as contacts_service
import app_service/service/message/service as message_service
import app_service/service/gif/service as gif_service
import app_service/service/mailservers/service as mailservers_service
import app_service/service/privacy/service as privacy_service
import app_service/service/node/service as node_service
import app_service/service/community_tokens/service as community_tokens_service
import app_service/service/wallet_account/service as wallet_account_service
import app_service/service/token/service as token_service
import app_service/service/network/service as networks_service
import app_service/service/visual_identity/service as procs_from_visual_identity_service
import ../../../app_service/service/community_tokens/community_collectible_owner
import app_service/service/community_tokens/community_collectible_owner
import io_interface
import ../shared_modules/keycard_popup/io_interface as keycard_shared_module
@ -31,8 +31,9 @@ import ../shared_modules/keycard_popup/io_interface as keycard_shared_module
logScope:
topics = "main-module-controller"
const UNIQUE_MAIN_MODULE_IDENTIFIER* = "MainModule"
const UNIQUE_MAIN_MODULE_AUTHENTICATE_KEYPAIR_IDENTIFIER* = "MainModule-AuthenticateKeypair"
const UNIQUE_MAIN_MODULE_KEYCARD_SYNC_IDENTIFIER* = "MainModule-KeycardSyncPurpose"
const UNIQUE_MAIN_MODULE_SHARED_KEYCARD_MODULE_IDENTIFIER* = "MainModule-SharedKeycardModule"
type
Controller* = ref object of RootObj
@ -418,10 +419,13 @@ proc init*(self: Controller) =
self.delegate.onSharedKeycarModuleKeycardSyncPurposeTerminated(args.lastStepInTheCurrentFlow)
self.events.emit(SIGNAL_SHARED_KEYCARD_MODULE_KEYCARD_SYNC_TERMINATED, Args())
return
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_IDENTIFIER or
if args.uniqueIdentifier == UNIQUE_MAIN_MODULE_SHARED_KEYCARD_MODULE_IDENTIFIER:
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
return
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_AUTHENTICATE_KEYPAIR_IDENTIFIER or
self.authenticateUserFlowRequestedBy.len == 0:
return
self.delegate.onSharedKeycarModuleFlowTerminated(args.lastStepInTheCurrentFlow)
self.delegate.onSharedKeycarModuleForAuthenticationTerminated(args.lastStepInTheCurrentFlow)
let data = SharedKeycarModuleArgs(uniqueIdentifier: self.authenticateUserFlowRequestedBy,
password: args.password,
pin: args.pin,
@ -435,10 +439,13 @@ proc init*(self: Controller) =
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_DISPLAY_POPUP) do(e: Args):
let args = SharedKeycarModuleBaseArgs(e)
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_IDENTIFIER or
if args.uniqueIdentifier == UNIQUE_MAIN_MODULE_SHARED_KEYCARD_MODULE_IDENTIFIER:
self.delegate.onDisplayKeycardSharedModuleFlow()
return
if args.uniqueIdentifier != UNIQUE_MAIN_MODULE_AUTHENTICATE_KEYPAIR_IDENTIFIER or
self.authenticateUserFlowRequestedBy.len == 0:
return
self.delegate.onDisplayKeycardSharedModuleFlow()
self.delegate.onDisplayKeycardSharedModuleForAuthentication()
self.events.on(SIGNAL_SHARED_KEYCARD_MODULE_AUTHENTICATE_USER) do(e: Args):
let args = SharedKeycarModuleAuthenticationArgs(e)
@ -449,6 +456,9 @@ proc init*(self: Controller) =
let args = SharedKeycarModuleArgs(e)
self.delegate.tryKeycardSync(args.keyUid, args.pin)
self.events.on(SIGNAL_PROFILE_MIGRATION_NEEDED_UPDATED) do(e: Args):
self.delegate.checkAndPerformProfileMigrationIfNeeded()
proc isConnected*(self: Controller): bool =
return self.nodeService.isConnected()
@ -551,4 +561,4 @@ proc asyncGetRevealedAccountsForAllMembers*(self: Controller, communityId: strin
self.communityService.asyncGetRevealedAccountsForAllMembers(communityId)
proc asyncReevaluateCommunityMembersPermissions*(self: Controller, communityId: string) =
self.communityService.asyncReevaluateCommunityMembersPermissions(communityId)
self.communityService.asyncReevaluateCommunityMembersPermissions(communityId)

View File

@ -261,6 +261,15 @@ method onStatusUrlRequested*(self: AccessInterface, action: StatusUrlAction, com
method getVerificationRequestFrom*(self: AccessInterface, publicKey: string): VerificationRequest {.base.} =
raise newException(ValueError, "No implementation available")
method getKeycardSharedModuleForAuthentication*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
method onDisplayKeycardSharedModuleForAuthentication*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onSharedKeycarModuleForAuthenticationTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method getKeycardSharedModule*(self: AccessInterface): QVariant {.base.} =
raise newException(ValueError, "No implementation available")
@ -273,6 +282,12 @@ method onSharedKeycarModuleFlowTerminated*(self: AccessInterface, lastStepInTheC
method runAuthenticationPopup*(self: AccessInterface, keyUid: string, bip44Paths: seq[string] = @[]) {.base.} =
raise newException(ValueError, "No implementation available")
method onSharedKeycarModuleRunningKeycardFlowsPurposeTerminated*(self: AccessInterface, lastStepInTheCurrentFlow: bool) {.base.} =
raise newException(ValueError, "No implementation available")
method checkAndPerformProfileMigrationIfNeeded*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onMyRequestAdded*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -103,8 +103,9 @@ type
communitiesModule: communities_module.AccessInterface
appSearchModule: app_search_module.AccessInterface
nodeSectionModule: node_section_module.AccessInterface
keycardSharedModule: keycard_shared_module.AccessInterface
keycardSharedModuleForAuthentication: keycard_shared_module.AccessInterface
keycardSharedModuleKeycardSyncPurpose: keycard_shared_module.AccessInterface
keycardSharedModule: keycard_shared_module.AccessInterface
networkConnectionModule: network_connection_module.AccessInterface
sharedUrlsModule: shared_urls_module.AccessInterface
moduleLoaded: bool
@ -233,10 +234,12 @@ method delete*[T](self: Module[T]) =
self.browserSectionModule.delete
self.appSearchModule.delete
self.nodeSectionModule.delete
if not self.keycardSharedModule.isNil:
self.keycardSharedModule.delete
if not self.keycardSharedModuleForAuthentication.isNil:
self.keycardSharedModuleForAuthentication.delete
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
self.keycardSharedModuleKeycardSyncPurpose.delete
if not self.keycardSharedModule.isNil:
self.keycardSharedModule.delete
self.networkConnectionModule.delete
self.sharedUrlsModule.delete
self.view.delete
@ -1284,30 +1287,40 @@ method onStatusUrlRequested*[T](self: Module[T], action: StatusUrlAction, commun
# self.setActiveSection(item)
# self.browserSectionModule.openUrl(url)
proc isSharedKeycardModuleFlowRunning[T](self: Module[T]): bool =
return not self.keycardSharedModule.isNil
################################################################################
## keycard shared module - authentication purpose
################################################################################
proc isSharedKeycardModuleForAuthenticationRunning[T](self: Module[T]): bool =
return not self.keycardSharedModuleForAuthentication.isNil
method getKeycardSharedModule*[T](self: Module[T]): QVariant =
if self.isSharedKeycardModuleFlowRunning():
return self.keycardSharedModule.getModuleAsVariant()
method getKeycardSharedModuleForAuthentication*[T](self: Module[T]): QVariant =
if self.isSharedKeycardModuleForAuthenticationRunning():
return self.keycardSharedModuleForAuthentication.getModuleAsVariant()
proc createSharedKeycardModule[T](self: Module[T]) =
self.keycardSharedModule = keycard_shared_module.newModule[Module[T]](self, UNIQUE_MAIN_MODULE_IDENTIFIER,
proc createSharedKeycardModuleForAuthentication[T](self: Module[T]) =
self.keycardSharedModuleForAuthentication = keycard_shared_module.newModule[Module[T]](self, UNIQUE_MAIN_MODULE_AUTHENTICATE_KEYPAIR_IDENTIFIER,
self.events, self.keycardService, self.settingsService, self.networkService, self.privacyService, self.accountsService,
self.walletAccountService, self.keychainService)
method onSharedKeycarModuleFlowTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
if self.isSharedKeycardModuleFlowRunning():
self.view.emitDestroyKeycardSharedModuleFlow()
self.keycardSharedModule.delete
self.keycardSharedModule = nil
method onSharedKeycarModuleForAuthenticationTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
if self.isSharedKeycardModuleForAuthenticationRunning():
self.view.emitDestroyKeycardSharedModuleForAuthentication()
self.keycardSharedModuleForAuthentication.delete
self.keycardSharedModuleForAuthentication = nil
method runAuthenticationPopup*[T](self: Module[T], keyUid: string, bip44Paths: seq[string] = @[]) =
self.createSharedKeycardModule()
if self.keycardSharedModule.isNil:
self.createSharedKeycardModuleForAuthentication()
if self.keycardSharedModuleForAuthentication.isNil:
return
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.Authentication, keyUid, bip44Paths)
self.keycardSharedModuleForAuthentication.runFlow(keycard_shared_module.FlowType.Authentication, keyUid, bip44Paths)
method onDisplayKeycardSharedModuleForAuthentication*[T](self: Module[T]) =
self.view.emitDisplayKeycardSharedModuleForAuthentication()
################################################################################
################################################################################
## keycard shared module - keycard syncing purpose
################################################################################
method onSharedKeycarModuleKeycardSyncPurposeTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
if not self.keycardSharedModuleKeycardSyncPurpose.isNil:
self.keycardSharedModuleKeycardSyncPurpose.delete
@ -1320,10 +1333,59 @@ method tryKeycardSync*[T](self: Module[T], keyUid: string, pin: string) =
if self.keycardSharedModuleKeycardSyncPurpose.isNil:
return
self.keycardSharedModuleKeycardSyncPurpose.syncKeycardBasedOnAppState(keyUid, pin)
################################################################################
################################################################################
## keycard shared module - general purpose
################################################################################
method getKeycardSharedModule*[T](self: Module[T]): QVariant =
if not self.keycardSharedModule.isNil:
return self.keycardSharedModule.getModuleAsVariant()
method onSharedKeycarModuleFlowTerminated*[T](self: Module[T], lastStepInTheCurrentFlow: bool) =
if not self.keycardSharedModule.isNil:
self.view.emitDestroyKeycardSharedModuleFlow()
self.keycardSharedModule.delete
self.keycardSharedModule = nil
method onDisplayKeycardSharedModuleFlow*[T](self: Module[T]) =
self.view.emitDisplayKeycardSharedModuleFlow()
proc runStopUsingKeycardForProfilePopup[T](self: Module[T]) =
if not self.keycardSharedModule.isNil:
info "shared keycard module is already running, cannot run stop using keycard flow"
return
self.keycardSharedModule = keycard_shared_module.newModule[Module[T]](self, UNIQUE_MAIN_MODULE_SHARED_KEYCARD_MODULE_IDENTIFIER,
self.events, self.keycardService, self.settingsService, self.networkService, self.privacyService, self.accountsService,
self.walletAccountService, self.keychainService)
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.MigrateFromKeycardToApp,
singletonInstance.userProfile.getKeyUid(), bip44Paths = @[], txHash = "", forceFlow = true)
################################################################################
method checkAndPerformProfileMigrationIfNeeded*[T](self: Module[T]) =
let migrationNeeded = self.settingsService.getProfileMigrationNeeded()
let profileKeypair = self.walletAccountService.getKeypairByKeyUid(singletonInstance.userProfile.getKeyUid())
if profileKeypair.isNil:
info "quit the app because of unresolved profile keypair"
quit() # quit the app
if not migrationNeeded:
if not self.keycardSharedModule.isNil:
let currentFlow = self.keycardSharedModule.getCurrentFlowType()
if currentFlow == FlowType.MigrateFromKeycardToApp:
self.keycardSharedModule.onCancelActionClicked()
return
if profileKeypair.keycards.len > 0:
if not self.keycardSharedModule.isNil:
let currentFlow = self.keycardSharedModule.getCurrentFlowType()
if currentFlow == FlowType.MigrateFromKeycardToApp:
return
self.keycardSharedModule.onCancelActionClicked()
info "run stop using keycard flow for the profile, cause profile was migrated on paired device"
self.runStopUsingKeycardForProfilePopup()
return
## TODO: we need to add a flow to handle migration to a Keycard
info "run migrate to a Keycard flow for the profile, cause profile was migrated on paired device"
method activateStatusDeepLink*[T](self: Module[T], statusDeepLink: string) =
let urlData = self.sharedUrlsModule.parseSharedUrl(statusDeepLink)
if urlData.community.communityId != "":

View File

@ -243,6 +243,17 @@ QtObject:
proc emitDisplayUserProfileSignal*(self: View, publicKey: string) =
self.displayUserProfile(publicKey)
proc getKeycardSharedModuleForAuthentication(self: View): QVariant {.slot.} =
let module = self.delegate.getKeycardSharedModuleForAuthentication()
if not module.isNil:
return module
return newQVariant()
QtProperty[QVariant] keycardSharedModuleForAuthentication:
read = getKeycardSharedModuleForAuthentication
proc activateStatusDeepLink*(self: View, statusDeepLink: string) {.slot.} =
self.delegate.activateStatusDeepLink(statusDeepLink)
proc getKeycardSharedModule(self: View): QVariant {.slot.} =
let module = self.delegate.getKeycardSharedModule()
if not module.isNil:
@ -251,8 +262,13 @@ QtObject:
QtProperty[QVariant] keycardSharedModule:
read = getKeycardSharedModule
proc activateStatusDeepLink*(self: View, statusDeepLink: string) {.slot.} =
self.delegate.activateStatusDeepLink(statusDeepLink)
proc displayKeycardSharedModuleForAuthentication*(self: View) {.signal.}
proc emitDisplayKeycardSharedModuleForAuthentication*(self: View) =
self.displayKeycardSharedModuleForAuthentication()
proc destroyKeycardSharedModuleForAuthentication*(self: View) {.signal.}
proc emitDestroyKeycardSharedModuleForAuthentication*(self: View) =
self.destroyKeycardSharedModuleForAuthentication()
proc displayKeycardSharedModuleFlow*(self: View) {.signal.}
proc emitDisplayKeycardSharedModuleFlow*(self: View) =

View File

@ -231,6 +231,11 @@ method load*(self: Module) =
if not args.success:
return
self.notifyFilterChanged()
self.events.on(SIGNAL_ALL_KEYCARDS_DELETED) do(e: Args):
let args = KeycardArgs(e)
if not args.success:
return
self.notifyFilterChanged()
self.events.on(SIGNAL_WALLET_ACCOUNT_POSITION_UPDATED) do(e:Args):
self.notifyFilterChanged()
self.events.on(SIGNAL_INCLUDE_WATCH_ONLY_ACCOUNTS_UPDATED) do(e: Args):

View File

@ -12,6 +12,10 @@ method executeCancelCommand*(self: CreatePasswordState, controller: Controller)
if self.flowType == FlowType.MigrateFromKeycardToApp:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
method executePreBackStateCommand*(self: CreatePasswordState, controller: Controller) =
if self.flowType == FlowType.MigrateFromKeycardToApp:
controller.setPassword("")
method getNextPrimaryState*(self: CreatePasswordState, controller: Controller): State =
if self.flowType == FlowType.MigrateFromKeycardToApp:
return createState(StateType.ConfirmPassword, self.flowType, self)

View File

@ -88,7 +88,8 @@ method getNextPrimaryState*(self: EnterSeedPhraseState, controller: Controller):
return
return createState(StateType.CreatePassword, self.flowType, self)
method getNextSecondaryState*(self: EnterSeedPhraseState, controller: Controller): State =
method getNextTertiaryState*(self: EnterSeedPhraseState, controller: Controller): State =
## Tertiary action is called after each async action during migration process.
if self.flowType == FlowType.MigrateFromKeycardToApp:
let migratingProfile = controller.getKeyPairForProcessing().getKeyUid() == singletonInstance.userProfile.getKeyUid()
if migratingProfile:

View File

@ -17,7 +17,8 @@ method executePrePrimaryStateCommand*(self: MigratingKeypairToAppState, controll
let migratingProfile = controller.getKeyPairForProcessing().getKeyUid() == singletonInstance.userProfile.getKeyUid()
if migratingProfile:
let newPassword = controller.getNewPassword()
controller.tryToStoreDataToKeychain(newPassword)
if singletonInstance.localAccountSettings.getStoreToKeychainValue() == LS_VALUE_STORE:
controller.tryToStoreDataToKeychain(newPassword)
controller.convertKeycardProfileKeypairToRegular(sp, password, newPassword)
return
controller.migrateNonProfileKeycardKeypairToApp(kpForProcessing.getKeyUid(), sp, password,
@ -26,7 +27,11 @@ method executePrePrimaryStateCommand*(self: MigratingKeypairToAppState, controll
method executePreTertiaryStateCommand*(self: MigratingKeypairToAppState, controller: Controller) =
## Tertiary action is called after each async action during migration process.
if self.flowType == FlowType.MigrateFromKeycardToApp:
self.migrationOk = controller.getConvertingProfileSuccess()
let migratingProfile = controller.getKeyPairForProcessing().getKeyUid() == singletonInstance.userProfile.getKeyUid()
if migratingProfile:
self.migrationOk = controller.getConvertingProfileSuccess()
return
self.migrationOk = controller.getAddingMigratedKeypairSuccess()
method getNextTertiaryState*(self: MigratingKeypairToAppState, controller: Controller): State =
if self.flowType == FlowType.MigrateFromKeycardToApp:

View File

@ -57,7 +57,8 @@ method getNextPrimaryState*(self: WrongSeedPhraseState, controller: Controller):
return createState(StateType.CreatePin, self.flowType, nil)
return self
method getNextSecondaryState*(self: WrongSeedPhraseState, controller: Controller): State =
method getNextTertiaryState*(self: WrongSeedPhraseState, controller: Controller): State =
## Tertiary action is called after each async action during migration process.
if self.flowType == FlowType.MigrateFromKeycardToApp:
let migratingProfile = controller.getKeyPairForProcessing().getKeyUid() == singletonInstance.userProfile.getKeyUid()
if migratingProfile:

View File

@ -120,7 +120,7 @@ method onCancelActionClicked*(self: AccessInterface) {.base.} =
method onKeycardResponse*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} =
raise newException(ValueError, "No implementation available")
method runFlow*(self: AccessInterface, flowToRun: FlowType, keyUid = "", bip44Paths: seq[string] = @[], txHash = "") {.base.} =
method runFlow*(self: AccessInterface, flowToRun: FlowType, keyUid = "", bip44Paths: seq[string] = @[], txHash = "", forceFlow = false) {.base.} =
raise newException(ValueError, "No implementation available")
method setPin*(self: AccessInterface, value: string) {.base.} =

View File

@ -460,7 +460,7 @@ method prepareKeyPairForProcessing*[T](self: Module[T], keyUid: string, keycardU
item.setIcon("keycard")
self.view.setKeyPairForProcessing(item)
method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths: seq[string] = @[], txHash = "") =
method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths: seq[string] = @[], txHash = "", forceFlow = false) =
## In case of `Authentication` if we're signing a transaction we need to provide a key uid of a keypair that an account
## we want to sign a transaction for belongs to. If we're just doing an authentication for a logged in user, then
## default key uid is always the key uid of the logged in user.
@ -469,6 +469,7 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths
error "sm_cannot run an general flow"
return
self.init()
self.view.setForceFlow(forceFlow)
if flowToRun == FlowType.FactoryReset:
if keyUid.len > 0:
self.prepareKeyPairForProcessing(keyUid)
@ -562,7 +563,7 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Paths
return
if flowToRun == FlowType.MigrateFromKeycardToApp:
if keyUid.len == 0:
error "sm_cannot run a migration from keycar to app flow without knowing in advance a keypair being migrated"
error "sm_cannot run a migration from keycard to app flow without knowing in advance a keypair being migrated"
self.controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
return
self.prepareKeyPairForProcessing(keyUid)

View File

@ -7,6 +7,7 @@ QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
forceFlow: bool # used to disable any possibility of closing the popup, the user is forced to complete the flow or forcibly close the app (no other way to close the popup)
disablePopup: bool # used to disable popup after each action, to block users do multiple clikcs which the action is ongoing
currentState: StateWrapper
currentStateVariant: QVariant
@ -44,6 +45,7 @@ QtObject:
result.currentState = newStateWrapper()
result.currentStateVariant = newQVariant(result.currentState)
result.remainingAttempts = -1
result.forceFlow = false
result.disablePopup = false
signalConnect(result.currentState, "backActionClicked()", result, "onBackActionClicked()", 2)
@ -52,6 +54,16 @@ QtObject:
signalConnect(result.currentState, "secondaryActionClicked()", result, "onSecondaryActionClicked()", 2)
signalConnect(result.currentState, "tertiaryActionClicked()", result, "onTertiaryActionClicked()", 2)
proc forceFlowChanged*(self: View) {.signal.}
proc getForceFlow*(self: View): bool {.slot.} =
return self.forceFlow
QtProperty[bool] forceFlow:
read = getForceFlow
notify = forceFlowChanged
proc setForceFlow*(self: View, value: bool) =
self.forceFlow = value
self.forceFlowChanged()
proc diablePopupChanged*(self: View) {.signal.}
proc getDisablePopup*(self: View): bool {.slot.} =
return self.disablePopup

View File

@ -45,6 +45,7 @@ const KEY_DISPLAY_NAME* = "display-name"
const KEY_BIO* = "bio"
const KEY_TEST_NETWORKS_ENABLED* = "test-networks-enabled?"
const INCLUDE_WATCH_ONLY_ACCOUNT* = "include-watch-only-account?"
const PROFILE_MIGRATION_NEEDED* = "profile-migration-needed"
# Notifications Settings Values
const VALUE_NOTIF_SEND_ALERTS* = "SendAlerts"
@ -137,6 +138,7 @@ type
notificationsVolume*: int
notificationsMessagePreview*: int
includeWatchOnlyAccount*: bool
profileMigrationNeeded*: bool
proc toPinnedMailserver*(jsonObj: JsonNode): PinnedMailserver =
# we maintain pinned mailserver per fleet
@ -191,6 +193,7 @@ proc toSettingsDto*(jsonObj: JsonNode): SettingsDto =
discard jsonObj.getProp(KEY_GIF_FAVORITES, result.gifFavorites)
discard jsonObj.getProp(KEY_TEST_NETWORKS_ENABLED, result.testNetworksEnabled)
discard jsonObj.getProp(INCLUDE_WATCH_ONLY_ACCOUNT, result.includeWatchOnlyAccount)
discard jsonObj.getProp(PROFILE_MIGRATION_NEEDED, result.profileMigrationNeeded)
var pinnedMailserverObj: JsonNode
if(jsonObj.getProp(KEY_PINNED_MAILSERVERS, pinnedMailserverObj)):

View File

@ -28,6 +28,7 @@ const SIGNAL_MNEMONIC_REMOVED* = "mnemonicRemoved"
const SIGNAL_SOCIAL_LINKS_UPDATED* = "socialLinksUpdated"
const SIGNAL_CURRENT_USER_STATUS_UPDATED* = "currentUserStatusUpdated"
const SIGNAL_INCLUDE_WATCH_ONLY_ACCOUNTS_UPDATED* = "includeWatchOnlyAccounts"
const SIGNAL_PROFILE_MIGRATION_NEEDED_UPDATED* = "profileMigrationNeededUpdated"
logScope:
topics = "settings-service"
@ -116,6 +117,9 @@ QtObject:
if settingsField.name == INCLUDE_WATCH_ONLY_ACCOUNT:
self.settings.includeWatchOnlyAccount = settingsField.value.getBool
self.events.emit(SIGNAL_INCLUDE_WATCH_ONLY_ACCOUNTS_UPDATED, Args())
if settingsField.name == PROFILE_MIGRATION_NEEDED:
self.settings.profileMigrationNeeded = settingsField.value.getBool
self.events.emit(SIGNAL_PROFILE_MIGRATION_NEEDED_UPDATED, SettingsBoolValueArgs(value: self.settings.profileMigrationNeeded))
if receivedData.socialLinksInfo.links.len > 0 or
receivedData.socialLinksInfo.removed:
@ -978,3 +982,6 @@ QtObject:
if(self.saveSetting(INCLUDE_WATCH_ONLY_ACCOUNT, newValue)):
self.settings.includeWatchOnlyAccount = newValue
self.events.emit(SIGNAL_INCLUDE_WATCH_ONLY_ACCOUNTS_UPDATED, SettingsBoolValueArgs(value: newValue))
proc getProfileMigrationNeeded*(self: Service): bool =
self.settings.profileMigrationNeeded

View File

@ -387,10 +387,6 @@ proc makePrivateKeyKeypairFullyOperable*(self: Service, keyUid, privateKey, pass
## Mandatory fields for all accounts: `address`, `keyUid`, `walletType`, `path`, `publicKey`, `name`, `emoji`, `colorId`
proc addNewSeedPhraseKeypair*(self: Service, seedPhrase, password: string, doPasswordHashing: bool,
keyUid, keypairName, rootWalletMasterKey: string, accounts: seq[WalletAccountDto]): string =
if password.len == 0:
let err = "for adding a new seed phrase keypair, password must be provided"
error "error", err
return err
var finalPassword = password
if password.len > 0 and doPasswordHashing:
finalPassword = utils.hashPassword(password)

View File

@ -71,6 +71,14 @@ Item {
popups.openProfilePopup(publicKey)
}
function onDisplayKeycardSharedModuleForAuthentication() {
keycardPopupForAuthentication.active = true
}
function onDestroyKeycardSharedModuleForAuthentication() {
keycardPopupForAuthentication.active = false
}
function onDisplayKeycardSharedModuleFlow() {
keycardPopup.active = true
}
@ -1558,6 +1566,18 @@ Item {
Global.settingsLoaded()
}
Loader {
id: keycardPopupForAuthentication
active: false
sourceComponent: KeycardPopup {
sharedKeycardModule: appMain.rootStore.mainModuleInst.keycardSharedModuleForAuthentication
}
onLoaded: {
keycardPopupForAuthentication.item.open()
}
}
Loader {
id: keycardPopup
active: false

View File

@ -13,8 +13,10 @@ StatusModal {
property var emojiPopup
width: Constants.keycard.general.popupWidth
closePolicy: d.disableActionPopupButtons || d.disableCloseButton? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
hasCloseButton: !d.disableActionPopupButtons && !d.disableCloseButton
closePolicy: root.sharedKeycardModule.forceFlow || d.disableActionPopupButtons || d.disableCloseButton?
Popup.NoAutoClose :
Popup.CloseOnEscape | Popup.CloseOnPressOutside
hasCloseButton: !root.sharedKeycardModule.forceFlow && !d.disableActionPopupButtons && !d.disableCloseButton
headerSettings.title: {
switch (root.sharedKeycardModule.currentState.flowType) {
@ -45,6 +47,10 @@ StatusModal {
case Constants.keycardSharedFlow.createCopyOfAKeycard:
return qsTr("Create a backup copy of this Keycard")
case Constants.keycardSharedFlow.migrateFromKeycardToApp:
// in the context of `migrateFromKeycardToApp` flow, `forceFlow` is set to `true` on paired devices
if (root.sharedKeycardModule.forceFlow) {
return qsTr("Enable password login on this device")
}
return qsTr("Migrate a keypair from Keycard to Status")
}

View File

@ -375,14 +375,14 @@ QtObject {
case Constants.keycardSharedState.wrongSeedPhrase:
case Constants.keycardSharedState.createPassword:
case Constants.keycardSharedState.confirmPassword:
return true
return !root.sharedKeycardModule.forceFlow
}
break
}
return false
}
enabled: !root.disableActionPopupButtons
enabled: !root.sharedKeycardModule.forceFlow && !root.disableActionPopupButtons
onClicked: {
root.cancelBtnClicked();
}

View File

@ -1371,6 +1371,11 @@ Item {
target: title
text: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migrateKeypairToApp) {
if (root.sharedKeycardModule.keyPairForProcessing.pairType === Constants.keycard.keyPairType.profile) {
if (root.sharedKeycardModule.forceFlow) {
return qsTr("Your profile keypair has been\nmigrated from Keycard to Status")
}
}
return qsTr("Are you sure you want to migrate\nthis keypair to Status?")
}
return ""
@ -1388,11 +1393,23 @@ Item {
target: message
text: {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.migrateKeypairToApp) {
if (root.sharedKeycardModule.keyPairForProcessing.pairType === Constants.keycard.keyPairType.profile) {
if (root.sharedKeycardModule.forceFlow) {
return qsTr("In order to continue using this profile on this device, you need to enter the keypairs seed phrase and create a new password to log in with on this device.")
}
let t = qsTr("%1 is your default Status keypair.").arg(root.sharedKeycardModule.keyPairForProcessing.name)
t += qsTr(" Migrating this keypair will mean you will no longer require this Keycard to login to Status or")
t += qsTr(" transact with the keypairs derived account(s).", "", root.sharedKeycardModule.keyPairForProcessing.accounts.count)
t += qsTr(" The keypair and account(s) will be fully removed from Keycard and stored on device.", "", root.sharedKeycardModule.keyPairForProcessing.accounts.count)
return t
}
let t = qsTr("%1 keypair and its derived account(s) will be fully removed from Keycard and stored on device.",
"",
root.sharedKeycardModule.keyPairForProcessing.accounts.count)
.arg(root.sharedKeycardModule.keyPairForProcessing.name)
t += qsTr("This will make your keypair and derived account(s) less secure as you will no longer require this Keycard to transact.",
t += qsTr(" This will make your keypair and derived account(s) less secure as you will no longer require this Keycard to transact.",
"",
root.sharedKeycardModule.keyPairForProcessing.accounts.count)
return t

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit c0f32748b4927b5e3272e1e8e9cb6b226f002720
Subproject commit 83d13548458c60d14eba6efdd92a928f9f0050a1