openedx-authz 2.2.0__tar.gz → 2.4.0__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 (277) hide show
  1. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/ci.yml +1 -1
  2. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/CHANGELOG.rst +11 -4
  3. {openedx_authz-2.2.0/src/openedx_authz.egg-info → openedx_authz-2.4.0}/PKG-INFO +2 -2
  4. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/README.rst +1 -1
  5. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0022-defer-authorization-implications.rst +11 -0
  6. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/authz/schema/course_permissions.yaml +1 -1
  7. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/authz/schema/course_roles.yaml +0 -2
  8. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/renderer.py +1 -1
  9. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/load_policies.py +10 -5
  10. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/api/test_roles.py +16 -21
  11. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/test_views.py +3 -15
  12. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/test_views.py +1 -1
  13. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_commands.py +9 -39
  14. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_enforcer.py +30 -61
  15. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_engine_utils.py +61 -30
  16. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_utils.py +54 -0
  17. {openedx_authz-2.2.0 → openedx_authz-2.4.0/src/openedx_authz.egg-info}/PKG-INFO +2 -2
  18. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/SOURCES.txt +0 -1
  19. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/scm_file_list.json +0 -1
  20. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/uv.lock +282 -228
  21. openedx_authz-2.2.0/src/openedx_authz/engine/config/authz.policy +0 -190
  22. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.annotation_safe_list.yml +0 -0
  23. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.editorconfig +0 -0
  24. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  25. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/dependabot.yml +0 -0
  26. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/add-depr-ticket-to-depr-board.yml +0 -0
  27. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/add-remove-label-on-comment.yml +0 -0
  28. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/commitlint.yml +0 -0
  29. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/release.yml +0 -0
  30. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/self-assign-issue.yml +0 -0
  31. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.github/workflows/upgrade-python-requirements.yml +0 -0
  32. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.gitignore +0 -0
  33. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.pii_annotations.yml +0 -0
  34. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/.readthedocs.yaml +0 -0
  35. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/LICENSE +0 -0
  36. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/MANIFEST.in +0 -0
  37. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/Makefile +0 -0
  38. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/catalog-info.yaml +0 -0
  39. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/codecov.yml +0 -0
  40. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/Makefile +0 -0
  41. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/_images/architecture-overview.png +0 -0
  42. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/_static/theme_overrides.css +0 -0
  43. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/changelog.rst +0 -0
  44. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/concepts/core_roles_and_permissions/content_library_roles.rst +0 -0
  45. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/concepts/core_roles_and_permissions/course_roles.rst +0 -0
  46. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/concepts/core_roles_and_permissions/index.rst +0 -0
  47. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/concepts/index.rst +0 -0
  48. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/conf.py +0 -0
  49. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0001-purpose-of-this-repo.rst +0 -0
  50. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0002-authorization-model-foundation.rst +0 -0
  51. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0003-jwt-usage.rst +0 -0
  52. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0004-technology-selection.rst +0 -0
  53. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0005-architecture-and-data-modeling.rst +0 -0
  54. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0007-enforcement-mechanisms-mfe.rst +0 -0
  55. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0008-compatibility-scheme.rst +0 -0
  56. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0009-authz-for-course-authoring.rst +0 -0
  57. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0010-course-authoring-flag.rst +0 -0
  58. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0011-course-authoring-migration-process.rst +0 -0
  59. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0012-auditability.rst +0 -0
  60. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0012-glob-support-for-role-assignments.rst +0 -0
  61. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0013-course-authoring-automatic-migration.rst +0 -0
  62. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0014-bulk-assignment-queries-without-casbin-enforce.rst +0 -0
  63. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0015-expose-course-authoring-waffle-flag-state-via-rest-api.rst +0 -0
  64. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0016-static-and-dynamic-roles.rst +0 -0
  65. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0017-static-authorization-schema.rst +0 -0
  66. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0018-authorization-schema-lifecycle.rst +0 -0
  67. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0019-authorization-schema-discovery.rst +0 -0
  68. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0020-authorization-schema-internationalization.rst +0 -0
  69. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0021-authorization-definition-api.rst +0 -0
  70. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0023-extend-static-roles.rst +0 -0
  71. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0024-api-contract-for-user-grouped-role-assignments.rst +0 -0
  72. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0025-authorization-schema-source-tracking.rst +0 -0
  73. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0026-rest-api-domain-ownership-boundary.rst +0 -0
  74. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0027-cross-domain-filtering-via-openedx-filters.rst +0 -0
  75. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0028-api-contract-for-role-catalog.rst +0 -0
  76. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0029-paragon-icon-list-maintenance.rst +0 -0
  77. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0030-tutor-implementation-authz-schema-discovery.rst +0 -0
  78. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/0031-priority-applies-to-role-extensions-only.rst +0 -0
  79. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions/README.rst +0 -0
  80. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/decisions.rst +0 -0
  81. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/getting_started.rst +0 -0
  82. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/how-tos/index.rst +0 -0
  83. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/index.rst +0 -0
  84. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/internationalization.rst +0 -0
  85. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/make.bat +0 -0
  86. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/quickstarts/index.rst +0 -0
  87. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/readme.rst +0 -0
  88. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/references/authorization-schema-json.rst +0 -0
  89. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/references/authorization-schema.rst +0 -0
  90. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/references/index.rst +0 -0
  91. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/docs/testing.rst +0 -0
  92. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/manage.py +0 -0
  93. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/pylintrc +0 -0
  94. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/pylintrc_tweaks +0 -0
  95. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/pyproject.toml +0 -0
  96. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/ruff.toml +0 -0
  97. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/scripts/generate_paragon_icons.py +0 -0
  98. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/setup.cfg +0 -0
  99. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/__init__.py +0 -0
  100. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/admin.py +0 -0
  101. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/__init__.py +0 -0
  102. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/catalog.py +0 -0
  103. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/data.py +0 -0
  104. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/permissions.py +0 -0
  105. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/roles.py +0 -0
  106. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/users.py +0 -0
  107. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/api/utils.py +0 -0
  108. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/apps.py +0 -0
  109. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/authz/__init__.py +0 -0
  110. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/authz/schema/library_permissions.yaml +0 -0
  111. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/authz/schema/library_roles.yaml +0 -0
  112. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/conf/locale/config.yaml +0 -0
  113. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/constants/__init__.py +0 -0
  114. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/constants/permissions.py +0 -0
  115. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/constants/roles.py +0 -0
  116. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/constants/schema.py +0 -0
  117. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/data.py +0 -0
  118. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/__init__.py +0 -0
  119. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/adapter.py +0 -0
  120. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/apps.py +0 -0
  121. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/config/model.conf +0 -0
  122. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/enforcer.py +0 -0
  123. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/filter.py +0 -0
  124. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/matcher.py +0 -0
  125. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/policy.py +0 -0
  126. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/__init__.py +0 -0
  127. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/authz-schema-v1.json +0 -0
  128. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/compilation.py +0 -0
  129. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/discovery.py +0 -0
  130. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/exceptions.py +0 -0
  131. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/issues.py +0 -0
  132. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/loading.py +0 -0
  133. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/paragon_icons.py +0 -0
  134. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/parsing.py +0 -0
  135. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/pipeline.py +0 -0
  136. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/syntax.py +0 -0
  137. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/translation.py +0 -0
  138. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/types/__init__.py +0 -0
  139. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/types/compilation.py +0 -0
  140. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/types/definitions.py +0 -0
  141. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/types/loading.py +0 -0
  142. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/schema/validation.py +0 -0
  143. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/engine/utils.py +0 -0
  144. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/filters.py +0 -0
  145. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/handlers.py +0 -0
  146. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/__init__.py +0 -0
  147. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/__init__.py +0 -0
  148. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/authz_migrate_course_authoring.py +0 -0
  149. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/authz_rollback_course_authoring.py +0 -0
  150. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/enforcement.py +0 -0
  151. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/extract_schema_translations.py +0 -0
  152. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/management/commands/load_authz_schema.py +0 -0
  153. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0001_add_casbin_dependency.py +0 -0
  154. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0002_initial.py +0 -0
  155. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0003_usersubject.py +0 -0
  156. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0004_contentlibraryscope.py +0 -0
  157. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0005_policycachecontrol.py +0 -0
  158. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0006_migrate_legacy_permissions.py +0 -0
  159. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0007_coursescope.py +0 -0
  160. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0008_authzcourseauthoringmigrationrun.py +0 -0
  161. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0009_roleassignmentaudit.py +0 -0
  162. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0010_scope_external_key.py +0 -0
  163. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/0011_authz_schema_definitions.py +0 -0
  164. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/migrations/__init__.py +0 -0
  165. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/__init__.py +0 -0
  166. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/authz_migration.py +0 -0
  167. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/base.py +0 -0
  168. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/core.py +0 -0
  169. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/engine.py +0 -0
  170. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/schema.py +0 -0
  171. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/scopes.py +0 -0
  172. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/models/subjects.py +0 -0
  173. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/__init__.py +0 -0
  174. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/data.py +0 -0
  175. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/decorators.py +0 -0
  176. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/urls.py +0 -0
  177. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/utils.py +0 -0
  178. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/__init__.py +0 -0
  179. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/admin_console/__init__.py +0 -0
  180. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/admin_console/filters.py +0 -0
  181. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/admin_console/serializers.py +0 -0
  182. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/admin_console/views.py +0 -0
  183. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/course_authoring/__init__.py +0 -0
  184. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/course_authoring/pipeline.py +0 -0
  185. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/course_authoring/views.py +0 -0
  186. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/fields.py +0 -0
  187. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/paginators.py +0 -0
  188. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/permissions.py +0 -0
  189. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/serializers.py +0 -0
  190. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/urls.py +0 -0
  191. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/rest_api/v1/views.py +0 -0
  192. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/settings/__init__.py +0 -0
  193. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/settings/common.py +0 -0
  194. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/settings/production.py +0 -0
  195. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/settings/test.py +0 -0
  196. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/templates/openedx_authz/base.html +0 -0
  197. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/__init__.py +0 -0
  198. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/api/__init__.py +0 -0
  199. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/api/test_catalog.py +0 -0
  200. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/api/test_data.py +0 -0
  201. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/api/test_users.py +0 -0
  202. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/authz/__init__.py +0 -0
  203. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/authz/test_resources.py +0 -0
  204. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/__init__.py +0 -0
  205. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/conftest.py +0 -0
  206. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_compiled/categories.yaml +0 -0
  207. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_compiled/extensions.yaml +0 -0
  208. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_compiled/permissions.yaml +0 -0
  209. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_compiled/roles.yaml +0 -0
  210. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_parse/malformed.yaml +0 -0
  211. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_semantics/unknown_category.yaml +0 -0
  212. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_structure/items.yml +0 -0
  213. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/invalid_structure/priority.yaml +0 -0
  214. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/valid/categories.yaml +0 -0
  215. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/valid/extensions.yaml +0 -0
  216. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/valid/permissions.yml +0 -0
  217. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/fixtures/schema_pipeline/valid/roles.yaml +0 -0
  218. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/test_handlers.py +0 -0
  219. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/test_models.py +0 -0
  220. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/test_schema_apply.py +0 -0
  221. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/integration/test_schema_pipeline.py +0 -0
  222. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/__init__.py +0 -0
  223. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/admin_console/__init__.py +0 -0
  224. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/admin_console/test_views.py +0 -0
  225. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/course_authoring/__init__.py +0 -0
  226. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/course_authoring/test_pipeline.py +0 -0
  227. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/course_authoring/test_views.py +0 -0
  228. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/mixins.py +0 -0
  229. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/test_permissions.py +0 -0
  230. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/rest_api/test_utils.py +0 -0
  231. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/__init__.py +0 -0
  232. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/factories.py +0 -0
  233. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_apply.py +0 -0
  234. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_compilation.py +0 -0
  235. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_discovery.py +0 -0
  236. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_load_authz_schema_command.py +0 -0
  237. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_loading.py +0 -0
  238. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_parsing.py +0 -0
  239. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_pipeline.py +0 -0
  240. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_renderer.py +0 -0
  241. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_schema_models.py +0 -0
  242. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_source_storage.py +0 -0
  243. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_syntax.py +0 -0
  244. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_translation.py +0 -0
  245. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_types.py +0 -0
  246. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/schema/test_validation.py +0 -0
  247. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/__init__.py +0 -0
  248. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/apps.py +0 -0
  249. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/migrations/0001_initial.py +0 -0
  250. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/migrations/0002_learningpackage_contentlibrary_learning_package.py +0 -0
  251. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/migrations/0002_userprofile.py +0 -0
  252. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/stubs/models.py +0 -0
  253. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_enforcement.py +0 -0
  254. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_filter.py +0 -0
  255. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_handlers.py +0 -0
  256. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_imports.py +0 -0
  257. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_migrations.py +0 -0
  258. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_models.py +0 -0
  259. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/tests/test_policy.py +0 -0
  260. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/urls.py +0 -0
  261. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz/utils.py +0 -0
  262. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/dependency_links.txt +0 -0
  263. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/entry_points.txt +0 -0
  264. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/requires.txt +0 -0
  265. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/src/openedx_authz.egg-info/top_level.txt +0 -0
  266. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tox.ini +0 -0
  267. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/.gitignore +0 -0
  268. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/LICENSE.txt +0 -0
  269. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/MANIFEST.in +0 -0
  270. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/Makefile +0 -0
  271. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/README.rst +0 -0
  272. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/pyproject.toml +0 -0
  273. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/tutor_authz/__about__.py +0 -0
  274. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/tutor_authz/__init__.py +0 -0
  275. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/tutor_authz/patches/openedx-common-settings +0 -0
  276. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/tutor_authz/plugin.py +0 -0
  277. {openedx_authz-2.2.0 → openedx_authz-2.4.0}/tutor/tutor-contrib-authz/tutor_authz/templates/apps/openedx/config/authz-schema.yml +0 -0
@@ -21,7 +21,7 @@ jobs:
21
21
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
22
 
23
23
  - name: Install uv
24
- uses: astral-sh/setup-uv@07a047e608db84b3d8c06cd9a7c7dcc9c355c18e # v10.3.2
24
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
25
25
  with:
26
26
  enable-cache: true
27
27
  python-version: ${{ matrix.python-version }}
@@ -18,8 +18,8 @@ Added
18
18
  =====
19
19
 
20
20
  * Added the static authorization schema, a versioned YAML format for declaring permissions,
21
- permission categories, roles, and role extensions (ADR 0017). The permissions and roles that
22
- ``authz.policy`` defines are now also expressed as schema files under
21
+ permission categories, roles, and role extensions (ADR 0017). The permissions and roles
22
+ previously defined in ``authz.policy`` are expressed as schema files under
23
23
  ``openedx_authz/authz/schema/``.
24
24
  * Added the schema loading pipeline in ``openedx_authz/engine/schema/``, covering the discover,
25
25
  load, validate and compile phases of the lifecycle (ADR 0018), plus render and apply in
@@ -61,11 +61,18 @@ Fixed
61
61
  by priority. This aligns the compiler with ADR 0031 and removes a path where a higher-priority file
62
62
  could silently replace a role's base definition.
63
63
 
64
+ Changed
65
+ ========
66
+
67
+ * Removed ``authz.policy`` after moving its static role-permission policies to the authorization schema. Existing Casbin action grouping remains supported until permission dependencies move to the schema.
68
+ * Calls to the deprecated ``load_policies`` command without an explicit policy file are now a no-op while deployment integrations move to ``load_authz_schema``.
69
+
64
70
  Notes
65
71
  =====
66
72
 
67
- * No authorization behavior changes in this release. Loading ``authz.policy`` works as before, and
68
- the new pipeline runs only when ``load_authz_schema`` is invoked.
73
+ * Existing custom policy files can still be loaded by passing their path to ``load_policies``.
74
+ Calls without a policy file do nothing while deployment integrations move to
75
+ ``load_authz_schema``.
69
76
  * Applying a schema is idempotent and preserves data the loader does not own: user assignments,
70
77
  dynamic roles, legacy ``g2`` action-inheritance rows, and pre-existing policy rows that no schema
71
78
  declares. Rows that already exist are adopted, gaining definition and source records rather than
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openedx-authz
3
- Version: 2.2.0
3
+ Version: 2.4.0
4
4
  Summary: Open edX AuthZ provides the architecture and foundations of the authorization framework.
5
5
  Author-email: Open edX Project <oscm@openedx.org>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -55,7 +55,7 @@ Integration with edx-platform
55
55
 
56
56
  This repository became an edx-platform's dependency starting with the Ulmo release. From that release onwards, system policies are automatically updated.
57
57
 
58
- If you need to update the policies manually, it is recommended to use the ``./manage.py lms load_policies`` command.
58
+ To update the static authorization definitions manually, run ``./manage.py lms load_authz_schema``.
59
59
 
60
60
  .. note::
61
61
  Currently, this package only supports the `content libraries' roles and permissions as documented here`_, and the migration of data from the old system to the new one is performed automatically.
@@ -18,7 +18,7 @@ Integration with edx-platform
18
18
 
19
19
  This repository became an edx-platform's dependency starting with the Ulmo release. From that release onwards, system policies are automatically updated.
20
20
 
21
- If you need to update the policies manually, it is recommended to use the ``./manage.py lms load_policies`` command.
21
+ To update the static authorization definitions manually, run ``./manage.py lms load_authz_schema``.
22
22
 
23
23
  .. note::
24
24
  Currently, this package only supports the `content libraries' roles and permissions as documented here`_, and the migration of data from the old system to the new one is performed automatically.
@@ -45,6 +45,13 @@ Consequences
45
45
  * The current policy loader continues to handle existing Casbin grouping rows.
46
46
  * Introducing implication later requires a schema-version change and migration guidance.
47
47
 
48
+ Addendum: Remove the Static Policy File (2026-11-03)
49
+ =====================================================
50
+
51
+ Static role-permission policies moved from ``authz.policy`` to the authorization schema, and the policy file was removed. The Casbin ``g2`` model, matcher behavior, and stored grouping rows remain unchanged.
52
+
53
+ New installations no longer load the default ``g2`` relationships that were stored in ``authz.policy``. `Issue #91`_ and `Issue #523`_ track defining permission dependencies in the authorization schema.
54
+
48
55
  Rejected Alternatives
49
56
  *********************
50
57
 
@@ -62,5 +69,9 @@ References
62
69
  **********
63
70
 
64
71
  * `ADR 0017`_
72
+ * `Issue #91`_
73
+ * `Issue #523`_
65
74
 
66
75
  .. _ADR 0017: 0017-static-authorization-schema.rst
76
+ .. _Issue #91: https://github.com/openedx/openedx-authz/issues/91
77
+ .. _Issue #523: https://github.com/openedx/openedx-authz/issues/523
@@ -56,7 +56,7 @@ permission_categories:
56
56
  description: Additional permissions not included in other categories, such as viewing checklists.
57
57
  icon: DrawShapes
58
58
  # Backend-only category for legacy compatibility actions that have no frontend
59
- # display metadata. These are referenced by legacy roles in authz.policy.
59
+ # display metadata. These are referenced by legacy roles in course_roles.yaml.
60
60
  - id: course_legacy
61
61
  display_name: Legacy compatibility
62
62
  description: Internal compatibility permissions that map legacy course roles to the authorization system. Not shown as individually assignable permissions.
@@ -88,7 +88,6 @@ roles:
88
88
  description: Can create and edit course content, but cannot publish or change critical course settings.
89
89
  scopes:
90
90
  - course-v1
91
- hidden: true
92
91
  permissions:
93
92
  - courses.view_course
94
93
  - courses.view_course_updates
@@ -118,7 +117,6 @@ roles:
118
117
  description: Can view course content and settings, but cannot make changes.
119
118
  scopes:
120
119
  - course-v1
121
- hidden: true
122
120
  permissions:
123
121
  - courses.view_course
124
122
  - courses.view_course_updates
@@ -7,7 +7,7 @@ the ``render`` and ``apply`` lifecycle steps:
7
7
  memory, without touching the database.
8
8
  * ``apply`` persists the rows in a single transaction, while preserving data
9
9
  owned by other services (ADR 0018 §3): dynamic roles, user assignments, and
10
- the legacy ``g2`` action-inheritance rows that still live in ``authz.policy``.
10
+ legacy ``g2`` action-inheritance rows.
11
11
 
12
12
  Key semantics:
13
13
  * Idempotent (ADR 0018 §2): re-applying identical definitions changes
@@ -1,7 +1,7 @@
1
1
  """Django management command to load policies into the authz Django model.
2
2
 
3
3
  The command supports:
4
- - Specifying the path to the Casbin policy file. Default is 'openedx_authz/engine/config/authz.policy'.
4
+ - Specifying the path to a Casbin policy file. Calls without a file are a no-op.
5
5
  - Specifying the Casbin model configuration file. Default is 'openedx_authz/engine/config/model.conf'.
6
6
  - Optionally clearing existing policies in the database before loading new ones.
7
7
  """
@@ -25,9 +25,8 @@ class Command(BaseCommand):
25
25
  and persistence of authorization policies within the Django application.
26
26
 
27
27
  Example Usage:
28
- python manage.py load_policies --policy-file-path /path/to/authz.policy
29
- python manage.py load_policies --policy-file-path /path/to/authz.policy --model-file-path /path/to/model.conf
30
- python manage.py load_policies
28
+ python manage.py load_policies --policy-file-path /path/to/custom.policy
29
+ python manage.py load_policies --policy-file-path /path/to/custom.policy --model-file-path /path/to/model.conf
31
30
  """
32
31
 
33
32
  help = "Load policies from a Casbin policy file into the Django database model."
@@ -74,7 +73,13 @@ class Command(BaseCommand):
74
73
  options["model_file_path"],
75
74
  )
76
75
  if policy_file_path is None:
77
- policy_file_path = os.path.join(ROOT_DIRECTORY, "engine", "config", "authz.policy")
76
+ self.stdout.write(
77
+ self.style.WARNING(
78
+ "No policy file supplied; nothing was loaded. "
79
+ "Use load_authz_schema for static role-permission policies."
80
+ )
81
+ )
82
+ return
78
83
  if model_file_path is None:
79
84
  model_file_path = os.path.join(ROOT_DIRECTORY, "engine", "config", "model.conf")
80
85
 
@@ -5,10 +5,8 @@ including role creation, assignment, permission management, and querying
5
5
  roles and permissions within specific scopes.
6
6
  """
7
7
 
8
- from importlib.resources import files
9
8
  from unittest.mock import patch
10
9
 
11
- import casbin
12
10
  from ddt import data as ddt_data
13
11
  from ddt import ddt, unpack
14
12
  from django.test import TestCase
@@ -50,8 +48,8 @@ from openedx_authz.constants.roles import (
50
48
  LIBRARY_USER_PERMISSIONS,
51
49
  )
52
50
  from openedx_authz.engine.enforcer import AuthzEnforcer
53
- from openedx_authz.engine.utils import migrate_policy_between_enforcers
54
51
  from openedx_authz.models import ExtendedCasbinRule, Scope, Subject
52
+ from openedx_authz.tests.test_utils import seed_static_authorization_policies
55
53
 
56
54
 
57
55
  def _mock_get_or_create_scope(scope_data):
@@ -76,20 +74,14 @@ class BaseRolesTestCase(TestCase):
76
74
 
77
75
  @classmethod
78
76
  def _seed_database_with_policies(cls):
79
- """Seed the database with policies from the policy file.
77
+ """Seed the database with policies from the authorization schema.
80
78
 
81
79
  This simulates the one-time database seeding that would happen
82
80
  during application deployment, separate from the runtime policy loading.
83
81
  """
84
82
  global_enforcer = AuthzEnforcer.get_enforcer()
85
83
  global_enforcer.load_policy()
86
- model_path = str(files("openedx_authz.engine").joinpath("config/model.conf"))
87
- policy_path = str(files("openedx_authz.engine").joinpath("config/authz.policy"))
88
-
89
- migrate_policy_between_enforcers(
90
- source_enforcer=casbin.Enforcer(model_path, policy_path),
91
- target_enforcer=global_enforcer,
92
- )
84
+ seed_static_authorization_policies(global_enforcer)
93
85
  global_enforcer.clear_policy() # Clear to simulate fresh start for each test
94
86
 
95
87
  @classmethod
@@ -181,7 +173,7 @@ class RolesTestSetupMixin(BaseRolesTestCase):
181
173
  super().setUpClass()
182
174
  # Define specific assignments for this test class
183
175
  assignments = [
184
- # Basic library roles from authz.policy
176
+ # Basic library roles from the authorization schema
185
177
  {
186
178
  "subject_name": "alice",
187
179
  "role_name": roles.LIBRARY_ADMIN.external_key,
@@ -202,7 +194,7 @@ class RolesTestSetupMixin(BaseRolesTestCase):
202
194
  "role_name": roles.LIBRARY_USER.external_key,
203
195
  "scope_name": "lib:Org1:english_101",
204
196
  },
205
- # Basic course roles from authz.policy
197
+ # Basic course roles from the authorization schema
206
198
  {
207
199
  "subject_name": "daniel",
208
200
  "role_name": roles.COURSE_STAFF.external_key,
@@ -430,17 +422,17 @@ class TestRolesAPI(RolesTestSetupMixin):
430
422
  self.assertFalse(ExtendedCasbinRule.objects.filter(subject=subj_obj, scope=scope_obj).exists())
431
423
 
432
424
  @ddt_data(
433
- # Library Admin role with actual permissions from authz.policy
425
+ # Library Admin role with permissions from the authorization schema
434
426
  (
435
427
  roles.LIBRARY_ADMIN.external_key,
436
428
  LIBRARY_ADMIN_PERMISSIONS,
437
429
  ),
438
- # Library Author role with actual permissions from authz.policy
430
+ # Library Author role with permissions from the authorization schema
439
431
  (
440
432
  roles.LIBRARY_AUTHOR.external_key,
441
433
  LIBRARY_AUTHOR_PERMISSIONS,
442
434
  ),
443
- # Library Contributor role with actual permissions from authz.policy
435
+ # Library Contributor role with permissions from the authorization schema
444
436
  (
445
437
  roles.LIBRARY_CONTRIBUTOR.external_key,
446
438
  LIBRARY_CONTRIBUTOR_PERMISSIONS,
@@ -466,7 +458,7 @@ class TestRolesAPI(RolesTestSetupMixin):
466
458
  """
467
459
  assigned_permissions = get_permissions_for_single_role(RoleData(external_key=role_name))
468
460
 
469
- self.assertEqual(assigned_permissions, expected_permissions)
461
+ self.assertEqual(sorted(assigned_permissions, key=str), sorted(expected_permissions, key=str))
470
462
 
471
463
  @ddt_data(
472
464
  # Role assigned to multiple users in different scopes
@@ -502,8 +494,8 @@ class TestRolesAPI(RolesTestSetupMixin):
502
494
 
503
495
  self.assertIn(role_name, assigned_permissions)
504
496
  self.assertEqual(
505
- assigned_permissions[role_name]["permissions"],
506
- expected_permissions,
497
+ sorted(assigned_permissions[role_name]["permissions"], key=str),
498
+ sorted(expected_permissions, key=str),
507
499
  )
508
500
 
509
501
  @ddt_data(
@@ -544,14 +536,17 @@ class TestRolesAPI(RolesTestSetupMixin):
544
536
  """Test retrieving role definitions from policy for a namespace wildcard scope.
545
537
 
546
538
  Expected result:
547
- - Role definitions defined in authz.policy for the scope are returned.
539
+ - Role definitions defined in the authorization schema for the scope are returned.
548
540
  - Each role includes the permissions declared in its policy rules.
549
541
  """
550
542
  roles_in_scope = get_role_definitions_in_scope(ScopeData(namespaced_key=namespaced_scope))
551
543
  roles_by_key = {role.external_key: role for role in roles_in_scope}
552
544
 
553
545
  self.assertEqual(set(roles_by_key.keys()), expected_roles)
554
- self.assertEqual(roles_by_key[sample_role].permissions, expected_permissions)
546
+ self.assertEqual(
547
+ sorted(roles_by_key[sample_role].permissions, key=str),
548
+ sorted(expected_permissions, key=str),
549
+ )
555
550
 
556
551
  def test_get_role_definitions_in_scope_returns_empty_for_concrete_scope(self):
557
552
  """Concrete scopes do not match namespace wildcard policy definitions."""
@@ -1,10 +1,8 @@
1
1
  """Integration tests for openedx_authz views."""
2
2
 
3
- import os
4
3
  import uuid
5
4
  from urllib.parse import urlencode
6
5
 
7
- import casbin
8
6
  import pytest
9
7
  from django.contrib.auth import get_user_model
10
8
  from django.test import TestCase
@@ -12,12 +10,11 @@ from django.urls import reverse
12
10
  from rest_framework import status
13
11
  from rest_framework.test import APIClient
14
12
 
15
- from openedx_authz import ROOT_DIRECTORY
16
13
  from openedx_authz.api.users import assign_role_to_user_in_scope
17
14
  from openedx_authz.engine.enforcer import AuthzEnforcer
18
- from openedx_authz.engine.utils import migrate_policy_between_enforcers
19
15
  from openedx_authz.models.core import ExtendedCasbinRule
20
16
  from openedx_authz.tests.integration.test_models import create_test_library
17
+ from openedx_authz.tests.test_utils import seed_static_authorization_policies
21
18
 
22
19
  User = get_user_model()
23
20
 
@@ -30,19 +27,10 @@ class TestRoleAssignmentView(TestCase):
30
27
  def setUpClass(cls):
31
28
  """Set up test class - seed database with policies."""
32
29
  super().setUpClass()
33
- # Seed the database with policies from the policy file
34
- # This loads the policy definitions (p, g rules) that define what permissions each role has
30
+ # Seed the database with policies from the authorization schema
35
31
  global_enforcer = AuthzEnforcer.get_enforcer()
36
32
  global_enforcer.load_policy()
37
-
38
- # Use absolute paths based on the package ROOT_DIRECTORY
39
- model_conf = os.path.join(ROOT_DIRECTORY, "engine", "config", "model.conf")
40
- authz_policy = os.path.join(ROOT_DIRECTORY, "engine", "config", "authz.policy")
41
-
42
- migrate_policy_between_enforcers(
43
- source_enforcer=casbin.Enforcer(model_conf, authz_policy),
44
- target_enforcer=global_enforcer,
45
- )
33
+ seed_static_authorization_policies(global_enforcer)
46
34
 
47
35
  def setUp(self):
48
36
  """Set up the test client and any required data."""
@@ -1559,7 +1559,7 @@ class TestArbitraryRoleSupport(ViewTestMixin):
1559
1559
  def setUpClass(cls):
1560
1560
  super().setUpClass()
1561
1561
  # Defined with the same wildcard library scope as the built-in library roles
1562
- # (see authz.policy), so it's visible for any specific library scope.
1562
+ # (see library_roles.yaml), so it's visible for any specific library scope.
1563
1563
  AuthzEnforcer.get_enforcer().add_policy(
1564
1564
  make_role_key(cls.CUSTOM_ROLE),
1565
1565
  make_action_key(cls.CUSTOM_ACTION),
@@ -15,7 +15,6 @@ from ddt import data, ddt
15
15
  from django.core.management import call_command
16
16
  from django.core.management.base import CommandError
17
17
 
18
- from openedx_authz import ROOT_DIRECTORY
19
18
  from openedx_authz import api as authz_api
20
19
  from openedx_authz.api.data import ContentLibraryData
21
20
  from openedx_authz.constants.permissions import (
@@ -334,7 +333,7 @@ class LoadPoliciesCommandTests(TestCase):
334
333
  Tests for the `load_policies` Django management command.
335
334
 
336
335
  This test class verifies the behavior of the load_policies command, including:
337
- - Default file path handling
336
+ - Missing policy file handling
338
337
  - Clearing existing policies
339
338
  """
340
339
 
@@ -343,43 +342,14 @@ class LoadPoliciesCommandTests(TestCase):
343
342
  self.buffer = io.StringIO()
344
343
 
345
344
  @patch("openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer")
346
- @patch("casbin.Enforcer")
347
- @patch("os.path.join")
348
- @patch("click.confirm")
349
- def test_handle_with_default_paths(self, mock_confirm, mock_join, mock_casbin_enforcer, mock_get_enforcer):
350
- """Test handle method with default policy and model paths."""
351
- # Setup mocks
352
- mock_target_enforcer = Mock()
353
- mock_get_enforcer.return_value = mock_target_enforcer
354
-
355
- mock_source_enforcer = Mock()
356
- mock_casbin_enforcer.return_value = mock_source_enforcer
357
-
358
- policy_path = f"{ROOT_DIRECTORY}/engine/config/authz.policy"
359
- model_path = f"{ROOT_DIRECTORY}/engine/config/model.conf"
360
-
361
- # Define paths that will be joined
362
- mock_join.side_effect = (
363
- policy_path,
364
- model_path,
365
- )
366
-
367
- # Create command instance
368
- command = LoadPoliciesCommand()
369
- command.migrate_policies = Mock()
345
+ def test_handle_without_policy_file_is_a_deprecated_noop(self, mock_get_enforcer):
346
+ """Existing deployment calls remain safe until they move to the schema command."""
347
+ command = LoadPoliciesCommand(stdout=self.buffer)
370
348
 
371
- # Call handle method
372
349
  command.handle(policy_file_path=None, model_file_path=None, clear_existing=False)
373
350
 
374
- # Assertions
375
- mock_casbin_enforcer.assert_called_once_with(
376
- model_path,
377
- policy_path,
378
- )
379
- mock_join.assert_any_call(ROOT_DIRECTORY, "engine", "config", "authz.policy")
380
- mock_join.assert_any_call(ROOT_DIRECTORY, "engine", "config", "model.conf")
381
- mock_confirm.assert_not_called()
382
- command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer)
351
+ self.assertIn("No policy file supplied", self.buffer.getvalue())
352
+ mock_get_enforcer.assert_not_called()
383
353
 
384
354
  @patch("openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer")
385
355
  @patch("casbin.Enforcer")
@@ -439,7 +409,7 @@ class LoadPoliciesCommandTests(TestCase):
439
409
  command._delete_permissions_inheritance = Mock()
440
410
 
441
411
  # Call handle method
442
- command.handle(policy_file_path=None, model_file_path=None, clear_existing=True)
412
+ command.handle(policy_file_path="/custom/policy.csv", model_file_path=None, clear_existing=True)
443
413
 
444
414
  # Assertions
445
415
  mock_target_enforcer.load_policy.assert_called_once()
@@ -474,7 +444,7 @@ class LoadPoliciesCommandTests(TestCase):
474
444
  command._delete_permissions_inheritance = Mock()
475
445
 
476
446
  # Call handle method
477
- command.handle(policy_file_path=None, model_file_path=None, clear_existing=True)
447
+ command.handle(policy_file_path="/custom/policy.csv", model_file_path=None, clear_existing=True)
478
448
 
479
449
  # Assertions
480
450
  mock_target_enforcer.load_policy.assert_called_once()
@@ -506,7 +476,7 @@ class LoadPoliciesCommandTests(TestCase):
506
476
  command._delete_permissions_inheritance = Mock()
507
477
 
508
478
  # Call handle method
509
- command.handle(policy_file_path=None, model_file_path=None, clear_existing=True)
479
+ command.handle(policy_file_path="/custom/policy.csv", model_file_path=None, clear_existing=True)
510
480
 
511
481
  # Assertions
512
482
  mock_target_enforcer.load_policy.assert_called_once()
@@ -5,36 +5,35 @@ including filtered loading, scope-based loading, and lifecycle management
5
5
  that would be used in production environments.
6
6
  """
7
7
 
8
- import os
9
8
  import time
10
9
  from uuid import uuid4
11
10
 
12
- import casbin
13
11
  from ddt import data as ddt_data
14
12
  from ddt import ddt
15
13
  from django.conf import settings
16
14
  from django.test import TestCase, TransactionTestCase, override_settings
17
15
 
18
- from openedx_authz import ROOT_DIRECTORY
19
16
  from openedx_authz.engine.enforcer import AuthzEnforcer
20
17
  from openedx_authz.engine.filter import Filter
21
- from openedx_authz.engine.utils import migrate_policy_between_enforcers
22
18
  from openedx_authz.models.engine import PolicyCacheControl
23
- from openedx_authz.tests.test_utils import make_action_key, make_role_key, make_scope_key, make_user_key
24
-
25
- MODEL_CONF_PATH = os.path.join(ROOT_DIRECTORY, "engine", "config", "model.conf")
26
- AUTHZ_POLICY_PATH = os.path.join(ROOT_DIRECTORY, "engine", "config", "authz.policy")
19
+ from openedx_authz.tests.test_utils import (
20
+ AuthzEnforcerTestMixin,
21
+ make_action_key,
22
+ make_role_key,
23
+ make_scope_key,
24
+ make_user_key,
25
+ seed_static_authorization_policies,
26
+ )
27
27
 
28
28
 
29
29
  class PolicyLoadingTestSetupMixin(TestCase):
30
30
  """Mixin providing policy loading test utilities."""
31
31
 
32
- @staticmethod
33
- def _count_policies_in_file(scope_pattern: str = None, role: str = None):
34
- """Count policies in the authz.policy file matching the given criteria.
32
+ def _count_schema_policies(self, scope_pattern: str = None, role: str = None):
33
+ """Count schema policies matching the given criteria.
35
34
 
36
35
  This provides a dynamic way to get expected policy counts without
37
- hardcoding values that might change as the policy file evolves.
36
+ hardcoding values that might change as the schema evolves.
38
37
 
39
38
  Args:
40
39
  scope_pattern: Scope pattern to match (e.g., 'lib^*')
@@ -43,32 +42,14 @@ class PolicyLoadingTestSetupMixin(TestCase):
43
42
  Returns:
44
43
  int: Number of matching policies
45
44
  """
46
- count = 0
47
- with open(AUTHZ_POLICY_PATH, "r") as f:
48
- for line in f:
49
- line = line.strip()
50
- if not line or line.startswith("#"):
51
- continue
52
- if not line.startswith("p,"):
53
- continue
54
-
55
- parts = [p.strip() for p in line.split(",")]
56
- if len(parts) < 4:
57
- continue
58
-
59
- # parts[0] = 'p', parts[1] = role, parts[2] = action, parts[3] = scope
60
- matches = True
61
- if role and parts[1] != role:
62
- matches = False
63
- if scope_pattern and parts[3] != scope_pattern:
64
- matches = False
65
-
66
- if matches:
67
- count += 1
68
- return count
45
+ return sum(
46
+ 1
47
+ for policy in self.schema_policies
48
+ if (not role or policy[0] == role) and (not scope_pattern or policy[2] == scope_pattern)
49
+ )
69
50
 
70
51
  def _seed_database_with_policies(self):
71
- """Seed the database with policies from the policy file.
52
+ """Seed the database with policies from the authorization schema.
72
53
 
73
54
  This simulates the one-time database seeding that would happen
74
55
  during application deployment, separate from runtime policy loading.
@@ -77,13 +58,7 @@ class PolicyLoadingTestSetupMixin(TestCase):
77
58
  global_enforcer = AuthzEnforcer.get_enforcer()
78
59
  global_enforcer.clear_policy()
79
60
 
80
- migrate_policy_between_enforcers(
81
- source_enforcer=casbin.Enforcer(
82
- MODEL_CONF_PATH,
83
- AUTHZ_POLICY_PATH,
84
- ),
85
- target_enforcer=global_enforcer,
86
- )
61
+ self.schema_policies = seed_static_authorization_policies(global_enforcer)
87
62
  # Ensure enforcer memory is clean for test isolation
88
63
  global_enforcer.clear_policy()
89
64
 
@@ -169,7 +144,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
169
144
 
170
145
  These tests demonstrate how policy loading would work in real-world scenarios,
171
146
  including scope-based loading, user-context loading, and role-specific loading.
172
- All based on our basic policy setup in authz.policy file.
147
+ Static role-permission policies come from the authorization schema.
173
148
  """
174
149
 
175
150
  LIBRARY_ROLES = [
@@ -190,7 +165,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
190
165
  super().tearDown()
191
166
 
192
167
  @ddt_data(
193
- "lib^*", # Library policies from authz.policy file
168
+ "lib^*", # Library policies from the authorization schema
194
169
  "course^*", # No course policies in basic setup
195
170
  "org^*", # No org policies in basic setup
196
171
  )
@@ -206,7 +181,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
206
181
  - Policy count matches expected for scope
207
182
  """
208
183
  global_enforcer = AuthzEnforcer.get_enforcer()
209
- expected_policy_count = self._count_policies_in_file(scope_pattern=scope)
184
+ expected_policy_count = self._count_schema_policies(scope_pattern=scope)
210
185
  initial_policy_count = len(global_enforcer.get_policy())
211
186
 
212
187
  self._load_policies_for_scope(scope)
@@ -382,7 +357,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
382
357
  - All loaded policies contain the specified role
383
358
  """
384
359
  global_enforcer = AuthzEnforcer.get_enforcer()
385
- expected_count = self._count_policies_in_file(role=role_name)
360
+ expected_count = self._count_schema_policies(role=role_name)
386
361
 
387
362
  self._load_policies_for_role_management(role_name)
388
363
  loaded_policies = global_enforcer.get_policy()
@@ -404,8 +379,8 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
404
379
  course_scope = "course-v1^*"
405
380
  org_scope = "org^*"
406
381
 
407
- expected_lib_count = self._count_policies_in_file(scope_pattern=lib_scope)
408
- expected_course_count = self._count_policies_in_file(scope_pattern=course_scope)
382
+ expected_lib_count = self._count_schema_policies(scope_pattern=lib_scope)
383
+ expected_course_count = self._count_schema_policies(scope_pattern=course_scope)
409
384
  self._add_test_policies_for_multiple_scopes()
410
385
  # The previous function added 6 custom policies for courses, add to expected
411
386
  expected_course_count += 6
@@ -436,7 +411,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin):
436
411
  self.assertEqual(total_count, lib_count + course_count + org_count)
437
412
 
438
413
 
439
- class TestAutoLoadPolicy(TransactionTestCase):
414
+ class TestAutoLoadPolicy(AuthzEnforcerTestMixin, TransactionTestCase):
440
415
  """Test cases for auto-load policy functionality.
441
416
 
442
417
  Uses TransactionTestCase to avoid database locking issues with SQLite
@@ -449,18 +424,12 @@ class TestAutoLoadPolicy(TransactionTestCase):
449
424
  AuthzEnforcer._enforcer = None # pylint: disable=protected-access
450
425
 
451
426
  def _seed_database_with_policies(self):
452
- """Seed the database with policies from the policy file."""
427
+ """Seed the database with policies from the authorization schema."""
453
428
  global_enforcer = AuthzEnforcer.get_enforcer()
454
- global_enforcer.clear_policy()
455
-
456
- migrate_policy_between_enforcers(
457
- source_enforcer=casbin.Enforcer(
458
- MODEL_CONF_PATH,
459
- AUTHZ_POLICY_PATH,
460
- ),
461
- target_enforcer=global_enforcer,
462
- )
463
- global_enforcer.clear_policy()
429
+ with self.pause_authz_auto_load(global_enforcer):
430
+ global_enforcer.clear_policy()
431
+ seed_static_authorization_policies(global_enforcer)
432
+ global_enforcer.clear_policy()
464
433
 
465
434
  def _wait_for_auto_load(self) -> None:
466
435
  """Wait for one auto-load cycle plus a small buffer.