jamlib 3.0.0rc2__tar.gz → 3.1.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.
Files changed (107) hide show
  1. {jamlib-3.0.0rc2/src/jamlib.egg-info → jamlib-3.1.0}/PKG-INFO +8 -7
  2. {jamlib-3.0.0rc2 → jamlib-3.1.0}/pyproject.toml +20 -18
  3. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/__init__.py +2 -2
  4. jamlib-3.1.0/src/jam/cli/__init__.py +7 -0
  5. jamlib-3.1.0/src/jam/cli/cli.py +27 -0
  6. jamlib-3.1.0/src/jam/cli/commands/__init__.py +5 -0
  7. jamlib-3.1.0/src/jam/cli/commands/keys.py +103 -0
  8. jamlib-3.1.0/src/jam/cli/commands/password.py +53 -0
  9. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/litestar/middleware.py +4 -0
  10. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/litestar/plugins.py +6 -0
  11. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/starlette/backends.py +7 -1
  12. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/lists/__base__.py +4 -4
  13. {jamlib-3.0.0rc2 → jamlib-3.1.0/src/jamlib.egg-info}/PKG-INFO +8 -7
  14. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jamlib.egg-info/SOURCES.txt +6 -0
  15. jamlib-3.1.0/src/jamlib.egg-info/entry_points.txt +2 -0
  16. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jamlib.egg-info/requires.txt +8 -5
  17. {jamlib-3.0.0rc2 → jamlib-3.1.0}/LICENSE.md +0 -0
  18. {jamlib-3.0.0rc2 → jamlib-3.1.0}/README.md +0 -0
  19. {jamlib-3.0.0rc2 → jamlib-3.1.0}/setup.cfg +0 -0
  20. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/__base__.py +0 -0
  21. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/__base_encoder__.py +0 -0
  22. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/__deprecated__.py +0 -0
  23. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/__base__.py +0 -0
  24. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/__init__.py +0 -0
  25. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/instance.py +0 -0
  26. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/jwt/__init__.py +0 -0
  27. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/__base__.py +0 -0
  28. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/__init__.py +0 -0
  29. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  30. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/builtin/github.py +0 -0
  31. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  32. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/builtin/google.py +0 -0
  33. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  34. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/oauth2/client.py +0 -0
  35. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/sessions/__base__.py +0 -0
  36. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/sessions/__init__.py +0 -0
  37. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/sessions/json.py +0 -0
  38. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/aio/sessions/redis.py +0 -0
  39. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/encoders.py +0 -0
  40. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/__init__.py +0 -0
  41. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/base.py +0 -0
  42. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/jwt.py +0 -0
  43. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/oauth2.py +0 -0
  44. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/paseto.py +0 -0
  45. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/plugins.py +0 -0
  46. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/exceptions/sessions.py +0 -0
  47. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/__init__.py +0 -0
  48. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/fastapi/__init__.py +0 -0
  49. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/flask/__init__.py +0 -0
  50. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/flask/extensions.py +0 -0
  51. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/flask/objects.py +0 -0
  52. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/litestar/__init__.py +0 -0
  53. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/litestar/objects.py +0 -0
  54. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/starlette/__init__.py +0 -0
  55. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/ext/starlette/objects.py +0 -0
  56. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/instance.py +0 -0
  57. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/__algorithms__.py +0 -0
  58. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/__base__.py +0 -0
  59. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/__init__.py +0 -0
  60. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/__types__.py +0 -0
  61. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/lists/__init__.py +0 -0
  62. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/lists/json.py +0 -0
  63. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/lists/redis.py +0 -0
  64. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/module.py +0 -0
  65. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/jwt/utils.py +0 -0
  66. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/logger.py +0 -0
  67. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/__base__.py +0 -0
  68. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/__init__.py +0 -0
  69. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/builtin/__init__.py +0 -0
  70. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/builtin/github.py +0 -0
  71. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/builtin/gitlab.py +0 -0
  72. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/builtin/google.py +0 -0
  73. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/builtin/yandex.py +0 -0
  74. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/oauth2/client.py +0 -0
  75. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/otp/__base__.py +0 -0
  76. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/otp/__init__.py +0 -0
  77. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/otp/hotp.py +0 -0
  78. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/otp/totp.py +0 -0
  79. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/__base__.py +0 -0
  80. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/__init__.py +0 -0
  81. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/utils.py +0 -0
  82. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/v1.py +0 -0
  83. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/v2.py +0 -0
  84. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/v3.py +0 -0
  85. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/paseto/v4.py +0 -0
  86. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/py.typed +0 -0
  87. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/sessions/__base__.py +0 -0
  88. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/sessions/__init__.py +0 -0
  89. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/sessions/json.py +0 -0
  90. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/sessions/redis.py +0 -0
  91. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/tests/__init__.py +0 -0
  92. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/tests/clients.py +0 -0
  93. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/tests/fakers.py +0 -0
  94. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/__init__.py +0 -0
  95. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/aes.py +0 -0
  96. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/await_maybe.py +0 -0
  97. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/basic_auth.py +0 -0
  98. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/config_maker.py +0 -0
  99. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/ed.py +0 -0
  100. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/otp_keys.py +0 -0
  101. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/rsa.py +0 -0
  102. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/salt_hash.py +0 -0
  103. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/symmetric.py +0 -0
  104. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/xchacha20poly1305.py +0 -0
  105. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jam/utils/xor.py +0 -0
  106. {jamlib-3.0.0rc2 → jamlib-3.1.0}/src/jamlib.egg-info/dependency_links.txt +0 -0
  107. {jamlib-3.0.0rc2 → jamlib-3.1.0}/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.0rc2
3
+ Version: 3.1.0
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
@@ -16,7 +16,6 @@ Classifier: Intended Audience :: Developers
16
16
  Classifier: Intended Audience :: Information Technology
17
17
  Classifier: Programming Language :: Python
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
@@ -32,20 +31,22 @@ Requires-Python: >=3.10
32
31
  Description-Content-Type: text/markdown
33
32
  License-File: LICENSE.md
34
33
  Requires-Dist: cryptography>=46.0.5
34
+ Provides-Extra: cli
35
+ Requires-Dist: click>=8.3.1; extra == "cli"
35
36
  Provides-Extra: redis
36
- Requires-Dist: redis>=6.4.0; extra == "redis"
37
+ Requires-Dist: redis>=7.3.0; extra == "redis"
37
38
  Provides-Extra: json
38
39
  Requires-Dist: tinydb>=4.8.2; extra == "json"
39
40
  Provides-Extra: yaml
40
- Requires-Dist: pyyaml>=6.0.2; extra == "yaml"
41
+ Requires-Dist: pyyaml>=6.0.3; extra == "yaml"
41
42
  Provides-Extra: toml
42
43
  Requires-Dist: toml>=0.10.2; extra == "toml"
43
44
  Provides-Extra: litestar
44
- Requires-Dist: litestar>=2.18.0; extra == "litestar"
45
+ Requires-Dist: litestar>=2.21.1; extra == "litestar"
45
46
  Provides-Extra: starlette
46
- Requires-Dist: starlette>=0.48.0; extra == "starlette"
47
+ Requires-Dist: starlette>=0.52.1; extra == "starlette"
47
48
  Provides-Extra: fastapi
48
- Requires-Dist: fastapi>=0.119.0; extra == "fastapi"
49
+ Requires-Dist: fastapi>=0.135.1; extra == "fastapi"
49
50
  Provides-Extra: flask
50
51
  Requires-Dist: flask>=3.1.2; extra == "flask"
51
52
  Dynamic: license-file
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jamlib"
3
- version = "3.0.0.rc2"
3
+ version = "3.1.0"
4
4
  description = "Simple and universal library for authorization."
5
5
  authors = [
6
6
  {name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
@@ -34,7 +34,6 @@ classifiers = [
34
34
  "Intended Audience :: Information Technology",
35
35
  "Programming Language :: Python",
36
36
  "Programming Language :: Python :: 3",
37
- "Programming Language :: Python :: 3.9",
38
37
  "Programming Language :: Python :: 3.10",
39
38
  "Programming Language :: Python :: 3.11",
40
39
  "Programming Language :: Python :: 3.12",
@@ -55,18 +54,22 @@ Issues = "https://github.com/lyaguxafrog/jam/issues"
55
54
  Changelog = "https://github.com/lyaguxafrog/jam/blob/master/CHANGELOG.md"
56
55
 
57
56
 
57
+ [project.scripts]
58
+ jam = "jam.cli:cli"
59
+
58
60
  [project.optional-dependencies]
59
- redis=["redis>=6.4.0"]
61
+ cli=["click>=8.3.1"]
62
+ redis=["redis>=7.3.0"]
60
63
  json=["tinydb>=4.8.2"]
61
- yaml=["pyyaml>=6.0.2"]
64
+ yaml=["pyyaml>=6.0.3"]
62
65
  toml=["toml>=0.10.2"]
63
- litestar=["litestar>=2.18.0"]
64
- starlette=["starlette>=0.48.0"]
65
- fastapi=["fastapi>=0.119.0"]
66
+ litestar=["litestar>=2.21.1"]
67
+ starlette=["starlette>=0.52.1"]
68
+ fastapi=["fastapi>=0.135.1"]
66
69
  flask=["flask>=3.1.2"]
67
70
 
68
71
  [build-system]
69
- requires = ["setuptools>=61.0", "wheel"]
72
+ requires = ["setuptools>=82.0.1", "wheel"]
70
73
  build-backend = "setuptools.build_meta"
71
74
 
72
75
  [tool.setuptools.packages.find]
@@ -75,15 +78,14 @@ include = ["jam*"]
75
78
 
76
79
  [dependency-groups]
77
80
  dev = [
78
- "black>=25.1.0",
79
- "fakeredis>=2.28.1",
80
- "icecream>=2.1.4",
81
- "isort>=6.0.1",
82
- "pre-commit>=4.2.0",
83
- "pyrefly>=0.39.1",
84
- "ruff>=0.11.2",
81
+ "click>=8.3.1",
82
+ "fakeredis>=2.34.1",
83
+ "icecream>=2.1.10",
84
+ "pre-commit>=4.5.1",
85
+ "pyrefly>=0.56.0",
86
+ "ruff>=0.15.6",
85
87
  "unicecream>=0.2.1",
86
- "uvicorn>=0.37.0",
88
+ "uvicorn>=0.41.0",
87
89
  ]
88
90
  docs = [
89
91
  "mkdocs>=1.6.1",
@@ -95,8 +97,8 @@ docs = [
95
97
  "termynal>=0.13.0",
96
98
  ]
97
99
  tests = [
98
- "pytest>=8.4.2",
99
- "pytest-asyncio>=1.1.0",
100
+ "pytest>=9.0.2",
101
+ "pytest-asyncio>=1.3.0",
100
102
  "coverage>=7.13.4"
101
103
  ]
102
104
 
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- """JAM - Universal auth* library
3
+ """JAM - Universal auth* library.
4
4
 
5
5
  Source code: https://github.com/lyaguxafrog/jam
6
6
  Documentation: https://jam.makridenko.ru
@@ -12,5 +12,5 @@ from jam.encoders import JsonEncoder
12
12
  from jam.instance import Jam
13
13
 
14
14
 
15
- __version__ = "3.0.0.rc2"
15
+ __version__ = "3.0.0"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -0,0 +1,7 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """CLI tool for Jam."""
4
+
5
+ from .cli import cli
6
+
7
+ __all__ = ["cli"]
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from importlib.metadata import version
4
+
5
+
6
+ try:
7
+ import click
8
+ except ImportError:
9
+ from jam.exceptions import JamError
10
+
11
+ raise JamError(
12
+ message="To use the Jam CLI, run 'pip install jamlib[cli]'.",
13
+ error_code="jam.cli",
14
+ )
15
+
16
+ from jam.cli.commands import keys, password
17
+
18
+
19
+ @click.group()
20
+ @click.version_option(version=version("jamlib"))
21
+ def cli() -> None:
22
+ """Jam CLI - Key generation and password utilities."""
23
+ pass
24
+
25
+
26
+ cli.add_command(keys.keys)
27
+ cli.add_command(password.password)
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from . import keys, password
4
+
5
+ __all__ = ["keys", "password"]
@@ -0,0 +1,103 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import click
4
+
5
+ from jam.utils import (
6
+ generate_aes_key,
7
+ generate_ecdsa_p384_keypair,
8
+ generate_ed25519_keypair,
9
+ generate_rsa_key_pair,
10
+ generate_symmetric_key,
11
+ )
12
+
13
+
14
+ @click.group()
15
+ def keys() -> None:
16
+ """Generate cryptographic keys."""
17
+ pass
18
+
19
+
20
+ @keys.command()
21
+ @click.option(
22
+ "--public-out", "-p", default="public.pem", help="Public key output file"
23
+ )
24
+ @click.option(
25
+ "--private-out", "-o", default="private.pem", help="Private key output file"
26
+ )
27
+ def rsa(public_out: str, private_out: str) -> None:
28
+ """Generate RSA key pair."""
29
+ key_pair = generate_rsa_key_pair()
30
+
31
+ with open(private_out, "w") as f:
32
+ f.write(key_pair["private"])
33
+ with open(public_out, "w") as f:
34
+ f.write(key_pair["public"])
35
+
36
+ click.echo(f"RSA key pair generated: {private_out}, {public_out}")
37
+
38
+
39
+ @keys.command()
40
+ @click.option(
41
+ "--public-out", "-p", default="public.pem", help="Public key output file"
42
+ )
43
+ @click.option(
44
+ "--private-out", "-o", default="private.pem", help="Private key output file"
45
+ )
46
+ def ed25519(public_out: str, private_out: str) -> None:
47
+ """Generate Ed25519 key pair."""
48
+ key_pair = generate_ed25519_keypair()
49
+
50
+ with open(private_out, "w") as f:
51
+ f.write(key_pair["private"])
52
+ with open(public_out, "w") as f:
53
+ f.write(key_pair["public"])
54
+
55
+ click.echo(f"Ed25519 key pair generated: {private_out}, {public_out}")
56
+
57
+
58
+ @keys.command()
59
+ @click.option(
60
+ "--public-out", "-p", default="public.pem", help="Public key output file"
61
+ )
62
+ @click.option(
63
+ "--private-out", "-o", default="private.pem", help="Private key output file"
64
+ )
65
+ def ecdsa(public_out: str, private_out: str) -> None:
66
+ """Generate ECDSA P-384 key pair."""
67
+ key_pair = generate_ecdsa_p384_keypair()
68
+
69
+ with open(private_out, "w") as f:
70
+ f.write(key_pair["private"])
71
+ with open(public_out, "w") as f:
72
+ f.write(key_pair["public"])
73
+
74
+ click.echo(f"ECDSA P-384 key pair generated: {private_out}, {public_out}")
75
+
76
+
77
+ @keys.command()
78
+ @click.option("--out", "-o", default="aes.key", help="AES key output file")
79
+ def aes(out: str) -> None:
80
+ """Generate AES key."""
81
+ key = generate_aes_key()
82
+
83
+ with open(out, "wb") as f:
84
+ f.write(key)
85
+
86
+ click.echo(f"AES key generated: {out}")
87
+
88
+
89
+ @keys.command()
90
+ @click.option(
91
+ "--out", "-o", default="symmetric.key", help="Symmetric key output file"
92
+ )
93
+ @click.option(
94
+ "--bytes", "-b", default=32, help="Key size in bytes (default: 32)"
95
+ )
96
+ def symmetric(out: str, bytes: int) -> None:
97
+ """Generate symmetric key."""
98
+ key = generate_symmetric_key(bytes)
99
+
100
+ with open(out, "w") as f:
101
+ f.write(key)
102
+
103
+ click.echo(f"Symmetric key generated: {out} ({bytes} bytes)")
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import click
4
+
5
+ from jam.utils import (
6
+ check_password,
7
+ deserialize_hash,
8
+ hash_password,
9
+ serialize_hash,
10
+ )
11
+
12
+
13
+ @click.group()
14
+ def password() -> None:
15
+ """Password hashing and verification utilities."""
16
+ pass
17
+
18
+
19
+ @password.command()
20
+ @click.argument("password")
21
+ @click.option("--out", "-o", default=None, help="Output file (default: stdout)")
22
+ def hash(password: str, out: str | None) -> None:
23
+ """Hash a password."""
24
+ salt, hash_hex = hash_password(password)
25
+ serialized = serialize_hash(salt, hash_hex)
26
+
27
+ if out:
28
+ with open(out, "w") as f:
29
+ f.write(serialized)
30
+ click.echo(f"Password hash written to: {out}")
31
+ else:
32
+ click.echo(serialized)
33
+
34
+
35
+ @password.command()
36
+ @click.option(
37
+ "--hash",
38
+ "-h",
39
+ required=True,
40
+ help="Hash to verify against (format: salt$hash)",
41
+ )
42
+ def verify(hash: str) -> None:
43
+ """Verify a password against a hash."""
44
+ salt_hex, hash_hex = deserialize_hash(hash)
45
+
46
+ password = click.prompt("Password", hide_input=True)
47
+
48
+ result = check_password(password, salt_hex, hash_hex)
49
+ if result:
50
+ click.echo("✓ Password matches")
51
+ else:
52
+ click.echo("✗ Password does not match")
53
+ raise click.Abort()
@@ -21,11 +21,15 @@ class BaseMiddleware(AbstractAuthenticationMiddleware):
21
21
  AUTH_MODULE: Callable
22
22
  HEADER_NAME: str | None
23
23
  COOKIE_NAME: str | None
24
+ BEARER: bool = False
24
25
  USER: type[BaseUser]
25
26
 
26
27
  def _get_auth_token(self, connection: ASGIConnection) -> str | None:
27
28
  if self.HEADER_NAME:
28
29
  token = connection.headers.get(self.HEADER_NAME, None)
30
+ if self.BEARER and token:
31
+ token = token.split(" ")[1]
32
+ return token
29
33
  elif self.COOKIE_NAME:
30
34
  token = connection.cookies.get(self.COOKIE_NAME, None)
31
35
  else:
@@ -39,6 +39,7 @@ class BasePlugin(InitPlugin):
39
39
  pointer: str = GENERIC_POINTER,
40
40
  cookie_name: str | None = None,
41
41
  header_name: str | None = None,
42
+ bearer: bool = False,
42
43
  middleware: bool = True,
43
44
  user: type[BaseUser] | None = None,
44
45
  **kwargs,
@@ -50,6 +51,7 @@ class BasePlugin(InitPlugin):
50
51
  pointer (str): Config pointer
51
52
  cookie_name (str | None): Cookie name to read token
52
53
  header_name (str | None): Header name to read token
54
+ bearer (bool): Use bearer prefix for token (e.g. "Bearer ")
53
55
  middleware (bool): Use middleware?
54
56
  user (type[BaseUser]): User for request state. See: DOCUMENTATION
55
57
  **kwargs: Config arguments if config=None
@@ -84,6 +86,7 @@ class BasePlugin(InitPlugin):
84
86
  _middleware.HEADER_NAME = header_name
85
87
  _middleware.AUTH_MODULE = self._auth
86
88
  _middleware.USER = user # type: ignore
89
+ _middleware.BEARER = bearer
87
90
  self._middleware = _middleware
88
91
 
89
92
  def on_app_init(self, app_config: AppConfig) -> AppConfig: # noqa
@@ -111,6 +114,7 @@ class JWTPlugin(BasePlugin):
111
114
  cookie_name: str | None = None,
112
115
  header_name: str | None = None,
113
116
  middleware: bool = True,
117
+ bearer: bool = False,
114
118
  use_list: bool = False,
115
119
  user: type[BaseUser] | None = None,
116
120
  **kwargs,
@@ -123,6 +127,7 @@ class JWTPlugin(BasePlugin):
123
127
  cookie_name (str | None): Cookie name to read token
124
128
  header_name (str | None): Header name to read token
125
129
  middleware (bool): Use middleware?
130
+ bearer (bool): Use bearer prefix for token (e.g. "Bearer ")
126
131
  use_list (bool): Use token list for authentication?
127
132
  user (type[BaseUser]): User for request state. See: DOCUMENTATION
128
133
  **kwargs: Config arguments if config=None
@@ -133,6 +138,7 @@ class JWTPlugin(BasePlugin):
133
138
  cookie_name=cookie_name,
134
139
  header_name=header_name,
135
140
  middleware=middleware,
141
+ bearer=bearer,
136
142
  user=user,
137
143
  **kwargs,
138
144
  )
@@ -31,6 +31,7 @@ class BaseBackend(AuthenticationBackend):
31
31
  pointer: str = GENERIC_POINTER,
32
32
  cookie_name: str | None = None,
33
33
  header_name: str | None = None,
34
+ bearer: bool = False,
34
35
  user: type[BaseUser] = SimpleUser,
35
36
  **kwargs,
36
37
  ) -> None:
@@ -44,6 +45,7 @@ class BaseBackend(AuthenticationBackend):
44
45
  )
45
46
  self._cookie_name = cookie_name
46
47
  self._header_name = header_name
48
+ self._bearer = bearer
47
49
  self._user = user
48
50
  self._config_setup(config, pointer, kwargs)
49
51
 
@@ -53,7 +55,11 @@ class BaseBackend(AuthenticationBackend):
53
55
  elif self._header_name:
54
56
  token_bear = connection.headers.get(self._header_name, None)
55
57
  if token_bear:
56
- token = token_bear.split("Bearer ")[1] # noqa: E701
58
+ token = (
59
+ token_bear.split("Bearer ")[1]
60
+ if self._bearer
61
+ else token_bear
62
+ ) # noqa: E701
57
63
  else:
58
64
  token = None # noqa: E701
59
65
  else:
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from typing import Any, Literal
4
+ from typing import Literal
5
5
 
6
6
 
7
7
  class BaseJWTList(ABC):
@@ -12,16 +12,16 @@ class BaseJWTList(ABC):
12
12
  self.__list_type__ = list_type
13
13
 
14
14
  @abstractmethod
15
- def add(self, token: str) -> Any:
15
+ def add(self, token: str) -> None:
16
16
  """Method for adding token to list."""
17
17
  raise NotImplementedError
18
18
 
19
19
  @abstractmethod
20
- def check(self, token: str) -> Any:
20
+ def check(self, token: str) -> bool:
21
21
  """Method for checking if a token is present in the list."""
22
22
  raise NotImplementedError
23
23
 
24
24
  @abstractmethod
25
- def delete(self, token: str) -> Any:
25
+ def delete(self, token: str) -> None:
26
26
  """Method for removing a token from a list."""
27
27
  raise NotImplementedError
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamlib
3
- Version: 3.0.0rc2
3
+ Version: 3.1.0
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
@@ -16,7 +16,6 @@ Classifier: Intended Audience :: Developers
16
16
  Classifier: Intended Audience :: Information Technology
17
17
  Classifier: Programming Language :: Python
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
22
21
  Classifier: Programming Language :: Python :: 3.12
@@ -32,20 +31,22 @@ Requires-Python: >=3.10
32
31
  Description-Content-Type: text/markdown
33
32
  License-File: LICENSE.md
34
33
  Requires-Dist: cryptography>=46.0.5
34
+ Provides-Extra: cli
35
+ Requires-Dist: click>=8.3.1; extra == "cli"
35
36
  Provides-Extra: redis
36
- Requires-Dist: redis>=6.4.0; extra == "redis"
37
+ Requires-Dist: redis>=7.3.0; extra == "redis"
37
38
  Provides-Extra: json
38
39
  Requires-Dist: tinydb>=4.8.2; extra == "json"
39
40
  Provides-Extra: yaml
40
- Requires-Dist: pyyaml>=6.0.2; extra == "yaml"
41
+ Requires-Dist: pyyaml>=6.0.3; extra == "yaml"
41
42
  Provides-Extra: toml
42
43
  Requires-Dist: toml>=0.10.2; extra == "toml"
43
44
  Provides-Extra: litestar
44
- Requires-Dist: litestar>=2.18.0; extra == "litestar"
45
+ Requires-Dist: litestar>=2.21.1; extra == "litestar"
45
46
  Provides-Extra: starlette
46
- Requires-Dist: starlette>=0.48.0; extra == "starlette"
47
+ Requires-Dist: starlette>=0.52.1; extra == "starlette"
47
48
  Provides-Extra: fastapi
48
- Requires-Dist: fastapi>=0.119.0; extra == "fastapi"
49
+ Requires-Dist: fastapi>=0.135.1; extra == "fastapi"
49
50
  Provides-Extra: flask
50
51
  Requires-Dist: flask>=3.1.2; extra == "flask"
51
52
  Dynamic: license-file
@@ -25,6 +25,11 @@ src/jam/aio/sessions/__base__.py
25
25
  src/jam/aio/sessions/__init__.py
26
26
  src/jam/aio/sessions/json.py
27
27
  src/jam/aio/sessions/redis.py
28
+ src/jam/cli/__init__.py
29
+ src/jam/cli/cli.py
30
+ src/jam/cli/commands/__init__.py
31
+ src/jam/cli/commands/keys.py
32
+ src/jam/cli/commands/password.py
28
33
  src/jam/exceptions/__init__.py
29
34
  src/jam/exceptions/base.py
30
35
  src/jam/exceptions/jwt.py
@@ -95,5 +100,6 @@ src/jam/utils/xor.py
95
100
  src/jamlib.egg-info/PKG-INFO
96
101
  src/jamlib.egg-info/SOURCES.txt
97
102
  src/jamlib.egg-info/dependency_links.txt
103
+ src/jamlib.egg-info/entry_points.txt
98
104
  src/jamlib.egg-info/requires.txt
99
105
  src/jamlib.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jam = jam.cli:cli
@@ -1,7 +1,10 @@
1
1
  cryptography>=46.0.5
2
2
 
3
+ [cli]
4
+ click>=8.3.1
5
+
3
6
  [fastapi]
4
- fastapi>=0.119.0
7
+ fastapi>=0.135.1
5
8
 
6
9
  [flask]
7
10
  flask>=3.1.2
@@ -10,16 +13,16 @@ flask>=3.1.2
10
13
  tinydb>=4.8.2
11
14
 
12
15
  [litestar]
13
- litestar>=2.18.0
16
+ litestar>=2.21.1
14
17
 
15
18
  [redis]
16
- redis>=6.4.0
19
+ redis>=7.3.0
17
20
 
18
21
  [starlette]
19
- starlette>=0.48.0
22
+ starlette>=0.52.1
20
23
 
21
24
  [toml]
22
25
  toml>=0.10.2
23
26
 
24
27
  [yaml]
25
- pyyaml>=6.0.2
28
+ pyyaml>=6.0.3
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes