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.
Files changed (145) hide show
  1. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/.gitlab-ci.yml +20 -7
  2. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/PKG-INFO +1 -1
  3. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/PKG-INFO +1 -1
  4. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/SOURCES.txt +6 -2
  5. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/top_level.txt +1 -0
  6. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/authentication.md +4 -4
  7. django_pfx-1.4.dev38/make_messages +3 -0
  8. django_pfx-1.4.dev38/manage.py +37 -0
  9. django_pfx-1.4.dev38/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  10. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +35 -34
  11. django_pfx-1.4.dev38/pfx/pfxcore/migrations/0001_initial.py +59 -0
  12. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/otp_user_mixin.py +5 -0
  13. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/authentication_views.py +26 -21
  14. django_pfx-1.4.dev38/pfx/settings/dev.py +8 -0
  15. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/setup.cfg +1 -0
  16. django_pfx-1.4.dev38/tests/__init__.py +0 -0
  17. django_pfx-1.4.dev38/tests/settings/__init__.py +0 -0
  18. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_auth_api.py +18 -13
  19. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_shortcuts.py +1 -1
  20. django-pfx-1.4.dev30/django-admin-test +0 -10
  21. django-pfx-1.4.dev30/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  22. django-pfx-1.4.dev30/runtest.py +0 -17
  23. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/.gitignore +0 -0
  24. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/.pre-commit-config.yaml +0 -0
  25. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/LICENSE +0 -0
  26. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/MANIFEST.in +0 -0
  27. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/README.md +0 -0
  28. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/dependency_links.txt +0 -0
  29. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/django_pfx.egg-info/requires.txt +0 -0
  30. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/Makefile +0 -0
  31. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/conf.py +0 -0
  32. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/index.rst +0 -0
  33. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/api.views.rst +0 -0
  34. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/decorator.md +0 -0
  35. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/generate_openapi.md +0 -0
  36. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/getting_started.md +0 -0
  37. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/internationalisation.md +0 -0
  38. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/model.md +0 -0
  39. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/pfx_views.md +0 -0
  40. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/profiling.md +0 -0
  41. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/settings.md +0 -0
  42. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/doc/source/testing.md +0 -0
  43. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/img/pfx.png +0 -0
  44. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/img/pfx.svg +0 -0
  45. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/__init__.py +0 -0
  46. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/__init__.py +0 -0
  47. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/__init__.py +0 -0
  48. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/parameters.py +0 -0
  49. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/schema.py +0 -0
  50. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apidoc/tags.py +0 -0
  51. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/apps.py +0 -0
  52. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/__init__.py +0 -0
  53. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/decorator/rest.py +0 -0
  54. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/default_settings.py +0 -0
  55. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/exceptions.py +0 -0
  56. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/fields.py +0 -0
  57. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/http/__init__.py +0 -0
  58. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/http/json_response.py +0 -0
  59. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/__init__.py +0 -0
  60. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/__init__.py +0 -0
  61. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  62. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/management/commands/profile.py +0 -0
  63. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/__init__.py +0 -0
  64. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/authentication.py +0 -0
  65. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/locale.py +0 -0
  66. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/middleware/profiling.py +0 -0
  67. {django-pfx-1.4.dev30/pfx/pfxcore/serializers → django_pfx-1.4.dev38/pfx/pfxcore/migrations}/__init__.py +0 -0
  68. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/__init__.py +0 -0
  69. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  70. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/cache_mixins.py +0 -0
  71. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/login_ban.py +0 -0
  72. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/not_null_fields.py +0 -0
  73. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_models.py +0 -0
  74. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/pfx_user.py +0 -0
  75. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  76. {django-pfx-1.4.dev30/tests → django_pfx-1.4.dev38/pfx/pfxcore/serializers}/__init__.py +0 -0
  77. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/serializers/json.py +0 -0
  78. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/settings.py +0 -0
  79. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/shortcuts.py +0 -0
  80. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/__init__.py +0 -0
  81. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/storage/s3_storage.py +0 -0
  82. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  83. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  84. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  85. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  86. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  87. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  88. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/test.py +0 -0
  89. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/urls.py +0 -0
  90. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/__init__.py +0 -0
  91. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/fields.py +0 -0
  92. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/filters_views.py +0 -0
  93. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/locale_views.py +0 -0
  94. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  95. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  96. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/groups.py +0 -0
  97. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  98. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  99. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  100. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  101. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  102. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  103. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  104. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  105. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  106. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset.py +0 -0
  107. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  108. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  109. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  110. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  111. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  112. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pfx/pfxcore/views/rest_views.py +0 -0
  113. {django-pfx-1.4.dev30/tests → django_pfx-1.4.dev38/pfx}/settings/__init__.py +0 -0
  114. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/pyproject.toml +0 -0
  115. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/requirements.txt +0 -0
  116. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/serve-doc +0 -0
  117. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/setup.py +0 -0
  118. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  119. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/models.py +0 -0
  120. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/ci.py +0 -0
  121. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/common.py +0 -0
  122. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev.py +0 -0
  123. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev_custom_example.py +0 -0
  124. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/settings/dev_default.py +0 -0
  125. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/__init__.py +0 -0
  126. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/basic_api_errors.py +0 -0
  127. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/basic_api_test.py +0 -0
  128. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_api_doc.py +0 -0
  129. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_body_mixin.py +0 -0
  130. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_cache.py +0 -0
  131. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_client.py +0 -0
  132. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_fields.py +0 -0
  133. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_filters.py +0 -0
  134. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_locale_api.py +0 -0
  135. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_perm_tests.py +0 -0
  136. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_perms_api.py +0 -0
  137. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_profiling_middleware.py +0 -0
  138. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_settings.py +0 -0
  139. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_timezone_middleware.py +0 -0
  140. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_tools.py +0 -0
  141. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_user_queryset.py +0 -0
  142. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_view_decorators.py +0 -0
  143. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/tests/test_view_fields.py +0 -0
  144. {django-pfx-1.4.dev30 → django_pfx-1.4.dev38}/tests/urls.py +0 -0
  145. {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
- TEST_SETTINGS_MODULE: 'tests.settings.ci'
43
+ DJANGO_SETTINGS_MODULE: 'tests.settings.ci'
32
44
  script:
33
45
  - apt-get update
34
46
  - apt-get install -y gettext
35
- - flake8 .
36
- - isort . -c
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
- - (cd pfx/pfxcore/ && django-admin compilemessages)
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/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-pfx
3
- Version: 1.4.dev30
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.dev30
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
@@ -4,10 +4,10 @@
4
4
  LICENSE
5
5
  MANIFEST.in
6
6
  README.md
7
- django-admin-test
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 `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
 
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ (cd pfx/pfxcore && django-admin makemessages -a)
3
+ (cd tests/tests && django-admin makemessages -a)
@@ -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()
@@ -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-08 13:51+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"
@@ -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:43
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:45
62
+ #: models/login_ban.py:43
63
63
  msgid "Username"
64
64
  msgstr "Nom d’utilisateur"
65
65
 
66
- #: models/login_ban.py:46
66
+ #: models/login_ban.py:44
67
67
  msgid "Failed counter"
68
68
  msgstr "Compteur d’échec"
69
69
 
70
- #: models/login_ban.py:47
70
+ #: models/login_ban.py:45
71
71
  msgid "Last failed"
72
72
  msgstr "Dernier échec"
73
73
 
74
- #: models/login_ban.py:52
74
+ #: models/login_ban.py:50
75
75
  msgid "Login ban"
76
76
  msgstr "Login banni"
77
77
 
78
- #: models/login_ban.py:53
78
+ #: models/login_ban.py:51
79
79
  msgid "Login bans"
80
80
  msgstr "Login bannis"
81
81
 
82
- #: models/otp_user_mixin.py:10
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:13
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:14
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:15
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:52
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:68
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:84
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:105
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:82
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:244 views/authentication_views.py:407
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:249
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:252 views/authentication_views.py:416
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:341
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:343
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:449
224
- msgid "OTP is already activated"
225
- msgstr "L'OTP est déjà activé"
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:488 views/authentication_views.py:522
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:521
236
- msgid "OTP is disabled"
237
- msgstr "L'OTP est désactivé"
236
+ #: views/authentication_views.py:523
237
+ msgid "OTP disabled"
238
+ msgstr "OTP désactivé"
238
239
 
239
- #: views/authentication_views.py:777
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:845
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
- 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)
@@ -0,0 +1,8 @@
1
+ SECRET_KEY = 'fake-key'
2
+ INSTALLED_APPS = [
3
+ 'django.contrib.auth',
4
+ 'django.contrib.contenttypes',
5
+ 'django.contrib.postgres',
6
+ 'pfx.pfxcore',
7
+ ]
8
+ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
@@ -53,6 +53,7 @@ exclude =
53
53
  [flake8]
54
54
  per-file-ignores =
55
55
  __init__.py: F401,F403
56
+ exclude = .git,__pycache__,venv
56
57
 
57
58
  [isort]
58
59
  skip_glob = */venv/*
File without changes
File without changes