Skip to content
Snippets Groups Projects
Commit 1ff67947 authored by inso's avatar inso
Browse files

Fix identity save on connection for revokation

parent bdb13e19
No related branches found
No related tags found
No related merge requests found
...@@ -28,5 +28,4 @@ class Identity: ...@@ -28,5 +28,4 @@ class Identity:
:return: the document :return: the document
:rtype: duniterpy.documents.Identity :rtype: duniterpy.documents.Identity
""" """
return IdentityDoc(3, self.currency, self.pubkey, return IdentityDoc(3, self.currency, self.pubkey, self.uid, self.blockstamp, self.signature)
self.uid, self.blockstamp, self.signature)
...@@ -158,8 +158,7 @@ class ConnectionConfigController(QObject): ...@@ -158,8 +158,7 @@ class ConnectionConfigController(QObject):
if mode == ConnectionConfigController.REGISTER: if mode == ConnectionConfigController.REGISTER:
self.view.display_info(self.tr("Broadcasting identity...")) self.view.display_info(self.tr("Broadcasting identity..."))
self.view.stream_log("Broadcasting identity...") self.view.stream_log("Broadcasting identity...")
password = await self.password_asker.async_exec() result, connection_identity = await self.model.publish_selfcert()
result, connection_identity = await self.model.publish_selfcert(password)
if result[0]: if result[0]:
await self.view.show_success(self.model.notification()) await self.view.show_success(self.model.notification())
else: else:
...@@ -191,6 +190,9 @@ class ConnectionConfigController(QObject): ...@@ -191,6 +190,9 @@ class ConnectionConfigController(QObject):
self.view.button_next.disconnect() self.view.button_next.disconnect()
asyncio.ensure_future(self.process()) asyncio.ensure_future(self.process())
return return
finally:
if self.model.node_connector:
await self.model.node_connector.session.close()
self.accept() self.accept()
def check_key(self): def check_key(self):
......
...@@ -30,14 +30,9 @@ class ConnectionConfigModel(QObject): ...@@ -30,14 +30,9 @@ class ConnectionConfigModel(QObject):
self.identities_processor = identities_processor self.identities_processor = identities_processor
async def create_connection(self, server, port, secured): async def create_connection(self, server, port, secured):
session = aiohttp.ClientSession() self.node_connector = await NodeConnector.from_address(None, secured, server, port)
try:
self.node_connector = await NodeConnector.from_address(None, secured, server, port, session)
self.connection = Connection(self.node_connector.node.currency, "", "") self.connection = Connection(self.node_connector.node.currency, "", "")
self.node_connector.node.state = Node.ONLINE self.node_connector.node.state = Node.ONLINE
except:
session.close()
raise
def notification(self): def notification(self):
return self.app.parameters.notifications return self.app.parameters.notifications
...@@ -50,11 +45,13 @@ class ConnectionConfigModel(QObject): ...@@ -50,11 +45,13 @@ class ConnectionConfigModel(QObject):
self.connection.N = scrypt_params.N self.connection.N = scrypt_params.N
self.connection.r = scrypt_params.r self.connection.r = scrypt_params.r
self.connection.p = scrypt_params.p self.connection.p = scrypt_params.p
self.connection.password = password
self.connection.pubkey = SigningKey(self.connection.salt, password, scrypt_params).pubkey self.connection.pubkey = SigningKey(self.connection.salt, password, scrypt_params).pubkey
def insert_or_update_connection(self): def insert_or_update_connection(self):
ConnectionsProcessor(self.app.db.connections_repo).commit_connection(self.connection) ConnectionsProcessor(self.app.db.connections_repo).commit_connection(self.connection)
NodesProcessor(self.app.db.nodes_repo).commit_node(self.node_connector.node) NodesProcessor(self.app.db.nodes_repo).commit_node(self.node_connector.node)
self.node_connector.session.close()
def insert_or_update_identity(self, identity): def insert_or_update_identity(self, identity):
self.identities_processor.insert_or_update_identity(identity) self.identities_processor.insert_or_update_identity(identity)
...@@ -106,11 +103,11 @@ class ConnectionConfigModel(QObject): ...@@ -106,11 +103,11 @@ class ConnectionConfigModel(QObject):
transactions_processor = TransactionsProcessor.instanciate(self.app) transactions_processor = TransactionsProcessor.instanciate(self.app)
await transactions_processor.initialize_transactions(identity, log_stream) await transactions_processor.initialize_transactions(identity, log_stream)
async def publish_selfcert(self, password): async def publish_selfcert(self):
"""" """"
Publish the self certification of the connection identity Publish the self certification of the connection identity
""" """
return await self.app.documents_service.broadcast_identity(self.connection, password) return await self.app.documents_service.broadcast_identity(self.connection, self.connection.password)
async def check_registered(self): async def check_registered(self):
""" """
...@@ -120,9 +117,6 @@ class ConnectionConfigModel(QObject): ...@@ -120,9 +117,6 @@ class ConnectionConfigModel(QObject):
identity = Identity(self.connection.currency, self.connection.pubkey, self.connection.uid) identity = Identity(self.connection.currency, self.connection.pubkey, self.connection.uid)
found_identity = Identity(self.connection.currency, self.connection.pubkey, self.connection.uid) found_identity = Identity(self.connection.currency, self.connection.pubkey, self.connection.uid)
def _parse_uid_certifiers(data):
return identity.uid == data['uid'], identity.uid, data['uid']
def _parse_uid_lookup(data): def _parse_uid_lookup(data):
timestamp = BlockUID.empty() timestamp = BlockUID.empty()
found_uid = "" found_uid = ""
...@@ -134,11 +128,9 @@ class ConnectionConfigModel(QObject): ...@@ -134,11 +128,9 @@ class ConnectionConfigModel(QObject):
timestamp = uid_data["meta"]["timestamp"] timestamp = uid_data["meta"]["timestamp"]
found_uid = uid_data["uid"] found_uid = uid_data["uid"]
found_identity.timestamp = timestamp # We save the timestamp in the found identity found_identity.timestamp = timestamp # We save the timestamp in the found identity
found_identity.signature = uid_data["self"]
return identity.uid == found_uid, identity.uid, found_uid return identity.uid == found_uid, identity.uid, found_uid
def _parse_pubkey_certifiers(data):
return identity.pubkey == data['pubkey'], identity.pubkey, data['pubkey']
def _parse_pubkey_lookup(data): def _parse_pubkey_lookup(data):
timestamp = BlockUID.empty() timestamp = BlockUID.empty()
found_uid = "" found_uid = ""
...@@ -150,6 +142,7 @@ class ConnectionConfigModel(QObject): ...@@ -150,6 +142,7 @@ class ConnectionConfigModel(QObject):
timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"])
found_uid = uid_data["uid"] found_uid = uid_data["uid"]
found_identity.timestamp = timestamp # We save the timestamp in the found identity found_identity.timestamp = timestamp # We save the timestamp in the found identity
found_identity.signature = uid_data["self"]
if found_uid == identity.uid: if found_uid == identity.uid:
found_result = result['pubkey'], found_uid found_result = result['pubkey'], found_uid
if found_result[1] == identity.uid: if found_result[1] == identity.uid:
...@@ -157,27 +150,23 @@ class ConnectionConfigModel(QObject): ...@@ -157,27 +150,23 @@ class ConnectionConfigModel(QObject):
else: else:
return False, identity.pubkey, None return False, identity.pubkey, None
async def execute_requests(parsers, search): async def execute_requests(parser, search):
tries = 0 tries = 0
request = bma.wot.certifiers_of
nonlocal registered nonlocal registered
for endpoint in [e for e in self.node_connector.node.endpoints if isinstance(e, BMAEndpoint)]: for endpoint in [e for e in self.node_connector.node.endpoints if isinstance(e, BMAEndpoint)]:
if not registered[0] and not registered[2]: if not registered[0] and not registered[2]:
try: try:
data = await self.node_connector.safe_request(endpoint, request, req_args={'search': search}) data = await self.node_connector.safe_request(endpoint, bma.wot.lookup,
req_args={'search': search})
if data: if data:
registered = parsers[request](data) registered = parser(data)
tries += 1 tries += 1
except errors.DuniterError as e: except errors.DuniterError as e:
if e.ucode in (errors.NO_MEMBER_MATCHING_PUB_OR_UID, if e.ucode in (errors.NO_MEMBER_MATCHING_PUB_OR_UID,
e.ucode == errors.NO_MATCHING_IDENTITY): e.ucode == errors.NO_MATCHING_IDENTITY):
if request == bma.wot.certifiers_of:
request = bma.wot.lookup
tries = 0
else:
tries += 1 tries += 1
else: else:
tries += 1 raise
else: else:
break break
...@@ -187,20 +176,12 @@ class ConnectionConfigModel(QObject): ...@@ -187,20 +176,12 @@ class ConnectionConfigModel(QObject):
registered = (False, identity.uid, None) registered = (False, identity.uid, None)
# We execute search based on pubkey # We execute search based on pubkey
# And look for account UID # And look for account UID
uid_parsers = { await execute_requests(_parse_uid_lookup, identity.pubkey)
bma.wot.certifiers_of: _parse_uid_certifiers,
bma.wot.lookup: _parse_uid_lookup
}
await execute_requests(uid_parsers, identity.pubkey)
# If the uid wasn't found when looking for the pubkey # If the uid wasn't found when looking for the pubkey
# We look for the uid and check for the pubkey # We look for the uid and check for the pubkey
if not registered[0] and not registered[2]: if not registered[0] and not registered[2]:
pubkey_parsers = { await execute_requests(_parse_pubkey_lookup, identity.uid)
bma.wot.certifiers_of: _parse_pubkey_certifiers,
bma.wot.lookup: _parse_pubkey_lookup
}
await execute_requests(pubkey_parsers, identity.uid)
return registered, found_identity return registered, found_identity
import logging
from PyQt5.QtCore import Qt, QObject from PyQt5.QtCore import Qt, QObject
from PyQt5.QtWidgets import QDialog, QMessageBox from PyQt5.QtWidgets import QDialog, QMessageBox
...@@ -9,6 +7,7 @@ from sakia.gui.dialogs.certification.controller import CertificationController ...@@ -9,6 +7,7 @@ from sakia.gui.dialogs.certification.controller import CertificationController
from sakia.gui.dialogs.transfer.controller import TransferController from sakia.gui.dialogs.transfer.controller import TransferController
from sakia.gui.widgets import toast from sakia.gui.widgets import toast
from sakia.gui.widgets.dialogs import QAsyncMessageBox, QAsyncFileDialog, dialog_async_exec from sakia.gui.widgets.dialogs import QAsyncMessageBox, QAsyncFileDialog, dialog_async_exec
from sakia.gui.password_asker import PasswordAskerDialog
from .model import ToolbarModel from .model import ToolbarModel
from .view import ToolbarView from .view import ToolbarView
...@@ -29,7 +28,6 @@ class ToolbarController(QObject): ...@@ -29,7 +28,6 @@ class ToolbarController(QObject):
self.model = model self.model = model
self.view.button_certification.clicked.connect(self.open_certification_dialog) self.view.button_certification.clicked.connect(self.open_certification_dialog)
self.view.button_send_money.clicked.connect(self.open_transfer_money_dialog) self.view.button_send_money.clicked.connect(self.open_transfer_money_dialog)
self.view.action_gen_revokation.triggered.connect(self.action_save_revokation)
self.view.action_publish_uid.triggered.connect(self.publish_uid) self.view.action_publish_uid.triggered.connect(self.publish_uid)
self.view.button_membership.clicked.connect(self.send_membership_demand) self.view.button_membership.clicked.connect(self.send_membership_demand)
self.view.action_add_connection.triggered.connect(self.open_add_connection_dialog) self.view.action_add_connection.triggered.connect(self.open_add_connection_dialog)
...@@ -53,31 +51,6 @@ class ToolbarController(QObject): ...@@ -53,31 +51,6 @@ class ToolbarController(QObject):
self.view.button_send_money.setEnabled(enabled) self.view.button_send_money.setEnabled(enabled)
self.view.button_membership.setEnabled(enabled) self.view.button_membership.setEnabled(enabled)
@asyncify
async def action_save_revokation(self, checked=False):
password = await self.password_asker.async_exec()
if self.password_asker.result() == QDialog.Rejected:
return
raw_document = await self.account.generate_revokation(self.community, password)
# Testable way of using a QFileDialog
selected_files = await QAsyncFileDialog.get_save_filename(self, self.tr("Save a revokation document"),
"", self.tr("All text files (*.txt)"))
if selected_files:
path = selected_files[0]
if not path.endswith('.txt'):
path = "{0}.txt".format(path)
with open(path, 'w') as save_file:
save_file.write(raw_document)
dialog = QMessageBox(QMessageBox.Information, self.tr("Revokation file"),
self.tr("""<div>Your revokation document has been saved.</div>
<div><b>Please keep it in a safe place.</b></div>
The publication of this document will remove your identity from the network.</p>"""), QMessageBox.Ok,
self)
dialog.setTextFormat(Qt.RichText)
await dialog_async_exec(dialog)
@asyncify @asyncify
async def send_membership_demand(self, checked=False): async def send_membership_demand(self, checked=False):
password = await self.password_asker.async_exec() password = await self.password_asker.async_exec()
......
...@@ -23,11 +23,6 @@ class ToolbarView(QFrame, Ui_SakiaToolbar): ...@@ -23,11 +23,6 @@ class ToolbarView(QFrame, Ui_SakiaToolbar):
tool_menu.addAction(self.action_publish_uid) tool_menu.addAction(self.action_publish_uid)
tool_menu.addAction(self.action_revoke_uid) tool_menu.addAction(self.action_revoke_uid)
menu_advanced = QMenu(self.tr("Advanced"), self.toolbutton_menu)
self.action_gen_revokation = QAction(self.tr("Save revokation document"), menu_advanced)
menu_advanced.addAction(self.action_gen_revokation)
tool_menu.addMenu(menu_advanced)
menu_options = QMenu(self.tr("Options"), self.toolbutton_menu) menu_options = QMenu(self.tr("Options"), self.toolbutton_menu)
self.action_add_connection = QAction(self.tr("Add a connection"), menu_options) self.action_add_connection = QAction(self.tr("Add a connection"), menu_options)
menu_options.addAction(self.action_add_connection) menu_options.addAction(self.action_add_connection)
......
from .model import NavigationModel from .model import NavigationModel
from .view import NavigationView from .view import NavigationView
from sakia.models.generic_tree import GenericTreeModel
from .txhistory.controller import TxHistoryController from .txhistory.controller import TxHistoryController
from .homescreen.controller import HomeScreenController from .homescreen.controller import HomeScreenController
from .network.controller import NetworkController from .network.controller import NetworkController
...@@ -7,7 +8,12 @@ from .identities.controller import IdentitiesController ...@@ -7,7 +8,12 @@ from .identities.controller import IdentitiesController
from .informations.controller import InformationsController from .informations.controller import InformationsController
from .graphs.wot.controller import WotController from .graphs.wot.controller import WotController
from sakia.data.entities import Connection from sakia.data.entities import Connection
from PyQt5.QtCore import pyqtSignal, QObject from PyQt5.QtCore import pyqtSignal, QObject, Qt
from PyQt5.QtWidgets import QMenu, QAction, QMessageBox, QDialog
from PyQt5.QtGui import QCursor
from sakia.decorators import asyncify
from sakia.gui.password_asker import PasswordAskerDialog
from sakia.gui.widgets.dialogs import QAsyncFileDialog, dialog_async_exec
class NavigationController(QObject): class NavigationController(QObject):
...@@ -36,6 +42,8 @@ class NavigationController(QObject): ...@@ -36,6 +42,8 @@ class NavigationController(QObject):
'Wot': WotController 'Wot': WotController
} }
self.view.current_view_changed.connect(self.handle_view_change) self.view.current_view_changed.connect(self.handle_view_change)
self.view.setContextMenuPolicy(Qt.CustomContextMenu)
self.view.customContextMenuRequested.connect(self.tree_context_menu)
@classmethod @classmethod
def create(cls, parent, app): def create(cls, parent, app):
...@@ -89,3 +97,41 @@ class NavigationController(QObject): ...@@ -89,3 +97,41 @@ class NavigationController(QObject):
raw_node = self.model.add_connection(connection) raw_node = self.model.add_connection(connection)
self.view.add_connection(raw_node) self.view.add_connection(raw_node)
self.parse_node(raw_node) self.parse_node(raw_node)
def tree_context_menu(self, point):
mapped = self.view.tree_view.mapFromParent(point)
index = self.view.tree_view.indexAt(mapped)
raw_data = self.view.tree_view.model().data(index, GenericTreeModel.ROLE_RAW_DATA)
if raw_data and raw_data["component"] == "Informations":
menu = QMenu(self.view)
action_gen_revokation = QAction(self.tr("Save revokation document"), menu)
menu.addAction(action_gen_revokation)
action_gen_revokation.triggered.connect(lambda c:
self.action_save_revokation(raw_data['misc']['connection']))
# Show the context menu.
menu.popup(QCursor.pos())
def action_save_revokation(self, connection):
password = PasswordAskerDialog(connection).exec()
if not password:
return
raw_document = self.model.generate_revokation(connection, password)
# Testable way of using a QFileDialog
selected_files = QAsyncFileDialog.get_save_filename(self, self.tr("Save a revokation document"),
"", self.tr("All text files (*.txt)"))
if selected_files:
path = selected_files[0]
if not path.endswith('.txt'):
path = "{0}.txt".format(path)
with open(path, 'w') as save_file:
save_file.write(raw_document)
dialog = QMessageBox(QMessageBox.Information, self.tr("Revokation file"),
self.tr("""<div>Your revokation document has been saved.</div>
<div><b>Please keep it in a safe place.</b></div>
The publication of this document will remove your identity from the network.</p>"""), QMessageBox.Ok,
self)
dialog.setTextFormat(Qt.RichText)
dialog.exec()
...@@ -38,7 +38,7 @@ class NavigationModel(QObject): ...@@ -38,7 +38,7 @@ class NavigationModel(QObject):
def create_node(self, connection): def create_node(self, connection):
return { return {
'title': connection.currency, 'title': connection.title(),
'component': "Informations", 'component': "Informations",
'dependencies': { 'dependencies': {
'blockchain_service': self.app.blockchain_services[connection.currency], 'blockchain_service': self.app.blockchain_services[connection.currency],
...@@ -124,3 +124,6 @@ class NavigationModel(QObject): ...@@ -124,3 +124,6 @@ class NavigationModel(QObject):
return self._current_data['misc'].get('connection', None) return self._current_data['misc'].get('connection', None)
else: else:
return None return None
def generate_revokation(self, connection, password):
return self.app.documents_service.generate_revokation(connection, password)
\ No newline at end of file
...@@ -28,6 +28,12 @@ ...@@ -28,6 +28,12 @@
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
</property> </property>
......
...@@ -139,7 +139,7 @@ class DocumentsService: ...@@ -139,7 +139,7 @@ class DocumentsService:
certification = Certification(6, connection.currency, certification = Certification(6, connection.currency,
connection.pubkey, identity.pubkey, blockUID, None) connection.pubkey, identity.pubkey, blockUID, None)
key = SigningKey(connection.salt, connection.password, connection.scrypt_params) key = SigningKey(connection.salt, password, connection.scrypt_params)
certification.sign(identity.document(), [key]) certification.sign(identity.document(), [key])
signed_cert = certification.signed_raw(identity.document()) signed_cert = certification.signed_raw(identity.document())
self._logger.debug("Certification : {0}".format(signed_cert)) self._logger.debug("Certification : {0}".format(signed_cert))
...@@ -190,19 +190,18 @@ class DocumentsService: ...@@ -190,19 +190,18 @@ class DocumentsService:
await r.release() await r.release()
return result return result
async def generate_revokation(self, currency, identity, salt, password): def generate_revokation(self, connection, password):
""" """
Generate account revokation document for given community Generate account revokation document for given community
:param str currency: The currency of the identity :param sakia.data.entities.Connection connection: The connection of the identity
:param sakia.data.entities.IdentityDoc identity: The certified identity
:param str salt: The account SigningKey salt
:param str password: The account SigningKey password :param str password: The account SigningKey password
""" """
document = Revocation(PROTOCOL_VERSION, currency, identity.pubkey, "") document = Revocation(2, connection.currency, connection.pubkey, "")
self_cert = identity.document() identity = self._identities_processor.get_written(connection.currency, connection.pubkey)
self_cert = identity[0].document()
key = SigningKey(salt, password) key = SigningKey(connection.salt, password, connection.scrypt_params)
document.sign(self_cert, [key]) document.sign(self_cert, [key])
return document.signed_raw(self_cert) return document.signed_raw(self_cert)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment