django-pfx 1.4.dev30__tar.gz → 1.4.dev38__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.dev30 → django_pfx-1.4.dev38}/.gitlab-ci.yml +20 -7
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/PKG-INFO +1 -1
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/PKG-INFO +1 -1
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/SOURCES.txt +6 -2
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/top_level.txt +1 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/authentication.md +4 -4
- django_pfx-1.4.dev38/make_messages +3 -0
- django_pfx-1.4.dev38/manage.py +37 -0
- django_pfx-1.4.dev38/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +35 -34
- django_pfx-1.4.dev38/pfx/pfxcore/migrations/0001_initial.py +59 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/otp_user_mixin.py +5 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/authentication_views.py +26 -21
- django_pfx-1.4.dev38/pfx/settings/dev.py +8 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/setup.cfg +1 -0
- django_pfx-1.4.dev38/tests/__init__.py +0 -0
- django_pfx-1.4.dev38/tests/settings/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_auth_api.py +18 -13
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_shortcuts.py +1 -1
- django-pfx-1.4.dev30/django-admin-test +0 -10
- django-pfx-1.4.dev30/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- django-pfx-1.4.dev30/runtest.py +0 -17
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/.gitignore +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/.pre-commit-config.yaml +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/LICENSE +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/MANIFEST.in +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/README.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/requires.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/Makefile +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/conf.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/index.rst +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/api.views.rst +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/decorator.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/generate_openapi.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/getting_started.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/internationalisation.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/model.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/pfx_views.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/profiling.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/settings.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/testing.md +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/img/pfx.png +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/img/pfx.svg +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apps.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/rest.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/default_settings.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/exceptions.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/http/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/http/json_response.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/locale.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django-pfx-1.4.dev30/pfx/pfxcore/serializers → django_pfx-1.4.dev38/pfx/pfxcore/migrations}/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/login_ban.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django-pfx-1.4.dev30/tests → django_pfx-1.4.dev38/pfx/pfxcore/serializers}/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/serializers/json.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/settings.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/shortcuts.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/test.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/urls.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/filters_views.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/locale_views.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/rest_views.py +0 -0
- {django-pfx-1.4.dev30/tests → django_pfx-1.4.dev38/pfx}/settings/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pyproject.toml +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/requirements.txt +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/serve-doc +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/setup.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/models.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/ci.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/common.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev_custom_example.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev_default.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/__init__.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/basic_api_errors.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/basic_api_test.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_api_doc.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_body_mixin.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_cache.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_client.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_filters.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_locale_api.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_perm_tests.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_perms_api.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_profiling_middleware.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_settings.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_timezone_middleware.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_tools.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_user_queryset.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_view_decorators.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_view_fields.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/urls.py +0 -0
- {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/views.py +0 -0
|
@@ -23,20 +23,29 @@ stages:
|
|
|
23
23
|
- build
|
|
24
24
|
- package
|
|
25
25
|
|
|
26
|
+
quality check:
|
|
27
|
+
variables:
|
|
28
|
+
DJANGO_VERSION: 'django==4.2.*' # LTS
|
|
29
|
+
script:
|
|
30
|
+
- apt-get update
|
|
31
|
+
- apt-get install -y gettext
|
|
32
|
+
- flake8 .
|
|
33
|
+
- isort . -c
|
|
34
|
+
- ./make_messages
|
|
35
|
+
- git diff --exit-code # exit if messages changed
|
|
36
|
+
- python manage.py makemigrations --check --dry-run --setting='pfx.settings.dev'
|
|
37
|
+
|
|
26
38
|
test:
|
|
27
39
|
variables:
|
|
28
40
|
POSTGRES_DB: ci
|
|
29
41
|
POSTGRES_USER: postgres
|
|
30
42
|
POSTGRES_PASSWORD: postgres
|
|
31
|
-
|
|
43
|
+
DJANGO_SETTINGS_MODULE: 'tests.settings.ci'
|
|
32
44
|
script:
|
|
33
45
|
- apt-get update
|
|
34
46
|
- apt-get install -y gettext
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- django-admin compilemessages --ignore venv
|
|
38
|
-
- export DJANGO_SETTINGS_MODULE=$TEST_SETTINGS_MODULE
|
|
39
|
-
- coverage run --source='.' runtest.py
|
|
47
|
+
- python manage.py compilemessages -i venv
|
|
48
|
+
- coverage run --source='.' manage.py test
|
|
40
49
|
- coverage report
|
|
41
50
|
- coverage xml
|
|
42
51
|
- coverage html
|
|
@@ -86,6 +95,10 @@ pages:
|
|
|
86
95
|
- master
|
|
87
96
|
|
|
88
97
|
package:
|
|
98
|
+
stage: package
|
|
99
|
+
needs:
|
|
100
|
+
- job: quality check
|
|
101
|
+
- job: test
|
|
89
102
|
rules:
|
|
90
103
|
- if: $CI_COMMIT_TAG =~ /^\d+\.\d+$/
|
|
91
104
|
when: on_success
|
|
@@ -97,6 +110,6 @@ package:
|
|
|
97
110
|
- git fetch --prune --unshallow
|
|
98
111
|
- pip install --upgrade twine
|
|
99
112
|
- pip install -r requirements.txt
|
|
100
|
-
-
|
|
113
|
+
- ./manage.py compilemessages -i venv -i tests
|
|
101
114
|
- python3 -m build
|
|
102
115
|
- TWINE_PASSWORD=${PYPI_DEPLOY_TOKEN} TWINE_USERNAME=__token__ python3 -m twine upload dist/*
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
LICENSE
|
|
5
5
|
MANIFEST.in
|
|
6
6
|
README.md
|
|
7
|
-
|
|
7
|
+
make_messages
|
|
8
|
+
manage.py
|
|
8
9
|
pyproject.toml
|
|
9
10
|
requirements.txt
|
|
10
|
-
runtest.py
|
|
11
11
|
serve-doc
|
|
12
12
|
setup.cfg
|
|
13
13
|
setup.py
|
|
@@ -60,6 +60,8 @@ pfx/pfxcore/middleware/__init__.py
|
|
|
60
60
|
pfx/pfxcore/middleware/authentication.py
|
|
61
61
|
pfx/pfxcore/middleware/locale.py
|
|
62
62
|
pfx/pfxcore/middleware/profiling.py
|
|
63
|
+
pfx/pfxcore/migrations/0001_initial.py
|
|
64
|
+
pfx/pfxcore/migrations/__init__.py
|
|
63
65
|
pfx/pfxcore/models/__init__.py
|
|
64
66
|
pfx/pfxcore/models/abstract_pfx_base_user.py
|
|
65
67
|
pfx/pfxcore/models/cache_mixins.py
|
|
@@ -103,6 +105,8 @@ pfx/pfxcore/views/parameters/subset_offset.py
|
|
|
103
105
|
pfx/pfxcore/views/parameters/subset_page.py
|
|
104
106
|
pfx/pfxcore/views/parameters/subset_page_size.py
|
|
105
107
|
pfx/pfxcore/views/parameters/subset_page_subset.py
|
|
108
|
+
pfx/settings/__init__.py
|
|
109
|
+
pfx/settings/dev.py
|
|
106
110
|
tests/__init__.py
|
|
107
111
|
tests/models.py
|
|
108
112
|
tests/urls.py
|
|
@@ -210,13 +210,13 @@ except that the login service returns a temporary JWT token valid only for the o
|
|
|
210
210
|
|
|
211
211
|
### Enable MFA OTP
|
|
212
212
|
Services to enable the MFA with OTP.
|
|
213
|
-
You have to call first the `
|
|
213
|
+
You have to call first the `setup-uri` service to get the URI,
|
|
214
214
|
encode it as a QR code and present it in the UI of your software.
|
|
215
215
|
Then user then scans this QR code to add the OTP secret to his OTP App.
|
|
216
|
-
Finally, the `
|
|
216
|
+
Finally, the `enable` service must be called with an OTP code retrieved
|
|
217
217
|
in the OTP App to confirm the activation.
|
|
218
218
|
|
|
219
|
-
**Request :** `
|
|
219
|
+
**Request :** `GET` `/auth/otp/setup-uri`
|
|
220
220
|
|
|
221
221
|
**Responses :**
|
|
222
222
|
|
|
@@ -227,7 +227,7 @@ in the OTP App to confirm the activation.
|
|
|
227
227
|
|-----------|---------------------------------------|
|
|
228
228
|
| setup_uri | the uri to enable the OTP application |
|
|
229
229
|
|
|
230
|
-
**Request :** `PUT` `/auth/otp/
|
|
230
|
+
**Request :** `PUT` `/auth/otp/enable`
|
|
231
231
|
|
|
232
232
|
**Request body:**
|
|
233
233
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Django's command-line utility for administrative tasks."""
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from django.db.migrations import writer
|
|
8
|
+
|
|
9
|
+
if len(sys.argv) > 1 and sys.argv[1] == 'test':
|
|
10
|
+
logging.disable(logging.WARNING)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
writer.MIGRATION_HEADER_TEMPLATE = """\
|
|
14
|
+
# Generated by Django %(version)s on %(timestamp)s
|
|
15
|
+
# flake8: noqa
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
"""Run administrative tasks."""
|
|
21
|
+
cmd = len(sys.argv) > 1 and sys.argv[1]
|
|
22
|
+
os.environ.setdefault(
|
|
23
|
+
"DJANGO_SETTINGS_MODULE",
|
|
24
|
+
cmd == 'test' and "tests.settings.dev" or "pfx.settings.dev")
|
|
25
|
+
try:
|
|
26
|
+
from django.core.management import execute_from_command_line
|
|
27
|
+
except ImportError as exc:
|
|
28
|
+
raise ImportError(
|
|
29
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
30
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
31
|
+
"forget to activate a virtual environment?"
|
|
32
|
+
) from exc
|
|
33
|
+
execute_from_command_line(sys.argv)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == '__main__':
|
|
37
|
+
main()
|
|
Binary file
|
|
@@ -7,7 +7,7 @@ msgid ""
|
|
|
7
7
|
msgstr ""
|
|
8
8
|
"Project-Id-Version: \n"
|
|
9
9
|
"Report-Msgid-Bugs-To: \n"
|
|
10
|
-
"POT-Creation-Date: 2024-04-
|
|
10
|
+
"POT-Creation-Date: 2024-04-18 14:37+0200\n"
|
|
11
11
|
"PO-Revision-Date: 2021-06-22 23:31+0200\n"
|
|
12
12
|
"Last-Translator: \n"
|
|
13
13
|
"Language-Team: \n"
|
|
@@ -18,7 +18,7 @@ msgstr ""
|
|
|
18
18
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
|
19
19
|
"X-Generator: Poedit 2.3\n"
|
|
20
20
|
|
|
21
|
-
#: decorator/rest.py:
|
|
21
|
+
#: decorator/rest.py:41
|
|
22
22
|
msgid "An internal server error occured."
|
|
23
23
|
msgstr "Une erreur interne du serveur est survenue."
|
|
24
24
|
|
|
@@ -59,63 +59,67 @@ msgstr ""
|
|
|
59
59
|
"Format non valide, il peut s’agir d’un nombre en heures, « 1:05 », « :05 », "
|
|
60
60
|
"« 1h 5m », « 1.5h » ou « 30m »."
|
|
61
61
|
|
|
62
|
-
#: models/login_ban.py:
|
|
62
|
+
#: models/login_ban.py:43
|
|
63
63
|
msgid "Username"
|
|
64
64
|
msgstr "Nom d’utilisateur"
|
|
65
65
|
|
|
66
|
-
#: models/login_ban.py:
|
|
66
|
+
#: models/login_ban.py:44
|
|
67
67
|
msgid "Failed counter"
|
|
68
68
|
msgstr "Compteur d’échec"
|
|
69
69
|
|
|
70
|
-
#: models/login_ban.py:
|
|
70
|
+
#: models/login_ban.py:45
|
|
71
71
|
msgid "Last failed"
|
|
72
72
|
msgstr "Dernier échec"
|
|
73
73
|
|
|
74
|
-
#: models/login_ban.py:
|
|
74
|
+
#: models/login_ban.py:50
|
|
75
75
|
msgid "Login ban"
|
|
76
76
|
msgstr "Login banni"
|
|
77
77
|
|
|
78
|
-
#: models/login_ban.py:
|
|
78
|
+
#: models/login_ban.py:51
|
|
79
79
|
msgid "Login bans"
|
|
80
80
|
msgstr "Login bannis"
|
|
81
81
|
|
|
82
|
-
#: models/otp_user_mixin.py:
|
|
82
|
+
#: models/otp_user_mixin.py:16
|
|
83
83
|
msgid "OTP secret token"
|
|
84
84
|
msgstr "Jeton secret OTP"
|
|
85
85
|
|
|
86
|
-
#: models/otp_user_mixin.py:
|
|
86
|
+
#: models/otp_user_mixin.py:20
|
|
87
87
|
msgid "Temporary OTP secret token"
|
|
88
88
|
msgstr "Jeton secret OTP temporaire"
|
|
89
89
|
|
|
90
|
-
#: models/otp_user_mixin.py:
|
|
90
|
+
#: models/otp_user_mixin.py:22
|
|
91
91
|
msgid "HOTP count"
|
|
92
92
|
msgstr "Compte HOTP"
|
|
93
93
|
|
|
94
|
-
#: models/otp_user_mixin.py:
|
|
94
|
+
#: models/otp_user_mixin.py:24
|
|
95
95
|
msgid "HOTP expiry"
|
|
96
96
|
msgstr "Expiration HOTP"
|
|
97
97
|
|
|
98
|
+
#: models/otp_user_mixin.py:29 views/authentication_views.py:489
|
|
99
|
+
msgid "OTP enabled"
|
|
100
|
+
msgstr "OTP activé"
|
|
101
|
+
|
|
98
102
|
#: models/pfx_models.py:14
|
|
99
103
|
#, python-format
|
|
100
104
|
msgid "%(model_name)s with this %(field_labels)s already exists."
|
|
101
105
|
msgstr "%(model_name)s avec ce/cette %(field_labels)s éxiste déjà."
|
|
102
106
|
|
|
103
|
-
#: shortcuts.py:
|
|
107
|
+
#: shortcuts.py:50
|
|
104
108
|
#, python-brace-format
|
|
105
109
|
msgid "{key} must be an integer number."
|
|
106
110
|
msgstr "{key} doit être un nombre entier."
|
|
107
111
|
|
|
108
|
-
#: shortcuts.py:
|
|
112
|
+
#: shortcuts.py:66
|
|
109
113
|
#, python-brace-format
|
|
110
114
|
msgid "{key} must be a number."
|
|
111
115
|
msgstr "{key} doit être un nombre."
|
|
112
116
|
|
|
113
|
-
#: shortcuts.py:
|
|
117
|
+
#: shortcuts.py:82
|
|
114
118
|
#, python-brace-format
|
|
115
119
|
msgid "{key} must be a date."
|
|
116
120
|
msgstr "{key} doit être une date."
|
|
117
121
|
|
|
118
|
-
#: shortcuts.py:
|
|
122
|
+
#: shortcuts.py:103
|
|
119
123
|
#, python-brace-format
|
|
120
124
|
msgid "{key} must be “true”, “false”, “1”, “0” or empty."
|
|
121
125
|
msgstr "{key} doit être « true », « false », « 1 », « 0 » ou vide"
|
|
@@ -152,6 +156,7 @@ msgid "The %(site_name)s team"
|
|
|
152
156
|
msgstr "L'équipe de %(site_name)s"
|
|
153
157
|
|
|
154
158
|
#: templates/registration/otp_code_subject.txt:2
|
|
159
|
+
#, python-format
|
|
155
160
|
msgid "New authentication code for %(site_name)s"
|
|
156
161
|
msgstr "Nouveau code d’authentication pour %(site_name)s"
|
|
157
162
|
|
|
@@ -191,7 +196,7 @@ msgstr "Bienvenue sur %(site_name)s."
|
|
|
191
196
|
msgid "Welcome on %(site_name)s"
|
|
192
197
|
msgstr "Bienvenue sur %(site_name)s"
|
|
193
198
|
|
|
194
|
-
#: views/authentication_views.py:
|
|
199
|
+
#: views/authentication_views.py:81
|
|
195
200
|
#, python-brace-format
|
|
196
201
|
msgid ""
|
|
197
202
|
"Your connection is temporarily disabled after several unsuccessful attempts, "
|
|
@@ -200,43 +205,39 @@ msgstr ""
|
|
|
200
205
|
"Votre connexion est temporairement désactivée après plusieurs tentatives "
|
|
201
206
|
"infructueuses, veuillez réessayer dans {seconds} secondes."
|
|
202
207
|
|
|
203
|
-
#: views/authentication_views.py:
|
|
208
|
+
#: views/authentication_views.py:243 views/authentication_views.py:408
|
|
204
209
|
msgid "password updated successfully"
|
|
205
210
|
msgstr "le mot de passe a été mis à jour avec succès"
|
|
206
211
|
|
|
207
|
-
#: views/authentication_views.py:
|
|
212
|
+
#: views/authentication_views.py:248
|
|
208
213
|
msgid "Incorrect password"
|
|
209
214
|
msgstr "Mot de passe incorrect"
|
|
210
215
|
|
|
211
|
-
#: views/authentication_views.py:
|
|
216
|
+
#: views/authentication_views.py:251 views/authentication_views.py:417
|
|
212
217
|
msgid "Empty password is not allowed"
|
|
213
218
|
msgstr "Un mot de passe vide n’est pas autorisé"
|
|
214
219
|
|
|
215
|
-
#: views/authentication_views.py:
|
|
220
|
+
#: views/authentication_views.py:342
|
|
216
221
|
msgid "User and token are valid"
|
|
217
222
|
msgstr "L'utilisateur et le token sont valides"
|
|
218
223
|
|
|
219
|
-
#: views/authentication_views.py:
|
|
224
|
+
#: views/authentication_views.py:344
|
|
220
225
|
msgid "User or token is invalid"
|
|
221
226
|
msgstr "L'utilisateur ou le token est invalide"
|
|
222
227
|
|
|
223
|
-
#: views/authentication_views.py:
|
|
224
|
-
msgid "OTP is already
|
|
225
|
-
msgstr "
|
|
226
|
-
|
|
227
|
-
#: views/authentication_views.py:487
|
|
228
|
-
msgid "OTP is activated"
|
|
229
|
-
msgstr "L'OTP est activé"
|
|
228
|
+
#: views/authentication_views.py:451
|
|
229
|
+
msgid "OTP is already enabled"
|
|
230
|
+
msgstr "OTP est déjà activé"
|
|
230
231
|
|
|
231
|
-
#: views/authentication_views.py:
|
|
232
|
+
#: views/authentication_views.py:490 views/authentication_views.py:524
|
|
232
233
|
msgid "Invalid OTP code"
|
|
233
234
|
msgstr "Code OTP invalide"
|
|
234
235
|
|
|
235
|
-
#: views/authentication_views.py:
|
|
236
|
-
msgid "OTP
|
|
237
|
-
msgstr "
|
|
236
|
+
#: views/authentication_views.py:523
|
|
237
|
+
msgid "OTP disabled"
|
|
238
|
+
msgstr "OTP désactivé"
|
|
238
239
|
|
|
239
|
-
#: views/authentication_views.py:
|
|
240
|
+
#: views/authentication_views.py:783
|
|
240
241
|
msgid ""
|
|
241
242
|
"If the email address you entered is correct, you will receive an email from "
|
|
242
243
|
"us with instructions to reset your password."
|
|
@@ -245,7 +246,7 @@ msgstr ""
|
|
|
245
246
|
"un courrier électronique de notre part contenant des instructions pour "
|
|
246
247
|
"réinitialiser votre mot de passe."
|
|
247
248
|
|
|
248
|
-
#: views/authentication_views.py:
|
|
249
|
+
#: views/authentication_views.py:857
|
|
249
250
|
msgid "A new authentication code has been sent by email."
|
|
250
251
|
msgstr "Un nouveau code d'authentification a été envoyé par e-mail."
|
|
251
252
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Generated by Django 4.2.11 on 2024-04-11 04:19
|
|
2
|
+
# flake8: noqa
|
|
3
|
+
|
|
4
|
+
import django.contrib.auth.models
|
|
5
|
+
import django.contrib.auth.validators
|
|
6
|
+
import django.utils.timezone
|
|
7
|
+
from django.db import migrations, models
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Migration(migrations.Migration):
|
|
11
|
+
|
|
12
|
+
initial = True
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
('auth', '0012_alter_user_first_name_max_length'),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
operations = [
|
|
19
|
+
migrations.CreateModel(
|
|
20
|
+
name='LoginBan',
|
|
21
|
+
fields=[
|
|
22
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
23
|
+
('username', models.CharField(max_length=150, unique=True, verbose_name='Username')),
|
|
24
|
+
('failed_counter', models.IntegerField(verbose_name='Failed counter')),
|
|
25
|
+
('last_failed', models.DateTimeField(auto_now=True, verbose_name='Last failed')),
|
|
26
|
+
],
|
|
27
|
+
options={
|
|
28
|
+
'verbose_name': 'Login ban',
|
|
29
|
+
'verbose_name_plural': 'Login bans',
|
|
30
|
+
},
|
|
31
|
+
),
|
|
32
|
+
migrations.CreateModel(
|
|
33
|
+
name='PFXUser',
|
|
34
|
+
fields=[
|
|
35
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
36
|
+
('password', models.CharField(max_length=128, verbose_name='password')),
|
|
37
|
+
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
|
38
|
+
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
|
39
|
+
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
|
40
|
+
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
|
41
|
+
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
|
42
|
+
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
|
43
|
+
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
|
44
|
+
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
|
45
|
+
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
|
46
|
+
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
|
47
|
+
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
|
48
|
+
],
|
|
49
|
+
options={
|
|
50
|
+
'verbose_name': 'user',
|
|
51
|
+
'verbose_name_plural': 'users',
|
|
52
|
+
'abstract': False,
|
|
53
|
+
'swappable': 'AUTH_USER_MODEL',
|
|
54
|
+
},
|
|
55
|
+
managers=[
|
|
56
|
+
('objects', django.contrib.auth.models.UserManager()),
|
|
57
|
+
],
|
|
58
|
+
),
|
|
59
|
+
]
|
|
@@ -4,6 +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.decorator import rest_property
|
|
7
8
|
from pfx.pfxcore.settings import settings
|
|
8
9
|
|
|
9
10
|
|
|
@@ -25,6 +26,10 @@ class OtpUserMixin(models.Model):
|
|
|
25
26
|
class Meta:
|
|
26
27
|
abstract = True
|
|
27
28
|
|
|
29
|
+
@rest_property(_("OTP enabled"), "BooleanField")
|
|
30
|
+
def is_otp(self):
|
|
31
|
+
return bool(self.otp_secret_token)
|
|
32
|
+
|
|
28
33
|
def enable_otp(self):
|
|
29
34
|
"""Activate OTP for this user.
|
|
30
35
|
|
|
@@ -281,11 +281,15 @@ class AuthenticationView(
|
|
|
281
281
|
Can be overridden to customize result.
|
|
282
282
|
|
|
283
283
|
:param user: The user"""
|
|
284
|
-
|
|
284
|
+
info = {
|
|
285
285
|
'username': user.get_username(),
|
|
286
286
|
'first_name': user.first_name,
|
|
287
287
|
'last_name': user.last_name,
|
|
288
|
-
'email': user.email
|
|
288
|
+
'email': user.email
|
|
289
|
+
}
|
|
290
|
+
if isinstance(user, OtpUserMixin):
|
|
291
|
+
info.update(is_otp=user.is_otp)
|
|
292
|
+
return info
|
|
289
293
|
|
|
290
294
|
@classmethod
|
|
291
295
|
def get_user_information_schema(cls):
|
|
@@ -414,21 +418,22 @@ class AuthenticationView(
|
|
|
414
418
|
raise UnauthorizedError()
|
|
415
419
|
|
|
416
420
|
@method_decorator(never_cache)
|
|
417
|
-
@rest_api("/otp/
|
|
418
|
-
def
|
|
419
|
-
"""Entrypoint for :code:`
|
|
421
|
+
@rest_api("/otp/setup-uri", public=False, method="get", priority_doc=52)
|
|
422
|
+
def otp_setup_uri(self, *args, **kwargs):
|
|
423
|
+
"""Entrypoint for :code:`GET /otp/secret-key` route.
|
|
420
424
|
|
|
421
|
-
|
|
425
|
+
Get the setup uri for the OTP authentication.
|
|
422
426
|
|
|
423
427
|
:returns: The JSON response
|
|
424
428
|
:rtype: :class:`JsonResponse`
|
|
425
429
|
---
|
|
426
430
|
put:
|
|
427
|
-
summary:
|
|
428
|
-
description:
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
431
|
+
summary: Get the activation url for the OTP authentication.
|
|
432
|
+
description: This service returns a setup URI to enable the OTP.
|
|
433
|
+
Your front-end application should provide this URI in the form
|
|
434
|
+
of a QR code so that the user can scan it with
|
|
435
|
+
an OTP application. You must then call the /confirm service
|
|
436
|
+
with a valid OTP code to activate OTP authentication.
|
|
432
437
|
responses:
|
|
433
438
|
200:
|
|
434
439
|
content:
|
|
@@ -443,26 +448,26 @@ class AuthenticationView(
|
|
|
443
448
|
raise NotFoundError()
|
|
444
449
|
if self.request.user.otp_secret_token:
|
|
445
450
|
return JsonResponse(
|
|
446
|
-
dict(message=_("OTP is already
|
|
451
|
+
dict(message=_("OTP is already enabled")), status=400)
|
|
447
452
|
self.request.user.enable_otp()
|
|
448
453
|
return JsonResponse(dict(
|
|
449
454
|
setup_uri=self.request.user.get_otp_setup_uri(tmp=True)))
|
|
450
455
|
|
|
451
456
|
@method_decorator(never_cache)
|
|
452
457
|
@rest_api(
|
|
453
|
-
"/otp/
|
|
458
|
+
"/otp/enable", public=False, method="put", priority_doc=53,
|
|
454
459
|
response_schema='message_schema')
|
|
455
|
-
def
|
|
456
|
-
"""Entrypoint for :code:`PUT /otp/
|
|
460
|
+
def otp_enable(self, *args, **kwargs):
|
|
461
|
+
"""Entrypoint for :code:`PUT /otp/enable` route.
|
|
457
462
|
|
|
458
|
-
|
|
463
|
+
Enable the OTP authentication.
|
|
459
464
|
|
|
460
465
|
:returns: The JSON response
|
|
461
466
|
:rtype: :class:`JsonResponse`
|
|
462
467
|
---
|
|
463
468
|
put:
|
|
464
|
-
summary:
|
|
465
|
-
description: A service to
|
|
469
|
+
summary: Enable OTP authentication
|
|
470
|
+
description: A service to enable the OTP authentication with a
|
|
466
471
|
valid OTP code.
|
|
467
472
|
requestBody:
|
|
468
473
|
content:
|
|
@@ -481,7 +486,7 @@ class AuthenticationView(
|
|
|
481
486
|
raise NotFoundError()
|
|
482
487
|
data = self.deserialize_body()
|
|
483
488
|
if self.request.user.confirm_otp(data.get('otp_code')):
|
|
484
|
-
return JsonResponse(dict(message=_("OTP
|
|
489
|
+
return JsonResponse(dict(message=_("OTP enabled")))
|
|
485
490
|
return JsonResponse(dict(message=_("Invalid OTP code")), status=422)
|
|
486
491
|
|
|
487
492
|
@method_decorator(never_cache)
|
|
@@ -489,7 +494,7 @@ class AuthenticationView(
|
|
|
489
494
|
def otp_disable(self, *args, **kwargs):
|
|
490
495
|
"""Entrypoint for :code:`PUT /otp/disable` route.
|
|
491
496
|
|
|
492
|
-
Disable the OTP
|
|
497
|
+
Disable the OTP authentication.
|
|
493
498
|
|
|
494
499
|
:returns: The JSON response
|
|
495
500
|
:rtype: :class:`JsonResponse`
|
|
@@ -515,7 +520,7 @@ class AuthenticationView(
|
|
|
515
520
|
data = self.deserialize_body()
|
|
516
521
|
if self.request.user.is_otp_valid(data.get('otp_code')):
|
|
517
522
|
self.request.user.disable_otp()
|
|
518
|
-
return JsonResponse(dict(message=_("OTP
|
|
523
|
+
return JsonResponse(dict(message=_("OTP disabled")))
|
|
519
524
|
return JsonResponse(dict(message=_("Invalid OTP code")), status=422)
|
|
520
525
|
|
|
521
526
|
@method_decorator(never_cache)
|
|
File without changes
|
|
File without changes
|