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.
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/PKG-INFO +1 -1
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/PKG-INFO +1 -1
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/SOURCES.txt +1 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/decorator/rest.py +1 -3
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/makeapidoc.py +1 -2
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/authentication.py +27 -10
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/profiling.py +1 -2
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/login_ban.py +1 -3
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/otp_user_mixin.py +1 -3
- django-pfx-1.4.dev28/pfx/pfxcore/settings.py +22 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/shortcuts.py +1 -3
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/authentication_views.py +13 -13
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/__init__.py +1 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_auth_api.py +6 -6
- django-pfx-1.4.dev28/tests/tests/test_settings.py +23 -0
- django-pfx-1.4.dev24/pfx/pfxcore/settings.py +0 -11
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.gitignore +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.gitlab-ci.yml +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/.pre-commit-config.yaml +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/LICENSE +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/MANIFEST.in +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/README.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django-admin-test +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/requires.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/django_pfx.egg-info/top_level.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/Makefile +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/conf.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/index.rst +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/api.views.rst +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/authentication.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/decorator.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/generate_openapi.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/getting_started.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/internationalisation.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/model.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/pfx_views.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/profiling.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/settings.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/doc/source/testing.md +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/img/pfx.png +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/img/pfx.svg +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/apps.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/default_settings.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/exceptions.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/http/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/http/json_response.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/middleware/locale.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/serializers/json.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/storage/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/test.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/urls.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/filters_views.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/locale_views.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/rest_views.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pyproject.toml +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/requirements.txt +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/runtest.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/serve-doc +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/setup.cfg +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/setup.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/models.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/__init__.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/ci.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/common.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev_custom_example.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/settings/dev_default.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/basic_api_errors.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/basic_api_test.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_api_doc.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_body_mixin.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_cache.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_client.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_filters.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_locale_api.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_perm_tests.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_perms_api.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_profiling_middleware.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_shortcuts.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_timezone_middleware.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_tools.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_user_queryset.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_view_decorators.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/tests/test_view_fields.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/urls.py +0 -0
- {django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/tests/views.py +0 -0
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
_,
|
|
92
|
+
_, token = authorization.split("Bearer ")
|
|
76
93
|
except ValueError:
|
|
77
|
-
|
|
94
|
+
token = ""
|
|
78
95
|
try:
|
|
79
|
-
request.user = self.decode_jwt(
|
|
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
|
-
|
|
106
|
-
if
|
|
122
|
+
token = request.COOKIES.get('token', "")
|
|
123
|
+
if token:
|
|
107
124
|
try:
|
|
108
|
-
request.user = self.decode_jwt(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
564
|
-
|
|
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(
|
|
578
|
+
LoginBan.objects.unban(ban_key)
|
|
578
579
|
return self._login_success(user, mode, remember_me)
|
|
579
|
-
LoginBan.objects.ban(
|
|
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
|
|
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 =
|
|
1290
|
+
otp_code = code_match.group(1)
|
|
1291
1291
|
|
|
1292
|
-
with freeze_time("2023-05-01 08:15:
|
|
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,
|
|
1296
|
+
self.assertRC(response, 200)
|
|
1297
|
+
self.assertJEExists(response, 'token')
|
|
1297
1298
|
|
|
1298
|
-
with freeze_time("2023-05-01 08:15:
|
|
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,
|
|
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())
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/models/user_filtered_queryset_mixin.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/otp_code_email.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_email.txt
RENAMED
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/templates/registration/welcome_subject.txt
RENAMED
|
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
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/media_redirect.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_size.py
RENAMED
|
File without changes
|
{django-pfx-1.4.dev24 → django-pfx-1.4.dev28}/pfx/pfxcore/views/parameters/subset_page_subset.py
RENAMED
|
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
|
|
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
|