Mercurial > libervia-backend
diff src/memory/memory.py @ 1030:15f43b54d697
core, memory, bridge: added profile password + password encryption:
/!\ This changeset updates the database version to 2 and modify the database content!
Description:
- new parameter General / Password to store the profile password
- profile password is initialized with XMPP password value, it is stored hashed
- bridge methods asyncCreateProfile/asyncConnect takes a new argument "password" (default = "")
- bridge method asyncConnect returns a boolean (True = connection already established, False = connection initiated)
- profile password is checked before initializing the XMPP connection
- new private individual parameter to store the personal encryption key of each profile
- personal key is randomly generated and encrypted with the profile password
- personal key is decrypted after profile authentification and stored in a Sessions instance
- personal key is used to encrypt/decrypt other passwords when they need to be retrieved/modified
- modifying the profile password re-encrypt the personal key
- Memory.setParam now returns a Deferred (the bridge method "setParam" is unchanged)
- Memory.asyncGetParamA eventually decrypts the password, Memory.getParamA would fail on a password parameter
TODO:
- if profile authentication is OK but XMPP authentication is KO, prompt the user for another XMPP password
- fix the method "registerNewAccount" (and move it to a plugin)
- remove bridge method "connect", sole "asyncConnect" should be used
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 07 May 2014 16:02:23 +0200 |
parents | f6182f6418ea |
children | 65fffdcb97f1 |
line wrap: on
line diff
--- a/src/memory/memory.py Sat May 10 17:37:32 2014 +0200 +++ b/src/memory/memory.py Wed May 07 16:02:23 2014 +0200 @@ -34,6 +34,7 @@ from sat.memory.persistent import PersistentDict from sat.memory.params import Params from sat.memory.disco import Discovery +from sat.memory.crypto import BlockCipher class Sessions(object): @@ -210,6 +211,7 @@ self._entities_cache = {} # XXX: keep presence/last resource/other data in cache # /!\ an entity is not necessarily in roster self.subscriptions = {} + self.auth_sessions = ProfileSessions() # remember the authenticated profiles self.disco = Discovery(host) fixLocalDir(False) # XXX: tmp update code, will be removed in the future self.config = self.parseMainConf() @@ -259,7 +261,7 @@ """Load parameters template from xml file @param filename (str): input file - @return bool: True in case of success + @return: bool: True in case of success """ if not filename: return False @@ -289,6 +291,24 @@ log.info(_("[%s] Profile session started" % profile)) self._entities_cache[profile] = {} + def newAuthSession(self, key, profile): + """Start a new session for the authenticated profile. + + The personal key is loaded encrypted from a PersistentDict before being decrypted. + + @param key: the key to decrypt the personal key + @param profile: %(doc_profile)s + @return: a deferred None value + """ + def gotPersonalKey(personal_key): + """Create the session for this profile and store the personal key""" + self.auth_sessions.newSession({C.MEMORY_CRYPTO_KEY: personal_key}, profile) + log.debug('auth session created for profile %s' % profile) + + d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() + d.addCallback(lambda data: BlockCipher.decrypt(key, data[C.MEMORY_CRYPTO_KEY])) + return d.addCallback(gotPersonalKey) + def purgeProfileSession(self, profile): """Delete cache of data of profile @param profile: %(doc_profile)s""" @@ -303,7 +323,7 @@ """Save parameters template to xml file @param filename (str): output file - @return bool: True in case of success + @return: bool: True in case of success """ if not filename: return False @@ -320,17 +340,23 @@ def getProfilesList(self): return self.storage.getProfilesList() - def getProfileName(self, profile_key, return_profile_keys = False): + def getProfileName(self, profile_key, return_profile_keys=False): """Return name of profile from keyword @param profile_key: can be the profile name or a keywork (like @DEFAULT@) @return: profile name or None if it doesn't exist""" return self.params.getProfileName(profile_key, return_profile_keys) - def asyncCreateProfile(self, name): + def asyncCreateProfile(self, name, password=''): """Create a new profile - @param name: Profile name + @param name: profile name + @param password: profile password + @return: Deferred """ - return self.params.asyncCreateProfile(name) + personal_key = BlockCipher.getRandomKey(base64=True) # generated once for all and saved in a PersistentDict + self.auth_sessions.newSession({C.MEMORY_CRYPTO_KEY: personal_key}, name) # will be encrypted by setParam + d = self.params.asyncCreateProfile(name) + d.addCallback(lambda dummy: self.setParam(C.PROFILE_PASS_PATH[1], password, C.PROFILE_PASS_PATH[0], profile_key=name)) + return d def asyncDeleteProfile(self, name, force=False): """Delete an existing profile @@ -355,7 +381,6 @@ jid_ = jid.JID(jid_s) return self.getLastResource(jid_, profile_key) or "" - def getLastResource(self, entity_jid, profile_key): """Return the last resource used by an entity @param entity_jid: entity jid @@ -502,7 +527,6 @@ """ return self.getEntityData(entity_jid, (key,), profile_key)[key] - def delEntityCache(self, entity_jid, delete_all_resources=True, profile_key=C.PROF_KEY_NONE): """Remove cached data for entity @param entity_jid: JID of the entity to delete @@ -527,6 +551,32 @@ except KeyError: log.debug("Can't delete entity [%s]: not in cache" % entity.full()) + def encryptPersonalData(self, data_key, data_value, crypto_key, profile): + """Re-encrypt a personal data (saved to a PersistentDict). + + @param data_key: key for the individual PersistentDict instance + @param data_value: the value to be encrypted + @param crypto_key: the key to encrypt the value + @param profile: %(profile_doc)s + @return: a deferred None value + """ + + def gotIndMemory(data): + d = BlockCipher.encrypt(crypto_key, data_value) + + def cb(cipher): + data[data_key] = cipher + return data.force(data_key) + + return d.addCallback(cb) + + def done(dummy): + log.debug(_('Personal data (%(ns)s, %(key)s) has been successfuly encrypted') % + {'ns': C.MEMORY_CRYPTO_NAMESPACE, 'key': data_key}) + + d = PersistentDict(C.MEMORY_CRYPTO_NAMESPACE, profile).load() + return d.addCallback(gotIndMemory).addCallback(done) + def addWaitingSub(self, type_, entity_jid, profile_key): """Called when a subcription request is received""" profile = self.getProfileName(profile_key)