core (memory/encryption): new encryptionNamespaceGet and encryptionTrustUIGet methods:

- encryptionNamespaceGet retrieves algorithm namespace from its short name
- encryptionTrustUIGet retrieves trust mangement XMLUI from encryption plugin
- new markAsUntrusted internal helper method, to add untrusted flag to message data
This commit is contained in:
Goffi 2019-01-02 18:22:30 +01:00
commit 24468a9138
6 changed files with 174 additions and 41 deletions

View file

@ -60,7 +60,7 @@ category=core
sig_in=sss
doc=A message encryption session has been started
doc_param_0=to_jid: JID of the recipient (bare jid if it's encrypted for all devices)
doc_param_1=encryption_data: [JSON_OBJ] data of the encryption algorithm used, encoded as a json object.
doc_param_1=encryption_data: (JSON_OBJ) data of the encryption algorithm used, encoded as a json object.
it always has the following keys:
- name: human readable name of the algorithm
- namespace: namespace of the encryption plugin
@ -445,8 +445,8 @@ param_2_default=False
param_3_default="@NONE@"
doc=Start an encryption session
doc_param_0=to_jid: JID of the recipient (bare jid if it must be encrypted for all devices)
doc_param_1=encryption_ns: Namespace of the encryption algorithm to use
doc_param_2=replace: If True and an encryption session already exists, it will be replaced by this one
doc_param_1=namespace: namespace of the encryption algorithm to use
doc_param_2=replace: if True and an encryption session already exists, it will be replaced by this one
else a ConflictError will be raised
doc_param_3=%(doc_profile_key)s
@ -455,7 +455,7 @@ type=method
category=core
sig_in=ss
sig_out=
doc=Start an encryption session
doc=Stop an encryption session
doc_param_0=to_jid: JID of the recipient (full jid if encryption must be stopped for one device only)
doc_param_1=%(doc_profile_key)s
@ -467,13 +467,20 @@ sig_out=s
doc=Retrieve encryption data for a given entity
doc_param_0=to_jid: bare JID of the recipient
doc_param_1=%(doc_profile_key)s
doc_return=[JSON_OBJ] empty string if session is unencrypted, else a json encoded objects.
doc_return=(JSON_OBJ) empty string if session is unencrypted, else a json encoded objects.
In case of dict, following keys are always present:
- name: human readable name of the encryption algorithm
- namespace: namespace of the plugin
following key can be present if suitable:
- directed_devices: list or resource where session is encrypted
[encryptionNamespaceGet]
type=method
category=core
sig_in=s
sig_out=s
doc=Get algorithm namespace from its name
[encryptionPluginsGet]
type=method
category=core
@ -481,6 +488,18 @@ sig_in=
sig_out=aa{ss}
doc=Retrieve registered plugins for encryption
[encryptionTrustUIGet]
async=
type=method
category=core
sig_in=sss
sig_out=s
doc=Get XMLUI to manage trust for given encryption algorithm
doc_param_0=to_jid: bare JID of entity to manage
doc_param_0=namespace: namespace of the algorithm to manage
doc_param_2=%(doc_profile_key)s
doc_return=(XMLUI) UI of the trust management
[setPresence]
type=method
category=core

View file

@ -271,12 +271,24 @@ class DbusObject(dbus.service.Object):
def disconnect(self, profile_key="@DEFAULT@", callback=None, errback=None):
return self._callback("disconnect", unicode(profile_key), callback=callback, errback=errback)
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='s', out_signature='s',
async_callbacks=None)
def encryptionNamespaceGet(self, arg_0):
return self._callback("encryptionNamespaceGet", unicode(arg_0))
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='', out_signature='aa{ss}',
async_callbacks=None)
def encryptionPluginsGet(self, ):
return self._callback("encryptionPluginsGet", )
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='sss', out_signature='s',
async_callbacks=('callback', 'errback'))
def encryptionTrustUIGet(self, namespace, arg_1, profile_key, callback=None, errback=None):
return self._callback("encryptionTrustUIGet", unicode(namespace), unicode(arg_1), unicode(profile_key), callback=callback, errback=errback)
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='ss', out_signature='s',
async_callbacks=None)
@ -412,8 +424,8 @@ class DbusObject(dbus.service.Object):
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='ssbs', out_signature='',
async_callbacks=None)
def messageEncryptionStart(self, to_jid, encryption_ns='', replace=False, profile_key="@NONE@"):
return self._callback("messageEncryptionStart", unicode(to_jid), unicode(encryption_ns), replace, unicode(profile_key))
def messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@"):
return self._callback("messageEncryptionStart", unicode(to_jid), unicode(namespace), replace, unicode(profile_key))
@dbus.service.method(const_INT_PREFIX+const_CORE_SUFFIX,
in_signature='ss', out_signature='',

View file

@ -122,7 +122,10 @@ class SAT(service.Service):
self._messageEncryptionStop)
self.bridge.register_method("messageEncryptionGet",
self._messageEncryptionGet)
self.bridge.register_method("encryptionNamespaceGet",
self._encryptionNamespaceGet)
self.bridge.register_method("encryptionPluginsGet", self._encryptionPluginsGet)
self.bridge.register_method("encryptionTrustUIGet", self._encryptionTrustUIGet)
self.bridge.register_method("getConfig", self._getConfig)
self.bridge.register_method("setParam", self.setParam)
self.bridge.register_method("getParamA", self.memory.getStringParamA)
@ -670,6 +673,26 @@ class SAT(service.Service):
def registerEncryptionPlugin(self, *args, **kwargs):
return encryption.EncryptionHandler.registerPlugin(*args, **kwargs)
def _messageEncryptionStart(self, to_jid_s, namespace, replace=False,
profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
return client.encryption.start(to_jid, namespace or None, replace)
def _messageEncryptionStop(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
return client.encryption.stop(to_jid)
def _messageEncryptionGet(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
session_data = client.encryption.getSession(to_jid)
return client.encryption.getBridgeData(session_data)
def _encryptionNamespaceGet(self, name):
return encryption.EncryptionHandler.getNSFromName(name)
def _encryptionPluginsGet(self):
plugins = encryption.EncryptionHandler.getPlugins()
ret = []
@ -681,6 +704,13 @@ class SAT(service.Service):
})
return ret
def _encryptionTrustUIGet(self, to_jid_s, namespace, profile_key):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
d = client.encryption.getTrustUI(to_jid, namespace=namespace or None)
d.addCallback(lambda xmlui: xmlui.toXml())
return d
## XMPP methods ##
def _messageSend(self, to_jid_s, message, subject=None, mess_type="auto", extra=None,
@ -697,23 +727,6 @@ class SAT(service.Service):
{unicode(key): unicode(value) for key, value in extra.items()},
)
def _messageEncryptionStart(self, to_jid_s, encryption_ns, replace=False,
profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
return client.encryption.start(to_jid, encryption_ns.strip() or None, replace)
def _messageEncryptionStop(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
return client.encryption.stop(to_jid)
def _messageEncryptionGet(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
session_data = client.encryption.getSession(to_jid)
return client.encryption.getBridgeData(session_data)
def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
return self.setPresence(jid.JID(to) if to else None, show, statuses, profile_key)
@ -1051,7 +1064,7 @@ class SAT(service.Service):
with_data(bool): True if the callback use the optional data dict
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid
if possible
one_shot(bool): True to delete callback once it have been called
one_shot(bool): True to delete callback once it has been called
@return: id of the registered callback
"""
callback_id = kwargs.pop("force_id", None)

View file

@ -51,8 +51,10 @@ class EncryptionHandler(object):
@param plg_instance(object): instance of the plugin
it must have the following methods:
- startEncryption(jid.JID): start an encryption session with a bare jid
- stopEncryption(jid.JID): stop an encryption session with a bare jid
- getTrustUI(entity): return a XMLUI for trust management
entity(jid.JID): entity to manage
The returned XMLUI must be a form
@param name(unicode): human readable name of the encryption algorithm
@param namespace(unicode): namespace of the encryption algorithm
@param priority(int): priority of this plugin to encrypt an message when not
@ -83,6 +85,15 @@ class EncryptionHandler(object):
def getPlugins(cls):
return cls.plugins
@classmethod
def getPlugin(cls, namespace):
try:
return next(p for p in cls.plugins if p.namespace == namespace)
except StopIteration:
raise exceptions.NotFound(_(
u"Can't find requested encryption plugin: {namespace}").format(
namespace=namespace))
@classmethod
def getNSFromName(cls, name):
"""Retrieve plugin namespace from its name
@ -94,7 +105,9 @@ class EncryptionHandler(object):
for p in cls.plugins:
if p.name.lower() == name.lower():
return p.namespace
raise exceptions.NotFound
raise exceptions.NotFound(_(
u"Can't find a plugin with the name \"{name}\".".format(
name=name)))
def getBridgeData(self, session):
"""Retrieve session data serialized for bridge.
@ -117,7 +130,8 @@ class EncryptionHandler(object):
@param entity(jid.JID): entity to start an encryption session with
must be bare jid is the algorithm encrypt for all devices
@param namespace(unicode, None): namespace of the encryption algorithm to use
@param namespace(unicode, None): namespace of the encryption algorithm
to use.
None to select automatically an algorithm
@param replace(bool): if True and an encrypted session already exists,
it will be replaced by the new one
@ -129,12 +143,7 @@ class EncryptionHandler(object):
if namespace is None:
plugin = self.plugins[0]
else:
try:
plugin = next(p for p in self.plugins if p.namespace == namespace)
except StopIteration:
raise exceptions.NotFound(_(
u"Can't find requested encryption plugin: {namespace}").format(
namespace=namespace))
plugin = self.getPlugin(namespace)
bare_jid = entity.userhostJID()
if bare_jid in self._sessions:
@ -142,7 +151,8 @@ class EncryptionHandler(object):
former_plugin = self._sessions[bare_jid]['plugin']
if former_plugin.namespace == namespace:
log.info(_(u"Session with {bare_jid} is already encrypted with {name}. "
u"Nothing to do.").format(bare_jid=bare_jid, name=plugin.name))
u"Nothing to do.").format(
bare_jid=bare_jid, name=former_plugin.name))
return
if replace:
@ -255,9 +265,39 @@ class EncryptionHandler(object):
None if there is not encryption for this session with this jid
"""
if entity.resource:
raise exceptions.InternalError(u"Full jid given when expecting bare jid")
raise ValueError(u"Full jid given when expecting bare jid")
return self._sessions.get(entity)
def getTrustUI(self, entity_jid, namespace=None):
"""Retrieve encryption UI
@param entity_jid(jid.JID): get the UI for this entity
must be a bare jid
@param namespace(unicode): namespace of the algorithm to manage
if None use current algorithm
@return D(xmlui): XMLUI for trust management
the xmlui is a form
None if there is not encryption for this session with this jid
@raise exceptions.NotFound: no algorithm/plugin found
@raise NotImplementedError: plugin doesn't handle UI management
"""
if namespace is None:
session = self.getSession(entity_jid)
if not session:
raise exceptions.NotFound(
u"No encryption session currently active for {entity_jid}"
.format(entity_jid=entity_jid.full()))
plugin = session['plugin']
else:
plugin = self.getPlugin(namespace)
try:
get_trust_ui = plugin.instance.getTrustUI
except AttributeError:
raise NotImplementedError(
u"Encryption plugin doesn't handle trust management UI")
else:
return get_trust_ui(self.client, entity_jid)
## Triggers ##
def setEncryptionFlag(self, mess_data):
@ -283,3 +323,13 @@ class EncryptionHandler(object):
"""
mess_data['encrypted'] = True
return mess_data
def markAsUntrusted(self, mess_data):
"""Helper methor to mark a message as sent from an untrusted entity.
This should be used in the post_treat workflow of MessageReceived trigger of
the plugin
@param mess_data(dict): message data as used in post treat workflow
"""
mess_data['untrusted'] = True
return mess_data

View file

@ -245,6 +245,20 @@ class Bridge(object):
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return self.db_core_iface.disconnect(profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler)
def encryptionNamespaceGet(self, arg_0, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
kwargs={}
if callback is not None:
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.encryptionNamespaceGet(arg_0, **kwargs))
def encryptionPluginsGet(self, callback=None, errback=None):
if callback is None:
error_handler = None
@ -259,6 +273,15 @@ class Bridge(object):
kwargs['error_handler'] = error_handler
return self.db_core_iface.encryptionPluginsGet(**kwargs)
def encryptionTrustUIGet(self, namespace, arg_1, profile_key, callback=None, errback=None):
if callback is None:
error_handler = None
else:
if errback is None:
errback = log.error
error_handler = lambda err:errback(dbus_to_bridge_exception(err))
return unicode(self.db_core_iface.encryptionTrustUIGet(namespace, arg_1, profile_key, timeout=const_TIMEOUT, reply_handler=callback, error_handler=error_handler))
def getConfig(self, section, name, callback=None, errback=None):
if callback is None:
error_handler = None
@ -532,7 +555,7 @@ class Bridge(object):
kwargs['error_handler'] = error_handler
return unicode(self.db_core_iface.messageEncryptionGet(to_jid, profile_key, **kwargs))
def messageEncryptionStart(self, to_jid, encryption_ns='', replace=False, profile_key="@NONE@", callback=None, errback=None):
def messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@", callback=None, errback=None):
if callback is None:
error_handler = None
else:
@ -544,7 +567,7 @@ class Bridge(object):
kwargs['timeout'] = const_TIMEOUT
kwargs['reply_handler'] = callback
kwargs['error_handler'] = error_handler
return self.db_core_iface.messageEncryptionStart(to_jid, encryption_ns, replace, profile_key, **kwargs)
return self.db_core_iface.messageEncryptionStart(to_jid, namespace, replace, profile_key, **kwargs)
def messageEncryptionStop(self, to_jid, profile_key, callback=None, errback=None):
if callback is None:

View file

@ -214,6 +214,14 @@ class Bridge(object):
errback = self._generic_errback
d.addErrback(errback)
def encryptionNamespaceGet(self, arg_0, callback=None, errback=None):
d = self.root.callRemote("encryptionNamespaceGet", arg_0)
if callback is not None:
d.addCallback(callback)
if errback is None:
errback = self._generic_errback
d.addErrback(errback)
def encryptionPluginsGet(self, callback=None, errback=None):
d = self.root.callRemote("encryptionPluginsGet")
if callback is not None:
@ -222,6 +230,14 @@ class Bridge(object):
errback = self._generic_errback
d.addErrback(errback)
def encryptionTrustUIGet(self, namespace, arg_1, profile_key, callback=None, errback=None):
d = self.root.callRemote("encryptionTrustUIGet", namespace, arg_1, profile_key)
if callback is not None:
d.addCallback(callback)
if errback is None:
errback = self._generic_errback
d.addErrback(errback)
def getConfig(self, section, name, callback=None, errback=None):
d = self.root.callRemote("getConfig", section, name)
if callback is not None:
@ -398,8 +414,8 @@ class Bridge(object):
errback = self._generic_errback
d.addErrback(errback)
def messageEncryptionStart(self, to_jid, encryption_ns='', replace=False, profile_key="@NONE@", callback=None, errback=None):
d = self.root.callRemote("messageEncryptionStart", to_jid, encryption_ns, replace, profile_key)
def messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@", callback=None, errback=None):
d = self.root.callRemote("messageEncryptionStart", to_jid, namespace, replace, profile_key)
if callback is not None:
d.addCallback(lambda dummy: callback())
if errback is None: