Pārlūkot izejas kodu

Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel

prieto 7 gadus atpakaļ
vecāks
revīzija
f0f1ed9a83

+ 29
- 32
lodel/auth/client.py Parādīt failu

@@ -121,31 +121,20 @@ a session is allready started !!!")
121 121
 #@todo Maybe we can delete this metaclass....
122 122
 class ClientMetaclass(type):
123 123
     
124
-    SESSION_ID_NAME = '__SESSION_ID__'
125
-
126 124
     def __init__(self, name, bases, attrs):
127
-        self.__session = dict()
128 125
         return super(ClientMetaclass, self).__init__(name, bases, attrs)
129 126
 
130 127
     def __getitem__(self, key):
131
-        if key not in self.__session:
132
-            raise KeyError("This client instance does not have a '%s' data" % key)
133
-        return self.__session[key]
128
+        return self.session()[key]
134 129
 
135
-    def __setitem__(self, key, value):
136
-        if SESSION_ID_NAME not in self.__session:
137
-            self.__session[SESSION_ID_NAME] = SessionHandler.start_session()
138
-        self.__session[key] = value
139
-    
140
-    def __token(self):
141
-        return None if SESSION_ID_NAME not in self.__sessions else self.__session[SESSION_ID_NAME]
130
+    def __delitem__(self, key):
131
+        del(self.session()[key])
142 132
 
143
-    ##@brief Return a copy of sessions infos
144
-    def session_dump(self): 
145
-        #first set all sessions values
146
-        SessionHandler.save_session(self.__session)
147
-        return copy.copy(self.__session)
133
+    def __setitem__(self, key, value):
134
+        self.session()[key] = value
148 135
 
136
+    def __str__(self):
137
+        return str(self._instance)
149 138
 
150 139
 ##@brief Abstract singleton class designed to handle client informations
151 140
 #
@@ -196,20 +185,6 @@ class Client(object, metaclass = ClientMetaclass):
196 185
         self.__session = LodelSession(session_token)
197 186
         logger.debug("New client : %s" % self)
198 187
     
199
-    ##@brief Attempt to restore a session given a session token
200
-    #@param token mixed : a session token
201
-    #@return Session datas (a dict)
202
-    #@throw ClientAuthenticationFailure if token is not valid or not
203
-    #existing
204
-    def _restore_session(self, token):
205
-        return self.__session.restore(token)
206
-    
207
-    ##@brief Return the current session token or None
208
-    #@return A session token or None
209
-    @classmethod
210
-    def session_token(cls):
211
-        return self.__session.retrieve_token()
212
-
213 188
     ##@brief Try to authenticate a user with a login and a password
214 189
     #@param login str : provided login
215 190
     #@param password str : provided password (hash)
@@ -245,6 +220,28 @@ class Client(object, metaclass = ClientMetaclass):
245 220
                 break
246 221
         if self.is_anon():
247 222
             self.fail() #Security logging
223
+    
224
+    ##@brief Attempt to restore a session given a session token
225
+    #@param token mixed : a session token
226
+    #@return Session datas (a dict)
227
+    #@throw ClientAuthenticationFailure if token is not valid or not
228
+    #existing
229
+    @classmethod
230
+    def restore_session(self, token):
231
+        cls._assert_instance()
232
+        return self.__session.restore(token)
233
+    
234
+    ##@brief Return the current session token or None
235
+    #@return A session token or None
236
+    @classmethod
237
+    def session_token(cls):
238
+        cls._assert_instance()
239
+        return cls._instance.__session.retrieve_token()
240
+
241
+    @classmethod
242
+    def session(cls):
243
+        cls._assert_instance()
244
+        return cls._instance.__session
248 245
 
249 246
     @classmethod
250 247
     def destroy(cls):

+ 8
- 6
lodel/auth/exceptions.py Parādīt failu

@@ -1,9 +1,10 @@
1 1
 from lodel import logger
2
+from lodel.plugin.hooks import LodelHook
2 3
 
3 4
 ##@brief Handles common errors with a Client
4 5
 class ClientError(Exception):
5 6
     ##@brief The logger function to use to log the error message
6
-    _loglvl = logger.warning
7
+    _loglvl = 'warning'
7 8
     ##@brief Error str
8 9
     _err_str = "Error"
9 10
     ##@brief the hook name to trigger with error
@@ -17,8 +18,9 @@ class ClientError(Exception):
17 18
     #"<client infos> : <_err_str>[ : <msg>]"
18 19
     def __init__(self, client, msg = ""):
19 20
         msg = self.build_message(client, msg)
20
-        if cls._loglvl is not None:
21
-            cls._loglvl(msg)
21
+        if self._loglvl is not None:
22
+            logfun = getattr(logger, self._loglvl)
23
+            logfun(msg)
22 24
         super().__init__(msg)
23 25
         if self._action is not None:
24 26
             LodelHook.call_hook(self._action, self, self._payload)
@@ -32,21 +34,21 @@ class ClientError(Exception):
32 34
 
33 35
 ##@brief Handles authentication failure errors
34 36
 class ClientAuthenticationFailure(ClientError):
35
-    _loglvl = logger.security
37
+    _loglvl = 'security'
36 38
     _err_str = 'Authentication failure'
37 39
     _action = 'lodel2_ui_authentication_failure'
38 40
 
39 41
 
40 42
 ##@brief Handles permission denied errors
41 43
 class ClientPermissionDenied(ClientError):
42
-    _loglvl = logger.security
44
+    _loglvl = 'security'
43 45
     _err_str = 'Permission denied'
44 46
     _action = 'lodel2_ui_permission_denied'
45 47
     
46 48
 
47 49
 ##@brief Handles common errors on authentication
48 50
 class ClientAuthenticationError(ClientError):
49
-    _loglvl = logger.error
51
+    _loglvl = 'error'
50 52
     _err_str = 'Authentication error'
51 53
     _action = 'lodel2_ui_error'
52 54
 

+ 5
- 4
lodel/plugin/core_scripts.py Parādīt failu

@@ -9,7 +9,8 @@ class DiscoverPlugin(lodel_script.LodelScript):
9 9
     
10 10
     @classmethod
11 11
     def argparser_config(cls, parser):
12
-        parser.add_argument('-d', '--directory',
12
+        #parser.add_argument('-d', '--directory',
13
+        parser.add_argument('PLUGIN_PATH',
13 14
             help="Directory to walk through looking for lodel2 plugins",
14 15
             nargs='+')
15 16
         parser.add_argument('-l', '--list-only', default=False,
@@ -20,11 +21,11 @@ without modifying existing cache")
20 21
     @classmethod
21 22
     def run(cls, args):
22 23
         from lodel.plugin.plugins import Plugin
23
-        if args.directory is None or len(args.directory) == 0:
24
+        if args.PLUGIN_PATH is None or len(args.PLUGIN_PATH) == 0:
24 25
             cls.help_exit("Specify a least one directory")
25 26
         no_cache = args.list_only
26
-        res = Plugin.discover(args.directory, no_cache)
27
-        print("Found plugins in : %s" % ', '.join(args.directory))
27
+        res = Plugin.discover(args.PLUGIN_PATH, no_cache)
28
+        print("Found plugins in : %s" % ', '.join(args.PLUGIN_PATH))
28 29
         for pname, pinfos in res['plugins'].items():
29 30
             print("\t- %s(%s) in %s" % (
30 31
                 pname, pinfos['version'], pinfos['path']))

+ 11
- 7
lodel/plugin/scripts.py Parādīt failu

@@ -142,19 +142,23 @@ def main_run():
142 142
     if len(sys.argv) == 1:
143 143
         default_parser.print_help()
144 144
         exit(1)
145
-    args = default_parser.parse_args()
146
-    if args.list_actions:
147
-        print("Available actions :")
148
-        for sname in sorted(__registered_scripts.keys()):
149
-            print("\t- %s" % __registered_scripts[sname])
150
-        exit(0)
151 145
     #preparing sys.argv (deleting action)
152 146
     action = sys.argv[1].lower()
153
-    del(sys.argv[1])
154 147
     if action not in __registered_scripts:
148
+        #Trying to parse argument with default parser
149
+        print("PASSAGE")
150
+        args = default_parser.parse_args()
151
+        if args.list_actions:
152
+            print("Available actions :")
153
+            for sname in sorted(__registered_scripts.keys()):
154
+                print("\t- %s" % __registered_scripts[sname])
155
+            exit(0)
156
+
155 157
         print("Unknow action '%s'\n" % action, file=sys.stderr)
156 158
         default_parser.print_help()
157 159
         exit(1)
160
+    #OK action is known, preparing argv to pass it to the action script
161
+    del(sys.argv[1])
158 162
     script = __registered_scripts[action]
159 163
     ret = script._run()
160 164
     ret = 0 if ret is None else ret

+ 7
- 0
plugins/dummy/main.py Parādīt failu

@@ -25,3 +25,10 @@ I'm a custom method on an instance of class %s" % self.__class__)
25 25
 def dummy_instance_method(self):
26 26
     print("Hello world !\
27 27
 I'm a custom method on class %s" % self.__class__)
28
+
29
+
30
+@LodelHook('lodel2_loader_main')
31
+def foofun(hname, caller, payload):
32
+    from lodel import dyncode
33
+    print("Hello world ! I read dyncode from lodel.dyncode : ",
34
+        dyncode.dynclasses)

+ 2
- 0
plugins/ram_sessions/main.py Parādīt failu

@@ -2,6 +2,7 @@
2 2
 import os
3 3
 import copy
4 4
 
5
+from lodel import logger
5 6
 from lodel.settings import Settings
6 7
 from lodel.auth.exceptions import *
7 8
 
@@ -24,6 +25,7 @@ def destroy_session(token):
24 25
 
25 26
 def restore_session(token):
26 27
     _check_token(token)
28
+    logger.debug("Restoring session : %s" %__sessions[token])
27 29
     return __sessions[token]
28 30
 
29 31
 def save_session(token, datas):

+ 84
- 29
plugins/webui/run.py Parādīt failu

@@ -2,14 +2,19 @@
2 2
 import loader # Lodel2 loader
3 3
 
4 4
 import os
5
+import hashlib
6
+import time
7
+
5 8
 from werkzeug.contrib.sessions import FilesystemSessionStore
6 9
 from werkzeug.wrappers import Response
10
+from werkzeug.contrib.securecookie import SecureCookie
7 11
 
8 12
 from lodel.settings import Settings
9 13
 from .interface.router import get_controller
10 14
 from .interface.lodelrequest import LodelRequest
11 15
 from .exceptions import *
12 16
 from .client import WebUiClient
17
+from lodel.auth.exceptions import *
13 18
 from lodel.utils.datetime import get_utc_timestamp
14 19
 from lodel.plugin.hooks import LodelHook
15 20
 
@@ -19,6 +24,48 @@ SESSION_EXPIRATION_LIMIT = Settings.webui.sessions.expiration
19 24
 
20 25
 session_store = FilesystemSessionStore(path=SESSION_FILES_BASE_DIR, filename_template=SESSION_FILES_TEMPLATE)
21 26
 
27
+COOKIE_SESSION_ID = 'toktoken'
28
+COOKIE_SESSION_HASH = 'nekotkot'
29
+COOKIE_SESSION_HASH_SALT = [ os.urandom(32) for _ in range(2) ] #Before and after salt (maybe useless)
30
+COOKIE_SESSION_HASH_ALGO = hashlib.sha512
31
+
32
+##@brief Return a salted hash of a cookie
33
+def cookie_hash(token):
34
+    return COOKIE_SESSION_HASH_ALGO(
35
+        COOKIE_SESSION_HASH_SALT[0]+token+COOKIE_SESSION_HASH_SALT[1]).hexdigest()
36
+    
37
+
38
+##@brief Load cookie from request
39
+#@note Can produce security warning logs
40
+#@param request
41
+#@return None or a session token
42
+def load_cookie(request):
43
+    token = request.cookies.get(COOKIE_SESSION_ID)
44
+    if token is None and token != '':
45
+        return None
46
+    token = bytes(token, 'utf-8')
47
+    hashtok = request.cookies.get(COOKIE_SESSION_HASH)
48
+    if hashtok is None:
49
+        raise ClientAuthenticationFailure(
50
+            WebUiClient, 'Bad cookies : no hash provided')
51
+    if cookie_hash(token) != hashtok:
52
+        raise ClientAuthenticationFailure(
53
+            WebUiClient, 'Bad cookies : hash mismatch')
54
+    return token
55
+
56
+##@brief Properly set cookies and hash given a token
57
+#@param response
58
+#@param token str : the session token
59
+def save_cookie(response, token):
60
+    response.set_cookie(COOKIE_SESSION_ID, token)
61
+    response.set_cookie(COOKIE_SESSION_HASH, cookie_hash(token))
62
+
63
+def empty_cookie(response):
64
+    response.set_cookie(COOKIE_SESSION_ID, '')
65
+    response.set_cookie(COOKIE_SESSION_HASH, '')
66
+    
67
+
68
+
22 69
 #Starting instance
23 70
 loader.start()
24 71
 #providing access to dyncode
@@ -49,38 +96,46 @@ def is_session_file_expired(timestamp_now, sid):
49 96
 
50 97
 # WSGI Application
51 98
 def application(env, start_response):
52
-    WebUiClient(env['REMOTE_ADDR'], env['HTTP_USER_AGENT'])
53
-    current_timestamp = get_utc_timestamp()
54
-    delete_old_session_files(current_timestamp)
55 99
     request = LodelRequest(env)
56
-    sid = request.cookies.get('sid')
57
-    if sid is None or sid not in session_store.list():
58
-        request.session = session_store.new()
59
-        request.session['last_accessed'] = current_timestamp
60
-    else:
61
-        request.session = session_store.get(sid)
62
-        if is_session_file_expired(current_timestamp, sid):
63
-            session_store.delete(request.session)
64
-            request.session = session_store.new()
65
-            request.session['user_context'] = None
66
-        request.session['last_accessed'] = current_timestamp
67
-    
100
+    session_token = None
68 101
     try:
69
-        controller = get_controller(request)
70
-        response = controller(request)
71
-    except HttpException as e:
102
+        #We have to create the client before restoring cookie in order to be able
103
+        #to log messages with client infos
104
+        client = WebUiClient(env['REMOTE_ADDR'], env['HTTP_USER_AGENT'], None)
105
+        session_token = load_cookie(request)
106
+
107
+        if session_token is not None:
108
+            WebClient.restore_session(token)
109
+        session_token = None
110
+        #test
111
+        WebUiClient['last_request'] = time.time()
72 112
         try:
73
-            response = e.render(request)
74
-        except Exception as eb:
75
-            res = Response()
76
-            res.status_code = 500
77
-            return res
78
-        
79
-        
80
-    if request.session.should_save:
81
-        session_store.save(request.session)
82
-        response.set_cookie('sid', request.session.sid)
83
-    
113
+            controller = get_controller(request)
114
+            response = controller(request)
115
+        except HttpException as e:
116
+            try:
117
+                response = e.render(request)
118
+            except Exception as eb:
119
+                raise eb
120
+                res = Response()
121
+                res.status_code = 500
122
+                return res
123
+        session_token = WebUiClient.session_token()
124
+        if session_token is not None:
125
+            save_cookie(response,session_token)
126
+        session_token = None
127
+            
128
+
129
+    except (ClientError, ClientAuthenticationError):
130
+        response = HttpException(400).render(request)
131
+        empty_cookie(response)
132
+    except ClientAuthenticationFailure:
133
+        response = HttpException(401).render(request)
134
+        empty_cookie(response)
135
+    except Exception as e:
136
+        raise e
137
+
84 138
     res = response(env, start_response)
139
+
85 140
     WebUiClient.destroy()
86 141
     return res

Notiek ielāde…
Atcelt
Saglabāt