Browse Source

Changed the sessions management layout

Roland Haroutiounian 8 years ago
parent
commit
a0818fe410

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

@@ -3,20 +3,157 @@
3 3
 from abc import ABC, abstractmethod
4 4
 from uuid import uuid1
5 5
 
6
+from werkzeug.contrib.sessions import generate_key, Session
7
+
6 8
 from lodel.auth.exceptions import AuthenticationError
7 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 136
 class SessionStore(ABC):
11 137
 
12 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 150
     ## @brief Generates a session unique ID
15 151
     # @param cls
152
+    # @param salt str
16 153
     # @return str
17 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 158
     ## @brief Creates a new session
22 159
     # @param content dict : session content (default: {})
@@ -87,10 +224,15 @@ class SessionStore(ABC):
87 224
     # @param content dict : items to update with their new value
88 225
     def update_session(self, sid, content):
89 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 237
     ## @brief gets a session's content
96 238
     # @param sid str : id of the session to read
@@ -103,6 +245,7 @@ class SessionStore(ABC):
103 245
                 self.delete_session(sid)
104 246
                 session = None
105 247
         else:
106
-            raise AuthenticationError("No session file found for the sid %s" % sid)
248
+            session = None
107 249
 
108 250
         return session
251
+'''

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

@@ -4,86 +4,106 @@ import os
4 4
 import pickle
5 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 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 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 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 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 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 86
         if sid_searching_result is not None:
60 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 98
     # @param sid str : session id
73 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 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 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,10 +3,7 @@
3 3
 from lodel.auth.exceptions import AuthenticationError
4 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 8
 ## @brief starts a new session and returns its sid
12 9
 # @param caller *
@@ -14,41 +11,30 @@ session_store = FileSystemSessionStore()
14 11
 # @return str
15 12
 @LodelHook('session_start')
16 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 18
 ## @brief destroys a session
21 19
 # @param caller *
22 20
 # @param sid str : session id
23 21
 @LodelHook('session_destroy')
24 22
 def stop_session(caller, sid):
25
-    session_store.delete_session(sid)
26
-
23
+    FileSystemSession.destroy(sid)
24
+'''
27 25
 
28 26
 ## @brief reads a session content
29 27
 # @param caller *
30 28
 # @param sid str: session id
31
-# @return dict
29
+# @return FileSystemSession
32 30
 @LodelHook('session_load')
33 31
 def read_session(caller, sid):
34
-    return session_store.get_session(sid)
35
-
32
+    return FileSystemSession.load(sid)
36 33
 
34
+'''
37 35
 ## @brief destroys all the old sessions (expired ones)
38 36
 # @param caller *
39 37
 @LodelHook('session_clean')
40 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