django-pfx 1.4.dev122__tar.gz → 1.4.dev126__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 (196) hide show
  1. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/PKG-INFO +1 -1
  2. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +16 -16
  4. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/abstract_pfx_base_user.py +4 -0
  5. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/authentication_views.py +12 -1
  6. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/rest_views.py +4 -4
  7. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_auth_api.py +31 -3
  8. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/views.py +1 -1
  9. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/.gitignore +0 -0
  10. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/.gitlab-ci.yml +0 -0
  11. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/.pre-commit-config.yaml +0 -0
  12. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/LICENSE +0 -0
  13. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/MANIFEST.in +0 -0
  14. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/README.md +0 -0
  15. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/django_pfx.egg-info/SOURCES.txt +0 -0
  16. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/django_pfx.egg-info/dependency_links.txt +0 -0
  17. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/django_pfx.egg-info/requires.txt +0 -0
  18. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/django_pfx.egg-info/top_level.txt +0 -0
  19. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/Makefile +0 -0
  20. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/conf.py +0 -0
  21. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/index.rst +0 -0
  22. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/api.views.rst +0 -0
  23. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/authentication.md +0 -0
  24. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/decorator.md +0 -0
  25. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/generate_openapi.md +0 -0
  26. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/getting_started.md +0 -0
  27. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/internationalisation.md +0 -0
  28. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/model.md +0 -0
  29. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/pfx_views.md +0 -0
  30. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/profiling.md +0 -0
  31. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/settings.md +0 -0
  32. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/doc/source/testing.md +0 -0
  33. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/img/pfx.png +0 -0
  34. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/img/pfx.svg +0 -0
  35. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/make_messages +0 -0
  36. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/manage.py +0 -0
  37. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/__init__.py +0 -0
  38. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/__init__.py +0 -0
  39. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/apidoc/__init__.py +0 -0
  40. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/apidoc/parameters.py +0 -0
  41. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/apidoc/schema.py +0 -0
  42. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/apidoc/tags.py +0 -0
  43. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/apps.py +0 -0
  44. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/decorator/__init__.py +0 -0
  45. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/decorator/rest.py +0 -0
  46. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/default_settings.py +0 -0
  47. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/exceptions.py +0 -0
  48. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/fields/__init__.py +0 -0
  49. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/fields/decimal_field.py +0 -0
  50. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/fields/media_field.py +0 -0
  51. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/fields/minutes_duration_field.py +0 -0
  52. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/fields/nh3_field.py +0 -0
  53. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/http/__init__.py +0 -0
  54. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/http/json_response.py +0 -0
  55. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  56. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/management/__init__.py +0 -0
  57. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/management/commands/__init__.py +0 -0
  58. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  59. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/management/commands/profile.py +0 -0
  60. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/middleware/__init__.py +0 -0
  61. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/middleware/authentication.py +0 -0
  62. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/middleware/locale.py +0 -0
  63. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/middleware/profiling.py +0 -0
  64. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  65. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  66. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  67. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/__init__.py +0 -0
  68. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/operations/__init__.py +0 -0
  69. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/migrations/operations/permissions.py +0 -0
  70. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/__init__.py +0 -0
  71. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/cache_mixins.py +0 -0
  72. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/login_ban.py +0 -0
  73. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/not_null_fields.py +0 -0
  74. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/ordered_model_mixin.py +0 -0
  75. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
  76. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/pfx_models.py +0 -0
  77. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/pfx_user.py +0 -0
  78. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  79. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/serializers/__init__.py +0 -0
  80. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/serializers/json.py +0 -0
  81. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/settings.py +0 -0
  82. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/shortcuts.py +0 -0
  83. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/storage/__init__.py +0 -0
  84. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/storage/exceptions.py +0 -0
  85. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/storage/local_storage.py +0 -0
  86. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/storage/s3_storage.py +0 -0
  87. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  88. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  89. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  90. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  91. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  92. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  93. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/test.py +0 -0
  94. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/urls.py +0 -0
  95. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/__init__.py +0 -0
  96. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/fields.py +0 -0
  97. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/filters_views.py +0 -0
  98. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/locale_views.py +0 -0
  99. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/media_rest_view_mixin.py +0 -0
  100. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/ordered_rest_view_mixin.py +0 -0
  101. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  102. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  103. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/groups.py +0 -0
  104. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  105. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  106. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  107. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  108. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  109. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  110. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  111. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  112. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  113. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset.py +0 -0
  114. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  115. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  116. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  117. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  118. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  119. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/settings/__init__.py +0 -0
  120. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pfx/settings/dev.py +0 -0
  121. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/pyproject.toml +0 -0
  122. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/requirements.txt +0 -0
  123. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/serve-doc +0 -0
  124. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/setup.cfg +0 -0
  125. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/setup.py +0 -0
  126. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/__init__.py +0 -0
  127. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/apps.py +0 -0
  128. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  129. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/migrations/0001_initial.py +0 -0
  130. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/migrations/0002_alter_book_cover.py +0 -0
  131. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/migrations/0003_book_local_file.py +0 -0
  132. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/migrations/__init__.py +0 -0
  133. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/models.py +0 -0
  134. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/__init__.py +0 -0
  135. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/ci.py +0 -0
  136. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/common.py +0 -0
  137. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/dev.py +0 -0
  138. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/dev_custom_example.py +0 -0
  139. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/settings/dev_default.py +0 -0
  140. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/__init__.py +0 -0
  141. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/basic_api_errors.py +0 -0
  142. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/basic_api_test.py +0 -0
  143. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_api_doc.py +0 -0
  144. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_api_doc_search.py +0 -0
  145. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_body_mixin.py +0 -0
  146. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_cache.py +0 -0
  147. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_client.py +0 -0
  148. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_fields_choices.py +0 -0
  149. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_fields_decimal.py +0 -0
  150. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_fields_minutes_duration.py +0 -0
  151. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_fields_nh3.py +0 -0
  152. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_filters.py +0 -0
  153. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_locale_api.py +0 -0
  154. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_ordered_rest_view_mixin.py +0 -0
  155. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_perm_tests.py +0 -0
  156. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_permissions.py +0 -0
  157. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_perms_api.py +0 -0
  158. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_post_migrate_groups_update.py +0 -0
  159. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_profiling_middleware.py +0 -0
  160. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_settings.py +0 -0
  161. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_shortcuts.py +0 -0
  162. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_timezone_middleware.py +0 -0
  163. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_tools.py +0 -0
  164. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_user_queryset.py +0 -0
  165. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_view_decorators.py +0 -0
  166. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/tests/test_view_fields.py +0 -0
  167. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests/urls.py +0 -0
  168. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/__init__.py +0 -0
  169. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/migrations/0001_initial.py +0 -0
  170. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/migrations/__init__.py +0 -0
  171. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/__init__.py +0 -0
  172. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/ci.py +0 -0
  173. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/common.py +0 -0
  174. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/dev.py +0 -0
  175. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/dev_custom_example.py +0 -0
  176. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/settings/dev_default.py +0 -0
  177. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/tests/__init__.py +0 -0
  178. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/tests/test_api.py +0 -0
  179. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/tests/test_auth_api.py +0 -0
  180. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/urls.py +0 -0
  181. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_base_user/views.py +0 -0
  182. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/__init__.py +0 -0
  183. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/migrations/0001_initial.py +0 -0
  184. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/migrations/__init__.py +0 -0
  185. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/models.py +0 -0
  186. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/__init__.py +0 -0
  187. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/ci.py +0 -0
  188. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/common.py +0 -0
  189. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/dev.py +0 -0
  190. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/dev_custom_example.py +0 -0
  191. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/settings/dev_default.py +0 -0
  192. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/tests/__init__.py +0 -0
  193. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/tests/test_api.py +0 -0
  194. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/tests/test_auth_api.py +0 -0
  195. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/urls.py +0 -0
  196. {django_pfx-1.4.dev122 → django_pfx-1.4.dev126}/tests_custom_user/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-pfx
3
- Version: 1.4.dev122
3
+ Version: 1.4.dev126
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.4
2
2
  Name: django-pfx
3
- Version: 1.4.dev122
3
+ Version: 1.4.dev126
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
@@ -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: 2025-04-16 11:28+0200\n"
10
+ "POT-Creation-Date: 2025-05-06 11:42+0200\n"
11
11
  "PO-Revision-Date: 2021-06-22 23:31+0200\n"
12
12
  "Last-Translator: \n"
13
13
  "Language-Team: \n"
@@ -59,11 +59,11 @@ 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/abstract_pfx_base_user.py:49
62
+ #: models/abstract_pfx_base_user.py:52
63
63
  msgid "user"
64
64
  msgstr ""
65
65
 
66
- #: models/abstract_pfx_base_user.py:50
66
+ #: models/abstract_pfx_base_user.py:53
67
67
  msgid "users"
68
68
  msgstr ""
69
69
 
@@ -103,7 +103,7 @@ msgstr "Compte HOTP"
103
103
  msgid "HOTP expiry"
104
104
  msgstr "Expiration HOTP"
105
105
 
106
- #: models/otp_user_mixin.py:29 views/authentication_views.py:492
106
+ #: models/otp_user_mixin.py:29 views/authentication_views.py:503
107
107
  msgid "OTP enabled"
108
108
  msgstr "OTP activé"
109
109
 
@@ -209,7 +209,7 @@ msgstr "Bienvenue sur %(site_name)s."
209
209
  msgid "Welcome on %(site_name)s"
210
210
  msgstr "Bienvenue sur %(site_name)s"
211
211
 
212
- #: views/authentication_views.py:82
212
+ #: views/authentication_views.py:83
213
213
  #, python-brace-format
214
214
  msgid ""
215
215
  "Your connection is temporarily disabled after several unsuccessful attempts, "
@@ -218,43 +218,43 @@ msgstr ""
218
218
  "Votre connexion est temporairement désactivée après plusieurs tentatives "
219
219
  "infructueuses, veuillez réessayer dans {seconds} secondes."
220
220
 
221
- #: views/authentication_views.py:174
221
+ #: views/authentication_views.py:177
222
222
  msgid "Successful login"
223
223
  msgstr "Connexion réussie"
224
224
 
225
- #: views/authentication_views.py:244 views/authentication_views.py:409
225
+ #: views/authentication_views.py:250 views/authentication_views.py:420
226
226
  msgid "password updated successfully"
227
227
  msgstr "le mot de passe a été mis à jour avec succès"
228
228
 
229
- #: views/authentication_views.py:249
229
+ #: views/authentication_views.py:255
230
230
  msgid "Incorrect password"
231
231
  msgstr "Mot de passe incorrect"
232
232
 
233
- #: views/authentication_views.py:252 views/authentication_views.py:418
233
+ #: views/authentication_views.py:258 views/authentication_views.py:429
234
234
  msgid "Empty password is not allowed"
235
235
  msgstr "Un mot de passe vide n’est pas autorisé"
236
236
 
237
- #: views/authentication_views.py:343
237
+ #: views/authentication_views.py:349
238
238
  msgid "User and token are valid"
239
239
  msgstr "L'utilisateur et le token sont valides"
240
240
 
241
- #: views/authentication_views.py:345
241
+ #: views/authentication_views.py:351
242
242
  msgid "User or token is invalid"
243
243
  msgstr "L'utilisateur ou le token est invalide"
244
244
 
245
- #: views/authentication_views.py:452
245
+ #: views/authentication_views.py:463
246
246
  msgid "OTP is already enabled"
247
247
  msgstr "OTP est déjà activé"
248
248
 
249
- #: views/authentication_views.py:493 views/authentication_views.py:529
249
+ #: views/authentication_views.py:504 views/authentication_views.py:540
250
250
  msgid "Invalid code"
251
251
  msgstr "Code invalide"
252
252
 
253
- #: views/authentication_views.py:528
253
+ #: views/authentication_views.py:539
254
254
  msgid "OTP disabled"
255
255
  msgstr "OTP désactivé"
256
256
 
257
- #: views/authentication_views.py:789
257
+ #: views/authentication_views.py:800
258
258
  msgid ""
259
259
  "If the email address you entered is correct, you will receive an email from "
260
260
  "us with instructions to reset your password."
@@ -263,7 +263,7 @@ msgstr ""
263
263
  "un courrier électronique de notre part contenant des instructions pour "
264
264
  "réinitialiser votre mot de passe."
265
265
 
266
- #: views/authentication_views.py:863
266
+ #: views/authentication_views.py:874
267
267
  msgid "A new authentication code has been sent by email."
268
268
  msgstr "Un nouveau code d'authentification a été envoyé par e-mail."
269
269
 
@@ -41,6 +41,9 @@ class AbstractPFXBaseUser(PFXModelMixin, AbstractBaseUser):
41
41
  """
42
42
  return self.password
43
43
 
44
+ def on_user_set_password(self, first_time=False):
45
+ pass
46
+
44
47
 
45
48
  class AbstractPFXUser(AbstractUser, AbstractPFXBaseUser):
46
49
  """The base abstract user for PFX with permissions mixin."""
@@ -56,5 +59,6 @@ class AbstractPFXUser(AbstractUser, AbstractPFXBaseUser):
56
59
  last_name=self.last_name,
57
60
  is_staff=self.is_staff,
58
61
  date_joined=self.date_joined,
62
+ last_login=self.last_login,
59
63
  **kw)
60
64
  return res
@@ -12,6 +12,7 @@ from django.core.mail import EmailMultiAlternatives
12
12
  from django.core.validators import validate_email
13
13
  from django.db import transaction
14
14
  from django.template import loader
15
+ from django.utils import timezone as tz
15
16
  from django.utils.decorators import method_decorator
16
17
  from django.utils.encoding import force_bytes
17
18
  from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
@@ -154,6 +155,8 @@ class AuthenticationView(
154
155
 
155
156
  def _login_success(
156
157
  self, user, mode, remember_me=False, message=None):
158
+ user.last_login = tz.now()
159
+ user.save(update_fields=['last_login'])
157
160
  token = self._prepare_token(user, mode, remember_me)
158
161
  if mode == 'cookie':
159
162
  if remember_me:
@@ -240,6 +243,9 @@ class AuthenticationView(
240
243
  user)
241
244
  user.set_password(data.get('new_password'))
242
245
  user.save()
246
+
247
+ user.on_user_set_password(first_time=False)
248
+
243
249
  return JsonResponse({
244
250
  'message': _('password updated successfully')
245
251
  })
@@ -399,9 +405,14 @@ class AuthenticationView(
399
405
  self.token_generator.check_token(user, data['token'])):
400
406
  password_validation.validate_password(data['password'], user)
401
407
  user.set_password(data['password'])
402
- user.is_active = True
403
408
  user.save()
404
409
  user.refresh_from_db()
410
+
411
+ if not user.last_login:
412
+ user.on_user_set_password(first_time=True)
413
+ else:
414
+ user.on_user_set_password(first_time=False)
415
+
405
416
  if 'autologin' in data and data['autologin'] in (
406
417
  'jwt', 'cookie'):
407
418
  return self._login_success(user, data['autologin'])
@@ -838,7 +838,7 @@ class ListRestViewMixin(ModelResponseMixin):
838
838
  limit=limit,
839
839
  offset=offset)
840
840
 
841
- def _get_list_extra_meta(self):
841
+ def _get_list_extra_meta(self, res):
842
842
  return {}
843
843
 
844
844
  def _get_list(self, *args, **kwargs):
@@ -854,14 +854,14 @@ class ListRestViewMixin(ModelResponseMixin):
854
854
  qs, meta['subset'] = self.offset_result(qs)
855
855
  elif settings.PFX_MAX_LIST_RESULT_SIZE:
856
856
  qs = qs[:settings.PFX_MAX_LIST_RESULT_SIZE]
857
- meta.update(self._get_list_extra_meta())
858
- if meta:
859
- res['meta'] = meta
860
857
  if get_bool(self.request.GET, 'items', 'count' not in meta):
861
858
  if self.request.GET.get('mode', 'list') == 'select':
862
859
  res['items'] = list(self.get_short_list_result(qs))
863
860
  else:
864
861
  res['items'] = list(self.get_list_result(qs))
862
+ meta.update(self._get_list_extra_meta(res))
863
+ if meta:
864
+ res['meta'] = meta
865
865
  return JsonResponse(res)
866
866
 
867
867
  @rest_api(
@@ -2,6 +2,7 @@ import logging
2
2
  import re
3
3
  from datetime import datetime, timedelta
4
4
  from http.cookies import SimpleCookie
5
+ from unittest.mock import patch
5
6
  from urllib.parse import parse_qs, urlparse
6
7
 
7
8
  from django.conf import settings
@@ -54,7 +55,6 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
54
55
  '/api/auth/login', {
55
56
  'username': 'jrr.tolkien',
56
57
  'password': 'RIGHT PASSWORD'})
57
-
58
58
  self.assertRC(response, 200)
59
59
  token = self.get_val(response, 'token')
60
60
  headers = jwt.get_unverified_header(token)
@@ -66,6 +66,9 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
66
66
  datetime.utcnow() + timedelta(minutes=25) <
67
67
  datetime.utcfromtimestamp(decoded['exp'])
68
68
  < datetime.utcnow() + timedelta(minutes=35))
69
+ self.assertEqual(
70
+ self.get_val(response, 'user.last_login')[0:17],
71
+ "2023-05-01T08:00:")
69
72
 
70
73
  @override_settings(PFX_TOKEN_LONG_VALIDITY={'minutes': 60})
71
74
  def test_valid_login_remember_me(self):
@@ -286,7 +289,8 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
286
289
  sameSite = re.findall(regex, str(cookie))[0]
287
290
  self.assertEqual(sameSite, 'Lax')
288
291
 
289
- def test_valid_change_password(self):
292
+ @patch.object(User, 'on_user_set_password')
293
+ def test_valid_change_password(self, mock_on_usr_set_pwd):
290
294
  self.client.login(
291
295
  username='jrr.tolkien',
292
296
  password='RIGHT PASSWORD')
@@ -295,7 +299,9 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
295
299
  'old_password': 'RIGHT PASSWORD',
296
300
  'new_password': 'NEW RIGHT PASSWORD'})
297
301
  self.assertRC(response, 200)
302
+ mock_on_usr_set_pwd.assert_called_once_with(first_time=False)
298
303
 
304
+ mock_on_usr_set_pwd.reset_mock()
299
305
  self.cookie_client.login(
300
306
  username='jrr.tolkien',
301
307
  password='NEW RIGHT PASSWORD')
@@ -304,6 +310,7 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
304
310
  'old_password': 'NEW RIGHT PASSWORD',
305
311
  'new_password': 'RIGHT PASSWORD'})
306
312
  self.assertRC(response, 200)
313
+ mock_on_usr_set_pwd.assert_called_once_with(first_time=False)
307
314
 
308
315
  @override_settings(AUTH_PASSWORD_VALIDATORS=[{
309
316
  'NAME':
@@ -717,7 +724,8 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
717
724
  self.assertRC(response, 200)
718
725
  self.assertJE(response, '__all__.@0', "User and token are valid")
719
726
 
720
- def test_set_password(self):
727
+ @patch.object(User, 'on_user_set_password')
728
+ def test_set_password(self, mock_on_usr_set_pwd):
721
729
  # Try with a wrong token and uid
722
730
  response = self.client.post(
723
731
  '/api/auth/set-password', {
@@ -738,6 +746,26 @@ class AuthAPITest(TestAssertMixin, TransactionTestCase):
738
746
  })
739
747
  self.assertRC(response, 200)
740
748
  self.assertJE(response, 'message', "password updated successfully")
749
+ mock_on_usr_set_pwd.assert_called_once_with(first_time=True)
750
+
751
+ @patch.object(User, 'on_user_set_password')
752
+ def test_set_password_active_user(self, mock_on_usr_set_pwd):
753
+ # Set the last_login datetime, so that the user is an active user.
754
+ self.user1.last_login = datetime.now()
755
+ self.user1.save()
756
+
757
+ # Then try with a valid token and uid
758
+ token = default_token_generator.make_token(self.user1)
759
+ uid = urlsafe_base64_encode(force_bytes(self.user1.pk))
760
+ response = self.client.post(
761
+ '/api/auth/set-password', {
762
+ 'token': token,
763
+ 'uidb64': uid,
764
+ 'password': 'NEW PASSWORD',
765
+ })
766
+ self.assertRC(response, 200)
767
+ self.assertJE(response, 'message', "password updated successfully")
768
+ mock_on_usr_set_pwd.assert_called_once_with(first_time=False)
741
769
 
742
770
  @override_settings(AUTH_PASSWORD_VALIDATORS=[{
743
771
  'NAME':
@@ -129,7 +129,7 @@ class AuthorRestView(AuthorRestViewMixin, SlugDetailRestViewMixin, RestView):
129
129
  class AuthorExtraMetaRestView(AuthorRestViewMixin, RestView):
130
130
  default_public = True
131
131
 
132
- def _get_list_extra_meta(self):
132
+ def _get_list_extra_meta(self, res):
133
133
  return dict(test="Hello world")
134
134
 
135
135
 
File without changes