django-pfx 1.4.dev70__tar.gz → 1.4.dev74__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 (182) hide show
  1. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/PKG-INFO +1 -1
  2. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/django_pfx.egg-info/PKG-INFO +1 -1
  3. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/django_pfx.egg-info/SOURCES.txt +3 -0
  4. django_pfx-1.4.dev74/pfx/pfxcore/apps.py +31 -0
  5. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/decorator/rest.py +3 -3
  6. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/default_settings.py +3 -0
  7. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.po +1 -1
  8. django_pfx-1.4.dev74/pfx/pfxcore/migrations/__init__.py +3 -0
  9. django_pfx-1.4.dev74/pfx/pfxcore/migrations/operations/__init__.py +1 -0
  10. django_pfx-1.4.dev74/pfx/pfxcore/migrations/operations/permissions.py +87 -0
  11. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/shortcuts.py +5 -2
  12. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/rest_views.py +7 -1
  13. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/migrations/0001_initial.py +2 -1
  14. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/models.py +1 -0
  15. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/__init__.py +1 -0
  16. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_permissions.py +46 -0
  17. django_pfx-1.4.dev74/tests/tests/test_post_migrate_groups_update.py +79 -0
  18. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/views.py +9 -1
  19. django_pfx-1.4.dev70/pfx/pfxcore/apps.py +0 -9
  20. django_pfx-1.4.dev70/tests_custom_user/settings/__init__.py +0 -0
  21. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/.gitignore +0 -0
  22. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/.gitlab-ci.yml +0 -0
  23. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/.pre-commit-config.yaml +0 -0
  24. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/LICENSE +0 -0
  25. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/MANIFEST.in +0 -0
  26. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/README.md +0 -0
  27. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/django_pfx.egg-info/dependency_links.txt +0 -0
  28. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/django_pfx.egg-info/requires.txt +0 -0
  29. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/django_pfx.egg-info/top_level.txt +0 -0
  30. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/Makefile +0 -0
  31. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/conf.py +0 -0
  32. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/index.rst +0 -0
  33. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/api.views.rst +0 -0
  34. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/authentication.md +0 -0
  35. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/decorator.md +0 -0
  36. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/generate_openapi.md +0 -0
  37. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/getting_started.md +0 -0
  38. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/internationalisation.md +0 -0
  39. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/model.md +0 -0
  40. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/pfx_views.md +0 -0
  41. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/profiling.md +0 -0
  42. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/settings.md +0 -0
  43. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/doc/source/testing.md +0 -0
  44. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/img/pfx.png +0 -0
  45. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/img/pfx.svg +0 -0
  46. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/make_messages +0 -0
  47. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/manage.py +0 -0
  48. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/__init__.py +0 -0
  49. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/__init__.py +0 -0
  50. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/apidoc/__init__.py +0 -0
  51. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/apidoc/parameters.py +0 -0
  52. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/apidoc/schema.py +0 -0
  53. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/apidoc/tags.py +0 -0
  54. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/decorator/__init__.py +0 -0
  55. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/exceptions.py +0 -0
  56. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/fields.py +0 -0
  57. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/http/__init__.py +0 -0
  58. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/http/json_response.py +0 -0
  59. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/locale/fr/LC_MESSAGES/django.mo +0 -0
  60. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/management/__init__.py +0 -0
  61. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/management/commands/__init__.py +0 -0
  62. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/management/commands/makeapidoc.py +0 -0
  63. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/management/commands/profile.py +0 -0
  64. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/middleware/__init__.py +0 -0
  65. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/middleware/authentication.py +0 -0
  66. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/middleware/locale.py +0 -0
  67. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/middleware/profiling.py +0 -0
  68. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/migrations/0001_initial.py +0 -0
  69. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/migrations/0002_pfxpermissionsuser.py +0 -0
  70. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py +0 -0
  71. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/__init__.py +0 -0
  72. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/abstract_pfx_base_user.py +0 -0
  73. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/cache_mixins.py +0 -0
  74. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/login_ban.py +0 -0
  75. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/not_null_fields.py +0 -0
  76. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/otp_user_mixin.py +0 -0
  77. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/pfx_models.py +0 -0
  78. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/pfx_user.py +0 -0
  79. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/models/user_filtered_queryset_mixin.py +0 -0
  80. {django_pfx-1.4.dev70/pfx/pfxcore/migrations → django_pfx-1.4.dev74/pfx/pfxcore/serializers}/__init__.py +0 -0
  81. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/serializers/json.py +0 -0
  82. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/settings.py +0 -0
  83. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/storage/__init__.py +0 -0
  84. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/storage/s3_storage.py +0 -0
  85. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/otp_code_email.txt +0 -0
  86. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/otp_code_subject.txt +0 -0
  87. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/password_reset_email.txt +0 -0
  88. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/password_reset_subject.txt +0 -0
  89. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/welcome_email.txt +0 -0
  90. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/templates/registration/welcome_subject.txt +0 -0
  91. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/test.py +0 -0
  92. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/urls.py +0 -0
  93. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/__init__.py +0 -0
  94. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/authentication_views.py +0 -0
  95. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/fields.py +0 -0
  96. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/filters_views.py +0 -0
  97. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/locale_views.py +0 -0
  98. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/__init__.py +0 -0
  99. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/date_format.py +0 -0
  100. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/groups.py +0 -0
  101. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/list_count.py +0 -0
  102. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/list_items.py +0 -0
  103. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/list_mode.py +0 -0
  104. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/list_order.py +0 -0
  105. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/list_search.py +0 -0
  106. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/media_redirect.py +0 -0
  107. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/meta_fields.py +0 -0
  108. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/meta_filters.py +0 -0
  109. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/meta_orders.py +0 -0
  110. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset.py +0 -0
  111. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset_limit.py +0 -0
  112. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset_offset.py +0 -0
  113. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset_page.py +0 -0
  114. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset_page_size.py +0 -0
  115. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/pfxcore/views/parameters/subset_page_subset.py +0 -0
  116. {django_pfx-1.4.dev70/pfx/pfxcore/serializers → django_pfx-1.4.dev74/pfx/settings}/__init__.py +0 -0
  117. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pfx/settings/dev.py +0 -0
  118. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/pyproject.toml +0 -0
  119. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/requirements.txt +0 -0
  120. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/serve-doc +0 -0
  121. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/setup.cfg +0 -0
  122. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/setup.py +0 -0
  123. {django_pfx-1.4.dev70/pfx/settings → django_pfx-1.4.dev74/tests}/__init__.py +0 -0
  124. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/locale/fr/LC_MESSAGES/django.po +0 -0
  125. {django_pfx-1.4.dev70/tests → django_pfx-1.4.dev74/tests/migrations}/__init__.py +0 -0
  126. {django_pfx-1.4.dev70/tests/migrations → django_pfx-1.4.dev74/tests/settings}/__init__.py +0 -0
  127. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/settings/ci.py +0 -0
  128. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/settings/common.py +0 -0
  129. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/settings/dev.py +0 -0
  130. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/settings/dev_custom_example.py +0 -0
  131. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/settings/dev_default.py +0 -0
  132. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/basic_api_errors.py +0 -0
  133. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/basic_api_test.py +0 -0
  134. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_api_doc.py +0 -0
  135. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_api_doc_search.py +0 -0
  136. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_auth_api.py +0 -0
  137. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_body_mixin.py +0 -0
  138. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_cache.py +0 -0
  139. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_client.py +0 -0
  140. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_fields.py +0 -0
  141. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_filters.py +0 -0
  142. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_locale_api.py +0 -0
  143. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_perm_tests.py +0 -0
  144. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_perms_api.py +0 -0
  145. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_profiling_middleware.py +0 -0
  146. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_settings.py +0 -0
  147. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_shortcuts.py +0 -0
  148. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_timezone_middleware.py +0 -0
  149. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_tools.py +0 -0
  150. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_user_queryset.py +0 -0
  151. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_view_decorators.py +0 -0
  152. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/tests/test_view_fields.py +0 -0
  153. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests/urls.py +0 -0
  154. {django_pfx-1.4.dev70/tests/settings → django_pfx-1.4.dev74/tests_base_user}/__init__.py +0 -0
  155. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/migrations/0001_initial.py +0 -0
  156. {django_pfx-1.4.dev70/tests_base_user → django_pfx-1.4.dev74/tests_base_user/migrations}/__init__.py +0 -0
  157. {django_pfx-1.4.dev70/tests_base_user/migrations → django_pfx-1.4.dev74/tests_base_user/settings}/__init__.py +0 -0
  158. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/settings/ci.py +0 -0
  159. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/settings/common.py +0 -0
  160. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/settings/dev.py +0 -0
  161. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/settings/dev_custom_example.py +0 -0
  162. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/settings/dev_default.py +0 -0
  163. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/tests/__init__.py +0 -0
  164. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/tests/test_api.py +0 -0
  165. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/tests/test_auth_api.py +0 -0
  166. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/urls.py +0 -0
  167. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_base_user/views.py +0 -0
  168. {django_pfx-1.4.dev70/tests_base_user/settings → django_pfx-1.4.dev74/tests_custom_user}/__init__.py +0 -0
  169. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/migrations/0001_initial.py +0 -0
  170. {django_pfx-1.4.dev70/tests_custom_user → django_pfx-1.4.dev74/tests_custom_user/migrations}/__init__.py +0 -0
  171. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/models.py +0 -0
  172. {django_pfx-1.4.dev70/tests_custom_user/migrations → django_pfx-1.4.dev74/tests_custom_user/settings}/__init__.py +0 -0
  173. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/settings/ci.py +0 -0
  174. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/settings/common.py +0 -0
  175. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/settings/dev.py +0 -0
  176. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/settings/dev_custom_example.py +0 -0
  177. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/settings/dev_default.py +0 -0
  178. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/tests/__init__.py +0 -0
  179. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/tests/test_api.py +0 -0
  180. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/tests/test_auth_api.py +0 -0
  181. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/urls.py +0 -0
  182. {django_pfx-1.4.dev70 → django_pfx-1.4.dev74}/tests_custom_user/views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: django-pfx
3
- Version: 1.4.dev70
3
+ Version: 1.4.dev74
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.2
2
2
  Name: django-pfx
3
- Version: 1.4.dev70
3
+ Version: 1.4.dev74
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
@@ -64,6 +64,8 @@ pfx/pfxcore/migrations/0001_initial.py
64
64
  pfx/pfxcore/migrations/0002_pfxpermissionsuser.py
65
65
  pfx/pfxcore/migrations/0003_delete_pfxpermissionsuser.py
66
66
  pfx/pfxcore/migrations/__init__.py
67
+ pfx/pfxcore/migrations/operations/__init__.py
68
+ pfx/pfxcore/migrations/operations/permissions.py
67
69
  pfx/pfxcore/models/__init__.py
68
70
  pfx/pfxcore/models/abstract_pfx_base_user.py
69
71
  pfx/pfxcore/models/cache_mixins.py
@@ -137,6 +139,7 @@ tests/tests/test_locale_api.py
137
139
  tests/tests/test_perm_tests.py
138
140
  tests/tests/test_permissions.py
139
141
  tests/tests/test_perms_api.py
142
+ tests/tests/test_post_migrate_groups_update.py
140
143
  tests/tests/test_profiling_middleware.py
141
144
  tests/tests/test_settings.py
142
145
  tests/tests/test_shortcuts.py
@@ -0,0 +1,31 @@
1
+ import logging
2
+
3
+ from django.apps import AppConfig
4
+ from django.db.models.signals import post_migrate
5
+
6
+ from pfx.pfxcore.shortcuts import permissions, settings
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def update_groups_permissions(sender, **kwargs):
12
+ from django.contrib.auth.models import Group
13
+ groups = {g.name: g for g in Group.objects.all()}
14
+
15
+ for name, perms in settings.PFX_AUTH_GROUPS.items():
16
+ created = name not in groups
17
+ group = Group.objects.create(name=name) if created else groups[name]
18
+ if created or not settings.PFX_AUTH_GROUPS_CREATE_ONLY:
19
+ group.permissions.set(permissions(*perms))
20
+
21
+ if not settings.PFX_AUTH_GROUPS_CREATE_ONLY:
22
+ names = groups.keys() - settings.PFX_AUTH_GROUPS.keys()
23
+ Group.objects.filter(name__in=names).delete()
24
+
25
+
26
+ class PfxCoreConfig(AppConfig):
27
+ name = 'pfx.pfxcore'
28
+
29
+ def ready(self):
30
+ if settings.PFX_AUTH_GROUPS is not None:
31
+ post_migrate.connect(update_groups_permissions, sender=self)
@@ -14,8 +14,8 @@ logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
16
  def rest_api(
17
- path, method='get', public=None, priority=0, priority_doc=0,
18
- parameters=None,
17
+ path, method='get', public=None, perms=None,
18
+ priority=0, priority_doc=0, parameters=None,
19
19
  request_schema=None, response_schema=None, filters=False,
20
20
  search=False, groups=None):
21
21
  def decorator(func):
@@ -31,7 +31,7 @@ def rest_api(
31
31
  'HTTP_X_PRINT_REQUEST' in request.META):
32
32
  print(format_request(request))
33
33
  try:
34
- self.check_perm(public, func.__name__, *args, **kwargs)
34
+ self.check_perm(public, func.__name__, perms, *args, **kwargs)
35
35
  return func(self, *args, **kwargs)
36
36
  except APIError as e:
37
37
  return e.response
@@ -30,3 +30,6 @@ STORAGE_S3_AWS_PUT_URL_EXPIRE = None
30
30
  STORAGE_S3_AWS_GET_URL_EXPIRE = None
31
31
 
32
32
  PFX_TEST_MODE = False
33
+
34
+ PFX_AUTH_GROUPS = None
35
+ PFX_AUTH_GROUPS_CREATE_ONLY = False
@@ -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-01-21 18:35+0100\n"
10
+ "POT-Creation-Date: 2025-01-22 16:36+0100\n"
11
11
  "PO-Revision-Date: 2021-06-22 23:31+0200\n"
12
12
  "Last-Translator: \n"
13
13
  "Language-Team: \n"
@@ -0,0 +1,3 @@
1
+ from django.db.migrations import *
2
+
3
+ from .operations import *
@@ -0,0 +1 @@
1
+ from .permissions import CreateGroup, DeleteGroup, RenameGroup
@@ -0,0 +1,87 @@
1
+ from django.db.migrations.operations.base import Operation
2
+
3
+
4
+ class GroupPermsOperation(Operation):
5
+ def state_forwards(self, app_label, state):
6
+ pass
7
+
8
+
9
+ class CreateGroup(GroupPermsOperation):
10
+ """Create a group."""
11
+
12
+ def __init__(self, name):
13
+ self.group_name = name
14
+
15
+ def deconstruct(self):
16
+ return (self.__class__.__qualname__, [self.group_name], {})
17
+
18
+ def database_forwards(
19
+ self, app_label, schema_editor, from_state, to_state):
20
+ Group = to_state.apps.get_model('auth', 'Group')
21
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
22
+ Group.objects.create(name=self.group_name)
23
+
24
+ def database_backwards(
25
+ self, app_label, schema_editor, from_state, to_state):
26
+ Group = to_state.apps.get_model('auth', 'Group')
27
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
28
+ Group.objects.get(name=self.group_name).delete()
29
+
30
+ def describe(self):
31
+ return (f"Create group {self.group_name}")
32
+
33
+
34
+ class DeleteGroup(GroupPermsOperation):
35
+ """Delete a group."""
36
+
37
+ def __init__(self, name):
38
+ self.group_name = name
39
+
40
+ def deconstruct(self):
41
+ return (self.__class__.__qualname__, [self.group_name], {})
42
+
43
+ def database_forwards(
44
+ self, app_label, schema_editor, from_state, to_state):
45
+ Group = to_state.apps.get_model('auth', 'Group')
46
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
47
+ Group.objects.get(name=self.group_name).delete()
48
+
49
+ def database_backwards(
50
+ self, app_label, schema_editor, from_state, to_state):
51
+ Group = to_state.apps.get_model('auth', 'Group')
52
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
53
+ Group.objects.create(name=self.group_name)
54
+
55
+ def describe(self):
56
+ return (f"Create group {self.group_name}")
57
+
58
+
59
+ class RenameGroup(GroupPermsOperation):
60
+ """Rename a group."""
61
+
62
+ def __init__(self, old_name, new_name):
63
+ self.group_old_name = old_name
64
+ self.group_new_name = new_name
65
+
66
+ def deconstruct(self):
67
+ return (self.__class__.__qualname__, [
68
+ self.group_old_name, self.group_new_name], {})
69
+
70
+ def database_forwards(
71
+ self, app_label, schema_editor, from_state, to_state):
72
+ Group = to_state.apps.get_model('auth', 'Group')
73
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
74
+ group = Group.objects.get(name=self.group_old_name)
75
+ group.name = self.group_new_name
76
+ group.save()
77
+
78
+ def database_backwards(
79
+ self, app_label, schema_editor, from_state, to_state):
80
+ Group = to_state.apps.get_model('auth', 'Group')
81
+ if self.allow_migrate_model(schema_editor.connection.alias, Group):
82
+ group = Group.objects.get(name=self.group_new_name)
83
+ group.name = self.group_old_name
84
+ group.save()
85
+
86
+ def describe(self):
87
+ return (f"Rename group {self.group_old_name} to {self.group_new_name}")
@@ -128,8 +128,11 @@ def permissions(*perms):
128
128
  pks = set()
129
129
  for perm in perms:
130
130
  app_label, codename = perm.split('.')
131
- pks.add(Permission.objects.get(
132
- codename=codename, content_type__app_label=app_label).pk)
131
+ try:
132
+ pks.add(Permission.objects.get(
133
+ codename=codename, content_type__app_label=app_label).pk)
134
+ except Permission.DoesNotExist:
135
+ raise Exception(f"Permission {perm} does not exists.")
133
136
  return Permission.objects.filter(pk__in=pks)
134
137
 
135
138
 
@@ -1325,7 +1325,7 @@ class SecuredRestViewMixin(View):
1325
1325
  return getattr(self, param)
1326
1326
  return self.default_public if public is None else public
1327
1327
 
1328
- def check_perm(self, public, func_name, *args, **kwargs):
1328
+ def check_perm(self, public, func_name, perms, *args, **kwargs):
1329
1329
  """Check permissions for a specific service.
1330
1330
 
1331
1331
  Do all checks for a specific service and raise
@@ -1338,6 +1338,12 @@ class SecuredRestViewMixin(View):
1338
1338
  return
1339
1339
  if not self.request.user.is_authenticated:
1340
1340
  raise UnauthorizedError()
1341
+ if isinstance(perms, str):
1342
+ if not self.request.user.has_perm(perms):
1343
+ raise ForbiddenError()
1344
+ elif perms:
1345
+ if not self.request.user.has_perms(perms):
1346
+ raise ForbiddenError()
1341
1347
  if not self.perm():
1342
1348
  raise ForbiddenError()
1343
1349
  fperm = f'{func_name}_perm'
@@ -1,4 +1,4 @@
1
- # Generated by Django 4.2.17 on 2025-01-21 08:32
1
+ # Generated by Django 4.2.17 on 2025-01-22 07:34
2
2
  # flake8: noqa
3
3
 
4
4
  import django.contrib.auth.models
@@ -40,6 +40,7 @@ class Migration(migrations.Migration):
40
40
  'verbose_name': 'Author',
41
41
  'verbose_name_plural': 'Authors',
42
42
  'ordering': ['last_name', 'first_name', 'pk'],
43
+ 'permissions': [('can_customize_author', 'Can customize author')],
43
44
  },
44
45
  bases=(pfx.pfxcore.models.cache_mixins.CacheableMixin, pfx.pfxcore.models.pfx_models.JSONReprMixin, models.Model),
45
46
  ),
@@ -89,6 +89,7 @@ class Author(CacheableMixin, JSONReprMixin, models.Model):
89
89
  verbose_name = "Author"
90
90
  verbose_name_plural = "Authors"
91
91
  ordering = ['last_name', 'first_name', 'pk']
92
+ permissions = [("can_customize_author", "Can customize author")]
92
93
 
93
94
  def __str__(self):
94
95
  return f"{self.first_name} {self.last_name}"
@@ -12,6 +12,7 @@ from .test_locale_api import LocaleAPITest
12
12
  from .test_perm_tests import PermTestsTest
13
13
  from .test_permissions import TestPermissions
14
14
  from .test_perms_api import PermsAPITest
15
+ from .test_post_migrate_groups_update import TestPostMigrateGroupsUpdate
15
16
  from .test_profiling_middleware import ProfilingMiddlewareTest
16
17
  from .test_settings import TestSettings
17
18
  from .test_shortcuts import ShortcutTest
@@ -237,3 +237,49 @@ class TestPermissions(TestAssertMixin, TestCase):
237
237
  response = self.client.get(
238
238
  f'/api/perms/books/{self.author1_book1.pk}/cover')
239
239
  self.assertRC(response, 200)
240
+
241
+ def test_custom_get_admin(self):
242
+ self.client.login(username='admin')
243
+
244
+ response = self.client.get('/api/perms/authors/custom')
245
+ self.assertRC(response, 200)
246
+
247
+ def test_custom_get_user(self):
248
+ self.client.login(username='user')
249
+
250
+ response = self.client.get('/api/perms/authors/custom')
251
+ self.assertRC(response, 403)
252
+
253
+ self.user.user_permissions.add(*permissions('tests.view_author'))
254
+
255
+ response = self.client.get('/api/perms/authors/custom')
256
+ self.assertRC(response, 200)
257
+
258
+ def test_custom_put_admin(self):
259
+ self.client.login(username='admin')
260
+
261
+ response = self.client.put('/api/perms/authors/custom')
262
+ self.assertRC(response, 200)
263
+
264
+ def test_custom_put_user(self):
265
+ self.client.login(username='user')
266
+
267
+ response = self.client.put('/api/perms/authors/custom')
268
+ self.assertRC(response, 403)
269
+
270
+ self.user.user_permissions.set(permissions('tests.change_author'))
271
+
272
+ response = self.client.put('/api/perms/authors/custom')
273
+ self.assertRC(response, 403)
274
+
275
+ self.user.user_permissions.set(permissions(
276
+ 'tests.can_customize_author'))
277
+
278
+ response = self.client.put('/api/perms/authors/custom')
279
+ self.assertRC(response, 403)
280
+
281
+ self.user.user_permissions.set(permissions(
282
+ 'tests.change_author', 'tests.can_customize_author'))
283
+
284
+ response = self.client.put('/api/perms/authors/custom')
285
+ self.assertRC(response, 200)
@@ -0,0 +1,79 @@
1
+ from django.contrib.auth.models import Group
2
+ from django.test import TestCase
3
+ from django.test.utils import override_settings
4
+
5
+ from pfx.pfxcore.apps import update_groups_permissions
6
+ from pfx.pfxcore.test import TestAssertMixin
7
+
8
+
9
+ class TestPostMigrateGroupsUpdate(TestAssertMixin, TestCase):
10
+
11
+ @override_settings(PFX_AUTH_GROUPS=dict(
12
+ reader=[
13
+ 'tests.view_author',
14
+ ],
15
+ editor=[
16
+ 'tests.view_author',
17
+ 'tests.add_author',
18
+ ]))
19
+ def test_groups(self):
20
+ update_groups_permissions(self)
21
+
22
+ groups = {g.name: g for g in Group.objects.all()}
23
+ self.assertEqual(groups.keys(), {'reader', 'editor'})
24
+ perms = {p.codename for p in groups['reader'].permissions.all()}
25
+ self.assertEqual(perms, {'view_author'})
26
+ perms = {p.codename for p in groups['editor'].permissions.all()}
27
+ self.assertEqual(perms, {'view_author', 'add_author'})
28
+
29
+ with override_settings(
30
+ PFX_AUTH_GROUPS=dict(
31
+ editor=[
32
+ 'tests.view_author',
33
+ 'tests.change_author',
34
+ ],
35
+ new=[])):
36
+ update_groups_permissions(self)
37
+
38
+ groups = {g.name: g for g in Group.objects.all()}
39
+ self.assertEqual(groups.keys(), {'editor', 'new'})
40
+ perms = {p.codename for p in groups['editor'].permissions.all()}
41
+ self.assertEqual(perms, {'view_author', 'change_author'})
42
+ perms = {p.codename for p in groups['new'].permissions.all()}
43
+ self.assertEqual(perms, set())
44
+
45
+ @override_settings(PFX_AUTH_GROUPS=dict(
46
+ reader=[
47
+ 'tests.view_author',
48
+ ],
49
+ editor=[
50
+ 'tests.view_author',
51
+ 'tests.add_author',
52
+ ]), PFX_AUTH_GROUPS_CREATE_ONLY=True)
53
+ def test_groups_create_only(self):
54
+ update_groups_permissions(self)
55
+
56
+ groups = {g.name: g for g in Group.objects.all()}
57
+ self.assertEqual(groups.keys(), {'reader', 'editor'})
58
+ perms = {p.codename for p in groups['reader'].permissions.all()}
59
+ self.assertEqual(perms, {'view_author'})
60
+ perms = {p.codename for p in groups['editor'].permissions.all()}
61
+ self.assertEqual(perms, {'view_author', 'add_author'})
62
+
63
+ with override_settings(
64
+ PFX_AUTH_GROUPS=dict(
65
+ editor=[
66
+ 'tests.view_author',
67
+ 'tests.change_author',
68
+ ],
69
+ new=[])):
70
+ update_groups_permissions(self)
71
+
72
+ groups = {g.name: g for g in Group.objects.all()}
73
+ self.assertEqual(groups.keys(), {'reader', 'editor', 'new'})
74
+ perms = {p.codename for p in groups['reader'].permissions.all()}
75
+ self.assertEqual(perms, {'view_author'})
76
+ perms = {p.codename for p in groups['editor'].permissions.all()}
77
+ self.assertEqual(perms, {'view_author', 'add_author'})
78
+ perms = {p.codename for p in groups['new'].permissions.all()}
79
+ self.assertEqual(perms, set())
@@ -191,7 +191,15 @@ class PrivateAuthorRestView(AuthorRestViewMixin, RestView):
191
191
  @rest_view("/perms/authors")
192
192
  class PermsAuthorRestView(
193
193
  AuthorRestViewMixin, SlugPermsDetailRestViewMixin, PermsRestView):
194
- pass
194
+
195
+ @rest_api("/custom", method="get", perms='tests.view_author')
196
+ def get_custom(self):
197
+ return JsonResponse(dict(message='Custom get'))
198
+
199
+ @rest_api("/custom", method="put", perms=[
200
+ 'tests.change_author', 'tests.can_customize_author'])
201
+ def get_custom_action(self):
202
+ return JsonResponse(dict(message='Custom put'))
195
203
 
196
204
 
197
205
  @rest_view("/admin-edit/authors")
@@ -1,9 +0,0 @@
1
- import logging
2
-
3
- from django.apps import AppConfig
4
-
5
- logger = logging.getLogger(__name__)
6
-
7
-
8
- class PfxCoreConfig(AppConfig):
9
- name = 'pfx.pfxcore'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes