|
@@ -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
|