Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Unreleased

*


0.20.1 - 2026-02-05
********************

Added
=====

* Add PoF role and permissions for the advanced course settings section

0.20.0 - 2025-11-27
********************

Expand Down
2 changes: 1 addition & 1 deletion openedx_authz/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

import os

__version__ = "0.20.0"
__version__ = "0.20.1"

ROOT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
9 changes: 9 additions & 0 deletions openedx_authz/constants/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,12 @@
action=ActionData(external_key=f"{CONTENT_LIBRARIES_NAMESPACE}.delete_library_collection"),
effect="allow",
)

# Course Permissions

COURSES_NAMESPACE = "courses"

MANAGE_ADVANCED_SETTINGS = PermissionData(
action=ActionData(external_key=f"{COURSES_NAMESPACE}.manage_advanced_settings"),
effect="allow",
)
10 changes: 10 additions & 0 deletions openedx_authz/constants/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,13 @@
LIBRARY_AUTHOR = RoleData(external_key="library_author", permissions=LIBRARY_AUTHOR_PERMISSIONS)
LIBRARY_CONTRIBUTOR = RoleData(external_key="library_contributor", permissions=LIBRARY_CONTRIBUTOR_PERMISSIONS)
LIBRARY_USER = RoleData(external_key="library_user", permissions=LIBRARY_USER_PERMISSIONS)


# Course Roles and Permissions


COURSE_STAFF_PERMISSIONS = [
permissions.MANAGE_ADVANCED_SETTINGS,
]

COURSE_STAFF = RoleData(external_key="course_staff", permissions=COURSE_STAFF_PERMISSIONS)
6 changes: 6 additions & 0 deletions openedx_authz/engine/config/authz.policy
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,9 @@ g2, act^content_libraries.manage_library_team, act^content_libraries.view_librar
g2, act^content_libraries.delete_library_collection, act^content_libraries.edit_library_collection
g2, act^content_libraries.create_library_collection, act^content_libraries.edit_library_collection
g2, act^content_libraries.edit_library_collection, act^content_libraries.view_library


# Course Policies

# Course Staff Permissions
p, role^course_staff, act^courses.manage_advanced_settings, course^*, allow
2 changes: 1 addition & 1 deletion openedx_authz/tests/test_enforcer.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ def test_multi_scope_filtering(self):
org_count = len(global_enforcer.get_policy())

self.assertEqual(lib_count, expected_lib_count)
self.assertEqual(course_count, 6)
self.assertEqual(course_count, 7)
self.assertEqual(org_count, 3)

global_enforcer.clear_policy()
Expand Down
14 changes: 7 additions & 7 deletions openedx_authz/tests/test_engine_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ def test_migrate_all_file_policies_to_database(self):

Expected Result:
- All policies from the file are loaded into the database
- The file contains 31 regular policies (p rules)
- The file contains 32 regular policies (p rules)
- Policy content matches expected file content
"""
expected_policy_count = 31
expected_policy_count = 32

migrate_policy_between_enforcers(self.source_enforcer, self.target_enforcer)
self.target_enforcer.load_policy()
Expand Down Expand Up @@ -216,7 +216,7 @@ def test_migrate_complete_file_contents(self):

self.assertEqual(
len(self.target_enforcer.get_policy()),
31,
32,
"Should have 31 regular policies from file",
)
self.assertEqual(
Expand Down Expand Up @@ -250,8 +250,8 @@ def test_migrate_partial_duplicates(self):
target_policies = self.target_enforcer.get_policy()
self.assertEqual(
len(target_policies),
31,
"Should have 31 policies total, with no duplicates",
32,
"Should have 32 policies total, with no duplicates",
)

duplicates = CasbinRule.objects.values("v0", "v1", "v2").annotate(total=Count("*")).filter(total__gt=1)
Expand Down Expand Up @@ -346,7 +346,7 @@ def test_migrate_preserves_existing_db_policies(self):
migrate_policy_between_enforcers(self.source_enforcer, self.target_enforcer)

target_policies = self.target_enforcer.get_policy()
self.assertEqual(len(target_policies), 32, "Should have 31 file policies + 1 custom policy")
self.assertEqual(len(target_policies), 33, "Should have 32 file policies + 1 custom policy")
self.assertIn(custom_policy, target_policies, "Custom database policy should be preserved")

def test_migrate_preserves_user_role_assignments_in_db(self):
Expand Down Expand Up @@ -382,4 +382,4 @@ def test_migrate_preserves_user_role_assignments_in_db(self):
)

target_policies = self.target_enforcer.get_policy()
self.assertEqual(len(target_policies), 31, "All 31 policies from file should be loaded")
self.assertEqual(len(target_policies), 32, "All 32 policies from file should be loaded")