django-pfx 1.4.dev36__tar.gz → 1.4.dev40__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.dev36 → django_pfx-1.4.dev40}/PKG-INFO +1 -1
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/django_pfx.egg-info/PKG-INFO +1 -1
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/authentication.md +4 -4
- django_pfx-1.4.dev40/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +24 -24
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/otp_user_mixin.py +5 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/authentication_views.py +28 -23
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_auth_api.py +19 -14
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_shortcuts.py +1 -1
- django-pfx-1.4.dev36/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/.gitignore +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/.gitlab-ci.yml +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/.pre-commit-config.yaml +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/LICENSE +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/MANIFEST.in +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/README.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/django_pfx.egg-info/SOURCES.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/django_pfx.egg-info/dependency_links.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/django_pfx.egg-info/requires.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/django_pfx.egg-info/top_level.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/Makefile +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/conf.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/index.rst +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/api.views.rst +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/decorator.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/generate_openapi.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/getting_started.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/internationalisation.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/model.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/pfx_views.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/profiling.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/settings.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/doc/source/testing.md +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/img/pfx.png +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/img/pfx.svg +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/make_messages +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/manage.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/apidoc/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/apidoc/parameters.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/apidoc/schema.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/apidoc/tags.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/apps.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/decorator/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/decorator/rest.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/default_settings.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/exceptions.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/http/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/http/json_response.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/management/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/management/commands/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/management/commands/profile.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/middleware/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/middleware/authentication.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/middleware/locale.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/middleware/profiling.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/migrations/0001_initial.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/migrations/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/cache_mixins.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/login_ban.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/not_null_fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/pfx_models.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/pfx_user.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/serializers/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/serializers/json.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/settings.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/shortcuts.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/storage/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/storage/s3_storage.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/test.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/urls.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/filters_views.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/locale_views.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/date_format.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/groups.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/list_count.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/list_items.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/list_order.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/list_search.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/rest_views.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/settings/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pfx/settings/dev.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/pyproject.toml +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/requirements.txt +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/serve-doc +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/setup.cfg +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/setup.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/models.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/ci.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/common.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/dev.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/dev_custom_example.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/settings/dev_default.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/__init__.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/basic_api_errors.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/basic_api_test.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_api_doc.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_body_mixin.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_cache.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_client.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_filters.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_locale_api.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_perm_tests.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_perms_api.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_profiling_middleware.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_settings.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_timezone_middleware.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_tools.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_user_queryset.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_view_decorators.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/tests/test_view_fields.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/urls.py +0 -0
- {django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/tests/views.py +0 -0
|
@@ -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
|
|
|
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"
|
|
@@ -79,22 +79,26 @@ msgstr "Login banni"
|
|
|
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."
|
|
@@ -201,7 +205,7 @@ msgstr ""
|
|
|
201
205
|
"Votre connexion est temporairement désactivée après plusieurs tentatives "
|
|
202
206
|
"infructueuses, veuillez réessayer dans {seconds} secondes."
|
|
203
207
|
|
|
204
|
-
#: views/authentication_views.py:243 views/authentication_views.py:
|
|
208
|
+
#: views/authentication_views.py:243 views/authentication_views.py:408
|
|
205
209
|
msgid "password updated successfully"
|
|
206
210
|
msgstr "le mot de passe a été mis à jour avec succès"
|
|
207
211
|
|
|
@@ -209,35 +213,31 @@ msgstr "le mot de passe a été mis à jour avec succès"
|
|
|
209
213
|
msgid "Incorrect password"
|
|
210
214
|
msgstr "Mot de passe incorrect"
|
|
211
215
|
|
|
212
|
-
#: views/authentication_views.py:251 views/authentication_views.py:
|
|
216
|
+
#: views/authentication_views.py:251 views/authentication_views.py:417
|
|
213
217
|
msgid "Empty password is not allowed"
|
|
214
218
|
msgstr "Un mot de passe vide n’est pas autorisé"
|
|
215
219
|
|
|
216
|
-
#: views/authentication_views.py:
|
|
220
|
+
#: views/authentication_views.py:342
|
|
217
221
|
msgid "User and token are valid"
|
|
218
222
|
msgstr "L'utilisateur et le token sont valides"
|
|
219
223
|
|
|
220
|
-
#: views/authentication_views.py:
|
|
224
|
+
#: views/authentication_views.py:344
|
|
221
225
|
msgid "User or token is invalid"
|
|
222
226
|
msgstr "L'utilisateur ou le token est invalide"
|
|
223
227
|
|
|
224
|
-
#: views/authentication_views.py:
|
|
225
|
-
msgid "OTP is already
|
|
226
|
-
msgstr "
|
|
227
|
-
|
|
228
|
-
#: views/authentication_views.py:484
|
|
229
|
-
msgid "OTP is activated"
|
|
230
|
-
msgstr "L'OTP est activé"
|
|
228
|
+
#: views/authentication_views.py:451
|
|
229
|
+
msgid "OTP is already enabled"
|
|
230
|
+
msgstr "OTP est déjà activé"
|
|
231
231
|
|
|
232
|
-
#: views/authentication_views.py:
|
|
233
|
-
msgid "Invalid
|
|
234
|
-
msgstr "Code
|
|
232
|
+
#: views/authentication_views.py:490 views/authentication_views.py:524
|
|
233
|
+
msgid "Invalid code"
|
|
234
|
+
msgstr "Code invalide"
|
|
235
235
|
|
|
236
|
-
#: views/authentication_views.py:
|
|
237
|
-
msgid "OTP
|
|
238
|
-
msgstr "
|
|
236
|
+
#: views/authentication_views.py:523
|
|
237
|
+
msgid "OTP disabled"
|
|
238
|
+
msgstr "OTP désactivé"
|
|
239
239
|
|
|
240
|
-
#: views/authentication_views.py:
|
|
240
|
+
#: views/authentication_views.py:783
|
|
241
241
|
msgid ""
|
|
242
242
|
"If the email address you entered is correct, you will receive an email from "
|
|
243
243
|
"us with instructions to reset your password."
|
|
@@ -246,7 +246,7 @@ msgstr ""
|
|
|
246
246
|
"un courrier électronique de notre part contenant des instructions pour "
|
|
247
247
|
"réinitialiser votre mot de passe."
|
|
248
248
|
|
|
249
|
-
#: views/authentication_views.py:
|
|
249
|
+
#: views/authentication_views.py:857
|
|
250
250
|
msgid "A new authentication code has been sent by email."
|
|
251
251
|
msgstr "Un nouveau code d'authentification a été envoyé par e-mail."
|
|
252
252
|
|
|
@@ -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,15 +486,15 @@ 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
|
|
485
|
-
return JsonResponse(dict(
|
|
489
|
+
return JsonResponse(dict(message=_("OTP enabled")))
|
|
490
|
+
return JsonResponse(dict(otp_code=[_("Invalid code")]), status=422)
|
|
486
491
|
|
|
487
492
|
@method_decorator(never_cache)
|
|
488
493
|
@rest_api("/otp/disable", public=False, method="put", priority_doc=54)
|
|
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,8 +520,8 @@ 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
|
|
519
|
-
return JsonResponse(dict(
|
|
523
|
+
return JsonResponse(dict(message=_("OTP disabled")))
|
|
524
|
+
return JsonResponse(dict(otp_code=[_("Invalid code")]), status=422)
|
|
520
525
|
|
|
521
526
|
@method_decorator(never_cache)
|
|
522
527
|
@rest_api(
|
|
@@ -884,10 +884,11 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
|
|
|
884
884
|
self.assertRC(response, 200)
|
|
885
885
|
self.client.token = self.get_val(response, 'token')
|
|
886
886
|
|
|
887
|
-
def
|
|
887
|
+
def test_otp_enable(self):
|
|
888
888
|
self.client.login(username='jrr.tolkien', password='RIGHT PASSWORD')
|
|
889
889
|
|
|
890
|
-
|
|
890
|
+
self.assertEqual(self.user1.is_otp, False)
|
|
891
|
+
response = self.client.get('/api/auth/otp/setup-uri')
|
|
891
892
|
self.assertRC(response, 200)
|
|
892
893
|
self.assertJIn(response, 'setup_uri', "otpauth://totp/")
|
|
893
894
|
self.user1.refresh_from_db()
|
|
@@ -898,51 +899,54 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
|
|
|
898
899
|
secret_key = parse_qs(url.query)['secret'][0]
|
|
899
900
|
totp = pyotp.totp.TOTP(secret_key)
|
|
900
901
|
|
|
901
|
-
response = self.client.put('/api/auth/otp/
|
|
902
|
+
response = self.client.put('/api/auth/otp/enable', dict(
|
|
902
903
|
otp_code=totp.now()))
|
|
903
904
|
self.assertRC(response, 200)
|
|
904
|
-
self.assertJE(response, 'message', "OTP
|
|
905
|
+
self.assertJE(response, 'message', "OTP enabled")
|
|
905
906
|
self.user1.refresh_from_db()
|
|
906
907
|
self.assertEqual(len(self.user1.otp_secret_token), 32)
|
|
907
908
|
self.assertIsNone(self.user1.otp_secret_token_tmp)
|
|
909
|
+
self.assertEqual(self.user1.is_otp, True)
|
|
908
910
|
|
|
909
|
-
def
|
|
911
|
+
def test_otp_enable_bad_code(self):
|
|
910
912
|
self.client.login(username='jrr.tolkien', password='RIGHT PASSWORD')
|
|
911
913
|
|
|
912
|
-
response = self.client.
|
|
914
|
+
response = self.client.get('/api/auth/otp/setup-uri')
|
|
913
915
|
self.assertRC(response, 200)
|
|
914
916
|
self.assertJIn(response, 'setup_uri', "otpauth://totp/")
|
|
915
917
|
self.user1.refresh_from_db()
|
|
916
918
|
self.assertIsNone(self.user1.otp_secret_token)
|
|
917
919
|
self.assertEqual(len(self.user1.otp_secret_token_tmp), 32)
|
|
918
920
|
|
|
919
|
-
response = self.client.put('/api/auth/otp/
|
|
921
|
+
response = self.client.put('/api/auth/otp/enable', dict(
|
|
920
922
|
otp_code='-'))
|
|
921
923
|
self.assertRC(response, 422)
|
|
922
|
-
self.assertJE(response, '
|
|
924
|
+
self.assertJE(response, 'otp_code.@0', "Invalid code")
|
|
923
925
|
self.user1.refresh_from_db()
|
|
924
926
|
self.assertIsNone(self.user1.otp_secret_token)
|
|
925
927
|
self.assertEqual(len(self.user1.otp_secret_token_tmp), 32)
|
|
926
928
|
|
|
927
|
-
def
|
|
929
|
+
def test_otp_setup_uri_already_activated(self):
|
|
928
930
|
self.enable_otp(self.user1)
|
|
929
931
|
|
|
930
932
|
self.otp_login(self.user1, 'RIGHT PASSWORD')
|
|
931
|
-
response = self.client.
|
|
933
|
+
response = self.client.get('/api/auth/otp/setup-uri')
|
|
932
934
|
self.assertRC(response, 400)
|
|
933
|
-
self.assertJE(response, 'message', "OTP is already
|
|
935
|
+
self.assertJE(response, 'message', "OTP is already enabled")
|
|
934
936
|
|
|
935
937
|
def test_otp_disable(self):
|
|
936
938
|
totp = self.enable_otp(self.user1)
|
|
937
939
|
|
|
940
|
+
self.assertEqual(self.user1.is_otp, True)
|
|
938
941
|
self.otp_login(self.user1, 'RIGHT PASSWORD')
|
|
939
942
|
response = self.client.put('/api/auth/otp/disable', dict(
|
|
940
943
|
otp_code=totp.now()))
|
|
941
944
|
self.assertRC(response, 200)
|
|
942
|
-
self.assertJE(response, 'message', "OTP
|
|
945
|
+
self.assertJE(response, 'message', "OTP disabled")
|
|
943
946
|
self.user1.refresh_from_db()
|
|
944
947
|
self.assertIsNone(self.user1.otp_secret_token)
|
|
945
948
|
self.assertIsNone(self.user1.otp_secret_token_tmp)
|
|
949
|
+
self.assertEqual(self.user1.is_otp, False)
|
|
946
950
|
|
|
947
951
|
@override_settings(
|
|
948
952
|
PFX_HOTP_CODE_VALIDITY=15,
|
|
@@ -967,7 +971,7 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
|
|
|
967
971
|
response = self.client.put('/api/auth/otp/disable', dict(
|
|
968
972
|
otp_code=otp_code))
|
|
969
973
|
self.assertRC(response, 200)
|
|
970
|
-
self.assertJE(response, 'message', "OTP
|
|
974
|
+
self.assertJE(response, 'message', "OTP disabled")
|
|
971
975
|
self.user1.refresh_from_db()
|
|
972
976
|
self.assertIsNone(self.user1.otp_secret_token)
|
|
973
977
|
self.assertIsNone(self.user1.otp_secret_token_tmp)
|
|
@@ -979,7 +983,7 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
|
|
|
979
983
|
response = self.client.put('/api/auth/otp/disable', dict(
|
|
980
984
|
otp_code='-'))
|
|
981
985
|
self.assertRC(response, 422)
|
|
982
|
-
self.assertJE(response, '
|
|
986
|
+
self.assertJE(response, 'otp_code.@0', "Invalid code")
|
|
983
987
|
self.user1.refresh_from_db()
|
|
984
988
|
self.assertEqual(len(self.user1.otp_secret_token), 32)
|
|
985
989
|
self.assertIsNone(self.user1.otp_secret_token_tmp)
|
|
@@ -1012,6 +1016,7 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
|
|
|
1012
1016
|
token=token,
|
|
1013
1017
|
otp_code=totp.now()))
|
|
1014
1018
|
self.assertRC(response, 200)
|
|
1019
|
+
self.assertJE(response, 'user.is_otp', True)
|
|
1015
1020
|
headers = jwt.get_unverified_header(token)
|
|
1016
1021
|
self.assertEqual(headers['pfx_user_pk'], self.user1.pk)
|
|
1017
1022
|
decoded = jwt.decode(
|
|
@@ -39,7 +39,7 @@ class ShortcutTest(TestAssertMixin, TestCase):
|
|
|
39
39
|
def test_get_pk(self):
|
|
40
40
|
author = dict(
|
|
41
41
|
pk=122,
|
|
42
|
-
|
|
42
|
+
resource_name='John Ronald Reuel Tolkien')
|
|
43
43
|
pk = get_pk(122)
|
|
44
44
|
self.assertEqual(pk, 122)
|
|
45
45
|
pk = get_pk(author)
|
|
Binary file
|
|
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
|
|
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.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/models/user_filtered_queryset_mixin.py
RENAMED
|
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.dev36 → django_pfx-1.4.dev40}/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.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/templates/registration/welcome_email.txt
RENAMED
|
File without changes
|
{django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/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.dev36 → django_pfx-1.4.dev40}/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.dev36 → django_pfx-1.4.dev40}/pfx/pfxcore/views/parameters/subset_page_size.py
RENAMED
|
File without changes
|
{django-pfx-1.4.dev36 → django_pfx-1.4.dev40}/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
|
|
File without changes
|
|
File without changes
|