diff --git a/Gui/AddNewObject.qml b/Gui/AddNewObject.qml index d3ffd6a..1641e18 100644 --- a/Gui/AddNewObject.qml +++ b/Gui/AddNewObject.qml @@ -11,6 +11,7 @@ GridLayout Layout.fillHeight: true rowSpacing: 9 + //// New grid row Label @@ -218,7 +219,9 @@ GridLayout function checkObjectField() { + return street.text.trim() && houseno.text.trim() && + (postcode.editText.trim() || postcode.currentText.trim()) && (city.editText.trim() || city.currentText.trim()) && cleaningproducts.text.trim() diff --git a/Gui/BackupSettings.qml b/Gui/BackupSettings.qml new file mode 100644 index 0000000..31a6076 --- /dev/null +++ b/Gui/BackupSettings.qml @@ -0,0 +1,115 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Dialogs +import QtCore +import "../js/qmldict.js" as JsLib + + + +GridLayout +{ + anchors.fill: parent + anchors.topMargin: 150 + columns: 2 + Label + { + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignHCenter + text: qsTr("Sicherung") + font.pixelSize: 35 + } + + Label + { + text: qsTr("Konfiguration") + Layout.alignment: Qt.AlignRight + + } + Button + { + id: saveConfig + text: qsTr("Jetzt sichern!") + onClicked: dialog.open() + } + Label + { + text: qsTr("Verschlüsselung") + Layout.alignment: Qt.AlignRight + } + Button + { + id: saveEncryption + text: qsTr("Jetzt sichern!") + onClicked: apploader.item.recoverEnc.open() + } + Item + { + id: spacer + Layout.fillHeight: true + } + Dialog + { + anchors.centerIn: parent + id: dialog + title: "Title" + standardButtons: Dialog.Apply | Dialog.Cancel + + onApplied: + { + + if (configPwd.text === repeatConfigPwd.text) + { + + saveConfigFile.open() + } + else + { + configPwd.text = "" + configPwd.placeholderText = qsTr("Passwort stimmt nicht überein") + configPwd.placeholderTextColor = "red" + repeatConfigPwd.placeholderText = qsTr("") + repeatConfigPwd.text = "" + } + } + onRejected: console.log("Cancel clicked") + GridLayout + { + id: gridPw + columns: 2 + Label + { + text: qsTr("Passwort eingeben") + } + TextField + { + + id: configPwd + placeholderText: qsTr("Sicherungspasswort festlegen") + } + Label + { + text: qsTr("Passwort wiederholen") + } + TextField + { + property string name: "password" + id: repeatConfigPwd + placeholderText: qsTr("Sicherungspasswort wiederholen") + } + + } + } + FileDialog + { + id: saveConfigFile + fileMode: FileDialog.SaveFile + nameFilters: ["PYQCRM Recovery files (*.pyqrec)"] + currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0] + onAccepted: + { + var pw = JsLib.parseForm(gridPw) + config.backupConfig(saveConfigFile.currentFile, pw["password"]) + } + } +} diff --git a/Gui/firststart.qml b/Gui/Firststart.qml similarity index 89% rename from Gui/firststart.qml rename to Gui/Firststart.qml index 38544a8..031d688 100644 --- a/Gui/firststart.qml +++ b/Gui/Firststart.qml @@ -11,7 +11,9 @@ Item { property string recpass: "" property bool adminAvailable: true + property alias recoverEnc: recoveryPaswordDialog + id: firstStartItem anchors.fill: parent StackView { @@ -87,6 +89,7 @@ Item Dialog { + id: recoveryPaswordDialog modal: true title: qsTr("Wiederherstellung") @@ -115,6 +118,19 @@ Item implicitWidth: 300 placeholderText: qsTr("Hier Wiederherstellungspasswort eingeben") } + Label + { + text: qsTr("Wiederherstellungspasswort eingeben: ") + } + + TextField + { + id: repeatRecoveryPaswordInput + text: "" + echoMode: TextInput.Password + implicitWidth: 300 + placeholderText: qsTr("Hier Wiederherstellungspasswort eingeben") + } } } } diff --git a/Gui/ObjectAddOnContactPerson.qml b/Gui/ObjectAddOnContactPerson.qml index fa5755c..e999b81 100644 --- a/Gui/ObjectAddOnContactPerson.qml +++ b/Gui/ObjectAddOnContactPerson.qml @@ -269,8 +269,10 @@ GridLayout highlightFollowsCurrentItem: false onActiveFocusChanged: if(!focus) currentIndex = -1 delegate: Item + { width: contactView.width + height: 15 MouseArea { @@ -281,17 +283,7 @@ GridLayout contactView.highlightFollowsCurrentItem = true } } - MouseArea - { - id: clickedRow - anchors.fill: parent - onClicked: - { - //var currentIndex = index - console.log(index) - console.log(contactView.currentItem.y) - } - } + Row { diff --git a/Gui/PyqcrmConf.qml b/Gui/PyqcrmConf.qml index 2527b3e..b37913c 100644 --- a/Gui/PyqcrmConf.qml +++ b/Gui/PyqcrmConf.qml @@ -21,6 +21,12 @@ Item { text: qsTr("Das Unternehmen") } + + TabButton + { + text: qsTr("Sicherung") + } + } StackLayout @@ -55,6 +61,17 @@ Item anchors.fill: parent } } + + Item + { + id: backup + BackupSettings + { + id: backupSettings + anchors.fill: parent + } + } + } RowLayout diff --git a/Gui/main.qml b/Gui/main.qml index 2e85370..3d43405 100644 --- a/Gui/main.qml +++ b/Gui/main.qml @@ -12,6 +12,8 @@ ApplicationWindow visible: true title: "PYQCRM" property string confile: "" + property alias settingsFileDialog: settingsFiledialog + TopBar { @@ -80,17 +82,18 @@ ApplicationWindow anchors.centerIn: parent standardButtons: Dialog.Yes | Dialog.No onAccepted: settingsFiledialog.open() - onRejected: appLoader.source= "firststart.qml" + onRejected: appLoader.source= "Firststart.qml" title: qsTr("Einstellungen importieren") } FileDialog { + id: settingsFiledialog title: qsTr("PYQCRM Einstellungen") currentFolder: StandardPaths.standardLocations(StandardPaths.DocumentsLocation)[0] modality: "ApplicationModal" - nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqcrm)")] + nameFilters: [qsTr("PYQCRM Einstellungen (*.pyqrec)")] onAccepted: { exportFilePassword.open() diff --git a/enc_key_backup.txt b/enc_key_backup.txt new file mode 100644 index 0000000..a4915d4 --- /dev/null +++ b/enc_key_backup.txt @@ -0,0 +1 @@ +Lj30yFOP7hJmY5Cub1Go8fJz0UE+Zyo9cEqNxfY23Sc= diff --git a/lib/ConfigLoader.py b/lib/ConfigLoader.py index a3bda34..b56ce66 100644 --- a/lib/ConfigLoader.py +++ b/lib/ConfigLoader.py @@ -110,6 +110,12 @@ class ConfigLoader(QObject): self.backupEncryptionKey.emit() return admin + """###################################################""" + """ """ + """ TODO: Rename method and rename self.__encrypt_key """ + """ """ + """###################################################""" + @Slot(str, str) def saveRecoveryKey(self, recovery_file, recovery_password): # print(f"In {__file__} file, saveRecoveryKey()") @@ -131,30 +137,39 @@ class ConfigLoader(QObject): @Slot(str, str) def getRecoveryKey(self, recovery_file, recovery_password): - # print(f"In {__file__} file, getRecoveryKey()") - local = False rec_file = urlparse(recovery_file) rec_file = rec_file.path if os.name == "nt": rec_file = rec_file [1:] try: - with open(rec_file, "r") as f: - rf = f.read() - rf = Vermasseln().entschluesseln(rf, local) - ek = rf[128:] - ek = ek[:-32] - password = rf[:128] - salt = rf[-32:] - ok = self.__checkRecoveryPassword(recovery_password, password, salt) - if ok: - self.__setEncryptionKey(ek) - self.configurationReady.emit() - else: - self.__invalidateEncryptionKey() - self.invalidEncryptionKey.emit() + ek = self.__parseImport(rec_file, recovery_password) + + if ek: + self.__setEncryptionKey(ek) + self.configurationReady.emit() + else: + self.__invalidateEncryptionKey() + self.invalidEncryptionKey.emit() except Exception as e: print(str(e)) + def __parseImport(self, rec_file, recovery_password): + local = False + with open(rec_file, "r") as f: + + rf = f.read() + rf = Vermasseln().entschluesseln(rf, local) + ek = rf[128:] + ek = ek[:-32] + password = rf[:128] + salt = rf[-32:] + ok = self.__checkRecoveryPassword(recovery_password, password, salt) + if ok: + return ek + else: + return None + + def __invalidateEncryptionKey(self): # print(f"In {__file__} file, __invalidateEncryptionKey()") self.__config['pyqcrm']['ENCRYPTION_KEY_VALID'] = 'No' @@ -173,13 +188,25 @@ class ConfigLoader(QObject): @Slot(str, str) # todo: non local encryption def importConfig(self, confile, password): - # print(f"In {__file__} file, importConfig()") confile = urlparse(confile) confile = confile.path - if os.name == "nt": confile = confile[1:] - shutil.copyfile(confile, self.config_dir+ '/pyqcrm.toml') + try: + ek = self.__parseImport(confile, password) + if ek: + self.__config = toml.loads(ek) + self.__saveConfig() + self.configurationReady.emit() + else: + self.invalidEncryptionKey.emit() + except Exception as e: + print(str(e)) + + + + + def __configLoad(self): # print(f"In {__file__} file, __configLoad()") @@ -195,6 +222,7 @@ class ConfigLoader(QObject): except Exception as e: print(str(e)) + def getConfig(self): # print(f"In {__file__} file, getConfig()") # print(self.__config) @@ -212,3 +240,8 @@ class ConfigLoader(QObject): self.__config['pyqcrm']['ENCRYPTION_KEY'] = enc_key self.__saveConfig() + @Slot(str, str) + def backupConfig(self, filename, password): + self.__encrypt_key = toml.dumps(self.getConfig()) + self.saveRecoveryKey(filename, password) + diff --git a/lib/Vermasseln.py b/lib/Vermasseln.py index 72e3190..67570cb 100644 --- a/lib/Vermasseln.py +++ b/lib/Vermasseln.py @@ -53,7 +53,6 @@ class Vermasseln: hash_pw = (salt + password).encode("utf-8") h_obj = SHA3_512.new(hash_pw) password = salt + "$" + h_obj.hexdigest() - return password diff --git a/qml.qrc b/qml.qrc index 7996b66..a524421 100644 --- a/qml.qrc +++ b/qml.qrc @@ -10,7 +10,6 @@ Gui/CustomerTable.qml Gui/Dashboard.qml Gui/EmployeeTable.qml - Gui/firststart.qml Gui/main.qml Gui/SearchBar.qml js/qmldict.js @@ -44,5 +43,7 @@ Gui/UsersPage.qml Gui/PyqcrmConf.qml Gui/CompanyConf.qml + Gui/Firststart.qml + Gui/BackupSettings.qml