django-pfx 1.4.dev24__tar.gz → 1.4.dev28__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 (138) hide show
  1. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/PKG-INFO +1 -1
  2. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/SOURCES.txt +1 -0
  4. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/decorator/rest.py +1 -3
  5. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/makeapidoc.py +1 -2
  6. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/authentication.py +27 -10
  7. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/profiling.py +1 -2
  8. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/login_ban.py +1 -3
  9. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/otp_user_mixin.py +1 -3
  10. django-pfx-1.4.dev28/pfx/pfxcore/settings.py +22 -0
  11. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/shortcuts.py +1 -3
  12. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/authentication_views.py +13 -13
  13. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/__init__.py +1 -0
  14. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_auth_api.py +6 -6
  15. django-pfx-1.4.dev28/tests/tests/test_settings.py +23 -0
  16. django-pfx-1.4.dev24/pfx/pfxcore/settings.py +0 -11
  17. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.gitignore +0 -0
  18. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.gitlab-ci.yml +0 -0
  19. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.pre-commit-config.yaml +0 -0
  20. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/LICENSE +0 -0
  21. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/MANIFEST.in +0 -0
  22. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/README.md +0 -0
  23. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django-admin-test +0 -0
  24. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/dependency_links.txt +0 -0
  25. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/requires.txt +0 -0
  26. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/top_level.txt +0 -0
  27. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/Makefile +0 -0
  28. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/conf.py +0 -0
  29. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/index.rst +0 -0
  30. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/api.views.rst +0 -0
  31. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/authentication.md +0 -0
  32. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/decorator.md +0 -0
  33. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/generate_openapi.md +0 -0
  34. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/getting_started.md +0 -0
  35. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/internationalisation.md +0 -0
  36. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/model.md +0 -0
  37. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/pfx_views.md +0 -0
  38. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/profiling.md +0 -0
  39. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/settings.md +0 -0
  40. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/testing.md +0 -0
  41. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/img/pfx.png +0 -0
  42. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/img/pfx.svg +0 -0
  43. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/__init__.py +0 -0
  44. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/__init__.py +0 -0
  45. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/__init__.py +0 -0
  46. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/parameters.py +0 -0
  47. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/schema.py +0 -0
  48. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/tags.py +0 -0
  49. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apps.py +0 -0
  50. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/decorator/__init__.py +0 -0
  51. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/default_settings.py +0 -0
  52. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/exceptions.py +0 -0
  53. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/fields.py +0 -0
  54. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/http/__init__.py +0 -0
  55. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/http/json_response.py +0 -0
  56. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  57. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
  58. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/__init__.py +0 -0
  59. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/__init__.py +0 -0
  60. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/profile.py +0 -0
  61. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/__init__.py +0 -0
  62. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/locale.py +0 -0
  63. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/__init__.py +0 -0
  64. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  65. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/cache_mixins.py +0 -0
  66. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/not_null_fields.py +0 -0
  67. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/pfx_models.py +0 -0
  68. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  69. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/serializers/__init__.py +0 -0
  70. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/serializers/json.py +0 -0
  71. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/storage/__init__.py +0 -0
  72. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/storage/s3_storage.py +0 -0
  73. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  74. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  75. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  76. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  77. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  78. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  79. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/test.py +0 -0
  80. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/urls.py +0 -0
  81. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/__init__.py +0 -0
  82. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/fields.py +0 -0
  83. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/filters_views.py +0 -0
  84. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/locale_views.py +0 -0
  85. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  86. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  87. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/groups.py +0 -0
  88. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  89. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  90. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  91. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  92. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  93. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  94. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  95. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  96. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  97. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset.py +0 -0
  98. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  99. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  100. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  101. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  102. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  103. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/rest_views.py +0 -0
  104. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pyproject.toml +0 -0
  105. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/requirements.txt +0 -0
  106. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/runtest.py +0 -0
  107. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/serve-doc +0 -0
  108. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/setup.cfg +0 -0
  109. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/setup.py +0 -0
  110. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/__init__.py +0 -0
  111. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  112. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/models.py +0 -0
  113. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/__init__.py +0 -0
  114. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/ci.py +0 -0
  115. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/common.py +0 -0
  116. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev.py +0 -0
  117. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev_custom_example.py +0 -0
  118. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev_default.py +0 -0
  119. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/basic_api_errors.py +0 -0
  120. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/basic_api_test.py +0 -0
  121. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_api_doc.py +0 -0
  122. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_body_mixin.py +0 -0
  123. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_cache.py +0 -0
  124. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_client.py +0 -0
  125. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_fields.py +0 -0
  126. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_filters.py +0 -0
  127. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_locale_api.py +0 -0
  128. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_perm_tests.py +0 -0
  129. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_perms_api.py +0 -0
  130. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_profiling_middleware.py +0 -0
  131. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_shortcuts.py +0 -0
  132. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_timezone_middleware.py +0 -0
  133. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_tools.py +0 -0
  134. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_user_queryset.py +0 -0
  135. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_view_decorators.py +0 -0
  136. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_view_fields.py +0 -0
  137. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/urls.py +0 -0
  138. {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev24
3
+ Version: 1.4.dev28
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev24
3
+ Version: 1.4.dev28
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -127,6 +127,7 @@ tests/tests/test_locale_api.py
127
127
  tests/tests/test_perm_tests.py
128
128
  tests/tests/test_perms_api.py
129
129
  tests/tests/test_profiling_middleware.py
130
+ tests/tests/test_settings.py
130
131
  tests/tests/test_shortcuts.py
131
132
  tests/tests/test_timezone_middleware.py
132
133
  tests/tests/test_tools.py
@@ -7,11 +7,9 @@ from apispec.utils import deepupdate
7
7
 
8
8
  from pfx.pfxcore.exceptions import APIError
9
9
  from pfx.pfxcore.http import JsonResponse
10
- from pfx.pfxcore.settings import PFXSettings
10
+ from pfx.pfxcore.settings import settings
11
11
  from pfx.pfxcore.test import format_request
12
12
 
13
- settings = PFXSettings()
14
-
15
13
  logger = logging.getLogger(__name__)
16
14
 
17
15
 
@@ -11,10 +11,9 @@ from apispec.yaml_utils import load_operations_from_docstring
11
11
 
12
12
  from pfx.pfxcore import __PFX_VIEWS__
13
13
  from pfx.pfxcore.apidoc import __PARAMETERS__, __TAGS__, ParameterGroup
14
- from pfx.pfxcore.settings import PFXSettings
14
+ from pfx.pfxcore.settings import settings
15
15
  from pfx.pfxcore.views import FilterGroup, ModelMixin
16
16
 
17
- settings = PFXSettings()
18
17
  DEFAULT_TEMPLATE = dict(
19
18
  title="PFX API",
20
19
  version="1.0.0",
@@ -30,13 +30,29 @@ class JWTTokenDecodeMixin:
30
30
  return user
31
31
 
32
32
  @classmethod
33
- def decode_jwt(cls, token, otp_login=False):
33
+ def decode_jwt_header(cls, token):
34
34
  try:
35
35
  headers = jwt.get_unverified_header(token)
36
36
  if 'pfx_user_pk' not in headers:
37
37
  raise jwt.InvalidTokenError(
38
38
  "Missing pfx_user_pk in token headers")
39
- user = cls.get_cached_user(headers['pfx_user_pk'])
39
+ return headers['pfx_user_pk']
40
+ except (jwt.ExpiredSignatureError,
41
+ jwt.InvalidTokenError, jwt.InvalidSignatureError,
42
+ DecodeError) as e:
43
+ # Log these exceptions only in debug mode
44
+ logger.debug(e, exc_info=True)
45
+ raise
46
+ except Exception as e:
47
+ # Always logs unexpected exceptions
48
+ logger.exception(e)
49
+ raise
50
+
51
+ @classmethod
52
+ def decode_jwt(cls, token, otp_login=False):
53
+ user_pk = cls.decode_jwt_header(token)
54
+ try:
55
+ user = cls.get_cached_user(user_pk)
40
56
  decoded = jwt.decode(
41
57
  token,
42
58
  user.get_user_jwt_signature_key() + settings.PFX_SECRET_KEY,
@@ -49,11 +65,12 @@ class JWTTokenDecodeMixin:
49
65
  "This token is reserved for OTP login")
50
66
  return user
51
67
  except (get_user_model().DoesNotExist, jwt.ExpiredSignatureError,
52
- jwt.InvalidTokenError, jwt.InvalidSignatureError) as e:
68
+ jwt.InvalidTokenError, jwt.InvalidSignatureError,
69
+ DecodeError) as e:
53
70
  # Log these exceptions only in debug mode
54
71
  logger.debug(e, exc_info=True)
55
72
  raise
56
- except (DecodeError, Exception) as e:
73
+ except Exception as e:
57
74
  # Always logs unexpected exceptions
58
75
  logger.exception(e)
59
76
  raise
@@ -72,11 +89,11 @@ class AuthenticationMiddleware(JWTTokenDecodeMixin, MiddlewareMixin):
72
89
  authorization = request.headers.get('Authorization')
73
90
  if authorization:
74
91
  try:
75
- _, key = authorization.split("Bearer ")
92
+ _, token = authorization.split("Bearer ")
76
93
  except ValueError:
77
- key = None
94
+ token = ""
78
95
  try:
79
- request.user = self.decode_jwt(key)
96
+ request.user = self.decode_jwt(token)
80
97
  except Exception:
81
98
  request.user = AnonymousUser()
82
99
  else:
@@ -102,10 +119,10 @@ class CookieAuthenticationMiddleware(JWTTokenDecodeMixin, MiddlewareMixin):
102
119
  """
103
120
 
104
121
  def process_request(self, request):
105
- key = request.COOKIES.get('token')
106
- if key:
122
+ token = request.COOKIES.get('token', "")
123
+ if token:
107
124
  try:
108
- request.user = self.decode_jwt(key)
125
+ request.user = self.decode_jwt(token)
109
126
  except Exception:
110
127
  request.user = AnonymousUser()
111
128
  request.delete_cookie = True
@@ -11,10 +11,9 @@ from django.utils.deprecation import MiddlewareMixin
11
11
 
12
12
  import sqlparse
13
13
 
14
- from pfx.pfxcore.settings import PFXSettings
14
+ from pfx.pfxcore.settings import settings
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
- settings = PFXSettings()
18
17
  rid = ContextVar('var')
19
18
 
20
19
 
@@ -4,9 +4,7 @@ from django.db import models
4
4
  from django.utils import timezone
5
5
  from django.utils.translation import gettext_lazy as _
6
6
 
7
- from pfx.pfxcore.settings import PFXSettings
8
-
9
- settings = PFXSettings()
7
+ from pfx.pfxcore.settings import settings
10
8
 
11
9
 
12
10
  class LoginBanQuerySet(models.QuerySet):
@@ -4,9 +4,7 @@ from django.db import models
4
4
  from django.utils import timezone
5
5
  from django.utils.translation import gettext_lazy as _
6
6
 
7
- from pfx.pfxcore.settings import PFXSettings
8
-
9
- settings = PFXSettings()
7
+ from pfx.pfxcore.settings import settings
10
8
 
11
9
 
12
10
  class OtpUserMixin(models.Model):
@@ -0,0 +1,22 @@
1
+ from django.conf import settings as django_settings
2
+ from django.core.exceptions import ImproperlyConfigured
3
+
4
+ from . import default_settings
5
+
6
+
7
+ class PFXSettings:
8
+ def __getattr__(self, name):
9
+ try:
10
+ val = getattr(django_settings, name)
11
+ except AttributeError:
12
+ if hasattr(default_settings, name):
13
+ val = getattr(default_settings, name)
14
+ else:
15
+ raise
16
+ if name == "PFX_SECRET_KEY" and not val:
17
+ raise ImproperlyConfigured(
18
+ "The PFX_SECRET_KEY setting must not be empty.")
19
+ return val
20
+
21
+
22
+ settings = PFXSettings()
@@ -5,9 +5,7 @@ from datetime import date
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.utils.translation import gettext_lazy as _
7
7
 
8
- from pfx.pfxcore.settings import PFXSettings
9
-
10
- settings = PFXSettings()
8
+ from .settings import settings
11
9
 
12
10
  logger = logging.getLogger(__name__)
13
11
 
@@ -32,7 +32,7 @@ from pfx.pfxcore.http import JsonResponse
32
32
  from pfx.pfxcore.middleware.authentication import JWTTokenDecodeMixin
33
33
  from pfx.pfxcore.models import CacheableMixin, OtpUserMixin
34
34
  from pfx.pfxcore.models.login_ban import LoginBan
35
- from pfx.pfxcore.settings import PFXSettings
35
+ from pfx.pfxcore.settings import settings
36
36
  from pfx.pfxcore.shortcuts import delete_token_cookie
37
37
 
38
38
  from .rest_views import (
@@ -42,7 +42,6 @@ from .rest_views import (
42
42
  ModelMixin,
43
43
  )
44
44
 
45
- settings = PFXSettings()
46
45
  logger = logging.getLogger(__name__)
47
46
  UserModel = get_user_model()
48
47
  AUTHENTICATION_TAG = Tag("Authentication")
@@ -261,9 +260,7 @@ class AuthenticationView(
261
260
  return jwt.encode(
262
261
  payload,
263
262
  user.get_user_jwt_signature_key() + settings.PFX_SECRET_KEY,
264
- headers=dict(
265
- pfx_user_pk=user.pk,
266
- username=user.get_username()),
263
+ headers=dict(pfx_user_pk=user.pk),
267
264
  algorithm="HS256")
268
265
 
269
266
  def get_extra_payload(self, user):
@@ -559,9 +556,13 @@ class AuthenticationView(
559
556
  description: If the OTP is disabled for this user.
560
557
  """
561
558
  data = self.deserialize_body()
562
- token = data.get('token')
563
- username = jwt.get_unverified_header(token).get('username')
564
- ban_dt = LoginBan.objects.is_ban(username)
559
+ token = data.get('token', "")
560
+ try:
561
+ user_pk = self.decode_jwt_header(token)
562
+ ban_key = f"otp_{user_pk}"
563
+ except Exception:
564
+ raise UnauthorizedError()
565
+ ban_dt = LoginBan.objects.is_ban(ban_key)
565
566
  if ban_dt:
566
567
  return self.login_ban_response(ban_dt)
567
568
  try:
@@ -574,9 +575,9 @@ class AuthenticationView(
574
575
  if not user.otp_secret_token:
575
576
  raise ForbiddenError()
576
577
  if user.is_otp_valid(data.get('otp_code')):
577
- LoginBan.objects.unban(username)
578
+ LoginBan.objects.unban(ban_key)
578
579
  return self._login_success(user, mode, remember_me)
579
- LoginBan.objects.ban(username)
580
+ LoginBan.objects.ban(ban_key)
580
581
  return self.login_failed_response()
581
582
 
582
583
  def get_user(self, uidb64):
@@ -836,9 +837,8 @@ class OtpEmailView(BodyMixin, JWTTokenDecodeMixin, BaseRestView):
836
837
  data = self.deserialize_body()
837
838
  try:
838
839
  user, __, __ = self.decode_jwt(
839
- data.get('token'), otp_login=True)
840
- except Exception as e:
841
- logger.exception(e)
840
+ data.get('token', ""), otp_login=True)
841
+ except Exception:
842
842
  raise UnauthorizedError()
843
843
  else:
844
844
  user = self.request.user
@@ -11,6 +11,7 @@ from .test_locale_api import LocaleAPITest
11
11
  from .test_perm_tests import PermTestsTest
12
12
  from .test_perms_api import PermsAPITest
13
13
  from .test_profiling_middleware import ProfilingMiddlewareTest
14
+ from .test_settings import TestSettings
14
15
  from .test_shortcuts import ShortcutTest
15
16
  from .test_timezone_middleware import TimezoneMiddlewareTest
16
17
  from .test_tools import TestTools
@@ -1287,20 +1287,20 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
1287
1287
  code_match = re.search(
1288
1288
  r'Authentication code: (\d{6})', mail.outbox[0].body)
1289
1289
  self.assertIsNotNone(code_match)
1290
- otp_code = int(code_match.group(1))
1290
+ otp_code = code_match.group(1)
1291
1291
 
1292
- with freeze_time("2023-05-01 08:15:01"):
1292
+ with freeze_time("2023-05-01 08:15:00"):
1293
1293
  response = self.client.post('/api/auth/otp/login', dict(
1294
1294
  token=otp_token,
1295
1295
  otp_code=otp_code))
1296
- self.assertRC(response, 422)
1296
+ self.assertRC(response, 200)
1297
+ self.assertJEExists(response, 'token')
1297
1298
 
1298
- with freeze_time("2023-05-01 08:15:00"):
1299
+ with freeze_time("2023-05-01 08:15:01"):
1299
1300
  response = self.client.post('/api/auth/otp/login', dict(
1300
1301
  token=otp_token,
1301
1302
  otp_code=otp_code))
1302
- self.assertRC(response, 200)
1303
- self.assertJEExists(response, 'token')
1303
+ self.assertRC(response, 422)
1304
1304
 
1305
1305
  @override_settings(
1306
1306
  PFX_HOTP_CODE_VALIDITY=15,
@@ -0,0 +1,23 @@
1
+ from datetime import timedelta
2
+
3
+ from django.core.exceptions import ImproperlyConfigured
4
+ from django.test import TestCase, override_settings
5
+
6
+ from pfx.pfxcore.test import TestAssertMixin
7
+ from pfx.pfxcore.views import AuthenticationView
8
+ from tests.models import User
9
+
10
+
11
+ class TestSettings(TestAssertMixin, TestCase):
12
+
13
+ @override_settings(PFX_SECRET_KEY="")
14
+ def test_mandatory_pfx_secret_key(self):
15
+ user = User.objects.create_user(
16
+ username='user',
17
+ email="user@example.com",
18
+ password='test',
19
+ first_name='User',
20
+ last_name='Test')
21
+
22
+ with self.assertRaises(ImproperlyConfigured):
23
+ AuthenticationView()._prepare_token(user, timedelta())
@@ -1,11 +0,0 @@
1
- from django.conf import settings
2
-
3
- from . import default_settings
4
-
5
-
6
- class PFXSettings:
7
- def __getattr__(self, attr):
8
- try:
9
- return getattr(settings, attr)
10
- except AttributeError:
11
- return getattr(default_settings, attr)
File without changes
File without changes
File without changes
File without changes
File without changes