diff --git a/src/sakia/data/entities/connection.py b/src/sakia/data/entities/connection.py index 09b453740b106506ddad0520368884072731aad8..adf9699d3d6aca72b9f7c6ac698667d329504606 100644 --- a/src/sakia/data/entities/connection.py +++ b/src/sakia/data/entities/connection.py @@ -20,6 +20,12 @@ class Connection: blockstamp = attr.ib(convert=block_uid, default=BlockUID.empty(), cmp=False, hash=False) password = attr.ib(init=False, convert=str, default="", cmp=False, hash=False) + def is_identity(self): + return self.uid is not "" + + def is_wallet(self): + return self.uid is "" + def title(self): return "@".join([self.uid, self.pubkey[:11]]) diff --git a/src/sakia/data/repositories/connections.py b/src/sakia/data/repositories/connections.py index 1c24fad895c86d783be23e527a290f7acb638c3f..42adf935987e0c7e3da5572ebfc2cd6f87d64a37 100644 --- a/src/sakia/data/repositories/connections.py +++ b/src/sakia/data/repositories/connections.py @@ -16,6 +16,8 @@ class ConnectionsRepo: Commit a connection to the database :param sakia.data.entities.Connection connection: the connection to commit """ + if connection.is_identity(): + connection = attr.assoc(connection, salt="") connection_tuple = attr.astuple(connection, filter=attr.filters.exclude(Connection.password)) values = ",".join(['?'] * len(connection_tuple)) self._conn.execute("INSERT INTO connections VALUES ({0})".format(values), connection_tuple) @@ -25,6 +27,8 @@ class ConnectionsRepo: Update an existing connection in the database :param sakia.data.entities.Connection connection: the certification to update """ + if connection.is_identity(): + connection = attr.assoc(connection, salt="") updated_fields = attr.astuple(connection, filter=attr.filters.exclude(Connection.password, *ConnectionsRepo._primary_keys)) where_fields = attr.astuple(connection, filter=attr.filters.include(*ConnectionsRepo._primary_keys)) diff --git a/src/sakia/gui/dialogs/certification/certification.ui b/src/sakia/gui/dialogs/certification/certification.ui index f67156ae2a03696f7c30b8337562a0ea5a3be949..c2c5f9ca38e5d7373bfa00e498f1b5976d8a1f73 100644 --- a/src/sakia/gui/dialogs/certification/certification.ui +++ b/src/sakia/gui/dialogs/certification/certification.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>517</width> - <height>338</height> + <height>360</height> </rect> </property> <property name="windowTitle"> @@ -27,7 +27,7 @@ </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> - <widget class="QComboBox" name="combo_pubkey"/> + <widget class="QComboBox" name="combo_connection"/> </item> <item> <widget class="QGroupBox" name="groupBox_3"> @@ -56,6 +56,18 @@ <layout class="QVBoxLayout" name="verticalLayout_3"/> </widget> </item> + <item> + <widget class="QGroupBox" name="group_box_password"> + <property name="title"> + <string>Password</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <layout class="QVBoxLayout" name="layout_password_input"/> + </item> + </layout> + </widget> + </item> <item> <widget class="QDialogButtonBox" name="button_box"> <property name="enabled"> diff --git a/src/sakia/gui/dialogs/certification/controller.py b/src/sakia/gui/dialogs/certification/controller.py index 135ce09145539cb0cdcdfe1541c19c9e00b9fdd3..8d984e57a5c38b21987e0c85b328e516db3d1c36 100644 --- a/src/sakia/gui/dialogs/certification/controller.py +++ b/src/sakia/gui/dialogs/certification/controller.py @@ -20,14 +20,15 @@ class CertificationController(QObject): view = attr.ib() model = attr.ib() - search_user = attr.ib(default=None) - user_information = attr.ib(default=None) + search_user = attr.ib() + user_information = attr.ib() + password_input = attr.ib() def __attrs_post_init__(self): super().__init__() self.view.button_box.accepted.connect(self.accept) self.view.button_box.rejected.connect(self.reject) - self.view.combo_pubkey.currentIndexChanged.connect(self.change_connection) + self.view.combo_connection.currentIndexChanged.connect(self.change_connection) @classmethod def create(cls, parent, app): @@ -38,15 +39,19 @@ class CertificationController(QObject): :return: a new Certification controller :rtype: CertificationController """ - view = CertificationView(parent.view if parent else None, None, None) - model = CertificationModel(app) - certification = cls(view, model, None, None) - search_user = SearchUserController.create(certification, app, "") - certification.set_search_user(search_user) + search_user = SearchUserController.create(None, app) + user_information = UserInformationController.create(None, app, None) + password_input = PasswordInputController.create(None, None) + + view = CertificationView(parent.view if parent else None, search_user.view, user_information.view, + password_input.view) + model = CertificationModel(app) + certification = cls(view, model, search_user, user_information, password_input) + search_user.identity_selected.connect(certification.refresh_user_information) + password_input.password_changed.connect(certification.refresh) - user_information = UserInformationController.create(certification, app, "", None) - certification.set_user_information(user_information) + user_information.identity_loaded.connect(certification.refresh) view.set_keys(certification.model.available_connections()) return certification @@ -62,8 +67,7 @@ class CertificationController(QObject): :return: """ dialog = cls.create(parent, app) - if connection: - dialog.view.combo_pubkey.setCurrentText(connection.title()) + dialog.set_connection(connection) dialog.refresh() return dialog.exec() @@ -78,30 +82,20 @@ class CertificationController(QObject): :return: """ dialog = cls.create(parent, app) - dialog.view.combo_pubkey.setCurrentText(connection.title()) + dialog.view.combo_connection.setCurrentText(connection.title()) dialog.user_information.change_identity(identity) dialog.refresh() return await dialog.async_exec() - def set_search_user(self, search_user): - """ - - :param search_user: - :return: - """ - self.search_user = search_user - self.view.set_search_user(search_user.view) - search_user.identity_selected.connect(self.refresh_user_information) - - def set_user_information(self, user_information): - """ + def change_connection(self, index): + self.model.set_connection(index) + self.password_input.set_connection(self.model.connection) + self.refresh() - :param user_information: - :return: - """ - self.user_information = user_information - self.view.set_user_information(user_information.view) - self.user_information.identity_loaded.connect(self.refresh) + def set_connection(self, connection): + if connection: + self.view.combo_connection.setCurrentText(connection.title()) + self.password_input.set_connection(connection) @asyncify async def accept(self): @@ -109,12 +103,9 @@ class CertificationController(QObject): Validate the dialog """ self.view.button_box.setDisabled(True) - password = await PasswordInputController.open_dialog(self, self.model.connection) - if not password: - self.view.button_box.setEnabled(True) - return + secret_key, password = self.password_input.get_salt_password() QApplication.setOverrideCursor(Qt.WaitCursor) - result = await self.model.certify_identity(password, self.user_information.model.identity) + result = await self.model.certify_identity(secret_key, password, self.user_information.model.identity) if result[0]: QApplication.restoreOverrideCursor() @@ -146,8 +137,10 @@ class CertificationController(QObject): remaining_localized = self.tr("{hours}h {min}min").format(hours=hours, min=minutes) self.view.set_button_box(CertificationView.ButtonBoxState.REMAINING_TIME_BEFORE_VALIDATION, remaining=remaining_localized) - else: + elif self.password_input.valid(): self.view.set_button_box(CertificationView.ButtonBoxState.OK) + else: + self.view.set_button_box(CertificationView.ButtonBoxState.WRONG_PASSWORD) else: self.view.set_button_box(CertificationView.ButtonBoxState.NO_MORE_CERTIFICATION) else: @@ -159,12 +152,6 @@ class CertificationController(QObject): """ self.user_information.search_identity(self.search_user.model.identity()) - def change_connection(self, index): - self.model.set_connection(index) - self.search_user.set_currency(self.model.connection.currency) - self.user_information.set_currency(self.model.connection.currency) - self.refresh() - def async_exec(self): future = asyncio.Future() self.view.finished.connect(lambda r: future.set_result(r)) diff --git a/src/sakia/gui/dialogs/certification/model.py b/src/sakia/gui/dialogs/certification/model.py index 783798edb113a6e70fe1dec8c4f4292f5f218eb2..81fe30f5740716e3306bcd189f0bc306e6edaa61 100644 --- a/src/sakia/gui/dialogs/certification/model.py +++ b/src/sakia/gui/dialogs/certification/model.py @@ -89,8 +89,8 @@ class CertificationModel(QObject): def notification(self): return self.app.parameters.notifications - async def certify_identity(self, password, identity): - result = await self.app.documents_service.certify(self.connection, password, identity) + async def certify_identity(self, secret_key, password, identity): + result = await self.app.documents_service.certify(self.connection, secret_key, password, identity) if result[0]: connection_identity = self._identities_processor.get_identity(self.connection.currency, self.connection.pubkey, diff --git a/src/sakia/gui/dialogs/certification/view.py b/src/sakia/gui/dialogs/certification/view.py index f06920d603330040b7223b5fa0e811b5af75d1d3..c21a3090b5a491bfcd1953250a7fbbbc9d36680b 100644 --- a/src/sakia/gui/dialogs/certification/view.py +++ b/src/sakia/gui/dialogs/certification/view.py @@ -17,6 +17,7 @@ class CertificationView(QDialog, Ui_CertificationDialog): REMAINING_TIME_BEFORE_VALIDATION = 2 OK = 3 SELECT_IDENTITY = 4 + WRONG_PASSWORD = 5 _button_box_values = { ButtonBoxState.NO_MORE_CERTIFICATION: (False, @@ -26,10 +27,11 @@ class CertificationView(QDialog, Ui_CertificationDialog): ButtonBoxState.REMAINING_TIME_BEFORE_VALIDATION: (True, QT_TRANSLATE_NOOP("CertificationView", "&Ok (Not validated before {remaining})")), - ButtonBoxState.OK: (True, QT_TRANSLATE_NOOP("CertificationView", "&Ok")) + ButtonBoxState.OK: (True, QT_TRANSLATE_NOOP("CertificationView", "&Ok")), + ButtonBoxState.WRONG_PASSWORD: (False, QT_TRANSLATE_NOOP("CertificationView", "Please enter correct password")) } - def __init__(self, parent, search_user_view, user_information_view): + def __init__(self, parent, search_user_view, user_information_view, password_input_view): """ :param parent: @@ -40,33 +42,24 @@ class CertificationView(QDialog, Ui_CertificationDialog): super().__init__(parent) self.setupUi(self) - self.search_user = search_user_view + self.search_user_view = search_user_view self.user_information_view = user_information_view + self.password_input_view = password_input_view + self.groupbox_certified.layout().addWidget(search_user_view) + self.search_user_view.button_reset.hide() + self.layout_password_input.addWidget(password_input_view) + self.groupbox_certified.layout().addWidget(user_information_view) def set_keys(self, connections): - self.combo_pubkey.clear() + self.combo_connection.clear() for c in connections: - self.combo_pubkey.addItem(c.title()) + self.combo_connection.addItem(c.title()) def set_selected_key(self, connection): """ :param sakia.data.entities.Connection connection: """ - self.combo_pubkey.setCurrentText(connection.title()) - - def set_search_user(self, search_user_view): - """ - - :param sakia.gui.search_user.view.SearchUserView search_user_view: - :return: - """ - self.search_user = search_user_view - self.groupbox_certified.layout().addWidget(search_user_view) - self.search_user.button_reset.hide() - - def set_user_information(self, user_information_view): - self.user_information_view = user_information_view - self.groupbox_certified.layout().addWidget(user_information_view) + self.combo_connection.setCurrentText(connection.title()) def pubkey_value(self): return self.edit_pubkey.text() diff --git a/src/sakia/gui/dialogs/transfer/controller.py b/src/sakia/gui/dialogs/transfer/controller.py index 02a2cf28d5c119edb57a9d6130ead42ccb99ce80..3e46aa3975a156b9bcd86bc9b6c54a5af1736a87 100644 --- a/src/sakia/gui/dialogs/transfer/controller.py +++ b/src/sakia/gui/dialogs/transfer/controller.py @@ -49,8 +49,8 @@ class TransferController(QObject): :return: a new Transfer controller :rtype: TransferController """ - search_user = SearchUserController.create(None, app, "") - user_information = UserInformationController.create(None, app, "", None) + search_user = SearchUserController.create(None, app) + user_information = UserInformationController.create(None, app, None) password_input = PasswordInputController.create(None, None) view = TransferView(parent.view if parent else None, @@ -140,13 +140,13 @@ class TransferController(QObject): amount_base = self.model.current_base() logging.debug("Showing password dialog...") - password = self.password_input.get_password() + secret_key, password = self.password_input.get_salt_password() logging.debug("Setting cursor...") QApplication.setOverrideCursor(Qt.WaitCursor) logging.debug("Send money...") - result, transaction = await self.model.send_money(recipient, password, amount, amount_base, comment) + result, transaction = await self.model.send_money(recipient, secret_key, password, amount, amount_base, comment) if result[0]: await self.view.show_success(self.model.notifications(), recipient) logging.debug("Restore cursor...") @@ -197,8 +197,6 @@ class TransferController(QObject): def change_current_connection(self, index): self.model.set_connection(index) - self.search_user.set_currency(self.model.connection.currency) - self.user_information.set_currency(self.model.connection.currency) self.password_input.set_connection(self.model.connection) self.refresh() diff --git a/src/sakia/gui/dialogs/transfer/model.py b/src/sakia/gui/dialogs/transfer/model.py index 4a32c58f12fa800f2e897471fc19953fb33aa708..eed29eee1773b46bedb53097f11f7e2804ea5ce9 100644 --- a/src/sakia/gui/dialogs/transfer/model.py +++ b/src/sakia/gui/dialogs/transfer/model.py @@ -79,7 +79,7 @@ class TransferModel(QObject): connections = self._connections_processor.connections() self.connection = connections[index] - async def send_money(self, recipient, password, amount, amount_base, comment): + async def send_money(self, recipient, secret_key, password, amount, amount_base, comment): """ Send money to given recipient using the account :param str recipient: @@ -90,7 +90,7 @@ class TransferModel(QObject): :return: the result of the send """ - result = await self.app.documents_service.send_money(self.connection, password, + result = await self.app.documents_service.send_money(self.connection, secret_key, password, recipient, amount, amount_base, comment) self.app.db.commit() return result diff --git a/src/sakia/gui/main_window/toolbar/controller.py b/src/sakia/gui/main_window/toolbar/controller.py index d93e2fa81db50b0e8c89a48c0fd3c58dc0f8d4ea..e07bff18c05156de650b4be26b74cd1b4792960b 100644 --- a/src/sakia/gui/main_window/toolbar/controller.py +++ b/src/sakia/gui/main_window/toolbar/controller.py @@ -60,10 +60,10 @@ class ToolbarController(QObject): connection = await self.view.ask_for_connection(self.model.connections_with_uids()) if not connection: return - password = await PasswordInputController.open_dialog(self, connection) - if not password: + secret_key, password = await PasswordInputController.open_dialog(self, connection) + if not password or not secret_key: return - result = await self.model.send_join(connection, password) + result = await self.model.send_join(connection, secret_key, password) if result[0]: if self.model.notifications(): toast.display(self.tr("Membership"), self.tr("Success sending Membership demand")) diff --git a/src/sakia/gui/main_window/toolbar/model.py b/src/sakia/gui/main_window/toolbar/model.py index 4f31381e902a59af7319d18ecdc07a207babe41f..5e312cdb4227b8142e8fd639ffa0f28fd8e7852f 100644 --- a/src/sakia/gui/main_window/toolbar/model.py +++ b/src/sakia/gui/main_window/toolbar/model.py @@ -19,8 +19,8 @@ class ToolbarModel(QObject): def __attrs_post_init__(self): super().__init__() - async def send_join(self, connection, password): - return await self.app.documents_service.send_membership(connection, password, "IN") + async def send_join(self, connection, secret_key, password): + return await self.app.documents_service.send_membership(connection, secret_key, password, "IN") def notifications(self): return self.app.parameters.notifications diff --git a/src/sakia/gui/navigation/controller.py b/src/sakia/gui/navigation/controller.py index 1f8d581b589eeb2bae0ebee75f6a3e5477d9bd03..7572ec6701371a76059c19e97e0678aecfa1c1d0 100644 --- a/src/sakia/gui/navigation/controller.py +++ b/src/sakia/gui/navigation/controller.py @@ -141,21 +141,21 @@ class NavigationController(QObject): @asyncify async def publish_uid(self, connection): - password = await PasswordInputController.open_dialog(self, connection) - if not password: + secret_key, password = await PasswordInputController.open_dialog(self, connection) + if not password or not secret_key: return - result = await self.account.send_selfcert(password, self.community) + result = await self.model.send_identity(connection, secret_key, password) if result[0]: if self.app.preferences['notifications']: toast.display(self.tr("UID"), self.tr("Success publishing your UID")) else: - await QAsyncMessageBox.information(self, self.tr("Membership"), + await QAsyncMessageBox.information(self.view, self.tr("Membership"), self.tr("Success publishing your UID")) else: if self.app.preferences['notifications']: toast.display(self.tr("UID"), result[1]) else: - await QAsyncMessageBox.critical(self, self.tr("UID"), + await QAsyncMessageBox.critical(self.view, self.tr("UID"), result[1]) @asyncify @@ -166,10 +166,11 @@ Sending a leaving demand cannot be canceled. The process to join back the community later will have to be done again.""") .format(self.account.pubkey), QMessageBox.Ok | QMessageBox.Cancel) if reply == QMessageBox.Ok: - password = await PasswordInputController.open_dialog(self.model.navigation_model.navigation.current_connection()).async_exec() - if not password: + connection = self.model.navigation_model.navigation.current_connection() + secret_key, password = await PasswordInputController.open_dialog(self, connection) + if not password or not secret_key: return - result = await self.model.send_leave(password) + result = await self.model.send_leave(connection, secret_key, password) if result[0]: if self.app.preferences['notifications']: toast.display(self.tr("Revoke"), self.tr("Success sending Revoke demand")) @@ -194,11 +195,11 @@ neither your identity from the network."""), QMessageBox.Ok | QMessageBox.Cancel @asyncify async def action_save_revokation(self, connection): - password = await PasswordInputController.open_dialog(connection) - if not password: + secret_key, password = await PasswordInputController.open_dialog(self, connection) + if not password or not secret_key: return - raw_document = self.model.generate_revokation(connection, password) + raw_document = self.model.generate_revokation(connection, secret_key, password) # Testable way of using a QFileDialog selected_files = QFileDialog.getSaveFileName(self.view, self.tr("Save a revokation document"), "", self.tr("All text files (*.txt)")) diff --git a/src/sakia/gui/navigation/model.py b/src/sakia/gui/navigation/model.py index 71aaa0bebd4e8613add469afc1981ccdaaefdba3..420a96242893dfd47530538bb89797eed9e3422c 100644 --- a/src/sakia/gui/navigation/model.py +++ b/src/sakia/gui/navigation/model.py @@ -121,8 +121,8 @@ class NavigationModel(QObject): else: return None - def generate_revokation(self, connection, password): - return self.app.documents_service.generate_revokation(connection, password) + def generate_revokation(self, connection, secret_key, password): + return self.app.documents_service.generate_revokation(connection, secret_key, password) def identity_published(self, connection): return self.app.identities_service.get_identity(connection.pubkey, connection.uid).written @@ -137,8 +137,11 @@ class NavigationModel(QObject): self._current_data['widget'].disconnect() await self.app.remove_connection(connection) - async def send_leave(self, connection, password): - return await self.app.documents_service.send_membership(connection, password, "OUT") + async def send_leave(self, connection, secret_key, password): + return await self.app.documents_service.send_membership(connection, secret_key, password, "OUT") + + async def send_identity(self, connection, secret_key, password): + return await self.app.documents_service.broadcast_identity(connection, secret_key, password) @staticmethod def copy_pubkey_to_clipboard(connection): diff --git a/src/sakia/gui/sub/password_input/controller.py b/src/sakia/gui/sub/password_input/controller.py index ee0e2c74aee2c10e40c2cf641cb036f9ddfb85db..cc2ecf0fb74c3a289b3e8f101d3d94169f4ffc43 100644 --- a/src/sakia/gui/sub/password_input/controller.py +++ b/src/sakia/gui/sub/password_input/controller.py @@ -23,6 +23,7 @@ class PasswordInputController(QObject): super().__init__() self.view = view self._password = "" + self._secret_key = "" self.connection = connection self.remember = False self.set_connection(connection) @@ -34,7 +35,8 @@ class PasswordInputController(QObject): def create(cls, parent, connection): view = PasswordInputView(parent.view if parent else None) password_input = cls(view, connection) - view.edit_password.textChanged.connect(password_input.handle_text_change) + view.edit_password.textChanged.connect(password_input.handle_password_change) + view.edit_secret_key.textChanged.connect(password_input.handle_secret_key_change) return password_input @classmethod @@ -52,35 +54,55 @@ class PasswordInputController(QObject): return connection.password result = await dialog_async_exec(dialog) if result == QDialog.Accepted: - return password_input.get_password() + return password_input.get_salt_password() else: return "" def valid(self): return self._password is not "" - def handle_text_change(self, password): - self._password = "" + def check_private_key(self, secret_key, password): + if detect_non_printable(secret_key): + self.view.error(self.tr("Non printable characters in secret key")) + return False + if detect_non_printable(password): self.view.error(self.tr("Non printable characters in password")) - self.password_changed.emit(False) - return + return False + + if SigningKey(secret_key, password, + self.connection.scrypt_params).pubkey != self.connection.pubkey: + self.view.error(self.tr("Wrong secret key or password. Cannot open the private key")) + return False + return True + + def handle_secret_key_change(self, secret_key): + self._secret_key = "" - if SigningKey(self.connection.salt, password, self.connection.scrypt_params).pubkey != self.connection.pubkey: - self.view.error(self.tr("Wrong password typed. Cannot open the private key")) + if self.check_private_key(secret_key, self.view.edit_password.text()): + self.view.valid() + self._secret_key = secret_key + self.password_changed.emit(True) + else: self.password_changed.emit(False) - return - self.view.valid() - self._password = password - self.password_changed.emit(True) + def handle_password_change(self, password): + self._password = "" + if self.check_private_key(self.view.edit_secret_key.text(), password): + self.view.valid() + self._password = password + self.password_changed.emit(True) + else: + self.password_changed.emit(False) - def get_password(self): + def get_salt_password(self): if self.view.check_remember.isChecked(): + self.connection.salt = self._secret_key self.connection.password = self._password - return self._password + return self._secret_key, self._password def set_connection(self, connection): if connection: self.connection = connection + self.view.edit_secret_key.setText(connection.salt) self.view.edit_password.setText(connection.password) diff --git a/src/sakia/gui/sub/password_input/password_input.ui b/src/sakia/gui/sub/password_input/password_input.ui index 6b248a68c7cfb87b141047b68bca4ed9d2674035..a57ae51f153e282941cd4869051efe687366a8a1 100644 --- a/src/sakia/gui/sub/password_input/password_input.ui +++ b/src/sakia/gui/sub/password_input/password_input.ui @@ -7,20 +7,27 @@ <x>0</x> <y>0</y> <width>400</width> - <height>98</height> + <height>110</height> </rect> </property> <property name="windowTitle"> <string>Please enter your password</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLineEdit" name="edit_secret_key"> + <property name="placeholderText"> + <string>Please enter your secret key</string> + </property> + </widget> + </item> <item> <widget class="QLineEdit" name="edit_password"> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> <property name="placeholderText"> - <string>Please enter your account password</string> + <string>Please enter your password</string> </property> </widget> </item> @@ -29,7 +36,7 @@ <item> <widget class="QCheckBox" name="check_remember"> <property name="text"> - <string>Remember my password during this session</string> + <string>Remember during this session</string> </property> </widget> </item> diff --git a/src/sakia/gui/sub/password_input/view.py b/src/sakia/gui/sub/password_input/view.py index 075fec6f9dfdd295bd4257f609281f3f5e1b4e23..2de53dd8380fc0a12c0293699fb8d3646761fa6a 100644 --- a/src/sakia/gui/sub/password_input/view.py +++ b/src/sakia/gui/sub/password_input/view.py @@ -1,8 +1,6 @@ from PyQt5.QtWidgets import QWidget, QDialogButtonBox from PyQt5.QtCore import QEvent, Qt -from duniterpy.key import SigningKey from .password_input_uic import Ui_PasswordInputWidget -import re class PasswordInputView(QWidget, Ui_PasswordInputWidget): diff --git a/src/sakia/gui/sub/search_user/controller.py b/src/sakia/gui/sub/search_user/controller.py index 4777d654c51418f300072c850cba2863bf6c482a..bb092cf47cf297e1496e903888e72c23f5488b18 100644 --- a/src/sakia/gui/sub/search_user/controller.py +++ b/src/sakia/gui/sub/search_user/controller.py @@ -27,9 +27,9 @@ class SearchUserController(QObject): self.view.node_selected.connect(self.select_node) @classmethod - def create(cls, parent, app, currency): + def create(cls, parent, app): view = SearchUserView(parent.view if parent else None) - model = SearchUserModel(parent, app, currency) + model = SearchUserModel(parent, app) search_user = cls(parent, view, model) model.setParent(search_user) return search_user @@ -60,5 +60,3 @@ class SearchUserController(QObject): self.model.select_identity(index) self.identity_selected.emit(self.model.identity()) - def set_currency(self, currency): - self.model.currency = currency diff --git a/src/sakia/gui/sub/search_user/model.py b/src/sakia/gui/sub/search_user/model.py index 222aeaa1a38dda2e5dacb773278ba84f00bcb530..9b8f166c21a2161398a5fb111b4ab1a856bb1f71 100644 --- a/src/sakia/gui/sub/search_user/model.py +++ b/src/sakia/gui/sub/search_user/model.py @@ -11,17 +11,15 @@ class SearchUserModel(QObject): The model of Navigation component """ - def __init__(self, parent, app, currency): + def __init__(self, parent, app): """ :param sakia.gui.search_user.controller.NetworkController parent: the controller :param sakia.app.Application app: the app - :param str currency: the currency network to look for users """ super().__init__(parent) self.app = app self.identities_processor = IdentitiesProcessor.instanciate(app) - self.currency = currency self._nodes = list() self._current_identity = None @@ -46,7 +44,7 @@ class SearchUserModel(QObject): :return: """ try: - self._nodes = await self.identities_processor.lookup(self.currency, text) + self._nodes = await self.identities_processor.lookup(self.app.currency, text) except errors.DuniterError as e: if e.ucode == errors.NO_MATCHING_IDENTITY: self._nodes = list() diff --git a/src/sakia/gui/sub/user_information/controller.py b/src/sakia/gui/sub/user_information/controller.py index 9a86ef27b755d61dee5e66820b40a35cf6bd256d..1e8bf38929aba6b71578b46c5e5bfdc1944ff4d8 100644 --- a/src/sakia/gui/sub/user_information/controller.py +++ b/src/sakia/gui/sub/user_information/controller.py @@ -24,25 +24,25 @@ class UserInformationController(QObject): self.model = model @classmethod - def create(cls, parent, app, currency, identity): + def create(cls, parent, app, identity): view = UserInformationView(parent.view if parent else None) - model = UserInformationModel(None, app, currency, identity) + model = UserInformationModel(None, app, identity) homescreen = cls(parent, view, model) model.setParent(homescreen) return homescreen @classmethod - def show_identity(cls, parent, app, currency, identity): + def show_identity(cls, parent, app, identity): dialog = QDialog() dialog.setWindowTitle("Informations") - user_info = cls.create(parent, app, currency, identity) + user_info = cls.create(parent, app, identity) user_info.view.setParent(dialog) user_info.refresh() dialog.exec() @classmethod @asyncify - async def search_and_show_pubkey(cls, parent, app, currency, pubkey): + async def search_and_show_pubkey(cls, parent, app, pubkey): dialog = QDialog(parent) dialog.setWindowTitle("Informations") layout = QVBoxLayout(dialog) @@ -51,7 +51,7 @@ class UserInformationController(QObject): identities = await app.identities_service.lookup(pubkey) for i in identities: - user_info = cls.create(parent, app, currency, i) + user_info = cls.create(parent, app, i) user_info.refresh() tabwidget.addTab(user_info.view, i.uid) return await dialog_async_exec(dialog) @@ -80,7 +80,3 @@ class UserInformationController(QObject): """ self.model.identity = identity self.refresh() - - def set_currency(self, currency): - self.model.set_currency(currency) - self.refresh() \ No newline at end of file diff --git a/src/sakia/gui/sub/user_information/model.py b/src/sakia/gui/sub/user_information/model.py index 20a478f04597416b8d3ffc30b02e07f68d50db61..d1fddeda6ecced6cfdc5ff8fa5df2897bb08cc19 100644 --- a/src/sakia/gui/sub/user_information/model.py +++ b/src/sakia/gui/sub/user_information/model.py @@ -7,12 +7,11 @@ class UserInformationModel(QObject): The model of HomeScreen component """ - def __init__(self, parent, app, currency, identity): + def __init__(self, parent, app, identity): """ :param sakia.gui.user_information.controller.UserInformationController parent: :param sakia.core.Application app: the app - :param str currency: the currency currently requested :param sakia.data.entities.Identity identity: the identity :param sakia.services.IdentitiesService identities_service: the identities service of current currency """ @@ -20,7 +19,6 @@ class UserInformationModel(QObject): self._certifications_processor = CertificationsProcessor.instanciate(app) self._blockchain_processor = BlockchainProcessor.instanciate(app) self.app = app - self.currency = currency self.identity = identity self.identities_service = self.app.identities_service if identity: @@ -35,9 +33,6 @@ class UserInformationModel(QObject): self.identity = await self.identities_service.load_memberships(self.identity) self.identity = await self.identities_service.load_requirements(self.identity) - def set_currency(self, currency): - self.currency = currency - async def nb_certs(self): certs = await self.identities_service.load_certifiers_of(self.identity) return len(certs) diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index 90d8fffcd32423e65c84df20fdf00f7470261302..20dbe7f5de3353b2f988130801681f2ebc77d791 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -120,10 +120,10 @@ class ContextMenu(QObject): def informations(self, identity): if identity.uid: - UserInformationController.show_identity(self.parent(), self._app, self._connection.currency, identity) + UserInformationController.show_identity(self.parent(), self._app, identity) self.identity_information_loaded.emit(identity) else: - UserInformationController.search_and_show_pubkey(self.parent(), self._app, self._connection.currency, + UserInformationController.search_and_show_pubkey(self.parent(), self._app, identity.pubkey) diff --git a/src/sakia/services/documents.py b/src/sakia/services/documents.py index 6a75fbb57ab8c349747331a30edbd1305262fe51..e7c72902ddd7bf28f78c59897262a1bb886a25c2 100644 --- a/src/sakia/services/documents.py +++ b/src/sakia/services/documents.py @@ -50,11 +50,12 @@ class DocumentsService: TransactionsProcessor.instanciate(app), SourcesProcessor.instanciate(app)) - async def broadcast_identity(self, connection, password): + async def broadcast_identity(self, connection, secret_key, password): """ Send our self certification to a target community :param sakia.data.entities.Connection connection: the connection published + :param str secret_key: the private key secret key :param str password: the private key password """ block_uid = self._blockchain_processor.current_buid(connection.currency) @@ -65,7 +66,7 @@ class DocumentsService: connection.uid, block_uid, None) - key = SigningKey(connection.salt, password, connection.scrypt_params) + key = SigningKey(secret_key, password, connection.scrypt_params) selfcert.sign([key]) self._logger.debug("Key publish : {0}".format(selfcert.signed_raw())) @@ -106,14 +107,13 @@ class DocumentsService: return result - async def send_membership(self, connection, password, mstype): + async def send_membership(self, connection, secret_key, password, mstype): """ Send a membership document to a target community. Signal "document_broadcasted" is emitted at the end. - :param str currency: the currency target - :param sakia.data.entities.IdentityDoc identity: the identitiy data - :param str salt: The account SigningKey salt + :param sakia.data.entities.Connection connection: the connection publishing ms doc + :param str secret_key: The account SigningKey salt :param str password: The account SigningKey password :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave """ @@ -123,7 +123,7 @@ class DocumentsService: membership = Membership(10, connection.currency, connection.pubkey, blockUID, mstype, connection.uid, connection.blockstamp, None) - key = SigningKey(connection.salt, password, connection.scrypt_params) + key = SigningKey(secret_key, password, connection.scrypt_params) membership.sign([key]) self._logger.debug("Membership : {0}".format(membership.signed_raw())) responses = await self._bma_connector.broadcast(connection.currency, bma.blockchain.membership, @@ -132,11 +132,12 @@ class DocumentsService: return result - async def certify(self, connection, password, identity): + async def certify(self, connection, secret_key, password, identity): """ Certify an other identity :param sakia.data.entities.Connection connection: the connection published + :param str secret_key: the private key salt :param str password: the private key password :param sakia.data.entities.Identity identity: the identity certified """ @@ -155,7 +156,7 @@ class DocumentsService: certification = Certification(10, connection.currency, connection.pubkey, identity.pubkey, blockUID, None) - key = SigningKey(connection.salt, password, connection.scrypt_params) + key = SigningKey(secret_key, password, connection.scrypt_params) certification.sign(identity.document(), [key]) signed_cert = certification.signed_raw(identity.document()) self._logger.debug("Certification : {0}".format(signed_cert)) @@ -197,18 +198,19 @@ class DocumentsService: result = await parse_bma_responses(responses) return result - def generate_revokation(self, connection, password): + def generate_revokation(self, connection, secret_key, password): """ Generate account revokation document for given community :param sakia.data.entities.Connection connection: The connection of the identity + :param str secret_key: The account SigningKey secret key :param str password: The account SigningKey password """ document = Revocation(10, connection.currency, connection.pubkey, "") identity = self._identities_processor.get_identity(connection.currency, connection.pubkey, connection.uid) self_cert = identity.document() - key = SigningKey(connection.salt, password, connection.scrypt_params) + key = SigningKey(secret_key, password, connection.scrypt_params) document.sign(self_cert, [key]) return document.signed_raw(self_cert) @@ -353,10 +355,11 @@ class DocumentsService: outputs, message, None) return tx - async def send_money(self, connection, password, recipient, amount, amount_base, message): + async def send_money(self, connection, secret_key, password, recipient, amount, amount_base, message): """ Send money to a given recipient in a specified community :param sakia.data.entities.Connection connection: The account salt + :param str secret_key: The account secret_key :param str password: The account password :param str recipient: The pubkey of the recipient :param int amount: The amount of money to transfer @@ -365,7 +368,7 @@ class DocumentsService: """ blockstamp = self._blockchain_processor.current_buid(connection.currency) time = self._blockchain_processor.time(connection.currency) - key = SigningKey(connection.salt, password, connection.scrypt_params) + key = SigningKey(secret_key, password, connection.scrypt_params) logging.debug("Sender pubkey:{0}".format(key.pubkey)) try: txdoc = self.prepare_tx(connection.pubkey, recipient, blockstamp, amount, amount_base, diff --git a/tests/functional/test_certification_dialog.py b/tests/functional/test_certification_dialog.py index 0dbd1e0cefa39ba431dc128ee147e76f9b4f5e97..29a0efa41d1ca3c0f28bd303099d96e914402a49 100644 --- a/tests/functional/test_certification_dialog.py +++ b/tests/functional/test_certification_dialog.py @@ -17,20 +17,25 @@ async def test_certification_init_community(application_with_one_connection, fak async def exec_test(): certification_dialog.model.connection.password = bob.password - QTest.keyClicks(certification_dialog.view.search_user.combobox_search.lineEdit(), "nothing") + QTest.keyClicks(certification_dialog.search_user.view.combobox_search.lineEdit(), "nothing") await asyncio.sleep(1) certification_dialog.search_user.view.search() await asyncio.sleep(1) assert certification_dialog.user_information.model.identity is None assert not certification_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled() - certification_dialog.view.search_user.combobox_search.lineEdit().clear() - QTest.keyClicks(certification_dialog.view.search_user.combobox_search.lineEdit(), alice.key.pubkey) + certification_dialog.search_user.view.combobox_search.lineEdit().clear() + QTest.keyClicks(certification_dialog.search_user.view.combobox_search.lineEdit(), alice.key.pubkey) await asyncio.sleep(0.1) certification_dialog.search_user.view.search() - await asyncio.sleep(0.1) - certification_dialog.search_user.view.node_selected.emit(0) await asyncio.sleep(1) + certification_dialog.search_user.view.node_selected.emit(0) + await asyncio.sleep(0.1) assert certification_dialog.user_information.model.identity.uid == "alice" + await asyncio.sleep(0.1) + assert not certification_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled() + await asyncio.sleep(0.1) + QTest.keyClicks(certification_dialog.password_input.view.edit_secret_key, bob.salt) + QTest.keyClicks(certification_dialog.password_input.view.edit_password, bob.password) assert certification_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled() QTest.mouseClick(certification_dialog.view.button_box.button(QDialogButtonBox.Ok), Qt.LeftButton) await asyncio.sleep(0.1) diff --git a/tests/functional/test_transfer_dialog.py b/tests/functional/test_transfer_dialog.py index d8a01b13a67981b255520b987d6d66faa3f954ce..f89e4024557fea531eaa6a575780d63c41745a47 100644 --- a/tests/functional/test_transfer_dialog.py +++ b/tests/functional/test_transfer_dialog.py @@ -22,6 +22,7 @@ async def test_transfer(application_with_one_connection, simple_fake_server, bob await asyncio.sleep(0.1) assert not transfer_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled() await asyncio.sleep(0.1) + QTest.keyClicks(transfer_dialog.view.password_input.edit_secret_key, bob.salt) QTest.keyClicks(transfer_dialog.view.password_input.edit_password, bob.password) assert transfer_dialog.view.button_box.button(QDialogButtonBox.Ok).isEnabled() QTest.mouseClick(transfer_dialog.view.button_box.button(QDialogButtonBox.Ok), Qt.LeftButton)