jamlib 0.0.0__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-0.0.0/LICENSE.md +23 -0
- jamlib-0.0.0/PKG-INFO +64 -0
- jamlib-0.0.0/README.md +42 -0
- jamlib-0.0.0/jam/__abc_instances__.py +25 -0
- jamlib-0.0.0/jam/__init__.py +14 -0
- jamlib-0.0.0/jam/exceptions/__init__.py +15 -0
- jamlib-0.0.0/jam/exceptions/jwt.py +38 -0
- jamlib-0.0.0/jam/instance.py +81 -0
- jamlib-0.0.0/jam/jwt/__init__.py +1 -0
- jamlib-0.0.0/jam/jwt/__utils__.py +32 -0
- jamlib-0.0.0/jam/jwt/lists/__abc_list_repo__.py +45 -0
- jamlib-0.0.0/jam/jwt/lists/__init__.py +7 -0
- jamlib-0.0.0/jam/jwt/lists/list_manipulations.py +151 -0
- jamlib-0.0.0/jam/jwt/tools.py +168 -0
- jamlib-0.0.0/jam/modules.py +159 -0
- jamlib-0.0.0/jam/utils/__init__.py +4 -0
- jamlib-0.0.0/jam/utils/config_maker.py +58 -0
- jamlib-0.0.0/jam/utils/rsa.py +44 -0
- jamlib-0.0.0/jamlib.egg-info/PKG-INFO +64 -0
- jamlib-0.0.0/jamlib.egg-info/SOURCES.txt +23 -0
- jamlib-0.0.0/jamlib.egg-info/dependency_links.txt +1 -0
- jamlib-0.0.0/jamlib.egg-info/requires.txt +8 -0
- jamlib-0.0.0/jamlib.egg-info/top_level.txt +1 -0
- jamlib-0.0.0/pyproject.toml +86 -0
- jamlib-0.0.0/setup.cfg +4 -0
jamlib-0.0.0/LICENSE.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Adrian Makridenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
|
jamlib-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jamlib
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Simple and univirsal library for authorization.
|
|
5
|
+
Author-email: Makridenko Adrian <adrianmakridenko@duck.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Project-URL: Homepage, https://github.com/lyaguxafrog/jam
|
|
8
|
+
Project-URL: Repository, https://github.com/lyaguxafrog/jam
|
|
9
|
+
Project-URL: Issues, https://github.com/lyaguxafrog/jam/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/lyaguxafrog/jam/releases
|
|
11
|
+
Keywords: auth,backend,jwt
|
|
12
|
+
Requires-Python: >=3.13
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE.md
|
|
15
|
+
Requires-Dist: pycryptodome<4.0.0,>=3.21.0
|
|
16
|
+
Requires-Dist: cryptography<45.0.0,>=44.0.2
|
|
17
|
+
Provides-Extra: json-lists
|
|
18
|
+
Requires-Dist: tinydb>=4.8.2; extra == "json-lists"
|
|
19
|
+
Provides-Extra: redis-lists
|
|
20
|
+
Requires-Dist: redis>=5.2.1; extra == "redis-lists"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Jam
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
```bash
|
|
34
|
+
pip install jamlib
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Getting start
|
|
38
|
+
```python
|
|
39
|
+
# -*- coding: utf-8 -*-
|
|
40
|
+
|
|
41
|
+
from typing import Any
|
|
42
|
+
|
|
43
|
+
from jam import Jam
|
|
44
|
+
|
|
45
|
+
config: dict[str, Any] = {
|
|
46
|
+
"jwt_secret_key": "some-secret",
|
|
47
|
+
"expire": 3600
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"user_id": 1,
|
|
52
|
+
"role": "admin"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
jam = Jam(auth_type="jwt", config=config)
|
|
56
|
+
|
|
57
|
+
payload = jam.make_payload(**data)
|
|
58
|
+
token = jam.gen_jwt_token(**payload)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Roadmap
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
© [Adrian Makridenko](https://github.com/lyaguxafrog) 2025
|
jamlib-0.0.0/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Jam
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
```bash
|
|
12
|
+
pip install jamlib
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Getting start
|
|
16
|
+
```python
|
|
17
|
+
# -*- coding: utf-8 -*-
|
|
18
|
+
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from jam import Jam
|
|
22
|
+
|
|
23
|
+
config: dict[str, Any] = {
|
|
24
|
+
"jwt_secret_key": "some-secret",
|
|
25
|
+
"expire": 3600
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
data = {
|
|
29
|
+
"user_id": 1,
|
|
30
|
+
"role": "admin"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
jam = Jam(auth_type="jwt", config=config)
|
|
34
|
+
|
|
35
|
+
payload = jam.make_payload(**data)
|
|
36
|
+
token = jam.gen_jwt_token(**payload)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Roadmap
|
|
40
|
+

|
|
41
|
+
|
|
42
|
+
© [Adrian Makridenko](https://github.com/lyaguxafrog) 2025
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class __AbstractInstance(ABC):
|
|
8
|
+
"""Abstract Instance object."""
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def gen_jwt_token(self, payload) -> str:
|
|
12
|
+
"""Generate new JWT token."""
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def verify_jwt_token(
|
|
17
|
+
self, token: str, check_exp: bool, check_list: bool
|
|
18
|
+
) -> Any:
|
|
19
|
+
"""Verify JWT token."""
|
|
20
|
+
raise NotImplementedError
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def make_payload(self, **payload) -> Any:
|
|
24
|
+
"""Generate new template."""
|
|
25
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class EmptySecretKey(Exception):
|
|
8
|
+
message: str | Exception = "Secret key cannot be NoneType"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class EmtpyPrivateKey(Exception):
|
|
13
|
+
message: str | Exception = "Private key cannot be NoneType"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class EmptyPublicKey(Exception):
|
|
18
|
+
message: str | Exception = "Public key cannot be NoneType"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class TokenLifeTimeExpired(Exception):
|
|
23
|
+
message: str | Exception = "Token lifetime has expired."
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class NotFoundSomeInPayload(Exception):
|
|
27
|
+
def __inti__(self, message: str | Exception) -> None:
|
|
28
|
+
self.message: str | Exception = message
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class TokenNotInWhiteList(Exception):
|
|
33
|
+
message: str | Exception = "Token not found on white list."
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class TokenInBlackList(Exception):
|
|
38
|
+
message: str | Exception = "The token is blacklisted."
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from jam.__abc_instances__ import __AbstractInstance
|
|
6
|
+
from jam.modules import JWTModule
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Jam(__AbstractInstance):
|
|
10
|
+
"""Main instance."""
|
|
11
|
+
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
auth_type: Literal["jwt"],
|
|
15
|
+
config: dict[str, Any],
|
|
16
|
+
) -> None:
|
|
17
|
+
"""Class construcotr.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
auth_type (Literal["jwt"]): Type of auth*
|
|
21
|
+
config (dict[str, Any]): Config for Jam, can use `jam.utils.config_maker`
|
|
22
|
+
"""
|
|
23
|
+
self.type = auth_type
|
|
24
|
+
if self.type == "jwt":
|
|
25
|
+
self.module = JWTModule(
|
|
26
|
+
alg=config["alg"],
|
|
27
|
+
secret_key=config["secret_key"],
|
|
28
|
+
public_key=config["public_key"],
|
|
29
|
+
private_key=config["private_key"],
|
|
30
|
+
expire=config["expire"],
|
|
31
|
+
list=config["list"],
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
raise NotImplementedError
|
|
35
|
+
|
|
36
|
+
def gen_jwt_token(self, payload: dict[str, Any]) -> str:
|
|
37
|
+
"""Creating a new token.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
payload (dict[str, Any]): Payload with information
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None
|
|
44
|
+
EmtpyPrivateKey: If RSA algorithm is selected, but private key None
|
|
45
|
+
"""
|
|
46
|
+
return self.module.gen_token(**payload)
|
|
47
|
+
|
|
48
|
+
def verify_jwt_token(
|
|
49
|
+
self, token: str, check_exp: bool = True, check_list: bool = True
|
|
50
|
+
) -> dict[str, Any]:
|
|
51
|
+
"""A method for verifying a token.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
token (str): The token to check
|
|
55
|
+
check_exp (bool): Check for expiration?
|
|
56
|
+
check_list (bool): Check if there is a black/white list
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
ValueError: If the token is invalid.
|
|
60
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None.
|
|
61
|
+
EmtpyPublicKey: If RSA algorithm is selected, but public key None.
|
|
62
|
+
NotFoundSomeInPayload: If 'exp' not found in payload.
|
|
63
|
+
TokenLifeTimeExpired: If token has expired.
|
|
64
|
+
TokenNotInWhiteList: If the list type is white, but the token is not there
|
|
65
|
+
TokenInBlackList: If the list type is black and the token is there
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
(dict[str, Any]): Payload from token
|
|
69
|
+
"""
|
|
70
|
+
return self.module.validate_payload(
|
|
71
|
+
token=token, check_exp=check_exp, check_list=check_list
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def make_payload(self, exp: int | None = None, **data) -> dict[str, Any]:
|
|
75
|
+
"""Payload maker tool.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
exp (int | None): If none exp = JWTModule.exp
|
|
79
|
+
**data: Custom data
|
|
80
|
+
"""
|
|
81
|
+
return self.module.make_payload(exp=exp, **data)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def __base64url_encode__(data: bytes) -> str:
|
|
7
|
+
"""Encodes data using URL-safe Base64 encoding.
|
|
8
|
+
|
|
9
|
+
Removes padding characters ('=') typically added in standard Base64 encoding.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
data (bytes): The data to encode.
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
str: A URL-safe Base64 encoded string without padding.
|
|
16
|
+
"""
|
|
17
|
+
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("utf-8")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def __base64url_decode__(data: str) -> bytes:
|
|
21
|
+
"""Decodes a URL-safe Base64 encoded string back to bytes.
|
|
22
|
+
|
|
23
|
+
Automatically adds the necessary padding characters ('=') before decoding.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
data (str): The Base64url encoded string to decode.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
bytes: The decoded byte data.
|
|
30
|
+
"""
|
|
31
|
+
padding = "=" * (4 - len(data) % 4)
|
|
32
|
+
return base64.urlsafe_b64decode(data + padding)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Literal, Protocol
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ABCList(ABC):
|
|
8
|
+
"""Abstrac class for lists manipulation."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, list_type: Literal["white", "black"]) -> None:
|
|
11
|
+
"""Class constructor."""
|
|
12
|
+
self.__list_type__ = list_type
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def add(self, token: str) -> None:
|
|
16
|
+
"""Method for adding token to list."""
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
|
|
19
|
+
@abstractmethod
|
|
20
|
+
def check(self, token: str) -> bool:
|
|
21
|
+
"""Method for checking if a token is present in the list."""
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def delete(self, token: str) -> None:
|
|
26
|
+
"""Method for removing a token from a list."""
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JWTList(Protocol):
|
|
31
|
+
"""Protocol class for lists."""
|
|
32
|
+
|
|
33
|
+
__list_type__: Literal["black", "white"]
|
|
34
|
+
|
|
35
|
+
def add(self, token: str) -> None:
|
|
36
|
+
"""Method for adding token to list."""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def check(self, token: str) -> bool:
|
|
40
|
+
"""Method for checking if a token is present in the list."""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def delete(self, token: str) -> None:
|
|
44
|
+
"""Method for removing a token from a list."""
|
|
45
|
+
pass
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
from redis import Redis
|
|
7
|
+
from tinydb import Query, TinyDB
|
|
8
|
+
|
|
9
|
+
from jam.jwt.lists.__abc_list_repo__ import ABCList
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class JSONList(ABCList):
|
|
13
|
+
"""Black/White list in JSON format, not recommended for blacklists because it is not convenient to control token lifetime.
|
|
14
|
+
|
|
15
|
+
Dependency required:
|
|
16
|
+
`pip install jamlib[json-lists]`
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
__list__ (TinyDB): TinyDB instance
|
|
20
|
+
|
|
21
|
+
Methods:
|
|
22
|
+
add: adding token to list
|
|
23
|
+
check: check token in list
|
|
24
|
+
delete: removing token from list
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self, type: Literal["white", "black"], json_path: str = "whitelist.json"
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Class constructor.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
type (Literal["white", "black"]): Type of list
|
|
34
|
+
json_path (str): Path to .json file
|
|
35
|
+
"""
|
|
36
|
+
super().__init__(list_type=type)
|
|
37
|
+
self.__list__ = TinyDB(json_path)
|
|
38
|
+
|
|
39
|
+
def add(self, token: str) -> None:
|
|
40
|
+
"""Method for adding token to list.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
token (str): Your JWT token
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
(None)
|
|
47
|
+
"""
|
|
48
|
+
_doc = {
|
|
49
|
+
"token": token,
|
|
50
|
+
"timestamp": datetime.datetime.now().timestamp(),
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
from icecream import ic
|
|
54
|
+
|
|
55
|
+
ic(self.__list__.insert(_doc))
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
def check(self, token: str) -> bool:
|
|
59
|
+
"""Method for checking if a token is present in list.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
token (str): Your jwt token
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
(bool)
|
|
66
|
+
"""
|
|
67
|
+
cond = Query()
|
|
68
|
+
_token = self.__list__.search(cond.token == token)
|
|
69
|
+
if _token:
|
|
70
|
+
return True
|
|
71
|
+
else:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
def delete(self, token: str) -> None:
|
|
75
|
+
"""Method for removing token from list.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
token (str): Your jwt token
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
(None)
|
|
82
|
+
"""
|
|
83
|
+
cond = Query()
|
|
84
|
+
self.__list__.remove(cond.token == token)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class RedisList(ABCList):
|
|
88
|
+
"""Black/White lists in Redis, most optimal format.
|
|
89
|
+
|
|
90
|
+
Dependency required: `pip install jamlib[redis-lists]`
|
|
91
|
+
|
|
92
|
+
Attributes:
|
|
93
|
+
__list__ (Redis): Redis instance
|
|
94
|
+
exp (int | None): Token lifetime
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
type: Literal["white", "black"],
|
|
100
|
+
redis_instance: Redis,
|
|
101
|
+
in_list_life_time: int | None,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Class constructor.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
type (Literal["white", "black"]): Type og list
|
|
107
|
+
redis_instance (Redis): `redis.Redis`
|
|
108
|
+
in_list_life_time (int | None): The lifetime of a token in the list
|
|
109
|
+
"""
|
|
110
|
+
super().__init__(list_type=type)
|
|
111
|
+
self.__list__ = redis_instance
|
|
112
|
+
self.exp = in_list_life_time
|
|
113
|
+
|
|
114
|
+
def add(self, token: str) -> None:
|
|
115
|
+
"""Method for adding token to list.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
token (str): Your JWT token
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
(None)
|
|
122
|
+
"""
|
|
123
|
+
self.__list__.set(name=token, value="", ex=self.exp)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
def check(self, token: str) -> bool:
|
|
127
|
+
"""Method for checking if a token is present in the list.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
token (str): Your JWT token
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
(bool)
|
|
134
|
+
"""
|
|
135
|
+
_token = self.__list__.get(name=token)
|
|
136
|
+
if not _token:
|
|
137
|
+
return False
|
|
138
|
+
else:
|
|
139
|
+
return True
|
|
140
|
+
|
|
141
|
+
def delete(self, token: str) -> None:
|
|
142
|
+
"""Method for removing a token from a list.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
token (str): Your JWT token
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
None
|
|
149
|
+
"""
|
|
150
|
+
self.__list__.delete(token)
|
|
151
|
+
return None
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import json
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
from uuid import uuid4
|
|
9
|
+
|
|
10
|
+
from Crypto.Hash import SHA256
|
|
11
|
+
from Crypto.PublicKey import RSA
|
|
12
|
+
from Crypto.Signature import pkcs1_15
|
|
13
|
+
|
|
14
|
+
from jam.exceptions import (
|
|
15
|
+
EmptyPublicKey,
|
|
16
|
+
EmptySecretKey,
|
|
17
|
+
EmtpyPrivateKey,
|
|
18
|
+
NotFoundSomeInPayload,
|
|
19
|
+
TokenLifeTimeExpired,
|
|
20
|
+
)
|
|
21
|
+
from jam.jwt.__utils__ import __base64url_decode__, __base64url_encode__
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __gen_jwt__(
|
|
25
|
+
header: dict[str, Any],
|
|
26
|
+
payload: dict[str, Any],
|
|
27
|
+
secret: str | None = None,
|
|
28
|
+
private_key: str | None = None,
|
|
29
|
+
) -> str:
|
|
30
|
+
"""Method for generating JWT token with different algorithms.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
```python
|
|
34
|
+
token = __gen_jwt__(
|
|
35
|
+
header={
|
|
36
|
+
"alg": "HS256",
|
|
37
|
+
"type": "jwt"
|
|
38
|
+
},
|
|
39
|
+
payload={
|
|
40
|
+
"id": 1
|
|
41
|
+
},
|
|
42
|
+
secret="SUPER_SECRET"
|
|
43
|
+
)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
header (Dict[str, str]): Dict with JWT headers
|
|
48
|
+
payload (Dict[str, Any]): Custom JWT payload
|
|
49
|
+
secret (str | None): Secret key for HMAC algorithms
|
|
50
|
+
private_key (str | None): Private key for RSA algorithms
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None
|
|
54
|
+
EmtpyPrivateKey: If RSA algorithm is selected, but private key None
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
(str): Access/refresh token
|
|
58
|
+
"""
|
|
59
|
+
header_encoded = __base64url_encode__(json.dumps(header).encode("utf-8"))
|
|
60
|
+
payload_encoded = __base64url_encode__(json.dumps(payload).encode("utf-8"))
|
|
61
|
+
|
|
62
|
+
signature_input = f"{header_encoded}.{payload_encoded}".encode()
|
|
63
|
+
|
|
64
|
+
if header["alg"].startswith("HS"):
|
|
65
|
+
if secret is None:
|
|
66
|
+
raise EmptySecretKey
|
|
67
|
+
signature = hmac.new(
|
|
68
|
+
secret.encode("utf-8"), signature_input, hashlib.sha256
|
|
69
|
+
).digest()
|
|
70
|
+
elif header["alg"].startswith("RS"):
|
|
71
|
+
if private_key is None:
|
|
72
|
+
raise EmtpyPrivateKey
|
|
73
|
+
rsa_key = RSA.import_key(private_key)
|
|
74
|
+
hash_obj = SHA256.new(signature_input)
|
|
75
|
+
signature = pkcs1_15.new(rsa_key).sign(hash_obj)
|
|
76
|
+
else:
|
|
77
|
+
raise ValueError("Unsupported algorithm")
|
|
78
|
+
|
|
79
|
+
signature_encoded = __base64url_encode__(signature)
|
|
80
|
+
|
|
81
|
+
jwt_token = f"{header_encoded}.{payload_encoded}.{signature_encoded}"
|
|
82
|
+
return jwt_token
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def __validate_jwt__(
|
|
86
|
+
token: str,
|
|
87
|
+
check_exp: bool = False,
|
|
88
|
+
secret: str | None = None,
|
|
89
|
+
public_key: str | None = None,
|
|
90
|
+
) -> dict[str, Any]:
|
|
91
|
+
"""Validate a JWT token and return the payload if valid.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
token (str): The JWT token to validate.
|
|
95
|
+
check_exp (bool): true to check token lifetime.
|
|
96
|
+
secret (str | None): Secret key for HMAC algorithms.
|
|
97
|
+
public_key (str | None): Public key for RSA algorithms.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
(Dict[str, Any]): The payload if the token is valid.
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ValueError: If the token is invalid.
|
|
104
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None.
|
|
105
|
+
EmtpyPublicKey: If RSA algorithm is selected, but public key None.
|
|
106
|
+
NotFoundSomeInPayload: If 'exp' not found in payload.
|
|
107
|
+
TokenLifeTimeExpired: If token has expired.
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
header_encoded, payload_encoded, signature_encoded = token.split(".")
|
|
111
|
+
except ValueError:
|
|
112
|
+
raise ValueError("Invalid token format")
|
|
113
|
+
|
|
114
|
+
header = json.loads(__base64url_decode__(header_encoded).decode("utf-8"))
|
|
115
|
+
payload = json.loads(__base64url_decode__(payload_encoded).decode("utf-8"))
|
|
116
|
+
signature = __base64url_decode__(signature_encoded)
|
|
117
|
+
|
|
118
|
+
signature_input = f"{header_encoded}.{payload_encoded}".encode()
|
|
119
|
+
|
|
120
|
+
if header["alg"].startswith("HS"):
|
|
121
|
+
if secret is None:
|
|
122
|
+
raise EmptySecretKey
|
|
123
|
+
expected_signature = hmac.new(
|
|
124
|
+
secret.encode("utf-8"), signature_input, hashlib.sha256
|
|
125
|
+
).digest()
|
|
126
|
+
elif header["alg"].startswith("RS"):
|
|
127
|
+
if public_key is None:
|
|
128
|
+
raise EmptyPublicKey
|
|
129
|
+
rsa_key = RSA.import_key(public_key)
|
|
130
|
+
hash_obj = SHA256.new(signature_input)
|
|
131
|
+
try:
|
|
132
|
+
pkcs1_15.new(rsa_key).verify(hash_obj, signature)
|
|
133
|
+
expected_signature = signature
|
|
134
|
+
except (ValueError, TypeError):
|
|
135
|
+
raise ValueError("Invalid signature")
|
|
136
|
+
else:
|
|
137
|
+
raise ValueError("Unsupported algorithm")
|
|
138
|
+
|
|
139
|
+
if expected_signature != signature:
|
|
140
|
+
raise ValueError("Invalid token signature")
|
|
141
|
+
|
|
142
|
+
if check_exp:
|
|
143
|
+
if payload["exp"] is None:
|
|
144
|
+
raise NotFoundSomeInPayload('"exp" not found in payload')
|
|
145
|
+
if payload["exp"] < datetime.today().timestamp():
|
|
146
|
+
raise TokenLifeTimeExpired
|
|
147
|
+
|
|
148
|
+
return payload
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def __payload_maker__(exp: int | None, **data) -> dict[str, Any]:
|
|
152
|
+
"""Tool for making base payload.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
exp (int | None): Token expire
|
|
156
|
+
**data: Data for payload
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
(Dict[str, Any])
|
|
160
|
+
"""
|
|
161
|
+
base_payload: dict = {
|
|
162
|
+
"iat": datetime.now().timestamp(),
|
|
163
|
+
"exp": exp,
|
|
164
|
+
"jti": str(uuid4()),
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
base_payload.update(data)
|
|
168
|
+
return base_payload
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from jam.exceptions import TokenInBlackList, TokenNotInWhiteList
|
|
8
|
+
from jam.jwt.lists.__abc_list_repo__ import JWTList
|
|
9
|
+
from jam.jwt.tools import __gen_jwt__, __validate_jwt__
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseModule:
|
|
13
|
+
"""The base module from which all other modules inherit."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
module_type: Literal[
|
|
18
|
+
"jwt", "session-redis", "session-mongo", "session-custom"
|
|
19
|
+
],
|
|
20
|
+
) -> None:
|
|
21
|
+
"""Class constructor.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
module_type (Litetal["jwt", "session-redis", "session-mongo", "session-custom"]): Type of module
|
|
25
|
+
"""
|
|
26
|
+
self._type = module_type
|
|
27
|
+
|
|
28
|
+
def __get_type(self) -> str:
|
|
29
|
+
return self._type
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class JWTModule(BaseModule):
|
|
33
|
+
"""Module for JWT auth.
|
|
34
|
+
|
|
35
|
+
Methods:
|
|
36
|
+
make_payload(exp: int | None, **data): Creating a generic payload for a token
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
alg: Literal[
|
|
42
|
+
"HS256",
|
|
43
|
+
"HS384",
|
|
44
|
+
"HS512",
|
|
45
|
+
"RS256",
|
|
46
|
+
"RS384",
|
|
47
|
+
"RS512",
|
|
48
|
+
# "PS256",
|
|
49
|
+
# "PS384",
|
|
50
|
+
# "PS512",
|
|
51
|
+
] = "HS256",
|
|
52
|
+
secret_key: str | None = None,
|
|
53
|
+
public_key: str | None = None,
|
|
54
|
+
private_key: str | None = None,
|
|
55
|
+
expire: int = 3600,
|
|
56
|
+
list: JWTList | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Class constructor.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
alg (Literal["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS512", "PS384", "PS512"]): Algorithm for token encryption
|
|
62
|
+
secret_key (str | None): Secret key for HMAC enecryption
|
|
63
|
+
private_key (str | None): Private key for RSA enecryption
|
|
64
|
+
public_key (str | None): Public key for RSA
|
|
65
|
+
expire (int): Token lifetime in seconds
|
|
66
|
+
list (JWTList | None): List module
|
|
67
|
+
"""
|
|
68
|
+
super().__init__(module_type="jwt")
|
|
69
|
+
self._secret_key = secret_key
|
|
70
|
+
self.alg = alg
|
|
71
|
+
self._private_key = (private_key,)
|
|
72
|
+
self.public_key = public_key
|
|
73
|
+
self.exp = expire
|
|
74
|
+
self.list = list
|
|
75
|
+
|
|
76
|
+
def make_payload(self, exp: int | None = None, **data) -> dict[str, Any]:
|
|
77
|
+
"""Payload maker tool.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
exp (int | None): If none exp = JWTModule.exp
|
|
81
|
+
**data: Custom data
|
|
82
|
+
"""
|
|
83
|
+
if not exp:
|
|
84
|
+
_exp = self.exp
|
|
85
|
+
else:
|
|
86
|
+
_exp = exp
|
|
87
|
+
payload = {
|
|
88
|
+
"jti": str(uuid4()),
|
|
89
|
+
"exp": _exp,
|
|
90
|
+
"iat": datetime.datetime.now().timestamp(),
|
|
91
|
+
}
|
|
92
|
+
payload.update(**data)
|
|
93
|
+
return payload
|
|
94
|
+
|
|
95
|
+
def gen_token(self, **payload) -> str:
|
|
96
|
+
"""Creating a new token.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
**payload: Payload with information
|
|
100
|
+
|
|
101
|
+
Raises:
|
|
102
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None
|
|
103
|
+
EmtpyPrivateKey: If RSA algorithm is selected, but private key None
|
|
104
|
+
"""
|
|
105
|
+
header = {"alg": self.alg, "typ": "jwt"}
|
|
106
|
+
token = __gen_jwt__(
|
|
107
|
+
header=header,
|
|
108
|
+
payload=payload,
|
|
109
|
+
secret=self._secret_key,
|
|
110
|
+
private_key=self._private_key, # type: ignore
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if self.list:
|
|
114
|
+
if self.list.__list_type__ == "white":
|
|
115
|
+
self.list.add(token)
|
|
116
|
+
return token
|
|
117
|
+
|
|
118
|
+
def validate_payload(
|
|
119
|
+
self, token: str, check_exp: bool = False, check_list: bool = True
|
|
120
|
+
) -> dict[str, Any]:
|
|
121
|
+
"""A method for verifying a token.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
token (str): The token to check
|
|
125
|
+
check_exp (bool): Check for expiration?
|
|
126
|
+
check_list (bool): Check if there is a black/white list
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ValueError: If the token is invalid.
|
|
130
|
+
EmptySecretKey: If the HMAC algorithm is selected, but the secret key is None.
|
|
131
|
+
EmtpyPublicKey: If RSA algorithm is selected, but public key None.
|
|
132
|
+
NotFoundSomeInPayload: If 'exp' not found in payload.
|
|
133
|
+
TokenLifeTimeExpired: If token has expired.
|
|
134
|
+
TokenNotInWhiteList: If the list type is white, but the token is not there
|
|
135
|
+
TokenInBlackList: If the list type is black and the token is there
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
(dict[str, Any]): Payload from token
|
|
139
|
+
"""
|
|
140
|
+
if check_list:
|
|
141
|
+
if self.list.__list_type__ == "white": # type: ignore
|
|
142
|
+
if not self.list.check(token): # type: ignore
|
|
143
|
+
raise TokenNotInWhiteList
|
|
144
|
+
else:
|
|
145
|
+
pass
|
|
146
|
+
if self.list.__list_type__ == "black": # type: ignore
|
|
147
|
+
if self.list.check(token): # type: ignore
|
|
148
|
+
raise TokenInBlackList
|
|
149
|
+
else:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
payload = __validate_jwt__(
|
|
153
|
+
token=token,
|
|
154
|
+
check_exp=check_exp,
|
|
155
|
+
secret=self._secret_key,
|
|
156
|
+
public_key=self.public_key,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return payload
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from jam.exceptions.jwt import EmptyPublicKey, EmptySecretKey, EmtpyPrivateKey
|
|
6
|
+
from jam.jwt.lists.__abc_list_repo__ import JWTList
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def make_jwt_config(
|
|
10
|
+
alg: Literal[
|
|
11
|
+
"HS256",
|
|
12
|
+
"HS384",
|
|
13
|
+
"HS512",
|
|
14
|
+
"RS256",
|
|
15
|
+
"RS384",
|
|
16
|
+
"RS512",
|
|
17
|
+
# "PS256",
|
|
18
|
+
# "PS384",
|
|
19
|
+
# "PS512",
|
|
20
|
+
] = "HS256",
|
|
21
|
+
secret_key: str | None = None,
|
|
22
|
+
public_key: str | None = None,
|
|
23
|
+
private_key: str | None = None,
|
|
24
|
+
expire: int = 3600,
|
|
25
|
+
list: JWTList | None = None,
|
|
26
|
+
) -> dict[str, Any]:
|
|
27
|
+
"""Util for making JWT config.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
alg (Literal["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS512", "PS384", "PS512"]): Algorithm for token encryption
|
|
31
|
+
secret_key (str | None): Secret key for HMAC enecryption
|
|
32
|
+
private_key (str | None): Private key for RSA enecryption
|
|
33
|
+
public_key (str | None): Public key for RSA
|
|
34
|
+
expire (int): Token lifetime in seconds
|
|
35
|
+
list (JWTList | None): List module for checking
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
EmptySecretKey: If HS* algorithm is selected, but the secret key is empty
|
|
39
|
+
EmtpyPrivateKey: If RS* algorithm is selected, but the private key is empty
|
|
40
|
+
EmtpyPublicKey: If RS* algorithm is selected, but the public key is empty
|
|
41
|
+
"""
|
|
42
|
+
if alg.startswith("HS") and secret_key is None:
|
|
43
|
+
raise EmptySecretKey
|
|
44
|
+
|
|
45
|
+
if alg.startswith("RS") and private_key is None:
|
|
46
|
+
raise EmtpyPrivateKey
|
|
47
|
+
|
|
48
|
+
if alg.startswith("RS") and public_key is None:
|
|
49
|
+
raise EmptyPublicKey
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
"alg": alg,
|
|
53
|
+
"secret_key": secret_key,
|
|
54
|
+
"private_key": private_key,
|
|
55
|
+
"public_key": public_key,
|
|
56
|
+
"expire": expire,
|
|
57
|
+
"list": list,
|
|
58
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from cryptography.hazmat.backends import default_backend
|
|
4
|
+
from cryptography.hazmat.primitives import serialization
|
|
5
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_rsa_key_pair(key_size: int = 2048) -> dict:
|
|
9
|
+
"""RSA key generation utility.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
key_size (int): Size of RSA key
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
(dict): with public and private keys in format:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
{
|
|
19
|
+
"public": "some_key",
|
|
20
|
+
"private": "key"
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
"""
|
|
24
|
+
private_key = rsa.generate_private_key(
|
|
25
|
+
public_exponent=65537, key_size=key_size, backend=default_backend()
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
public_key = private_key.public_key()
|
|
29
|
+
|
|
30
|
+
pem_private = private_key.private_bytes(
|
|
31
|
+
encoding=serialization.Encoding.PEM,
|
|
32
|
+
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
33
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
pem_public = public_key.public_bytes(
|
|
37
|
+
encoding=serialization.Encoding.PEM,
|
|
38
|
+
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
"public": pem_public.decode("utf-8"),
|
|
43
|
+
"private": pem_private.decode("utf-8"),
|
|
44
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jamlib
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Simple and univirsal library for authorization.
|
|
5
|
+
Author-email: Makridenko Adrian <adrianmakridenko@duck.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
Project-URL: Homepage, https://github.com/lyaguxafrog/jam
|
|
8
|
+
Project-URL: Repository, https://github.com/lyaguxafrog/jam
|
|
9
|
+
Project-URL: Issues, https://github.com/lyaguxafrog/jam/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/lyaguxafrog/jam/releases
|
|
11
|
+
Keywords: auth,backend,jwt
|
|
12
|
+
Requires-Python: >=3.13
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE.md
|
|
15
|
+
Requires-Dist: pycryptodome<4.0.0,>=3.21.0
|
|
16
|
+
Requires-Dist: cryptography<45.0.0,>=44.0.2
|
|
17
|
+
Provides-Extra: json-lists
|
|
18
|
+
Requires-Dist: tinydb>=4.8.2; extra == "json-lists"
|
|
19
|
+
Provides-Extra: redis-lists
|
|
20
|
+
Requires-Dist: redis>=5.2.1; extra == "redis-lists"
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# Jam
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+

|
|
28
|
+

|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
```bash
|
|
34
|
+
pip install jamlib
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Getting start
|
|
38
|
+
```python
|
|
39
|
+
# -*- coding: utf-8 -*-
|
|
40
|
+
|
|
41
|
+
from typing import Any
|
|
42
|
+
|
|
43
|
+
from jam import Jam
|
|
44
|
+
|
|
45
|
+
config: dict[str, Any] = {
|
|
46
|
+
"jwt_secret_key": "some-secret",
|
|
47
|
+
"expire": 3600
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
data = {
|
|
51
|
+
"user_id": 1,
|
|
52
|
+
"role": "admin"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
jam = Jam(auth_type="jwt", config=config)
|
|
56
|
+
|
|
57
|
+
payload = jam.make_payload(**data)
|
|
58
|
+
token = jam.gen_jwt_token(**payload)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Roadmap
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
© [Adrian Makridenko](https://github.com/lyaguxafrog) 2025
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
jam/__abc_instances__.py
|
|
5
|
+
jam/__init__.py
|
|
6
|
+
jam/instance.py
|
|
7
|
+
jam/modules.py
|
|
8
|
+
jam/exceptions/__init__.py
|
|
9
|
+
jam/exceptions/jwt.py
|
|
10
|
+
jam/jwt/__init__.py
|
|
11
|
+
jam/jwt/__utils__.py
|
|
12
|
+
jam/jwt/tools.py
|
|
13
|
+
jam/jwt/lists/__abc_list_repo__.py
|
|
14
|
+
jam/jwt/lists/__init__.py
|
|
15
|
+
jam/jwt/lists/list_manipulations.py
|
|
16
|
+
jam/utils/__init__.py
|
|
17
|
+
jam/utils/config_maker.py
|
|
18
|
+
jam/utils/rsa.py
|
|
19
|
+
jamlib.egg-info/PKG-INFO
|
|
20
|
+
jamlib.egg-info/SOURCES.txt
|
|
21
|
+
jamlib.egg-info/dependency_links.txt
|
|
22
|
+
jamlib.egg-info/requires.txt
|
|
23
|
+
jamlib.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jam
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "jamlib"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Simple and univirsal library for authorization."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"}
|
|
7
|
+
]
|
|
8
|
+
license = {text = "MIT License"}
|
|
9
|
+
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
|
+
keywords = [
|
|
11
|
+
"auth",
|
|
12
|
+
"backend",
|
|
13
|
+
"jwt"
|
|
14
|
+
]
|
|
15
|
+
requires-python = ">=3.13"
|
|
16
|
+
dependencies = [
|
|
17
|
+
"pycryptodome>=3.21.0,<4.0.0",
|
|
18
|
+
"cryptography (>=44.0.2,<45.0.0)",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/lyaguxafrog/jam"
|
|
23
|
+
Repository = "https://github.com/lyaguxafrog/jam"
|
|
24
|
+
Issues = "https://github.com/lyaguxafrog/jam/issues"
|
|
25
|
+
Changelog = "https://github.com/lyaguxafrog/jam/releases"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
json-lists=["tinydb>=4.8.2"]
|
|
29
|
+
redis-lists=["redis>=5.2.1"]
|
|
30
|
+
|
|
31
|
+
[dependency-groups]
|
|
32
|
+
dev = [
|
|
33
|
+
"black>=25.1.0",
|
|
34
|
+
"icecream>=2.1.4",
|
|
35
|
+
"isort>=6.0.1",
|
|
36
|
+
"mkdocs>=1.6.1",
|
|
37
|
+
"mkdocs-material>=9.6.9",
|
|
38
|
+
"mkdocstrings>=0.29.0",
|
|
39
|
+
"mkdocstrings-python>=1.16.7",
|
|
40
|
+
"mypy>=1.15.0",
|
|
41
|
+
"pre-commit>=4.2.0",
|
|
42
|
+
"pytest>=8.3.5",
|
|
43
|
+
"redis>=5.2.1",
|
|
44
|
+
"ruff>=0.11.2",
|
|
45
|
+
"termynal>=0.13.0",
|
|
46
|
+
"tinydb>=4.8.2",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py313"
|
|
52
|
+
line-length = 80
|
|
53
|
+
ignore = ["UP009", "D100"]
|
|
54
|
+
exclude = ["*__init__.py", "tests/", "*exceptions/"]
|
|
55
|
+
|
|
56
|
+
[tool.ruff.lint]
|
|
57
|
+
extend-select = ["UP", "D"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint.pydocstyle]
|
|
60
|
+
convention = "google"
|
|
61
|
+
|
|
62
|
+
[tool.isort]
|
|
63
|
+
profile = "black"
|
|
64
|
+
default_section = "THIRDPARTY"
|
|
65
|
+
balanced_wrapping = true
|
|
66
|
+
known_first_party = "src"
|
|
67
|
+
line_length = 80
|
|
68
|
+
lines_after_imports = 2
|
|
69
|
+
lines_between_sections = 1
|
|
70
|
+
multi_line_output = 3
|
|
71
|
+
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
|
|
72
|
+
skip = ["env", ".env", ".env.example", "migrations/", ".venv/"]
|
|
73
|
+
|
|
74
|
+
[tool.black]
|
|
75
|
+
line-length = 80
|
|
76
|
+
skip-string-normalization = false
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
disable_error_code = ["no-redef", "import-not-found", "import-untyped", "attr-defined"]
|
|
80
|
+
|
|
81
|
+
[tool.pytest]
|
|
82
|
+
#asyncio_default_fixture_loop_scope = "function"
|
|
83
|
+
python_files = ["test*.py"]
|
|
84
|
+
|
|
85
|
+
[tool.pytest.ini_options]
|
|
86
|
+
addopts = "--capture=no"
|
jamlib-0.0.0/setup.cfg
ADDED