jamlib 3.2.0b2__tar.gz → 3.2.0.post0__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.2.0b2/src/jamlib.egg-info → jamlib-3.2.0.post0}/PKG-INFO +5 -5
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/README.md +3 -3
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/pyproject.toml +2 -2
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__base__.py +187 -27
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__init__.py +2 -4
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/__base__.py +84 -2
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/instance.py +101 -10
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/__base__.py +5 -1
- jamlib-3.2.0.post0/src/jam/exceptions/jose.py +73 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/extensions.py +2 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/plugins.py +3 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/backends.py +2 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/instance.py +87 -5
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/__algorithms__.py +156 -54
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/__base__.py +116 -36
- jamlib-3.2.0.post0/src/jam/jose/__init__.py +113 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwe.py +11 -6
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwk.py +31 -10
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jws.py +20 -1
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/jwt.py +143 -79
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/redis.py +4 -1
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/logger.py +8 -1
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v1.py +4 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v2.py +4 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v3.py +4 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/v4.py +4 -0
- jamlib-3.2.0.post0/src/jam/plugins/__base__.py +58 -0
- jamlib-3.2.0.post0/src/jam/plugins/__init__.py +10 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/__base__.py +5 -1
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/tests/clients.py +271 -53
- jamlib-3.2.0.post0/src/jam/tests/fakers.py +187 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/config_maker.py +50 -4
- jamlib-3.2.0.post0/src/jam/utils/version_check.py +18 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0/src/jamlib.egg-info}/PKG-INFO +5 -5
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/SOURCES.txt +3 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/requires.txt +1 -1
- jamlib-3.2.0b2/src/jam/exceptions/jose.py +0 -23
- jamlib-3.2.0b2/src/jam/jose/__init__.py +0 -55
- jamlib-3.2.0b2/src/jam/tests/fakers.py +0 -102
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/LICENSE.md +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/setup.cfg +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__base_encoder__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/__deprecated__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/jwt/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/github.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/google.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/oauth2/client.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/json.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/aio/sessions/redis.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/cli.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/keys.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/cli/commands/password.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/encoders.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/base.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/jwt.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/oauth2.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/paseto.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/plugins.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/exceptions/sessions.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/fastapi/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/flask/objects.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/middleware.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/litestar/objects.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/ext/starlette/objects.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/json.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/lists/memory.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jose/utils.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__algorithms__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/__types__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/json.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/lists/redis.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/module.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/jwt/utils.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/github.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/gitlab.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/google.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/builtin/yandex.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/oauth2/client.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/hotp.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/otp/totp.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/__base__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/paseto/utils.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/py.typed +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/json.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/sessions/redis.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/tests/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/__init__.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/aes.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/await_maybe.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/basic_auth.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/ed.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/otp_keys.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/rsa.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/salt_hash.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/symmetric.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/xchacha20poly1305.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jam/utils/xor.py +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/dependency_links.txt +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/src/jamlib.egg-info/entry_points.txt +0 -0
- {jamlib-3.2.0b2 → jamlib-3.2.0.post0}/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.2.
|
|
3
|
+
Version: 3.2.0.post0
|
|
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
|
|
@@ -30,7 +30,7 @@ Classifier: Framework :: FastAPI
|
|
|
30
30
|
Requires-Python: >=3.10
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
License-File: LICENSE.md
|
|
33
|
-
Requires-Dist: cryptography>=
|
|
33
|
+
Requires-Dist: cryptography>=48.0.0
|
|
34
34
|
Provides-Extra: cli
|
|
35
35
|
Requires-Dist: click>=8.3.1; extra == "cli"
|
|
36
36
|
Provides-Extra: redis
|
|
@@ -77,7 +77,7 @@ from jam import Jam
|
|
|
77
77
|
|
|
78
78
|
jam = Jam(config="config.toml")
|
|
79
79
|
|
|
80
|
-
jwt = jam.
|
|
80
|
+
jwt = jam.jwt_encode(payload={"user": 1})
|
|
81
81
|
session_id = jam.session_create(session_key="username", data={"user": 1})
|
|
82
82
|
otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
|
|
83
83
|
```
|
|
@@ -85,7 +85,7 @@ otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
|
|
|
85
85
|
## Why Jam?
|
|
86
86
|
Jam is a library that provides the most popular AUTH* mechanisms right out of the box.
|
|
87
87
|
|
|
88
|
-
* [
|
|
88
|
+
* [JOSE](https://jam.makridenko.ru/usage/jose/)
|
|
89
89
|
* [PASETO](https://jam.makridenko.ru/usage/paseto/)
|
|
90
90
|
* [Server side sessions](https://jam.makridenko.ru/usage/sessions/)
|
|
91
91
|
* [OTP](https://jam.makridenko.ru/usage/otp/)
|
|
@@ -109,7 +109,7 @@ Here is a comparison with other libraries:
|
|
|
109
109
|
|
|
110
110
|
| Features / Library | **Jam** | [Authx](https://authx.yezz.me/) | [PyJWT](https://pyjwt.readthedocs.io) | [AuthLib](https://docs.authlib.org) | [OTP Auth](https://otp.authlib.org/) |
|
|
111
111
|
|-----------------------|--------|----------------------------------|---------------------------------------|-------------------------------------|--------------------------------------|
|
|
112
|
-
|
|
|
112
|
+
| JOSE | ✅ | ❌ only JWT | ❌ only JWT | ✅ | ❌ |
|
|
113
113
|
| JWT black/white lists | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
114
114
|
| PASETO | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
115
115
|
| Server side sessions | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
@@ -24,7 +24,7 @@ from jam import Jam
|
|
|
24
24
|
|
|
25
25
|
jam = Jam(config="config.toml")
|
|
26
26
|
|
|
27
|
-
jwt = jam.
|
|
27
|
+
jwt = jam.jwt_encode(payload={"user": 1})
|
|
28
28
|
session_id = jam.session_create(session_key="username", data={"user": 1})
|
|
29
29
|
otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
|
|
30
30
|
```
|
|
@@ -32,7 +32,7 @@ otp_code = jam.otp_code(secret="3DB7FOAOFBCI3WFDRE7EPF43CA")
|
|
|
32
32
|
## Why Jam?
|
|
33
33
|
Jam is a library that provides the most popular AUTH* mechanisms right out of the box.
|
|
34
34
|
|
|
35
|
-
* [
|
|
35
|
+
* [JOSE](https://jam.makridenko.ru/usage/jose/)
|
|
36
36
|
* [PASETO](https://jam.makridenko.ru/usage/paseto/)
|
|
37
37
|
* [Server side sessions](https://jam.makridenko.ru/usage/sessions/)
|
|
38
38
|
* [OTP](https://jam.makridenko.ru/usage/otp/)
|
|
@@ -56,7 +56,7 @@ Here is a comparison with other libraries:
|
|
|
56
56
|
|
|
57
57
|
| Features / Library | **Jam** | [Authx](https://authx.yezz.me/) | [PyJWT](https://pyjwt.readthedocs.io) | [AuthLib](https://docs.authlib.org) | [OTP Auth](https://otp.authlib.org/) |
|
|
58
58
|
|-----------------------|--------|----------------------------------|---------------------------------------|-------------------------------------|--------------------------------------|
|
|
59
|
-
|
|
|
59
|
+
| JOSE | ✅ | ❌ only JWT | ❌ only JWT | ✅ | ❌ |
|
|
60
60
|
| JWT black/white lists | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
61
61
|
| PASETO | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
62
62
|
| Server side sessions | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "jamlib"
|
|
3
|
-
version = "3.2.
|
|
3
|
+
version = "3.2.0.post0"
|
|
4
4
|
description = "Simple and universal library for authorization."
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
|
|
@@ -23,7 +23,7 @@ keywords = [
|
|
|
23
23
|
]
|
|
24
24
|
requires-python = ">=3.10"
|
|
25
25
|
dependencies = [
|
|
26
|
-
"cryptography>=
|
|
26
|
+
"cryptography>=48.0.0",
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
classifiers = [
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
from abc import ABC, abstractmethod
|
|
4
6
|
import gc
|
|
7
|
+
import os
|
|
5
8
|
from typing import Any, Literal
|
|
6
9
|
|
|
7
|
-
from jam.
|
|
10
|
+
from jam.__base_encoder__ import BaseEncoder
|
|
11
|
+
from jam.encoders import JsonEncoder
|
|
8
12
|
from jam.exceptions import JamConfigurationError
|
|
9
|
-
from jam.jose.__base__ import BaseJWT
|
|
13
|
+
from jam.jose.__base__ import BaseJWE, BaseJWS, BaseJWT
|
|
10
14
|
from jam.logger import BaseLogger, JamLogger
|
|
11
15
|
from jam.oauth2.__base__ import BaseOAuth2Client
|
|
12
16
|
from jam.otp.__base__ import BaseOTP, OTPConfig
|
|
13
17
|
from jam.paseto.__base__ import BasePASETO
|
|
18
|
+
from jam.plugins.__base__ import BasePlugin
|
|
14
19
|
from jam.sessions.__base__ import BaseSessionModule
|
|
15
20
|
from jam.utils.config_maker import __config_maker__, __module_loader__
|
|
16
21
|
|
|
@@ -18,7 +23,7 @@ from jam.utils.config_maker import __config_maker__, __module_loader__
|
|
|
18
23
|
class BaseJam(ABC):
|
|
19
24
|
"""Base jam instance."""
|
|
20
25
|
|
|
21
|
-
MODULES: dict[str, str] = {}
|
|
26
|
+
MODULES: dict[str, str | dict[str, str]] = {}
|
|
22
27
|
|
|
23
28
|
def __init__(
|
|
24
29
|
self,
|
|
@@ -30,15 +35,17 @@ class BaseJam(ABC):
|
|
|
30
35
|
"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
31
36
|
] = "INFO",
|
|
32
37
|
serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
|
|
38
|
+
plugins: list[type[BasePlugin]] = [],
|
|
33
39
|
) -> None:
|
|
34
40
|
"""Initialize instance.
|
|
35
41
|
|
|
36
42
|
Args:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
config (Union[str, dict[str, Any]]): Configuration
|
|
44
|
+
pointer (str): Pointer
|
|
45
|
+
logger (BaseLogger): Logger
|
|
46
|
+
log_level (Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]): Log level
|
|
47
|
+
serializer (Union[BaseEncoder, type[BaseBrowser]]): Serializer
|
|
48
|
+
plugins (list[type[BasePlugin]]): List of plugins
|
|
42
49
|
|
|
43
50
|
Returns:
|
|
44
51
|
None
|
|
@@ -54,7 +61,12 @@ class BaseJam(ABC):
|
|
|
54
61
|
|
|
55
62
|
self._logger = logger(log_level)
|
|
56
63
|
self._serializer = serializer
|
|
64
|
+
self._plugins = []
|
|
65
|
+
|
|
57
66
|
self.jwt: BaseJWT | None = None
|
|
67
|
+
self.jws: BaseJWS | None = None
|
|
68
|
+
self.jwe: BaseJWE | None = None
|
|
69
|
+
self.jose: dict[str, Any] | None = None
|
|
58
70
|
self.session: BaseSessionModule | None = None
|
|
59
71
|
self.oauth2: dict[str, BaseOAuth2Client] | None = None
|
|
60
72
|
self.otp: OTPConfig | None = None
|
|
@@ -69,8 +81,11 @@ class BaseJam(ABC):
|
|
|
69
81
|
)
|
|
70
82
|
self._logger.debug(
|
|
71
83
|
"BaseJam initialization complete. Modules loaded:\n"
|
|
72
|
-
f" jwt={self.jwt is not None}, session={self.session is not None}, oauth2={self.oauth2 is not None}"
|
|
84
|
+
f" jwt={self.jwt is not None}, jose={self.jose is not None}, session={self.session is not None}, oauth2={self.oauth2 is not None}"
|
|
73
85
|
)
|
|
86
|
+
if os.getenv("JAM_ENABLE_PLUGINS", "0") == "1":
|
|
87
|
+
self._logger.warning("Experimental plugins are enabled!")
|
|
88
|
+
self.__setup_plugins(plugins)
|
|
74
89
|
gc.collect()
|
|
75
90
|
|
|
76
91
|
def __build_main_config(
|
|
@@ -139,6 +154,7 @@ class BaseJam(ABC):
|
|
|
139
154
|
"""Build instance.
|
|
140
155
|
|
|
141
156
|
Load modules from configuration and initialize them.
|
|
157
|
+
Supports both flat modules (name -> path) and nested modules (name -> {subname -> path}).
|
|
142
158
|
|
|
143
159
|
Args:
|
|
144
160
|
config (dict[str, Any]): Configuration
|
|
@@ -151,24 +167,68 @@ class BaseJam(ABC):
|
|
|
151
167
|
self._logger.debug(f"Missing configuration for module {name}")
|
|
152
168
|
continue
|
|
153
169
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
if isinstance(path, dict):
|
|
171
|
+
subconfig = config.get(name, {})
|
|
172
|
+
if not isinstance(subconfig, dict):
|
|
173
|
+
subconfig = {}
|
|
174
|
+
|
|
175
|
+
for subname, subpath in path.items():
|
|
176
|
+
if subname not in subconfig:
|
|
177
|
+
self._logger.debug(
|
|
178
|
+
f"Missing configuration for module {name}.{subname}"
|
|
179
|
+
)
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
module_cls = __module_loader__(subpath)
|
|
184
|
+
self._logger.debug(
|
|
185
|
+
f"Loading module {name}.{subname} from {subpath}"
|
|
186
|
+
)
|
|
187
|
+
params = subconfig.get(subname, {})
|
|
188
|
+
self._logger.debug(
|
|
189
|
+
f"Module {name}.{subname} config params: {list(params.keys())}"
|
|
190
|
+
)
|
|
191
|
+
module_instance = module_cls(**params)
|
|
192
|
+
|
|
193
|
+
if self.jose is None:
|
|
194
|
+
self.jose = {}
|
|
195
|
+
self.jose[subname] = module_instance
|
|
196
|
+
|
|
197
|
+
if subname == "jwt":
|
|
198
|
+
self.jwt = module_instance
|
|
199
|
+
elif subname == "jws":
|
|
200
|
+
self.jws = module_instance
|
|
201
|
+
elif subname == "jwe":
|
|
202
|
+
self.jwe = module_instance
|
|
203
|
+
|
|
204
|
+
self._logger.debug(
|
|
205
|
+
f"Module {name}.{subname} initialized successfully"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
self._logger.error(
|
|
210
|
+
f"Failed to load module {name}.{subname} from {subpath}: {e}",
|
|
211
|
+
exc_info=True,
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
try:
|
|
215
|
+
module_cls = __module_loader__(path)
|
|
216
|
+
self._logger.debug(f"Loading module {name} from {path}")
|
|
217
|
+
params = config.get(name, {})
|
|
218
|
+
self._logger.debug(
|
|
219
|
+
f"Module {name} config params: {list(params.keys())}"
|
|
220
|
+
)
|
|
221
|
+
module_instance = module_cls(**params)
|
|
222
|
+
self.__setattr__(name, module_instance)
|
|
223
|
+
self._logger.debug(
|
|
224
|
+
f"Module {name} initialized successfully"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
self._logger.error(
|
|
229
|
+
f"Failed to load module {name} from {path}: {e}",
|
|
230
|
+
exc_info=True,
|
|
231
|
+
)
|
|
172
232
|
|
|
173
233
|
def __otp(
|
|
174
234
|
self, type: Literal["totp", "hotp"] | None = None
|
|
@@ -188,6 +248,40 @@ class BaseJam(ABC):
|
|
|
188
248
|
case _:
|
|
189
249
|
raise JamConfigurationError(message="Unknown OTP type.")
|
|
190
250
|
|
|
251
|
+
def __setup_plugins(self, plugins: list[type[BasePlugin]]) -> None:
|
|
252
|
+
"""Setup plugins."""
|
|
253
|
+
for plugin in plugins:
|
|
254
|
+
self._logger.debug(f"Setup plugin: {plugin.name}")
|
|
255
|
+
from jam.utils.version_check import __is_compatible__
|
|
256
|
+
|
|
257
|
+
if not __is_compatible__(None, plugin.jam_requires):
|
|
258
|
+
continue
|
|
259
|
+
|
|
260
|
+
_plugin = plugin(self)
|
|
261
|
+
_plugin.setup()
|
|
262
|
+
self._plugins.append(_plugin)
|
|
263
|
+
|
|
264
|
+
def emit(self, event: str, **kwargs) -> Any:
|
|
265
|
+
"""Emit event.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
event (str): Event name,
|
|
269
|
+
**kwargs: Event data
|
|
270
|
+
"""
|
|
271
|
+
for plugin in self._plugins:
|
|
272
|
+
handler = getattr(plugin, f"on_{event}", None)
|
|
273
|
+
|
|
274
|
+
if handler:
|
|
275
|
+
try:
|
|
276
|
+
result = handler(**kwargs)
|
|
277
|
+
if isinstance(result, dict):
|
|
278
|
+
kwargs.update(result)
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
self._logger.error(f"Plugin:{plugin.name} | error: {e}")
|
|
282
|
+
|
|
283
|
+
return kwargs
|
|
284
|
+
|
|
191
285
|
@abstractmethod
|
|
192
286
|
def jwt_encode(
|
|
193
287
|
self,
|
|
@@ -196,6 +290,7 @@ class BaseJam(ABC):
|
|
|
196
290
|
aud: str | None = None,
|
|
197
291
|
exp: int | None = None,
|
|
198
292
|
nbf: int | None = None,
|
|
293
|
+
jti: str | None = None,
|
|
199
294
|
*,
|
|
200
295
|
payload: dict[str, Any] | None = None,
|
|
201
296
|
header: dict[str, Any] | None = None,
|
|
@@ -208,6 +303,7 @@ class BaseJam(ABC):
|
|
|
208
303
|
iss (str | None): The issuer.
|
|
209
304
|
sub (str | None): The subject.
|
|
210
305
|
aud (str | None): The audience.
|
|
306
|
+
jti (str | None): The JWT ID. If none use the JTI fabric function.
|
|
211
307
|
header (dict[str, Any] | None): The header to include in the JWT.
|
|
212
308
|
payload (dict[str, Any] | None): The payload to include in the JWT.
|
|
213
309
|
|
|
@@ -240,6 +336,70 @@ class BaseJam(ABC):
|
|
|
240
336
|
"""
|
|
241
337
|
raise NotImplementedError
|
|
242
338
|
|
|
339
|
+
@abstractmethod
|
|
340
|
+
def jws_sign(
|
|
341
|
+
self,
|
|
342
|
+
data: dict[str, Any] | str,
|
|
343
|
+
header: dict[str, Any] | None = None,
|
|
344
|
+
) -> str:
|
|
345
|
+
"""Sign data using JWS.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
data: Data to sign. If dict, will be JSON encoded.
|
|
349
|
+
header: JWS header.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
str: JWS token.
|
|
353
|
+
"""
|
|
354
|
+
raise NotImplementedError
|
|
355
|
+
|
|
356
|
+
@abstractmethod
|
|
357
|
+
def jws_verify(self, token: str) -> dict[str, Any]:
|
|
358
|
+
"""Verify JWS token.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
token: JWS token.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
dict[str, Any]: Decoded payload.
|
|
365
|
+
|
|
366
|
+
Raises:
|
|
367
|
+
JamJWSVerificationError: If verification fails.
|
|
368
|
+
"""
|
|
369
|
+
raise NotImplementedError
|
|
370
|
+
|
|
371
|
+
@abstractmethod
|
|
372
|
+
def jwe_encrypt(
|
|
373
|
+
self,
|
|
374
|
+
data: dict[str, Any] | str,
|
|
375
|
+
header: dict[str, Any] | None = None,
|
|
376
|
+
) -> str:
|
|
377
|
+
"""Encrypt data using JWE.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
data: Data to encrypt. If dict, will be JSON encoded.
|
|
381
|
+
header: JWE header.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
str: JWE token.
|
|
385
|
+
"""
|
|
386
|
+
raise NotImplementedError
|
|
387
|
+
|
|
388
|
+
@abstractmethod
|
|
389
|
+
def jwe_decrypt(self, token: str) -> bytes:
|
|
390
|
+
"""Decrypt JWE token.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
token: JWE token.
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
bytes: Decrypted data.
|
|
397
|
+
|
|
398
|
+
Raises:
|
|
399
|
+
JamJWEDecryptionError: If decryption fails.
|
|
400
|
+
"""
|
|
401
|
+
raise NotImplementedError
|
|
402
|
+
|
|
243
403
|
@abstractmethod
|
|
244
404
|
def session_create(self, session_key: str, data: dict[str, Any]) -> str:
|
|
245
405
|
"""Create new session.
|
|
@@ -7,10 +7,8 @@ Documentation: https://jam.makridenko.ru
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from jam.__base__ import BaseJam
|
|
10
|
-
from jam.__base_encoder__ import BaseEncoder
|
|
11
|
-
from jam.encoders import JsonEncoder
|
|
12
10
|
from jam.instance import Jam
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
__version__ = "3.2.
|
|
16
|
-
__all__ = ["Jam", "
|
|
13
|
+
__version__ = "3.2.0"
|
|
14
|
+
__all__ = ["Jam", "BaseJam"]
|
|
@@ -4,24 +4,35 @@ from abc import abstractmethod
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from jam.__base__ import BaseJam
|
|
7
|
+
from jam.__deprecated__ import deprecated
|
|
7
8
|
from jam.aio.oauth2.__base__ import BaseAsyncOAuth2Client
|
|
8
9
|
from jam.aio.sessions.__base__ import BaseAsyncSessionModule
|
|
10
|
+
from jam.jose.__base__ import BaseJWE, BaseJWS
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class BaseAsyncJam(BaseJam):
|
|
12
14
|
"""Base async jam instance."""
|
|
13
15
|
|
|
14
|
-
MODULES: dict[str, str] = {}
|
|
16
|
+
MODULES: dict[str, str | dict[str, str]] = {}
|
|
15
17
|
|
|
16
18
|
session: BaseAsyncSessionModule | None = None # type: ignore[override]
|
|
17
19
|
oauth2: dict[str, BaseAsyncOAuth2Client] | None = None # type: ignore[override]
|
|
20
|
+
jose: dict[str, Any] | None = None # type: ignore[override]
|
|
21
|
+
jws: BaseJWS | None = None # type: ignore[override]
|
|
22
|
+
jwe: BaseJWE | None = None # type: ignore[override]
|
|
18
23
|
|
|
19
24
|
@abstractmethod
|
|
25
|
+
@deprecated(
|
|
26
|
+
"This method is deprecated; the JWT payload is generated automatically in accordance with the specification."
|
|
27
|
+
)
|
|
20
28
|
async def jwt_make_payload( # type: ignore[override]
|
|
21
29
|
self, exp: int | None, data: dict[str, Any]
|
|
22
30
|
) -> dict[str, Any]:
|
|
23
31
|
"""Make JWT-specific payload.
|
|
24
32
|
|
|
33
|
+
!!! Deprecated
|
|
34
|
+
This method is deprecated; the JWT payload is generated automatically in accordance with the specification.
|
|
35
|
+
|
|
25
36
|
Args:
|
|
26
37
|
exp (int | None): Token expire, if None -> use default
|
|
27
38
|
data (dict[str, Any]): Data to payload
|
|
@@ -32,6 +43,7 @@ class BaseAsyncJam(BaseJam):
|
|
|
32
43
|
raise NotImplementedError
|
|
33
44
|
|
|
34
45
|
@abstractmethod
|
|
46
|
+
@deprecated("Use jam.jwt_encode")
|
|
35
47
|
async def jwt_create( # type: ignore[override]
|
|
36
48
|
self, payload: dict[str, Any]
|
|
37
49
|
) -> str:
|
|
@@ -46,13 +58,14 @@ class BaseAsyncJam(BaseJam):
|
|
|
46
58
|
raise NotImplementedError
|
|
47
59
|
|
|
48
60
|
@abstractmethod
|
|
49
|
-
async def jwt_encode(
|
|
61
|
+
async def jwt_encode( # type: ignore[override]
|
|
50
62
|
self,
|
|
51
63
|
iss: str | None = None,
|
|
52
64
|
sub: str | None = None,
|
|
53
65
|
aud: str | None = None,
|
|
54
66
|
exp: int | None = None,
|
|
55
67
|
nbf: int | None = None,
|
|
68
|
+
jti: str | None = None,
|
|
56
69
|
*,
|
|
57
70
|
payload: dict[str, Any] | None = None,
|
|
58
71
|
header: dict[str, Any] | None = None,
|
|
@@ -65,6 +78,7 @@ class BaseAsyncJam(BaseJam):
|
|
|
65
78
|
iss (str | None): The issuer.
|
|
66
79
|
sub (str | None): The subject.
|
|
67
80
|
aud (str | None): The audience.
|
|
81
|
+
jti (str | None): The JWT ID. If none use the JTI fabric function.
|
|
68
82
|
header (dict[str, Any] | None): The header to include in the JWT.
|
|
69
83
|
payload (dict[str, Any] | None): The payload to include in the JWT.
|
|
70
84
|
|
|
@@ -340,6 +354,74 @@ class BaseAsyncJam(BaseJam):
|
|
|
340
354
|
"""
|
|
341
355
|
raise NotImplementedError
|
|
342
356
|
|
|
357
|
+
@abstractmethod
|
|
358
|
+
async def jws_sign( # type: ignore[override]
|
|
359
|
+
self,
|
|
360
|
+
data: dict[str, Any] | str,
|
|
361
|
+
header: dict[str, Any] | None = None,
|
|
362
|
+
) -> str:
|
|
363
|
+
"""Sign data using JWS.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
data: Data to sign. If dict, will be JSON encoded.
|
|
367
|
+
header: JWS header.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
str: JWS token.
|
|
371
|
+
"""
|
|
372
|
+
raise NotImplementedError
|
|
373
|
+
|
|
374
|
+
@abstractmethod
|
|
375
|
+
async def jws_verify( # type: ignore[override]
|
|
376
|
+
self, token: str
|
|
377
|
+
) -> dict[str, Any]:
|
|
378
|
+
"""Verify JWS token.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
token: JWS token.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
dict[str, Any]: Decoded payload.
|
|
385
|
+
|
|
386
|
+
Raises:
|
|
387
|
+
JamJWSVerificationError: If verification fails.
|
|
388
|
+
"""
|
|
389
|
+
raise NotImplementedError
|
|
390
|
+
|
|
391
|
+
@abstractmethod
|
|
392
|
+
async def jwe_encrypt( # type: ignore[override]
|
|
393
|
+
self,
|
|
394
|
+
data: dict[str, Any] | str,
|
|
395
|
+
header: dict[str, Any] | None = None,
|
|
396
|
+
) -> str:
|
|
397
|
+
"""Encrypt data using JWE.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
data: Data to encrypt. If dict, will be JSON encoded.
|
|
401
|
+
header: JWE header.
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
str: JWE token.
|
|
405
|
+
"""
|
|
406
|
+
raise NotImplementedError
|
|
407
|
+
|
|
408
|
+
@abstractmethod
|
|
409
|
+
async def jwe_decrypt( # type: ignore[override]
|
|
410
|
+
self, token: str
|
|
411
|
+
) -> bytes:
|
|
412
|
+
"""Decrypt JWE token.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
token: JWE token.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
bytes: Decrypted data.
|
|
419
|
+
|
|
420
|
+
Raises:
|
|
421
|
+
JamJWEDecryptionError: If decryption fails.
|
|
422
|
+
"""
|
|
423
|
+
raise NotImplementedError
|
|
424
|
+
|
|
343
425
|
@abstractmethod
|
|
344
426
|
async def paseto_decode( # type: ignore[override]
|
|
345
427
|
self, token: str, check_exp: bool = True, check_list: bool = True
|