jamlib 3.2.0b2__tar.gz → 3.2.0b3__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 (122) hide show
  1. {jamlib-3.2.0b2/src/jamlib.egg-info → jamlib-3.2.0b3}/PKG-INFO +1 -1
  2. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/pyproject.toml +1 -1
  3. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/__base__.py +133 -21
  4. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/__init__.py +1 -1
  5. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/__base__.py +69 -1
  6. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/instance.py +76 -2
  7. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/instance.py +79 -2
  8. jamlib-3.2.0b3/src/jam/jose/__init__.py +113 -0
  9. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/jwt.py +1 -1
  10. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/config_maker.py +35 -4
  11. {jamlib-3.2.0b2 → jamlib-3.2.0b3/src/jamlib.egg-info}/PKG-INFO +1 -1
  12. jamlib-3.2.0b2/src/jam/jose/__init__.py +0 -55
  13. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/LICENSE.md +0 -0
  14. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/README.md +0 -0
  15. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/setup.cfg +0 -0
  16. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/__base_encoder__.py +0 -0
  17. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/__deprecated__.py +0 -0
  18. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/__init__.py +0 -0
  19. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/jwt/__init__.py +0 -0
  20. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/__base__.py +0 -0
  21. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/__init__.py +0 -0
  22. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/__init__.py +0 -0
  23. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/github.py +0 -0
  24. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/gitlab.py +0 -0
  25. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/google.py +0 -0
  26. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/builtin/yandex.py +0 -0
  27. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/oauth2/client.py +0 -0
  28. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/sessions/__base__.py +0 -0
  29. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/sessions/__init__.py +0 -0
  30. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/sessions/json.py +0 -0
  31. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/aio/sessions/redis.py +0 -0
  32. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/cli/__init__.py +0 -0
  33. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/cli/cli.py +0 -0
  34. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/cli/commands/__init__.py +0 -0
  35. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/cli/commands/keys.py +0 -0
  36. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/cli/commands/password.py +0 -0
  37. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/encoders.py +0 -0
  38. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/__init__.py +0 -0
  39. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/base.py +0 -0
  40. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/jose.py +0 -0
  41. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/jwt.py +0 -0
  42. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/oauth2.py +0 -0
  43. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/paseto.py +0 -0
  44. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/plugins.py +0 -0
  45. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/exceptions/sessions.py +0 -0
  46. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/__init__.py +0 -0
  47. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/fastapi/__init__.py +0 -0
  48. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/flask/__init__.py +0 -0
  49. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/flask/extensions.py +0 -0
  50. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/flask/objects.py +0 -0
  51. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/litestar/__init__.py +0 -0
  52. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/litestar/middleware.py +0 -0
  53. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/litestar/objects.py +0 -0
  54. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/litestar/plugins.py +0 -0
  55. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/starlette/__init__.py +0 -0
  56. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/starlette/backends.py +0 -0
  57. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/ext/starlette/objects.py +0 -0
  58. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/__algorithms__.py +0 -0
  59. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/__base__.py +0 -0
  60. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/jwe.py +0 -0
  61. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/jwk.py +0 -0
  62. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/jws.py +0 -0
  63. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/lists/__base__.py +0 -0
  64. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/lists/__init__.py +0 -0
  65. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/lists/json.py +0 -0
  66. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/lists/memory.py +0 -0
  67. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/lists/redis.py +0 -0
  68. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jose/utils.py +0 -0
  69. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/__algorithms__.py +0 -0
  70. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/__base__.py +0 -0
  71. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/__init__.py +0 -0
  72. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/__types__.py +0 -0
  73. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/lists/__base__.py +0 -0
  74. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/lists/__init__.py +0 -0
  75. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/lists/json.py +0 -0
  76. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/lists/redis.py +0 -0
  77. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/module.py +0 -0
  78. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/jwt/utils.py +0 -0
  79. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/logger.py +0 -0
  80. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/__base__.py +0 -0
  81. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/__init__.py +0 -0
  82. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/__init__.py +0 -0
  83. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/github.py +0 -0
  84. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/gitlab.py +0 -0
  85. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/google.py +0 -0
  86. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/builtin/yandex.py +0 -0
  87. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/oauth2/client.py +0 -0
  88. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/otp/__base__.py +0 -0
  89. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/otp/__init__.py +0 -0
  90. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/otp/hotp.py +0 -0
  91. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/otp/totp.py +0 -0
  92. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/__base__.py +0 -0
  93. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/__init__.py +0 -0
  94. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/utils.py +0 -0
  95. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/v1.py +0 -0
  96. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/v2.py +0 -0
  97. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/v3.py +0 -0
  98. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/paseto/v4.py +0 -0
  99. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/py.typed +0 -0
  100. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/sessions/__base__.py +0 -0
  101. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/sessions/__init__.py +0 -0
  102. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/sessions/json.py +0 -0
  103. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/sessions/redis.py +0 -0
  104. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/tests/__init__.py +0 -0
  105. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/tests/clients.py +0 -0
  106. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/tests/fakers.py +0 -0
  107. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/__init__.py +0 -0
  108. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/aes.py +0 -0
  109. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/await_maybe.py +0 -0
  110. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/basic_auth.py +0 -0
  111. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/ed.py +0 -0
  112. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/otp_keys.py +0 -0
  113. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/rsa.py +0 -0
  114. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/salt_hash.py +0 -0
  115. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/symmetric.py +0 -0
  116. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/xchacha20poly1305.py +0 -0
  117. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jam/utils/xor.py +0 -0
  118. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jamlib.egg-info/SOURCES.txt +0 -0
  119. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jamlib.egg-info/dependency_links.txt +0 -0
  120. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jamlib.egg-info/entry_points.txt +0 -0
  121. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/src/jamlib.egg-info/requires.txt +0 -0
  122. {jamlib-3.2.0b2 → jamlib-3.2.0b3}/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.0b2
3
+ Version: 3.2.0b3
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jamlib"
3
- version = "3.2.0b2"
3
+ version = "3.2.0b3"
4
4
  description = "Simple and universal library for authorization."
5
5
  authors = [
6
6
  {name = "Makridenko Adrian",email = "adrianmakridenko@duck.com"},
@@ -6,7 +6,7 @@ from typing import Any, Literal
6
6
 
7
7
  from jam.encoders import BaseEncoder, JsonEncoder
8
8
  from jam.exceptions import JamConfigurationError
9
- from jam.jose.__base__ import BaseJWT
9
+ from jam.jose.__base__ import BaseJWE, BaseJWS, BaseJWT
10
10
  from jam.logger import BaseLogger, JamLogger
11
11
  from jam.oauth2.__base__ import BaseOAuth2Client
12
12
  from jam.otp.__base__ import BaseOTP, OTPConfig
@@ -18,7 +18,7 @@ from jam.utils.config_maker import __config_maker__, __module_loader__
18
18
  class BaseJam(ABC):
19
19
  """Base jam instance."""
20
20
 
21
- MODULES: dict[str, str] = {}
21
+ MODULES: dict[str, str | dict[str, str]] = {}
22
22
 
23
23
  def __init__(
24
24
  self,
@@ -55,6 +55,9 @@ class BaseJam(ABC):
55
55
  self._logger = logger(log_level)
56
56
  self._serializer = serializer
57
57
  self.jwt: BaseJWT | None = None
58
+ self.jws: BaseJWS | None = None
59
+ self.jwe: BaseJWE | None = None
60
+ self.jose: dict[str, Any] | None = None
58
61
  self.session: BaseSessionModule | None = None
59
62
  self.oauth2: dict[str, BaseOAuth2Client] | None = None
60
63
  self.otp: OTPConfig | None = None
@@ -69,7 +72,7 @@ class BaseJam(ABC):
69
72
  )
70
73
  self._logger.debug(
71
74
  "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}"
75
+ 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
76
  )
74
77
  gc.collect()
75
78
 
@@ -139,6 +142,7 @@ class BaseJam(ABC):
139
142
  """Build instance.
140
143
 
141
144
  Load modules from configuration and initialize them.
145
+ Supports both flat modules (name -> path) and nested modules (name -> {subname -> path}).
142
146
 
143
147
  Args:
144
148
  config (dict[str, Any]): Configuration
@@ -151,24 +155,68 @@ class BaseJam(ABC):
151
155
  self._logger.debug(f"Missing configuration for module {name}")
152
156
  continue
153
157
 
154
- try:
155
- module_cls = __module_loader__(path)
156
- self._logger.debug(f"Loading module {name} from {path}")
157
- params = config.get(name, {})
158
- self._logger.debug(
159
- f"Module {name} config params: {list(params.keys())}"
160
- )
161
- # params["logger"] = self._logger
162
- # params["serializer"] = self._serializer
163
- module_instance = module_cls(**params)
164
- self.__setattr__(name, module_instance)
165
- self._logger.debug(f"Module {name} initialized successfully")
166
-
167
- except Exception as e:
168
- self._logger.error(
169
- f"Failed to load module {name} from {path}: {e}",
170
- exc_info=True,
171
- )
158
+ if isinstance(path, dict):
159
+ subconfig = config.get(name, {})
160
+ if not isinstance(subconfig, dict):
161
+ subconfig = {}
162
+
163
+ for subname, subpath in path.items():
164
+ if subname not in subconfig:
165
+ self._logger.debug(
166
+ f"Missing configuration for module {name}.{subname}"
167
+ )
168
+ continue
169
+
170
+ try:
171
+ module_cls = __module_loader__(subpath)
172
+ self._logger.debug(
173
+ f"Loading module {name}.{subname} from {subpath}"
174
+ )
175
+ params = subconfig.get(subname, {})
176
+ self._logger.debug(
177
+ f"Module {name}.{subname} config params: {list(params.keys())}"
178
+ )
179
+ module_instance = module_cls(**params)
180
+
181
+ if self.jose is None:
182
+ self.jose = {}
183
+ self.jose[subname] = module_instance
184
+
185
+ if subname == "jwt":
186
+ self.jwt = module_instance
187
+ elif subname == "jws":
188
+ self.jws = module_instance
189
+ elif subname == "jwe":
190
+ self.jwe = module_instance
191
+
192
+ self._logger.debug(
193
+ f"Module {name}.{subname} initialized successfully"
194
+ )
195
+
196
+ except Exception as e:
197
+ self._logger.error(
198
+ f"Failed to load module {name}.{subname} from {subpath}: {e}",
199
+ exc_info=True,
200
+ )
201
+ else:
202
+ try:
203
+ module_cls = __module_loader__(path)
204
+ self._logger.debug(f"Loading module {name} from {path}")
205
+ params = config.get(name, {})
206
+ self._logger.debug(
207
+ f"Module {name} config params: {list(params.keys())}"
208
+ )
209
+ module_instance = module_cls(**params)
210
+ self.__setattr__(name, module_instance)
211
+ self._logger.debug(
212
+ f"Module {name} initialized successfully"
213
+ )
214
+
215
+ except Exception as e:
216
+ self._logger.error(
217
+ f"Failed to load module {name} from {path}: {e}",
218
+ exc_info=True,
219
+ )
172
220
 
173
221
  def __otp(
174
222
  self, type: Literal["totp", "hotp"] | None = None
@@ -240,6 +288,70 @@ class BaseJam(ABC):
240
288
  """
241
289
  raise NotImplementedError
242
290
 
291
+ @abstractmethod
292
+ def jws_sign(
293
+ self,
294
+ data: dict[str, Any] | str,
295
+ header: dict[str, Any] | None = None,
296
+ ) -> str:
297
+ """Sign data using JWS.
298
+
299
+ Args:
300
+ data: Data to sign. If dict, will be JSON encoded.
301
+ header: JWS header.
302
+
303
+ Returns:
304
+ str: JWS token.
305
+ """
306
+ raise NotImplementedError
307
+
308
+ @abstractmethod
309
+ def jws_verify(self, token: str) -> dict[str, Any]:
310
+ """Verify JWS token.
311
+
312
+ Args:
313
+ token: JWS token.
314
+
315
+ Returns:
316
+ dict[str, Any]: Decoded payload.
317
+
318
+ Raises:
319
+ JamJWSVerificationError: If verification fails.
320
+ """
321
+ raise NotImplementedError
322
+
323
+ @abstractmethod
324
+ def jwe_encrypt(
325
+ self,
326
+ data: dict[str, Any] | str,
327
+ header: dict[str, Any] | None = None,
328
+ ) -> str:
329
+ """Encrypt data using JWE.
330
+
331
+ Args:
332
+ data: Data to encrypt. If dict, will be JSON encoded.
333
+ header: JWE header.
334
+
335
+ Returns:
336
+ str: JWE token.
337
+ """
338
+ raise NotImplementedError
339
+
340
+ @abstractmethod
341
+ def jwe_decrypt(self, token: str) -> bytes:
342
+ """Decrypt JWE token.
343
+
344
+ Args:
345
+ token: JWE token.
346
+
347
+ Returns:
348
+ bytes: Decrypted data.
349
+
350
+ Raises:
351
+ JamJWEDecryptionError: If decryption fails.
352
+ """
353
+ raise NotImplementedError
354
+
243
355
  @abstractmethod
244
356
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
245
357
  """Create new session.
@@ -12,5 +12,5 @@ from jam.encoders import JsonEncoder
12
12
  from jam.instance import Jam
13
13
 
14
14
 
15
- __version__ = "3.2.0b2"
15
+ __version__ = "3.2.0b3"
16
16
  __all__ = ["Jam", "JsonEncoder", "BaseJam", "BaseEncoder"]
@@ -6,15 +6,19 @@ from typing import Any
6
6
  from jam.__base__ import BaseJam
7
7
  from jam.aio.oauth2.__base__ import BaseAsyncOAuth2Client
8
8
  from jam.aio.sessions.__base__ import BaseAsyncSessionModule
9
+ from jam.jose.__base__ import BaseJWE, BaseJWS
9
10
 
10
11
 
11
12
  class BaseAsyncJam(BaseJam):
12
13
  """Base async jam instance."""
13
14
 
14
- MODULES: dict[str, str] = {}
15
+ MODULES: dict[str, str | dict[str, str]] = {}
15
16
 
16
17
  session: BaseAsyncSessionModule | None = None # type: ignore[override]
17
18
  oauth2: dict[str, BaseAsyncOAuth2Client] | None = None # type: ignore[override]
19
+ jose: dict[str, Any] | None = None # type: ignore[override]
20
+ jws: BaseJWS | None = None # type: ignore[override]
21
+ jwe: BaseJWE | None = None # type: ignore[override]
18
22
 
19
23
  @abstractmethod
20
24
  async def jwt_make_payload( # type: ignore[override]
@@ -340,6 +344,70 @@ class BaseAsyncJam(BaseJam):
340
344
  """
341
345
  raise NotImplementedError
342
346
 
347
+ @abstractmethod
348
+ async def jws_sign(
349
+ self,
350
+ data: dict[str, Any] | str,
351
+ header: dict[str, Any] | None = None,
352
+ ) -> str:
353
+ """Sign data using JWS.
354
+
355
+ Args:
356
+ data: Data to sign. If dict, will be JSON encoded.
357
+ header: JWS header.
358
+
359
+ Returns:
360
+ str: JWS token.
361
+ """
362
+ raise NotImplementedError
363
+
364
+ @abstractmethod
365
+ async def jws_verify(self, token: str) -> dict[str, Any]:
366
+ """Verify JWS token.
367
+
368
+ Args:
369
+ token: JWS token.
370
+
371
+ Returns:
372
+ dict[str, Any]: Decoded payload.
373
+
374
+ Raises:
375
+ JamJWSVerificationError: If verification fails.
376
+ """
377
+ raise NotImplementedError
378
+
379
+ @abstractmethod
380
+ async def jwe_encrypt(
381
+ self,
382
+ data: dict[str, Any] | str,
383
+ header: dict[str, Any] | None = None,
384
+ ) -> str:
385
+ """Encrypt data using JWE.
386
+
387
+ Args:
388
+ data: Data to encrypt. If dict, will be JSON encoded.
389
+ header: JWE header.
390
+
391
+ Returns:
392
+ str: JWE token.
393
+ """
394
+ raise NotImplementedError
395
+
396
+ @abstractmethod
397
+ async def jwe_decrypt(self, token: str) -> bytes:
398
+ """Decrypt JWE token.
399
+
400
+ Args:
401
+ token: JWE token.
402
+
403
+ Returns:
404
+ bytes: Decrypted data.
405
+
406
+ Raises:
407
+ JamJWEDecryptionError: If decryption fails.
408
+ """
409
+ raise NotImplementedError
410
+
343
411
  @abstractmethod
344
412
  async def paseto_decode( # type: ignore[override]
345
413
  self, token: str, check_exp: bool = True, check_list: bool = True
@@ -18,8 +18,12 @@ from jam.exceptions import (
18
18
  class Jam(BaseAsyncJam):
19
19
  """Main async Jam instance."""
20
20
 
21
- MODULES: dict[str, str] = {
22
- "jwt": "jam.aio.jwt.create_instance",
21
+ MODULES: dict[str, str | dict[str, str]] = {
22
+ "jose": {
23
+ "jwt": "jam.jose.create_jwt_instance",
24
+ "jws": "jam.jose.create_jws_instance",
25
+ "jwe": "jam.jose.create_jwe_instance",
26
+ },
23
27
  "session": "jam.aio.sessions.create_instance",
24
28
  "oauth2": "jam.aio.oauth2.create_instance",
25
29
  "paseto": "jam.paseto.create_instance",
@@ -184,6 +188,76 @@ class Jam(BaseAsyncJam):
184
188
  return {"header": headers, "payload": payload}
185
189
  return payload
186
190
 
191
+ async def jws_sign(
192
+ self,
193
+ data: dict[str, Any] | str,
194
+ header: dict[str, Any] | None = None,
195
+ ) -> str:
196
+ """Sign data using JWS.
197
+
198
+ Args:
199
+ data: Data to sign. If dict, will be JSON encoded.
200
+ header: JWS header.
201
+
202
+ Returns:
203
+ str: JWS token.
204
+ """
205
+ assert self.jws is not None
206
+ self._logger.debug(f"Signing data with JWS, header: {header}")
207
+ token = self.jws.sign(header or {}, data)
208
+ self._logger.debug(f"JWS token created, length: {len(token)}")
209
+ return token
210
+
211
+ async def jws_verify(self, token: str) -> dict[str, Any]:
212
+ """Verify JWS token.
213
+
214
+ Args:
215
+ token: JWS token.
216
+
217
+ Returns:
218
+ dict[str, Any]: Decoded payload.
219
+ """
220
+ assert self.jws is not None
221
+ self._logger.debug(f"Verifying JWS token, length: {len(token)}")
222
+ result = self.jws.verify(token)
223
+ self._logger.debug("JWS token verified successfully")
224
+ return result
225
+
226
+ async def jwe_encrypt(
227
+ self,
228
+ data: dict[str, Any] | str,
229
+ header: dict[str, Any] | None = None,
230
+ ) -> str:
231
+ """Encrypt data using JWE.
232
+
233
+ Args:
234
+ data: Data to encrypt. If dict, will be JSON encoded.
235
+ header: JWE header.
236
+
237
+ Returns:
238
+ str: JWE token.
239
+ """
240
+ assert self.jwe is not None
241
+ self._logger.debug(f"Encrypting data with JWE, header: {header}")
242
+ token = self.jwe.encrypt(data, header)
243
+ self._logger.debug(f"JWE token created, length: {len(token)}")
244
+ return token
245
+
246
+ async def jwe_decrypt(self, token: str) -> bytes:
247
+ """Decrypt JWE token.
248
+
249
+ Args:
250
+ token: JWE token.
251
+
252
+ Returns:
253
+ bytes: Decrypted data.
254
+ """
255
+ assert self.jwe is not None
256
+ self._logger.debug(f"Decrypting JWE token, length: {len(token)}")
257
+ result = self.jwe.decrypt(token)
258
+ self._logger.debug("JWE token decrypted successfully")
259
+ return result
260
+
187
261
  async def session_create(
188
262
  self, session_key: str, data: dict[str, Any]
189
263
  ) -> str:
@@ -19,8 +19,12 @@ from jam.exceptions import (
19
19
  class Jam(BaseJam):
20
20
  """Main instance."""
21
21
 
22
- MODULES: dict[str, str] = {
23
- "jwt": "jam.jose.JWT",
22
+ MODULES: dict[str, str | dict[str, str]] = {
23
+ "jose": {
24
+ "jwt": "jam.jose.create_jwt_instance",
25
+ "jws": "jam.jose.create_jws_instance",
26
+ "jwe": "jam.jose.create_jwe_instance",
27
+ },
24
28
  "session": "jam.sessions.create_instance",
25
29
  "oauth2": "jam.oauth2.create_instance",
26
30
  "paseto": "jam.paseto.create_instance",
@@ -190,6 +194,79 @@ class Jam(BaseJam):
190
194
  return data
191
195
  return payload
192
196
 
197
+ def jws_sign(
198
+ self,
199
+ data: dict[str, Any] | str,
200
+ header: dict[str, Any] | None = None,
201
+ ) -> str:
202
+ """Sign data using JWS.
203
+
204
+ Args:
205
+ data: Data to sign. If dict, will be JSON encoded.
206
+ header: JWS header.
207
+
208
+ Returns:
209
+ str: JWS token.
210
+ """
211
+ assert self.jws is not None
212
+ self._logger.debug(f"Signing data with JWS, header: {header}")
213
+ token = self.jws.sign(header or {}, data)
214
+ self._logger.debug(f"JWS token created, length: {len(token)}")
215
+ return token
216
+
217
+ def jws_verify(self, token: str) -> dict[str, Any]:
218
+ """Verify JWS token.
219
+
220
+ Args:
221
+ token: JWS token.
222
+
223
+ Returns:
224
+ dict[str, Any]: Decoded payload.
225
+ """
226
+ assert self.jws is not None
227
+ self._logger.debug(f"Verifying JWS token, length: {len(token)}")
228
+ result = self.jws.verify(token)
229
+ self._logger.debug("JWS token verified successfully")
230
+ return result
231
+
232
+ def jwe_encrypt(
233
+ self,
234
+ data: dict[str, Any] | str,
235
+ header: dict[str, Any] | None = None,
236
+ ) -> str:
237
+ """Encrypt data using JWE.
238
+
239
+ Args:
240
+ data: Data to encrypt. If dict, will be JSON encoded.
241
+ header: JWE header.
242
+
243
+ Returns:
244
+ str: JWE token.
245
+ """
246
+ assert self.jwe is not None
247
+ self._logger.debug(f"Encrypting data with JWE, header: {header}")
248
+ token = self.jwe.encrypt(
249
+ self._serializer.dumps(data) if isinstance(data, dict) else data,
250
+ header,
251
+ )
252
+ self._logger.debug(f"JWE token created, length: {len(token)}")
253
+ return token
254
+
255
+ def jwe_decrypt(self, token: str) -> bytes:
256
+ """Decrypt JWE token.
257
+
258
+ Args:
259
+ token: JWE token.
260
+
261
+ Returns:
262
+ bytes: Decrypted data.
263
+ """
264
+ assert self.jwe is not None
265
+ self._logger.debug(f"Decrypting JWE token, length: {len(token)}")
266
+ result = self.jwe.decrypt(token)
267
+ self._logger.debug("JWE token decrypted successfully")
268
+ return result
269
+
193
270
  def session_create(self, session_key: str, data: dict[str, Any]) -> str:
194
271
  """Create new session.
195
272
 
@@ -0,0 +1,113 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ """JOSE tools."""
4
+
5
+ from typing import Any
6
+
7
+ from jam.jose.jwk import JWK, JWKOct, JWKSet, JWKRSA, JWKEC
8
+ from jam.jose.jws import JWS
9
+ from jam.jose.jwe import JWE
10
+ from jam.jose.jwt import JWT
11
+ from jam.logger import BaseLogger, logger
12
+ from jam.encoders import BaseEncoder, JsonEncoder
13
+
14
+
15
+ def create_jwt_instance(
16
+ alg: str,
17
+ secret: Any = None,
18
+ secret_key: Any = None,
19
+ password: str | bytes | None = None,
20
+ list: dict[str, Any] | None = None,
21
+ logger: BaseLogger = logger,
22
+ serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
23
+ **kwargs: Any,
24
+ ) -> JWT:
25
+ """Create JWT instance."""
26
+ if secret is None and secret_key is not None:
27
+ secret = secret_key
28
+ elif secret is None:
29
+ raise ValueError("Either 'secret' or 'secret_key' must be provided")
30
+
31
+ key = secret
32
+ if isinstance(secret, JWK):
33
+ key = secret._to_keylike()
34
+
35
+ return JWT(
36
+ alg=alg,
37
+ secret_key=key,
38
+ password=password,
39
+ list=list,
40
+ serializer=serializer,
41
+ logger=logger,
42
+ )
43
+
44
+
45
+ def create_jws_instance(
46
+ alg: str,
47
+ key: Any = None,
48
+ password: bytes | None = None,
49
+ logger: BaseLogger = logger,
50
+ **kwargs: Any,
51
+ ) -> JWS:
52
+ """Create JWS instance."""
53
+ if key is None:
54
+ raise ValueError("'key' must be provided")
55
+
56
+ from jam.jose.jwk import JWK as JWKClass
57
+
58
+ if isinstance(key, JWKClass):
59
+ key = key._to_keylike()
60
+
61
+ return JWS(
62
+ alg=alg,
63
+ key=key,
64
+ password=password,
65
+ logger=logger,
66
+ )
67
+
68
+
69
+ def create_jwe_instance(
70
+ alg: str,
71
+ enc: str,
72
+ key: Any = None,
73
+ password: bytes | None = None,
74
+ serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
75
+ logger: BaseLogger = logger,
76
+ **kwargs: Any,
77
+ ) -> JWE:
78
+ """Create JWE instance."""
79
+ if key is None:
80
+ raise ValueError("'key' must be provided")
81
+
82
+ from jam.jose.jwk import JWK as JWKClass
83
+
84
+ if isinstance(key, JWKClass):
85
+ key = key._to_keylike()
86
+
87
+ return JWE(
88
+ alg=alg,
89
+ enc=enc,
90
+ key=key,
91
+ password=password,
92
+ serializer=serializer,
93
+ logger=logger,
94
+ )
95
+
96
+
97
+ create_instance = create_jwt_instance
98
+
99
+
100
+ __all__ = [
101
+ "JWK",
102
+ "JWKSet",
103
+ "JWS",
104
+ "JWE",
105
+ "JWT",
106
+ "JWKRSA",
107
+ "JWKEC",
108
+ "JWKOct",
109
+ "create_instance",
110
+ "create_jwt_instance",
111
+ "create_jws_instance",
112
+ "create_jwe_instance",
113
+ ]
@@ -360,7 +360,7 @@ class JWT(BaseJWT):
360
360
  ) -> dict[str, Any]:
361
361
  now = int(datetime.now().timestamp())
362
362
  payload = {
363
- "tid": str(uuid4()),
363
+ "jti": str(uuid4()), # TODO: Make it replecable
364
364
  "iat": now,
365
365
  "iss": iss,
366
366
  "sub": sub,
@@ -6,6 +6,7 @@ import os
6
6
  import re
7
7
  import sys
8
8
  from typing import Any
9
+ import warnings
9
10
 
10
11
  from jam.encoders import BaseEncoder, JsonEncoder
11
12
  from jam.exceptions import JamConfigurationError
@@ -351,18 +352,48 @@ def __config_maker__(
351
352
  ext = config.split(".")[-1].lower()
352
353
  match ext:
353
354
  case "yml" | "yaml":
354
- return __yaml_config_parser(path=config, pointer=pointer).copy()
355
+ result = __yaml_config_parser(
356
+ path=config, pointer=pointer
357
+ ).copy()
355
358
  case "toml":
356
- return __toml_config_parser(path=config, pointer=pointer).copy()
359
+ result = __toml_config_parser(
360
+ path=config, pointer=pointer
361
+ ).copy()
357
362
  case "json":
358
- return __json_config_parser(path=config).copy()
363
+ result = __json_config_parser(path=config).copy()
359
364
  case _:
360
365
  raise JamConfigurationError(
361
366
  message="YML/YAML, TOML or JSON configs only!",
362
367
  error_code="configuration.invalid_config_type",
363
368
  )
364
369
  else:
365
- return config.copy()
370
+ result = config.copy()
371
+
372
+ result = __apply_jwt_config_migration__(result)
373
+ return result
374
+
375
+
376
+ # TODO: Delete this after deleting jam.jwt
377
+ def __apply_jwt_config_migration__(config: dict) -> dict:
378
+ """Backward compatibility: copy jam.jwt -> jam.jose.jwt with deprecation warning.
379
+
380
+ Args:
381
+ config: Parsed configuration dict
382
+
383
+ Returns:
384
+ dict: Updated config with migration applied
385
+ """
386
+ if "jwt" in config and "jose" not in config:
387
+ warnings.warn(
388
+ "[jam.jwt] is deprecated. Use [jam.jose.jwt] instead. See: https://jam.makridenko.ru/usage/jose",
389
+ DeprecationWarning,
390
+ stacklevel=2,
391
+ )
392
+ config["jose"] = {"jwt": config["jwt"]}
393
+ elif "jwt" in config and "jose" in config:
394
+ if "jwt" not in config.get("jose", {}):
395
+ config["jose"]["jwt"] = config["jwt"]
396
+ return config
366
397
 
367
398
 
368
399
  def __module_loader__(path: str) -> Callable:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jamlib
3
- Version: 3.2.0b2
3
+ Version: 3.2.0b3
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
@@ -1,55 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- """JOSE tools."""
4
-
5
- from typing import Any
6
-
7
- from jam.jose.jwk import JWK, JWKOct, JWKSet, JWKRSA, JWKEC
8
- from jam.jose.jws import JWS
9
- from jam.jose.jwe import JWE
10
- from jam.jose.jwt import JWT
11
- from jam.logger import BaseLogger, logger
12
- from jam.encoders import BaseEncoder, JsonEncoder
13
-
14
-
15
- def create_instance(
16
- alg: str,
17
- secret: Any = None,
18
- secret_key: Any = None,
19
- password: str | bytes | None = None,
20
- list: dict[str, Any] | None = None,
21
- logger: BaseLogger = logger,
22
- serializer: BaseEncoder | type[BaseEncoder] = JsonEncoder,
23
- **kwargs: Any,
24
- ) -> JWT:
25
- """Create JWT instance."""
26
- if secret is None and secret_key is not None:
27
- secret = secret_key
28
- elif secret is None:
29
- raise ValueError("Either 'secret' or 'secret_key' must be provided")
30
-
31
- key = secret
32
- if isinstance(secret, JWK):
33
- key = secret._to_keylike()
34
-
35
- return JWT(
36
- alg=alg,
37
- secret_key=key,
38
- password=password,
39
- list=list,
40
- serializer=serializer,
41
- logger=logger,
42
- )
43
-
44
-
45
- __all__ = [
46
- "JWK",
47
- "JWKSet",
48
- "JWS",
49
- "JWE",
50
- "JWT",
51
- "JWKRSA",
52
- "JWKEC",
53
- "JWKOct",
54
- "create_instance",
55
- ]
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
File without changes
File without changes
File without changes