navigator-session 0.4.2__tar.gz → 0.6.2__tar.gz
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-0.4.2 → navigator_session-0.6.2}/Makefile +2 -4
- {navigator-session-0.4.2 → navigator_session-0.6.2}/PKG-INFO +9 -2
- navigator_session-0.6.2/docs/requirements-dev.txt +21 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/__init__.py +6 -3
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/conf.py +7 -3
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/data.py +19 -8
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/middleware.py +5 -3
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/session.py +11 -9
- navigator_session-0.6.2/navigator_session/storages/__init__.py +1 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/storages/abstract.py +53 -32
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/storages/cookie.py +4 -3
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/storages/redis.py +135 -66
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session/version.py +1 -1
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/PKG-INFO +9 -2
- navigator_session-0.6.2/navigator_session.egg-info/requires.txt +6 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/pyproject.toml +5 -4
- {navigator-session-0.4.2 → navigator_session-0.6.2}/setup.py +15 -14
- navigator-session-0.4.2/docs/requirements-dev.txt +0 -19
- navigator-session-0.4.2/navigator_session/storages/__init__.py +0 -6
- navigator-session-0.4.2/navigator_session.egg-info/requires.txt +0 -9
- {navigator-session-0.4.2 → navigator_session-0.6.2}/CHANGES.rst +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/LICENSE +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/MANIFEST.in +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/README.md +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/Makefile +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/api.rst +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/authors.rst +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/conf.py +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/examples.rst +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/index.rst +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/make.bat +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/docs/requirements.txt +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/SOURCES.txt +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/dependency_links.txt +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/top_level.txt +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/setup.cfg +0 -0
- {navigator-session-0.4.2 → navigator_session-0.6.2}/tests/__init__.py +0 -0
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
venv:
|
|
2
|
-
python3.
|
|
2
|
+
python3.11 -m venv .venv
|
|
3
3
|
echo 'run `source .venv/bin/activate` to start develop Navigator-Session'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
pip install wheel==0.38.4
|
|
5
|
+
install:
|
|
7
6
|
pip install -e .
|
|
8
7
|
|
|
9
8
|
develop:
|
|
10
|
-
pip install wheel==0.38.4
|
|
11
9
|
pip install -e .
|
|
12
10
|
pip install -Ur docs/requirements-dev.txt
|
|
13
11
|
flit install --symlink
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: navigator-session
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.2
|
|
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
|
|
@@ -21,11 +21,18 @@ Classifier: Topic :: Database :: Front-Ends
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.9
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
25
|
Classifier: Framework :: AsyncIO
|
|
25
26
|
Classifier: Framework :: aiohttp
|
|
26
|
-
Requires-Python: >=3.9.
|
|
27
|
+
Requires-Python: >=3.9.13
|
|
27
28
|
Description-Content-Type: text/markdown
|
|
28
29
|
License-File: LICENSE
|
|
30
|
+
Requires-Dist: aiohttp>=3.9.5
|
|
31
|
+
Requires-Dist: asyncio==3.4.3
|
|
32
|
+
Requires-Dist: jsonpickle>=3.0.2
|
|
33
|
+
Requires-Dist: redis>=5.0.4
|
|
34
|
+
Requires-Dist: python_datamodel>=0.7.0
|
|
35
|
+
Requires-Dist: navconfig[default]>=1.7.0
|
|
29
36
|
|
|
30
37
|
# Navigator_Session #
|
|
31
38
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
aiounittest==1.4.2
|
|
2
|
+
black==24.3.0
|
|
3
|
+
build==1.0.3
|
|
4
|
+
coverage[toml]==7.2.7
|
|
5
|
+
flit==3.9.0
|
|
6
|
+
hypothesis==6.91.0
|
|
7
|
+
ipython==8.14.0
|
|
8
|
+
lint==1.2.1
|
|
9
|
+
mypy==1.7.1
|
|
10
|
+
pylint==2.16.1
|
|
11
|
+
pyperf==2.6.2
|
|
12
|
+
pytest>=6.0.0
|
|
13
|
+
pytest-asyncio==0.23.2
|
|
14
|
+
pytest-cov==4.0.0
|
|
15
|
+
pytest-cython==0.2.0
|
|
16
|
+
pytest-xdist==3.5.0
|
|
17
|
+
pytest-assume==2.4.3
|
|
18
|
+
sdist==0.0.0
|
|
19
|
+
sphinx==6.1.3
|
|
20
|
+
tox==4.6.4
|
|
21
|
+
twine==5.1.1
|
|
@@ -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 "
|
|
@@ -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')
|
|
@@ -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
|
|
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,21 +46,27 @@ 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
|
|
57
|
-
self._now = pendulum.now('UTC')
|
|
66
|
+
self._now = pendulum.now('UTC') # time for this instance creation
|
|
58
67
|
self.__created__ = self._now
|
|
59
68
|
now = int(self._now.int_timestamp)
|
|
60
|
-
self._now = now
|
|
69
|
+
self._now = now # time for this instance creation
|
|
61
70
|
age = now - created if created else now
|
|
62
71
|
if max_age is not None and age > max_age:
|
|
63
72
|
data = 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
|
|
@@ -3,25 +3,27 @@ from collections.abc import Callable, Awaitable
|
|
|
3
3
|
from aiohttp import web
|
|
4
4
|
from aiohttp.web_middlewares import Handler
|
|
5
5
|
from navconfig.logging import logging
|
|
6
|
-
from
|
|
6
|
+
from .conf import (
|
|
7
7
|
SESSION_OBJECT,
|
|
8
8
|
SESSION_STORAGE
|
|
9
9
|
)
|
|
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
|
-
app: web.Application,
|
|
19
|
+
app: web.Application, # pylint: disable=W0613
|
|
18
20
|
storage: AbstractStorage
|
|
19
21
|
) -> Middleware:
|
|
20
22
|
"""Middleware to attach Session Storage to every Request."""
|
|
21
23
|
if not isinstance(storage, AbstractStorage):
|
|
22
24
|
raise RuntimeError(
|
|
23
25
|
f"Expected an AbstractStorage got {storage!s}"
|
|
24
|
-
|
|
26
|
+
)
|
|
25
27
|
|
|
26
28
|
@web.middleware
|
|
27
29
|
async def middleware(
|
|
@@ -8,10 +8,13 @@ class SessionHandler:
|
|
|
8
8
|
"""Authentication Backend for Navigator."""
|
|
9
9
|
storage: Callable = None
|
|
10
10
|
|
|
11
|
-
def __init__(self,storage: str = 'redis', **kwargs) -> None:
|
|
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}"
|
|
@@ -19,9 +22,9 @@ class SessionHandler:
|
|
|
19
22
|
|
|
20
23
|
def setup(self, app: web.Application) -> None:
|
|
21
24
|
if isinstance(app, web.Application):
|
|
22
|
-
self.app = app
|
|
25
|
+
self.app = app # register the app into the Extension
|
|
23
26
|
else:
|
|
24
|
-
self.app = app.get_app()
|
|
27
|
+
self.app = app.get_app() # Nav Application
|
|
25
28
|
## Configure the Middleware for NAV Session.
|
|
26
29
|
self.app.middlewares.append(
|
|
27
30
|
session_middleware(app, self.storage)
|
|
@@ -34,10 +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[
|
|
40
|
-
|
|
42
|
+
self.app[self._session_object] = self
|
|
41
43
|
|
|
42
44
|
async def session_startup(self, app: web.Application):
|
|
43
45
|
"""
|
|
@@ -46,7 +48,7 @@ class SessionHandler:
|
|
|
46
48
|
try:
|
|
47
49
|
await self.storage.on_startup(app)
|
|
48
50
|
except Exception as ex:
|
|
49
|
-
|
|
51
|
+
self.logger.exception(f'{ex}')
|
|
50
52
|
raise RuntimeError(
|
|
51
53
|
f"Session Storage Error: cannot start Storage Backend {ex}"
|
|
52
54
|
) from ex
|
|
@@ -58,7 +60,7 @@ class SessionHandler:
|
|
|
58
60
|
try:
|
|
59
61
|
await self.storage.on_cleanup(app)
|
|
60
62
|
except Exception as ex:
|
|
61
|
-
|
|
63
|
+
self.logger.exception(f'{ex}')
|
|
62
64
|
raise RuntimeError(
|
|
63
65
|
f"Session Storage Error: cannot start Storage Backend {ex}"
|
|
64
66
|
) from ex
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""NAV Session Storage Module."""
|
|
@@ -1,19 +1,26 @@
|
|
|
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
|
|
10
|
+
from datamodel.parsers.json import ( # pylint: disable=C0411
|
|
11
|
+
json_encoder,
|
|
12
|
+
json_decoder
|
|
13
|
+
)
|
|
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
|
-
from
|
|
22
|
+
from ..data import SessionData
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
if sys.version_info >= (3, 8):
|
|
19
26
|
from typing import TypedDict
|
|
@@ -29,13 +36,14 @@ class _CookieParams(TypedDict, total=False):
|
|
|
29
36
|
samesite: Optional[str]
|
|
30
37
|
expires: str
|
|
31
38
|
|
|
32
|
-
class AbstractStorage(metaclass=
|
|
39
|
+
class AbstractStorage(metaclass=ABCMeta):
|
|
33
40
|
|
|
34
|
-
|
|
41
|
+
_use_cookies: bool = False
|
|
35
42
|
|
|
36
43
|
def __init__(
|
|
37
44
|
self,
|
|
38
45
|
*,
|
|
46
|
+
logger: Optional[logging.Logger] = None,
|
|
39
47
|
max_age: int = None,
|
|
40
48
|
secure: bool = True,
|
|
41
49
|
domain: Optional[str] = None,
|
|
@@ -43,14 +51,17 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
43
51
|
httponly: bool = True,
|
|
44
52
|
samesite: Optional[str] = 'Lax',
|
|
45
53
|
**kwargs
|
|
46
|
-
|
|
54
|
+
) -> None:
|
|
55
|
+
if logger is not None:
|
|
56
|
+
self._logger = logger
|
|
57
|
+
else:
|
|
58
|
+
self._logger = logging.getLogger('Nav_Session.Storage')
|
|
47
59
|
if not max_age:
|
|
48
60
|
self.max_age = SESSION_TIMEOUT
|
|
49
61
|
else:
|
|
50
62
|
self.max_age = max_age
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
del kwargs['use_cookie']
|
|
63
|
+
# Using session cookies:
|
|
64
|
+
self._use_cookies = kwargs.get('use_cookies', False)
|
|
54
65
|
# Storage Name
|
|
55
66
|
self.__name__: str = SESSION_COOKIE_SECURE
|
|
56
67
|
self._domain: Optional[str] = domain
|
|
@@ -60,8 +71,8 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
60
71
|
self._httponly = httponly
|
|
61
72
|
self._samesite = samesite
|
|
62
73
|
self._objencoder = DefaultEncoder()
|
|
63
|
-
self._encoder =
|
|
64
|
-
self._decoder =
|
|
74
|
+
self._encoder = json_encoder
|
|
75
|
+
self._decoder = json_decoder
|
|
65
76
|
|
|
66
77
|
def id_factory(self) -> str:
|
|
67
78
|
return uuid.uuid4().hex
|
|
@@ -70,15 +81,15 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
70
81
|
def cookie_name(self) -> str:
|
|
71
82
|
return self.__name__
|
|
72
83
|
|
|
73
|
-
@
|
|
84
|
+
@abstractmethod
|
|
74
85
|
async def on_startup(self, app: web.Application):
|
|
75
86
|
pass
|
|
76
87
|
|
|
77
|
-
@
|
|
88
|
+
@abstractmethod
|
|
78
89
|
async def on_cleanup(self, app: web.Application):
|
|
79
90
|
pass
|
|
80
91
|
|
|
81
|
-
@
|
|
92
|
+
@abstractmethod
|
|
82
93
|
async def new_session(
|
|
83
94
|
self,
|
|
84
95
|
request: web.Request,
|
|
@@ -86,7 +97,7 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
86
97
|
) -> SessionData:
|
|
87
98
|
pass
|
|
88
99
|
|
|
89
|
-
@
|
|
100
|
+
@abstractmethod
|
|
90
101
|
async def load_session(
|
|
91
102
|
self,
|
|
92
103
|
request: web.Request,
|
|
@@ -97,55 +108,66 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
97
108
|
) -> SessionData:
|
|
98
109
|
pass
|
|
99
110
|
|
|
100
|
-
@
|
|
111
|
+
@abstractmethod
|
|
101
112
|
async def get_session(self, request: web.Request) -> SessionData:
|
|
102
113
|
pass
|
|
103
114
|
|
|
104
115
|
def empty_session(self) -> SessionData:
|
|
105
|
-
return SessionData(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
return SessionData(
|
|
117
|
+
None,
|
|
118
|
+
data=None,
|
|
119
|
+
new=True,
|
|
120
|
+
max_age=self.max_age
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@abstractmethod
|
|
124
|
+
async def save_session(
|
|
125
|
+
self,
|
|
109
126
|
request: web.Request,
|
|
110
127
|
response: web.StreamResponse,
|
|
111
128
|
session: SessionData
|
|
112
129
|
) -> None:
|
|
113
130
|
pass
|
|
114
131
|
|
|
115
|
-
@
|
|
132
|
+
@abstractmethod
|
|
116
133
|
async def invalidate(
|
|
117
134
|
self,
|
|
118
135
|
request: web.Request,
|
|
119
136
|
session: SessionData
|
|
120
137
|
) -> None:
|
|
121
138
|
"""Try to Invalidate the Session in the Storage."""
|
|
139
|
+
pass
|
|
122
140
|
|
|
123
141
|
async def forgot(self, request: web.Request, response: web.StreamResponse = None):
|
|
124
|
-
"""
|
|
142
|
+
"""forgot.
|
|
143
|
+
|
|
144
|
+
Forgot (delete) a user session.
|
|
145
|
+
"""
|
|
125
146
|
session = await self.get_session(request)
|
|
126
147
|
await self.invalidate(request, session)
|
|
127
|
-
request[
|
|
148
|
+
request[SESSION_REQUEST_KEY] = None
|
|
128
149
|
try:
|
|
129
150
|
del request[SESSION_KEY]
|
|
151
|
+
del request[SESSION_ID]
|
|
130
152
|
del request[SESSION_OBJECT]
|
|
131
|
-
except Exception as err:
|
|
132
|
-
|
|
153
|
+
except Exception as err: # pylint: disable=W0703
|
|
154
|
+
self._logger.warning(
|
|
133
155
|
f'Session: Error on Forgot Method: {err}'
|
|
134
156
|
)
|
|
135
157
|
if response is not None:
|
|
136
158
|
# also, forgot the secure Cookie:
|
|
137
|
-
self.
|
|
159
|
+
self.forgot_cookie(response)
|
|
138
160
|
|
|
139
161
|
def load_cookie(self, request: web.Request) -> str:
|
|
140
162
|
"""Getting Cookie from User (if needed)"""
|
|
141
|
-
if self.
|
|
163
|
+
if self._use_cookies is True:
|
|
142
164
|
cookie = request.cookies.get(self.__name__, None)
|
|
143
165
|
if cookie:
|
|
144
166
|
return self._decoder(cookie)
|
|
145
167
|
return None
|
|
146
168
|
|
|
147
|
-
def
|
|
148
|
-
if self.
|
|
169
|
+
def forgot_cookie(self, response: web.StreamResponse) -> None:
|
|
170
|
+
if self._use_cookies is True:
|
|
149
171
|
response.del_cookie(
|
|
150
172
|
self.__name__, domain=self._domain, path=self._path
|
|
151
173
|
)
|
|
@@ -157,7 +179,7 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
157
179
|
*,
|
|
158
180
|
max_age: Optional[int] = None,
|
|
159
181
|
) -> None:
|
|
160
|
-
if self.
|
|
182
|
+
if self._use_cookies is True:
|
|
161
183
|
expires = None
|
|
162
184
|
if max_age is not None:
|
|
163
185
|
t = time.gmtime(time.time() + max_age)
|
|
@@ -180,7 +202,6 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
180
202
|
else:
|
|
181
203
|
response.set_cookie(self.__name__, cookie_data, **params)
|
|
182
204
|
|
|
183
|
-
|
|
184
205
|
def session_info(self, session: SessionData, request: web.Request) -> SessionData:
|
|
185
206
|
"""session_info.
|
|
186
207
|
Session Helper for adding more information extracted from Request.
|
|
@@ -198,6 +219,6 @@ class AbstractStorage(metaclass=abc.ABCMeta):
|
|
|
198
219
|
session.headers = request.headers
|
|
199
220
|
session.rel_url = request.rel_url
|
|
200
221
|
except (TypeError, AttributeError, ValueError) as ex:
|
|
201
|
-
|
|
222
|
+
self._logger.warning(f'Unable to read Request info: {ex}')
|
|
202
223
|
### modified Session Object:
|
|
203
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,
|
|
@@ -24,7 +24,7 @@ class CookieStorage(AbstractStorage):
|
|
|
24
24
|
path: str = "/",
|
|
25
25
|
secret_key: Union[str, bytes, bytearray, fernet.Fernet] = None,
|
|
26
26
|
**kwargs
|
|
27
|
-
|
|
27
|
+
) -> None:
|
|
28
28
|
super(
|
|
29
29
|
CookieStorage, self
|
|
30
30
|
).__init__(
|
|
@@ -75,7 +75,8 @@ class CookieStorage(AbstractStorage):
|
|
|
75
75
|
async def get_session(self, request: web.Request) -> SessionData:
|
|
76
76
|
pass
|
|
77
77
|
|
|
78
|
-
async def save_session(
|
|
78
|
+
async def save_session(
|
|
79
|
+
self,
|
|
79
80
|
request: web.Request,
|
|
80
81
|
response: web.StreamResponse,
|
|
81
82
|
session: SessionData
|
|
@@ -1,14 +1,15 @@
|
|
|
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
|
|
7
|
-
import aioredis
|
|
8
|
-
from
|
|
6
|
+
from redis import asyncio as aioredis
|
|
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
|
|
@@ -24,7 +25,7 @@ class RedisStorage(AbstractStorage):
|
|
|
24
25
|
domain: Optional[str] = None,
|
|
25
26
|
path: str = "/",
|
|
26
27
|
**kwargs
|
|
27
|
-
|
|
28
|
+
) -> None:
|
|
28
29
|
self._redis: Callable = None
|
|
29
30
|
super(
|
|
30
31
|
RedisStorage, self
|
|
@@ -43,21 +44,27 @@ class RedisStorage(AbstractStorage):
|
|
|
43
44
|
decode_responses=True,
|
|
44
45
|
encoding='utf-8'
|
|
45
46
|
)
|
|
46
|
-
except Exception as err:
|
|
47
|
-
|
|
47
|
+
except Exception as err: # pylint: disable=W0703
|
|
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
|
-
await self._redis.disconnect(inuse_connections
|
|
53
|
-
except Exception as ex:
|
|
54
|
-
|
|
53
|
+
await self._redis.disconnect(inuse_connections=True)
|
|
54
|
+
except Exception as ex: # pylint: disable=W0703
|
|
55
|
+
self._logger.warning(ex)
|
|
55
56
|
|
|
56
|
-
async def get_session(
|
|
57
|
+
async def get_session(
|
|
58
|
+
self,
|
|
59
|
+
request: web.Request,
|
|
60
|
+
userdata: dict = None
|
|
61
|
+
) -> SessionData:
|
|
57
62
|
try:
|
|
58
63
|
session = request.get(SESSION_OBJECT)
|
|
59
|
-
except Exception as err:
|
|
60
|
-
|
|
64
|
+
except Exception as err: # pylint: disable=W0703
|
|
65
|
+
self._logger.debug(
|
|
66
|
+
f'Redis Storage: Error on get Session: {err!s}'
|
|
67
|
+
)
|
|
61
68
|
session = None
|
|
62
69
|
if session is None:
|
|
63
70
|
storage = request.get(SESSION_STORAGE)
|
|
@@ -67,28 +74,40 @@ class RedisStorage(AbstractStorage):
|
|
|
67
74
|
)
|
|
68
75
|
session = await self.load_session(request, userdata)
|
|
69
76
|
request[SESSION_OBJECT] = session
|
|
70
|
-
request[
|
|
77
|
+
request[SESSION_REQUEST_KEY] = session
|
|
71
78
|
return session
|
|
72
79
|
|
|
73
80
|
async def invalidate(self, request: web.Request, session: SessionData) -> None:
|
|
74
81
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
75
82
|
if not session:
|
|
76
83
|
data = None
|
|
77
|
-
session_id = request.get(
|
|
84
|
+
session_id = request.get(SESSION_ID, None)
|
|
78
85
|
if session_id:
|
|
79
|
-
|
|
86
|
+
_id_ = f"session:{session_id}"
|
|
87
|
+
data = await conn.get(_id_)
|
|
80
88
|
if data is None:
|
|
81
89
|
# nothing to forgot
|
|
82
90
|
return True
|
|
83
91
|
try:
|
|
84
92
|
# delete the ID of the session
|
|
85
|
-
await conn.delete(session.
|
|
86
|
-
session.invalidate()
|
|
87
|
-
except Exception as err:
|
|
88
|
-
|
|
93
|
+
await conn.delete(f"session:{session.session_id}")
|
|
94
|
+
session.invalidate() # invalidate this session object
|
|
95
|
+
except Exception as err: # pylint: disable=W0703
|
|
96
|
+
self._logger.error(err)
|
|
89
97
|
return False
|
|
90
98
|
return True
|
|
91
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
|
+
|
|
92
111
|
async def load_session(
|
|
93
112
|
self,
|
|
94
113
|
request: web.Request,
|
|
@@ -106,9 +125,9 @@ class RedisStorage(AbstractStorage):
|
|
|
106
125
|
---
|
|
107
126
|
new: if False, new session is not created.
|
|
108
127
|
"""
|
|
109
|
-
#
|
|
128
|
+
# first: for security, check if cookie csrf_secure exists
|
|
110
129
|
session_id = None
|
|
111
|
-
if ignore_cookie is False and
|
|
130
|
+
if ignore_cookie is False and self._use_cookies is True:
|
|
112
131
|
cookie = self.load_cookie(request)
|
|
113
132
|
try:
|
|
114
133
|
session_id = cookie['session_id']
|
|
@@ -118,26 +137,35 @@ class RedisStorage(AbstractStorage):
|
|
|
118
137
|
try:
|
|
119
138
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
120
139
|
except Exception as err:
|
|
121
|
-
|
|
140
|
+
self._logger.exception(
|
|
122
141
|
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
123
142
|
)
|
|
124
143
|
raise RuntimeError(
|
|
125
144
|
f'Redis Storage: Error loading Redis Session: {err!s}'
|
|
126
145
|
) from err
|
|
127
146
|
if session_id is None:
|
|
128
|
-
session_id = request.get(
|
|
147
|
+
session_id = request.get(SESSION_ID, None)
|
|
129
148
|
if not session_id:
|
|
130
|
-
session_id = userdata.get(
|
|
131
|
-
|
|
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)
|
|
153
|
+
if not session_id:
|
|
154
|
+
session_id = await self.get_session_id(conn, session_identity)
|
|
155
|
+
print('SESSION IDENTITY IS:', session_identity, session_id)
|
|
132
156
|
if session_id is None and new is False:
|
|
157
|
+
# No Session was found, returning false:
|
|
133
158
|
return False
|
|
134
159
|
# we need to load session data from redis
|
|
135
|
-
|
|
160
|
+
self._logger.debug(
|
|
161
|
+
f':::::: LOAD SESSION FOR {session_id} ::::: '
|
|
162
|
+
)
|
|
163
|
+
_id_ = f"session:{session_id}"
|
|
136
164
|
try:
|
|
137
|
-
data = await conn.get(
|
|
138
|
-
except Exception as err:
|
|
139
|
-
|
|
140
|
-
f'Redis Storage: Error Getting Session
|
|
165
|
+
data = await conn.get(_id_)
|
|
166
|
+
except Exception as err: # pylint: disable=W0703
|
|
167
|
+
self._logger.error(
|
|
168
|
+
f'Redis Storage: Error Getting Session: {err!s}'
|
|
141
169
|
)
|
|
142
170
|
data = None
|
|
143
171
|
if data is None:
|
|
@@ -145,46 +173,57 @@ class RedisStorage(AbstractStorage):
|
|
|
145
173
|
# create a new session if not exists:
|
|
146
174
|
return await self.new_session(request, userdata)
|
|
147
175
|
else:
|
|
176
|
+
# No Session Was Found
|
|
148
177
|
return False
|
|
149
178
|
try:
|
|
150
179
|
data = self._decoder(data)
|
|
151
180
|
session = SessionData(
|
|
152
|
-
|
|
181
|
+
id=session_id,
|
|
182
|
+
identity=session_identity,
|
|
153
183
|
data=data,
|
|
154
184
|
new=False,
|
|
155
185
|
max_age=self.max_age
|
|
156
186
|
)
|
|
157
|
-
except Exception as err:
|
|
158
|
-
|
|
187
|
+
except Exception as err: # pylint: disable=W0703
|
|
188
|
+
self._logger.warning(
|
|
189
|
+
f"Error creating Session Data: {err}"
|
|
190
|
+
)
|
|
159
191
|
session = SessionData(
|
|
160
|
-
|
|
192
|
+
id=session_id,
|
|
193
|
+
identity=session_identity,
|
|
161
194
|
data=None,
|
|
162
195
|
new=True,
|
|
163
196
|
max_age=self.max_age
|
|
164
197
|
)
|
|
165
198
|
## add other options to session:
|
|
166
199
|
self.session_info(session, request)
|
|
167
|
-
request[SESSION_KEY] = session_id
|
|
168
200
|
session[SESSION_KEY] = session_id
|
|
201
|
+
request[SESSION_KEY] = session_identity
|
|
202
|
+
request[SESSION_ID] = session_id
|
|
169
203
|
request[SESSION_OBJECT] = session
|
|
170
|
-
request[
|
|
171
|
-
if self.
|
|
204
|
+
request[SESSION_REQUEST_KEY] = session
|
|
205
|
+
if self._use_cookies is True and response is not None:
|
|
172
206
|
cookie_data = {
|
|
173
207
|
"session_id": session_id
|
|
174
208
|
}
|
|
175
209
|
cookie_data = self._encoder(cookie_data)
|
|
176
|
-
self.save_cookie(
|
|
210
|
+
self.save_cookie(
|
|
211
|
+
response,
|
|
212
|
+
cookie_data=cookie_data,
|
|
213
|
+
max_age=self.max_age
|
|
214
|
+
)
|
|
177
215
|
return session
|
|
178
216
|
|
|
179
|
-
async def save_session(
|
|
217
|
+
async def save_session(
|
|
218
|
+
self,
|
|
180
219
|
request: web.Request,
|
|
181
220
|
response: web.StreamResponse,
|
|
182
221
|
session: SessionData
|
|
183
222
|
) -> None:
|
|
184
223
|
"""Save the whole session in the backend Storage."""
|
|
185
|
-
session_id = session.
|
|
224
|
+
session_id = session.session_id if session else request.get(SESSION_ID, None)
|
|
186
225
|
if not session_id:
|
|
187
|
-
session_id = session.get(
|
|
226
|
+
session_id = session.get(SESSION_ID, None)
|
|
188
227
|
if not session_id:
|
|
189
228
|
session_id = self.id_factory()
|
|
190
229
|
if session.empty:
|
|
@@ -192,15 +231,14 @@ class RedisStorage(AbstractStorage):
|
|
|
192
231
|
data = self._encoder(session.session_data())
|
|
193
232
|
max_age = session.max_age
|
|
194
233
|
expire = max_age if max_age is not None else 0
|
|
195
|
-
# TODO: add expiration on redis to value
|
|
196
234
|
try:
|
|
197
235
|
conn = aioredis.Redis(connection_pool=self._redis)
|
|
198
|
-
|
|
199
|
-
|
|
236
|
+
_id_ = f"session:{session_id}"
|
|
237
|
+
await conn.set(
|
|
238
|
+
_id_, data, expire
|
|
200
239
|
)
|
|
201
|
-
except Exception as err:
|
|
202
|
-
|
|
203
|
-
logging.exception(err, stack_info=True)
|
|
240
|
+
except Exception as err: # pylint: disable=W0703
|
|
241
|
+
self._logger.exception(err, stack_info=True)
|
|
204
242
|
return False
|
|
205
243
|
|
|
206
244
|
async def new_session(
|
|
@@ -210,53 +248,84 @@ class RedisStorage(AbstractStorage):
|
|
|
210
248
|
response: web.StreamResponse = None
|
|
211
249
|
) -> SessionData:
|
|
212
250
|
"""Create a New Session Object for this User."""
|
|
213
|
-
|
|
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)
|
|
214
264
|
if not session_id:
|
|
215
265
|
try:
|
|
216
|
-
session_id = data[
|
|
266
|
+
session_id = data[SESSION_ID]
|
|
217
267
|
except KeyError:
|
|
218
268
|
session_id = self.id_factory()
|
|
219
|
-
|
|
269
|
+
self._logger.debug(
|
|
270
|
+
f':::::: CREATING A NEW SESSION FOR {session_id} ::::: '
|
|
271
|
+
)
|
|
220
272
|
if not data:
|
|
221
273
|
data = {}
|
|
222
274
|
# saving this new session on DB
|
|
223
275
|
try:
|
|
224
|
-
conn = aioredis.Redis(connection_pool=self._redis)
|
|
225
276
|
t = time.time()
|
|
226
277
|
data['created'] = t
|
|
227
278
|
data['last_visit'] = t
|
|
228
|
-
data[
|
|
229
|
-
data[
|
|
279
|
+
data[SESSION_KEY] = session_identity
|
|
280
|
+
data[SESSION_ID] = session_id
|
|
230
281
|
dt = self._encoder(data)
|
|
282
|
+
_id_ = f'session:{session_id}'
|
|
231
283
|
result = await conn.set(
|
|
232
|
-
|
|
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
|
|
233
294
|
)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
logging.exception(err)
|
|
295
|
+
except Exception as err: # pylint: disable=W0703
|
|
296
|
+
self._logger.exception(err)
|
|
237
297
|
return False
|
|
238
298
|
try:
|
|
239
299
|
session = SessionData(
|
|
240
|
-
|
|
300
|
+
id=session_id,
|
|
301
|
+
identity=session_identity,
|
|
241
302
|
data=data,
|
|
242
303
|
new=True,
|
|
243
304
|
max_age=self.max_age
|
|
244
305
|
)
|
|
245
|
-
if self.
|
|
306
|
+
if self._use_cookies is True and response is not None:
|
|
246
307
|
cookie_data = {
|
|
247
308
|
"last_visit": t,
|
|
248
309
|
"session_id": session_id
|
|
249
310
|
}
|
|
311
|
+
# TODO: adding crypt
|
|
250
312
|
cookie_data = self._encoder(cookie_data)
|
|
251
|
-
self.save_cookie(
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
313
|
+
self.save_cookie(
|
|
314
|
+
response,
|
|
315
|
+
cookie_data=cookie_data,
|
|
316
|
+
max_age=self.max_age
|
|
317
|
+
)
|
|
318
|
+
except Exception as err: # pylint: disable=W0703
|
|
319
|
+
self._logger.exception(
|
|
320
|
+
f'Error creating Session Data: {err!s}'
|
|
321
|
+
)
|
|
322
|
+
return False
|
|
255
323
|
# Saving Session Object:
|
|
256
324
|
## add other options to session:
|
|
257
325
|
self.session_info(session, request)
|
|
258
|
-
session[SESSION_KEY] =
|
|
326
|
+
session[SESSION_KEY] = session_identity
|
|
327
|
+
request[SESSION_ID] = session_id
|
|
259
328
|
request[SESSION_OBJECT] = session
|
|
260
|
-
request[SESSION_KEY] =
|
|
261
|
-
request[
|
|
329
|
+
request[SESSION_KEY] = session_identity
|
|
330
|
+
request[SESSION_REQUEST_KEY] = session
|
|
262
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.
|
|
8
|
+
__version__ = '0.6.2'
|
|
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.
|
|
3
|
+
Version: 0.6.2
|
|
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
|
|
@@ -21,11 +21,18 @@ Classifier: Topic :: Database :: Front-Ends
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.9
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.10
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
25
|
Classifier: Framework :: AsyncIO
|
|
25
26
|
Classifier: Framework :: aiohttp
|
|
26
|
-
Requires-Python: >=3.9.
|
|
27
|
+
Requires-Python: >=3.9.13
|
|
27
28
|
Description-Content-Type: text/markdown
|
|
28
29
|
License-File: LICENSE
|
|
30
|
+
Requires-Dist: aiohttp>=3.9.5
|
|
31
|
+
Requires-Dist: asyncio==3.4.3
|
|
32
|
+
Requires-Dist: jsonpickle>=3.0.2
|
|
33
|
+
Requires-Dist: redis>=5.0.4
|
|
34
|
+
Requires-Dist: python_datamodel>=0.7.0
|
|
35
|
+
Requires-Dist: navconfig[default]>=1.7.0
|
|
29
36
|
|
|
30
37
|
# Navigator_Session #
|
|
31
38
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
'setuptools==74.0.0',
|
|
4
|
+
'Cython==3.0.11',
|
|
5
|
+
'wheel==0.44.0'
|
|
6
6
|
]
|
|
7
7
|
|
|
8
8
|
build-backend = "setuptools.build_meta"
|
|
@@ -20,6 +20,7 @@ classifiers=[
|
|
|
20
20
|
"Programming Language :: Python :: 3.9",
|
|
21
21
|
"Programming Language :: Python :: 3.10",
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
23
24
|
"Programming Language :: Python",
|
|
24
25
|
"Typing :: Typed",
|
|
25
26
|
"Environment :: Web Environment",
|
|
@@ -32,7 +33,7 @@ classifiers=[
|
|
|
32
33
|
"License :: OSI Approved :: BSD License",
|
|
33
34
|
]
|
|
34
35
|
description-file = "README.md"
|
|
35
|
-
requires-python = ">=3.9.
|
|
36
|
+
requires-python = ">=3.9.13"
|
|
36
37
|
|
|
37
38
|
[tool.pytest.ini_options]
|
|
38
39
|
addopts = [
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
"""Navigator-Session.
|
|
3
3
|
|
|
4
|
-
Asynchronous library for managing user-specific data into a session object,
|
|
4
|
+
Asynchronous library for managing user-specific data into a session object,
|
|
5
|
+
used by Navigator.
|
|
5
6
|
See:
|
|
6
7
|
https://github.com/phenobarbital/navigator-session
|
|
7
8
|
"""
|
|
@@ -32,7 +33,9 @@ with open(version, 'r', encoding='utf-8') as meta:
|
|
|
32
33
|
'__title__',
|
|
33
34
|
'__description__',
|
|
34
35
|
'__author__',
|
|
35
|
-
'__license__',
|
|
36
|
+
'__license__',
|
|
37
|
+
'__author_email__'
|
|
38
|
+
):
|
|
36
39
|
v = node.value
|
|
37
40
|
if name.id == '__version__':
|
|
38
41
|
__version__ = v.s
|
|
@@ -51,7 +54,7 @@ with open(version, 'r', encoding='utf-8') as meta:
|
|
|
51
54
|
setup(
|
|
52
55
|
name="navigator-session",
|
|
53
56
|
version=__version__,
|
|
54
|
-
python_requires=">=3.9.
|
|
57
|
+
python_requires=">=3.9.13",
|
|
55
58
|
url="https://github.com/phenobarbital/navigator-session",
|
|
56
59
|
description=__description__,
|
|
57
60
|
keywords=['asyncio', 'session', 'aioredis', 'aiohttp'],
|
|
@@ -69,6 +72,7 @@ setup(
|
|
|
69
72
|
"Programming Language :: Python :: 3.9",
|
|
70
73
|
"Programming Language :: Python :: 3.10",
|
|
71
74
|
"Programming Language :: Python :: 3.11",
|
|
75
|
+
"Programming Language :: Python :: 3.12",
|
|
72
76
|
"Framework :: AsyncIO",
|
|
73
77
|
"Framework :: aiohttp",
|
|
74
78
|
],
|
|
@@ -77,20 +81,17 @@ setup(
|
|
|
77
81
|
packages=find_packages(exclude=["contrib", "docs", "tests"]),
|
|
78
82
|
license=__license__,
|
|
79
83
|
setup_requires=[
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
'setuptools==74.0.0',
|
|
85
|
+
'Cython==3.0.11',
|
|
86
|
+
'wheel==0.44.0'
|
|
83
87
|
],
|
|
84
88
|
install_requires=[
|
|
85
|
-
"
|
|
86
|
-
"aiohttp==3.8.4",
|
|
87
|
-
"uvloop==0.17.0",
|
|
89
|
+
"aiohttp>=3.9.5",
|
|
88
90
|
"asyncio==3.4.3",
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"python_datamodel>=0.2.1"
|
|
91
|
+
"jsonpickle>=3.0.2",
|
|
92
|
+
"redis>=5.0.4",
|
|
93
|
+
"python_datamodel>=0.7.0",
|
|
94
|
+
"navconfig[default]>=1.7.0",
|
|
94
95
|
],
|
|
95
96
|
project_urls={ # Optional
|
|
96
97
|
"Source": "https://github.com/phenobarbital/navigator-session",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
aiounittest==1.4.2
|
|
2
|
-
black==23.1.0
|
|
3
|
-
codecov==2.1.12
|
|
4
|
-
coverage[toml]==7.2.1
|
|
5
|
-
flit==3.8.0
|
|
6
|
-
hypothesis==6.68.2
|
|
7
|
-
ipython==8.11.0
|
|
8
|
-
lint==1.2.1
|
|
9
|
-
mypy==1.1.1
|
|
10
|
-
pylint==2.17.0
|
|
11
|
-
sphinx==6.1.3
|
|
12
|
-
pytest>=6.0.0
|
|
13
|
-
pytest-asyncio==0.20.3
|
|
14
|
-
pytest-cov==4.0.0
|
|
15
|
-
pytest-cython==0.2.0
|
|
16
|
-
pytest-xdist==3.2.0
|
|
17
|
-
pytest-assume==2.4.3
|
|
18
|
-
sphinx==6.1.3
|
|
19
|
-
tox==4.4.6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{navigator-session-0.4.2 → navigator_session-0.6.2}/navigator_session.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|