Mercurial > libervia-backend
comparison libervia/backend/memory/encryption.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | 4b842c1fb686 |
children | e9971a4b0627 |
comparison
equal
deleted
inserted
replaced
4269:64a85ce8be70 | 4270:0d7bb4df2343 |
---|---|
21 from functools import partial | 21 from functools import partial |
22 from typing import Optional | 22 from typing import Optional |
23 from twisted.words.protocols.jabber import jid | 23 from twisted.words.protocols.jabber import jid |
24 from twisted.internet import defer | 24 from twisted.internet import defer |
25 from twisted.python import failure | 25 from twisted.python import failure |
26 from libervia.backend.core.core_types import EncryptionPlugin, EncryptionSession, MessageData | 26 from libervia.backend.core.core_types import ( |
27 EncryptionPlugin, | |
28 EncryptionSession, | |
29 MessageData, | |
30 ) | |
27 from libervia.backend.core.i18n import D_, _ | 31 from libervia.backend.core.i18n import D_, _ |
28 from libervia.backend.core.constants import Const as C | 32 from libervia.backend.core.constants import Const as C |
29 from libervia.backend.core import exceptions | 33 from libervia.backend.core import exceptions |
30 from libervia.backend.core.log import getLogger | 34 from libervia.backend.core.log import getLogger |
31 from libervia.backend.tools.common import data_format | 35 from libervia.backend.tools.common import data_format |
36 log = getLogger(__name__) | 40 log = getLogger(__name__) |
37 | 41 |
38 | 42 |
39 class EncryptionHandler: | 43 class EncryptionHandler: |
40 """Class to handle encryption sessions for a client""" | 44 """Class to handle encryption sessions for a client""" |
45 | |
41 plugins = [] # plugin able to encrypt messages | 46 plugins = [] # plugin able to encrypt messages |
42 | 47 |
43 def __init__(self, client): | 48 def __init__(self, client): |
44 self.client = client | 49 self.client = client |
45 self._sessions = {} # bare_jid ==> encryption_data | 50 self._sessions = {} # bare_jid ==> encryption_data |
46 self._stored_session = persistent.PersistentDict( | 51 self._stored_session = persistent.PersistentDict( |
47 "core:encryption", profile=client.profile) | 52 "core:encryption", profile=client.profile |
53 ) | |
48 | 54 |
49 @property | 55 @property |
50 def host(self): | 56 def host(self): |
51 return self.client.host_app | 57 return self.client.host_app |
52 | 58 |
61 if start_d_list: | 67 if start_d_list: |
62 result = await defer.DeferredList(start_d_list) | 68 result = await defer.DeferredList(start_d_list) |
63 for idx, (success, err) in enumerate(result): | 69 for idx, (success, err) in enumerate(result): |
64 if not success: | 70 if not success: |
65 entity_jid_s, namespace = list(self._stored_session.items())[idx] | 71 entity_jid_s, namespace = list(self._stored_session.items())[idx] |
66 log.warning(_( | 72 log.warning( |
67 "Could not restart {namespace!r} encryption with {entity}: {err}" | 73 _( |
68 ).format(namespace=namespace, entity=entity_jid_s, err=err)) | 74 "Could not restart {namespace!r} encryption with {entity}: {err}" |
75 ).format(namespace=namespace, entity=entity_jid_s, err=err) | |
76 ) | |
69 log.info(_("encryption sessions restored")) | 77 log.info(_("encryption sessions restored")) |
70 | 78 |
71 @classmethod | 79 @classmethod |
72 def register_plugin(cls, plg_instance, name, namespace, priority=0, directed=False): | 80 def register_plugin(cls, plg_instance, name, namespace, priority=0, directed=False): |
73 """Register a plugin handling an encryption algorithm | 81 """Register a plugin handling an encryption algorithm |
103 plugin = EncryptionPlugin( | 111 plugin = EncryptionPlugin( |
104 instance=plg_instance, | 112 instance=plg_instance, |
105 name=name, | 113 name=name, |
106 namespace=namespace, | 114 namespace=namespace, |
107 priority=priority, | 115 priority=priority, |
108 directed=directed) | 116 directed=directed, |
117 ) | |
109 cls.plugins.append(plugin) | 118 cls.plugins.append(plugin) |
110 cls.plugins.sort(key=lambda p: p.priority) | 119 cls.plugins.sort(key=lambda p: p.priority) |
111 log.info(_("Encryption plugin registered: {name}").format(name=name)) | 120 log.info(_("Encryption plugin registered: {name}").format(name=name)) |
112 | 121 |
113 @classmethod | 122 @classmethod |
117 @classmethod | 126 @classmethod |
118 def get_plugin(cls, namespace): | 127 def get_plugin(cls, namespace): |
119 try: | 128 try: |
120 return next(p for p in cls.plugins if p.namespace == namespace) | 129 return next(p for p in cls.plugins if p.namespace == namespace) |
121 except StopIteration: | 130 except StopIteration: |
122 raise exceptions.NotFound(_( | 131 raise exceptions.NotFound( |
123 "Can't find requested encryption plugin: {namespace}").format( | 132 _("Can't find requested encryption plugin: {namespace}").format( |
124 namespace=namespace)) | 133 namespace=namespace |
134 ) | |
135 ) | |
125 | 136 |
126 @classmethod | 137 @classmethod |
127 def get_namespaces(cls): | 138 def get_namespaces(cls): |
128 """Get available plugin namespaces""" | 139 """Get available plugin namespaces""" |
129 return {p.namespace for p in cls.getPlugins()} | 140 return {p.namespace for p in cls.getPlugins()} |
137 @raise exceptions.NotFound: there is not encryption plugin of this name | 148 @raise exceptions.NotFound: there is not encryption plugin of this name |
138 """ | 149 """ |
139 for p in cls.plugins: | 150 for p in cls.plugins: |
140 if p.name.lower() == name.lower(): | 151 if p.name.lower() == name.lower(): |
141 return p.namespace | 152 return p.namespace |
142 raise exceptions.NotFound(_( | 153 raise exceptions.NotFound( |
143 "Can't find a plugin with the name \"{name}\".".format( | 154 _('Can\'t find a plugin with the name "{name}".'.format(name=name)) |
144 name=name))) | 155 ) |
145 | 156 |
146 def get_bridge_data(self, session): | 157 def get_bridge_data(self, session): |
147 """Retrieve session data serialized for bridge. | 158 """Retrieve session data serialized for bridge. |
148 | 159 |
149 @param session(dict): encryption session | 160 @param session(dict): encryption session |
150 @return (unicode): serialized data for bridge | 161 @return (unicode): serialized data for bridge |
151 """ | 162 """ |
152 if session is None: | 163 if session is None: |
153 return '' | 164 return "" |
154 plugin = session['plugin'] | 165 plugin = session["plugin"] |
155 bridge_data = {'name': plugin.name, | 166 bridge_data = {"name": plugin.name, "namespace": plugin.namespace} |
156 'namespace': plugin.namespace} | 167 if "directed_devices" in session: |
157 if 'directed_devices' in session: | 168 bridge_data["directed_devices"] = session["directed_devices"] |
158 bridge_data['directed_devices'] = session['directed_devices'] | |
159 | 169 |
160 return data_format.serialise(bridge_data) | 170 return data_format.serialise(bridge_data) |
161 | 171 |
162 async def _start_encryption(self, plugin, entity): | 172 async def _start_encryption(self, plugin, entity): |
163 """Start encryption with a plugin | 173 """Start encryption with a plugin |
203 None to select automatically an algorithm | 213 None to select automatically an algorithm |
204 @param replace(bool): if True and an encrypted session already exists, | 214 @param replace(bool): if True and an encrypted session already exists, |
205 it will be replaced by the new one | 215 it will be replaced by the new one |
206 """ | 216 """ |
207 if not self.plugins: | 217 if not self.plugins: |
208 raise exceptions.NotFound(_("No encryption plugin is registered, " | 218 raise exceptions.NotFound( |
209 "an encryption session can't be started")) | 219 _( |
220 "No encryption plugin is registered, " | |
221 "an encryption session can't be started" | |
222 ) | |
223 ) | |
210 | 224 |
211 if namespace is None: | 225 if namespace is None: |
212 plugin = self.plugins[0] | 226 plugin = self.plugins[0] |
213 else: | 227 else: |
214 plugin = self.get_plugin(namespace) | 228 plugin = self.get_plugin(namespace) |
216 bare_jid = entity.userhostJID() | 230 bare_jid = entity.userhostJID() |
217 if bare_jid in self._sessions: | 231 if bare_jid in self._sessions: |
218 # we have already an encryption session with this contact | 232 # we have already an encryption session with this contact |
219 former_plugin = self._sessions[bare_jid]["plugin"] | 233 former_plugin = self._sessions[bare_jid]["plugin"] |
220 if former_plugin.namespace == namespace: | 234 if former_plugin.namespace == namespace: |
221 log.info(_("Session with {bare_jid} is already encrypted with {name}. " | 235 log.info( |
222 "Nothing to do.").format( | 236 _( |
223 bare_jid=bare_jid, name=former_plugin.name)) | 237 "Session with {bare_jid} is already encrypted with {name}. " |
238 "Nothing to do." | |
239 ).format(bare_jid=bare_jid, name=former_plugin.name) | |
240 ) | |
224 return | 241 return |
225 | 242 |
226 if replace: | 243 if replace: |
227 # there is a conflict, but replacement is requested | 244 # there is a conflict, but replacement is requested |
228 # so we stop previous encryption to use new one | 245 # so we stop previous encryption to use new one |
229 del self._sessions[bare_jid] | 246 del self._sessions[bare_jid] |
230 await self._stop_encryption(former_plugin, entity) | 247 await self._stop_encryption(former_plugin, entity) |
231 else: | 248 else: |
232 msg = (_("Session with {bare_jid} is already encrypted with {name}. " | 249 msg = _( |
233 "Please stop encryption session before changing algorithm.") | 250 "Session with {bare_jid} is already encrypted with {name}. " |
234 .format(bare_jid=bare_jid, name=plugin.name)) | 251 "Please stop encryption session before changing algorithm." |
252 ).format(bare_jid=bare_jid, name=plugin.name) | |
235 log.warning(msg) | 253 log.warning(msg) |
236 raise exceptions.ConflictError(msg) | 254 raise exceptions.ConflictError(msg) |
237 | 255 |
238 data = {"plugin": plugin} | 256 data = {"plugin": plugin} |
239 if plugin.directed: | 257 if plugin.directed: |
240 if not entity.resource: | 258 if not entity.resource: |
241 entity.resource = self.host.memory.main_resource_get(self.client, entity) | 259 entity.resource = self.host.memory.main_resource_get(self.client, entity) |
242 if not entity.resource: | 260 if not entity.resource: |
243 raise exceptions.NotFound( | 261 raise exceptions.NotFound( |
244 _("No resource found for {destinee}, can't encrypt with {name}") | 262 _( |
245 .format(destinee=entity.full(), name=plugin.name)) | 263 "No resource found for {destinee}, can't encrypt with {name}" |
246 log.info(_("No resource specified to encrypt with {name}, using " | 264 ).format(destinee=entity.full(), name=plugin.name) |
247 "{destinee}.").format(destinee=entity.full(), | 265 ) |
248 name=plugin.name)) | 266 log.info( |
267 _( | |
268 "No resource specified to encrypt with {name}, using " | |
269 "{destinee}." | |
270 ).format(destinee=entity.full(), name=plugin.name) | |
271 ) | |
249 # indicate that we encrypt only for some devices | 272 # indicate that we encrypt only for some devices |
250 directed_devices = data['directed_devices'] = [entity.resource] | 273 directed_devices = data["directed_devices"] = [entity.resource] |
251 elif entity.resource: | 274 elif entity.resource: |
252 raise ValueError(_("{name} encryption must be used with bare jids.")) | 275 raise ValueError(_("{name} encryption must be used with bare jids.")) |
253 | 276 |
254 await self._start_encryption(plugin, entity) | 277 await self._start_encryption(plugin, entity) |
255 self._sessions[entity.userhostJID()] = data | 278 self._sessions[entity.userhostJID()] = data |
256 log.info(_("Encryption session has been set for {entity_jid} with " | 279 log.info( |
257 "{encryption_name}").format( | 280 _( |
258 entity_jid=entity.full(), encryption_name=plugin.name)) | 281 "Encryption session has been set for {entity_jid} with " |
282 "{encryption_name}" | |
283 ).format(entity_jid=entity.full(), encryption_name=plugin.name) | |
284 ) | |
259 self.host.bridge.message_encryption_started( | 285 self.host.bridge.message_encryption_started( |
260 entity.full(), | 286 entity.full(), self.get_bridge_data(data), self.client.profile |
261 self.get_bridge_data(data), | 287 ) |
262 self.client.profile) | 288 msg = D_( |
263 msg = D_("Encryption session started: your messages with {destinee} are " | 289 "Encryption session started: your messages with {destinee} are " |
264 "now end to end encrypted using {name} algorithm.").format( | 290 "now end to end encrypted using {name} algorithm." |
265 destinee=entity.full(), name=plugin.name) | 291 ).format(destinee=entity.full(), name=plugin.name) |
266 directed_devices = data.get('directed_devices') | 292 directed_devices = data.get("directed_devices") |
267 if directed_devices: | 293 if directed_devices: |
268 msg += "\n" + D_("Message are encrypted only for {nb_devices} device(s): " | 294 msg += "\n" + D_( |
269 "{devices_list}.").format( | 295 "Message are encrypted only for {nb_devices} device(s): " |
270 nb_devices=len(directed_devices), | 296 "{devices_list}." |
271 devices_list = ', '.join(directed_devices)) | 297 ).format( |
298 nb_devices=len(directed_devices), devices_list=", ".join(directed_devices) | |
299 ) | |
272 | 300 |
273 self.client.feedback(bare_jid, msg) | 301 self.client.feedback(bare_jid, msg) |
274 | 302 |
275 async def stop(self, entity, namespace=None): | 303 async def stop(self, entity, namespace=None): |
276 """Stop an encryption session with an entity | 304 """Stop an encryption session with an entity |
281 when specified, used to check that we stop the right encryption session | 309 when specified, used to check that we stop the right encryption session |
282 """ | 310 """ |
283 session = self.getSession(entity.userhostJID()) | 311 session = self.getSession(entity.userhostJID()) |
284 if not session: | 312 if not session: |
285 raise failure.Failure( | 313 raise failure.Failure( |
286 exceptions.NotFound(_("There is no encryption session with this " | 314 exceptions.NotFound( |
287 "entity."))) | 315 _("There is no encryption session with this " "entity.") |
288 plugin = session['plugin'] | 316 ) |
317 ) | |
318 plugin = session["plugin"] | |
289 if namespace is not None and plugin.namespace != namespace: | 319 if namespace is not None and plugin.namespace != namespace: |
290 raise exceptions.InternalError(_( | 320 raise exceptions.InternalError( |
291 "The encryption session is not run with the expected plugin: encrypted " | 321 _( |
292 "with {current_name} and was expecting {expected_name}").format( | 322 "The encryption session is not run with the expected plugin: encrypted " |
293 current_name=session['plugin'].namespace, | 323 "with {current_name} and was expecting {expected_name}" |
294 expected_name=namespace)) | 324 ).format( |
325 current_name=session["plugin"].namespace, expected_name=namespace | |
326 ) | |
327 ) | |
295 if entity.resource: | 328 if entity.resource: |
296 try: | 329 try: |
297 directed_devices = session['directed_devices'] | 330 directed_devices = session["directed_devices"] |
298 except KeyError: | 331 except KeyError: |
299 raise exceptions.NotFound(_( | 332 raise exceptions.NotFound( |
300 "There is a session for the whole entity (i.e. all devices of the " | 333 _( |
301 "entity), not a directed one. Please use bare jid if you want to " | 334 "There is a session for the whole entity (i.e. all devices of the " |
302 "stop the whole encryption with this entity.")) | 335 "entity), not a directed one. Please use bare jid if you want to " |
336 "stop the whole encryption with this entity." | |
337 ) | |
338 ) | |
303 | 339 |
304 try: | 340 try: |
305 directed_devices.remove(entity.resource) | 341 directed_devices.remove(entity.resource) |
306 except ValueError: | 342 except ValueError: |
307 raise exceptions.NotFound(_("There is no directed session with this " | 343 raise exceptions.NotFound( |
308 "entity.")) | 344 _("There is no directed session with this " "entity.") |
345 ) | |
309 else: | 346 else: |
310 if not directed_devices: | 347 if not directed_devices: |
311 # if we have no more directed device sessions, | 348 # if we have no more directed device sessions, |
312 # we stop the whole session | 349 # we stop the whole session |
313 # see comment below for deleting session before stopping encryption | 350 # see comment below for deleting session before stopping encryption |
317 # plugin's stop_encryption may call stop again (that's the case with OTR) | 354 # plugin's stop_encryption may call stop again (that's the case with OTR) |
318 # so we need to remove plugin from session before calling self._stop_encryption | 355 # so we need to remove plugin from session before calling self._stop_encryption |
319 del self._sessions[entity.userhostJID()] | 356 del self._sessions[entity.userhostJID()] |
320 await self._stop_encryption(plugin, entity) | 357 await self._stop_encryption(plugin, entity) |
321 | 358 |
322 log.info(_("encryption session stopped with entity {entity}").format( | 359 log.info( |
323 entity=entity.full())) | 360 _("encryption session stopped with entity {entity}").format( |
361 entity=entity.full() | |
362 ) | |
363 ) | |
324 self.host.bridge.message_encryption_stopped( | 364 self.host.bridge.message_encryption_stopped( |
325 entity.full(), | 365 entity.full(), |
326 {'name': plugin.name, | 366 { |
327 'namespace': plugin.namespace, | 367 "name": plugin.name, |
368 "namespace": plugin.namespace, | |
328 }, | 369 }, |
329 self.client.profile) | 370 self.client.profile, |
330 msg = D_("Encryption session finished: your messages with {destinee} are " | 371 ) |
331 "NOT end to end encrypted anymore.\nYour server administrators or " | 372 msg = D_( |
332 "{destinee} server administrators will be able to read them.").format( | 373 "Encryption session finished: your messages with {destinee} are " |
333 destinee=entity.full()) | 374 "NOT end to end encrypted anymore.\nYour server administrators or " |
375 "{destinee} server administrators will be able to read them." | |
376 ).format(destinee=entity.full()) | |
334 | 377 |
335 self.client.feedback(entity, msg) | 378 self.client.feedback(entity, msg) |
336 | 379 |
337 def getSession(self, entity: jid.JID) -> Optional[EncryptionSession]: | 380 def getSession(self, entity: jid.JID) -> Optional[EncryptionSession]: |
338 """Get encryption session for this contact | 381 """Get encryption session for this contact |
373 """ | 416 """ |
374 if namespace is None: | 417 if namespace is None: |
375 session = self.getSession(entity_jid) | 418 session = self.getSession(entity_jid) |
376 if not session: | 419 if not session: |
377 raise exceptions.NotFound( | 420 raise exceptions.NotFound( |
378 "No encryption session currently active for {entity_jid}" | 421 "No encryption session currently active for {entity_jid}".format( |
379 .format(entity_jid=entity_jid.full())) | 422 entity_jid=entity_jid.full() |
380 plugin = session['plugin'] | 423 ) |
424 ) | |
425 plugin = session["plugin"] | |
381 else: | 426 else: |
382 plugin = self.get_plugin(namespace) | 427 plugin = self.get_plugin(namespace) |
383 try: | 428 try: |
384 get_trust_ui = plugin.instance.get_trust_ui | 429 get_trust_ui = plugin.instance.get_trust_ui |
385 except AttributeError: | 430 except AttributeError: |
386 raise NotImplementedError( | 431 raise NotImplementedError( |
387 "Encryption plugin doesn't handle trust management UI") | 432 "Encryption plugin doesn't handle trust management UI" |
433 ) | |
388 else: | 434 else: |
389 return utils.as_deferred(get_trust_ui, self.client, entity_jid) | 435 return utils.as_deferred(get_trust_ui, self.client, entity_jid) |
390 | 436 |
391 ## Menus ## | 437 ## Menus ## |
392 | 438 |
393 @classmethod | 439 @classmethod |
394 def _import_menus(cls, host): | 440 def _import_menus(cls, host): |
395 host.import_menu( | 441 host.import_menu( |
396 (D_("Encryption"), D_("unencrypted (plain text)")), | 442 (D_("Encryption"), D_("unencrypted (plain text)")), |
397 partial(cls._on_menu_unencrypted, host=host), | 443 partial(cls._on_menu_unencrypted, host=host), |
398 security_limit=0, | 444 security_limit=0, |
399 help_string=D_("End encrypted session"), | 445 help_string=D_("End encrypted session"), |
400 type_=C.MENU_SINGLE, | 446 type_=C.MENU_SINGLE, |
401 ) | 447 ) |
402 for plg in cls.getPlugins(): | 448 for plg in cls.getPlugins(): |
403 host.import_menu( | 449 host.import_menu( |
404 (D_("Encryption"), plg.name), | 450 (D_("Encryption"), plg.name), |
405 partial(cls._on_menu_name, host=host, plg=plg), | 451 partial(cls._on_menu_name, host=host, plg=plg), |
406 security_limit=0, | 452 security_limit=0, |
407 help_string=D_("Start {name} session").format(name=plg.name), | 453 help_string=D_("Start {name} session").format(name=plg.name), |
408 type_=C.MENU_SINGLE, | 454 type_=C.MENU_SINGLE, |
409 ) | 455 ) |
410 host.import_menu( | 456 host.import_menu( |
411 (D_("Encryption"), D_("⛨ {name} trust").format(name=plg.name)), | 457 (D_("Encryption"), D_("⛨ {name} trust").format(name=plg.name)), |
412 partial(cls._on_menu_trust, host=host, plg=plg), | 458 partial(cls._on_menu_trust, host=host, plg=plg), |
413 security_limit=0, | 459 security_limit=0, |
414 help_string=D_("Manage {name} trust").format(name=plg.name), | 460 help_string=D_("Manage {name} trust").format(name=plg.name), |
415 type_=C.MENU_SINGLE, | 461 type_=C.MENU_SINGLE, |
416 ) | 462 ) |
417 | 463 |
418 @classmethod | 464 @classmethod |
419 def _on_menu_unencrypted(cls, data, host, profile): | 465 def _on_menu_unencrypted(cls, data, host, profile): |
420 client = host.get_client(profile) | 466 client = host.get_client(profile) |
421 peer_jid = jid.JID(data['jid']).userhostJID() | 467 peer_jid = jid.JID(data["jid"]).userhostJID() |
422 d = defer.ensureDeferred(client.encryption.stop(peer_jid)) | 468 d = defer.ensureDeferred(client.encryption.stop(peer_jid)) |
423 d.addCallback(lambda __: {}) | 469 d.addCallback(lambda __: {}) |
424 return d | 470 return d |
425 | 471 |
426 @classmethod | 472 @classmethod |
427 def _on_menu_name(cls, data, host, plg, profile): | 473 def _on_menu_name(cls, data, host, plg, profile): |
428 client = host.get_client(profile) | 474 client = host.get_client(profile) |
429 peer_jid = jid.JID(data['jid']) | 475 peer_jid = jid.JID(data["jid"]) |
430 if not plg.directed: | 476 if not plg.directed: |
431 peer_jid = peer_jid.userhostJID() | 477 peer_jid = peer_jid.userhostJID() |
432 d = defer.ensureDeferred( | 478 d = defer.ensureDeferred( |
433 client.encryption.start(peer_jid, plg.namespace, replace=True)) | 479 client.encryption.start(peer_jid, plg.namespace, replace=True) |
480 ) | |
434 d.addCallback(lambda __: {}) | 481 d.addCallback(lambda __: {}) |
435 return d | 482 return d |
436 | 483 |
437 @classmethod | 484 @classmethod |
438 @defer.inlineCallbacks | 485 @defer.inlineCallbacks |
439 def _on_menu_trust(cls, data, host, plg, profile): | 486 def _on_menu_trust(cls, data, host, plg, profile): |
440 client = host.get_client(profile) | 487 client = host.get_client(profile) |
441 peer_jid = jid.JID(data['jid']).userhostJID() | 488 peer_jid = jid.JID(data["jid"]).userhostJID() |
442 ui = yield client.encryption.get_trust_ui(peer_jid, plg.namespace) | 489 ui = yield client.encryption.get_trust_ui(peer_jid, plg.namespace) |
443 defer.returnValue({'xmlui': ui.toXml()}) | 490 defer.returnValue({"xmlui": ui.toXml()}) |
444 | 491 |
445 ## Triggers ## | 492 ## Triggers ## |
446 | 493 |
447 def set_encryption_flag(self, mess_data): | 494 def set_encryption_flag(self, mess_data): |
448 """Set "encryption" key in mess_data if session with destinee is encrypted""" | 495 """Set "encryption" key in mess_data if session with destinee is encrypted""" |
449 to_jid = mess_data['to'] | 496 to_jid = mess_data["to"] |
450 encryption = self._sessions.get(to_jid.userhostJID()) | 497 encryption = self._sessions.get(to_jid.userhostJID()) |
451 if encryption is not None: | 498 if encryption is not None: |
452 plugin = encryption['plugin'] | 499 plugin = encryption["plugin"] |
453 if mess_data["type"] == "groupchat" and plugin.directed: | 500 if mess_data["type"] == "groupchat" and plugin.directed: |
454 raise exceptions.InternalError( | 501 raise exceptions.InternalError( |
455 f"encryption flag must not be set for groupchat if encryption algorithm " | 502 f"encryption flag must not be set for groupchat if encryption algorithm " |
456 f"({encryption['plugin'].name}) is directed!") | 503 f"({encryption['plugin'].name}) is directed!" |
504 ) | |
457 mess_data[C.MESS_KEY_ENCRYPTION] = encryption | 505 mess_data[C.MESS_KEY_ENCRYPTION] = encryption |
458 self.mark_as_encrypted(mess_data, plugin.namespace) | 506 self.mark_as_encrypted(mess_data, plugin.namespace) |
459 | 507 |
460 ## Misc ## | 508 ## Misc ## |
461 | 509 |
465 This should be used in the post_treat workflow of message_received trigger of | 513 This should be used in the post_treat workflow of message_received trigger of |
466 the plugin | 514 the plugin |
467 @param mess_data(dict): message data as used in post treat workflow | 515 @param mess_data(dict): message data as used in post treat workflow |
468 @param namespace(str): namespace of the algorithm used for encrypting the message | 516 @param namespace(str): namespace of the algorithm used for encrypting the message |
469 """ | 517 """ |
470 mess_data['extra'][C.MESS_KEY_ENCRYPTED] = True | 518 mess_data["extra"][C.MESS_KEY_ENCRYPTED] = True |
471 from_bare_jid = mess_data['from'].userhostJID() | 519 from_bare_jid = mess_data["from"].userhostJID() |
472 if from_bare_jid != self.client.jid.userhostJID(): | 520 if from_bare_jid != self.client.jid.userhostJID(): |
473 session = self.getSession(from_bare_jid) | 521 session = self.getSession(from_bare_jid) |
474 if session is None: | 522 if session is None: |
475 # if we are currently unencrypted, we start a session automatically | 523 # if we are currently unencrypted, we start a session automatically |
476 # to avoid sending unencrypted messages in an encrypted context | 524 # to avoid sending unencrypted messages in an encrypted context |
477 log.info(_( | 525 log.info( |
478 "Starting e2e session with {peer_jid} as we receive encrypted " | 526 _( |
479 "messages") | 527 "Starting e2e session with {peer_jid} as we receive encrypted " |
480 .format(peer_jid=from_bare_jid) | 528 "messages" |
529 ).format(peer_jid=from_bare_jid) | |
481 ) | 530 ) |
482 defer.ensureDeferred(self.start(from_bare_jid, namespace)) | 531 defer.ensureDeferred(self.start(from_bare_jid, namespace)) |
483 | 532 |
484 return mess_data | 533 return mess_data |
485 | 534 |
486 def is_encryption_requested( | 535 def is_encryption_requested( |
487 self, | 536 self, mess_data: MessageData, namespace: Optional[str] = None |
488 mess_data: MessageData, | |
489 namespace: Optional[str] = None | |
490 ) -> bool: | 537 ) -> bool: |
491 """Helper method to check if encryption is requested in an outgoind message | 538 """Helper method to check if encryption is requested in an outgoind message |
492 | 539 |
493 @param mess_data: message data for outgoing message | 540 @param mess_data: message data for outgoing message |
494 @param namespace: if set, check if encryption is requested for the algorithm | 541 @param namespace: if set, check if encryption is requested for the algorithm |
497 """ | 544 """ |
498 encryption = mess_data.get(C.MESS_KEY_ENCRYPTION) | 545 encryption = mess_data.get(C.MESS_KEY_ENCRYPTION) |
499 if encryption is None: | 546 if encryption is None: |
500 return False | 547 return False |
501 # we get plugin even if namespace is None to be sure that the key exists | 548 # we get plugin even if namespace is None to be sure that the key exists |
502 plugin = encryption['plugin'] | 549 plugin = encryption["plugin"] |
503 if namespace is None: | 550 if namespace is None: |
504 return True | 551 return True |
505 return plugin.namespace == namespace | 552 return plugin.namespace == namespace |
506 | 553 |
507 def isEncrypted(self, mess_data): | 554 def isEncrypted(self, mess_data): |
508 """Helper method to check if a message has the e2e encrypted flag | 555 """Helper method to check if a message has the e2e encrypted flag |
509 | 556 |
510 @param mess_data(dict): message data | 557 @param mess_data(dict): message data |
511 @return (bool): True if the encrypted flag is present | 558 @return (bool): True if the encrypted flag is present |
512 """ | 559 """ |
513 return mess_data['extra'].get(C.MESS_KEY_ENCRYPTED, False) | 560 return mess_data["extra"].get(C.MESS_KEY_ENCRYPTED, False) |
514 | |
515 | 561 |
516 def mark_as_trusted(self, mess_data): | 562 def mark_as_trusted(self, mess_data): |
517 """Helper methor to mark a message as sent from a trusted entity. | 563 """Helper methor to mark a message as sent from a trusted entity. |
518 | 564 |
519 This should be used in the post_treat workflow of message_received trigger of | 565 This should be used in the post_treat workflow of message_received trigger of |
528 | 574 |
529 This should be used in the post_treat workflow of message_received trigger of | 575 This should be used in the post_treat workflow of message_received trigger of |
530 the plugin | 576 the plugin |
531 @param mess_data(dict): message data as used in post treat workflow | 577 @param mess_data(dict): message data as used in post treat workflow |
532 """ | 578 """ |
533 mess_data['trusted'] = False | 579 mess_data["trusted"] = False |
534 return mess_data | 580 return mess_data |