django-pfx 1.4.dev36__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.
Files changed (143) hide show
  1. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/PKG-INFO +1 -1
  2. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/authentication.md +4 -4
  4. django_pfx-1.4.dev38/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  5. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +22 -22
  6. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/otp_user_mixin.py +5 -0
  7. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/authentication_views.py +26 -21
  8. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_auth_api.py +17 -12
  9. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_shortcuts.py +1 -1
  10. django-pfx-1.4.dev36/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  11. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/.gitignore +0 -0
  12. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/.gitlab-ci.yml +0 -0
  13. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/.pre-commit-config.yaml +0 -0
  14. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/LICENSE +0 -0
  15. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/MANIFEST.in +0 -0
  16. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/README.md +0 -0
  17. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/django_pfx.egg-info/SOURCES.txt +0 -0
  18. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/django_pfx.egg-info/dependency_links.txt +0 -0
  19. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/django_pfx.egg-info/requires.txt +0 -0
  20. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/django_pfx.egg-info/top_level.txt +0 -0
  21. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/Makefile +0 -0
  22. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/conf.py +0 -0
  23. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/index.rst +0 -0
  24. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/api.views.rst +0 -0
  25. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/decorator.md +0 -0
  26. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/generate_openapi.md +0 -0
  27. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/getting_started.md +0 -0
  28. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/internationalisation.md +0 -0
  29. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/model.md +0 -0
  30. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/pfx_views.md +0 -0
  31. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/profiling.md +0 -0
  32. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/settings.md +0 -0
  33. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/doc/source/testing.md +0 -0
  34. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/img/pfx.png +0 -0
  35. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/img/pfx.svg +0 -0
  36. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/make_messages +0 -0
  37. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/manage.py +0 -0
  38. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/__init__.py +0 -0
  39. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/__init__.py +0 -0
  40. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/__init__.py +0 -0
  41. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/parameters.py +0 -0
  42. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/schema.py +0 -0
  43. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/tags.py +0 -0
  44. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/apps.py +0 -0
  45. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/__init__.py +0 -0
  46. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/rest.py +0 -0
  47. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/default_settings.py +0 -0
  48. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/exceptions.py +0 -0
  49. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/fields.py +0 -0
  50. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/http/__init__.py +0 -0
  51. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/http/json_response.py +0 -0
  52. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/management/__init__.py +0 -0
  53. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/__init__.py +0 -0
  54. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  55. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/profile.py +0 -0
  56. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/__init__.py +0 -0
  57. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/authentication.py +0 -0
  58. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/locale.py +0 -0
  59. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/profiling.py +0 -0
  60. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  61. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/migrations/__init__.py +0 -0
  62. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/__init__.py +0 -0
  63. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  64. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/cache_mixins.py +0 -0
  65. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/login_ban.py +0 -0
  66. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/not_null_fields.py +0 -0
  67. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_models.py +0 -0
  68. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_user.py +0 -0
  69. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  70. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/serializers/__init__.py +0 -0
  71. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/serializers/json.py +0 -0
  72. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/settings.py +0 -0
  73. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/shortcuts.py +0 -0
  74. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/__init__.py +0 -0
  75. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/s3_storage.py +0 -0
  76. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  77. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  78. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  79. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  80. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  81. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  82. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/test.py +0 -0
  83. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/urls.py +0 -0
  84. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/__init__.py +0 -0
  85. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/fields.py +0 -0
  86. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/filters_views.py +0 -0
  87. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/locale_views.py +0 -0
  88. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  89. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  90. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/groups.py +0 -0
  91. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  92. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  93. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  94. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  95. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  96. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  97. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  98. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  99. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  100. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset.py +0 -0
  101. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  102. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  103. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  104. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  105. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  106. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/pfxcore/views/rest_views.py +0 -0
  107. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/settings/__init__.py +0 -0
  108. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pfx/settings/dev.py +0 -0
  109. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/pyproject.toml +0 -0
  110. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/requirements.txt +0 -0
  111. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/serve-doc +0 -0
  112. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/setup.cfg +0 -0
  113. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/setup.py +0 -0
  114. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/__init__.py +0 -0
  115. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  116. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/models.py +0 -0
  117. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/__init__.py +0 -0
  118. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/ci.py +0 -0
  119. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/common.py +0 -0
  120. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/dev.py +0 -0
  121. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/dev_custom_example.py +0 -0
  122. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/settings/dev_default.py +0 -0
  123. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/__init__.py +0 -0
  124. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/basic_api_errors.py +0 -0
  125. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/basic_api_test.py +0 -0
  126. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_api_doc.py +0 -0
  127. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_body_mixin.py +0 -0
  128. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_cache.py +0 -0
  129. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_client.py +0 -0
  130. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_fields.py +0 -0
  131. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_filters.py +0 -0
  132. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_locale_api.py +0 -0
  133. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_perm_tests.py +0 -0
  134. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_perms_api.py +0 -0
  135. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_profiling_middleware.py +0 -0
  136. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_settings.py +0 -0
  137. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_timezone_middleware.py +0 -0
  138. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_tools.py +0 -0
  139. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_user_queryset.py +0 -0
  140. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_view_decorators.py +0 -0
  141. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/tests/test_view_fields.py +0 -0
  142. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/urls.py +0 -0
  143. {django-pfx-1.4.dev36 → django_pfx-1.4.dev38}/tests/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev36
3
+ Version: 1.4.dev38
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev36
3
+ Version: 1.4.dev38
4
4
  Summary: Django PFX is a toolkit designed to streamline the development of RESTful APIs using the Django framework.
5
5
  Author: Hervé Martinet
6
6
  Author-email: herve.martinet@gmail.com
@@ -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 `activate` service to get the URI,
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 `confirm` service must be called with an OTP code retrieved
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 :** `PUT` `/auth/otp/activate`
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/confirm`
230
+ **Request :** `PUT` `/auth/otp/enable`
231
231
 
232
232
  **Request body:**
233
233
 
@@ -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-11 11:40+0200\n"
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:15
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:19
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:21
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:23
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:404
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:413
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:338
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:340
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:446
225
- msgid "OTP is already activated"
226
- msgstr "L'OTP est déjà activé"
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:485 views/authentication_views.py:519
232
+ #: views/authentication_views.py:490 views/authentication_views.py:524
233
233
  msgid "Invalid OTP code"
234
234
  msgstr "Code OTP invalide"
235
235
 
236
- #: views/authentication_views.py:518
237
- msgid "OTP is disabled"
238
- msgstr "L'OTP est désactivé"
236
+ #: views/authentication_views.py:523
237
+ msgid "OTP disabled"
238
+ msgstr "OTP désactivé"
239
239
 
240
- #: views/authentication_views.py:778
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:852
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
- return {
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/activate", public=False, method="put", priority_doc=52)
418
- def otp_activate(self, *args, **kwargs):
419
- """Entrypoint for :code:`PUT /otp/activate` route.
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
- Activate the OTP authentication and return the setup URI.
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: Activate OTP
428
- description: A service to activate OTP authentication for
429
- the user. Returns a setup URI to build a QR code. You
430
- have to call the confirm service with a valid code to
431
- fully activate OTP authentication.
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 activated")), status=400)
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/confirm", public=False, method="put", priority_doc=53,
458
+ "/otp/enable", public=False, method="put", priority_doc=53,
454
459
  response_schema='message_schema')
455
- def otp_confirm(self, *args, **kwargs):
456
- """Entrypoint for :code:`PUT /otp/confirm` route.
460
+ def otp_enable(self, *args, **kwargs):
461
+ """Entrypoint for :code:`PUT /otp/enable` route.
457
462
 
458
- Confirm the OTP authentification activation.
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: Confirm OTP
465
- description: A service to confirm the OTP activation with a
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 is activated")))
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 authentification.
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 is disabled")))
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)
@@ -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 test_otp_activate(self):
887
+ def test_otp_enable(self):
888
888
  self.client.login(username='jrr.tolkien', password='RIGHT PASSWORD')
889
889
 
890
- response = self.client.put('/api/auth/otp/activate')
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,25 +899,26 @@ 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/confirm', dict(
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 is activated")
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 test_otp_activate_bad_code(self):
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.put('/api/auth/otp/activate')
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/confirm', dict(
921
+ response = self.client.put('/api/auth/otp/enable', dict(
920
922
  otp_code='-'))
921
923
  self.assertRC(response, 422)
922
924
  self.assertJE(response, 'message', "Invalid OTP code")
@@ -924,25 +926,27 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
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 test_otp_activate_already_activated(self):
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.put('/api/auth/otp/activate')
933
+ response = self.client.get('/api/auth/otp/setup-uri')
932
934
  self.assertRC(response, 400)
933
- self.assertJE(response, 'message', "OTP is already activated")
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 is disabled")
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 is disabled")
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)
@@ -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
- ressource_name='John Ronald Reuel Tolkien')
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)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes