jamlib 3.0.0b13.dev2__tar.gz → 3.0.0b15__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.
- {jamlib-3.0.0b13.dev2/src/jamlib.egg-info → jamlib-3.0.0b15}/PKG-INFO +1 -1
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/pyproject.toml +1 -1
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__init__.py +1 -1
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/__init__.py +7 -11
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/plugins.py +9 -8
- jamlib-3.0.0b15/src/jam/ext/flask/__init__.py +26 -0
- jamlib-3.0.0b15/src/jam/ext/flask/extensions.py +212 -0
- jamlib-3.0.0b15/src/jam/ext/flask/objects.py +12 -0
- jamlib-3.0.0b15/src/jam/ext/starlette/__init__.py +12 -0
- jamlib-3.0.0b15/src/jam/ext/starlette/auth_backends.py +143 -0
- jamlib-3.0.0b15/src/jam/ext/starlette/value.py +18 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15/src/jamlib.egg-info}/PKG-INFO +1 -1
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/SOURCES.txt +3 -2
- jamlib-3.0.0b13.dev2/src/jam/ext/flask/__init__.py +0 -11
- jamlib-3.0.0b13.dev2/src/jam/ext/flask/extensions.py +0 -167
- jamlib-3.0.0b13.dev2/src/jam/ext/starlette/__init__.py +0 -17
- jamlib-3.0.0b13.dev2/src/jam/ext/starlette/backends.py +0 -132
- jamlib-3.0.0b13.dev2/src/jam/ext/starlette/objects.py +0 -53
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/LICENSE.md +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/README.md +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/setup.cfg +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__base_encoder__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/__deprecated__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/instance.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/jwt/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/github.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/google.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/oauth2/client.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/json.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/aio/sessions/redis.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/encoders.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/base.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/jwt.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/oauth2.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/paseto.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/exceptions/sessions.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/fastapi/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/middleware.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/objects.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/ext/litestar/plugins.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/instance.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__algorithms__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/__types__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/__abc_list_repo__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/json.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/lists/redis.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/module.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/jwt/utils.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/logger.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/github.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/google.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/oauth2/client.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/hotp.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/otp/totp.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/utils.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v1.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v2.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v3.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/paseto/v4.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/py.typed +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/__base__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/json.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/sessions/redis.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/clients.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/tests/fakers.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/__init__.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/aes.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/await_maybe.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/basic_auth.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/config_maker.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/ed.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/otp_keys.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/rsa.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/salt_hash.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/symmetric.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/xchacha20poly1305.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jam/utils/xor.py +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/dependency_links.txt +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/requires.txt +0 -0
- {jamlib-3.0.0b13.dev2 → jamlib-3.0.0b15}/src/jamlib.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jamlib
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b15
|
|
4
4
|
Summary: Simple and universal library for authorization.
|
|
5
5
|
Author-email: Makridenko Adrian <adrianmakridenko@duck.com>, Ksenia Travnikova <kseniatravnikova@duck.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -4,16 +4,12 @@
|
|
|
4
4
|
All Jam exceptions
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .base import
|
|
8
|
-
JamError,
|
|
9
|
-
JamConfigurationError,
|
|
10
|
-
JamValidationError
|
|
11
|
-
)
|
|
7
|
+
from .base import JamError, JamConfigurationError, JamValidationError
|
|
12
8
|
|
|
13
9
|
from .oauth2 import (
|
|
14
10
|
JamOAuth2Error,
|
|
15
11
|
JamOAuth2EmptyRaw,
|
|
16
|
-
JamOAuth2ProviderNotConfigured
|
|
12
|
+
JamOAuth2ProviderNotConfigured,
|
|
17
13
|
)
|
|
18
14
|
|
|
19
15
|
from .jwt import (
|
|
@@ -33,14 +29,14 @@ from .paseto import (
|
|
|
33
29
|
JamPASETOInvalidSecp384r1Key,
|
|
34
30
|
JamPASTOKeyVerificationError,
|
|
35
31
|
JamPASETOInvalidPurpose,
|
|
36
|
-
JamPASETOInvalidTokenFormat
|
|
32
|
+
JamPASETOInvalidTokenFormat,
|
|
37
33
|
)
|
|
38
34
|
|
|
39
35
|
from .plugins import (
|
|
36
|
+
JamFlaskPluginConfigError,
|
|
37
|
+
JamFlaskPluginError,
|
|
40
38
|
JamLitestarPluginConfigError,
|
|
41
39
|
JamLitestarPluginError,
|
|
42
|
-
JamStarlettePluginConfigError,
|
|
43
|
-
JamStarlettePluginError
|
|
44
40
|
)
|
|
45
41
|
|
|
46
42
|
from .sessions import (
|
|
@@ -72,8 +68,8 @@ __all__ = [
|
|
|
72
68
|
"JamPASTOKeyVerificationError",
|
|
73
69
|
"JamLitestarPluginConfigError",
|
|
74
70
|
"JamLitestarPluginError",
|
|
75
|
-
"
|
|
76
|
-
"
|
|
71
|
+
"JamFlaskPluginConfigError",
|
|
72
|
+
"JamFlaskPluginError",
|
|
77
73
|
"JamSessionNotFound",
|
|
78
74
|
"JamSessionEmptyAESKey",
|
|
79
75
|
]
|
|
@@ -16,15 +16,16 @@ class JamLitestarPluginError(JamError):
|
|
|
16
16
|
default_message = "An error occurred for a litestar plugin."
|
|
17
17
|
default_code = "jam.plugin.litestar"
|
|
18
18
|
|
|
19
|
-
class JamStarlettePluginConfigError(JamConfigurationError):
|
|
20
|
-
"""Exception raised when a configuration error occurs for a starlette plugin."""
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
class JamFlaskPluginConfigError(JamConfigurationError):
|
|
21
|
+
"""Exception raised when a configuration error occurs for a flask plugin."""
|
|
24
22
|
|
|
23
|
+
default_message = "Configuration error occurred for a flask plugin."
|
|
24
|
+
default_code = "jam.configuration.plugin.flask"
|
|
25
25
|
|
|
26
|
-
class JamStarlettePluginError(JamError):
|
|
27
|
-
"""Exception raised when an error occurs for a starlette plugin."""
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
class JamFlaskPluginError(JamError):
|
|
28
|
+
"""Exception raised when an error occurs for a flask plugin."""
|
|
29
|
+
|
|
30
|
+
default_message = "An error occurred for a flask plugin."
|
|
31
|
+
default_code = "jam.plugin.flask"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""Flask integration.
|
|
4
|
+
|
|
5
|
+
Flask docs: https://flask.palletsprojects.com
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .extensions import (
|
|
9
|
+
JWTExtension,
|
|
10
|
+
OAuth2Extension,
|
|
11
|
+
PASETOExtension,
|
|
12
|
+
SessionExtension,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from .objects import (
|
|
16
|
+
Token,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"Token",
|
|
22
|
+
"JWTExtension",
|
|
23
|
+
"SessionExtension",
|
|
24
|
+
"PASETOExtension",
|
|
25
|
+
"OAuth2Extension",
|
|
26
|
+
]
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import flask
|
|
6
|
+
|
|
7
|
+
from jam.exceptions import JamFlaskPluginConfigError
|
|
8
|
+
from jam.jwt import JWT
|
|
9
|
+
from jam.oauth2 import create_instance as create_oauth2
|
|
10
|
+
from jam.paseto import create_instance as create_paseto
|
|
11
|
+
from jam.sessions import create_instance as create_session
|
|
12
|
+
from jam.utils.config_maker import GENERIC_POINTER, __config_maker__
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class BaseExtension:
|
|
16
|
+
"""Base Jam extension for Flask."""
|
|
17
|
+
|
|
18
|
+
MODULE: Any
|
|
19
|
+
_CONFIG_KEY: str
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
app: flask.Flask | None = None,
|
|
24
|
+
auth: Any | None = None,
|
|
25
|
+
**kwargs: Any,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the extension.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
app (flask.Flask | None): Flask application instance
|
|
31
|
+
auth (Any | None): Pre-created auth module instance
|
|
32
|
+
**kwargs: Configuration arguments
|
|
33
|
+
"""
|
|
34
|
+
self.app = app
|
|
35
|
+
self._auth = auth
|
|
36
|
+
self._config = kwargs
|
|
37
|
+
if app is not None:
|
|
38
|
+
self.init_app(app)
|
|
39
|
+
|
|
40
|
+
def init_app(self, app: flask.Flask) -> None:
|
|
41
|
+
"""Initialize the Flask application.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
app (flask.Flask): Flask application instance
|
|
45
|
+
"""
|
|
46
|
+
self.app = app
|
|
47
|
+
if self._auth is None:
|
|
48
|
+
self._auth = self.MODULE(**self._config)
|
|
49
|
+
app.extensions[self._CONFIG_KEY] = self._auth
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BaseAuthExtension(BaseExtension):
|
|
53
|
+
"""Base Jam authentication extension for Flask."""
|
|
54
|
+
|
|
55
|
+
MODULE: Any
|
|
56
|
+
_CONFIG_KEY: str
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
app: flask.Flask | None = None,
|
|
61
|
+
auth: Any | None = None,
|
|
62
|
+
config: dict[str, Any] | str | None = None,
|
|
63
|
+
pointer: str = GENERIC_POINTER,
|
|
64
|
+
cookie_name: str | None = None,
|
|
65
|
+
header_name: str | None = None,
|
|
66
|
+
bearer: bool = True,
|
|
67
|
+
**kwargs: Any,
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Initialize the authentication extension.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
app (flask.Flask | None): Flask application instance
|
|
73
|
+
auth (Any | None): Pre-created auth module instance
|
|
74
|
+
config (dict[str, Any] | str | None): Jam config as path/to/file or dict.
|
|
75
|
+
pointer (str): Config pointer
|
|
76
|
+
cookie_name (str | None): Cookie name to read token
|
|
77
|
+
header_name (str | None): Header name to read token
|
|
78
|
+
bearer (bool): Strip "Bearer " prefix from header
|
|
79
|
+
**kwargs: Configuration arguments if config=None
|
|
80
|
+
"""
|
|
81
|
+
self._cookie_name = cookie_name
|
|
82
|
+
self._header_name = header_name
|
|
83
|
+
self._bearer = bearer
|
|
84
|
+
|
|
85
|
+
if not cookie_name and not header_name:
|
|
86
|
+
raise JamFlaskPluginConfigError(
|
|
87
|
+
message="Cookie name and header name cannot be both None.",
|
|
88
|
+
details={
|
|
89
|
+
"cookie_name": cookie_name,
|
|
90
|
+
"header_name": header_name,
|
|
91
|
+
},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
_config: dict[str, Any] | None = (
|
|
95
|
+
__config_maker__(config, pointer) if config else None
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
params = _config.pop(self._CONFIG_KEY) if _config else kwargs
|
|
99
|
+
super().__init__(app, auth=auth, **params)
|
|
100
|
+
|
|
101
|
+
def _get_token(self) -> str | None:
|
|
102
|
+
token = None
|
|
103
|
+
if self._header_name:
|
|
104
|
+
token = flask.request.headers.get(self._header_name, None)
|
|
105
|
+
if token and self._bearer and token.startswith("Bearer "):
|
|
106
|
+
token = token[7:]
|
|
107
|
+
elif self._cookie_name:
|
|
108
|
+
token = flask.request.cookies.get(self._cookie_name, None)
|
|
109
|
+
return token
|
|
110
|
+
|
|
111
|
+
def _get_payload(self) -> dict[str, Any] | None:
|
|
112
|
+
raise NotImplementedError
|
|
113
|
+
|
|
114
|
+
def init_app(self, app: flask.Flask) -> None:
|
|
115
|
+
"""Initialize the Flask application."""
|
|
116
|
+
super().init_app(app)
|
|
117
|
+
app.before_request(self._put_auth_in_g)
|
|
118
|
+
|
|
119
|
+
def _put_auth_in_g(self) -> None:
|
|
120
|
+
setattr(flask.g, self._CONFIG_KEY, self._auth)
|
|
121
|
+
self._get_payload()
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class JWTExtension(BaseAuthExtension):
|
|
125
|
+
"""JWT extension for Flask."""
|
|
126
|
+
|
|
127
|
+
MODULE = staticmethod(JWT)
|
|
128
|
+
_CONFIG_KEY = "jwt"
|
|
129
|
+
|
|
130
|
+
def _get_payload(self) -> dict[str, Any] | None:
|
|
131
|
+
token = self._get_token()
|
|
132
|
+
flask.g.payload = None
|
|
133
|
+
if not token:
|
|
134
|
+
return None
|
|
135
|
+
try:
|
|
136
|
+
payload = self._auth.decode(token)
|
|
137
|
+
flask.g.payload = payload
|
|
138
|
+
return payload
|
|
139
|
+
except Exception:
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class SessionExtension(BaseAuthExtension):
|
|
144
|
+
"""Session extension for Flask."""
|
|
145
|
+
|
|
146
|
+
MODULE = staticmethod(create_session)
|
|
147
|
+
_CONFIG_KEY = "sessions"
|
|
148
|
+
|
|
149
|
+
def _get_payload(self) -> dict[str, Any] | None:
|
|
150
|
+
token = self._get_token()
|
|
151
|
+
flask.g.payload = None
|
|
152
|
+
if not token:
|
|
153
|
+
return None
|
|
154
|
+
try:
|
|
155
|
+
payload = self._auth.get(token)
|
|
156
|
+
flask.g.payload = payload
|
|
157
|
+
return payload
|
|
158
|
+
except Exception:
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class PASETOExtension(BaseAuthExtension):
|
|
163
|
+
"""PASETO extension for Flask."""
|
|
164
|
+
|
|
165
|
+
MODULE = staticmethod(create_paseto)
|
|
166
|
+
_CONFIG_KEY = "paseto"
|
|
167
|
+
|
|
168
|
+
def _get_payload(self) -> dict[str, Any] | None:
|
|
169
|
+
token = self._get_token()
|
|
170
|
+
flask.g.payload = None
|
|
171
|
+
if not token:
|
|
172
|
+
return None
|
|
173
|
+
try:
|
|
174
|
+
data = self._auth.decode(token)
|
|
175
|
+
if not data:
|
|
176
|
+
return None
|
|
177
|
+
payload = data[0] if isinstance(data, tuple) else data
|
|
178
|
+
flask.g.payload = payload
|
|
179
|
+
return payload
|
|
180
|
+
except Exception:
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class OAuth2Extension(BaseExtension):
|
|
185
|
+
"""OAuth2 extension for Flask."""
|
|
186
|
+
|
|
187
|
+
MODULE = staticmethod(create_oauth2)
|
|
188
|
+
_CONFIG_KEY = "oauth2"
|
|
189
|
+
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
app: flask.Flask | None = None,
|
|
193
|
+
auth: Any | None = None,
|
|
194
|
+
config: dict[str, Any] | str | None = None,
|
|
195
|
+
pointer: str = GENERIC_POINTER,
|
|
196
|
+
**kwargs: Any,
|
|
197
|
+
) -> None:
|
|
198
|
+
"""Initialize the OAuth2 extension.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
app (flask.Flask | None): Flask application instance
|
|
202
|
+
auth (Any | None): Pre-created auth module instance
|
|
203
|
+
config (dict[str, Any] | str | None): Jam config as path/to/file or dict.
|
|
204
|
+
pointer (str): Config pointer
|
|
205
|
+
**kwargs: Configuration arguments if config=None
|
|
206
|
+
"""
|
|
207
|
+
_config: dict[str, Any] | None = (
|
|
208
|
+
__config_maker__(config, pointer) if config else None
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
params = _config.pop(self._CONFIG_KEY) if _config else kwargs
|
|
212
|
+
super().__init__(app, auth=auth, **params)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from starlette.authentication import (
|
|
6
|
+
AuthCredentials,
|
|
7
|
+
AuthenticationBackend,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
BaseUser,
|
|
10
|
+
)
|
|
11
|
+
from starlette.requests import HTTPConnection
|
|
12
|
+
|
|
13
|
+
from jam.__base__ import BaseJam
|
|
14
|
+
from jam.utils.await_maybe import await_maybe
|
|
15
|
+
|
|
16
|
+
from .value import Payload
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class JWTBackend(AuthenticationBackend):
|
|
20
|
+
"""JWT Backend for Starlette AuthenticationMiddleware."""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
jam: BaseJam,
|
|
25
|
+
cookie_name: str | None = None,
|
|
26
|
+
header_name: str | None = "Authorization",
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Constructor.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
jam (BaseJam): Jam instance
|
|
32
|
+
cookie_name (str | None): Access token cookie name
|
|
33
|
+
header_name (str | None): Access token header name
|
|
34
|
+
"""
|
|
35
|
+
self._jam = jam
|
|
36
|
+
self.cookie_name = cookie_name
|
|
37
|
+
self.header_name = header_name
|
|
38
|
+
self.__use_list = bool(getattr(self._jam.jwt, "list", False))
|
|
39
|
+
|
|
40
|
+
async def authenticate(
|
|
41
|
+
self, conn: HTTPConnection
|
|
42
|
+
) -> tuple[AuthCredentials, BaseUser] | None:
|
|
43
|
+
"""Starlette authentication handler."""
|
|
44
|
+
logger = self._jam._logger
|
|
45
|
+
token = None
|
|
46
|
+
|
|
47
|
+
logger.debug("JWTBackend: Attempting to extract token from request")
|
|
48
|
+
if self.cookie_name:
|
|
49
|
+
token = conn.cookies.get(self.cookie_name)
|
|
50
|
+
if token:
|
|
51
|
+
logger.debug(f"Token found in cookie '{self.cookie_name}'")
|
|
52
|
+
|
|
53
|
+
if not token and self.header_name:
|
|
54
|
+
header = conn.headers.get(self.header_name)
|
|
55
|
+
if header and header.startswith("Bearer "):
|
|
56
|
+
token = header.split("Bearer ")[1]
|
|
57
|
+
logger.debug(f"Token found in header '{self.header_name}'")
|
|
58
|
+
|
|
59
|
+
if not token:
|
|
60
|
+
logger.debug("No token found in request")
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
logger.debug(
|
|
64
|
+
f"Verifying JWT token (length: {len(token)} chars), check_list={self.__use_list}"
|
|
65
|
+
)
|
|
66
|
+
try:
|
|
67
|
+
payload: dict[str, Any] = await await_maybe(
|
|
68
|
+
self._jam.jwt_verify_token(
|
|
69
|
+
token=token, check_exp=True, check_list=self.__use_list
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
logger.debug(
|
|
73
|
+
f"JWT token verified successfully, payload keys: {list(payload.keys())}"
|
|
74
|
+
)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.warning(f"Token verify error: {e}")
|
|
77
|
+
raise AuthenticationError("Token verification failed.")
|
|
78
|
+
|
|
79
|
+
return AuthCredentials(["authenticated"]), Payload(payload=payload)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class SessionBackend(AuthenticationBackend):
|
|
83
|
+
"""Sessions backend for starlette."""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
jam: BaseJam,
|
|
88
|
+
cookie_name: str | None = "sessionId",
|
|
89
|
+
header_name: str | None = None,
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Constructor.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
jam (BaseJam): Jam instance
|
|
95
|
+
cookie_name (str | None): Session id cookie name
|
|
96
|
+
header_name (str | None): Session id header name
|
|
97
|
+
"""
|
|
98
|
+
self._jam = jam
|
|
99
|
+
self.cookie_name = cookie_name
|
|
100
|
+
self.header_name = header_name
|
|
101
|
+
|
|
102
|
+
async def authenticate(
|
|
103
|
+
self, conn: HTTPConnection
|
|
104
|
+
) -> tuple[AuthCredentials, BaseUser] | None:
|
|
105
|
+
"""Starlette authentication handler."""
|
|
106
|
+
logger = self._jam._logger
|
|
107
|
+
session_id = None
|
|
108
|
+
|
|
109
|
+
logger.debug(
|
|
110
|
+
"SessionBackend: Attempting to extract session ID from request"
|
|
111
|
+
)
|
|
112
|
+
if self.cookie_name:
|
|
113
|
+
session_id = conn.cookies.get(self.cookie_name)
|
|
114
|
+
if session_id:
|
|
115
|
+
logger.debug(f"Session ID found in cookie '{self.cookie_name}'")
|
|
116
|
+
|
|
117
|
+
if not session_id and self.header_name:
|
|
118
|
+
header = conn.headers.get(self.header_name)
|
|
119
|
+
if header and header.startswith("Bearer "):
|
|
120
|
+
session_id = header.split("Bearer ")[1]
|
|
121
|
+
logger.debug(f"Session ID found in header '{self.header_name}'")
|
|
122
|
+
|
|
123
|
+
if not session_id:
|
|
124
|
+
logger.debug("No session ID found in request")
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
logger.debug(f"Getting session data for session ID: {session_id}")
|
|
128
|
+
try:
|
|
129
|
+
payload: dict[str, Any] | None = await await_maybe( # type: ignore[arg-type]
|
|
130
|
+
self._jam.session_get(session_id) # type: ignore[arg-type]
|
|
131
|
+
)
|
|
132
|
+
if payload:
|
|
133
|
+
logger.debug(
|
|
134
|
+
f"Session data retrieved successfully, keys: {list(payload.keys())}"
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
logger.debug(f"Session {session_id} not found")
|
|
138
|
+
# TODO: Return unauthized user
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.warning(f"Session retrieval error: {e}")
|
|
141
|
+
raise AuthenticationError("Token verification failed.")
|
|
142
|
+
|
|
143
|
+
return AuthCredentials(["authenticated"]), Payload(payload=payload)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from starlette.authentication import BaseUser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Payload(BaseUser):
|
|
9
|
+
"""Auth payload."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, payload: dict[str, Any] | None):
|
|
12
|
+
"""Auth payload."""
|
|
13
|
+
self.payload = payload
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def is_authenticated(self) -> bool:
|
|
17
|
+
"""Auth checker."""
|
|
18
|
+
return True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jamlib
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.0b15
|
|
4
4
|
Summary: Simple and universal library for authorization.
|
|
5
5
|
Author-email: Makridenko Adrian <adrianmakridenko@duck.com>, Ksenia Travnikova <kseniatravnikova@duck.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -36,13 +36,14 @@ src/jam/ext/__init__.py
|
|
|
36
36
|
src/jam/ext/fastapi/__init__.py
|
|
37
37
|
src/jam/ext/flask/__init__.py
|
|
38
38
|
src/jam/ext/flask/extensions.py
|
|
39
|
+
src/jam/ext/flask/objects.py
|
|
39
40
|
src/jam/ext/litestar/__init__.py
|
|
40
41
|
src/jam/ext/litestar/middleware.py
|
|
41
42
|
src/jam/ext/litestar/objects.py
|
|
42
43
|
src/jam/ext/litestar/plugins.py
|
|
43
44
|
src/jam/ext/starlette/__init__.py
|
|
44
|
-
src/jam/ext/starlette/
|
|
45
|
-
src/jam/ext/starlette/
|
|
45
|
+
src/jam/ext/starlette/auth_backends.py
|
|
46
|
+
src/jam/ext/starlette/value.py
|
|
46
47
|
src/jam/jwt/__algorithms__.py
|
|
47
48
|
src/jam/jwt/__base__.py
|
|
48
49
|
src/jam/jwt/__init__.py
|