navigator-session 0.5.7__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- navigator_session/__init__.py +6 -3
- navigator_session/conf.py +7 -3
- navigator_session/data.py +17 -6
- navigator_session/middleware.py +2 -0
- navigator_session/session.py +8 -5
- navigator_session/storages/__init__.py +0 -5
- navigator_session/storages/abstract.py +46 -26
- navigator_session/storages/cookie.py +1 -1
- navigator_session/storages/redis.py +109 -48
- navigator_session/version.py +1 -1
- {navigator_session-0.5.7.dist-info → navigator_session-0.6.0.dist-info}/METADATA +1 -1
- navigator_session-0.6.0.dist-info/RECORD +15 -0
- navigator_session-0.5.7.dist-info/RECORD +0 -15
- {navigator_session-0.5.7.dist-info → navigator_session-0.6.0.dist-info}/LICENSE +0 -0
- {navigator_session-0.5.7.dist-info → navigator_session-0.6.0.dist-info}/WHEEL +0 -0
- {navigator_session-0.5.7.dist-info → navigator_session-0.6.0.dist-info}/top_level.txt +0 -0
navigator_session/__init__.py
CHANGED
|
@@ -12,6 +12,8 @@ from .conf import (
|
|
|
12
12
|
AUTH_SESSION_OBJECT,
|
|
13
13
|
SESSION_TIMEOUT,
|
|
14
14
|
SESSION_KEY,
|
|
15
|
+
SESSION_ID,
|
|
16
|
+
SESSION_REQUEST_KEY,
|
|
15
17
|
SESSION_URL,
|
|
16
18
|
SESSION_PREFIX,
|
|
17
19
|
SESSION_USER_PROPERTY
|
|
@@ -26,6 +28,7 @@ __all__ = (
|
|
|
26
28
|
'SESSION_URL',
|
|
27
29
|
'SESSION_PREFIX',
|
|
28
30
|
'SESSION_KEY',
|
|
31
|
+
'SESSION_ID',
|
|
29
32
|
'SESSION_USER_PROPERTY',
|
|
30
33
|
)
|
|
31
34
|
|
|
@@ -37,7 +40,7 @@ async def new_session(request: web.Request, userdata: dict = None) -> SessionDat
|
|
|
37
40
|
storage = request.get(SESSION_STORAGE)
|
|
38
41
|
if storage is None:
|
|
39
42
|
raise RuntimeError(
|
|
40
|
-
"Missing Configuration for Session Middleware
|
|
43
|
+
"Missing Configuration for NAV Session Middleware."
|
|
41
44
|
)
|
|
42
45
|
session = await storage.new_session(request, userdata)
|
|
43
46
|
if not isinstance(session, SessionData):
|
|
@@ -75,7 +78,7 @@ async def get_session(
|
|
|
75
78
|
storage = request.get(SESSION_STORAGE)
|
|
76
79
|
if storage is None:
|
|
77
80
|
raise RuntimeError(
|
|
78
|
-
"Missing Configuration
|
|
81
|
+
"Missing Configuration of Session Storage, please install it."
|
|
79
82
|
)
|
|
80
83
|
# using the storage session for Load an existing Session
|
|
81
84
|
try:
|
|
@@ -90,7 +93,7 @@ async def get_session(
|
|
|
90
93
|
f"Error Loading user Session: {err!s}"
|
|
91
94
|
) from err
|
|
92
95
|
request[SESSION_OBJECT] = session
|
|
93
|
-
request[
|
|
96
|
+
request[SESSION_REQUEST_KEY] = session
|
|
94
97
|
if new is True and not isinstance(session, SessionData):
|
|
95
98
|
raise RuntimeError(
|
|
96
99
|
f"Installed {session!r} storage should return session instance "
|
navigator_session/conf.py
CHANGED
|
@@ -22,9 +22,12 @@ SESSION_NAME = f"{APP_TITLE}_SESSION"
|
|
|
22
22
|
JWT_ALGORITHM = config.get("JWT_ALGORITHM", fallback="HS256")
|
|
23
23
|
SESSION_PREFIX = f'{SESSION_PREFIX}_session'
|
|
24
24
|
SESSION_TIMEOUT = config.getint('SESSION_TIMEOUT', fallback=360000)
|
|
25
|
-
SESSION_STORAGE =
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
SESSION_STORAGE = config.get(
|
|
26
|
+
'NAV_SESSION_STORAGE',
|
|
27
|
+
fallback='NAVIGATOR_SESSION_STORAGE'
|
|
28
|
+
)
|
|
29
|
+
SESSION_OBJECT = config.get('SESSION_OBJECT', fallback='NAV_SESSION')
|
|
30
|
+
SESSION_REQUEST_KEY = config.get('SESSION_REQUEST_KEY', fallback='session')
|
|
28
31
|
|
|
29
32
|
# SESSION BACKEND:
|
|
30
33
|
SESSION_BACKEND = config.get('SESSION_BACKEND', fallback='redis')
|
|
@@ -38,4 +41,5 @@ SESSION_URL = f"{SESSION_BACKEND}://{REDIS_HOST}:{REDIS_PORT}/{REDIS_SESSION_DB}
|
|
|
38
41
|
# User Attributes:
|
|
39
42
|
SESSION_USER_PROPERTY = config.get('SESSION_USER_PROPERTY', fallback='user')
|
|
40
43
|
SESSION_KEY = config.get('SESSION_KEY', fallback='id')
|
|
44
|
+
SESSION_ID = config.get('SESSION_ID', fallback='session_id')
|
|
41
45
|
SESSION_COOKIE_SECURE = config.get('SESSION_COOKIE_SECURE', fallback='csrf_secure')
|
navigator_session/data.py
CHANGED
|
@@ -8,8 +8,11 @@ import jsonpickle
|
|
|
8
8
|
from jsonpickle.unpickler import loadclass
|
|
9
9
|
from aiohttp import web
|
|
10
10
|
from datamodel import BaseModel
|
|
11
|
-
from .conf import
|
|
12
|
-
|
|
11
|
+
from .conf import (
|
|
12
|
+
SESSION_KEY,
|
|
13
|
+
SESSION_ID,
|
|
14
|
+
SESSION_STORAGE
|
|
15
|
+
)
|
|
13
16
|
|
|
14
17
|
class ModelHandler(jsonpickle.handlers.BaseHandler):
|
|
15
18
|
"""ModelHandler.
|
|
@@ -43,14 +46,20 @@ class SessionData(MutableMapping[str, Any]):
|
|
|
43
46
|
*args,
|
|
44
47
|
data: Optional[Mapping[str, Any]] = None,
|
|
45
48
|
new: bool = False,
|
|
49
|
+
id: Optional[str] = None,
|
|
46
50
|
identity: Optional[Any] = None,
|
|
47
51
|
max_age: Optional[int] = None
|
|
48
52
|
) -> None:
|
|
49
53
|
self._changed = False
|
|
50
54
|
self._data = {}
|
|
55
|
+
# Unique ID:
|
|
56
|
+
self._id_ = data.get(SESSION_ID, None) if data else id
|
|
57
|
+
if not self._id_:
|
|
58
|
+
self._id_ = uuid.uuid4().hex
|
|
59
|
+
# Session Identity
|
|
51
60
|
self._identity = data.get(SESSION_KEY, None) if data else identity
|
|
52
61
|
if not self._identity:
|
|
53
|
-
self._identity =
|
|
62
|
+
self._identity = self._id_
|
|
54
63
|
self._new = new if data != {} else True
|
|
55
64
|
self._max_age = max_age if max_age else None
|
|
56
65
|
created = data.get('created', None) if data else None
|
|
@@ -75,9 +84,7 @@ class SessionData(MutableMapping[str, Any]):
|
|
|
75
84
|
self.args = args
|
|
76
85
|
|
|
77
86
|
def __repr__(self) -> str:
|
|
78
|
-
return '<
|
|
79
|
-
'NAV-Session ', self.new, self.created, self._data
|
|
80
|
-
)
|
|
87
|
+
return f'<NAV-Session [new:{self.new}, created:{self.created}] {self._data!r}>'
|
|
81
88
|
|
|
82
89
|
@property
|
|
83
90
|
def new(self) -> bool:
|
|
@@ -87,6 +94,10 @@ class SessionData(MutableMapping[str, Any]):
|
|
|
87
94
|
def logon_time(self) -> datetime:
|
|
88
95
|
return self.__created__
|
|
89
96
|
|
|
97
|
+
@property
|
|
98
|
+
def session_id(self) -> str:
|
|
99
|
+
return self._id_
|
|
100
|
+
|
|
90
101
|
@property
|
|
91
102
|
def identity(self) -> Optional[Any]: # type: ignore[misc]
|
|
92
103
|
return self._identity
|
navigator_session/middleware.py
CHANGED
|
@@ -10,8 +10,10 @@ from .conf import (
|
|
|
10
10
|
from .storages.abstract import AbstractStorage
|
|
11
11
|
from .data import SessionData
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
Middleware = Callable[[web.Request, Handler], Awaitable[web.StreamResponse]]
|
|
14
15
|
|
|
16
|
+
|
|
15
17
|
### Basic Middleware for Session System
|
|
16
18
|
def session_middleware(
|
|
17
19
|
app: web.Application, # pylint: disable=W0613
|
navigator_session/session.py
CHANGED
|
@@ -10,8 +10,11 @@ class SessionHandler:
|
|
|
10
10
|
|
|
11
11
|
def __init__(self, storage: str = 'redis', **kwargs) -> None:
|
|
12
12
|
# TODO: Session Support with parametrization (other storages):
|
|
13
|
+
self._session_object: str = kwargs.get('session_object', 'nav_session')
|
|
14
|
+
# Logging Object:
|
|
15
|
+
self.logger = logging.getLogger(self.__class__.__name__)
|
|
13
16
|
if storage == 'redis':
|
|
14
|
-
self.storage = RedisStorage(**kwargs)
|
|
17
|
+
self.storage = RedisStorage(logger=self.logger, **kwargs)
|
|
15
18
|
else:
|
|
16
19
|
raise NotImplementedError(
|
|
17
20
|
f"Cannot load a Session Storage {storage}"
|
|
@@ -34,9 +37,9 @@ class SessionHandler:
|
|
|
34
37
|
self.app.on_cleanup.append(
|
|
35
38
|
self.session_cleanup
|
|
36
39
|
)
|
|
37
|
-
|
|
40
|
+
self.logger.debug(':::: Session Handler Loaded ::::')
|
|
38
41
|
# register the Auth extension into the app
|
|
39
|
-
self.app[
|
|
42
|
+
self.app[self._session_object] = self
|
|
40
43
|
|
|
41
44
|
async def session_startup(self, app: web.Application):
|
|
42
45
|
"""
|
|
@@ -45,7 +48,7 @@ class SessionHandler:
|
|
|
45
48
|
try:
|
|
46
49
|
await self.storage.on_startup(app)
|
|
47
50
|
except Exception as ex:
|
|
48
|
-
|
|
51
|
+
self.logger.exception(f'{ex}')
|
|
49
52
|
raise RuntimeError(
|
|
50
53
|
f"Session Storage Error: cannot start Storage Backend {ex}"
|
|
51
54
|
) from ex
|
|
@@ -57,7 +60,7 @@ class SessionHandler:
|
|
|
57
60
|
try:
|
|
58
61
|
await self.storage.on_cleanup(app)
|
|
59
62
|
except Exception as ex:
|
|
60
|
-
|
|
63
|
+
self.logger.exception(f'{ex}')
|
|
61
64
|
raise RuntimeError(
|
|
62
65
|
f"Session Storage Error: cannot start Storage Backend {ex}"
|
|
63
66
|
) from ex
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
"""Base Class for all Session Storages."""
|
|
2
2
|
import sys
|
|
3
|
-
import
|
|
3
|
+
from abc import ABCMeta, abstractmethod
|
|
4
4
|
import uuid
|
|
5
5
|
import time
|
|
6
6
|
import logging
|
|
7
7
|
from typing import Optional
|
|
8
8
|
from aiohttp import web
|
|
9
9
|
from datamodel.parsers.encoders import DefaultEncoder
|
|
10
|
+
from datamodel.parsers.json import ( # pylint: disable=C0411
|
|
11
|
+
json_encoder,
|
|
12
|
+
json_decoder
|
|
13
|
+
)
|
|
10
14
|
from ..conf import (
|
|
11
15
|
SESSION_TIMEOUT,
|
|
12
16
|
SESSION_KEY,
|
|
17
|
+
SESSION_ID,
|
|
13
18
|
SESSION_OBJECT,
|
|
19
|
+
SESSION_REQUEST_KEY,
|
|
14
20
|
SESSION_COOKIE_SECURE
|
|
15
21
|
)
|
|
16
22
|
from ..data import SessionData
|
|
@@ -30,13 +36,14 @@ class _CookieParams(TypedDict, total=False):
|
|
|
30
36
|
samesite: Optional[str]
|
|
31
37
|
expires: str
|
|
32
38
|
|
|
33
|
-
class AbstractStorage(metaclass=
|
|
39
|
+
class AbstractStorage(metaclass=ABCMeta):
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
_use_cookies: bool = False
|
|
36
42
|
|
|
37
43
|
def __init__(
|
|
38
44
|
self,
|
|
39
45
|
*,
|
|
46
|
+
logger: Optional[logging.Logger] = None,
|
|
40
47
|
max_age: int = None,
|
|
41
48
|
secure: bool = True,
|
|
42
49
|
domain: Optional[str] = None,
|
|
@@ -45,13 +52,16 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
45
52
|
samesite: Optional[str] = 'Lax',
|
|
46
53
|
**kwargs
|
|
47
54
|
) -> None:
|
|
55
|
+
if logger is not None:
|
|
56
|
+
self._logger = logger
|
|
57
|
+
else:
|
|
58
|
+
self._logger = logging.getLogger('Nav_Session.Storage')
|
|
48
59
|
if not max_age:
|
|
49
60
|
self.max_age = SESSION_TIMEOUT
|
|
50
61
|
else:
|
|
51
62
|
self.max_age = max_age
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
del kwargs['use_cookie']
|
|
63
|
+
# Using session cookies:
|
|
64
|
+
self._use_cookies = kwargs.get('use_cookies', False)
|
|
55
65
|
# Storage Name
|
|
56
66
|
self.__name__: str = SESSION_COOKIE_SECURE
|
|
57
67
|
self._domain: Optional[str] = domain
|
|
@@ -61,8 +71,8 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
61
71
|
self._httponly = httponly
|
|
62
72
|
self._samesite = samesite
|
|
63
73
|
self._objencoder = DefaultEncoder()
|
|
64
|
-
self._encoder =
|
|
65
|
-
self._decoder =
|
|
74
|
+
self._encoder = json_encoder
|
|
75
|
+
self._decoder = json_decoder
|
|
66
76
|
|
|
67
77
|
def id_factory(self) -> str:
|
|
68
78
|
return uuid.uuid4().hex
|
|
@@ -71,15 +81,15 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
71
81
|
def cookie_name(self) -> str:
|
|
72
82
|
return self.__name__
|
|
73
83
|
|
|
74
|
-
@
|
|
84
|
+
@abstractmethod
|
|
75
85
|
async def on_startup(self, app: web.Application):
|
|
76
86
|
pass
|
|
77
87
|
|
|
78
|
-
@
|
|
88
|
+
@abstractmethod
|
|
79
89
|
async def on_cleanup(self, app: web.Application):
|
|
80
90
|
pass
|
|
81
91
|
|
|
82
|
-
@
|
|
92
|
+
@abstractmethod
|
|
83
93
|
async def new_session(
|
|
84
94
|
self,
|
|
85
95
|
request: web.Request,
|
|
@@ -87,7 +97,7 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
87
97
|
) -> SessionData:
|
|
88
98
|
pass
|
|
89
99
|
|
|
90
|
-
@
|
|
100
|
+
@abstractmethod
|
|
91
101
|
async def load_session(
|
|
92
102
|
self,
|
|
93
103
|
request: web.Request,
|
|
@@ -98,14 +108,19 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
98
108
|
) -> SessionData:
|
|
99
109
|
pass
|
|
100
110
|
|
|
101
|
-
@
|
|
111
|
+
@abstractmethod
|
|
102
112
|
async def get_session(self, request: web.Request) -> SessionData:
|
|
103
113
|
pass
|
|
104
114
|
|
|
105
115
|
def empty_session(self) -> SessionData:
|
|
106
|
-
return SessionData(
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
return SessionData(
|
|
117
|
+
None,
|
|
118
|
+
data=None,
|
|
119
|
+
new=True,
|
|
120
|
+
max_age=self.max_age
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
109
124
|
async def save_session(
|
|
110
125
|
self,
|
|
111
126
|
request: web.Request,
|
|
@@ -114,40 +129,45 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
114
129
|
) -> None:
|
|
115
130
|
pass
|
|
116
131
|
|
|
117
|
-
@
|
|
132
|
+
@abstractmethod
|
|
118
133
|
async def invalidate(
|
|
119
134
|
self,
|
|
120
135
|
request: web.Request,
|
|
121
136
|
session: SessionData
|
|
122
137
|
) -> None:
|
|
123
138
|
"""Try to Invalidate the Session in the Storage."""
|
|
139
|
+
pass
|
|
124
140
|
|
|
125
141
|
async def forgot(self, request: web.Request, response: web.StreamResponse = None):
|
|
126
|
-
"""
|
|
142
|
+
"""forgot.
|
|
143
|
+
|
|
144
|
+
Forgot (delete) a user session.
|
|
145
|
+
"""
|
|
127
146
|
session = await self.get_session(request)
|
|
128
147
|
await self.invalidate(request, session)
|
|
129
|
-
request[
|
|
148
|
+
request[SESSION_REQUEST_KEY] = None
|
|
130
149
|
try:
|
|
131
150
|
del request[SESSION_KEY]
|
|
151
|
+
del request[SESSION_ID]
|
|
132
152
|
del request[SESSION_OBJECT]
|
|
133
153
|
except Exception as err: # pylint: disable=W0703
|
|
134
|
-
|
|
154
|
+
self._logger.warning(
|
|
135
155
|
f'Session: Error on Forgot Method: {err}'
|
|
136
156
|
)
|
|
137
157
|
if response is not None:
|
|
138
158
|
# also, forgot the secure Cookie:
|
|
139
|
-
self.
|
|
159
|
+
self.forgot_cookie(response)
|
|
140
160
|
|
|
141
161
|
def load_cookie(self, request: web.Request) -> str:
|
|
142
162
|
"""Getting Cookie from User (if needed)"""
|
|
143
|
-
if self.
|
|
163
|
+
if self._use_cookies is True:
|
|
144
164
|
cookie = request.cookies.get(self.__name__, None)
|
|
145
165
|
if cookie:
|
|
146
166
|
return self._decoder(cookie)
|
|
147
167
|
return None
|
|
148
168
|
|
|
149
|
-
def
|
|
150
|
-
if self.
|
|
169
|
+
def forgot_cookie(self, response: web.StreamResponse) -> None:
|
|
170
|
+
if self._use_cookies is True:
|
|
151
171
|
response.del_cookie(
|
|
152
172
|
self.__name__, domain=self._domain, path=self._path
|
|
153
173
|
)
|
|
@@ -159,7 +179,7 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
159
179
|
*,
|
|
160
180
|
max_age: Optional[int] = None,
|
|
161
181
|
) -> None:
|
|
162
|
-
if self.
|
|
182
|
+
if self._use_cookies is True:
|
|
163
183
|
expires = None
|
|
164
184
|
if max_age is not None:
|
|
165
185
|
t = time.gmtime(time.time() + max_age)
|
|
@@ -199,6 +219,6 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
199
219
|
session.headers = request.headers
|
|
200
220
|
session.rel_url = request.rel_url
|
|
201
221
|
except (TypeError, AttributeError, ValueError) as ex:
|
|
202
|
-
|
|
222
|
+
self._logger.warning(f'Unable to read Request info: {ex}')
|
|
203
223
|
### modified Session Object:
|
|
204
224
|
return session
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Using Redis for Saving Session Storage."""
|
|
2
2
|
import time
|
|
3
|
-
import logging
|
|
4
3
|
from typing import Optional
|
|
5
4
|
from collections.abc import Callable
|
|
6
5
|
from aiohttp import web
|
|
@@ -8,7 +7,9 @@ from redis import asyncio as aioredis
|
|
|
8
7
|
from ..conf import (
|
|
9
8
|
SESSION_URL,
|
|
10
9
|
SESSION_KEY,
|
|
10
|
+
SESSION_ID,
|
|
11
11
|
SESSION_OBJECT,
|
|
12
|
+
SESSION_REQUEST_KEY,
|
|
12
13
|
SESSION_STORAGE
|
|
13
14
|
)
|
|
14
15
|
from .abstract import AbstractStorage, SessionData
|
|
@@ -44,14 +45,14 @@ class RedisStorage(AbstractStorage):
|
|
|
44
45
|
encoding='utf-8'
|
|
45
46
|
)
|
|
46
47
|
except Exception as err: # pylint: disable=W0703
|
|
47
|
-
|
|
48
|
+
self._logger.exception(err, stack_info=True)
|
|
48
49
|
return False
|
|
49
50
|
|
|
50
51
|
async def on_cleanup(self, app: web.Application):
|
|
51
52
|
try:
|
|
52
53
|
await self._redis.disconnect(inuse_connections=True)
|
|
53
54
|
except Exception as ex: # pylint: disable=W0703
|
|
54
|
-
|
|
55
|
+
self._logger.warning(ex)
|
|
55
56
|
|
|
56
57
|
async def get_session(
|
|
57
58
|
self,
|
|
@@ -61,7 +62,9 @@ class RedisStorage(AbstractStorage):
|
|
|
61
62
|
try:
|
|
62
63
|
session = request.get(SESSION_OBJECT)
|
|
63
64
|
except Exception as err: # pylint: disable=W0703
|
|
64
|
-
|
|
65
|
+
self._logger.debug(
|
|
66
|
+
f'Redis Storage: Error on get Session: {err!s}'
|
|
67
|
+
)
|
|
65
68
|
session = None
|
|
66
69
|
if session is None:
|
|
67
70
|
storage = request.get(SESSION_STORAGE)
|
|
@@ -71,28 +74,40 @@ class RedisStorage(AbstractStorage):
|
|
|
71
74
|
)
|
|
72
75
|
session = await self.load_session(request, userdata)
|
|
73
76
|
request[SESSION_OBJECT] = session
|
|
74
|
-
request[
|
|
77
|
+
request[SESSION_REQUEST_KEY] = session
|
|
75
78
|
return session
|
|
76
79
|
|
|
77
80
|
async def invalidate(self, request: web.Request, session: SessionData) -> None:
|
|
78
81
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
79
82
|
if not session:
|
|
80
83
|
data = None
|
|
81
|
-
session_id = request.get(
|
|
84
|
+
session_id = request.get(SESSION_ID, None)
|
|
82
85
|
if session_id:
|
|
83
|
-
|
|
86
|
+
_id_ = f"session:{session_id}"
|
|
87
|
+
data = await conn.get(_id_)
|
|
84
88
|
if data is None:
|
|
85
89
|
# nothing to forgot
|
|
86
90
|
return True
|
|
87
91
|
try:
|
|
88
92
|
# delete the ID of the session
|
|
89
|
-
await conn.delete(session.
|
|
93
|
+
await conn.delete(f"session:{session.session_id}")
|
|
90
94
|
session.invalidate() # invalidate this session object
|
|
91
95
|
except Exception as err: # pylint: disable=W0703
|
|
92
|
-
|
|
96
|
+
self._logger.error(err)
|
|
93
97
|
return False
|
|
94
98
|
return True
|
|
95
99
|
|
|
100
|
+
async def get_session_id(self, conn: aioredis.Redis, identity: str) -> str:
|
|
101
|
+
"""Get Session ID from Redis."""
|
|
102
|
+
try:
|
|
103
|
+
session_id = await conn.get(f"user:{identity}")
|
|
104
|
+
except Exception as err:
|
|
105
|
+
self._logger.error(
|
|
106
|
+
f'Redis Storage: Error Getting Session ID: {err!s}'
|
|
107
|
+
)
|
|
108
|
+
session_id = None
|
|
109
|
+
return session_id
|
|
110
|
+
|
|
96
111
|
async def load_session(
|
|
97
112
|
self,
|
|
98
113
|
request: web.Request,
|
|
@@ -110,9 +125,9 @@ class RedisStorage(AbstractStorage):
|
|
|
110
125
|
---
|
|
111
126
|
new: if False, new session is not created.
|
|
112
127
|
"""
|
|
113
|
-
#
|
|
128
|
+
# first: for security, check if cookie csrf_secure exists
|
|
114
129
|
session_id = None
|
|
115
|
-
if ignore_cookie is False and self.
|
|
130
|
+
if ignore_cookie is False and self._use_cookies is True:
|
|
116
131
|
cookie = self.load_cookie(request)
|
|
117
132
|
try:
|
|
118
133
|
session_id = cookie['session_id']
|
|
@@ -122,26 +137,35 @@ class RedisStorage(AbstractStorage):
|
|
|
122
137
|
try:
|
|
123
138
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
124
139
|
except Exception as err:
|
|
125
|
-
|
|
140
|
+
self._logger.exception(
|
|
126
141
|
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
127
142
|
)
|
|
128
143
|
raise RuntimeError(
|
|
129
144
|
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
130
145
|
) from err
|
|
131
146
|
if session_id is None:
|
|
132
|
-
session_id = request.get(
|
|
147
|
+
session_id = request.get(SESSION_ID, None)
|
|
148
|
+
if not session_id:
|
|
149
|
+
session_id = userdata.get(SESSION_ID, None) if userdata else None
|
|
150
|
+
# get session id from redis using identity:
|
|
151
|
+
session_identity = userdata.get(
|
|
152
|
+
SESSION_KEY, None) if userdata else request.get(SESSION_KEY, None)
|
|
133
153
|
if not session_id:
|
|
134
|
-
session_id =
|
|
135
|
-
|
|
154
|
+
session_id = await self.get_session_id(conn, session_identity)
|
|
155
|
+
print('SESSION IDENTITY IS:', session_identity, session_id)
|
|
136
156
|
if session_id is None and new is False:
|
|
157
|
+
# No Session was found, returning false:
|
|
137
158
|
return False
|
|
138
159
|
# we need to load session data from redis
|
|
139
|
-
|
|
160
|
+
self._logger.debug(
|
|
161
|
+
f':::::: LOAD SESSION FOR {session_id} ::::: '
|
|
162
|
+
)
|
|
163
|
+
_id_ = f"session:{session_id}"
|
|
140
164
|
try:
|
|
141
|
-
data = await conn.get(
|
|
165
|
+
data = await conn.get(_id_)
|
|
142
166
|
except Exception as err: # pylint: disable=W0703
|
|
143
|
-
|
|
144
|
-
f'Redis Storage: Error Getting Session
|
|
167
|
+
self._logger.error(
|
|
168
|
+
f'Redis Storage: Error Getting Session: {err!s}'
|
|
145
169
|
)
|
|
146
170
|
data = None
|
|
147
171
|
if data is None:
|
|
@@ -149,35 +173,45 @@ class RedisStorage(AbstractStorage):
|
|
|
149
173
|
# create a new session if not exists:
|
|
150
174
|
return await self.new_session(request, userdata)
|
|
151
175
|
else:
|
|
176
|
+
# No Session Was Found
|
|
152
177
|
return False
|
|
153
178
|
try:
|
|
154
179
|
data = self._decoder(data)
|
|
155
180
|
session = SessionData(
|
|
156
|
-
|
|
181
|
+
id=session_id,
|
|
182
|
+
identity=session_identity,
|
|
157
183
|
data=data,
|
|
158
184
|
new=False,
|
|
159
185
|
max_age=self.max_age
|
|
160
186
|
)
|
|
161
187
|
except Exception as err: # pylint: disable=W0703
|
|
162
|
-
|
|
188
|
+
self._logger.warning(
|
|
189
|
+
f"Error creating Session Data: {err}"
|
|
190
|
+
)
|
|
163
191
|
session = SessionData(
|
|
164
|
-
|
|
192
|
+
id=session_id,
|
|
193
|
+
identity=session_identity,
|
|
165
194
|
data=None,
|
|
166
195
|
new=True,
|
|
167
196
|
max_age=self.max_age
|
|
168
197
|
)
|
|
169
198
|
## add other options to session:
|
|
170
199
|
self.session_info(session, request)
|
|
171
|
-
request[SESSION_KEY] = session_id
|
|
172
200
|
session[SESSION_KEY] = session_id
|
|
201
|
+
request[SESSION_KEY] = session_identity
|
|
202
|
+
request[SESSION_ID] = session_id
|
|
173
203
|
request[SESSION_OBJECT] = session
|
|
174
|
-
request[
|
|
175
|
-
if self.
|
|
204
|
+
request[SESSION_REQUEST_KEY] = session
|
|
205
|
+
if self._use_cookies is True and response is not None:
|
|
176
206
|
cookie_data = {
|
|
177
207
|
"session_id": session_id
|
|
178
208
|
}
|
|
179
209
|
cookie_data = self._encoder(cookie_data)
|
|
180
|
-
self.save_cookie(
|
|
210
|
+
self.save_cookie(
|
|
211
|
+
response,
|
|
212
|
+
cookie_data=cookie_data,
|
|
213
|
+
max_age=self.max_age
|
|
214
|
+
)
|
|
181
215
|
return session
|
|
182
216
|
|
|
183
217
|
async def save_session(
|
|
@@ -187,9 +221,9 @@ class RedisStorage(AbstractStorage):
|
|
|
187
221
|
session: SessionData
|
|
188
222
|
) -> None:
|
|
189
223
|
"""Save the whole session in the backend Storage."""
|
|
190
|
-
session_id = session.
|
|
224
|
+
session_id = session.session_id if session else request.get(SESSION_ID, None)
|
|
191
225
|
if not session_id:
|
|
192
|
-
session_id = session.get(
|
|
226
|
+
session_id = session.get(SESSION_ID, None)
|
|
193
227
|
if not session_id:
|
|
194
228
|
session_id = self.id_factory()
|
|
195
229
|
if session.empty:
|
|
@@ -197,15 +231,14 @@ class RedisStorage(AbstractStorage):
|
|
|
197
231
|
data = self._encoder(session.session_data())
|
|
198
232
|
max_age = session.max_age
|
|
199
233
|
expire = max_age if max_age is not None else 0
|
|
200
|
-
# TODO: add expiration on redis to value
|
|
201
234
|
try:
|
|
202
235
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
203
|
-
|
|
204
|
-
|
|
236
|
+
_id_ = f"session:{session_id}"
|
|
237
|
+
await conn.set(
|
|
238
|
+
_id_, data, expire
|
|
205
239
|
)
|
|
206
240
|
except Exception as err: # pylint: disable=W0703
|
|
207
|
-
|
|
208
|
-
logging.exception(err, stack_info=True)
|
|
241
|
+
self._logger.exception(err, stack_info=True)
|
|
209
242
|
return False
|
|
210
243
|
|
|
211
244
|
async def new_session(
|
|
@@ -215,43 +248,67 @@ class RedisStorage(AbstractStorage):
|
|
|
215
248
|
response: web.StreamResponse = None
|
|
216
249
|
) -> SessionData:
|
|
217
250
|
"""Create a New Session Object for this User."""
|
|
218
|
-
|
|
251
|
+
session_identity = request.get(SESSION_KEY, None)
|
|
252
|
+
session_id = request.get(SESSION_ID, None)
|
|
253
|
+
try:
|
|
254
|
+
conn = aioredis.Redis(connection_pool=self._redis)
|
|
255
|
+
except Exception as err:
|
|
256
|
+
self._logger.error(
|
|
257
|
+
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
258
|
+
)
|
|
259
|
+
raise RuntimeError(
|
|
260
|
+
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
261
|
+
) from err
|
|
262
|
+
if not session_id:
|
|
263
|
+
session_id = await self.get_session_id(conn, session_identity)
|
|
219
264
|
if not session_id:
|
|
220
265
|
try:
|
|
221
|
-
session_id = data[
|
|
266
|
+
session_id = data[SESSION_ID]
|
|
222
267
|
except KeyError:
|
|
223
268
|
session_id = self.id_factory()
|
|
224
|
-
|
|
269
|
+
self._logger.debug(
|
|
270
|
+
f':::::: CREATING A NEW SESSION FOR {session_id} ::::: '
|
|
271
|
+
)
|
|
225
272
|
if not data:
|
|
226
273
|
data = {}
|
|
227
274
|
# saving this new session on DB
|
|
228
275
|
try:
|
|
229
|
-
conn = aioredis.Redis(connection_pool=self._redis)
|
|
230
276
|
t = time.time()
|
|
231
277
|
data['created'] = t
|
|
232
278
|
data['last_visit'] = t
|
|
233
|
-
data[
|
|
234
|
-
data[
|
|
279
|
+
data[SESSION_KEY] = session_identity
|
|
280
|
+
data[SESSION_ID] = session_id
|
|
235
281
|
dt = self._encoder(data)
|
|
282
|
+
_id_ = f'session:{session_id}'
|
|
236
283
|
result = await conn.set(
|
|
237
|
-
|
|
284
|
+
_id_, dt, self.max_age
|
|
285
|
+
)
|
|
286
|
+
self._logger.debug(
|
|
287
|
+
f'Session Creation: {result}'
|
|
288
|
+
)
|
|
289
|
+
# Saving the Session ID on redis:
|
|
290
|
+
await conn.set(
|
|
291
|
+
f"user:{session_identity}",
|
|
292
|
+
session_id,
|
|
293
|
+
self.max_age
|
|
238
294
|
)
|
|
239
|
-
logging.info(f'Creation of New Session: {result}')
|
|
240
295
|
except Exception as err: # pylint: disable=W0703
|
|
241
|
-
|
|
296
|
+
self._logger.exception(err)
|
|
242
297
|
return False
|
|
243
298
|
try:
|
|
244
299
|
session = SessionData(
|
|
245
|
-
|
|
300
|
+
id=session_id,
|
|
301
|
+
identity=session_identity,
|
|
246
302
|
data=data,
|
|
247
303
|
new=True,
|
|
248
304
|
max_age=self.max_age
|
|
249
305
|
)
|
|
250
|
-
if self.
|
|
306
|
+
if self._use_cookies is True and response is not None:
|
|
251
307
|
cookie_data = {
|
|
252
308
|
"last_visit": t,
|
|
253
309
|
"session_id": session_id
|
|
254
310
|
}
|
|
311
|
+
# TODO: adding crypt
|
|
255
312
|
cookie_data = self._encoder(cookie_data)
|
|
256
313
|
self.save_cookie(
|
|
257
314
|
response,
|
|
@@ -259,12 +316,16 @@ class RedisStorage(AbstractStorage):
|
|
|
259
316
|
max_age=self.max_age
|
|
260
317
|
)
|
|
261
318
|
except Exception as err: # pylint: disable=W0703
|
|
262
|
-
|
|
319
|
+
self._logger.exception(
|
|
320
|
+
f'Error creating Session Data: {err!s}'
|
|
321
|
+
)
|
|
322
|
+
return False
|
|
263
323
|
# Saving Session Object:
|
|
264
324
|
## add other options to session:
|
|
265
325
|
self.session_info(session, request)
|
|
266
|
-
session[SESSION_KEY] =
|
|
326
|
+
session[SESSION_KEY] = session_identity
|
|
327
|
+
request[SESSION_ID] = session_id
|
|
267
328
|
request[SESSION_OBJECT] = session
|
|
268
|
-
request[SESSION_KEY] =
|
|
269
|
-
request[
|
|
329
|
+
request[SESSION_KEY] = session_identity
|
|
330
|
+
request[SESSION_REQUEST_KEY] = session
|
|
270
331
|
return session
|
navigator_session/version.py
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
__title__ = 'navigator_session'
|
|
6
6
|
__description__ = ('Navigator Session allows us to store user-specific data '
|
|
7
7
|
'into session object.')
|
|
8
|
-
__version__ = '0.
|
|
8
|
+
__version__ = '0.6.0'
|
|
9
9
|
__author__ = 'Jesus Lara'
|
|
10
10
|
__author_email__ = 'jesuslarag@gmail.com'
|
|
11
11
|
__license__ = 'Apache-2.0'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
navigator_session/__init__.py,sha256=U1MtM30ccs5N6c_lzUYFRS5NURz6kheDC8jZmRL_2yc,3056
|
|
2
|
+
navigator_session/conf.py,sha256=Pe8qsukmt5cXXPc3hwFphSWF5xCIpZfeFQ8JhICEnow,1695
|
|
3
|
+
navigator_session/data.py,sha256=9bNj_tBGvyqOpcYP8MSKt--5DG5TlTIrYwbQYK7cfAM,6037
|
|
4
|
+
navigator_session/middleware.py,sha256=_bQKfD81BxNZTGuyGTc4yh7y1Rb95I-y2hM1ihE4wCk,1944
|
|
5
|
+
navigator_session/session.py,sha256=CG-TjsmQqjuk6N7GCYNgQTCkrXptgq-swHDiemgHmKI,2398
|
|
6
|
+
navigator_session/version.py,sha256=N6sPpSUtraKYGwgN10X5nuZUoj-0nIuKu8709Z7EquQ,394
|
|
7
|
+
navigator_session/storages/__init__.py,sha256=kTcHfqps5LmnDpFAktxevPuSIwRbnrcMmB7Uygq4axQ,34
|
|
8
|
+
navigator_session/storages/abstract.py,sha256=wTv0yWMVXTwrzA_bjexcwI7c6_W3tzosXlMOIZIQrBM,6503
|
|
9
|
+
navigator_session/storages/cookie.py,sha256=VILzVCatKlDhMoQDoE2y0u6s7lp4wbdep5Oc0NfIJcg,2474
|
|
10
|
+
navigator_session/storages/redis.py,sha256=W-3I_hpabDToLOl7nVrIupV3Taf_IQUO_t8BP6EscYk,11506
|
|
11
|
+
navigator_session-0.6.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
12
|
+
navigator_session-0.6.0.dist-info/METADATA,sha256=j2GJylJ5csGzsI__0pmCqh0xmQh8223niPIx7Ee_2dE,2339
|
|
13
|
+
navigator_session-0.6.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
14
|
+
navigator_session-0.6.0.dist-info/top_level.txt,sha256=ZpOEy3wLKGsxG2rc0nHqcqJCV3HIOG_XCfE6mtsYYYY,18
|
|
15
|
+
navigator_session-0.6.0.dist-info/RECORD,,
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
navigator_session/__init__.py,sha256=xsmc90K04EgQEtYidPN7bsXn0WIqw6GucNwy138Mmno,3038
|
|
2
|
-
navigator_session/conf.py,sha256=PwCGoYpg_2MEq5_zObvbx4yXKf7AIKObO-lzTh2AvxM,1466
|
|
3
|
-
navigator_session/data.py,sha256=tD40x16dujarX3F1RMPAuYqDP3oiG_8kyqLNYyD3Ik4,5798
|
|
4
|
-
navigator_session/middleware.py,sha256=7RO83pz4q16_56GvmbswEukbDZQ0JutJdVdF4UHbEw8,1942
|
|
5
|
-
navigator_session/session.py,sha256=I6qWkMbqIpVX1RjxmccGsk1sCcqWZ_ZBVGwLXjV8wjw,2188
|
|
6
|
-
navigator_session/version.py,sha256=XNGR1srIFz8RhkuogeeZHcBM4wHg9PZ-z8ZUoVbepPE,394
|
|
7
|
-
navigator_session/storages/__init__.py,sha256=ln0Xli0lppUopDMrvQGt69BFtUAe1Grmy2DGkEpANNU,132
|
|
8
|
-
navigator_session/storages/abstract.py,sha256=5nqEF0I0G3p4pcTNfOQYspbXp-U7QJntJWVsKYDu9KY,6043
|
|
9
|
-
navigator_session/storages/cookie.py,sha256=_qL5kIGbT4rtCMze4yj1h4J7Zauz_e7HnorPnFbi90w,2468
|
|
10
|
-
navigator_session/storages/redis.py,sha256=qk8V3FGYDLbfTT851NOU864hSsgup661b8GY1u_uhtQ,9345
|
|
11
|
-
navigator_session-0.5.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
12
|
-
navigator_session-0.5.7.dist-info/METADATA,sha256=bRuE-fKSuYIWaW04qQptq9fBln1lfCz6HqMkTgmzYmw,2339
|
|
13
|
-
navigator_session-0.5.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
14
|
-
navigator_session-0.5.7.dist-info/top_level.txt,sha256=ZpOEy3wLKGsxG2rc0nHqcqJCV3HIOG_XCfE6mtsYYYY,18
|
|
15
|
-
navigator_session-0.5.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|