cardo-python-utils 0.5.dev55__py3-none-any.whl → 0.5.1__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cardo-python-utils
3
- Version: 0.5.dev55
3
+ Version: 0.5.1
4
4
  Summary: Python library enhanced with a wide range of functions for different scenarios.
5
5
  Author-email: CardoAI <hello@cardoai.com>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- cardo_python_utils-0.5.dev55.dist-info/licenses/LICENSE,sha256=N-YtxDy8n5A1Mo7JKKItNIlboiK_pMOZ48ojx76jo3g,1046
1
+ cardo_python_utils-0.5.1.dist-info/licenses/LICENSE,sha256=N-YtxDy8n5A1Mo7JKKItNIlboiK_pMOZ48ojx76jo3g,1046
2
2
  python_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  python_utils/choices.py,sha256=_sLNkSnQqhg55gGKNRsOQCJ75W6gnz8J8Q00528MEYk,2548
4
4
  python_utils/data_structures.py,sha256=ZqkZYPy20zyGYOVhwb9qst4vF_P7X2A9z5E36rMUC6I,16820
@@ -24,8 +24,8 @@ python_utils/django/admin/views.py,sha256=D4Ez-RkZa_c7O8AN1ljosBBJWieSodrY2TAZ7-
24
24
  python_utils/django/admin/templates/__init__.py,sha256=LxCKcnJ1Ty48CFDJ8XtAZYTW45xDw5o7H4GLsRo6iQk,53
25
25
  python_utils/django/admin/templates/user_groups_changelist.html,sha256=KbO6bsH-nh3DDYCq4UB8j25NdjLP_nh_GuGZ4lYSarM,182
26
26
  python_utils/django/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- python_utils/django/api/drf.py,sha256=ar94uAVIaHDxzi3K7ubNSJDuysUWjV_xv0sFXtkJEuc,3101
28
- python_utils/django/api/ninja.py,sha256=Rz7331hYmKo4jyxtuvEKzOaNUjdmuCpXeHYkWGf0NsA,4863
27
+ python_utils/django/api/drf.py,sha256=jYiOkU3laT_JnIMjyArn6-ILARq-UsLJIOm5v9NnyBc,3002
28
+ python_utils/django/api/ninja.py,sha256=FnH0oAjznKAUVnGCSihiCwNmiH3FarTneqjPbH0wZo0,4793
29
29
  python_utils/django/api/utils.py,sha256=ycpSnTtGcfdGP1_Hk0P2c8ZNId70xOYtjx1m0nAUWRM,3465
30
30
  python_utils/django/auth/service.py,sha256=ggv_qc5B3vmoamoqLiEvU5XWJWcVr57rL0INz5826HE,7485
31
31
  python_utils/django/celery/__init__.py,sha256=eqKpBqhClH-7oK-kD1SUEpzt4Gqu7VWLWmhFUktee0A,79
@@ -44,7 +44,7 @@ python_utils/django/management/commands/showmigrations.py,sha256=mwNnL2EYAqX3iZ5
44
44
  python_utils/django/management/commands/tenant_aware_command.py,sha256=MBI7N0BEHZAhK183QmFjjEdDecM060O0nsxg0spH9Mc,2440
45
45
  python_utils/django/middleware/__init__.py,sha256=GyhuzaOtaXW13iB9GL0XF7OvEEzKoJzorJzYX597idA,117
46
46
  python_utils/django/middleware/tenant_aware_http_middleware.py,sha256=ifEkUtGtd9wEyBrPWzUhysSaUM2BkkNu8dnIpabhXI0,3950
47
- python_utils/django/middleware/tenant_aware_websocket_middleware.py,sha256=k6bn4pS4uZcTPafF7Yz_8VAj0vchEfJSXM4Bxb-Od-k,4100
47
+ python_utils/django/middleware/tenant_aware_websocket_middleware.py,sha256=2zdrY6LcR8CSnOSaOu76r2gPJSL8nYv1RcWv2rLzueA,4333
48
48
  python_utils/django/migrations/0001_initial.py,sha256=j-y64JqkDv2A1o2YzJB9NE41KFOOiVmQn0-fv9A98WU,4841
49
49
  python_utils/django/migrations/0001_initial_squashed_0005_alter_userrole_id.py,sha256=PveIcroiawK2Mr4Vh0lsj5bjJfyaA98wdSfn_g53WZI,5803
50
50
  python_utils/django/migrations/0002_auto_20220120_1617.py,sha256=TOTmFU8wejKOX-bjq4D1s3B-QXTEub_FOX4STWAcJ4o,378
@@ -64,7 +64,7 @@ python_utils/django/storage/__init__.py,sha256=mNn2YmD7pkXhBLHMM1444BLsCMq78YdYx
64
64
  python_utils/django/storage/tenant_aware_storage.py,sha256=5dDes6xLv7_R8hIBbFIzRvPL7HL9K_RM-G6LI8qUSxM,2550
65
65
  python_utils/django/tests/__init__.py,sha256=Nkt0a7LEHyjLvuEBZ7113VjjAWJlyZlMy-H-JZ5tNcs,252
66
66
  python_utils/django/tests/conftest.py,sha256=KozXmXUWVcDLbkVAb7Aq4sDydGLh2YZkbRa4tkA8Z6U,3167
67
- cardo_python_utils-0.5.dev55.dist-info/METADATA,sha256=493CmMRlAB8Nmk60wyMjUXF6iNUuAVhs9dnOs4Qx3lU,3007
68
- cardo_python_utils-0.5.dev55.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
69
- cardo_python_utils-0.5.dev55.dist-info/top_level.txt,sha256=zAx6OfEsjJs8BEW3okSiG_j9gpkI69xWShzum6oBgKI,13
70
- cardo_python_utils-0.5.dev55.dist-info/RECORD,,
67
+ cardo_python_utils-0.5.1.dist-info/METADATA,sha256=5lttaasWWF_75Ig27FONaPSKC3ublt8ikBwZnljH7qU,3003
68
+ cardo_python_utils-0.5.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
69
+ cardo_python_utils-0.5.1.dist-info/top_level.txt,sha256=zAx6OfEsjJs8BEW3okSiG_j9gpkI69xWShzum6oBgKI,13
70
+ cardo_python_utils-0.5.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (82.0.0)
2
+ Generator: setuptools (82.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,5 +1,5 @@
1
1
  from django.conf import settings
2
- from jwt.exceptions import ExpiredSignatureError, InvalidTokenError, PyJWKClientError
2
+ from jwt.exceptions import ExpiredSignatureError, PyJWTError
3
3
 
4
4
  from rest_framework import authentication
5
5
  from rest_framework.exceptions import AuthenticationFailed, PermissionDenied
@@ -16,19 +16,17 @@ class AuthenticationBackend(authentication.TokenAuthentication):
16
16
  payload = decode_jwt(token, audience=self._get_audience())
17
17
  except ExpiredSignatureError as e:
18
18
  raise AuthenticationFailed("Token has expired.") from e
19
- except (InvalidTokenError, PyJWKClientError) as e:
19
+ except PyJWTError as e:
20
20
  raise PermissionDenied(f"Invalid token: {str(e)}") from e
21
21
 
22
22
  try:
23
23
  username = payload["preferred_username"]
24
24
  except KeyError as e:
25
- raise PermissionDenied(
26
- "Invalid token: preferred_username not present."
27
- ) from e
25
+ raise PermissionDenied("Invalid token: preferred_username not present.") from e
28
26
 
29
27
  user = create_or_update_user(username, payload)
30
28
  return user, payload
31
-
29
+
32
30
  def _get_audience(self):
33
31
  """
34
32
  Allows subclasses to override the audience used for JWT decoding.
@@ -50,9 +48,9 @@ class HasScope(BasePermission):
50
48
  allowed_scopes = ["jobs"]
51
49
  ...
52
50
 
53
- It is possible to define different scopes per HTTP method
51
+ It is possible to define different scopes per HTTP method
54
52
  by setting `allowed_scopes` as a dict:
55
-
53
+
56
54
  class MyApiView(APIView):
57
55
  permission_classes = [IsAuthenticated, HasScope]
58
56
  allowed_scopes = {
@@ -73,7 +71,7 @@ class HasScope(BasePermission):
73
71
  f"No allowed_scopes defined on the view '{view.__class__.__name__}'. "
74
72
  "Define allowed_scopes or set it to '*' to allow any scope."
75
73
  )
76
-
74
+
77
75
  if isinstance(allowed_scopes, dict):
78
76
  allowed_scopes = allowed_scopes.get(request.method.lower(), [])
79
77
 
@@ -1,12 +1,12 @@
1
1
  import logging
2
2
  from typing import Literal, Optional, Union
3
3
 
4
- from jwt.exceptions import ExpiredSignatureError, InvalidTokenError, PyJWKClientError
4
+ from jwt.exceptions import ExpiredSignatureError, PyJWTError
5
5
 
6
6
  from django.conf import settings
7
7
  from django.http import HttpRequest
8
8
  from ninja.security import HttpBearer
9
- from ninja.errors import AuthenticationError, AuthorizationError, HttpError
9
+ from ninja.errors import AuthenticationError, HttpError
10
10
 
11
11
  from .utils import (
12
12
  acreate_or_update_user,
@@ -63,14 +63,14 @@ class AuthBearer(HttpBearer):
63
63
  return decode_jwt(token)
64
64
  except ExpiredSignatureError as e:
65
65
  raise AuthenticationError("Token has expired.") from e
66
- except (InvalidTokenError, PyJWKClientError) as e:
67
- raise AuthorizationError(f"Invalid token: {str(e)}") from e
66
+ except PyJWTError as e:
67
+ raise AuthenticationError(f"Invalid token: {str(e)}") from e
68
68
 
69
69
  def _get_username(self, payload: TokenPayload) -> str:
70
70
  try:
71
71
  return payload["preferred_username"]
72
72
  except KeyError as e:
73
- raise AuthorizationError("Invalid token: 'preferred_username' claim not present.") from e
73
+ raise AuthenticationError("Invalid token: 'preferred_username' claim not present.") from e
74
74
 
75
75
  def _verify_scopes(self, request, token_payload):
76
76
  allowed_scopes = self._get_view_allowed_scopes(request)
@@ -3,6 +3,7 @@ from urllib.parse import parse_qs
3
3
 
4
4
  from django.conf import settings
5
5
  from django.contrib.auth import get_user_model
6
+ from jwt.exceptions import PyJWTError
6
7
 
7
8
  from ..api.utils import decode_jwt, TokenPayload
8
9
  from ..settings import DEVELOPMENT_TENANT
@@ -50,7 +51,13 @@ class TenantAwareWebsocketMiddleware:
50
51
  tenant = self._get_tenant(scope)
51
52
 
52
53
  async with TenantContext(tenant):
53
- token_payload: TokenPayload = decode_jwt(access_token)
54
+ try:
55
+ token_payload: TokenPayload = decode_jwt(access_token)
56
+ except PyJWTError as e:
57
+ logger.info(f"Failed to decode JWT token: {e}")
58
+ await self._reject_connection(send, f"Invalid authorization token: {str(e)}")
59
+ return
60
+
54
61
  username = token_payload.get("preferred_username")
55
62
  if not username:
56
63
  await self._reject_connection(send, "Username cannot be extracted from the token")
@@ -84,9 +91,7 @@ class TenantAwareWebsocketMiddleware:
84
91
  Returns:
85
92
  None
86
93
  """
87
- await send(
88
- {"type": "websocket.close", "code": 4000, "reason": reason}
89
- ) # Custom close code for rejection
94
+ await send({"type": "websocket.close", "code": 4000, "reason": reason}) # Custom close code for rejection
90
95
 
91
96
  def _get_tenant(self, scope) -> str:
92
97
  """
@@ -110,9 +115,7 @@ class TenantAwareWebsocketMiddleware:
110
115
  logger.debug(f"Tenant '{tenant}' extracted from websocket host: {host}")
111
116
  return tenant
112
117
 
113
- raise Exception(
114
- f"Could not determine tenant from websocket subdomain. Host: {host}"
115
- )
118
+ raise Exception(f"Could not determine tenant from websocket subdomain. Host: {host}")
116
119
 
117
120
  @staticmethod
118
121
  def _get_host_from_scope(scope) -> str: