core (memory/encryption): encryption session are now restored on client connection
This commit is contained in:
parent
e754d16000
commit
900daa7ad9
4 changed files with 61 additions and 34 deletions
|
|
@ -849,12 +849,14 @@ class SAT(service.Service):
|
|||
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)
|
||||
return defer.ensureDeferred(
|
||||
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)
|
||||
return defer.ensureDeferred(
|
||||
client.encryption.stop(to_jid))
|
||||
|
||||
def _messageEncryptionGet(self, to_jid_s, profile_key=C.PROF_KEY_NONE):
|
||||
client = self.getClient(profile_key)
|
||||
|
|
|
|||
|
|
@ -209,6 +209,8 @@ class SatXMPPEntity:
|
|||
port, max_retries,
|
||||
)
|
||||
|
||||
await entity.encryption.loadSessions()
|
||||
|
||||
entity._createSubProtocols()
|
||||
|
||||
entity.fallBack = SatFallbackHandler(host)
|
||||
|
|
|
|||
|
|
@ -17,18 +17,20 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import copy
|
||||
from functools import partial
|
||||
from sat.core.i18n import D_, _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
from collections import namedtuple
|
||||
from sat.core.log import getLogger
|
||||
from sat.tools.common import data_format
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.internet import defer
|
||||
from twisted.python import failure
|
||||
import copy
|
||||
log = getLogger(__name__)
|
||||
from sat.core.i18n import D_, _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.core import exceptions
|
||||
from sat.core.log import getLogger
|
||||
from sat.tools.common import data_format
|
||||
from sat.tools import utils
|
||||
from sat.memory import persistent
|
||||
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
|
@ -39,18 +41,38 @@ EncryptionPlugin = namedtuple("EncryptionPlugin", ("instance",
|
|||
"directed"))
|
||||
|
||||
|
||||
class EncryptionHandler(object):
|
||||
class EncryptionHandler:
|
||||
"""Class to handle encryption sessions for a client"""
|
||||
plugins = [] # plugin able to encrypt messages
|
||||
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
self._sessions = {} # bare_jid ==> encryption_data
|
||||
self._stored_session = persistent.PersistentDict(
|
||||
"core:encryption", profile=client.profile)
|
||||
|
||||
@property
|
||||
def host(self):
|
||||
return self.client.host_app
|
||||
|
||||
async def loadSessions(self):
|
||||
"""Load persistent sessions"""
|
||||
await self._stored_session.load()
|
||||
start_d_list = []
|
||||
for entity_jid_s, namespace in self._stored_session.items():
|
||||
entity = jid.JID(entity_jid_s)
|
||||
start_d_list.append(defer.ensureDeferred(self.start(entity, namespace)))
|
||||
|
||||
if start_d_list:
|
||||
result = await defer.DeferredList(start_d_list)
|
||||
for idx, (success, err) in enumerate(result):
|
||||
if not success:
|
||||
entity_jid_s, namespace = list(self._stored_session.items())[idx]
|
||||
log.warning(_(
|
||||
"Could not restart {namespace!r} encryption with {entity}: {err}"
|
||||
).format(namespace=namespace, entity=entity_jid_s, err=err))
|
||||
log.info(_("encryption sessions restored"))
|
||||
|
||||
@classmethod
|
||||
def registerPlugin(cls, plg_instance, name, namespace, priority=0, directed=False):
|
||||
"""Register a plugin handling an encryption algorithm
|
||||
|
|
@ -142,40 +164,41 @@ class EncryptionHandler(object):
|
|||
|
||||
return data_format.serialise(bridge_data)
|
||||
|
||||
def _startEncryption(self, plugin, entity):
|
||||
async def _startEncryption(self, plugin, entity):
|
||||
"""Start encryption with a plugin
|
||||
|
||||
This method must be called just before adding a plugin session.
|
||||
StartEncryptionn method of plugin will be called if it exists.
|
||||
"""
|
||||
if not plugin.directed:
|
||||
await self._stored_session.aset(entity.userhost(), plugin.namespace)
|
||||
try:
|
||||
start_encryption = plugin.instance.startEncryption
|
||||
except AttributeError:
|
||||
log.debug("No startEncryption method found for {plugin}".format(
|
||||
plugin = plugin.namespace))
|
||||
return defer.succeed(None)
|
||||
log.debug(f"No startEncryption method found for {plugin.namespace}")
|
||||
else:
|
||||
# we copy entity to avoid having the resource changed by stop_encryption
|
||||
return defer.maybeDeferred(start_encryption, self.client, copy.copy(entity))
|
||||
await utils.asDeferred(start_encryption, self.client, copy.copy(entity))
|
||||
|
||||
def _stopEncryption(self, plugin, entity):
|
||||
async def _stopEncryption(self, plugin, entity):
|
||||
"""Stop encryption with a plugin
|
||||
|
||||
This method must be called just before removing a plugin session.
|
||||
StopEncryptionn method of plugin will be called if it exists.
|
||||
"""
|
||||
try:
|
||||
await self._stored_session.adel(entity.userhost())
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
stop_encryption = plugin.instance.stopEncryption
|
||||
except AttributeError:
|
||||
log.debug("No stopEncryption method found for {plugin}".format(
|
||||
plugin = plugin.namespace))
|
||||
return defer.succeed(None)
|
||||
log.debug(f"No stopEncryption method found for {plugin.namespace}")
|
||||
else:
|
||||
# we copy entity to avoid having the resource changed by stop_encryption
|
||||
return defer.maybeDeferred(stop_encryption, self.client, copy.copy(entity))
|
||||
return utils.asDeferred(stop_encryption, self.client, copy.copy(entity))
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def start(self, entity, namespace=None, replace=False):
|
||||
async def start(self, entity, namespace=None, replace=False):
|
||||
"""Start an encryption session with an entity
|
||||
|
||||
@param entity(jid.JID): entity to start an encryption session with
|
||||
|
|
@ -209,7 +232,7 @@ class EncryptionHandler(object):
|
|||
# there is a conflict, but replacement is requested
|
||||
# so we stop previous encryption to use new one
|
||||
del self._sessions[bare_jid]
|
||||
yield self._stopEncryption(former_plugin, entity)
|
||||
await self._stopEncryption(former_plugin, entity)
|
||||
else:
|
||||
msg = (_("Session with {bare_jid} is already encrypted with {name}. "
|
||||
"Please stop encryption session before changing algorithm.")
|
||||
|
|
@ -233,7 +256,7 @@ class EncryptionHandler(object):
|
|||
elif entity.resource:
|
||||
raise ValueError(_("{name} encryption must be used with bare jids."))
|
||||
|
||||
yield self._startEncryption(plugin, entity)
|
||||
await self._startEncryption(plugin, entity)
|
||||
self._sessions[entity.userhostJID()] = data
|
||||
log.info(_("Encryption session has been set for {entity_jid} with "
|
||||
"{encryption_name}").format(
|
||||
|
|
@ -254,14 +277,13 @@ class EncryptionHandler(object):
|
|||
|
||||
self.client.feedback(bare_jid, msg)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def stop(self, entity, namespace=None):
|
||||
async def stop(self, entity, namespace=None):
|
||||
"""Stop an encryption session with an entity
|
||||
|
||||
@param entity(jid.JID): entity with who the encryption session must be stopped
|
||||
must be bare jid if the algorithm encrypt for all devices
|
||||
@param namespace(unicode): namespace of the session to stop
|
||||
when specified, used to check we stop the right encryption session
|
||||
when specified, used to check that we stop the right encryption session
|
||||
"""
|
||||
session = self.getSession(entity.userhostJID())
|
||||
if not session:
|
||||
|
|
@ -295,12 +317,12 @@ class EncryptionHandler(object):
|
|||
# we stop the whole session
|
||||
# see comment below for deleting session before stopping encryption
|
||||
del self._sessions[entity.userhostJID()]
|
||||
yield self._stopEncryption(plugin, entity)
|
||||
await self._stopEncryption(plugin, entity)
|
||||
else:
|
||||
# plugin's stopEncryption may call stop again (that's the case with OTR)
|
||||
# so we need to remove plugin from session before calling self._stopEncryption
|
||||
del self._sessions[entity.userhostJID()]
|
||||
yield self._stopEncryption(plugin, entity)
|
||||
await self._stopEncryption(plugin, entity)
|
||||
|
||||
log.info(_("encryption session stopped with entity {entity}").format(
|
||||
entity=entity.full()))
|
||||
|
|
@ -390,7 +412,7 @@ class EncryptionHandler(object):
|
|||
def _onMenuUnencrypted(cls, data, host, profile):
|
||||
client = host.getClient(profile)
|
||||
peer_jid = jid.JID(data['jid']).userhostJID()
|
||||
d = client.encryption.stop(peer_jid)
|
||||
d = defer.ensureDeferred(client.encryption.stop(peer_jid))
|
||||
d.addCallback(lambda __: {})
|
||||
return d
|
||||
|
||||
|
|
@ -400,7 +422,8 @@ class EncryptionHandler(object):
|
|||
peer_jid = jid.JID(data['jid'])
|
||||
if not plg.directed:
|
||||
peer_jid = peer_jid.userhostJID()
|
||||
d = client.encryption.start(peer_jid, plg.namespace, replace=True)
|
||||
d = defer.ensureDeferred(
|
||||
client.encryption.start(peer_jid, plg.namespace, replace=True))
|
||||
d.addCallback(lambda __: {})
|
||||
return d
|
||||
|
||||
|
|
|
|||
|
|
@ -169,12 +169,12 @@ class Context(potr.context.Context):
|
|||
feedback = _("/!\\ conversation with %(other_jid)s is now UNENCRYPTED") % {
|
||||
"other_jid": self.peer.full()
|
||||
}
|
||||
d = client.encryption.stop(self.peer, NS_OTR)
|
||||
d = defer.ensureDeferred(client.encryption.stop(self.peer, NS_OTR))
|
||||
d.addCallback(self.stopCb, feedback=feedback)
|
||||
d.addErrback(self.stopEb)
|
||||
return
|
||||
elif state == potr.context.STATE_ENCRYPTED:
|
||||
client.encryption.start(self.peer, NS_OTR)
|
||||
defer.ensureDeferred(client.encryption.start(self.peer, NS_OTR))
|
||||
try:
|
||||
trusted = self.isTrusted()
|
||||
except TypeError:
|
||||
|
|
@ -201,7 +201,7 @@ class Context(potr.context.Context):
|
|||
feedback = D_("OTR conversation with {other_jid} is FINISHED").format(
|
||||
other_jid=self.peer.full()
|
||||
)
|
||||
d = client.encryption.stop(self.peer, NS_OTR)
|
||||
d = defer.ensureDeferred(client.encryption.stop(self.peer, NS_OTR))
|
||||
d.addCallback(self.stopCb, feedback=feedback)
|
||||
d.addErrback(self.stopEb)
|
||||
return
|
||||
|
|
@ -808,7 +808,7 @@ class OTR(object):
|
|||
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
||||
|
||||
if otrctx.state != potr.context.STATE_PLAINTEXT:
|
||||
client.encryption.start(to_jid, NS_OTR)
|
||||
defer.ensureDeferred(client.encryption.start(to_jid, NS_OTR))
|
||||
client.encryption.setEncryptionFlag(mess_data)
|
||||
if not mess_data["to"].resource:
|
||||
# if not resource was given, we force it here
|
||||
|
|
|
|||
Loading…
Reference in a new issue