From 467784b491ad4868e4eab41d67194ab918c21ee8 Mon Sep 17 00:00:00 2001 From: Bryann Valderrama Date: Thu, 12 Nov 2026 12:32:06 -0500 Subject: [PATCH] refactor: import specific names from openedx_authz.authz Replace module-style authz.X access with explicit imports of only the constants each module needs. Co-Authored-By: Claude Sonnet 5.5 --- src/openedx_authz/engine/utils.py | 2 +- .../rest_api/v1/admin_console/views.py | 25 +- src/openedx_authz/rest_api/v1/permissions.py | 2 +- src/openedx_authz/rest_api/v1/views.py | 18 +- src/openedx_authz/tests/api/test_data.py | 29 +- src/openedx_authz/tests/api/test_roles.py | 345 +++++++++--------- src/openedx_authz/tests/api/test_users.py | 159 ++++---- .../tests/applets/test_builtin_definitions.py | 39 +- .../test_instructor_dashboard_permissions.py | 24 +- .../rest_api/admin_console/test_views.py | 127 ++++--- src/openedx_authz/tests/rest_api/mixins.py | 26 +- .../tests/rest_api/test_views.py | 219 +++++------ src/openedx_authz/tests/role_permissions.py | 217 +++++++---- src/openedx_authz/tests/test_commands.py | 4 +- src/openedx_authz/tests/test_enforcement.py | 45 ++- src/openedx_authz/tests/test_engine_utils.py | 84 +++-- 16 files changed, 763 insertions(+), 602 deletions(-) diff --git a/src/openedx_authz/engine/utils.py b/src/openedx_authz/engine/utils.py index 344ab4e8..d7778a0e 100644 --- a/src/openedx_authz/engine/utils.py +++ b/src/openedx_authz/engine/utils.py @@ -21,7 +21,7 @@ batch_assign_role_to_users_in_scope, batch_unassign_role_from_users, ) -from openedx_authz.constants.roles import ( +from openedx_authz.authz import ( LEGACY_COURSE_ROLE_EQUIVALENCES, LIBRARY_ADMIN, LIBRARY_AUTHOR, diff --git a/src/openedx_authz/rest_api/v1/admin_console/views.py b/src/openedx_authz/rest_api/v1/admin_console/views.py index 65009b66..8c2fd4c6 100644 --- a/src/openedx_authz/rest_api/v1/admin_console/views.py +++ b/src/openedx_authz/rest_api/v1/admin_console/views.py @@ -38,7 +38,10 @@ get_visible_role_assignments_for_user, get_visible_user_role_assignments_filtered_by_current_user, ) -from openedx_authz.constants import permissions +from openedx_authz.authz import ( + COURSES_VIEW_COURSE_TEAM, + VIEW_LIBRARY_TEAM, +) from openedx_authz.models.scopes import get_content_library_model, get_course_overview_model from openedx_authz.rest_api.data import ScopesQuerySetFields, ScopesTypeField from openedx_authz.rest_api.decorators import authz_permissions, view_auth_classes @@ -73,8 +76,8 @@ @method_decorator( authz_permissions( [ - permissions.VIEW_LIBRARY_TEAM.identifier, - permissions.COURSES_VIEW_COURSE_TEAM.identifier, + VIEW_LIBRARY_TEAM.identifier, + COURSES_VIEW_COURSE_TEAM.identifier, ] ), name="get", @@ -183,8 +186,8 @@ def get_queryset(self) -> QuerySet: @method_decorator( authz_permissions( [ - permissions.VIEW_LIBRARY_TEAM.identifier, - permissions.COURSES_VIEW_COURSE_TEAM.identifier, + VIEW_LIBRARY_TEAM.identifier, + COURSES_VIEW_COURSE_TEAM.identifier, ] ), name="get", @@ -639,8 +642,8 @@ class TeamMembersAPIView(APIView): ) @authz_permissions( [ - permissions.VIEW_LIBRARY_TEAM.identifier, - permissions.COURSES_VIEW_COURSE_TEAM.identifier, + VIEW_LIBRARY_TEAM.identifier, + COURSES_VIEW_COURSE_TEAM.identifier, ] ) def get(self, request: HttpRequest) -> Response: @@ -765,8 +768,8 @@ class TeamMemberAssignmentsAPIView(APIView): ) @authz_permissions( [ - permissions.VIEW_LIBRARY_TEAM.identifier, - permissions.COURSES_VIEW_COURSE_TEAM.identifier, + VIEW_LIBRARY_TEAM.identifier, + COURSES_VIEW_COURSE_TEAM.identifier, ] ) def get(self, request: HttpRequest, username: str) -> Response: @@ -887,8 +890,8 @@ class AssignmentsAPIView(APIView): ) @authz_permissions( [ - permissions.VIEW_LIBRARY_TEAM.identifier, - permissions.COURSES_VIEW_COURSE_TEAM.identifier, + VIEW_LIBRARY_TEAM.identifier, + COURSES_VIEW_COURSE_TEAM.identifier, ] ) def get(self, request: HttpRequest) -> Response: diff --git a/src/openedx_authz/rest_api/v1/permissions.py b/src/openedx_authz/rest_api/v1/permissions.py index dbdce0b4..83d76339 100644 --- a/src/openedx_authz/rest_api/v1/permissions.py +++ b/src/openedx_authz/rest_api/v1/permissions.py @@ -6,7 +6,7 @@ from rest_framework.permissions import BasePermission from openedx_authz import api -from openedx_authz.constants.permissions import COURSES_VIEW_COURSE_TEAM, VIEW_LIBRARY_TEAM +from openedx_authz.authz import COURSES_VIEW_COURSE_TEAM, VIEW_LIBRARY_TEAM from openedx_authz.rest_api.data import ScopesTypeField from openedx_authz.rest_api.utils import parse_scope_types diff --git a/src/openedx_authz/rest_api/v1/views.py b/src/openedx_authz/rest_api/v1/views.py index 68111c7a..3143e608 100644 --- a/src/openedx_authz/rest_api/v1/views.py +++ b/src/openedx_authz/rest_api/v1/views.py @@ -19,8 +19,14 @@ from openedx_authz import api from openedx_authz.api.utils import get_user_map -from openedx_authz.constants import permissions -from openedx_authz.constants.roles import COURSE_ADMIN, LIBRARY_ADMIN +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSES_MANAGE_COURSE_TEAM, + COURSES_VIEW_COURSE_TEAM, + LIBRARY_ADMIN, + MANAGE_LIBRARY_TEAM, + VIEW_LIBRARY, +) from openedx_authz.filters import PermissionValidationRequested, RoleAssignmentRequested, RoleRemovalRequested from openedx_authz.rest_api.data import RoleOperationError, RoleOperationStatus from openedx_authz.rest_api.decorators import authz_permissions, view_auth_classes @@ -287,7 +293,7 @@ class RoleUserAPIView(APIView): status.HTTP_401_UNAUTHORIZED: "The user is not authenticated or does not have the required permissions", }, ) - @authz_permissions([permissions.VIEW_LIBRARY.identifier, permissions.COURSES_VIEW_COURSE_TEAM.identifier]) + @authz_permissions([VIEW_LIBRARY.identifier, COURSES_VIEW_COURSE_TEAM.identifier]) def get(self, request: HttpRequest) -> Response: """Retrieve all users with role assignments within a specific scope.""" serializer = ListUsersInRoleWithScopeSerializer(data=request.query_params) @@ -314,7 +320,7 @@ def get(self, request: HttpRequest) -> Response: status.HTTP_401_UNAUTHORIZED: "The user is not authenticated or does not have the required permissions", }, ) - @authz_permissions([permissions.MANAGE_LIBRARY_TEAM.identifier, permissions.COURSES_MANAGE_COURSE_TEAM.identifier]) + @authz_permissions([MANAGE_LIBRARY_TEAM.identifier, COURSES_MANAGE_COURSE_TEAM.identifier]) def put(self, request: HttpRequest) -> Response: """Assign multiple users to a specific role within one or more scopes.""" serializer = AddUsersToRoleWithScopeSerializer(data=request.data) @@ -362,7 +368,7 @@ def put(self, request: HttpRequest) -> Response: status.HTTP_401_UNAUTHORIZED: "The user is not authenticated or does not have the required permissions", }, ) - @authz_permissions([permissions.MANAGE_LIBRARY_TEAM.identifier, permissions.COURSES_MANAGE_COURSE_TEAM.identifier]) + @authz_permissions([MANAGE_LIBRARY_TEAM.identifier, COURSES_MANAGE_COURSE_TEAM.identifier]) def delete(self, request: HttpRequest) -> Response: """Remove multiple users from a specific role within a scope.""" serializer = RemoveUsersFromRoleWithScopeSerializer(data=request.query_params) @@ -535,7 +541,7 @@ class UserValidationAPIView(APIView): status.HTTP_500_INTERNAL_SERVER_ERROR: "An unexpected error occurred while validating users", }, ) - @authz_permissions([permissions.MANAGE_LIBRARY_TEAM.identifier, permissions.COURSES_MANAGE_COURSE_TEAM.identifier]) + @authz_permissions([MANAGE_LIBRARY_TEAM.identifier, COURSES_MANAGE_COURSE_TEAM.identifier]) def post(self, request: HttpRequest) -> Response: """Validates the provided usernames or emails correspond to existing users.""" request_serializer = UserValidationAPIViewSerializer(data=request.data) diff --git a/src/openedx_authz/tests/api/test_data.py b/src/openedx_authz/tests/api/test_data.py index 56823076..f2097ed3 100644 --- a/src/openedx_authz/tests/api/test_data.py +++ b/src/openedx_authz/tests/api/test_data.py @@ -23,7 +23,14 @@ SubjectData, UserData, ) -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSES_MANAGE_COURSE_TEAM, + COURSES_VIEW_COURSE_TEAM, + DELETE_LIBRARY, + LIBRARY_ADMIN, + MANAGE_LIBRARY_TEAM, + VIEW_LIBRARY_TEAM, +) from openedx_authz.tests.stubs.models import ContentLibrary, CourseOverview, Organization @@ -713,7 +720,7 @@ def test_user_data_str_and_repr(self, external_key, expected_str, expected_repr) ("read", "Read", "act^read"), ("write", "Write", "act^write"), ( - permissions.DELETE_LIBRARY.identifier, + DELETE_LIBRARY.identifier, "Content Libraries > Delete Library", "act^content_libraries.delete_library", ), @@ -757,7 +764,7 @@ def test_scope_data_str_and_repr(self, external_key, expected_str, expected_repr @data( ("instructor", "Instructor", "role^instructor"), - (roles.LIBRARY_ADMIN.external_key, "Library Admin", "role^library_admin"), + (LIBRARY_ADMIN.external_key, "Library Admin", "role^library_admin"), ("course_staff", "Course Staff", "role^course_staff"), ) @unpack @@ -798,7 +805,7 @@ def test_role_data_str_with_permissions(self): ("read", "allow", "Read - allow", "act^read => allow"), ("write", "deny", "Write - deny", "act^write => deny"), ( - permissions.DELETE_LIBRARY.identifier, + DELETE_LIBRARY.identifier, "allow", "Content Libraries > Delete Library - allow", "act^content_libraries.delete_library => allow", @@ -829,7 +836,7 @@ def test_role_assignment_data_str(self): """ user = UserData(external_key="john_doe") role1 = RoleData(external_key="instructor") - role2 = RoleData(external_key=roles.LIBRARY_ADMIN.external_key) + role2 = RoleData(external_key=LIBRARY_ADMIN.external_key) scope = ContentLibraryData(external_key="lib:DemoX:CSPROB") assignment = RoleAssignmentData(subject=user, roles=[role1, role2], scope=scope) @@ -846,7 +853,7 @@ def test_role_assignment_data_repr(self): """ user = UserData(external_key="john_doe") role1 = RoleData(external_key="instructor") - role2 = RoleData(external_key=roles.LIBRARY_ADMIN.external_key) + role2 = RoleData(external_key=LIBRARY_ADMIN.external_key) scope = ContentLibraryData(external_key="lib:DemoX:CSPROB") assignment = RoleAssignmentData(subject=user, roles=[role1, role2], scope=scope) @@ -1227,15 +1234,13 @@ def test_dynamic_instantiation_via_scope_data(self): def test_get_admin_view_permission(self): """View permission matches course team view permission.""" - self.assertEqual( - PlatformCourseOverviewGlobData.get_admin_view_permission(), permissions.COURSES_VIEW_COURSE_TEAM - ) + self.assertEqual(PlatformCourseOverviewGlobData.get_admin_view_permission(), COURSES_VIEW_COURSE_TEAM) def test_get_admin_manage_permission(self): """Manage permission matches course team manage permission.""" self.assertEqual( PlatformCourseOverviewGlobData.get_admin_manage_permission(), - permissions.COURSES_MANAGE_COURSE_TEAM, + COURSES_MANAGE_COURSE_TEAM, ) def test_is_platform_glob(self): @@ -1303,13 +1308,13 @@ def test_dynamic_instantiation_via_scope_data(self): def test_get_admin_view_permission(self): """View permission matches library team view permission.""" - self.assertEqual(PlatformContentLibraryGlobData.get_admin_view_permission(), permissions.VIEW_LIBRARY_TEAM) + self.assertEqual(PlatformContentLibraryGlobData.get_admin_view_permission(), VIEW_LIBRARY_TEAM) def test_get_admin_manage_permission(self): """Manage permission matches library team manage permission.""" self.assertEqual( PlatformContentLibraryGlobData.get_admin_manage_permission(), - permissions.MANAGE_LIBRARY_TEAM, + MANAGE_LIBRARY_TEAM, ) def test_is_platform_glob(self): diff --git a/src/openedx_authz/tests/api/test_roles.py b/src/openedx_authz/tests/api/test_roles.py index b99e82ab..e124b4ae 100644 --- a/src/openedx_authz/tests/api/test_roles.py +++ b/src/openedx_authz/tests/api/test_roles.py @@ -39,7 +39,22 @@ unassign_role_from_subject_in_scope, unassign_subject_from_all_roles, ) -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_AUDITOR, + COURSE_BETA_TESTER, + COURSE_DATA_RESEARCHER, + COURSE_EDITOR, + COURSE_LIMITED_STAFF, + COURSE_STAFF, + DELETE_LIBRARY, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_CONTRIBUTOR, + LIBRARY_USER, + PUBLISH_LIBRARY_CONTENT, + VIEW_LIBRARY, +) from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.models import ExtendedCasbinRule, Scope, Subject from openedx_authz.tests.role_permissions import ( @@ -182,181 +197,181 @@ def setUpClass(cls): # Basic library roles from the authorization schema { "subject_name": "alice", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org1:math_101", }, { "subject_name": "bob", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org1:history_201", }, { "subject_name": "carol", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org1:science_301", }, { "subject_name": "dave", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:english_101", }, # Basic course roles from the authorization schema { "subject_name": "daniel", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T1", }, { "subject_name": "judy", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T2", }, # Multi-role assignments - same subject with different roles in different libraries { "subject_name": "eve", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org2:physics_401", }, { "subject_name": "eve", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org2:chemistry_501", }, { "subject_name": "eve", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org2:biology_601", }, # Multiple subjects with same role in same scope { "subject_name": "grace", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org1:math_advanced", }, { "subject_name": "heidi", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org1:math_advanced", }, { "subject_name": "maria", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T3", }, { "subject_name": "aida", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T3", }, # Hierarchical scope assignments - different specificity levels { "subject_name": "ivy", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org3:cs_101", }, { "subject_name": "jack", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org3:cs_101", }, { "subject_name": "kate", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org3:cs_101", }, # Edge case: same user, same role, different scopes { "subject_name": "liam", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org4:art_101", }, { "subject_name": "liam", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org4:art_201", }, { "subject_name": "liam", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org4:art_301", }, { "subject_name": "carlos", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T1", }, { "subject_name": "carlos", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T2", }, { "subject_name": "carlos", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T3", }, # Mixed permission levels across libraries for comprehensive testing { "subject_name": "maya", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org5:economics_101", }, { "subject_name": "noah", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org5:economics_101", }, { "subject_name": "olivia", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org5:economics_101", }, # Complex multi-library, multi-role scenario { "subject_name": "peter", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org6:project_alpha", }, { "subject_name": "peter", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org6:project_beta", }, { "subject_name": "peter", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org6:project_gamma", }, { "subject_name": "peter", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org6:project_delta", }, { "subject_name": "frank", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org6:project_epsilon", }, # Same user, same scope, different role { "subject_name": "george", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org6:project_zeta", }, { "subject_name": "george", - "role_name": roles.LIBRARY_CONTRIBUTOR.external_key, + "role_name": LIBRARY_CONTRIBUTOR.external_key, "scope_name": "lib:Org6:project_zeta", }, # Course editor and auditor assignments for view/manage permission testing { "subject_name": "editor_user", - "role_name": roles.COURSE_EDITOR.external_key, + "role_name": COURSE_EDITOR.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T1", }, { "subject_name": "auditor_user", - "role_name": roles.COURSE_AUDITOR.external_key, + "role_name": COURSE_AUDITOR.external_key, "scope_name": "course-v1:TestOrg+TestCourse+2024_T1", }, ] @@ -430,22 +445,22 @@ def test_assign_role_fails_when_extended_rule_not_created(self): @ddt_data( # Library Admin role with permissions from the authorization schema ( - roles.LIBRARY_ADMIN.external_key, + LIBRARY_ADMIN.external_key, LIBRARY_ADMIN_PERMISSIONS, ), # Library Author role with permissions from the authorization schema ( - roles.LIBRARY_AUTHOR.external_key, + LIBRARY_AUTHOR.external_key, LIBRARY_AUTHOR_PERMISSIONS, ), # Library Contributor role with permissions from the authorization schema ( - roles.LIBRARY_CONTRIBUTOR.external_key, + LIBRARY_CONTRIBUTOR.external_key, LIBRARY_CONTRIBUTOR_PERMISSIONS, ), # Library User role with minimal permissions ( - roles.LIBRARY_USER.external_key, + LIBRARY_USER.external_key, LIBRARY_USER_PERMISSIONS, ), # Non existent role @@ -469,19 +484,19 @@ def test_get_permissions_for_roles(self, role_name, expected_permissions): @ddt_data( # Role assigned to multiple users in different scopes ( - roles.LIBRARY_USER.external_key, + LIBRARY_USER.external_key, "lib:Org1:english_101", LIBRARY_USER_PERMISSIONS, ), # Role assigned to single user in single scope ( - roles.LIBRARY_AUTHOR.external_key, + LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", LIBRARY_AUTHOR_PERMISSIONS, ), # Role assigned to single user in multiple scopes ( - roles.LIBRARY_ADMIN.external_key, + LIBRARY_ADMIN.external_key, "lib:Org1:math_101", LIBRARY_ADMIN_PERMISSIONS, ), @@ -508,22 +523,22 @@ def test_get_permissions_for_active_role_in_specific_scope(self, role_name, scop ( "lib^*", { - roles.LIBRARY_ADMIN.external_key: LIBRARY_ADMIN_PERMISSIONS, - roles.LIBRARY_AUTHOR.external_key: LIBRARY_AUTHOR_PERMISSIONS, - roles.LIBRARY_CONTRIBUTOR.external_key: LIBRARY_CONTRIBUTOR_PERMISSIONS, - roles.LIBRARY_USER.external_key: LIBRARY_USER_PERMISSIONS, + LIBRARY_ADMIN.external_key: LIBRARY_ADMIN_PERMISSIONS, + LIBRARY_AUTHOR.external_key: LIBRARY_AUTHOR_PERMISSIONS, + LIBRARY_CONTRIBUTOR.external_key: LIBRARY_CONTRIBUTOR_PERMISSIONS, + LIBRARY_USER.external_key: LIBRARY_USER_PERMISSIONS, }, ), ( "course-v1^*", { - roles.COURSE_ADMIN.external_key: COURSE_ADMIN_PERMISSIONS, - roles.COURSE_AUDITOR.external_key: COURSE_AUDITOR_PERMISSIONS, - roles.COURSE_BETA_TESTER.external_key: COURSE_BETA_TESTER_PERMISSIONS, - roles.COURSE_DATA_RESEARCHER.external_key: COURSE_DATA_RESEARCHER_PERMISSIONS, - roles.COURSE_EDITOR.external_key: COURSE_EDITOR_PERMISSIONS, - roles.COURSE_LIMITED_STAFF.external_key: COURSE_LIMITED_STAFF_PERMISSIONS, - roles.COURSE_STAFF.external_key: COURSE_STAFF_PERMISSIONS, + COURSE_ADMIN.external_key: COURSE_ADMIN_PERMISSIONS, + COURSE_AUDITOR.external_key: COURSE_AUDITOR_PERMISSIONS, + COURSE_BETA_TESTER.external_key: COURSE_BETA_TESTER_PERMISSIONS, + COURSE_DATA_RESEARCHER.external_key: COURSE_DATA_RESEARCHER_PERMISSIONS, + COURSE_EDITOR.external_key: COURSE_EDITOR_PERMISSIONS, + COURSE_LIMITED_STAFF.external_key: COURSE_LIMITED_STAFF_PERMISSIONS, + COURSE_STAFF.external_key: COURSE_STAFF_PERMISSIONS, }, ), ) @@ -557,27 +572,27 @@ def test_get_role_definitions_in_scope_returns_empty_for_concrete_scope(self): self.assertEqual(roles_in_scope, []) @ddt_data( - ("alice", "lib:Org1:math_101", {roles.LIBRARY_ADMIN.external_key}), - ("bob", "lib:Org1:history_201", {roles.LIBRARY_AUTHOR.external_key}), - ("carol", "lib:Org1:science_301", {roles.LIBRARY_CONTRIBUTOR.external_key}), - ("dave", "lib:Org1:english_101", {roles.LIBRARY_USER.external_key}), - ("eve", "lib:Org2:physics_401", {roles.LIBRARY_ADMIN.external_key}), - ("eve", "lib:Org2:chemistry_501", {roles.LIBRARY_AUTHOR.external_key}), - ("eve", "lib:Org2:biology_601", {roles.LIBRARY_USER.external_key}), - ("grace", "lib:Org1:math_advanced", {roles.LIBRARY_CONTRIBUTOR.external_key}), - ("ivy", "lib:Org3:cs_101", {roles.LIBRARY_ADMIN.external_key}), - ("jack", "lib:Org3:cs_101", {roles.LIBRARY_AUTHOR.external_key}), - ("kate", "lib:Org3:cs_101", {roles.LIBRARY_USER.external_key}), - ("liam", "lib:Org4:art_101", {roles.LIBRARY_AUTHOR.external_key}), - ("liam", "lib:Org4:art_201", {roles.LIBRARY_AUTHOR.external_key}), - ("liam", "lib:Org4:art_301", {roles.LIBRARY_AUTHOR.external_key}), - ("maya", "lib:Org5:economics_101", {roles.LIBRARY_ADMIN.external_key}), - ("noah", "lib:Org5:economics_101", {roles.LIBRARY_CONTRIBUTOR.external_key}), - ("olivia", "lib:Org5:economics_101", {roles.LIBRARY_USER.external_key}), - ("peter", "lib:Org6:project_alpha", {roles.LIBRARY_ADMIN.external_key}), - ("peter", "lib:Org6:project_beta", {roles.LIBRARY_AUTHOR.external_key}), - ("peter", "lib:Org6:project_gamma", {roles.LIBRARY_CONTRIBUTOR.external_key}), - ("peter", "lib:Org6:project_delta", {roles.LIBRARY_USER.external_key}), + ("alice", "lib:Org1:math_101", {LIBRARY_ADMIN.external_key}), + ("bob", "lib:Org1:history_201", {LIBRARY_AUTHOR.external_key}), + ("carol", "lib:Org1:science_301", {LIBRARY_CONTRIBUTOR.external_key}), + ("dave", "lib:Org1:english_101", {LIBRARY_USER.external_key}), + ("eve", "lib:Org2:physics_401", {LIBRARY_ADMIN.external_key}), + ("eve", "lib:Org2:chemistry_501", {LIBRARY_AUTHOR.external_key}), + ("eve", "lib:Org2:biology_601", {LIBRARY_USER.external_key}), + ("grace", "lib:Org1:math_advanced", {LIBRARY_CONTRIBUTOR.external_key}), + ("ivy", "lib:Org3:cs_101", {LIBRARY_ADMIN.external_key}), + ("jack", "lib:Org3:cs_101", {LIBRARY_AUTHOR.external_key}), + ("kate", "lib:Org3:cs_101", {LIBRARY_USER.external_key}), + ("liam", "lib:Org4:art_101", {LIBRARY_AUTHOR.external_key}), + ("liam", "lib:Org4:art_201", {LIBRARY_AUTHOR.external_key}), + ("liam", "lib:Org4:art_301", {LIBRARY_AUTHOR.external_key}), + ("maya", "lib:Org5:economics_101", {LIBRARY_ADMIN.external_key}), + ("noah", "lib:Org5:economics_101", {LIBRARY_CONTRIBUTOR.external_key}), + ("olivia", "lib:Org5:economics_101", {LIBRARY_USER.external_key}), + ("peter", "lib:Org6:project_alpha", {LIBRARY_ADMIN.external_key}), + ("peter", "lib:Org6:project_beta", {LIBRARY_AUTHOR.external_key}), + ("peter", "lib:Org6:project_gamma", {LIBRARY_CONTRIBUTOR.external_key}), + ("peter", "lib:Org6:project_delta", {LIBRARY_USER.external_key}), ("non_existent_user", "lib:Org1:math_101", set()), ("alice", "lib:Org999:non_existent_scope", set()), ("non_existent_user", "lib:Org999:non_existent_scope", set()), @@ -600,21 +615,21 @@ def test_get_subject_role_assignments_in_scope(self, subject_name, scope_name, e ( "alice", [ - RoleData(external_key=roles.LIBRARY_ADMIN.external_key), + RoleData(external_key=LIBRARY_ADMIN.external_key), ], ), ( "eve", [ - RoleData(external_key=roles.LIBRARY_ADMIN.external_key), - RoleData(external_key=roles.LIBRARY_AUTHOR.external_key), - RoleData(external_key=roles.LIBRARY_USER.external_key), + RoleData(external_key=LIBRARY_ADMIN.external_key), + RoleData(external_key=LIBRARY_AUTHOR.external_key), + RoleData(external_key=LIBRARY_USER.external_key), ], ), ( "frank", [ - RoleData(external_key=roles.LIBRARY_USER.external_key), + RoleData(external_key=LIBRARY_USER.external_key), ], ), ("non_existent_user", []), @@ -636,29 +651,29 @@ def test_get_all_role_assignments_scopes(self, subject_name, expected_roles): self.assertTrue(found, f"Expected role {expected_role} not found in assignments") @ddt_data( - (roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", 1), - (roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", 1), - (roles.LIBRARY_USER.external_key, "lib:Org1:english_101", 1), - (roles.LIBRARY_ADMIN.external_key, "lib:Org2:physics_401", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org2:chemistry_501", 1), - (roles.LIBRARY_USER.external_key, "lib:Org2:biology_601", 1), - (roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", 2), - (roles.LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org3:cs_101", 1), - (roles.LIBRARY_USER.external_key, "lib:Org3:cs_101", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_201", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_301", 1), - (roles.LIBRARY_ADMIN.external_key, "lib:Org5:economics_101", 1), - (roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org5:economics_101", 1), - (roles.LIBRARY_USER.external_key, "lib:Org5:economics_101", 1), - (roles.LIBRARY_ADMIN.external_key, "lib:Org6:project_alpha", 1), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org6:project_beta", 1), - (roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org6:project_gamma", 1), - (roles.LIBRARY_USER.external_key, "lib:Org6:project_delta", 1), + (LIBRARY_ADMIN.external_key, "lib:Org1:math_101", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", 1), + (LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", 1), + (LIBRARY_USER.external_key, "lib:Org1:english_101", 1), + (LIBRARY_ADMIN.external_key, "lib:Org2:physics_401", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org2:chemistry_501", 1), + (LIBRARY_USER.external_key, "lib:Org2:biology_601", 1), + (LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", 2), + (LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org3:cs_101", 1), + (LIBRARY_USER.external_key, "lib:Org3:cs_101", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_201", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_301", 1), + (LIBRARY_ADMIN.external_key, "lib:Org5:economics_101", 1), + (LIBRARY_CONTRIBUTOR.external_key, "lib:Org5:economics_101", 1), + (LIBRARY_USER.external_key, "lib:Org5:economics_101", 1), + (LIBRARY_ADMIN.external_key, "lib:Org6:project_alpha", 1), + (LIBRARY_AUTHOR.external_key, "lib:Org6:project_beta", 1), + (LIBRARY_CONTRIBUTOR.external_key, "lib:Org6:project_gamma", 1), + (LIBRARY_USER.external_key, "lib:Org6:project_delta", 1), ("non_existent_role", "global:any_library", 0), - (roles.LIBRARY_ADMIN.external_key, "global:non_existent_scope", 0), + (LIBRARY_ADMIN.external_key, "global:non_existent_scope", 0), ("non_existent_role", "global:non_existent_scope", 0), ) @unpack @@ -679,11 +694,11 @@ def test_get_role_assignments_in_scope(self, role_name, scope_name, expected_cou ("alice", None, None, 1), ("eve", None, None, 3), # Test with role only - (None, roles.LIBRARY_ADMIN.external_key, None, 5), - (None, roles.LIBRARY_AUTHOR.external_key, None, 8), - (None, roles.LIBRARY_CONTRIBUTOR.external_key, None, 6), - (None, roles.LIBRARY_USER.external_key, None, 6), - (None, roles.COURSE_STAFF.external_key, None, 7), + (None, LIBRARY_ADMIN.external_key, None, 5), + (None, LIBRARY_AUTHOR.external_key, None, 8), + (None, LIBRARY_CONTRIBUTOR.external_key, None, 6), + (None, LIBRARY_USER.external_key, None, 6), + (None, COURSE_STAFF.external_key, None, 7), # Test with scope only (None, None, "lib:Org1:math_101", 1), (None, None, "lib:Org1:history_201", 1), @@ -696,22 +711,22 @@ def test_get_role_assignments_in_scope(self, role_name, scope_name, expected_cou ("carol", None, "lib:Org1:science_301", 1), ("george", None, "lib:Org6:project_zeta", 2), # Test with subject and role - ("daniel", roles.COURSE_STAFF.external_key, None, 1), - ("liam", roles.LIBRARY_AUTHOR.external_key, None, 3), - ("carlos", roles.COURSE_STAFF.external_key, None, 3), + ("daniel", COURSE_STAFF.external_key, None, 1), + ("liam", LIBRARY_AUTHOR.external_key, None, 3), + ("carlos", COURSE_STAFF.external_key, None, 3), # Test with role and scope - (None, roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", 2), - (None, roles.LIBRARY_ADMIN.external_key, "lib:Org5:economics_101", 1), - (None, roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org5:economics_101", 1), - (None, roles.LIBRARY_USER.external_key, "lib:Org5:economics_101", 1), + (None, LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", 2), + (None, LIBRARY_ADMIN.external_key, "lib:Org5:economics_101", 1), + (None, LIBRARY_CONTRIBUTOR.external_key, "lib:Org5:economics_101", 1), + (None, LIBRARY_USER.external_key, "lib:Org5:economics_101", 1), # Test with subject, role, and scope - ("ivy", roles.LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", 1), - ("jack", roles.LIBRARY_AUTHOR.external_key, "lib:Org3:cs_101", 1), - ("kate", roles.LIBRARY_USER.external_key, "lib:Org3:cs_101", 1), - ("peter", roles.LIBRARY_ADMIN.external_key, "lib:Org6:project_alpha", 1), - ("peter", roles.LIBRARY_AUTHOR.external_key, "lib:Org6:project_beta", 1), - ("peter", roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org6:project_gamma", 1), - ("peter", roles.LIBRARY_USER.external_key, "lib:Org6:project_delta", 1), + ("ivy", LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", 1), + ("jack", LIBRARY_AUTHOR.external_key, "lib:Org3:cs_101", 1), + ("kate", LIBRARY_USER.external_key, "lib:Org3:cs_101", 1), + ("peter", LIBRARY_ADMIN.external_key, "lib:Org6:project_alpha", 1), + ("peter", LIBRARY_AUTHOR.external_key, "lib:Org6:project_beta", 1), + ("peter", LIBRARY_CONTRIBUTOR.external_key, "lib:Org6:project_gamma", 1), + ("peter", LIBRARY_USER.external_key, "lib:Org6:project_delta", 1), # Edge cases - non-existent entities ("non_existent_user", None, "lib:Org1:math_101", 0), ("alice", None, "lib:Org999:non_existent_scope", 0), @@ -771,73 +786,73 @@ def test_get_role_assignments_with_filters(self, subject_name, role_name, scope_ # Test case: alice with 'view_library' permission (has library_admin in math_101) ( "alice", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, ["lib:Org1:math_101"], ), # Test case: alice with 'publish_library_content' permission (admin grants publish) ( "alice", - permissions.PUBLISH_LIBRARY_CONTENT.identifier, + PUBLISH_LIBRARY_CONTENT.identifier, ["lib:Org1:math_101"], ), # Test case: alice with 'delete_library' permission (admin grants delete) ( "alice", - permissions.DELETE_LIBRARY.identifier, + DELETE_LIBRARY.identifier, ["lib:Org1:math_101"], ), # Test case: bob with 'view_library' permission (has library_author in history_201) ( "bob", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, ["lib:Org1:history_201"], ), # Test case: bob with 'publish_library_content' permission (author grants publish) ( "bob", - permissions.PUBLISH_LIBRARY_CONTENT.identifier, + PUBLISH_LIBRARY_CONTENT.identifier, ["lib:Org1:history_201"], ), # Test case: bob with 'delete_library' permission (author does NOT grant delete) ( "bob", - permissions.DELETE_LIBRARY.identifier, + DELETE_LIBRARY.identifier, [], ), # Test case: carol with 'view_library' permission (has library_contributor in science_301) ( "carol", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, ["lib:Org1:science_301"], ), # Test case: carol with 'publish_library_content' permission (contributor does NOT grant publish) ( "carol", - permissions.PUBLISH_LIBRARY_CONTENT.identifier, + PUBLISH_LIBRARY_CONTENT.identifier, [], ), # Test case: dave with 'view_library' permission (has library_user in english_101) ( "dave", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, ["lib:Org1:english_101"], ), # Test case: dave with 'publish_library_content' permission (user does NOT grant publish) ( "dave", - permissions.PUBLISH_LIBRARY_CONTENT.identifier, + PUBLISH_LIBRARY_CONTENT.identifier, [], ), # Test case: liam with 'view_library' permission (has library_author in 3 art libraries) ( "liam", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, ["lib:Org4:art_101", "lib:Org4:art_201", "lib:Org4:art_301"], ), # Test case: non-existent user ( "nonexistent", - permissions.VIEW_LIBRARY.identifier, + VIEW_LIBRARY.identifier, [], ), ) @@ -885,18 +900,18 @@ def test_get_scopes_for_subject_and_permission_no_duplicates(self): assign_role_to_subject_in_scope( SubjectData(external_key=test_subject), - RoleData(external_key=roles.LIBRARY_ADMIN.external_key), + RoleData(external_key=LIBRARY_ADMIN.external_key), ScopeData(external_key=test_scope), ) assign_role_to_subject_in_scope( SubjectData(external_key=test_subject), - RoleData(external_key=roles.LIBRARY_AUTHOR.external_key), + RoleData(external_key=LIBRARY_AUTHOR.external_key), ScopeData(external_key=test_scope), ) subject = SubjectData(external_key=test_subject) - permission = PermissionData(action=ActionData(external_key=permissions.VIEW_LIBRARY.identifier)) + permission = PermissionData(action=ActionData(external_key=VIEW_LIBRARY.identifier)) scopes = get_scopes_for_subject_and_permission(subject, permission) scope_external_keys = [scope.external_key for scope in scopes] @@ -906,11 +921,11 @@ def test_get_scopes_for_subject_and_permission_no_duplicates(self): self.assertEqual(len(scope_external_keys), len(set(scope_external_keys))) @ddt_data( - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", {"liam"}), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_201", {"liam"}), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_301", {"liam"}), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", {"liam"}), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_201", {"liam"}), + (LIBRARY_AUTHOR.external_key, "lib:Org4:art_301", {"liam"}), ("non_existent_role", "lib:Org4:art_101", set()), - (roles.LIBRARY_AUTHOR.external_key, "global:non_existent_scope", set()), + (LIBRARY_AUTHOR.external_key, "global:non_existent_scope", set()), ("non_existent_role", "global:non_existent_scope", set()), ) @unpack @@ -939,24 +954,24 @@ class TestRoleAssignmentAPI(RolesTestSetupMixin): """ @ddt_data( - (["mary", "john"], roles.LIBRARY_USER.external_key, "global:batch_test", True), + (["mary", "john"], LIBRARY_USER.external_key, "global:batch_test", True), ( ["paul", "diana", "lila"], - roles.LIBRARY_CONTRIBUTOR.external_key, + LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", True, ), - (["sarina", "ty"], roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), - (["fran", "bob"], roles.LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", True), + (["sarina", "ty"], LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), + (["fran", "bob"], LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", True), ( ["anna", "tom", "jerry"], - roles.LIBRARY_USER.external_key, + LIBRARY_USER.external_key, "lib:Org1:history_201", True, ), - ("joe", roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", False), - ("nina", roles.LIBRARY_AUTHOR.external_key, "lib:Org1:english_101", False), - ("oliver", roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), + ("joe", LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", False), + ("nina", LIBRARY_AUTHOR.external_key, "lib:Org1:english_101", False), + ("oliver", LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), ) @unpack def test_batch_assign_role_to_subjects_in_scope(self, subject_names, role, scope_name, batch): @@ -997,24 +1012,24 @@ def test_batch_assign_role_to_subjects_in_scope(self, subject_names, role, scope self.assertIn(role, role_names) @ddt_data( - (["mary", "john"], roles.LIBRARY_USER.external_key, "global:batch_test", True), + (["mary", "john"], LIBRARY_USER.external_key, "global:batch_test", True), ( ["paul", "diana", "lila"], - roles.LIBRARY_CONTRIBUTOR.external_key, + LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", True, ), - (["sarina", "ty"], roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), - (["fran", "bob"], roles.LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", True), + (["sarina", "ty"], LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), + (["fran", "bob"], LIBRARY_ADMIN.external_key, "lib:Org3:cs_101", True), ( ["anna", "tom", "jerry"], - roles.LIBRARY_USER.external_key, + LIBRARY_USER.external_key, "lib:Org1:history_201", True, ), - ("joe", roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", False), - ("nina", roles.LIBRARY_AUTHOR.external_key, "lib:Org1:english_101", False), - ("oliver", roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), + ("joe", LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", False), + ("nina", LIBRARY_AUTHOR.external_key, "lib:Org1:english_101", False), + ("oliver", LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), ) @unpack def test_unassign_role_from_subject_in_scope(self, subject_names, role, scope_name, batch): @@ -1057,7 +1072,7 @@ def test_unassign_role_from_subject_in_scope(self, subject_names, role, scope_na [ RoleAssignmentData( subject=SubjectData(external_key="alice"), - roles=[RoleData(external_key=roles.LIBRARY_ADMIN.external_key)], + roles=[RoleData(external_key=LIBRARY_ADMIN.external_key)], scope=ScopeData(external_key="lib:Org1:math_101"), ) ], @@ -1067,7 +1082,7 @@ def test_unassign_role_from_subject_in_scope(self, subject_names, role, scope_na [ RoleAssignmentData( subject=SubjectData(external_key="bob"), - roles=[RoleData(external_key=roles.LIBRARY_AUTHOR.external_key)], + roles=[RoleData(external_key=LIBRARY_AUTHOR.external_key)], scope=ScopeData(external_key="lib:Org1:history_201"), ) ], @@ -1077,7 +1092,7 @@ def test_unassign_role_from_subject_in_scope(self, subject_names, role, scope_na [ RoleAssignmentData( subject=SubjectData(external_key="carol"), - roles=[RoleData(external_key=roles.LIBRARY_CONTRIBUTOR.external_key)], + roles=[RoleData(external_key=LIBRARY_CONTRIBUTOR.external_key)], scope=ScopeData(external_key="lib:Org1:science_301"), ) ], @@ -1087,7 +1102,7 @@ def test_unassign_role_from_subject_in_scope(self, subject_names, role, scope_na [ RoleAssignmentData( subject=SubjectData(external_key="dave"), - roles=[RoleData(external_key=roles.LIBRARY_USER.external_key)], + roles=[RoleData(external_key=LIBRARY_USER.external_key)], scope=ScopeData(external_key="lib:Org1:english_101"), ) ], @@ -1133,15 +1148,15 @@ def test_get_all_subject_role_assignments(self): (a.subject.external_key, a.scope.external_key, a.roles[0].external_key) for a in role_assignments } self.assertIn( - ("alice", "lib:Org1:math_101", roles.LIBRARY_ADMIN.external_key), + ("alice", "lib:Org1:math_101", LIBRARY_ADMIN.external_key), subject_scope_role_triples, ) self.assertIn( - ("eve", "lib:Org2:physics_401", roles.LIBRARY_ADMIN.external_key), + ("eve", "lib:Org2:physics_401", LIBRARY_ADMIN.external_key), subject_scope_role_triples, ) self.assertIn( - ("liam", "lib:Org4:art_101", roles.LIBRARY_AUTHOR.external_key), + ("liam", "lib:Org4:art_101", LIBRARY_AUTHOR.external_key), subject_scope_role_triples, ) @@ -1155,7 +1170,7 @@ def test_assign_role_creates_extended_casbin_rule(self): """ initial_count = ExtendedCasbinRule.objects.count() subject_data = SubjectData(external_key="test_user_extended") - role_data = RoleData(external_key=roles.LIBRARY_USER.external_key) + role_data = RoleData(external_key=LIBRARY_USER.external_key) scope_data = ScopeData(external_key="lib:TestOrg:TestLib") assign_role_to_subject_in_scope(subject_data, role_data, scope_data) @@ -1332,7 +1347,7 @@ def test_no_event_sent_when_role_already_assigned(self): - ``transaction.on_commit`` is not called. """ subject = SubjectData(external_key="alice") - role = RoleData(external_key=roles.LIBRARY_ADMIN.external_key) + role = RoleData(external_key=LIBRARY_ADMIN.external_key) scope = ScopeData(external_key="lib:Org1:math_101") with patch("openedx_authz.api.roles.transaction.on_commit") as mock_on_commit: @@ -1349,7 +1364,7 @@ def test_no_event_sent_when_role_not_present_for_removal(self): - ``transaction.on_commit`` is not called. """ subject = SubjectData(external_key="alice") - role = RoleData(external_key=roles.LIBRARY_USER.external_key) + role = RoleData(external_key=LIBRARY_USER.external_key) scope = ScopeData(external_key="lib:Org1:math_101") with patch("openedx_authz.api.roles.transaction.on_commit") as mock_on_commit: diff --git a/src/openedx_authz/tests/api/test_users.py b/src/openedx_authz/tests/api/test_users.py index 834033c9..b94e662c 100644 --- a/src/openedx_authz/tests/api/test_users.py +++ b/src/openedx_authz/tests/api/test_users.py @@ -37,7 +37,26 @@ unassign_role_from_user, validate_users, ) -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSE_STAFF, + COURSES_EDIT_COURSE_CONTENT, + COURSES_MANAGE_ADVANCED_SETTINGS, + COURSES_MANAGE_CERTIFICATES, + COURSES_MANAGE_COURSE_TEAM, + COURSES_MANAGE_GROUP_CONFIGURATIONS, + COURSES_MANAGE_LIBRARY_UPDATES, + COURSES_PUBLISH_COURSE_CONTENT, + COURSES_VIEW_COURSE, + CREATE_LIBRARY_COLLECTION, + DELETE_LIBRARY, + EDIT_LIBRARY_CONTENT, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_CONTRIBUTOR, + LIBRARY_USER, + MANAGE_LIBRARY_TEAM, + PUBLISH_LIBRARY_CONTENT, +) from openedx_authz.tests.api.test_roles import RolesTestSetupMixin @@ -76,22 +95,22 @@ class TestUserRoleAssignmentsPerScopeType(UserAssignmentsSetupMixin): GLOB_SCOPE_TEST_ASSIGNMENTS = [ { "subject_name": "nina", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": OrgContentLibraryGlobData.build_external_key("GlobTest"), }, { "subject_name": "nina", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": OrgCourseOverviewGlobData.build_external_key("GlobTest"), }, { "subject_name": "nina", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": PlatformContentLibraryGlobData.build_external_key(), }, { "subject_name": "nina", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": PlatformCourseOverviewGlobData.build_external_key(), }, ] @@ -144,10 +163,10 @@ class TestUserRoleAssignments(UserAssignmentsSetupMixin): """Test suite for user-role assignment API functions.""" @data( - ("john", roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), - ("jane", roles.LIBRARY_USER.external_key, "lib:Org1:english_101", False), - (["mary", "charlie"], roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", True), - (["david", "sarah"], roles.LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", True), + ("john", LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), + ("jane", LIBRARY_USER.external_key, "lib:Org1:english_101", False), + (["mary", "charlie"], LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:science_301", True), + (["david", "sarah"], LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", True), ) @unpack def test_assign_role_to_user_in_scope(self, username, role, scope_name, batch): @@ -173,10 +192,10 @@ def test_assign_role_to_user_in_scope(self, username, role, scope_name, batch): self.assertIn(role, role_names) @data( - (["grace"], roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", True), - (["liam", "maya"], roles.LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), - ("alice", roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), - ("bob", roles.LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", False), + (["grace"], LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", True), + (["liam", "maya"], LIBRARY_AUTHOR.external_key, "lib:Org4:art_101", True), + ("alice", LIBRARY_ADMIN.external_key, "lib:Org1:math_101", False), + ("bob", LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", False), ) @unpack def test_unassign_role_from_user(self, username, role, scope_name, batch): @@ -203,9 +222,9 @@ def test_unassign_role_from_user(self, username, role, scope_name, batch): self.assertNotIn(role, role_names) @data( - ("eve", {roles.LIBRARY_ADMIN.external_key, roles.LIBRARY_AUTHOR.external_key, roles.LIBRARY_USER.external_key}), - ("alice", {roles.LIBRARY_ADMIN.external_key}), - ("liam", {roles.LIBRARY_AUTHOR.external_key}), + ("eve", {LIBRARY_ADMIN.external_key, LIBRARY_AUTHOR.external_key, LIBRARY_USER.external_key}), + ("alice", {LIBRARY_ADMIN.external_key}), + ("liam", {LIBRARY_AUTHOR.external_key}), ) @unpack def test_get_user_role_assignments(self, username, expected_roles): @@ -221,10 +240,10 @@ def test_get_user_role_assignments(self, username, expected_roles): self.assertEqual(assigned_role_names, expected_roles) @data( - ("alice", "lib:Org1:math_101", {roles.LIBRARY_ADMIN.external_key}), - ("bob", "lib:Org1:history_201", {roles.LIBRARY_AUTHOR.external_key}), - ("eve", "lib:Org2:physics_401", {roles.LIBRARY_ADMIN.external_key}), - ("grace", "lib:Org1:math_advanced", {roles.LIBRARY_CONTRIBUTOR.external_key}), + ("alice", "lib:Org1:math_101", {LIBRARY_ADMIN.external_key}), + ("bob", "lib:Org1:history_201", {LIBRARY_AUTHOR.external_key}), + ("eve", "lib:Org2:physics_401", {LIBRARY_ADMIN.external_key}), + ("grace", "lib:Org1:math_advanced", {LIBRARY_CONTRIBUTOR.external_key}), ) @unpack def test_get_user_role_assignments_in_scope(self, username, scope_name, expected_roles): @@ -240,9 +259,9 @@ def test_get_user_role_assignments_in_scope(self, username, scope_name, expected self.assertEqual(role_names, expected_roles) @data( - (roles.LIBRARY_ADMIN.external_key, "lib:Org1:math_101", {"alice"}), - (roles.LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", {"bob"}), - (roles.LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", {"grace", "heidi"}), + (LIBRARY_ADMIN.external_key, "lib:Org1:math_101", {"alice"}), + (LIBRARY_AUTHOR.external_key, "lib:Org1:history_201", {"bob"}), + (LIBRARY_CONTRIBUTOR.external_key, "lib:Org1:math_advanced", {"grace", "heidi"}), ) @unpack def test_get_user_role_assignments_for_role_in_scope(self, role_name, scope_name, expected_users): @@ -266,7 +285,7 @@ def test_get_user_role_assignments_for_role_in_scope(self, role_name, scope_name [ RoleAssignmentData( subject=UserData(external_key="alice"), - roles=[RoleData(external_key=roles.LIBRARY_ADMIN.external_key)], + roles=[RoleData(external_key=LIBRARY_ADMIN.external_key)], scope=ContentLibraryData(external_key="lib:Org1:math_101"), ), ], @@ -276,7 +295,7 @@ def test_get_user_role_assignments_for_role_in_scope(self, role_name, scope_name [ RoleAssignmentData( subject=UserData(external_key="bob"), - roles=[RoleData(external_key=roles.LIBRARY_AUTHOR.external_key)], + roles=[RoleData(external_key=LIBRARY_AUTHOR.external_key)], scope=ContentLibraryData(external_key="lib:Org1:history_201"), ), ], @@ -286,7 +305,7 @@ def test_get_user_role_assignments_for_role_in_scope(self, role_name, scope_name [ RoleAssignmentData( subject=UserData(external_key="eve"), - roles=[RoleData(external_key=roles.LIBRARY_ADMIN.external_key)], + roles=[RoleData(external_key=LIBRARY_ADMIN.external_key)], scope=ContentLibraryData(external_key="lib:Org2:physics_401"), ), ], @@ -478,7 +497,7 @@ def test_unassign_all_roles_impacts_permissions(self): """ username = "alice" scope = "lib:Org1:math_101" - action = permissions.DELETE_LIBRARY.identifier + action = DELETE_LIBRARY.identifier # Verify alice has the permission before unassignment has_permission_before = is_user_allowed( @@ -509,110 +528,110 @@ class TestUserPermissions(UserAssignmentsSetupMixin): # Course permissions ( "daniel", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", True, ), ( "daniel", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T2", False, ), ( "judy", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), - ("judy", permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T2", True), + ("judy", COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T2", True), # Multiple subjects with same role in same scope ( "maria", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T3", True, ), - ("aida", permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T3", True), + ("aida", COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T3", True), ( "maria", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "aida", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), # Same user, same role, different scopes ( "carlos", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", True, ), ( "carlos", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T2", True, ), ( "carlos", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T3", True, ), # View/manage permission split: editor and auditor should NOT get manage permissions ( "editor_user", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "auditor_user", - permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, + COURSES_MANAGE_ADVANCED_SETTINGS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "editor_user", - permissions.COURSES_MANAGE_CERTIFICATES.identifier, + COURSES_MANAGE_CERTIFICATES.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "auditor_user", - permissions.COURSES_MANAGE_CERTIFICATES.identifier, + COURSES_MANAGE_CERTIFICATES.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "auditor_user", - permissions.COURSES_MANAGE_GROUP_CONFIGURATIONS.identifier, + COURSES_MANAGE_GROUP_CONFIGURATIONS.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), ( "auditor_user", - permissions.COURSES_MANAGE_LIBRARY_UPDATES.identifier, + COURSES_MANAGE_LIBRARY_UPDATES.identifier, "course-v1:TestOrg+TestCourse+2024_T1", False, ), # Library permissions - ("alice", permissions.DELETE_LIBRARY.identifier, "lib:Org1:math_101", True), - ("bob", permissions.PUBLISH_LIBRARY_CONTENT.identifier, "lib:Org1:history_201", True), - ("eve", permissions.MANAGE_LIBRARY_TEAM.identifier, "lib:Org2:physics_401", True), - ("grace", permissions.EDIT_LIBRARY_CONTENT.identifier, "lib:Org1:math_advanced", True), - ("heidi", permissions.CREATE_LIBRARY_COLLECTION.identifier, "lib:Org1:math_advanced", True), - ("charlie", permissions.DELETE_LIBRARY.identifier, "lib:Org1:science_301", False), - ("david", permissions.PUBLISH_LIBRARY_CONTENT.identifier, "lib:Org1:history_201", False), - ("mallory", permissions.MANAGE_LIBRARY_TEAM.identifier, "lib:Org1:math_101", False), - ("oscar", permissions.EDIT_LIBRARY_CONTENT.identifier, "lib:Org4:art_101", False), - ("peggy", permissions.CREATE_LIBRARY_COLLECTION.identifier, "lib:Org2:physics_401", False), + ("alice", DELETE_LIBRARY.identifier, "lib:Org1:math_101", True), + ("bob", PUBLISH_LIBRARY_CONTENT.identifier, "lib:Org1:history_201", True), + ("eve", MANAGE_LIBRARY_TEAM.identifier, "lib:Org2:physics_401", True), + ("grace", EDIT_LIBRARY_CONTENT.identifier, "lib:Org1:math_advanced", True), + ("heidi", CREATE_LIBRARY_COLLECTION.identifier, "lib:Org1:math_advanced", True), + ("charlie", DELETE_LIBRARY.identifier, "lib:Org1:science_301", False), + ("david", PUBLISH_LIBRARY_CONTENT.identifier, "lib:Org1:history_201", False), + ("mallory", MANAGE_LIBRARY_TEAM.identifier, "lib:Org1:math_101", False), + ("oscar", EDIT_LIBRARY_CONTENT.identifier, "lib:Org4:art_101", False), + ("peggy", CREATE_LIBRARY_COLLECTION.identifier, "lib:Org2:physics_401", False), ) @unpack def test_is_user_allowed(self, username, action, scope_name, expected_result): @@ -630,26 +649,26 @@ def test_is_user_allowed(self, username, action, scope_name, expected_result): @data( # alice is library_admin on lib:Org1:math_101, so she holds these in at least one scope. - ("alice", permissions.DELETE_LIBRARY.identifier, True), - ("alice", permissions.MANAGE_LIBRARY_TEAM.identifier, True), + ("alice", DELETE_LIBRARY.identifier, True), + ("alice", MANAGE_LIBRARY_TEAM.identifier, True), # jane is library_user on lib:Org1:english_101, which never grants these permissions. - ("jane", permissions.DELETE_LIBRARY.identifier, False), - ("jane", permissions.MANAGE_LIBRARY_TEAM.identifier, False), + ("jane", DELETE_LIBRARY.identifier, False), + ("jane", MANAGE_LIBRARY_TEAM.identifier, False), # daniel is course_staff on course-v1:TestOrg+TestCourse+2024_T1, so he holds the # staff course permissions in at least one scope. - ("daniel", permissions.COURSES_VIEW_COURSE.identifier, True), - ("daniel", permissions.COURSES_EDIT_COURSE_CONTENT.identifier, True), - ("daniel", permissions.COURSES_PUBLISH_COURSE_CONTENT.identifier, True), + ("daniel", COURSES_VIEW_COURSE.identifier, True), + ("daniel", COURSES_EDIT_COURSE_CONTENT.identifier, True), + ("daniel", COURSES_PUBLISH_COURSE_CONTENT.identifier, True), # course_staff never grants team management, so daniel holds it in no scope. - ("daniel", permissions.COURSES_MANAGE_COURSE_TEAM.identifier, False), + ("daniel", COURSES_MANAGE_COURSE_TEAM.identifier, False), # carlos is course_staff on three different courses; he still holds these in some scope. - ("carlos", permissions.COURSES_VIEW_COURSE.identifier, True), - ("carlos", permissions.COURSES_MANAGE_ADVANCED_SETTINGS.identifier, True), + ("carlos", COURSES_VIEW_COURSE.identifier, True), + ("carlos", COURSES_MANAGE_ADVANCED_SETTINGS.identifier, True), # jane only holds a library role, so she has no course permission in any scope. - ("jane", permissions.COURSES_VIEW_COURSE.identifier, False), + ("jane", COURSES_VIEW_COURSE.identifier, False), # A user without any assignment is not allowed in any scope. - ("nonexistent_user", permissions.MANAGE_LIBRARY_TEAM.identifier, False), - ("nonexistent_user", permissions.COURSES_VIEW_COURSE.identifier, False), + ("nonexistent_user", MANAGE_LIBRARY_TEAM.identifier, False), + ("nonexistent_user", COURSES_VIEW_COURSE.identifier, False), ) @unpack def test_is_user_allowed_in_any_scope(self, username, action, expected_result): @@ -686,7 +705,7 @@ def test_is_user_allowed_in_any_scope_staff_always_allowed(self, username, flags result = is_user_allowed_in_any_scope( user_external_key=username, - action_external_key=permissions.MANAGE_LIBRARY_TEAM.identifier, + action_external_key=MANAGE_LIBRARY_TEAM.identifier, ) self.assertTrue(result) @@ -796,12 +815,12 @@ def test_skips_assignments_with_unsupported_scope(self): supported_scope = ContentLibraryData(external_key="lib:Org1:math_101") unsupported_assignment = RoleAssignmentData( subject=UserData(external_key="john_doe"), - roles=[RoleData(external_key=roles.LIBRARY_ADMIN.external_key)], + roles=[RoleData(external_key=LIBRARY_ADMIN.external_key)], scope=unsupported_scope, ) supported_assignment = RoleAssignmentData( subject=UserData(external_key="alice"), - roles=[RoleData(external_key=roles.LIBRARY_ADMIN.external_key)], + roles=[RoleData(external_key=LIBRARY_ADMIN.external_key)], scope=supported_scope, ) diff --git a/src/openedx_authz/tests/applets/test_builtin_definitions.py b/src/openedx_authz/tests/applets/test_builtin_definitions.py index c4040f3d..a340b82c 100644 --- a/src/openedx_authz/tests/applets/test_builtin_definitions.py +++ b/src/openedx_authz/tests/applets/test_builtin_definitions.py @@ -4,7 +4,20 @@ import yaml -from openedx_authz.constants import permissions, roles +from openedx_authz import authz +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_AUDITOR, + COURSE_BETA_TESTER, + COURSE_DATA_RESEARCHER, + COURSE_EDITOR, + COURSE_LIMITED_STAFF, + COURSE_STAFF, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_CONTRIBUTOR, + LIBRARY_USER, +) from openedx_authz.data import PermissionData from openedx_authz.engine.schema.pipeline import SchemaPipeline from openedx_authz.engine.schema.snapshot import snapshot_data @@ -28,17 +41,17 @@ def test_compiled_schema_matches_expected_definitions(self): def test_compiled_roles_match_public_role_constants(self): """Every compiled role has a public constant, and every constant names a compiled role.""" public_roles = ( - roles.LIBRARY_ADMIN, - roles.LIBRARY_AUTHOR, - roles.LIBRARY_CONTRIBUTOR, - roles.LIBRARY_USER, - roles.COURSE_ADMIN, - roles.COURSE_STAFF, - roles.COURSE_EDITOR, - roles.COURSE_AUDITOR, - roles.COURSE_LIMITED_STAFF, - roles.COURSE_DATA_RESEARCHER, - roles.COURSE_BETA_TESTER, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_CONTRIBUTOR, + LIBRARY_USER, + COURSE_ADMIN, + COURSE_STAFF, + COURSE_EDITOR, + COURSE_AUDITOR, + COURSE_LIMITED_STAFF, + COURSE_DATA_RESEARCHER, + COURSE_BETA_TESTER, ) compiled = SchemaPipeline().compile() @@ -55,7 +68,7 @@ def test_course_editor_and_auditor_remain_visible(self): def test_compiled_permissions_match_public_permission_constants(self): """Every compiled permission has a public constant, and every constant names a compiled permission.""" public_permissions = { - constant.identifier for constant in vars(permissions).values() if isinstance(constant, PermissionData) + constant.identifier for constant in vars(authz).values() if isinstance(constant, PermissionData) } compiled = SchemaPipeline().compile() diff --git a/src/openedx_authz/tests/applets/test_instructor_dashboard_permissions.py b/src/openedx_authz/tests/applets/test_instructor_dashboard_permissions.py index 369c9249..a7c8cec6 100644 --- a/src/openedx_authz/tests/applets/test_instructor_dashboard_permissions.py +++ b/src/openedx_authz/tests/applets/test_instructor_dashboard_permissions.py @@ -5,7 +5,13 @@ from ddt import data, ddt, unpack from openedx_authz.api.users import assign_role_to_user_in_scope, is_user_allowed -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_STAFF, + COURSES_MANAGE_COURSE_TEAM, + COURSES_VIEW_COURSE_TEAM, + COURSES_VIEW_GRADING_SETTINGS, +) from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.engine.renderer import PolicyRenderer from openedx_authz.engine.schema.pipeline import SchemaPipeline @@ -39,8 +45,8 @@ def _seed_database_with_policies(cls): def setUpClass(cls): """Assign the course-team roles used by every test in the class.""" super().setUpClass() - assign_role_to_user_in_scope("dashboard_admin", roles.COURSE_ADMIN.external_key, COURSE_KEY) - assign_role_to_user_in_scope("dashboard_staff", roles.COURSE_STAFF.external_key, COURSE_KEY) + assign_role_to_user_in_scope("dashboard_admin", COURSE_ADMIN.external_key, COURSE_KEY) + assign_role_to_user_in_scope("dashboard_staff", COURSE_STAFF.external_key, COURSE_KEY) @data( *( @@ -74,12 +80,12 @@ def test_new_permissions_are_limited_to_the_assigned_course(self, username, perm self.assertFalse(result) @data( - ("dashboard_admin", permissions.COURSES_VIEW_COURSE_TEAM, True), - ("dashboard_staff", permissions.COURSES_VIEW_COURSE_TEAM, True), - ("dashboard_admin", permissions.COURSES_MANAGE_COURSE_TEAM, True), - ("dashboard_staff", permissions.COURSES_MANAGE_COURSE_TEAM, False), - ("dashboard_admin", permissions.COURSES_VIEW_GRADING_SETTINGS, True), - ("dashboard_staff", permissions.COURSES_VIEW_GRADING_SETTINGS, True), + ("dashboard_admin", COURSES_VIEW_COURSE_TEAM, True), + ("dashboard_staff", COURSES_VIEW_COURSE_TEAM, True), + ("dashboard_admin", COURSES_MANAGE_COURSE_TEAM, True), + ("dashboard_staff", COURSES_MANAGE_COURSE_TEAM, False), + ("dashboard_admin", COURSES_VIEW_GRADING_SETTINGS, True), + ("dashboard_staff", COURSES_VIEW_GRADING_SETTINGS, True), ) @unpack def test_reused_permissions_keep_their_current_assignments(self, username, permission, expected_result): diff --git a/src/openedx_authz/tests/rest_api/admin_console/test_views.py b/src/openedx_authz/tests/rest_api/admin_console/test_views.py index 4de5a468..835fedf7 100644 --- a/src/openedx_authz/tests/rest_api/admin_console/test_views.py +++ b/src/openedx_authz/tests/rest_api/admin_console/test_views.py @@ -22,7 +22,18 @@ PlatformCourseOverviewGlobData, ) from openedx_authz.api.users import assign_role_to_user_in_scope -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_AUDITOR, + COURSE_STAFF, + COURSES_MANAGE_COURSE_TEAM, + COURSES_VIEW_COURSE_TEAM, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_USER, + MANAGE_LIBRARY_TEAM, + VIEW_LIBRARY_TEAM, +) from openedx_authz.models.scopes import get_content_library_model, get_course_overview_model from openedx_authz.rest_api.v1.admin_console.views import ScopesAPIView from openedx_authz.tests.rest_api.mixins import ViewTestMixin @@ -68,13 +79,13 @@ def setUpClass(cls): # regular_9: can view course team on Org1 course { "subject_name": "regular_9", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": cls.COURSE_ORG1, }, # regular_10: can manage course team on Org2 course { "subject_name": "regular_10", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": cls.COURSE_ORG2, }, ] @@ -543,8 +554,8 @@ def test_manage_permission_only_uses_manage_permission(self): self.client.get(self.url, {"management_permission_only": "true"}) called_permissions = [call.args[1] for call in mock_get_scopes.call_args_list] - self.assertIn(permissions.MANAGE_LIBRARY_TEAM.identifier, called_permissions) - self.assertIn(permissions.COURSES_MANAGE_COURSE_TEAM.identifier, called_permissions) + self.assertIn(MANAGE_LIBRARY_TEAM.identifier, called_permissions) + self.assertIn(COURSES_MANAGE_COURSE_TEAM.identifier, called_permissions) def test_view_permission_only_uses_view_permission(self): """management_permission_only=false (default) calls get_admin_view_permission.""" @@ -558,8 +569,8 @@ def test_view_permission_only_uses_view_permission(self): self.client.get(self.url) called_permissions = [call.args[1] for call in mock_get_scopes.call_args_list] - self.assertIn(permissions.VIEW_LIBRARY_TEAM.identifier, called_permissions) - self.assertIn(permissions.COURSES_VIEW_COURSE_TEAM.identifier, called_permissions) + self.assertIn(VIEW_LIBRARY_TEAM.identifier, called_permissions) + self.assertIn(COURSES_VIEW_COURSE_TEAM.identifier, called_permissions) # ------------------------------------------------------------------ # # Org filter # @@ -897,7 +908,7 @@ def setUpClass(cls): [ { "subject_name": "regular_9", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_SCOPE_ORG1, }, ] @@ -1055,7 +1066,7 @@ def test_get_orgs_user_with_both_permissions_allowed(self): [ { "subject_name": "regular_1", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_SCOPE_ORG1, }, ] @@ -1100,32 +1111,32 @@ def test_django_staff_sees_all_orgs_regardless_of_assignments(self): @data( # Specific (non-glob) scopes - ([("lib:AlphaU:LIB_ADMIN_TEST", roles.LIBRARY_USER.external_key)], {"AlphaU"}), - ([("course-v1:BetaI+COURSE1+2024", roles.COURSE_STAFF.external_key)], {"BetaI"}), + ([("lib:AlphaU:LIB_ADMIN_TEST", LIBRARY_USER.external_key)], {"AlphaU"}), + ([("course-v1:BetaI+COURSE1+2024", COURSE_STAFF.external_key)], {"BetaI"}), # Org-level glob scopes - ([(OrgContentLibraryGlobData.build_external_key("AlphaU"), roles.LIBRARY_ADMIN.external_key)], {"AlphaU"}), - ([(OrgCourseOverviewGlobData.build_external_key("BetaI"), roles.COURSE_STAFF.external_key)], {"BetaI"}), + ([(OrgContentLibraryGlobData.build_external_key("AlphaU"), LIBRARY_ADMIN.external_key)], {"AlphaU"}), + ([(OrgCourseOverviewGlobData.build_external_key("BetaI"), COURSE_STAFF.external_key)], {"BetaI"}), # Multiple assignments across different orgs and scope types ( [ - ("course-v1:AlphaU+COURSE1+2024", roles.COURSE_STAFF.external_key), - (OrgContentLibraryGlobData.build_external_key("BetaI"), roles.LIBRARY_ADMIN.external_key), + ("course-v1:AlphaU+COURSE1+2024", COURSE_STAFF.external_key), + (OrgContentLibraryGlobData.build_external_key("BetaI"), LIBRARY_ADMIN.external_key), ], {"AlphaU", "BetaI"}, ), # Two specific course scopes, different orgs ( [ - ("course-v1:AlphaU+COURSE1+2024", roles.COURSE_STAFF.external_key), - ("course-v1:BetaI+COURSE2+2024", roles.COURSE_STAFF.external_key), + ("course-v1:AlphaU+COURSE1+2024", COURSE_STAFF.external_key), + ("course-v1:BetaI+COURSE2+2024", COURSE_STAFF.external_key), ], {"AlphaU", "BetaI"}, ), # Two org-level glob scopes, different orgs ( [ - (OrgContentLibraryGlobData.build_external_key("AlphaU"), roles.LIBRARY_ADMIN.external_key), - (OrgCourseOverviewGlobData.build_external_key("BetaI"), roles.COURSE_STAFF.external_key), + (OrgContentLibraryGlobData.build_external_key("AlphaU"), LIBRARY_ADMIN.external_key), + (OrgCourseOverviewGlobData.build_external_key("BetaI"), COURSE_STAFF.external_key), ], {"AlphaU", "BetaI"}, ), @@ -1171,8 +1182,8 @@ def test_non_staff_sees_only_assigned_orgs(self, assignments: list[tuple[str, st self.assertEqual(result_short_names, expected_orgs) @data( - (PLATFORM_COURSE_GLOB, roles.COURSE_STAFF.external_key), - (PLATFORM_LIBRARY_GLOB, roles.LIBRARY_ADMIN.external_key), + (PLATFORM_COURSE_GLOB, COURSE_STAFF.external_key), + (PLATFORM_LIBRARY_GLOB, LIBRARY_ADMIN.external_key), ) @unpack def test_non_staff_with_platform_glob_sees_all_orgs(self, scope_name: str, role_name: str): @@ -1482,7 +1493,7 @@ def test_assignments_limit_defaults_to_three(self): # Add 4 more assignments so total >= 5, exceeding the default limit of 3. extra_scopes = ["lib:Org2:LIB2", "lib:Org3:LIB3", "lib:Org4:LIB4", "lib:Org5:LIB5"] for scope in extra_scopes: - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_USER.external_key, scope) + assign_role_to_user_in_scope("regular_1", LIBRARY_USER.external_key, scope) response = self.client.get(self.url, {"search": "regular_1"}) @@ -1503,7 +1514,7 @@ def test_assignments_limit_custom_value(self): # Assign enough to exceed the requested limit. extra_scopes = ["lib:Org2:LIB2", "lib:Org3:LIB3", "lib:Org4:LIB4"] for scope in extra_scopes: - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_USER.external_key, scope) + assign_role_to_user_in_scope("regular_1", LIBRARY_USER.external_key, scope) response = self.client.get(self.url, {"search": "regular_1", "assignments_limit": 2}) @@ -1528,7 +1539,7 @@ def test_assignments_limit_capped_at_ten(self): # Add 12 more assignments so total >= 12, which exceeds the hard cap of 10. extra_scopes = [f"lib:CapOrg{i}:CAPLIB{i}" for i in range(1, 12)] for scope in extra_scopes: - assign_role_to_user_in_scope("regular_2", roles.LIBRARY_USER.external_key, scope) + assign_role_to_user_in_scope("regular_2", LIBRARY_USER.external_key, scope) response = self.client.get(self.url, {"search": "regular_2", "assignments_limit": 50}) @@ -1588,7 +1599,7 @@ def test_scope_display_name_resolved_for_courses(self): CourseOverview.objects.get_or_create( id=course_scope, defaults={"org": "Org1", "display_name": "Introduction to Computer Science"} ) - assign_role_to_user_in_scope("regular_1", roles.COURSE_STAFF.external_key, course_scope) + assign_role_to_user_in_scope("regular_1", COURSE_STAFF.external_key, course_scope) response = self.client.get(self.url, {"search": "regular_1"}) @@ -1612,7 +1623,7 @@ def test_scope_display_name_empty_for_missing_resource(self): be resolved to a DB record. """ orphan_scope = "lib:OrgX:GONE_LIB" - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_USER.external_key, orphan_scope) + assign_role_to_user_in_scope("regular_1", LIBRARY_USER.external_key, orphan_scope) response = self.client.get(self.url, {"search": "regular_1"}) @@ -1633,7 +1644,7 @@ def test_scope_display_name_empty_for_glob_scopes(self): Expected result: - The glob assignment has scope_display_name == "". """ - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_ADMIN.external_key, "lib:Org1:*") + assign_role_to_user_in_scope("regular_1", LIBRARY_ADMIN.external_key, "lib:Org1:*") response = self.client.get(self.url, {"search": "regular_1"}) @@ -1681,7 +1692,7 @@ def test_scope_display_name_graceful_on_course_db_error(self, mock_course_overvi - scope_display_name is "" for the affected course scopes. """ course_scope = "course-v1:Org1+CS101+2024" - assign_role_to_user_in_scope("regular_1", roles.COURSE_STAFF.external_key, course_scope) + assign_role_to_user_in_scope("regular_1", COURSE_STAFF.external_key, course_scope) mock_course_overview.objects.filter.side_effect = Exception("DB error") response = self.client.get(self.url, {"search": "regular_1"}) @@ -1830,12 +1841,12 @@ def test_filter_by_multiple_orgs(self): # ------------------------------------------------------------------ # @data( - ("admin_1", roles.LIBRARY_ADMIN.external_key, 1), - ("admin_1", roles.LIBRARY_USER.external_key, 0), - ("regular_5", roles.LIBRARY_ADMIN.external_key, 1), - ("regular_5", roles.LIBRARY_USER.external_key, 0), - ("regular_6", roles.LIBRARY_AUTHOR.external_key, 1), - ("regular_6", roles.LIBRARY_ADMIN.external_key, 0), + ("admin_1", LIBRARY_ADMIN.external_key, 1), + ("admin_1", LIBRARY_USER.external_key, 0), + ("regular_5", LIBRARY_ADMIN.external_key, 1), + ("regular_5", LIBRARY_USER.external_key, 0), + ("regular_6", LIBRARY_AUTHOR.external_key, 1), + ("regular_6", LIBRARY_ADMIN.external_key, 0), ("admin_1", "non_existent_role", 0), ) @unpack @@ -1859,7 +1870,7 @@ def test_filter_by_multiple_roles(self): # admin_3 has library_admin in Org3:LIB3; filter for admin + author returns 1 role assignment response = self.client.get( self._url("admin_3"), - {"roles": f"{roles.LIBRARY_ADMIN.external_key},{roles.LIBRARY_AUTHOR.external_key}"}, + {"roles": f"{LIBRARY_ADMIN.external_key},{LIBRARY_AUTHOR.external_key}"}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -1889,7 +1900,7 @@ def test_sorting(self, sort_by: str, order: str): - Returns 200 OK. - Results are ordered according to the requested field and direction. """ - assign_role_to_user_in_scope("regular_8", roles.LIBRARY_ADMIN.external_key, "lib:Org1:LIB1") + assign_role_to_user_in_scope("regular_8", LIBRARY_ADMIN.external_key, "lib:Org1:LIB1") response = self.client.get(self._url("regular_8"), {"sort_by": sort_by, "order": order}) @@ -1936,7 +1947,7 @@ def test_pagination(self, query_params: dict, expected_page_count: int, has_next - Page contains the expected number of items. - `next` link is present only when more pages exist. """ - assign_role_to_user_in_scope("regular_8", roles.LIBRARY_ADMIN.external_key, "lib:Org1:LIB1") + assign_role_to_user_in_scope("regular_8", LIBRARY_ADMIN.external_key, "lib:Org1:LIB1") response = self.client.get(self._url("regular_8"), query_params) @@ -1954,8 +1965,8 @@ def test_platform_glob_assignment_serializes_wildcard_org(self): regular_9 is assigned course_admin on the same scope so they can view team assignments for that platform-level glob. """ - assign_role_to_user_in_scope("regular_10", roles.COURSE_STAFF.external_key, PLATFORM_COURSE_GLOB) - assign_role_to_user_in_scope("regular_9", roles.COURSE_ADMIN.external_key, PLATFORM_COURSE_GLOB) + assign_role_to_user_in_scope("regular_10", COURSE_STAFF.external_key, PLATFORM_COURSE_GLOB) + assign_role_to_user_in_scope("regular_9", COURSE_ADMIN.external_key, PLATFORM_COURSE_GLOB) self.client.force_authenticate(user=User.objects.get(username="regular_9")) response = self.client.get(self._url("regular_10")) @@ -1967,7 +1978,7 @@ def test_platform_glob_assignment_serializes_wildcard_org(self): self.assertEqual(assignment["org"], "*") self.assertEqual(assignment["scope_display_name"], "") self.assertEqual(assignment["scope"], PLATFORM_COURSE_GLOB) - self.assertEqual(assignment["role"], roles.COURSE_STAFF.external_key) + self.assertEqual(assignment["role"], COURSE_STAFF.external_key) # ------------------------------------------------------------------ # # Response shape # @@ -1994,7 +2005,7 @@ def test_response_shape(self): set(role_item.keys()), {"role", "org", "scope", "scope_display_name", "permission_count"}, ) - self.assertEqual(role_item["role"], roles.LIBRARY_ADMIN.external_key) + self.assertEqual(role_item["role"], LIBRARY_ADMIN.external_key) self.assertEqual(role_item["org"], "Org1") self.assertEqual(role_item["scope"], "lib:Org1:LIB1") self.assertGreater(role_item["permission_count"], 0) @@ -2025,7 +2036,7 @@ def test_scope_display_name_resolved_for_courses(self): CourseOverview.objects.get_or_create( id=course_scope, defaults={"org": "Org1", "display_name": "Introduction to Computer Science"} ) - assign_role_to_user_in_scope("regular_1", roles.COURSE_STAFF.external_key, course_scope) + assign_role_to_user_in_scope("regular_1", COURSE_STAFF.external_key, course_scope) response = self.client.get(self._url("regular_1")) @@ -2036,7 +2047,7 @@ def test_scope_display_name_resolved_for_courses(self): def test_scope_display_name_empty_for_missing_resource(self): """scope_display_name is empty when the backing library no longer exists.""" orphan_scope = "lib:OrgX:GONE_LIB" - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_USER.external_key, orphan_scope) + assign_role_to_user_in_scope("regular_1", LIBRARY_USER.external_key, orphan_scope) response = self.client.get(self._url("regular_1")) @@ -2047,7 +2058,7 @@ def test_scope_display_name_empty_for_missing_resource(self): @data("lib:Org1:*", "lib:*") def test_scope_display_name_empty_for_glob_scopes(self, glob_scope: str): """scope_display_name is empty for org-level and platform-level glob scopes.""" - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_ADMIN.external_key, glob_scope) + assign_role_to_user_in_scope("regular_1", LIBRARY_ADMIN.external_key, glob_scope) response = self.client.get(self._url("regular_1")) @@ -2058,7 +2069,7 @@ def test_scope_display_name_empty_for_glob_scopes(self, glob_scope: str): @patch("openedx_authz.rest_api.utils.get_scope_display_name_map", return_value={}) def test_scope_display_names_resolved_in_single_bulk_call_per_page(self, mock_map): """Names are resolved with one bulk call containing only the current page's scopes.""" - assign_role_to_user_in_scope("regular_1", roles.LIBRARY_ADMIN.external_key, "lib:Org1:*") + assign_role_to_user_in_scope("regular_1", LIBRARY_ADMIN.external_key, "lib:Org1:*") response = self.client.get(self._url("regular_1"), {"page_size": 1}) @@ -2084,7 +2095,7 @@ def test_no_superadmin_entries_when_filtering_by_org(self): def test_no_superadmin_entries_when_filtering_by_role(self): """A role filter does not add superadmin entries.""" - response = self.client.get(self._url("admin_1"), {"roles": roles.LIBRARY_ADMIN.external_key}) + response = self.client.get(self._url("admin_1"), {"roles": LIBRARY_ADMIN.external_key}) self.assertEqual(response.status_code, status.HTTP_200_OK) for item in response.data["results"]: @@ -2213,11 +2224,11 @@ def test_filter_by_multiple_orgs(self): @data( # library_admin: admin_1 (Org1), admin_3 (Org3), regular_5 (Org3) = 3 - (roles.LIBRARY_ADMIN.external_key, 3), + (LIBRARY_ADMIN.external_key, 3), # library_user: admin_2 (Org2), regular_1..4 (Org1+Org2), regular_8 (Org3) = 6 - (roles.LIBRARY_USER.external_key, 6), + (LIBRARY_USER.external_key, 6), # library_author: regular_6 (Org3) = 1 - (roles.LIBRARY_AUTHOR.external_key, 1), + (LIBRARY_AUTHOR.external_key, 1), # library_contributor: regular_7 (Org3) = 1 ("library_contributor", 1), # Non-existent role: no matches @@ -2244,7 +2255,7 @@ def test_filter_by_multiple_roles(self): # library_admin (3) + library_author (1) = 4 response = self.client.get( self.url, - {"roles": f"{roles.LIBRARY_ADMIN.external_key},{roles.LIBRARY_AUTHOR.external_key}"}, + {"roles": f"{LIBRARY_ADMIN.external_key},{LIBRARY_AUTHOR.external_key}"}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -2499,7 +2510,7 @@ def test_no_superadmin_entries_when_filtering_by_role(self): Expected result: - No items with is_superadmin=True in the response. """ - response = self.client.get(self.url, {"roles": roles.LIBRARY_ADMIN.external_key}) + response = self.client.get(self.url, {"roles": LIBRARY_ADMIN.external_key}) self.assertEqual(response.status_code, status.HTTP_200_OK) superadmin_items = [item for item in response.data["results"] if item["is_superadmin"]] @@ -2560,7 +2571,7 @@ def test_combined_org_and_role_filter(self): # library_admin in Org1 = admin_1 (1 assignment) response = self.client.get( self.url, - {"orgs": "Org1", "roles": roles.LIBRARY_ADMIN.external_key}, + {"orgs": "Org1", "roles": LIBRARY_ADMIN.external_key}, ) self.assertEqual(response.status_code, status.HTTP_200_OK) @@ -2657,12 +2668,12 @@ def setUpClass(cls): [ { "subject_name": "regular_9", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "regular_10", - "role_name": roles.COURSE_AUDITOR.external_key, + "role_name": COURSE_AUDITOR.external_key, "scope_name": COURSE_SCOPE_ORG1, }, ] @@ -2836,7 +2847,7 @@ def test_user_with_org_course_permission_sees_org_course_assignments(self): [ { "subject_name": "regular_10", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_ORG1_GLOB, }, ] @@ -2873,7 +2884,7 @@ def test_user_with_org_library_permission_sees_org_library_assignments(self): [ { "subject_name": "regular_9", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:*", }, ] @@ -2907,7 +2918,7 @@ def test_user_with_org_library_permission_does_not_see_other_org_libraries(self) [ { "subject_name": "regular_9", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:*", }, ] @@ -2940,7 +2951,7 @@ def test_user_with_both_library_and_course_permissions(self): [ { "subject_name": "regular_9", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:*", }, ] diff --git a/src/openedx_authz/tests/rest_api/mixins.py b/src/openedx_authz/tests/rest_api/mixins.py index 6fde7fca..c61637bd 100644 --- a/src/openedx_authz/tests/rest_api/mixins.py +++ b/src/openedx_authz/tests/rest_api/mixins.py @@ -4,7 +4,11 @@ from rest_framework.test import APIClient from openedx_authz.api.users import assign_role_to_user_in_scope -from openedx_authz.constants import roles +from openedx_authz.authz import ( + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_USER, +) from openedx_authz.tests.api.test_roles import BaseRolesTestCase User = get_user_model() @@ -41,48 +45,48 @@ def setUpClass(cls): # Assign roles to admin users { "subject_name": "admin_1", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org1:LIB1", }, { "subject_name": "admin_2", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org2:LIB2", }, { "subject_name": "admin_3", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org3:LIB3", }, # Assign roles to regular users { "subject_name": "regular_1", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:LIB1", }, { "subject_name": "regular_2", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org1:LIB1", }, { "subject_name": "regular_3", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org2:LIB2", }, { "subject_name": "regular_4", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org2:LIB2", }, { "subject_name": "regular_5", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": "lib:Org3:LIB3", }, { "subject_name": "regular_6", - "role_name": roles.LIBRARY_AUTHOR.external_key, + "role_name": LIBRARY_AUTHOR.external_key, "scope_name": "lib:Org3:LIB3", }, { @@ -92,7 +96,7 @@ def setUpClass(cls): }, { "subject_name": "regular_8", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org3:LIB3", }, ] diff --git a/src/openedx_authz/tests/rest_api/test_views.py b/src/openedx_authz/tests/rest_api/test_views.py index cc7656f8..a72895d5 100644 --- a/src/openedx_authz/tests/rest_api/test_views.py +++ b/src/openedx_authz/tests/rest_api/test_views.py @@ -26,7 +26,20 @@ PlatformCourseOverviewGlobData, ) from openedx_authz.api.users import assign_role_to_user_in_scope -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_AUDITOR, + COURSE_EDITOR, + COURSE_STAFF, + COURSES_MANAGE_COURSE_TEAM, + COURSES_VIEW_COURSE, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_CONTRIBUTOR, + LIBRARY_USER, + MANAGE_LIBRARY_TEAM, + VIEW_LIBRARY, +) from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.models.schema import ( AuthzPermissionCategory, @@ -62,16 +75,16 @@ def setUp(self): @data( # Single permission - allowed - ([{"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"}], [True]), + ([{"action": VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"}], [True]), # Single permission - denied (scope not assigned to user) - ([{"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org2:LIB2"}], [False]), + ([{"action": VIEW_LIBRARY.identifier, "scope": "lib:Org2:LIB2"}], [False]), # Single permission - denied (action not assigned to user) ([{"action": "content_libraries.edit_library", "scope": "lib:Org1:LIB1"}], [False]), # Multiple permissions - mixed results ( [ - {"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"}, - {"action": permissions.VIEW_LIBRARY.identifier, "scope": "lib:Org2:LIB2"}, + {"action": VIEW_LIBRARY.identifier, "scope": "lib:Org1:LIB1"}, + {"action": VIEW_LIBRARY.identifier, "scope": "lib:Org2:LIB2"}, {"action": "content_libraries.edit_library", "scope": "lib:Org1:LIB1"}, ], [True, False, False], @@ -161,15 +174,15 @@ def test_permission_validation_invalid_data(self, invalid_data: list[dict]): @data( # Single action the user has in some scope (LIBRARY_USER on lib:Org1:LIB1) - allowed - ([{"action": permissions.VIEW_LIBRARY.identifier}], [True]), + ([{"action": VIEW_LIBRARY.identifier}], [True]), # Single action the user has in no scope - denied - ([{"action": permissions.MANAGE_LIBRARY_TEAM.identifier}], [False]), + ([{"action": MANAGE_LIBRARY_TEAM.identifier}], [False]), # Multiple actions - mixed results ( [ - {"action": permissions.VIEW_LIBRARY.identifier}, - {"action": permissions.MANAGE_LIBRARY_TEAM.identifier}, - {"action": permissions.COURSES_MANAGE_COURSE_TEAM.identifier}, + {"action": VIEW_LIBRARY.identifier}, + {"action": MANAGE_LIBRARY_TEAM.identifier}, + {"action": COURSES_MANAGE_COURSE_TEAM.identifier}, ], [True, False, False], ), @@ -204,8 +217,8 @@ def test_permission_validation_any_scope_staff_always_allowed(self): """ self.client.force_authenticate(user=self.admin_user) request_data = [ - {"action": permissions.MANAGE_LIBRARY_TEAM.identifier}, - {"action": permissions.COURSES_MANAGE_COURSE_TEAM.identifier}, + {"action": MANAGE_LIBRARY_TEAM.identifier}, + {"action": COURSES_MANAGE_COURSE_TEAM.identifier}, ] expected_response = [{"action": perm["action"], "allowed": True} for perm in request_data] @@ -275,12 +288,12 @@ def test_permission_validation_marks_hidden_course_scope_as_disallowed(self): self.client.force_authenticate(user=self.regular_user) assign_role_to_user_in_scope( user_external_key=self.regular_user.username, - role_external_key=roles.COURSE_STAFF.external_key, + role_external_key=COURSE_STAFF.external_key, scope_external_key=COURSE_SCOPE_ORG1, ) request_data = [ - {"action": permissions.COURSES_VIEW_COURSE.identifier, "scope": COURSE_SCOPE_ORG1}, - {"action": permissions.VIEW_LIBRARY.identifier, "scope": LIB_SCOPE_ORG1}, + {"action": COURSES_VIEW_COURSE.identifier, "scope": COURSE_SCOPE_ORG1}, + {"action": VIEW_LIBRARY.identifier, "scope": LIB_SCOPE_ORG1}, ] with patch( @@ -301,17 +314,17 @@ class TestRoleUserAPIView(ViewTestMixin): _COURSE_ASSIGNMENTS = [ { "subject_name": "course_admin", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "course_editor", - "role_name": roles.COURSE_EDITOR.external_key, + "role_name": COURSE_EDITOR.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "course_auditor", - "role_name": roles.COURSE_AUDITOR.external_key, + "role_name": COURSE_AUDITOR.external_key, "scope_name": COURSE_SCOPE_ORG1, }, ] @@ -339,23 +352,23 @@ def setUp(self): ({"search": "@example.com"}, 3), ({"search": "nonexistent@example.com"}, 0), # Search by single role - ({"roles": roles.LIBRARY_ADMIN.external_key}, 1), - ({"roles": roles.LIBRARY_AUTHOR.external_key}, 0), - ({"roles": roles.LIBRARY_USER.external_key}, 2), + ({"roles": LIBRARY_ADMIN.external_key}, 1), + ({"roles": LIBRARY_AUTHOR.external_key}, 0), + ({"roles": LIBRARY_USER.external_key}, 2), # Search by multiple roles ({"roles": "library_admin,library_author"}, 1), ({"roles": "library_author,library_user"}, 2), ({"roles": "library_user,library_admin"}, 3), ({"roles": "library_admin,library_author,library_user"}, 3), # Search by role and username - ({"search": "admin_1", "roles": roles.LIBRARY_ADMIN.external_key}, 1), - ({"search": "regular_1", "roles": roles.LIBRARY_USER.external_key}, 1), - ({"search": "regular_1", "roles": roles.LIBRARY_ADMIN.external_key}, 0), + ({"search": "admin_1", "roles": LIBRARY_ADMIN.external_key}, 1), + ({"search": "regular_1", "roles": LIBRARY_USER.external_key}, 1), + ({"search": "regular_1", "roles": LIBRARY_ADMIN.external_key}, 0), # Search by role and email - ({"search": "admin_1@example.com", "roles": roles.LIBRARY_ADMIN.external_key}, 1), - ({"search": "@example.com", "roles": roles.LIBRARY_ADMIN.external_key}, 1), - ({"search": "@example.com", "roles": roles.LIBRARY_USER.external_key}, 2), - ({"search": "regular_1@example.com", "roles": roles.LIBRARY_ADMIN.external_key}, 0), + ({"search": "admin_1@example.com", "roles": LIBRARY_ADMIN.external_key}, 1), + ({"search": "@example.com", "roles": LIBRARY_ADMIN.external_key}, 1), + ({"search": "@example.com", "roles": LIBRARY_USER.external_key}, 2), + ({"search": "regular_1@example.com", "roles": LIBRARY_ADMIN.external_key}, 0), ) @unpack def test_get_users_by_scope_success(self, query_params: dict, expected_count: int): @@ -499,7 +512,7 @@ def test_add_users_to_role_success(self, users: list[str], expected_completed: i - Returns 207 MULTI-STATUS status - Returns appropriate completed and error counts """ - role = roles.LIBRARY_ADMIN.external_key + role = LIBRARY_ADMIN.external_key request_data = {"role": role, "scope": "lib:Org1:LIB3", "users": users} with patch.object(api.ContentLibraryData, "exists", return_value=True): @@ -522,7 +535,7 @@ def test_add_users_to_role_success(self, users: list[str], expected_completed: i @unpack def test_add_users_to_role_already_has_role(self, users: list[str], expected_completed: int, expected_errors: int): """Test adding users to a role that already has the role.""" - role = roles.LIBRARY_USER.external_key + role = LIBRARY_USER.external_key scope = "lib:Org2:LIB2" request_data = {"role": role, "scope": scope, "users": users} @@ -537,7 +550,7 @@ def test_add_users_to_role_already_has_role(self, users: list[str], expected_com def test_add_users_to_role_exception_handling(self, mock_assign_role_to_user_in_scope): """Test adding users to a role with exception handling.""" request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": ["regular_1"], } @@ -557,15 +570,15 @@ def test_add_users_to_role_exception_handling(self, mock_assign_role_to_user_in_ @data( {}, - {"role": roles.LIBRARY_ADMIN.external_key}, + {"role": LIBRARY_ADMIN.external_key}, {"scope": "lib:Org1:LIB1"}, {"users": ["admin_1"]}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1"}, + {"role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1"}, {"scope": "lib:Org1:LIB1", "users": ["admin_1"]}, - {"users": ["admin_1", "regular_1"], "role": roles.LIBRARY_ADMIN.external_key}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": []}, + {"users": ["admin_1", "regular_1"], "role": LIBRARY_ADMIN.external_key}, + {"role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": []}, {"role": "", "scope": "lib:Org1:LIB1", "users": ["admin_1"]}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "", "users": ["admin_1"]}, + {"role": LIBRARY_ADMIN.external_key, "scope": "", "users": ["admin_1"]}, ) def test_add_users_to_role_invalid_data(self, request_data: dict): """Test adding users with invalid request data. @@ -605,7 +618,7 @@ def test_add_users_to_role_multi_scope_success( - Completed count equals len(users) * len(scopes) when all succeed - Each completed entry contains a 'scope' field """ - role = roles.LIBRARY_ADMIN.external_key + role = LIBRARY_ADMIN.external_key request_data = {"role": role, "scopes": scopes, "users": users} with patch.object(api.ContentLibraryData, "exists", return_value=True): @@ -627,7 +640,7 @@ def test_add_users_to_role_backward_compat_response_includes_scope(self): """ scope = "lib:Org1:LIB3" request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": scope, "users": ["admin_1", "regular_1"], } @@ -644,14 +657,14 @@ def test_add_users_to_role_backward_compat_response_includes_scope(self): @data( # Both 'scope' and 'scopes' provided { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "scopes": ["lib:Org2:LIB2"], "users": ["admin_1"], }, # 'scopes' as empty list { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scopes": [], "users": ["admin_1"], }, @@ -685,7 +698,7 @@ def test_add_users_to_role_permissions(self, username: str, status_code: int): - Returns appropriate status code based on permissions """ request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org3:LIB3", "users": ["regular_2"], } @@ -721,7 +734,7 @@ def test_add_users_to_role_course_permissions(self, username: str, status_code: - Returns appropriate status code based on course-scope permissions. """ request_data = { - "role": roles.COURSE_ADMIN.external_key, + "role": COURSE_ADMIN.external_key, "scope": COURSE_SCOPE_ORG1, "users": ["regular_2"], } @@ -748,7 +761,7 @@ def test_add_users_to_role_reports_hidden_scope_without_writing(self, mock_assig """A scope hidden by the configured filter is reported and never written.""" self.client.force_authenticate(user=User.objects.get(username="course_admin")) request_data = { - "role": roles.COURSE_STAFF.external_key, + "role": COURSE_STAFF.external_key, "scope": COURSE_SCOPE_ORG1, "users": ["regular_2"], } @@ -819,7 +832,7 @@ def test_remove_users_from_role_success(self, users: list[str], expected_complet - Returns appropriate completed and error counts """ query_params = { - "role": roles.LIBRARY_USER.external_key, + "role": LIBRARY_USER.external_key, "scope": "lib:Org2:LIB2", "users": ",".join(users), } @@ -833,11 +846,11 @@ def test_remove_users_from_role_success(self, users: list[str], expected_complet @data( # library_admin removing its own library_admin: refused - ("admin_3", roles.LIBRARY_ADMIN.external_key, "lib:Org3:LIB3", "admin_3", "api.ContentLibraryData"), + ("admin_3", LIBRARY_ADMIN.external_key, "lib:Org3:LIB3", "admin_3", "api.ContentLibraryData"), # same, naming itself by email - ("admin_3", roles.LIBRARY_ADMIN.external_key, "lib:Org3:LIB3", "admin_3@example.com", "api.ContentLibraryData"), + ("admin_3", LIBRARY_ADMIN.external_key, "lib:Org3:LIB3", "admin_3@example.com", "api.ContentLibraryData"), # course_admin removing its own course_admin: refused - ("course_admin", roles.COURSE_ADMIN.external_key, COURSE_SCOPE_ORG1, "course_admin", "api.CourseOverviewData"), + ("course_admin", COURSE_ADMIN.external_key, COURSE_SCOPE_ORG1, "course_admin", "api.CourseOverviewData"), ) @unpack def test_remove_own_admin_role_is_refused( @@ -850,8 +863,8 @@ def test_remove_own_admin_role_is_refused( - The requester is reported with ``cannot_remove_own_admin_role`` and keeps the role - Another admin in the same request is removed """ - other = "regular_5" if role == roles.LIBRARY_ADMIN.external_key else "regular_2" - if role == roles.COURSE_ADMIN.external_key: + other = "regular_5" if role == LIBRARY_ADMIN.external_key else "regular_2" + if role == COURSE_ADMIN.external_key: self._assign_roles_to_users(assignments=[{"subject_name": other, "role_name": role, "scope_name": scope}]) user = User.objects.get(username=username) self.client.force_authenticate(user=user) @@ -882,12 +895,12 @@ def test_remove_own_non_admin_role_is_allowed(self): assignments=[ { "subject_name": "admin_3", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": "lib:Org3:LIB3", } ] ) - query_params = {"role": roles.LIBRARY_USER.external_key, "scope": "lib:Org3:LIB3", "users": "admin_3"} + query_params = {"role": LIBRARY_USER.external_key, "scope": "lib:Org3:LIB3", "users": "admin_3"} with patch.object(api.ContentLibraryData, "exists", return_value=True): response = self.client.delete(f"{self.url}?{urlencode(query_params)}") @@ -900,7 +913,7 @@ def test_remove_own_non_admin_role_is_allowed(self): def test_remove_users_from_role_exception_handling(self, mock_unassign_role_from_user): """Test removing users from a role with exception handling.""" query_params = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": "regular_1,regular_2,regular_3", } @@ -929,15 +942,15 @@ def test_remove_users_from_role_exception_handling(self, mock_unassign_role_from @data( {}, - {"role": roles.LIBRARY_ADMIN.external_key}, + {"role": LIBRARY_ADMIN.external_key}, {"scope": "lib:Org1:LIB1"}, {"users": "admin_1"}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1"}, + {"role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1"}, {"scope": "lib:Org1:LIB1", "users": "admin_1"}, - {"users": "admin_1,regular_1", "role": roles.LIBRARY_ADMIN.external_key}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": ""}, + {"users": "admin_1,regular_1", "role": LIBRARY_ADMIN.external_key}, + {"role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:LIB1", "users": ""}, {"role": "", "scope": "lib:Org1:LIB1", "users": "admin_1"}, - {"role": roles.LIBRARY_ADMIN.external_key, "scope": "", "users": "admin_1"}, + {"role": LIBRARY_ADMIN.external_key, "scope": "", "users": "admin_1"}, ) def test_remove_users_from_role_invalid_params(self, query_params: dict): """Test removing users with invalid query parameters. @@ -967,7 +980,7 @@ def test_remove_users_from_role_permissions(self, username: str, status_code: in - Returns appropriate status code based on permissions """ query_params = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org3:LIB3", "users": "user1,user2", } @@ -1003,7 +1016,7 @@ def test_remove_users_from_role_course_permissions(self, username: str, status_c - Returns appropriate status code based on course-scope permissions. """ query_params = { - "role": roles.COURSE_ADMIN.external_key, + "role": COURSE_ADMIN.external_key, "scope": COURSE_SCOPE_ORG1, "users": "regular_2", } @@ -1030,7 +1043,7 @@ def test_remove_users_from_role_reports_hidden_scope_without_writing(self, mock_ """A scope hidden by the configured filter is reported and never written.""" self.client.force_authenticate(user=User.objects.get(username="course_admin")) query_params = { - "role": roles.COURSE_STAFF.external_key, + "role": COURSE_STAFF.external_key, "scope": COURSE_SCOPE_ORG1, "users": "regular_2", } @@ -1102,7 +1115,7 @@ def setUp(self): def test_put_rejects_malformed_or_overbroad_scope_strings(self, invalid_scope: str): """PUT must return 400 when the scope is not a valid concrete key or org-level glob.""" request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": invalid_scope, "users": ["regular_1"], } @@ -1123,7 +1136,7 @@ def test_put_rejects_malformed_or_overbroad_scope_strings(self, invalid_scope: s def test_delete_rejects_malformed_or_overbroad_scope_strings(self, invalid_scope: str): """DELETE must return 400 for the same invalid scope strings as PUT.""" query_params = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": invalid_scope, "users": "regular_1", } @@ -1143,7 +1156,7 @@ def test_delete_rejects_malformed_or_overbroad_scope_strings(self, invalid_scope def test_put_rejects_scope_that_does_not_exist(self, scope: str): """Well-formed keys that do not resolve to an existing org/course must return 400.""" request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": scope, "users": ["regular_1"], } @@ -1159,7 +1172,7 @@ def test_put_rejects_scope_that_does_not_exist(self, scope: str): def test_put_accepts_valid_library_org_glob_scope(self, _mock_exists, _mock_assign): """Valid library org glob passes serializer validation and reaches assignment.""" request_data = { - "role": roles.LIBRARY_ADMIN.external_key, + "role": LIBRARY_ADMIN.external_key, "scope": "lib:Org1:*", "users": ["regular_1"], } @@ -1174,7 +1187,7 @@ def test_put_accepts_valid_library_org_glob_scope(self, _mock_exists, _mock_assi def test_put_accepts_valid_course_org_glob_scope(self, _mock_exists, _mock_assign): """Valid course org glob (course-v1:ORG+*) passes validation for a course role.""" request_data = { - "role": roles.COURSE_STAFF.external_key, + "role": COURSE_STAFF.external_key, "scope": "course-v1:OpenedX+*", "users": ["regular_1"], } @@ -1189,7 +1202,7 @@ def test_put_accepts_valid_course_org_glob_scope(self, _mock_exists, _mock_assig def test_put_accepts_valid_full_course_key_scope(self, _mock_exists, _mock_assign): """A full course run key is accepted for a course role when the course exists.""" request_data = { - "role": roles.COURSE_STAFF.external_key, + "role": COURSE_STAFF.external_key, "scope": "course-v1:OpenedX+DemoCourse+2026_T1", "users": ["regular_1"], } @@ -1207,22 +1220,22 @@ class TestRoleListView(ViewTestMixin): _COURSE_ASSIGNMENTS = [ { "subject_name": "course_admin", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "course_editor", - "role_name": roles.COURSE_EDITOR.external_key, + "role_name": COURSE_EDITOR.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "course_auditor", - "role_name": roles.COURSE_AUDITOR.external_key, + "role_name": COURSE_AUDITOR.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "regular_9", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_SCOPE_ORG1, }, ] @@ -1261,13 +1274,13 @@ def setUp(self): namespace="courses", name="view_course", display_name="View course", category=category, scopes=["course-v1"] ) author = AuthzRoleDefinition.objects.create( - role_id=roles.LIBRARY_AUTHOR.external_key, display_name="Library Author", scopes=["lib"] + role_id=LIBRARY_AUTHOR.external_key, display_name="Library Author", scopes=["lib"] ) user = AuthzRoleDefinition.objects.create( - role_id=roles.LIBRARY_USER.external_key, display_name="Library User", scopes=["lib"] + role_id=LIBRARY_USER.external_key, display_name="Library User", scopes=["lib"] ) hidden = AuthzRoleDefinition.objects.create( - role_id=roles.LIBRARY_ADMIN.external_key, display_name="Hidden", scopes=["lib"], hidden=True + role_id=LIBRARY_ADMIN.external_key, display_name="Hidden", scopes=["lib"], hidden=True ) for role, permission, scope in [ (author, view_library, "lib"), @@ -1317,14 +1330,14 @@ def test_get_roles_catalog(self): ) by_role = {r["role"]: r for r in response.data["results"]} self.assertEqual(response.data["count"], len(by_role)) - self.assertNotIn(roles.LIBRARY_ADMIN.external_key, by_role) + self.assertNotIn(LIBRARY_ADMIN.external_key, by_role) self.assertEqual( - by_role[roles.LIBRARY_AUTHOR.external_key]["permissions"], + by_role[LIBRARY_AUTHOR.external_key]["permissions"], ["content_libraries.edit_library", "content_libraries.view_library"], ) - self.assertEqual(by_role[roles.LIBRARY_AUTHOR.external_key]["definition_kind"], DefinitionKind.STATIC.value) - self.assertEqual(by_role[roles.LIBRARY_AUTHOR.external_key]["scope_types"], ["lib"]) - self.assertEqual(by_role[roles.LIBRARY_USER.external_key]["display_name"], "Library User") + self.assertEqual(by_role[LIBRARY_AUTHOR.external_key]["definition_kind"], DefinitionKind.STATIC.value) + self.assertEqual(by_role[LIBRARY_AUTHOR.external_key]["scope_types"], ["lib"]) + self.assertEqual(by_role[LIBRARY_USER.external_key]["display_name"], "Library User") self.assertEqual(list(by_role), sorted(by_role)) def test_get_roles_permission_without_category(self): @@ -1352,12 +1365,12 @@ def test_get_roles_without_definition_is_not_listed(self): Expected result: - The catalog is read from the schema definitions only """ - AuthzRoleDefinition.objects.filter(role_id=roles.LIBRARY_CONTRIBUTOR.external_key).delete() + AuthzRoleDefinition.objects.filter(role_id=LIBRARY_CONTRIBUTOR.external_key).delete() response = self.client.get(self.url, {"scope_types": "lib"}) by_role = {r["role"]: r for r in response.data["results"]} - self.assertNotIn(roles.LIBRARY_CONTRIBUTOR.external_key, by_role) + self.assertNotIn(LIBRARY_CONTRIBUTOR.external_key, by_role) def test_get_roles_query_count_does_not_depend_on_roles(self): """Definitions are loaded with a constant number of queries. @@ -1431,7 +1444,7 @@ def test_get_roles_several_scope_types(self): ) by_role = {r["role"]: r for r in response.data["results"]} self.assertEqual( - by_role[roles.LIBRARY_AUTHOR.external_key]["permissions"], + by_role[LIBRARY_AUTHOR.external_key]["permissions"], ["content_libraries.edit_library", "content_libraries.view_library", "courses.view_course"], ) @@ -1443,7 +1456,7 @@ def test_get_roles_course_scope_type_lists_only_roles_with_course_grants(self): """ response = self.client.get(self.url, {"scope_types": "course-v1"}) - self.assertEqual([r["role"] for r in response.data["results"]], [roles.LIBRARY_AUTHOR.external_key]) + self.assertEqual([r["role"] for r in response.data["results"]], [LIBRARY_AUTHOR.external_key]) self.assertEqual(response.data["results"][0]["permissions"], ["courses.view_course"]) def test_get_roles_pagination_only_paginates_roles(self): @@ -1906,12 +1919,12 @@ def test_post_multiple_roles_user_access(self): test_user = User.objects.create(username="multi_role_user", email="multi@example.com") assign_role_to_user_in_scope( user_external_key="multi_role_user", - role_external_key=roles.LIBRARY_ADMIN.external_key, + role_external_key=LIBRARY_ADMIN.external_key, scope_external_key="lib:Org1:LIB1", ) assign_role_to_user_in_scope( user_external_key="multi_role_user", - role_external_key=roles.LIBRARY_USER.external_key, + role_external_key=LIBRARY_USER.external_key, scope_external_key="lib:Org2:LIB2", ) self.client.force_authenticate(user=test_user) @@ -1971,33 +1984,33 @@ def setUpClass(cls): # -- Course hierarchy: Org1 -- { "subject_name": "regular_1", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "regular_2", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": COURSE_ORG1_GLOB, }, { "subject_name": "regular_3", - "role_name": roles.COURSE_STAFF.external_key, + "role_name": COURSE_STAFF.external_key, "scope_name": PLATFORM_COURSE_GLOB, }, # -- Library hierarchy: Org1 -- { "subject_name": "regular_4", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": LIB_SCOPE_ORG1, }, { "subject_name": "regular_5", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": LIB_ORG1_GLOB, }, { "subject_name": "regular_6", - "role_name": roles.LIBRARY_USER.external_key, + "role_name": LIBRARY_USER.external_key, "scope_name": PLATFORM_LIBRARY_GLOB, }, ] @@ -2201,17 +2214,17 @@ class TestBulkPutScopesAllLogic(ViewTestMixin): _COURSE_ASSIGNMENTS = [ { "subject_name": "course_admin", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": COURSE_SCOPE_ORG1, }, { "subject_name": "course_admin_org", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": COURSE_ORG1_GLOB, }, { "subject_name": "course_admin_platform", - "role_name": roles.COURSE_ADMIN.external_key, + "role_name": COURSE_ADMIN.external_key, "scope_name": PLATFORM_COURSE_GLOB, }, ] @@ -2228,7 +2241,7 @@ def _put_course(self, scopes): Patches scope existence checks so validation does not depend on the database. """ - request_data = {"role": roles.COURSE_ADMIN.external_key, "scopes": scopes, "users": ["regular_2"]} + request_data = {"role": COURSE_ADMIN.external_key, "scopes": scopes, "users": ["regular_2"]} with ( patch.object(api.CourseOverviewData, "exists", return_value=True), patch.object(api.OrgCourseOverviewGlobData, "exists", return_value=True), @@ -2240,7 +2253,7 @@ def _put_lib(self, scopes): Patches ContentLibraryData.exists so validation does not depend on the database. """ - request_data = {"role": roles.LIBRARY_ADMIN.external_key, "scopes": scopes, "users": ["regular_2"]} + request_data = {"role": LIBRARY_ADMIN.external_key, "scopes": scopes, "users": ["regular_2"]} with patch.object(api.ContentLibraryData, "exists", return_value=True): return self.client.put(self.url, data=request_data, format="json") @@ -2339,17 +2352,17 @@ class TestPlatformGlobLibraryRoleAssignment(ViewTestMixin): _LIBRARY_ASSIGNMENTS = [ { "subject_name": "library_admin", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": LIB_SCOPE_ORG1, }, { "subject_name": "library_admin_org", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": LIB_ORG1_GLOB, }, { "subject_name": "library_admin_platform", - "role_name": roles.LIBRARY_ADMIN.external_key, + "role_name": LIBRARY_ADMIN.external_key, "scope_name": PLATFORM_LIBRARY_GLOB, }, ] @@ -2370,10 +2383,10 @@ def _put_lib(self, scopes, role): return self.client.put(self.url, data=request_data, format="json") @data( - roles.LIBRARY_ADMIN.external_key, - roles.LIBRARY_AUTHOR.external_key, - roles.LIBRARY_CONTRIBUTOR.external_key, - roles.LIBRARY_USER.external_key, + LIBRARY_ADMIN.external_key, + LIBRARY_AUTHOR.external_key, + LIBRARY_CONTRIBUTOR.external_key, + LIBRARY_USER.external_key, ) def test_platform_library_admin_can_assign_any_role_globally(self, role): """A platform library_admin can assign any library role at lib:*.""" @@ -2411,6 +2424,6 @@ def test_scope_permission_vs_platform_permission(self, username, scopes, expecte user = User.objects.get(username=username) self.client.force_authenticate(user=user) - response = self._put_lib(scopes, roles.LIBRARY_ADMIN.external_key) + response = self._put_lib(scopes, LIBRARY_ADMIN.external_key) self.assertEqual(response.status_code, expected_status) diff --git a/src/openedx_authz/tests/role_permissions.py b/src/openedx_authz/tests/role_permissions.py index ad9f765b..04f75c50 100644 --- a/src/openedx_authz/tests/role_permissions.py +++ b/src/openedx_authz/tests/role_permissions.py @@ -1,121 +1,180 @@ """Expected permissions for built-in roles, as defined in the applets' YAML schema files.""" -from openedx_authz.constants import permissions +from openedx_authz.authz import ( + COURSES_CREATE_FILES, + COURSES_DELETE_FILES, + COURSES_EDIT_COURSE_CONTENT, + COURSES_EDIT_DETAILS, + COURSES_EDIT_FILES, + COURSES_EDIT_GRADING_SETTINGS, + COURSES_EDIT_SCHEDULE, + COURSES_ENROLL_LEARNERS, + COURSES_EXPORT_COURSE, + COURSES_EXPORT_TAGS, + COURSES_IMPORT_COURSE, + COURSES_LEGACY_BETA_TESTER_PERMISSIONS, + COURSES_LEGACY_DATA_RESEARCHER_PERMISSIONS, + COURSES_LEGACY_INSTRUCTOR_ROLE_PERMISSIONS, + COURSES_LEGACY_LIMITED_STAFF_ROLE_PERMISSIONS, + COURSES_LEGACY_STAFF_ROLE_PERMISSIONS, + COURSES_MANAGE_ADVANCED_SETTINGS, + COURSES_MANAGE_CERTIFICATES, + COURSES_MANAGE_COHORTS, + COURSES_MANAGE_COURSE_TEAM, + COURSES_MANAGE_COURSE_UPDATES, + COURSES_MANAGE_GROUP_CONFIGURATIONS, + COURSES_MANAGE_LEARNER_EXTENSIONS, + COURSES_MANAGE_LEARNER_GRADING, + COURSES_MANAGE_LIBRARY_UPDATES, + COURSES_MANAGE_PAGES_AND_RESOURCES, + COURSES_MANAGE_TAGS, + COURSES_MANAGE_TAXONOMIES, + COURSES_PUBLISH_COURSE_CONTENT, + COURSES_UNENROLL_LEARNERS, + COURSES_VIEW_ADVANCED_SETTINGS, + COURSES_VIEW_CERTIFICATES, + COURSES_VIEW_CHECKLISTS, + COURSES_VIEW_COHORTS, + COURSES_VIEW_COURSE, + COURSES_VIEW_COURSE_INFO, + COURSES_VIEW_COURSE_TEAM, + COURSES_VIEW_COURSE_UPDATES, + COURSES_VIEW_ENROLLMENTS, + COURSES_VIEW_FILES, + COURSES_VIEW_GRADEBOOK, + COURSES_VIEW_GRADING_SETTINGS, + COURSES_VIEW_GROUP_CONFIGURATIONS, + COURSES_VIEW_LEARNER_EXTENSIONS, + COURSES_VIEW_LIBRARY_UPDATES, + COURSES_VIEW_PAGES_AND_RESOURCES, + COURSES_VIEW_SCHEDULE_AND_DETAILS, + CREATE_LIBRARY_COLLECTION, + DELETE_LIBRARY, + DELETE_LIBRARY_COLLECTION, + EDIT_LIBRARY_COLLECTION, + EDIT_LIBRARY_CONTENT, + MANAGE_LIBRARY_TAGS, + MANAGE_LIBRARY_TEAM, + PUBLISH_LIBRARY_CONTENT, + REUSE_LIBRARY_CONTENT, + VIEW_LIBRARY, + VIEW_LIBRARY_TEAM, +) LIBRARY_ADMIN_PERMISSIONS = [ - permissions.VIEW_LIBRARY, - permissions.MANAGE_LIBRARY_TAGS, - permissions.DELETE_LIBRARY, - permissions.EDIT_LIBRARY_CONTENT, - permissions.PUBLISH_LIBRARY_CONTENT, - permissions.REUSE_LIBRARY_CONTENT, - permissions.VIEW_LIBRARY_TEAM, - permissions.MANAGE_LIBRARY_TEAM, - permissions.CREATE_LIBRARY_COLLECTION, - permissions.EDIT_LIBRARY_COLLECTION, - permissions.DELETE_LIBRARY_COLLECTION, + VIEW_LIBRARY, + MANAGE_LIBRARY_TAGS, + DELETE_LIBRARY, + EDIT_LIBRARY_CONTENT, + PUBLISH_LIBRARY_CONTENT, + REUSE_LIBRARY_CONTENT, + VIEW_LIBRARY_TEAM, + MANAGE_LIBRARY_TEAM, + CREATE_LIBRARY_COLLECTION, + EDIT_LIBRARY_COLLECTION, + DELETE_LIBRARY_COLLECTION, ] LIBRARY_AUTHOR_PERMISSIONS = [ - permissions.VIEW_LIBRARY, - permissions.MANAGE_LIBRARY_TAGS, - permissions.EDIT_LIBRARY_CONTENT, - permissions.PUBLISH_LIBRARY_CONTENT, - permissions.REUSE_LIBRARY_CONTENT, - permissions.VIEW_LIBRARY_TEAM, - permissions.CREATE_LIBRARY_COLLECTION, - permissions.EDIT_LIBRARY_COLLECTION, - permissions.DELETE_LIBRARY_COLLECTION, + VIEW_LIBRARY, + MANAGE_LIBRARY_TAGS, + EDIT_LIBRARY_CONTENT, + PUBLISH_LIBRARY_CONTENT, + REUSE_LIBRARY_CONTENT, + VIEW_LIBRARY_TEAM, + CREATE_LIBRARY_COLLECTION, + EDIT_LIBRARY_COLLECTION, + DELETE_LIBRARY_COLLECTION, ] LIBRARY_CONTRIBUTOR_PERMISSIONS = [ - permissions.VIEW_LIBRARY, - permissions.MANAGE_LIBRARY_TAGS, - permissions.EDIT_LIBRARY_CONTENT, - permissions.REUSE_LIBRARY_CONTENT, - permissions.VIEW_LIBRARY_TEAM, - permissions.CREATE_LIBRARY_COLLECTION, - permissions.EDIT_LIBRARY_COLLECTION, - permissions.DELETE_LIBRARY_COLLECTION, + VIEW_LIBRARY, + MANAGE_LIBRARY_TAGS, + EDIT_LIBRARY_CONTENT, + REUSE_LIBRARY_CONTENT, + VIEW_LIBRARY_TEAM, + CREATE_LIBRARY_COLLECTION, + EDIT_LIBRARY_COLLECTION, + DELETE_LIBRARY_COLLECTION, ] LIBRARY_USER_PERMISSIONS = [ - permissions.VIEW_LIBRARY, - permissions.REUSE_LIBRARY_CONTENT, - permissions.VIEW_LIBRARY_TEAM, + VIEW_LIBRARY, + REUSE_LIBRARY_CONTENT, + VIEW_LIBRARY_TEAM, ] COURSE_AUDITOR_PERMISSIONS = [ - permissions.COURSES_VIEW_COURSE, - permissions.COURSES_VIEW_COURSE_UPDATES, - permissions.COURSES_VIEW_PAGES_AND_RESOURCES, - permissions.COURSES_VIEW_FILES, - permissions.COURSES_VIEW_GRADING_SETTINGS, - permissions.COURSES_VIEW_CHECKLISTS, - permissions.COURSES_VIEW_COURSE_TEAM, - permissions.COURSES_VIEW_SCHEDULE_AND_DETAILS, - permissions.COURSES_VIEW_ADVANCED_SETTINGS, - permissions.COURSES_VIEW_CERTIFICATES, - permissions.COURSES_VIEW_GROUP_CONFIGURATIONS, - permissions.COURSES_VIEW_LIBRARY_UPDATES, + COURSES_VIEW_COURSE, + COURSES_VIEW_COURSE_UPDATES, + COURSES_VIEW_PAGES_AND_RESOURCES, + COURSES_VIEW_FILES, + COURSES_VIEW_GRADING_SETTINGS, + COURSES_VIEW_CHECKLISTS, + COURSES_VIEW_COURSE_TEAM, + COURSES_VIEW_SCHEDULE_AND_DETAILS, + COURSES_VIEW_ADVANCED_SETTINGS, + COURSES_VIEW_CERTIFICATES, + COURSES_VIEW_GROUP_CONFIGURATIONS, + COURSES_VIEW_LIBRARY_UPDATES, ] COURSE_EDITOR_PERMISSIONS = [ *COURSE_AUDITOR_PERMISSIONS, - permissions.COURSES_EDIT_COURSE_CONTENT, - permissions.COURSES_MANAGE_LIBRARY_UPDATES, - permissions.COURSES_MANAGE_COURSE_UPDATES, - permissions.COURSES_MANAGE_PAGES_AND_RESOURCES, - permissions.COURSES_CREATE_FILES, - permissions.COURSES_EDIT_FILES, - permissions.COURSES_EDIT_GRADING_SETTINGS, - permissions.COURSES_MANAGE_GROUP_CONFIGURATIONS, - permissions.COURSES_EDIT_DETAILS, - permissions.COURSES_MANAGE_TAGS, + COURSES_EDIT_COURSE_CONTENT, + COURSES_MANAGE_LIBRARY_UPDATES, + COURSES_MANAGE_COURSE_UPDATES, + COURSES_MANAGE_PAGES_AND_RESOURCES, + COURSES_CREATE_FILES, + COURSES_EDIT_FILES, + COURSES_EDIT_GRADING_SETTINGS, + COURSES_MANAGE_GROUP_CONFIGURATIONS, + COURSES_EDIT_DETAILS, + COURSES_MANAGE_TAGS, ] INSTRUCTOR_DASHBOARD_PERMISSIONS = [ - permissions.COURSES_VIEW_COURSE_INFO, - permissions.COURSES_VIEW_ENROLLMENTS, - permissions.COURSES_ENROLL_LEARNERS, - permissions.COURSES_UNENROLL_LEARNERS, - permissions.COURSES_VIEW_GRADEBOOK, - permissions.COURSES_MANAGE_LEARNER_GRADING, - permissions.COURSES_VIEW_LEARNER_EXTENSIONS, - permissions.COURSES_MANAGE_LEARNER_EXTENSIONS, - permissions.COURSES_VIEW_COHORTS, - permissions.COURSES_MANAGE_COHORTS, + COURSES_VIEW_COURSE_INFO, + COURSES_VIEW_ENROLLMENTS, + COURSES_ENROLL_LEARNERS, + COURSES_UNENROLL_LEARNERS, + COURSES_VIEW_GRADEBOOK, + COURSES_MANAGE_LEARNER_GRADING, + COURSES_VIEW_LEARNER_EXTENSIONS, + COURSES_MANAGE_LEARNER_EXTENSIONS, + COURSES_VIEW_COHORTS, + COURSES_MANAGE_COHORTS, ] COURSE_STAFF_PERMISSIONS = [ - permissions.COURSES_LEGACY_STAFF_ROLE_PERMISSIONS, + COURSES_LEGACY_STAFF_ROLE_PERMISSIONS, *COURSE_EDITOR_PERMISSIONS, - permissions.COURSES_PUBLISH_COURSE_CONTENT, - permissions.COURSES_DELETE_FILES, - permissions.COURSES_EDIT_SCHEDULE, - permissions.COURSES_MANAGE_ADVANCED_SETTINGS, - permissions.COURSES_MANAGE_CERTIFICATES, - permissions.COURSES_IMPORT_COURSE, - permissions.COURSES_EXPORT_COURSE, - permissions.COURSES_EXPORT_TAGS, + COURSES_PUBLISH_COURSE_CONTENT, + COURSES_DELETE_FILES, + COURSES_EDIT_SCHEDULE, + COURSES_MANAGE_ADVANCED_SETTINGS, + COURSES_MANAGE_CERTIFICATES, + COURSES_IMPORT_COURSE, + COURSES_EXPORT_COURSE, + COURSES_EXPORT_TAGS, *INSTRUCTOR_DASHBOARD_PERMISSIONS, ] COURSE_ADMIN_PERMISSIONS = [ - permissions.COURSES_LEGACY_INSTRUCTOR_ROLE_PERMISSIONS, + COURSES_LEGACY_INSTRUCTOR_ROLE_PERMISSIONS, *COURSE_STAFF_PERMISSIONS[1:], - permissions.COURSES_MANAGE_COURSE_TEAM, - permissions.COURSES_MANAGE_TAXONOMIES, + COURSES_MANAGE_COURSE_TEAM, + COURSES_MANAGE_TAXONOMIES, ] COURSE_LIMITED_STAFF_PERMISSIONS = [ - permissions.COURSES_LEGACY_LIMITED_STAFF_ROLE_PERMISSIONS, + COURSES_LEGACY_LIMITED_STAFF_ROLE_PERMISSIONS, ] COURSE_DATA_RESEARCHER_PERMISSIONS = [ - permissions.COURSES_LEGACY_DATA_RESEARCHER_PERMISSIONS, + COURSES_LEGACY_DATA_RESEARCHER_PERMISSIONS, ] COURSE_BETA_TESTER_PERMISSIONS = [ - permissions.COURSES_LEGACY_BETA_TESTER_PERMISSIONS, + COURSES_LEGACY_BETA_TESTER_PERMISSIONS, ] diff --git a/src/openedx_authz/tests/test_commands.py b/src/openedx_authz/tests/test_commands.py index e8fef73b..20f9c34a 100644 --- a/src/openedx_authz/tests/test_commands.py +++ b/src/openedx_authz/tests/test_commands.py @@ -17,13 +17,13 @@ from openedx_authz import api as authz_api from openedx_authz.api.data import ContentLibraryData -from openedx_authz.constants.permissions import ( +from openedx_authz.authz import ( DELETE_LIBRARY, + LIBRARY_ADMIN, MANAGE_LIBRARY_TEAM, PUBLISH_LIBRARY_CONTENT, VIEW_LIBRARY, ) -from openedx_authz.constants.roles import LIBRARY_ADMIN from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.engine.schema.translation import SchemaTranslationExtractionError from openedx_authz.management.commands.load_policies import Command as LoadPoliciesCommand diff --git a/src/openedx_authz/tests/test_enforcement.py b/src/openedx_authz/tests/test_enforcement.py index 93dd82b0..87927d92 100644 --- a/src/openedx_authz/tests/test_enforcement.py +++ b/src/openedx_authz/tests/test_enforcement.py @@ -21,10 +21,13 @@ ContentLibraryData, CourseOverviewData, ) -from openedx_authz.constants import roles -from openedx_authz.constants.permissions import ( +from openedx_authz.authz import ( + COURSE_ADMIN, + COURSE_STAFF, COURSES_CREATE_FILES, COURSES_VIEW_COURSE, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, MANAGE_LIBRARY_TEAM, VIEW_LIBRARY, ) @@ -278,7 +281,7 @@ class RoleAssignmentTests(CasbinEnforcementTestCase): ], [ "p", - make_role_key(roles.LIBRARY_ADMIN.external_key), + make_role_key(LIBRARY_ADMIN.external_key), make_action_key("manage"), make_scope_key("lib", GLOBAL_SCOPE_WILDCARD), "allow", @@ -299,7 +302,7 @@ class RoleAssignmentTests(CasbinEnforcementTestCase): ], [ "p", - make_role_key(roles.LIBRARY_AUTHOR.external_key), + make_role_key(LIBRARY_AUTHOR.external_key), make_action_key("write"), make_scope_key("lib", GLOBAL_SCOPE_WILDCARD), "allow", @@ -333,7 +336,7 @@ class RoleAssignmentTests(CasbinEnforcementTestCase): [ "g", make_user_key("user-6"), - make_role_key(roles.LIBRARY_ADMIN.external_key), + make_role_key(LIBRARY_ADMIN.external_key), make_library_key("lib:DemoX:CSPROB"), ], [ @@ -351,7 +354,7 @@ class RoleAssignmentTests(CasbinEnforcementTestCase): [ "g", make_user_key("user-9"), - make_role_key(roles.LIBRARY_AUTHOR.external_key), + make_role_key(LIBRARY_AUTHOR.external_key), make_library_key("lib:DemoX:CSPROB"), ], ] + COMMON_ACTION_GROUPING @@ -501,21 +504,21 @@ class WildcardScopeTests(CasbinEnforcementTestCase): # Policies [ "p", - make_role_key(roles.COURSE_ADMIN.external_key), + make_role_key(COURSE_ADMIN.external_key), make_action_key("manage"), make_wildcard_key(CourseOverviewData.NAMESPACE), "allow", ], [ "p", - make_role_key(roles.LIBRARY_ADMIN.external_key), + make_role_key(LIBRARY_ADMIN.external_key), make_action_key("manage"), make_wildcard_key(ContentLibraryData.NAMESPACE), "allow", ], # Role assignments - ["g", make_user_key("user-1"), make_role_key(roles.COURSE_ADMIN.external_key), GLOBAL_SCOPE_WILDCARD], - ["g", make_user_key("user-2"), make_role_key(roles.LIBRARY_ADMIN.external_key), GLOBAL_SCOPE_WILDCARD], + ["g", make_user_key("user-1"), make_role_key(COURSE_ADMIN.external_key), GLOBAL_SCOPE_WILDCARD], + ["g", make_user_key("user-2"), make_role_key(LIBRARY_ADMIN.external_key), GLOBAL_SCOPE_WILDCARD], ] + COMMON_ACTION_GROUPING @data( @@ -559,12 +562,10 @@ class OrgGlobCourseEnforcementTests(CasbinEnforcementTestCase): scopes that belong to those organizations. """ - POLICIES = [ - make_policy(roles.COURSE_STAFF.external_key, COURSES_VIEW_COURSE.identifier, CourseOverviewData.NAMESPACE) - ] + POLICIES = [make_policy(COURSE_STAFF.external_key, COURSES_VIEW_COURSE.identifier, CourseOverviewData.NAMESPACE)] ASSIGNMENTS = [ - make_course_assignment("user1", roles.COURSE_STAFF.external_key, "course-v1:OpenedX+*"), + make_course_assignment("user1", COURSE_STAFF.external_key, "course-v1:OpenedX+*"), ] CASES = [ @@ -596,11 +597,11 @@ class OrgGlobLibraryEnforcementTests(CasbinEnforcementTestCase): """ POLICIES = [ - make_policy(roles.LIBRARY_ADMIN.external_key, VIEW_LIBRARY.identifier, ContentLibraryData.NAMESPACE), + make_policy(LIBRARY_ADMIN.external_key, VIEW_LIBRARY.identifier, ContentLibraryData.NAMESPACE), ] ASSIGNMENTS = [ - make_library_assignment("user1", roles.LIBRARY_ADMIN.external_key, "lib:DemoX:*"), + make_library_assignment("user1", LIBRARY_ADMIN.external_key, "lib:DemoX:*"), ] CASES = [ @@ -631,12 +632,10 @@ class PlatformGlobCourseEnforcementTests(CasbinEnforcementTestCase): all organizations on the platform. """ - POLICIES = [ - make_policy(roles.COURSE_STAFF.external_key, COURSES_VIEW_COURSE.identifier, CourseOverviewData.NAMESPACE) - ] + POLICIES = [make_policy(COURSE_STAFF.external_key, COURSES_VIEW_COURSE.identifier, CourseOverviewData.NAMESPACE)] ASSIGNMENTS = [ - make_course_assignment("user1", roles.COURSE_STAFF.external_key, "course-v1:*"), + make_course_assignment("user1", COURSE_STAFF.external_key, "course-v1:*"), ] CASES = [ @@ -667,12 +666,12 @@ class PlatformGlobLibraryEnforcementTests(CasbinEnforcementTestCase): """ POLICIES = [ - make_policy(roles.LIBRARY_ADMIN.external_key, VIEW_LIBRARY.identifier, ContentLibraryData.NAMESPACE), - make_policy(roles.LIBRARY_ADMIN.external_key, MANAGE_LIBRARY_TEAM.identifier, ContentLibraryData.NAMESPACE), + make_policy(LIBRARY_ADMIN.external_key, VIEW_LIBRARY.identifier, ContentLibraryData.NAMESPACE), + make_policy(LIBRARY_ADMIN.external_key, MANAGE_LIBRARY_TEAM.identifier, ContentLibraryData.NAMESPACE), ] ASSIGNMENTS = [ - make_library_assignment("user1", roles.LIBRARY_ADMIN.external_key, "lib:*"), + make_library_assignment("user1", LIBRARY_ADMIN.external_key, "lib:*"), ] CASES = [ diff --git a/src/openedx_authz/tests/test_engine_utils.py b/src/openedx_authz/tests/test_engine_utils.py index c982e7c7..fc5b34e2 100644 --- a/src/openedx_authz/tests/test_engine_utils.py +++ b/src/openedx_authz/tests/test_engine_utils.py @@ -16,7 +16,16 @@ from django.test import TestCase from openedx_authz import ROOT_DIRECTORY -from openedx_authz.constants import permissions, roles +from openedx_authz.authz import ( + DELETE_LIBRARY, + EDIT_LIBRARY_CONTENT, + LIBRARY_ADMIN, + LIBRARY_AUTHOR, + LIBRARY_USER, + MANAGE_LIBRARY_TEAM, + VIEW_LIBRARY, + VIEW_LIBRARY_TEAM, +) from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.engine.utils import migrate_policy_between_enforcers from openedx_authz.tests.test_utils import make_action_key, make_role_key, make_scope_key, make_user_key @@ -56,28 +65,27 @@ def setUp(self): """ policy_file = NamedTemporaryFile(mode="w", suffix=".policy", delete=False) policy_file.write( - f"p, {make_role_key(roles.LIBRARY_ADMIN.external_key)}, " - f"{make_action_key(permissions.DELETE_LIBRARY.identifier)}, {make_scope_key('lib', '*')}, allow\n" + f"p, {make_role_key(LIBRARY_ADMIN.external_key)}, " + f"{make_action_key(DELETE_LIBRARY.identifier)}, {make_scope_key('lib', '*')}, allow\n" ) policy_file.write( - f"p, {make_role_key(roles.LIBRARY_USER.external_key)}, " - f"{make_action_key(permissions.VIEW_LIBRARY.identifier)}, {make_scope_key('lib', '*')}, allow\n" + f"p, {make_role_key(LIBRARY_USER.external_key)}, " + f"{make_action_key(VIEW_LIBRARY.identifier)}, {make_scope_key('lib', '*')}, allow\n" ) policy_file.write( - f"p, {make_role_key(roles.LIBRARY_AUTHOR.external_key)}, " - f"{make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier)}, {make_scope_key('lib', '*')}, allow\n" + f"p, {make_role_key(LIBRARY_AUTHOR.external_key)}, " + f"{make_action_key(EDIT_LIBRARY_CONTENT.identifier)}, {make_scope_key('lib', '*')}, allow\n" ) policy_file.write( - f"g2, {make_action_key(permissions.DELETE_LIBRARY.identifier)}, " - f"{make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier)}\n" + f"g2, {make_action_key(DELETE_LIBRARY.identifier)}, " + f"{make_action_key(EDIT_LIBRARY_CONTENT.identifier)}\n" ) policy_file.write( - f"g2, {make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier)}, " - f"{make_action_key(permissions.VIEW_LIBRARY.identifier)}\n" + f"g2, {make_action_key(EDIT_LIBRARY_CONTENT.identifier)}, " f"{make_action_key(VIEW_LIBRARY.identifier)}\n" ) policy_file.write( - f"g2, {make_action_key(permissions.MANAGE_LIBRARY_TEAM.identifier)}, " - f"{make_action_key(permissions.VIEW_LIBRARY_TEAM.identifier)}\n" + f"g2, {make_action_key(MANAGE_LIBRARY_TEAM.identifier)}, " + f"{make_action_key(VIEW_LIBRARY_TEAM.identifier)}\n" ) policy_file.close() self.addCleanup(os.unlink, policy_file.name) @@ -122,8 +130,8 @@ def test_migrate_all_file_policies_to_database(self): self.assertIn( [ - make_role_key(roles.LIBRARY_ADMIN.external_key), - make_action_key(permissions.DELETE_LIBRARY.identifier), + make_role_key(LIBRARY_ADMIN.external_key), + make_action_key(DELETE_LIBRARY.identifier), make_scope_key("lib", "*"), "allow", ], @@ -131,8 +139,8 @@ def test_migrate_all_file_policies_to_database(self): ) self.assertIn( [ - make_role_key(roles.LIBRARY_USER.external_key), - make_action_key(permissions.VIEW_LIBRARY.identifier), + make_role_key(LIBRARY_USER.external_key), + make_action_key(VIEW_LIBRARY.identifier), make_scope_key("lib", "*"), "allow", ], @@ -181,15 +189,15 @@ def test_migrate_action_inheritance_from_file(self): # Verify a sample of expected g2 rules from the file self.assertIn( [ - make_action_key(permissions.DELETE_LIBRARY.identifier), - make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier), + make_action_key(DELETE_LIBRARY.identifier), + make_action_key(EDIT_LIBRARY_CONTENT.identifier), ], target_g2, ) self.assertIn( [ - make_action_key(permissions.MANAGE_LIBRARY_TEAM.identifier), - make_action_key(permissions.VIEW_LIBRARY_TEAM.identifier), + make_action_key(MANAGE_LIBRARY_TEAM.identifier), + make_action_key(VIEW_LIBRARY_TEAM.identifier), ], target_g2, ) @@ -268,8 +276,8 @@ def test_migrate_partial_duplicates(self): - Mixed state is handled correctly """ self.target_enforcer.add_policy( - make_role_key(roles.LIBRARY_ADMIN.external_key), - make_action_key(permissions.DELETE_LIBRARY.identifier), + make_role_key(LIBRARY_ADMIN.external_key), + make_action_key(DELETE_LIBRARY.identifier), make_scope_key("lib", "*"), "allow", ) @@ -293,18 +301,18 @@ def test_migrate_partial_duplicates(self): @data( ( - make_role_key(roles.LIBRARY_ADMIN.external_key), - make_action_key(permissions.DELETE_LIBRARY.identifier), + make_role_key(LIBRARY_ADMIN.external_key), + make_action_key(DELETE_LIBRARY.identifier), make_scope_key("lib", "*"), ), ( - make_role_key(roles.LIBRARY_USER.external_key), - make_action_key(permissions.VIEW_LIBRARY.identifier), + make_role_key(LIBRARY_USER.external_key), + make_action_key(VIEW_LIBRARY.identifier), make_scope_key("lib", "*"), ), ( - make_role_key(roles.LIBRARY_AUTHOR.external_key), - make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier), + make_role_key(LIBRARY_AUTHOR.external_key), + make_action_key(EDIT_LIBRARY_CONTENT.identifier), make_scope_key("lib", "*"), ), ) @@ -327,16 +335,16 @@ def test_migrate_specific_file_policies(self, role, action, scope): @data( ( - make_action_key(permissions.DELETE_LIBRARY.identifier), - make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier), + make_action_key(DELETE_LIBRARY.identifier), + make_action_key(EDIT_LIBRARY_CONTENT.identifier), ), ( - make_action_key(permissions.EDIT_LIBRARY_CONTENT.identifier), - make_action_key(permissions.VIEW_LIBRARY.identifier), + make_action_key(EDIT_LIBRARY_CONTENT.identifier), + make_action_key(VIEW_LIBRARY.identifier), ), ( - make_action_key(permissions.MANAGE_LIBRARY_TEAM.identifier), - make_action_key(permissions.VIEW_LIBRARY_TEAM.identifier), + make_action_key(MANAGE_LIBRARY_TEAM.identifier), + make_action_key(VIEW_LIBRARY_TEAM.identifier), ), ) @unpack @@ -390,12 +398,12 @@ def test_migrate_preserves_user_role_assignments_in_db(self): """ self.target_enforcer.add_grouping_policy( make_user_key("user-1"), - make_role_key(roles.LIBRARY_ADMIN.external_key), + make_role_key(LIBRARY_ADMIN.external_key), make_scope_key("lib", "demo"), ) self.target_enforcer.add_grouping_policy( make_user_key("user-2"), - make_role_key(roles.LIBRARY_USER.external_key), + make_role_key(LIBRARY_USER.external_key), make_scope_key("lib", "*"), ) @@ -406,7 +414,7 @@ def test_migrate_preserves_user_role_assignments_in_db(self): self.assertIn( [ make_user_key("user-1"), - make_role_key(roles.LIBRARY_ADMIN.external_key), + make_role_key(LIBRARY_ADMIN.external_key), make_scope_key("lib", "demo"), ], target_grouping,