Преглед на файлове

Connection between WebUi Client & Session started

Yann Weber преди 8 години
родител
ревизия
eae2f83891
променени са 4 файла, в които са добавени 163 реда и са изтрити 157 реда
  1. 129
    7
      lodel/auth/client.py
  2. 0
    142
      lodel/auth/session.py
  3. 33
    7
      lodel/plugin/sessionhandler.py
  4. 1
    1
      plugins/webui/client.py

+ 129
- 7
lodel/auth/client.py Целия файл

@@ -1,14 +1,123 @@
1 1
 #-*- Coding: utf-8 -*-
2 2
 
3 3
 import copy
4
+import sys
5
+import warnings
4 6
 
5 7
 from lodel.settings import Settings
6 8
 from lodel import logger
7 9
 from lodel.plugin.hooks import LodelHook
8 10
 from lodel.plugin import SessionHandlerPlugin as SessionHandler
9 11
 
12
+##@brief Class designed to handle sessions and its datas
13
+class LodelSession(object):
14
+    
15
+    ##@brief Try to restore or to create a session
16
+    #@param token None | mixed : If None no session will be loaded nor created
17
+    #restore an existing session
18
+    #@throw  ClientAuthenticationFailure if a session restore fails
19
+    def __init__(self, token = None):
20
+        ##@brief Stores the session token
21
+        self.__token = token
22
+        ##@brief Stores the session datas
23
+        self.__datas = dict()
24
+        if token is not None:
25
+            self.restore(token)
26
+    
27
+    ##@brief A token reference checker to ensure token deletion at the end of
28
+    #a session
29
+    #@warning Cause maybe a performance issue !
30
+    def __token_checker(self):
31
+        refcount = sys.getrefcount(self.__token)
32
+        if refcount > 1:
33
+            warnings.warn("More than one reference to the session token \
34
+exists !")
35
+
36
+    ##@brief Property. Equals True if a session is started else False
37
+    @property
38
+    def started(self):
39
+        res = self.__token is None
40
+        if res:
41
+            self.__token_checker()
42
+        return res
43
+    
44
+    ##@brief Property that ensure ro acces to sessions datas
45
+    @property
46
+    def datas(self):
47
+        return copy.copy(self.__datas)
48
+    
49
+    ##@brief Return the session token
50
+    def retrieve_token(self):
51
+        # DO NOT COPY THE TOKEN TO ENSURE THAT NOT MORE THAN ONE REFERENCE TO
52
+        # IT EXISTS
53
+        return self.__token
54
+
55
+    ##@brief Late restore of a session
56
+    #@param token mixed : the session token
57
+    #@throw ClientAuthenticationError if a session was allready started
58
+    #@throw ClientAuthenticationFailure if no session exists with this token
59
+    def restore(self, token):
60
+        if self.started:
61
+            raise ClientAuthenticationError("Trying to restore a session, but \
62
+a session is allready started !!!")
63
+        self.__datas = SessionHandler.restore(token)
64
+        return self.datas
65
+
66
+    ##@brief Save the current session state
67
+    def save(self):
68
+        if not self.started:
69
+            raise ClientAuthenticationError(
70
+                "Trying to save a non started session")
71
+        SessionHandler.save(self.__token)
72
+    
73
+    ##@brief Destroy a session
74
+    def destroy(self):
75
+        if not self.started:
76
+            logger.debug("Destroying a session that is not started")
77
+        SessionHandler.destroy(self.__token)
78
+        self.__token = None
79
+        self.__datas = dict()
80
+    
81
+    ##@brief Destructor
82
+    def __del__(self):
83
+        del(self.__token)
84
+        del(self.__datas)
85
+
86
+    ##@brief Implements setter for dict access to instance
87
+    #@todo Custom exception throwing
88
+    def __setitem__(self, key, value):
89
+        self.__init_session() #Start the sesssion
90
+        self.__datas[key] = value
91
+    
92
+    ##@brief Implements destructor for dict access to instance
93
+    #@todo Custom exception throwing
94
+    def __delitem__(self, key):
95
+        if not self.started:
96
+            raise ClientAuthenticationError(
97
+                "Data read access to a non started session is not possible")
98
+        del(self.__datas[key])
99
+    
100
+    ##@brief Implements getter for dict acces to instance
101
+    #@todo Custom exception throwing
102
+    def __getitem__(self, key):
103
+        if not self.started:
104
+            raise ClientAuthenticationError(
105
+                "Data read access to a non started session is not possible")
106
+        return self.__datas[key]
107
+
108
+    ##@brief Start a new session
109
+    #@note start a new session only if no session started yet
110
+    def __init_session(self):
111
+        if self.__token is not None:
112
+            return
113
+        self.__token = SessionHandler.start()
114
+            
115
+        
116
+
10 117
 ##@brief Client metaclass designed to implements container accessor on 
11 118
 #Client Class
119
+#
120
+#@todo Maybe we can delete this metaclass....
12 121
 class ClientMetaclass(type):
13 122
     
14 123
     SESSION_ID_NAME = '__SESSION_ID__'
@@ -27,11 +136,14 @@ class ClientMetaclass(type):
27 136
             self.__session[SESSION_ID_NAME] = SessionHandler.start_session()
28 137
         self.__session[key] = value
29 138
     
139
+    def __token(self):
140
+        return None if SESSION_ID_NAME not in self.__sessions else self.__session[SESSION_ID_NAME]
141
+
30 142
     ##@brief Return a copy of sessions infos
31 143
     def session_dump(self): 
144
+        #first set all sessions values
145
+        SessionHandler.save_session(self.__session)
32 146
         return copy.copy(self.__session)
33
-        
34
-            
35 147
 
36 148
 
37 149
 ##@brief Abstract singleton class designed to handle client informations
@@ -55,6 +167,10 @@ class Client(object, metaclass = ClientMetaclass):
55 167
     #- password typle contains (LeObjectChild, FieldName)
56 168
     _infos_fields = None
57 169
     
170
+    ##@brief Constant that stores the session key that stores authentication
171
+    #informations
172
+    _AUTH_DATANAME = '__auth_user_infos'
173
+    
58 174
 
59 175
     ##@brief Constructor
60 176
     #@param ui_instance Lodel2Ui child class instance
@@ -79,6 +195,8 @@ class Client(object, metaclass = ClientMetaclass):
79 195
         self.__user = None
80 196
         ##@brief Stores the session handler
81 197
         Client._instance = self
198
+        ##@brief Stores LodelSession instance
199
+        self.__session = LodelSession(token)
82 200
         logger.debug("New client : %s" % self)
83 201
     
84 202
     ##@brief Attempt to restore a session given a session token
@@ -87,11 +205,13 @@ class Client(object, metaclass = ClientMetaclass):
87 205
     #@throw ClientAuthenticationFailure if token is not valid or not
88 206
     #existing
89 207
     def _restore_session(self, token):
90
-        res = self._session_handler.restore_session(token)
91
-        if res is False:
92
-            raise ClientAuthenticationFailure(client = self,
93
-                msg = "Invalid or not existing session token provided")
94
-        pass
208
+        return self.__session.restore(token)
209
+    
210
+    ##@brief Return the current session token or None
211
+    #@return A session token or None
212
+    @classmethod
213
+    def session_token(cls):
214
+        return self.__session.retrieve_token()
95 215
 
96 216
     ##@brief Try to authenticate a user with a login and a password
97 217
     #@param login str : provided login
@@ -216,5 +336,7 @@ login EmClass '%s' and password EmClass '%s'. Abording..." % (
216 336
     #@return None
217 337
     def __set_authenticated(self, leo, uid):
218 338
         self.__user = {'classname': leo.__name__, 'uid': uid, 'leoclass': leo}
339
+        #Store auth infos in session
340
+        self.__session[self.__class__._AUTH_DATANAME] = copy.copy(self.__user)
219 341
         
220 342
     

+ 0
- 142
lodel/auth/session.py Целия файл

@@ -1,142 +0,0 @@
1
-# -*- coding; utf-8 -*-
2
-
3
-from abc import ABC, abstractmethod
4
-from uuid import uuid1
5
-
6
-from werkzeug.contrib.sessions import generate_key, Session
7
-
8
-#from lodel.auth.exceptions import AuthenticationError
9
-from lodel.utils.datetime import get_utc_timestamp
10
-
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(self, key)
127
-        self.save()
128
-
129
-    ## @brief lists all the sessions in the session store
130
-    # @return list
131
-    @classmethod
132
-    @abstractmethod
133
-    def list_all_sessions(cls):
134
-        return []
135
-
136
-    ## @brief checks if a given session id corresponds to an existing session
137
-    # @param sid str: session id
138
-    # @return bool
139
-    @classmethod
140
-    @abstractmethod
141
-    def exists(cls, sid):
142
-        return True

+ 33
- 7
lodel/plugin/sessionhandler.py Целия файл

@@ -1,7 +1,32 @@
1
-from .plugins import Plugin
1
+from .plugins import Plugin, MetaPlugType
2 2
 from .exceptions import *
3 3
 from lodel.settings.validator import SettingValidator
4 4
 
5
+##@brief SessionHandlerPlugin metaclass designed to implements a wrapper
6
+#between SessionHandlerPlugin classmethod and plugin loader functions
7
+class SessionPluginWrapper(MetaPlugType):
8
+    ##@brief Constant that stores all possible session actions
9
+    #
10
+    #Key is the SessionHandlerPlugin method name and value is SessionHandler
11
+    #plugin function name
12
+    _ACTIONS = {
13
+        'start': 'start_session',
14
+        'destroy': 'destroy_session', 
15
+        'restore': 'restore_session',
16
+        'save': 'save_session'}
17
+
18
+    def __init__(self, name, bases, attrs):
19
+        super().__init__(name, bases, attrs)
20
+        
21
+    ##@brief Handles wrapper between class method and plugin loader functions
22
+    def __get_attribute__(self, name):
23
+        if name in SessionPluginWrapper._Actions:
24
+            return getattr(
25
+                self._instance.loader_module(),
26
+                SessionPluginWrapper._Actions[name])
27
+        return super().__get_attribute__(name)
28
+
29
+
5 30
 ##@page lodel2_plugins Lodel2 plugins system
6 31
 #
7 32
 # @par Plugin structure
@@ -11,18 +36,19 @@ from lodel.settings.validator import SettingValidator
11 36
 # - an _activate() method that returns True if the plugin can be activated (
12 37
 # optionnal)
13 38
 #
14
-class SessionHandlerPlugin(Plugin):
15
-    __instance = None
39
+class SessionHandlerPlugin(Plugin, metaclass=SessionPluginWrapper): 
40
+    ##@brief Stores the singleton instance
41
+    _instance = None
42
+
16 43
     _plist_confspecs = {
17 44
         'section': 'lodel2',
18 45
         'key': 'session_handler',
19 46
         'default': None,
20 47
         'validator': SettingValidator('string', none_is_valid=False)}
21
-        
48
+            
22 49
     def __init__(self, plugin_name):
23
-        if self.__instance is None:
50
+        if self._instance is None:
24 51
             super(Plugin, self).__init__(plugin_name)
25
-            self.__instance = True
52
+            self._instance = self
26 53
         else:
27 54
             raise RuntimeError("A SessionHandler Plugin is already plug")
28
-

+ 1
- 1
plugins/webui/client.py Целия файл

@@ -1,4 +1,4 @@
1
-from lodel.auth.auth import Client
1
+from lodel.auth.client import Client
2 2
 
3 3
 class WebUiClient(Client):
4 4
     

Loading…
Отказ
Запис