Browse Source

Changed the sessions management layout

Roland Haroutiounian 8 years ago
parent
commit
a0818fe410

+ 150
- 7
lodel/auth/session.py View File

3
 from abc import ABC, abstractmethod
3
 from abc import ABC, abstractmethod
4
 from uuid import uuid1
4
 from uuid import uuid1
5
 
5
 
6
+from werkzeug.contrib.sessions import generate_key, Session
7
+
6
 from lodel.auth.exceptions import AuthenticationError
8
 from lodel.auth.exceptions import AuthenticationError
7
 from lodel.utils.datetime import get_utc_timestamp
9
 from lodel.utils.datetime import get_utc_timestamp
8
 
10
 
9
 
11
 
12
+class LodelSession(ABC):
13
+    expiration_limit = 900
14
+
15
+    __sessions = {}
16
+
17
+    ## @brief creates a session
18
+    # @param contests dict : session initialization keys
19
+    def __init__(self, contents={}):
20
+
21
+        self.sid = self.__class__.generate_new_sid()
22
+
23
+        for key, value in contents.items():
24
+            self.set_key(key, value)
25
+
26
+        self.store()
27
+        self.register_session()
28
+
29
+    ## @brief stores the session
30
+    @abstractmethod
31
+    def store(self):
32
+        pass
33
+
34
+    ## @brief registers the session
35
+    @abstractmethod
36
+    def register_session(self):
37
+        pass
38
+
39
+    ## @brief saves a session
40
+    @abstractmethod
41
+    def save(self):
42
+        pass
43
+
44
+    ## @brief loads a session
45
+    # @param sid str : session id
46
+    # @return LodelSession
47
+    @classmethod
48
+    @abstractmethod
49
+    def load(cls, sid):
50
+        return None
51
+
52
+    ## @brief cleans the session store
53
+    @classmethod
54
+    @abstractmethod
55
+    def clean(cls):
56
+        pass
57
+
58
+    ## @brief destroys a session
59
+    # @param sid str : session id
60
+    @classmethod
61
+    def destroy(cls, sid):
62
+        cls.delete_session(sid)
63
+        cls.unregister_session(sid)
64
+
65
+    ## @brief deletes a session
66
+    # @param sid str : session id
67
+    @classmethod
68
+    @abstractmethod
69
+    def delete_session(cls, sid):
70
+        pass
71
+
72
+    ## @brief unregisters a session from the session store list
73
+    # @param sid str : session id
74
+    @classmethod
75
+    def unregister_session(cls, sid):
76
+        if sid in cls.__sessions:
77
+            del cls.__sessions[sid]
78
+
79
+    ## @brief checks if a session if registered
80
+    # @param sid str : session id
81
+    # @return bool
82
+    @classmethod
83
+    def is_registered(cls, sid):
84
+        return (sid in cls.__sessions)
85
+
86
+
87
+    ## @brief checks if a session is expired
88
+    # @param sid str : session id
89
+    @classmethod
90
+    def is_expired(cls, sid):
91
+        expiration_timestamp = cls.get_last_modified(sid) + cls.EXPIRATION_LIMIT
92
+        now_timestamp = get_utc_timestamp()
93
+        return now_timestamp > expiration_timestamp
94
+
95
+    ## @brief gets the timestamp of the last modification
96
+    # @param sid str : session id
97
+    # @return float
98
+    @classmethod
99
+    @abstractmethod
100
+    def get_last_modified(cls, sid):
101
+        return 0.0
102
+
103
+    ## @brief generates a new session id
104
+    # @return sid
105
+    # @todo find a more secure way of building a session id
106
+    @classmethod
107
+    def generate_new_sid(cls):
108
+        return uuid1()
109
+
110
+    ## @brief sets a key's value in the session
111
+    # @param key str
112
+    # @param value unknown
113
+    def set_key(self, key, value):
114
+        setattr(self, key, value)
115
+        self.save()
116
+
117
+    ## @brief gets a key's value from the session
118
+    # @param key str
119
+    # @return unknown
120
+    def get_key(self, key):
121
+        return getattr(object=self, name=key, default=None)
122
+
123
+    ## @brief deletes a given key in the session
124
+    # @param key str
125
+    def delete_key(self, key):
126
+        delattr(p_object=self, key)
127
+        self.save()
128
+
129
+
130
+
131
+
132
+
133
+
134
+
135
+'''
10
 class SessionStore(ABC):
136
 class SessionStore(ABC):
11
 
137
 
12
     expiration_limit = 900
138
     expiration_limit = 900
13
 
139
 
140
+    _instance = None
141
+
142
+    def __init__(self, session_class=Session):
143
+        self.session_class = session_class
144
+        if self.__class__._instance is None:
145
+            self.__class__._instance = self.__class__()
146
+
147
+    def __getattr__(self, item):
148
+        return getattr(self.__class__._instance, item)
149
+
14
     ## @brief Generates a session unique ID
150
     ## @brief Generates a session unique ID
15
     # @param cls
151
     # @param cls
152
+    # @param salt str
16
     # @return str
153
     # @return str
17
     @classmethod
154
     @classmethod
18
-    def generate_new_sid(cls):
19
-        return uuid1()
155
+    def generate_new_sid(cls, salt=None):
156
+        return generate_key(salt)
20
 
157
 
21
     ## @brief Creates a new session
158
     ## @brief Creates a new session
22
     # @param content dict : session content (default: {})
159
     # @param content dict : session content (default: {})
87
     # @param content dict : items to update with their new value
224
     # @param content dict : items to update with their new value
88
     def update_session(self, sid, content):
225
     def update_session(self, sid, content):
89
         session = self.get_session(sid)
226
         session = self.get_session(sid)
90
-        for key, value in content.items():
91
-            if key != 'sid':
92
-                session[key] = value
93
-        self.save_session(sid, session)
227
+        if session is not None:
228
+            for key, value in content.items():
229
+                if key != 'sid':
230
+                    session[key] = value
231
+            self.save_session(sid, session)
232
+            return session
233
+        else:
234
+            session = self.create_new_session(content)
235
+            return session
94
 
236
 
95
     ## @brief gets a session's content
237
     ## @brief gets a session's content
96
     # @param sid str : id of the session to read
238
     # @param sid str : id of the session to read
103
                 self.delete_session(sid)
245
                 self.delete_session(sid)
104
                 session = None
246
                 session = None
105
         else:
247
         else:
106
-            raise AuthenticationError("No session file found for the sid %s" % sid)
248
+            session = None
107
 
249
 
108
         return session
250
         return session
251
+'''

+ 80
- 60
plugins/filesystem_session/filesystem_session_store.py View File

4
 import pickle
4
 import pickle
5
 import re
5
 import re
6
 
6
 
7
-from lodel.auth.exceptions import AuthenticationError
8
-from lodel.auth.session import SessionStore
7
+from lodel.auth.session import LodelSession
9
 from lodel.settings import Settings
8
 from lodel.settings import Settings
10
 
9
 
11
 
10
 
12
-class FileSystemSessionStore(SessionStore):
11
+class FileSystemSession(LodelSession):
13
 
12
 
14
-    ## @brief instanciates a FileSystemSessionStore
15
-    # @param base_directory str : path to the base directory containing the session files (default: session.directory param of the settings)
16
-    # @param file_name_template str : template for the session files name (default : session.file_template param of the settings)
17
-    # @param expiration_limit int : duration of a session validity without any action made (defaut : session.expiration param of the settings)
18
-    def __init__(self, base_directory=Settings.sessions.directory, file_name_template=Settings.sessions.file_template, expiration_limit=Settings.sessions.expiration):
19
-        self.expiration_limit = expiration_limit
20
-        self.base_directory = base_directory
21
-        self.file_name_template = file_name_template
13
+    __sessions = {}
22
 
14
 
23
-    # === CRUD === #
15
+    EXPIRATION_LIMIT = Settings.sessions.expiration
16
+    BASE_DIRECTORY = Settings.sessions.directory
17
+    FILENAME_TEMPLATE = Settings.sessions.file_template
24
 
18
 
25
-    ## @brief delete a session
26
-    # @param sid str : id of the session to be deleted
27
-    def delete_session(self, sid):
28
-        if self.is_session_existing(sid):
29
-            os.unlink(self.get_session_file_path(sid))
30
-        else:
31
-            raise AuthenticationError("No session file found for the sid %s" % sid)
19
+    def __init__(self):
20
+        self.path = None  # Is initialized in the store method
21
+        super().__init__()
32
 
22
 
33
-    ## @brief reads the content of a session
23
+    ## @brief stores the session
24
+    def store(self):
25
+        self.path = self.__class__.generate_session_file_name(self.sid)
26
+        self.save()
27
+
28
+    ## @brief registers the session in the active sessions list
29
+    def register_session(self):
30
+        self.__class__.__sessions[self.sid] = self.path
31
+
32
+    ## @brief saves the session
33
+    # @todo security
34
+    def save(self):
35
+        with open(self.path, 'wb') as session_file:
36
+            pickle.dump(self, session_file)
37
+
38
+    ## @brief loads a session
34
     # @param sid str : session id
39
     # @param sid str : session id
35
-    def read_session(self, sid):
36
-        session_file_path = self.get_session_file_path(sid)
37
-        with open(session_file_path, 'rb') as session_file:
38
-            session_content = pickle.load(session_file)
39
-        return session_content
40
+    # @return FileSystemSession
41
+    @classmethod
42
+    def load(cls, sid):
43
+        if sid in cls.__sessions.keys():
44
+            session_file_path = cls.__sessions[sid]
45
+            with open(session_file_path, 'rb') as session_file:
46
+                session = pickle.load(session_file)
47
+            return session
48
+        return None
40
 
49
 
41
-    ## @brief saves a session to a file
50
+    ## @brief cleans the session store
51
+    # @todo add more checks
52
+    @classmethod
53
+    def clean(cls):
54
+        # unregistered files in the session directory (if any)
55
+        session_dir_files = cls.list_all_session_files()
56
+        for session_dir_file in session_dir_files:
57
+            sid = cls.filename_to_sid(session_dir_file)
58
+            if sid is None or sid not in cls.__sessions.keys():
59
+                os.unlink(session_dir_file)
60
+        # registered sessions
61
+        for sid in cls.__sessions.keys():
62
+            if cls.is_expired(sid):
63
+                cls.destroy(sid)
64
+
65
+    ## @brief gets the last modified date of a session
42
     # @param sid str : session id
66
     # @param sid str : session id
43
-    # @param session dict : content to be saved
44
-    def save_session(self, sid, session):
45
-        session_file_path = self.get_session_file_path(sid)
46
-        with open(session_file_path, 'wb') as session_file:
47
-            pickle.dump(session, session_file)
67
+    @classmethod
68
+    def get_last_modified(cls, sid):
69
+        return os.stat(cls.__sessions[sid]).st_mtime
48
 
70
 
71
+    ## @brief lists all the files contained in the session directory
72
+    # @return list
73
+    @classmethod
74
+    def list_all_session_files(cls):
75
+        session_files_directory = os.abspath(cls.BASE_DIRECTORY)
76
+        files_list = [file_path for file_path in os.listdir(session_files_directory) if os.path.isfile(os.path.join(session_files_directory, file_path))]
77
+        return files_list
49
 
78
 
50
-    # === UTILS === #
51
     ## @brief returns the session id from the filename
79
     ## @brief returns the session id from the filename
52
-    # @param filename str : session file's name (not the complete path)
80
+    # @param filename str : session file's name
53
     # @return str
81
     # @return str
54
-    # @raise AuthenticationError : in case the sid could not be found for the given filename
55
-    def filename_to_sid(self,filename):
56
-        sid_regex = self.file_name_template % '(?P<sid>.*)'
57
-        sid_regex_compiled = re.compile(sid_regex)
58
-        sid_searching_result = sid_regex_compiled.match(filename)
82
+    @classmethod
83
+    def filename_to_sid(cls, filename):
84
+        sid_regex = re.compile(cls.FILENAME_TEMPLATE % '(?P<sid>.*)')
85
+        sid_searching_result = sid_regex.match(filename)
59
         if sid_searching_result is not None:
86
         if sid_searching_result is not None:
60
             return sid_searching_result.groupdict()['sid']
87
             return sid_searching_result.groupdict()['sid']
61
-        else:
62
-            raise AuthenticationError('No session id could be found for this filename')
88
+        return None
63
 
89
 
64
-    ## @brief lists all the session files paths
65
-    # @return list
66
-    def list_all_sessions(self):
67
-        session_files_directory = os.path.abspath(self.base_directory)
68
-        sid_list = [self.filename_to_sid(file_object) for file_object in os.listdir(session_files_directory) if os.path.isfile(os.path.join(session_files_directory, file_object))]
69
-        return sid_list
90
+    ## @brief deletes a session's informations
91
+    @classmethod
92
+    def delete_session(cls, sid):
93
+        if os.path.isfile(cls.__sessions[sid]):
94
+            # Deletes the session file
95
+            os.unlink(cls.__sessions[sid])
70
 
96
 
71
-    ## @brief returns the file path for a given session id
97
+    ## @brief generates session file name
72
     # @param sid str : session id
98
     # @param sid str : session id
73
     # @return str
99
     # @return str
74
-    def get_session_file_path(self, sid):
75
-        return os.path.join(self.base_directory, self.file_name_template) % sid
100
+    @classmethod
101
+    def generate_session_file_name(cls, sid):
102
+        return os.path.join(cls.BASE_DIRECTORY, cls.FILENAME_TEMPLATE) % sid
76
 
103
 
77
     ## @brief checks if a session exists
104
     ## @brief checks if a session exists
78
-    # @param sid str : session id
79
-    # @return bool
80
-    def is_session_existing(self, sid):
81
-        session_file = self.get_session_file_path(sid)
82
-        return os.path.is_file(session_file)
83
-
84
-    ## @brief gets a session's last modified timestamp
85
     # @param sid str: session id
105
     # @param sid str: session id
86
-    # @return float
87
-    def get_session_last_modified(self, sid):
88
-        session_file = self.get_session_file_path(sid)
89
-        return os.stat(session_file).st_mtime
106
+    # @return bool
107
+    @classmethod
108
+    def exists(cls, sid):
109
+        return cls.is_registered(sid) and (os.path.isfile(cls.__sessions[sid]))

+ 11
- 25
plugins/filesystem_session/main.py View File

3
 from lodel.auth.exceptions import AuthenticationError
3
 from lodel.auth.exceptions import AuthenticationError
4
 from lodel.plugin import LodelHook
4
 from lodel.plugin import LodelHook
5
 
5
 
6
-from .filesystem_session_store import FileSystemSessionStore
7
-
8
-session_store = FileSystemSessionStore()
9
-
6
+from .filesystem_session_store import FileSystemSession
10
 
7
 
11
 ## @brief starts a new session and returns its sid
8
 ## @brief starts a new session and returns its sid
12
 # @param caller *
9
 # @param caller *
14
 # @return str
11
 # @return str
15
 @LodelHook('session_start')
12
 @LodelHook('session_start')
16
 def start_session(caller, payload):
13
 def start_session(caller, payload):
17
-    return session_store.create_new_session(payload)
18
-
14
+    new_session = FileSystemSession(content=payload)
15
+    return new_session.sid
19
 
16
 
17
+'''
20
 ## @brief destroys a session
18
 ## @brief destroys a session
21
 # @param caller *
19
 # @param caller *
22
 # @param sid str : session id
20
 # @param sid str : session id
23
 @LodelHook('session_destroy')
21
 @LodelHook('session_destroy')
24
 def stop_session(caller, sid):
22
 def stop_session(caller, sid):
25
-    session_store.delete_session(sid)
26
-
23
+    FileSystemSession.destroy(sid)
24
+'''
27
 
25
 
28
 ## @brief reads a session content
26
 ## @brief reads a session content
29
 # @param caller *
27
 # @param caller *
30
 # @param sid str: session id
28
 # @param sid str: session id
31
-# @return dict
29
+# @return FileSystemSession
32
 @LodelHook('session_load')
30
 @LodelHook('session_load')
33
 def read_session(caller, sid):
31
 def read_session(caller, sid):
34
-    return session_store.get_session(sid)
35
-
32
+    return FileSystemSession.load(sid)
36
 
33
 
34
+'''
37
 ## @brief destroys all the old sessions (expired ones)
35
 ## @brief destroys all the old sessions (expired ones)
38
 # @param caller *
36
 # @param caller *
39
 @LodelHook('session_clean')
37
 @LodelHook('session_clean')
40
 def clean_sessions(caller):
38
 def clean_sessions(caller):
41
-    session_store.clean()
42
-
43
-
44
-## @brief updates the content of the session
45
-# @param caller *
46
-# @param payload dict : datas to insert/update in the session
47
-@LodelHook('update_session')
48
-def update_session_content(caller, payload):
49
-    if 'sid' in payload:
50
-        sid = payload['sid']
51
-        del payload['sid']
52
-        session_store.update_session(sid, payload)
53
-    else:
54
-        raise AuthenticationError("Session Update: Missing sid (session id) in the given payload argument")
39
+    FileSystemSession.clean()
40
+'''

Loading…
Cancel
Save