navigator-session 0.5.7__py3-none-any.whl → 0.6.1__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.
@@ -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, please install on Aiohttp Middlewares"
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 for Session Storage, please install Session Middleware."
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['session'] = session
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 = 'NAVIGATOR_SESSION_STORAGE'
26
- SESSION_OBJECT = 'NAV_SESSION'
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 SESSION_KEY, SESSION_STORAGE
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 = uuid.uuid4().hex
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 '<{} [new:{}, created:{}] {!r}>'.format( # pylint: disable=C0209
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
@@ -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
@@ -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
- logging.debug(':::: Session Handler Loaded ::::')
40
+ self.logger.debug(':::: Session Handler Loaded ::::')
38
41
  # register the Auth extension into the app
39
- self.app['nav_session'] = self
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
- logging.exception(f'{ex}')
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
- logging.exception(f'{ex}')
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,6 +1 @@
1
1
  """NAV Session Storage Module."""
2
-
3
- # from .redis import RedisStorage
4
- from .abstract import SessionData
5
-
6
- __all__ = ('SessionData', )
@@ -1,16 +1,22 @@
1
1
  """Base Class for all Session Storages."""
2
2
  import sys
3
- import abc
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=abc.ABCMeta):
39
+ class AbstractStorage(metaclass=ABCMeta):
34
40
 
35
- use_cookie: bool = False
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
- if 'use_cookie' in kwargs:
53
- self.use_cookie = kwargs['use_cookie']
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 = self._objencoder.dumps
65
- self._decoder = self._objencoder.loads
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
- @abc.abstractmethod
84
+ @abstractmethod
75
85
  async def on_startup(self, app: web.Application):
76
86
  pass
77
87
 
78
- @abc.abstractmethod
88
+ @abstractmethod
79
89
  async def on_cleanup(self, app: web.Application):
80
90
  pass
81
91
 
82
- @abc.abstractmethod
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
- @abc.abstractmethod
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
- @abc.abstractmethod
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(None, data=None, new=True, max_age=self.max_age)
107
-
108
- @abc.abstractmethod
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
- @abc.abstractmethod
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
- """Delete a User Session."""
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["session"] = None
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
- logging.warning(
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.forgot_cooke(response)
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.use_cookie is True:
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 forgot_cooke(self, response: web.StreamResponse) -> None:
150
- if self.use_cookie is True:
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.use_cookie is True:
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
- logging.warning(f'Unable to read Request info: {ex}')
222
+ self._logger.warning(f'Unable to read Request info: {ex}')
203
223
  ### modified Session Object:
204
224
  return session
@@ -1,4 +1,4 @@
1
- """Encrypted JSON Cookie Storage."""
1
+ """TODO: Encrypted JSON Cookie Storage."""
2
2
  import base64
3
3
  from typing import (
4
4
  Optional,
@@ -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
- logging.exception(err, stack_info=True)
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
- logging.warning(ex)
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
- logging.debug(f'Redis Storage: Error on get Session: {err!s}')
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["session"] = session
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(SESSION_KEY, None)
84
+ session_id = request.get(SESSION_ID, None)
82
85
  if session_id:
83
- data = await conn.get(session_id)
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.identity)
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
- logging.error(err)
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
- # TODO: first: for security, check if cookie csrf_secure exists
128
+ # first: for security, check if cookie csrf_secure exists
114
129
  session_id = None
115
- if ignore_cookie is False and self.use_cookie is True:
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
- logging.exception(
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(SESSION_KEY, None)
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 = userdata.get(SESSION_KEY, None) if userdata else None
135
- # TODO: getting from cookie
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
- logging.debug(f':::::: LOAD SESSION FOR {session_id} ::::: ')
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(session_id)
165
+ data = await conn.get(_id_)
142
166
  except Exception as err: # pylint: disable=W0703
143
- logging.error(
144
- f'Redis Storage: Error Getting Session data: {err!s}'
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
- identity=session_id,
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
- logging.warning(err)
188
+ self._logger.warning(
189
+ f"Error creating Session Data: {err}"
190
+ )
163
191
  session = SessionData(
164
- identity=None,
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["session"] = session
175
- if self.use_cookie is True and response is not None:
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(response, cookie_data=cookie_data, max_age=self.max_age)
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.identity
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(SESSION_KEY, None)
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
- result = await conn.set(
204
- session_id, data, expire
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
- print('Error Saving Session: ', err)
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
- session_id = request.get(SESSION_KEY, None)
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[SESSION_KEY]
266
+ session_id = data[SESSION_ID]
222
267
  except KeyError:
223
268
  session_id = self.id_factory()
224
- logging.debug(f':::::: START CREATING A NEW SESSION FOR {session_id} ::::: ')
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["last_visited"] = f"Last visited: {t!s}"
234
- data[SESSION_KEY] = session_id
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
- session_id, dt, self.max_age
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
- logging.exception(err)
296
+ self._logger.exception(err)
242
297
  return False
243
298
  try:
244
299
  session = SessionData(
245
- identity=session_id,
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.use_cookie is True and response is not None:
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
- logging.exception(f'Error creating Session Data: {err!s}')
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] = session_id
326
+ session[SESSION_KEY] = session_identity
327
+ request[SESSION_ID] = session_id
267
328
  request[SESSION_OBJECT] = session
268
- request[SESSION_KEY] = session_id
269
- request["session"] = session
329
+ request[SESSION_KEY] = session_identity
330
+ request[SESSION_REQUEST_KEY] = session
270
331
  return session
@@ -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.5.7'
8
+ __version__ = '0.6.1'
9
9
  __author__ = 'Jesus Lara'
10
10
  __author_email__ = 'jesuslarag@gmail.com'
11
11
  __license__ = 'Apache-2.0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: navigator-session
3
- Version: 0.5.7
3
+ Version: 0.6.1
4
4
  Summary: Navigator Session allows us to store user-specific data into session object.
5
5
  Home-page: https://github.com/phenobarbital/navigator-session
6
6
  Author: Jesus Lara
@@ -27,13 +27,13 @@ Classifier: Framework :: aiohttp
27
27
  Requires-Python: >=3.9.16
28
28
  Description-Content-Type: text/markdown
29
29
  License-File: LICENSE
30
- Requires-Dist: aiohttp >=3.9.5
31
- Requires-Dist: uvloop ==0.19.0
32
- Requires-Dist: asyncio ==3.4.3
33
- Requires-Dist: jsonpickle >=3.0.2
34
- Requires-Dist: redis >=5.0.4
35
- Requires-Dist: python-datamodel >=0.2.1
36
- Requires-Dist: navconfig[default] >=1.6.3
30
+ Requires-Dist: aiohttp>=3.9.5
31
+ Requires-Dist: uvloop==0.20.0
32
+ Requires-Dist: asyncio==3.4.3
33
+ Requires-Dist: jsonpickle>=3.0.2
34
+ Requires-Dist: redis>=5.0.4
35
+ Requires-Dist: python-datamodel>=0.7.0
36
+ Requires-Dist: navconfig[default]>=1.7.0
37
37
 
38
38
  # Navigator_Session #
39
39
 
@@ -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=bLAeol-M_2wgKYBII2KhUeoKwGiyUbWzbTUsd9cghpk,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.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
12
+ navigator_session-0.6.1.dist-info/METADATA,sha256=It6PEyQL6vLklsGQsMRCtxYhC7-Ls9fyBHqbckasW5s,2332
13
+ navigator_session-0.6.1.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
14
+ navigator_session-0.6.1.dist-info/top_level.txt,sha256=ZpOEy3wLKGsxG2rc0nHqcqJCV3HIOG_XCfE6mtsYYYY,18
15
+ navigator_session-0.6.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (74.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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,,