keychain feature partly refactored

This commit is contained in:
Sale Djenic 2021-10-16 21:03:01 +02:00 committed by Iuri Matias
parent 3e0d312fee
commit 08e7fef491
22 changed files with 254 additions and 75 deletions

View File

@ -113,8 +113,9 @@ proc newAppController*(appService: AppService): AppController =
result.startupModule = startup_module.newModule[AppController](result,
appService.status.events, appService.status.fleet, result.localSettingsService,
result.keychainService, result.accountsService)
result.mainModule = main_module.newModule[AppController](result, result.chatService,
result.communityService)
result.mainModule = main_module.newModule[AppController](result,
appService.status.events, result.localSettingsService, result.keychainService,
result.accountsService, result.chatService, result.communityService)
#################################################
# At the end of refactoring this will be moved to
@ -161,10 +162,14 @@ proc startupDidLoad*(self: AppController) =
singletonInstance.engine.load(newQUrl("qrc:///main.qml"))
#self.startupModule.offerToLoginUsingKeychain()
proc mainDidLoad*(self: AppController) =
self.appService.onLoggedIn()
self.startupModule.moveToAppState()
self.mainModule.checkForStoringPassword()
#################################################
# At the end of refactoring this will be moved to
# appropriate place or removed:

View File

@ -1,22 +1,40 @@
import Tables
import NimQml, Tables
import controller_interface
import io_interface
import ../../../app_service/service/local_settings/service as local_settings_service
import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/accounts/service_interface as accounts_service
import ../../../app_service/service/community/service as community_service
import eventemitter
import status/[signals]
export controller_interface
type
Controller* = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
events: EventEmitter
localSettingsService: local_settings_service.Service
keychainService: keychain_service.Service
accountsService: accounts_service.ServiceInterface
communityService: community_service.ServiceInterface
proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter,
localSettingsService: local_settings_service.Service,
keychainService: keychain_service.Service,
accountsService: accounts_service.ServiceInterface,
communityService: community_service.ServiceInterface):
Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.localSettingsService = localSettingsService
result.keychainService = keychainService
result.accountsService = accountsService
result.communityService = communityService
method delete*(self: Controller) =
@ -26,4 +44,33 @@ method init*(self: Controller) =
discard
method getCommunities*(self: Controller): seq[community_service.CommunityDto] =
return self.communityService.getCommunities()
return self.communityService.getCommunities()
method checkForStoringPassword*(self: Controller) =
# This method is called once user is logged in irrespective he is logged in
# through the onboarding or login view.
# This is MacOS only feature
if(not defined(macosx)):
return
let account = self.accountsService.getLoggedInAccount()
self.localSettingsService.updateAccountSettingsFilePath(account.name)
let value = self.localSettingsService.getAccountValue(
LS_KEY_STORE_TO_KEYCHAIN).stringVal
if (value == LS_VALUE_STORE or value == LS_VALUE_NEVER):
return
# We are here if stored "storeToKeychain" property for the logged in user
# is either empty or set to "NotNow".
self.delegate.offerToStorePassword()
method storePassword*(self: Controller, password: string) =
let account = self.accountsService.getLoggedInAccount()
self.keychainService.storePassword(account.name, password)
method updateUserPreferenceForStoreToKeychain*(self: Controller,
selection: string) =
self.localSettingsService.setAccountValue(LS_KEY_STORE_TO_KEYCHAIN,
newQVariant(selection))

View File

@ -12,4 +12,14 @@ method init*(self: AccessInterface) {.base.} =
method getCommunities*(self: AccessInterface):
seq[community_service.CommunityDto] {.base.} =
raise newException(ValueError, "No implementation available")
method checkForStoringPassword*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method storePassword*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method updateUserPreferenceForStoreToKeychain*(self: AccessInterface,
selection: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -5,9 +5,14 @@ import ../../../app/boot/global_singleton
import chat_section/module as chat_section_module
import ../../../app_service/service/local_settings/service as local_settings_service
import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/accounts/service_interface as accounts_service
import ../../../app_service/service/chat/service as chat_service
import ../../../app_service/service/community/service as community_service
import eventemitter
export io_interface
type
@ -29,7 +34,11 @@ type
chatSectionModule: chat_section_module.AccessInterface
communitySectionsModule: OrderedTable[string, chat_section_module.AccessInterface]
proc newModule*[T](delegate: T,
proc newModule*[T](delegate: T,
events: EventEmitter,
localSettingsService: local_settings_service.Service,
keychainService: keychain_service.Service,
accountsService: accounts_service.ServiceInterface,
chatService: chat_service.Service,
communityService: community_service.Service):
Module[T] =
@ -37,7 +46,8 @@ proc newModule*[T](delegate: T,
result.delegate = delegate
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, communityService)
result.controller = controller.newController(result, events, localSettingsService,
keychainService, accountsService, communityService)
# Submodules
result.chatSectionModule = chat_section_module.newModule(result, "chat",
@ -93,4 +103,17 @@ method communitySectionDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
method viewDidLoad*[T](self: Module[T]) =
self.checkIfModuleDidLoad()
self.checkIfModuleDidLoad()
method checkForStoringPassword*[T](self: Module[T]) =
self.controller.checkForStoringPassword()
method offerToStorePassword*[T](self: Module[T]) =
self.view.offerToStorePassword()
method storePassword*[T](self: Module[T], password: string) =
self.controller.storePassword(password)
method updateUserPreferenceForStoreToKeychain*[T](self: Module[T],
selection: string) =
self.controller.updateUserPreferenceForStoreToKeychain(selection)

View File

@ -3,3 +3,6 @@ method delete*(self: AccessInterface) {.base.} =
method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method checkForStoringPassword*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -0,0 +1,2 @@
method offerToStorePassword*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,2 +1,9 @@
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
raise newException(ValueError, "No implementation available")
method storePassword*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method updateUserPreferenceForStoreToKeychain*(self: AccessInterface,
selection: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -35,4 +35,18 @@ QtObject:
QtProperty[QVariant] sectionsModel:
read = getModel
notify = modelChanged
notify = modelChanged
proc openStoreToKeychainPopup*(self: View) {.signal.}
proc offerToStorePassword*(self: View) =
self.openStoreToKeychainPopup()
proc storePassword(self: View, password: string) {.slot.} =
self.delegate.storePassword(password)
proc updateUserPreferenceForStoreToKeychain(self: View, selection: string)
{.slot.} =
self.delegate.updateUserPreferenceForStoreToKeychain(selection)

View File

@ -1,8 +1,10 @@
import Tables
import NimQml, Tables
import controller_interface
import io_interface
import ../../../../app_service/service/local_settings/service as local_settings_service
import ../../../../app_service/service/keychain/service as keychain_service
import ../../../../app_service/service/accounts/service_interface as accounts_service
import eventemitter
@ -14,16 +16,22 @@ type
Controller* = ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface
events: EventEmitter
localSettingsService: local_settings_service.Service
keychainService: keychain_service.Service
accountsService: accounts_service.ServiceInterface
selectedAccountKeyUid: string
proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter,
localSettingsService: local_settings_service.Service,
keychainService: keychain_service.Service,
accountsService: accounts_service.ServiceInterface):
Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.localSettingsService = localSettingsService
result.keychainService = keychainService
result.accountsService = accountsService
method delete*(self: Controller) =
@ -38,17 +46,27 @@ method init*(self: Controller) =
method getOpenedAccounts*(self: Controller): seq[AccountDto] =
return self.accountsService.openedAccounts()
proc getSelectedAccount(self: Controller): AccountDto =
let openedAccounts = self.getOpenedAccounts()
for acc in openedAccounts:
if(acc.keyUid == self.selectedAccountKeyUid):
return acc
method setSelectedAccountKeyUid*(self: Controller, keyUid: string) =
self.selectedAccountKeyUid = keyUid
let selectedAccount = self.getSelectedAccount()
self.localSettingsService.updateAccountSettingsFilePath(selectedAccount.name)
self.delegate.emitStoreToKeychainValueChanged()
self.keychainService.tryToObtainPassword(selectedAccount.name)
method login*(self: Controller, password: string) =
let openedAccounts = self.getOpenedAccounts()
var selectedAccount: AccountDto
for acc in openedAccounts:
if(acc.keyUid == self.selectedAccountKeyUid):
selectedAccount = acc
break
let selectedAccount = self.getSelectedAccount()
let error = self.accountsService.login(selectedAccount, password)
if(error.len > 0):
self.delegate.loginAccountError(error)
self.delegate.loginAccountError(error)
method getStoreToKeychainValue*(self: Controller): string =
return self.localSettingsService.getAccountValue(
LS_KEY_STORE_TO_KEYCHAIN).stringVal

View File

@ -17,4 +17,7 @@ method setSelectedAccountKeyUid*(self: AccessInterface, keyUid: string) {.base.}
raise newException(ValueError, "No implementation available")
method login*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getStoreToKeychainValue*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -4,6 +4,8 @@ import ../io_interface as delegate_interface
import view, controller, item
import ../../../../app/boot/global_singleton
import ../../../../app_service/service/local_settings/service as local_settings_service
import ../../../../app_service/service/keychain/service as keychain_service
import ../../../../app_service/service/accounts/service_interface as accounts_service
import eventemitter
@ -20,13 +22,16 @@ type
proc newModule*(delegate: delegate_interface.AccessInterface,
events: EventEmitter,
localSettingsService: local_settings_service.Service,
keychainService: keychain_service.Service,
accountsService: accounts_service.ServiceInterface):
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, localSettingsService,
keychainService, accountsService)
result.moduleLoaded = false
singletonInstance.engine.setRootContextProperty("loginModule", result.viewVariant)
@ -79,4 +84,10 @@ method login*(self: Module, password: string) =
self.controller.login(password)
method loginAccountError*(self: Module, error: string) =
self.view.loginAccountError(error)
self.view.loginAccountError(error)
method emitStoreToKeychainValueChanged*(self: Module) =
self.view.emitStoreToKeychainValueChanged()
method getStoreToKeychainValue*(self: Module): string =
return self.controller.getStoreToKeychainValue()

View File

@ -1,2 +1,5 @@
method loginAccountError*(self: AccessInterface, error: string) {.base.} =
raise newException(ValueError, "No implementation available")
method emitStoreToKeychainValueChanged*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -7,4 +7,7 @@ method setSelectedAccount*(self: AccessInterface, item: Item) {.base.} =
raise newException(ValueError, "No implementation available")
method login*(self: AccessInterface, password: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getStoreToKeychainValue*(self: AccessInterface): string {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -66,4 +66,16 @@ QtObject:
proc accountLoginError*(self: View, error: string) {.signal.}
proc loginAccountError*(self: View, error: string) =
self.accountLoginError(error)
self.accountLoginError(error)
proc storeToKeychainValueChanged*(self: View) {.signal.}
proc emitStoreToKeychainValueChanged*(self: View) =
self.storeToKeychainValueChanged()
proc getStoreToKeychainValue(self: View): string {.slot.} =
return self.delegate.getStoreToKeychainValue()
QtProperty[string] storeToKeychainValue:
read = getStoreToKeychainValue
notify = storeToKeychainValueChanged

View File

@ -40,8 +40,10 @@ proc newModule*[T](delegate: T,
keychainService, accountsService)
# Submodules
result.onboardingModule = onboarding_module.newModule(result, events, fleet, accountsService)
result.loginModule = login_module.newModule(result, events, accountsService)
result.onboardingModule = onboarding_module.newModule(result, events, fleet,
accountsService)
result.loginModule = login_module.newModule(result, events, localSettingsService,
keychainService, accountsService)
method delete*[T](self: Module[T]) =
self.onboardingModule.delete

View File

@ -217,19 +217,6 @@ QtObject:
read = getSettingsFile
notify = settingsFileChanged
proc accountSettingsFileChanged*(self: ProfileView) {.signal.}
proc setAccountSettingsFile*(self: ProfileView, alias: string) =
self.localSettingsService.updateAccountSettingsFilePath(alias)
self.accountSettingsFileChanged()
proc getAccountSettingsFile*(self: ProfileView): string {.slot.} =
self.localSettingsService.getAccountSettingsFilePath
QtProperty[string] accountSettingsFile:
read = getAccountSettingsFile
notify = accountSettingsFileChanged
proc setSendUserStatus*(self: ProfileView, sendUserStatus: bool) {.slot.} =
if (sendUserStatus == self.profile.sendUserStatus):
return

View File

@ -13,6 +13,8 @@ logScope:
const LS_KEY_STORE_TO_KEYCHAIN* = "storeToKeychain"
# Local Account Settings values:
const LS_VALUE_STORE* = "store"
const LS_VALUE_NOTNOW* = "notNow"
const LS_VALUE_NEVER* = "never"
const ERROR_TYPE_AUTHENTICATION = "authentication"
const ERROR_TYPE_KEYCHAIN = "keychain"
@ -65,7 +67,8 @@ QtObject:
if (value != LS_VALUE_STORE):
return
echo "READ DATA FOR USER: ", username
self.keychainManager.readDataAsync(username)
proc onKeychainManagerError*(self: Service, errorType: string, errorCode: int,
@ -85,4 +88,5 @@ QtObject:
proc onKeychainManagerSuccess*(self: Service, data: string) {.slot.} =
## This slot is called in case a password is successfully retrieved from the
## Keychain. In this case @data contains required password.
echo "USER PASSWORD RECEIVED: ", data
self.events.emit("obtainingPasswordSuccess", KeyChainServiceArg(data: data))

View File

@ -54,7 +54,7 @@ QtObject:
self.settingsFilePath = os.joinPath(DATADIR, "qt", pubKey)
self.settings = newQSettings(self.settingsFilePath, QSettingsFormat.IniFormat)
proc updateAccountSettingsFilePath*(self: Service, alias: string) =
proc updateAccountSettingsFilePath*(self: Service, filePath: string) =
let unknownAccountSettingsPath = os.joinPath(DATADIR, "qt", UNKNOWN_ACCOUNT)
if (not unknownAccountSettingsPath.tryRemoveFile):
# Only fails if the file exists and an there was an error removing it
@ -62,7 +62,7 @@ QtObject:
warn "Failed to remove unused settings file", file=unknownAccountSettingsPath
self.accountSettings.delete
self.accountSettingsFilePath = os.joinPath(DATADIR, "qt", alias)
self.accountSettingsFilePath = os.joinPath(DATADIR, "qt", filePath)
self.accountSettings = newQSettings(self.accountSettingsFilePath, QSettingsFormat.IniFormat)
proc setAccountValue*(self: Service, key: string, value: QVariant) =

View File

@ -160,9 +160,7 @@ ModalPopup {
if (storingPasswordModal)
{
accountSettings.storeToKeychain = Constants.storeToKeychainValueStore
// NEED TO HANDLE IT
// This part should be done via PrivacyAndSecurity submodule section of ProfileSection module
// loginModel.storePassword(profileModel.profile.username, repeatPasswordField.text)
applicationWindow.prepareForStoring(repeatPasswordField.text)
popup.close()
}
else
@ -175,9 +173,7 @@ ModalPopup {
return importError.open()
}
// NEED TO HANDLE IT
// applicationWindow.checkForStoringPassToKeychain(onboardingModel.currentAccount.username,
// repeatPasswordField.text, true)
applicationWindow.prepareForStoring(repeatPasswordField.text)
}
}
}

View File

@ -10,9 +10,9 @@ QtObject {
loginModul.login(password)
}
function tryToObtainPassword() {
loginModel.tryToObtainPassword()
}
// function tryToObtainPassword() {
// loginModel.tryToObtainPassword()
// }
function setCurrentAccount(index) {
loginModul.setSelectedAccountByIndex(index)

View File

@ -31,8 +31,7 @@ Item {
loading = true
LoginStore.login(password)
// NEED TO HANDLE IT
//applicationWindow.checkForStoringPassToKeychain(LoginStore.currentAccount.username, password, false)
applicationWindow.prepareForStoring(password)
txtPassword.textField.clear()
}
@ -41,7 +40,7 @@ Item {
{
connection.enabled = true
txtPassword.visible = false
LoginStore.tryToObtainPassword()
// LoginStore.tryToObtainPassword()
}
else
{

View File

@ -40,12 +40,31 @@ StatusWindow {
}
}
Settings {
id: accountSettings
fileName: profileModel.accountSettingsFile
// Settings {
// id: accountSettings
// //fileName: loginModule.accountSettingsFile
property string storeToKeychain: ""
}
// property string storeToKeychain: ""
//// onFileNameChanged: {
//// console.warn("OnFileNameChanged: path: ", accountSettings.fileName, " kcValue: ", accountSettings.storeToKeychain)
//// }
// onStoreToKeychainChanged: {
// console.warn("onStoreToKeychainChanged: path: ", accountSettings.fileName, " kcValue: ", accountSettings.storeToKeychain)
// }
// }
// Connections {
// target: loginModule
// onAccountSettingsFileChanged: {
// console.warn("OnFileNameChanged1111: path: ", accountSettings.fileName, " kcValue: ", accountSettings.storeToKeychain)
// accountSettings.fileName = ""
// console.warn("OnFileNameChanged2222: path: ", accountSettings.fileName, " kcValue: ", accountSettings.storeToKeychain)
// accountSettings.fileName = loginModule.accountSettingsFile
// console.warn("OnFileNameChanged3333: path: ", accountSettings.fileName, " kcValue: ", accountSettings.storeToKeychain)
// }
// }
Settings {
id: appSettings
@ -167,6 +186,13 @@ StatusWindow {
}
}
Connections {
target: mainModule
onOpenStoreToKeychainPopup: {
storeToKeychainConfirmationPopup.open()
}
}
//! Workaround for custom QQuickWindow
Connections {
target: applicationWindow
@ -281,28 +307,34 @@ StatusWindow {
}
}
function checkForStoringPassToKeychain(username, password, clearStoredValue) {
// function checkForStoringPassToKeychain(username, password, clearStoredValue) {
// if(Qt.platform.os == "osx")
// {
// if(clearStoredValue)
// {
// accountSettings.storeToKeychain = ""
// }
// if(accountSettings.storeToKeychain === "" ||
// accountSettings.storeToKeychain === Constants.storeToKeychainValueNotNow)
// {
// storeToKeychainConfirmationPopup.password = password
// storeToKeychainConfirmationPopup.username = username
// storeToKeychainConfirmationPopup.open()
// }
// }
// }
function prepareForStoring(password) {
if(Qt.platform.os == "osx")
{
if(clearStoredValue)
{
accountSettings.storeToKeychain = ""
}
if(accountSettings.storeToKeychain === "" ||
accountSettings.storeToKeychain === Constants.storeToKeychainValueNotNow)
{
storeToKeychainConfirmationPopup.password = password
storeToKeychainConfirmationPopup.username = username
storeToKeychainConfirmationPopup.open()
}
storeToKeychainConfirmationPopup.password = password
}
}
ConfirmationDialog {
id: storeToKeychainConfirmationPopup
property string password: ""
property string username: ""
height: 200
confirmationText: qsTr("Would you like to store password to the Keychain?")
showRejectButton: true
@ -314,24 +346,22 @@ StatusWindow {
function finish()
{
password = ""
username = ""
storeToKeychainConfirmationPopup.close()
}
onConfirmButtonClicked: {
accountSettings.storeToKeychain = Constants.storeToKeychainValueStore
// This is need to be handled using KeyChain service via LoginModule
// loginModel.storePassword(username, password)
console.warn("STOREEEEEE....pass: ", password)
mainModule.storePassword(password)
finish()
}
onRejectButtonClicked: {
accountSettings.storeToKeychain = Constants.storeToKeychainValueNotNow
mainModule.updateUserPreferenceForStoreToKeychain(Constants.storeToKeychainValueNotNow)
finish()
}
onCancelButtonClicked: {
accountSettings.storeToKeychain = Constants.storeToKeychainValueNever
mainModule.updateUserPreferenceForStoreToKeychain(Constants.storeToKeychainValueNever)
finish()
}
}