diff src/memory/memory.py @ 1591:0df9c6247474

core: profile session starting and connection are now separated. Moved profile session starting/authentication to memory module
author Goffi <goffi@goffi.org>
date Sat, 14 Nov 2015 19:18:10 +0100
parents 698d6755d62a
children a3d0cfa5b7a6
line wrap: on
line diff
--- a/src/memory/memory.py	Sat Nov 14 19:18:10 2015 +0100
+++ b/src/memory/memory.py	Sat Nov 14 19:18:10 2015 +0100
@@ -37,6 +37,7 @@
 from sat.memory.params import Params
 from sat.memory.disco import Discovery
 from sat.memory.crypto import BlockCipher
+from sat.memory.crypto import PasswordHasher
 from sat.tools import config as tools_config
 
 
@@ -316,11 +317,80 @@
 
     ## Profiles/Sessions management ##
 
-    def startProfileSession(self, profile):
+    def _startSession(self, password, profile_key):
+        profile = self.getProfileName(profile_key)
+        return self.startSession(password, profile)
+
+    def startSession(self, password, profile):
         """"Iniatialise session for a profile
-        @param profile: %(doc_profile)s"""
-        log.info(_("[%s] Profile session started" % profile))
-        self._entities_cache[profile] = {}
+
+        @param password(unicode): profile session password
+            or empty string is no password is set
+        @param profile: %(doc_profile)s
+        """
+        def createSession(dummy):
+            """Called once params are loaded."""
+            self._entities_cache[profile] = {}
+            log.info(u"[{}] Profile session started".format(profile))
+            return False
+
+        def backendInitialised(dummy):
+            def doStartSession(dummy=None):
+                if self.isSessionStarted(profile):
+                    log.info("Session already started!")
+                    return True
+                try:
+                    # if there is a value at this point in self._entities_cache,
+                    # it is the loadIndividualParams Deferred, the session is starting
+                    session_d = self._entities_cache[profile]
+                except KeyError:
+                    # else we do request the params
+                    session_d = self._entities_cache[profile] = self.loadIndividualParams(profile)
+                    session_d.addCallback(createSession)
+                finally:
+                    return session_d
+
+            auth_d = self.profileAuthenticate(password, profile)
+            auth_d.addCallback(doStartSession)
+            return auth_d
+
+        return self.host.initialised.addCallback(backendInitialised)
+
+    def _isSessionStarted(self, profile_key):
+        return self.isSessionStarted(self.getProfileName(profile_key))
+
+    def isSessionStarted(self, profile):
+        try:
+            # XXX: if the value in self._entities_cache is a Deferred,
+            #      the session is starting but not started yet
+            return not isinstance(self._entities_cache[profile], defer.Deferred)
+        except KeyError:
+            return False
+
+    def profileAuthenticate(self, password, profile):
+        """Authenticate the profile.
+
+        @param password (unicode): the SàT profile password
+        @param profile: %(doc_profile)s
+        @return (D): a deferred None in case of success, a failure otherwise.
+        """
+        session_data = self.auth_sessions.profileGetUnique(profile)
+        if not password and session_data:
+            # XXX: this allows any frontend to connect with the empty password as soon as
+            # the profile has been authenticated at least once before. It is OK as long as
+            # submitting a form with empty passwords is restricted to local frontends.
+            return defer.succeed(None)
+
+        def check_result(result):
+            if not result:
+                log.warning(u'Authentication failure of profile {}'.format(profile))
+                raise exceptions.PasswordError(u"The provided profile password doesn't match.")
+            if not session_data:  # avoid to create two profile sessions when password if specified
+                return self.newAuthSession(password, profile)
+
+        d = self.asyncGetParamA(C.PROFILE_PASS_PATH[1], C.PROFILE_PASS_PATH[0], profile_key=profile)
+        d.addCallback(lambda sat_cipher: PasswordHasher.verify(password, sat_cipher))
+        return d.addCallback(check_result)
 
     def newAuthSession(self, key, profile):
         """Start a new session for the authenticated profile.