diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fb46589b..ea9c849d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,20 @@ Unreleased * +0.5.0 - 2025-10-21 +****************** + +Added +===== + +* Default policy for Content Library roles and permissions. + +Fixed +===== + +* Add plugin_settings in test settings. +* Update permissions for RoleListView. + 0.4.1 - 2025-10-16 ****************** diff --git a/openedx_authz/__init__.py b/openedx_authz/__init__.py index f8857e48..7fb03111 100644 --- a/openedx_authz/__init__.py +++ b/openedx_authz/__init__.py @@ -4,6 +4,6 @@ import os -__version__ = "0.4.1" +__version__ = "0.5.0" ROOT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) diff --git a/openedx_authz/engine/config/authz.policy b/openedx_authz/engine/config/authz.policy index 6c2c6759..d167b388 100644 --- a/openedx_authz/engine/config/authz.policy +++ b/openedx_authz/engine/config/authz.policy @@ -9,52 +9,62 @@ # For role definitions use: lib^*, course^*, org^* to specify the scope of the role # Library Admin Role Policies -p, role^library_admin, act^delete_library, lib^*, allow -p, role^library_admin, act^publish_library, lib^*, allow -p, role^library_admin, act^manage_library_team, lib^*, allow +p, role^library_admin, act^view_library, lib^*, allow p, role^library_admin, act^manage_library_tags, lib^*, allow -p, role^library_admin, act^delete_library_content, lib^*, allow +p, role^library_admin, act^delete_library, lib^*, allow +p, role^library_admin, act^edit_library_content, lib^*, allow p, role^library_admin, act^publish_library_content, lib^*, allow -p, role^library_admin, act^delete_library_collection, lib^*, allow -p, role^library_admin, act^create_library, lib^*, allow +p, role^library_admin, act^reuse_library_content, lib^*, allow +p, role^library_admin, act^view_library_team, lib^*, allow +p, role^library_admin, act^manage_library_team, lib^*, allow p, role^library_admin, act^create_library_collection, lib^*, allow +p, role^library_admin, act^edit_library_collection, lib^*, allow +p, role^library_admin, act^delete_library_collection, lib^*, allow # Library Author Role Policies -p, role^library_author, act^delete_library_content, lib^*, allow -p, role^library_author, act^publish_library_content, lib^*, allow -p, role^library_author, act^edit_library, lib^*, allow +p, role^library_author, act^view_library, lib^*, allow p, role^library_author, act^manage_library_tags, lib^*, allow +p, role^library_author, act^edit_library_content, lib^*, allow +p, role^library_author, act^publish_library_content, lib^*, allow +p, role^library_author, act^reuse_library_content, lib^*, allow +p, role^library_author, act^view_library_team, lib^*, allow p, role^library_author, act^create_library_collection, lib^*, allow p, role^library_author, act^edit_library_collection, lib^*, allow p, role^library_author, act^delete_library_collection, lib^*, allow -# Library Collaborator Role Policies -p, role^library_collaborator, act^edit_library, lib^*, allow -p, role^library_collaborator, act^delete_library_content, lib^*, allow -p, role^library_collaborator, act^manage_library_tags, lib^*, allow -p, role^library_collaborator, act^create_library_collection, lib^*, allow -p, role^library_collaborator, act^edit_library_collection, lib^*, allow -p, role^library_collaborator, act^delete_library_collection, lib^*, allow +# Library Contributor Role Policies +p, role^library_contributor, act^view_library, lib^*, allow +p, role^library_contributor, act^manage_library_tags, lib^*, allow +p, role^library_contributor, act^edit_library_content, lib^*, allow +p, role^library_contributor, act^reuse_library_content, lib^*, allow +p, role^library_contributor, act^view_library_team, lib^*, allow +p, role^library_contributor, act^create_library_collection, lib^*, allow +p, role^library_contributor, act^edit_library_collection, lib^*, allow +p, role^library_contributor, act^delete_library_collection, lib^*, allow # Library User Role Policies p, role^library_user, act^view_library, lib^*, allow -p, role^library_user, act^view_library_team, lib^*, allow p, role^library_user, act^reuse_library_content, lib^*, allow +p, role^library_user, act^view_library_team, lib^*, allow # Action Inheritance (g2) - format: g2 = granted_action, implied_action # Higher-level permissions automatically grant lower-level permissions # If a user has the granted_action, they also have the implied_action # Example: g2, act^delete_library, act^view_library means delete permission includes view permission -g2, act^delete_library, act^view_library -g2, act^edit_library, act^view_library -g2, act^create_library, act^view_library -g2, act^publish_library, act^view_library +# Library +g2, act^manage_library_tags, act^edit_library_content +g2, act^delete_library, act^edit_library_content + +# Content +g2, act^publish_library_content, act^edit_library_content +g2, act^edit_library_content, act^view_library +g2, act^reuse_library_content, act^view_library +g2, act^publish_library_content, act^view_library + +# Team g2, act^manage_library_team, act^view_library_team -g2, act^manage_library_tags, act^view_library_tags + +# Collections g2, act^delete_library_collection, act^edit_library_collection -g2, act^edit_library_collection, act^view_library_collection g2, act^create_library_collection, act^edit_library_collection -g2, act^edit_library_content, act^view_library_content -g2, act^delete_library_content, act^edit_library_content -g2, act^publish_library_content, act^view_library_content -g2, act^reuse_library_content, act^view_library_content +g2, act^edit_library_collection, act^view_library diff --git a/openedx_authz/management/commands/load_policies.py b/openedx_authz/management/commands/load_policies.py index bb00439c..57ca79de 100644 --- a/openedx_authz/management/commands/load_policies.py +++ b/openedx_authz/management/commands/load_policies.py @@ -9,6 +9,7 @@ import os import casbin +import click from django.core.management.base import BaseCommand from openedx_authz import ROOT_DIRECTORY @@ -49,6 +50,11 @@ def add_arguments(self, parser) -> None: default=None, help="Path to the Casbin model configuration file", ) + parser.add_argument( + "--clear-existing", + action="store_true", + help="Flag to clear existing policies before loading new ones", + ) def handle(self, *args, **options): """Execute the policy loading command. @@ -73,8 +79,21 @@ def handle(self, *args, **options): ROOT_DIRECTORY, "engine", "config", "model.conf" ) + target_enforcer = AuthzEnforcer.get_enforcer() + + if options.get("clear_existing"): + target_enforcer.load_policy() + if click.confirm(click.style('Do you want to delete existing roles? ' + '(This will also delete the assignments related to those roles)', + fg='yellow', bold=True), default=False): + self._delete_existing_roles(target_enforcer) + + if click.confirm(click.style('Do you want to delete existing permissions inheritance?', + fg='yellow', bold=True), default=False): + self._delete_permissions_inheritance(target_enforcer) + source_enforcer = casbin.Enforcer(model_file_path, policy_file_path) - self.migrate_policies(source_enforcer, AuthzEnforcer.get_enforcer()) + self.migrate_policies(source_enforcer, target_enforcer) def migrate_policies(self, source_enforcer, target_enforcer): """Migrate policies from the source enforcer to the target enforcer. @@ -88,3 +107,27 @@ def migrate_policies(self, source_enforcer, target_enforcer): target_enforcer: The Casbin enforcer instance to migrate policies to. """ migrate_policy_between_enforcers(source_enforcer, target_enforcer) + + def _delete_existing_roles(self, target_enforcer): + """Delete existing roles from the target enforcer. + + Args: + target_enforcer: The Casbin enforcer instance to delete roles from. + """ + list_of_roles = target_enforcer.get_all_subjects() + for role in list_of_roles: + result = target_enforcer.delete_role(role) + if result: + click.echo(f"Deleted role: {role}") + + def _delete_permissions_inheritance(self, target_enforcer): + """Delete existing permissions inheritance from the target enforcer. + + Args: + target_enforcer: The Casbin enforcer instance to delete permissions inheritance from. + """ + list_of_permissions = target_enforcer.get_named_grouping_policy("g2") + for permission in list(list_of_permissions): + result = target_enforcer.remove_named_grouping_policy("g2", *permission) + if result: + click.echo(f"Deleted permission inheritance: {permission}") diff --git a/openedx_authz/tests/api/test_roles.py b/openedx_authz/tests/api/test_roles.py index e7da8fd5..f2b898cc 100644 --- a/openedx_authz/tests/api/test_roles.py +++ b/openedx_authz/tests/api/test_roles.py @@ -10,15 +10,7 @@ from ddt import ddt, unpack from django.test import TestCase -from openedx_authz.api.data import ( - ActionData, - ContentLibraryData, - PermissionData, - RoleAssignmentData, - RoleData, - ScopeData, - SubjectData, -) +from openedx_authz.api.data import ContentLibraryData, RoleAssignmentData, RoleData, ScopeData, SubjectData from openedx_authz.api.roles import ( assign_role_to_subject_in_scope, batch_assign_role_to_subjects_in_scope, @@ -33,6 +25,12 @@ ) from openedx_authz.engine.enforcer import AuthzEnforcer from openedx_authz.engine.utils import migrate_policy_between_enforcers +from openedx_authz.tests.constants import ( + LIST_LIBRARY_ADMIN_PERMISSIONS, + LIST_LIBRARY_AUTHOR_PERMISSIONS, + LIST_LIBRARY_CONTRIBUTOR_PERMISSIONS, + LIST_LIBRARY_USER_PERMISSIONS, +) class BaseRolesTestCase(TestCase): @@ -120,7 +118,7 @@ def setUpClass(cls): }, { "subject_name": "carol", - "role_name": "library_collaborator", + "role_name": "library_contributor", "scope_name": "lib:Org1:science_301", }, { @@ -147,12 +145,12 @@ def setUpClass(cls): # Multiple subjects with same role in same scope { "subject_name": "grace", - "role_name": "library_collaborator", + "role_name": "library_contributor", "scope_name": "lib:Org1:math_advanced", }, { "subject_name": "heidi", - "role_name": "library_collaborator", + "role_name": "library_contributor", "scope_name": "lib:Org1:math_advanced", }, # Hierarchical scope assignments - different specificity levels @@ -195,7 +193,7 @@ def setUpClass(cls): }, { "subject_name": "noah", - "role_name": "library_collaborator", + "role_name": "library_contributor", "scope_name": "lib:Org5:economics_101", }, { @@ -216,7 +214,7 @@ def setUpClass(cls): }, { "subject_name": "peter", - "role_name": "library_collaborator", + "role_name": "library_contributor", "scope_name": "lib:Org6:project_gamma", }, { @@ -267,126 +265,22 @@ class TestRolesAPI(RolesTestSetupMixin): # Library Admin role with actual permissions from authz.policy ( "library_admin", - [ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - ], + LIST_LIBRARY_ADMIN_PERMISSIONS, ), # Library Author role with actual permissions from authz.policy ( "library_author", - [ - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - ], + LIST_LIBRARY_AUTHOR_PERMISSIONS, ), - # Library Collaborator role with actual permissions from authz.policy + # Library Contributor role with actual permissions from authz.policy ( - "library_collaborator", - [ - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - ], + "library_contributor", + LIST_LIBRARY_CONTRIBUTOR_PERMISSIONS, ), # Library User role with minimal permissions ( "library_user", - [ - PermissionData( - action=ActionData(external_key="view_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="view_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="reuse_library_content"), - effect="allow", - ), - ], + LIST_LIBRARY_USER_PERMISSIONS, ), # Non existent role ( @@ -413,92 +307,19 @@ def test_get_permissions_for_roles(self, role_name, expected_permissions): ( "library_user", "lib:Org1:english_101", - [ - PermissionData( - action=ActionData(external_key="view_library"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="view_library_team"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="reuse_library_content"), - effect="allow", - ), - ], + LIST_LIBRARY_USER_PERMISSIONS, ), # Role assigned to single user in single scope ( "library_author", "lib:Org1:history_201", - [ - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - ], + LIST_LIBRARY_AUTHOR_PERMISSIONS, ), # Role assigned to single user in multiple scopes ( "library_admin", "lib:Org1:math_101", - [ - PermissionData( - action=ActionData(external_key="delete_library"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="publish_library"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), effect="allow" - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - ], + LIST_LIBRARY_ADMIN_PERMISSIONS, ), ) @unpack @@ -527,7 +348,7 @@ def test_get_permissions_for_active_role_in_specific_scope( { "library_admin", "library_author", - "library_collaborator", + "library_contributor", "library_user", }, ), @@ -554,12 +375,12 @@ def test_get_roles_in_scope(self, scope_name, expected_roles): @ddt_data( ("alice", "lib:Org1:math_101", {"library_admin"}), ("bob", "lib:Org1:history_201", {"library_author"}), - ("carol", "lib:Org1:science_301", {"library_collaborator"}), + ("carol", "lib:Org1:science_301", {"library_contributor"}), ("dave", "lib:Org1:english_101", {"library_user"}), ("eve", "lib:Org2:physics_401", {"library_admin"}), ("eve", "lib:Org2:chemistry_501", {"library_author"}), ("eve", "lib:Org2:biology_601", {"library_user"}), - ("grace", "lib:Org1:math_advanced", {"library_collaborator"}), + ("grace", "lib:Org1:math_advanced", {"library_contributor"}), ("ivy", "lib:Org3:cs_101", {"library_admin"}), ("jack", "lib:Org3:cs_101", {"library_author"}), ("kate", "lib:Org3:cs_101", {"library_user"}), @@ -567,11 +388,11 @@ def test_get_roles_in_scope(self, scope_name, expected_roles): ("liam", "lib:Org4:art_201", {"library_author"}), ("liam", "lib:Org4:art_301", {"library_author"}), ("maya", "lib:Org5:economics_101", {"library_admin"}), - ("noah", "lib:Org5:economics_101", {"library_collaborator"}), + ("noah", "lib:Org5:economics_101", {"library_contributor"}), ("olivia", "lib:Org5:economics_101", {"library_user"}), ("peter", "lib:Org6:project_alpha", {"library_admin"}), ("peter", "lib:Org6:project_beta", {"library_author"}), - ("peter", "lib:Org6:project_gamma", {"library_collaborator"}), + ("peter", "lib:Org6:project_gamma", {"library_contributor"}), ("peter", "lib:Org6:project_delta", {"library_user"}), ("non_existent_user", "lib:Org1:math_101", set()), ("alice", "lib:Org999:non_existent_scope", set()), @@ -599,44 +420,7 @@ def test_get_subject_role_assignments_in_scope( [ RoleData( external_key="library_admin", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_ADMIN_PERMISSIONS, ), ], ), @@ -645,94 +429,15 @@ def test_get_subject_role_assignments_in_scope( [ RoleData( external_key="library_admin", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_ADMIN_PERMISSIONS, ), RoleData( external_key="library_author", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library_content"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library_collection"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="delete_library_collection"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_AUTHOR_PERMISSIONS, ), RoleData( external_key="library_user", - permissions=[ - PermissionData( - action=ActionData(external_key="view_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="view_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="reuse_library_content"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_USER_PERMISSIONS, ), ], ), @@ -741,20 +446,7 @@ def test_get_subject_role_assignments_in_scope( [ RoleData( external_key="library_user", - permissions=[ - PermissionData( - action=ActionData(external_key="view_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="view_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="reuse_library_content"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_USER_PERMISSIONS, ), ], ), @@ -785,12 +477,12 @@ def test_get_all_role_assignments_scopes(self, subject_name, expected_roles): @ddt_data( ("library_admin", "lib:Org1:math_101", 1), ("library_author", "lib:Org1:history_201", 1), - ("library_collaborator", "lib:Org1:science_301", 1), + ("library_contributor", "lib:Org1:science_301", 1), ("library_user", "lib:Org1:english_101", 1), ("library_admin", "lib:Org2:physics_401", 1), ("library_author", "lib:Org2:chemistry_501", 1), ("library_user", "lib:Org2:biology_601", 1), - ("library_collaborator", "lib:Org1:math_advanced", 2), + ("library_contributor", "lib:Org1:math_advanced", 2), ("library_admin", "lib:Org3:cs_101", 1), ("library_author", "lib:Org3:cs_101", 1), ("library_user", "lib:Org3:cs_101", 1), @@ -798,11 +490,11 @@ def test_get_all_role_assignments_scopes(self, subject_name, expected_roles): ("library_author", "lib:Org4:art_201", 1), ("library_author", "lib:Org4:art_301", 1), ("library_admin", "lib:Org5:economics_101", 1), - ("library_collaborator", "lib:Org5:economics_101", 1), + ("library_contributor", "lib:Org5:economics_101", 1), ("library_user", "lib:Org5:economics_101", 1), ("library_admin", "lib:Org6:project_alpha", 1), ("library_author", "lib:Org6:project_beta", 1), - ("library_collaborator", "lib:Org6:project_gamma", 1), + ("library_contributor", "lib:Org6:project_gamma", 1), ("library_user", "lib:Org6:project_delta", 1), ("non_existent_role", "sc:any_library", 0), ("library_admin", "sc:non_existent_scope", 0), @@ -838,7 +530,7 @@ class TestRoleAssignmentAPI(RolesTestSetupMixin): (["mary", "john"], "library_user", "sc:batch_test", True), ( ["paul", "diana", "lila"], - "library_collaborator", + "library_contributor", "lib:Org1:math_advanced", True, ), @@ -850,7 +542,7 @@ class TestRoleAssignmentAPI(RolesTestSetupMixin): "lib:Org1:history_201", True, ), - ("joe", "library_collaborator", "lib:Org1:science_301", False), + ("joe", "library_contributor", "lib:Org1:science_301", False), ("nina", "library_author", "lib:Org1:english_101", False), ("oliver", "library_admin", "lib:Org1:math_101", False), ) @@ -897,7 +589,7 @@ def test_batch_assign_role_to_subjects_in_scope( (["mary", "john"], "library_user", "sc:batch_test", True), ( ["paul", "diana", "lila"], - "library_collaborator", + "library_contributor", "lib:Org1:math_advanced", True, ), @@ -909,7 +601,7 @@ def test_batch_assign_role_to_subjects_in_scope( "lib:Org1:history_201", True, ), - ("joe", "library_collaborator", "lib:Org1:science_301", False), + ("joe", "library_contributor", "lib:Org1:science_301", False), ("nina", "library_author", "lib:Org1:english_101", False), ("oliver", "library_admin", "lib:Org1:math_101", False), ) @@ -958,52 +650,7 @@ def test_unassign_role_from_subject_in_scope( subject=SubjectData(external_key="alice"), roles=[RoleData( external_key="library_admin", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="publish_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_ADMIN_PERMISSIONS, )], scope=ScopeData(external_key="lib:Org1:math_101"), ) @@ -1016,46 +663,7 @@ def test_unassign_role_from_subject_in_scope( subject=SubjectData(external_key="bob"), roles=[RoleData( external_key="library_author", - permissions=[ - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="publish_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="edit_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_AUTHOR_PERMISSIONS, )], scope=ScopeData(external_key="lib:Org1:history_201"), ) @@ -1067,41 +675,8 @@ def test_unassign_role_from_subject_in_scope( RoleAssignmentData( subject=SubjectData(external_key="carol"), roles=[RoleData( - external_key="library_collaborator", - permissions=[ - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="edit_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - ], + external_key="library_contributor", + permissions=LIST_LIBRARY_CONTRIBUTOR_PERMISSIONS, )], scope=ScopeData(external_key="lib:Org1:science_301"), ) @@ -1114,20 +689,7 @@ def test_unassign_role_from_subject_in_scope( subject=SubjectData(external_key="dave"), roles=[RoleData( external_key="library_user", - permissions=[ - PermissionData( - action=ActionData(external_key="view_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="view_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="reuse_library_content"), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_USER_PERMISSIONS, )], scope=ScopeData(external_key="lib:Org1:english_101"), ) diff --git a/openedx_authz/tests/api/test_users.py b/openedx_authz/tests/api/test_users.py index 32b7d85a..5590c1b2 100644 --- a/openedx_authz/tests/api/test_users.py +++ b/openedx_authz/tests/api/test_users.py @@ -2,14 +2,7 @@ from ddt import data, ddt, unpack -from openedx_authz.api.data import ( - ActionData, - ContentLibraryData, - PermissionData, - RoleAssignmentData, - RoleData, - UserData, -) +from openedx_authz.api.data import ContentLibraryData, RoleAssignmentData, RoleData, UserData from openedx_authz.api.users import ( assign_role_to_user_in_scope, batch_assign_role_to_users_in_scope, @@ -22,6 +15,7 @@ unassign_role_from_user, ) from openedx_authz.tests.api.test_roles import RolesTestSetupMixin +from openedx_authz.tests.constants import LIST_LIBRARY_ADMIN_PERMISSIONS, LIST_LIBRARY_AUTHOR_PERMISSIONS class UserAssignmentsSetupMixin(RolesTestSetupMixin): @@ -59,7 +53,7 @@ class TestUserRoleAssignments(UserAssignmentsSetupMixin): @data( ("john", "library_admin", "lib:Org1:math_101", False), ("jane", "library_user", "lib:Org1:english_101", False), - (["mary", "charlie"], "library_collaborator", "lib:Org1:science_301", True), + (["mary", "charlie"], "library_contributor", "lib:Org1:science_301", True), (["david", "sarah"], "library_author", "lib:Org1:history_201", True), ) @unpack @@ -92,7 +86,7 @@ def test_assign_role_to_user_in_scope(self, username, role, scope_name, batch): self.assertIn(role, role_names) @data( - (["grace"], "library_collaborator", "lib:Org1:math_advanced", True), + (["grace"], "library_contributor", "lib:Org1:math_advanced", True), (["liam", "maya"], "library_author", "lib:Org4:art_101", True), ("alice", "library_admin", "lib:Org1:math_101", False), ("bob", "library_author", "lib:Org1:history_201", False), @@ -151,7 +145,7 @@ def test_get_user_role_assignments(self, username, expected_roles): ("alice", "lib:Org1:math_101", {"library_admin"}), ("bob", "lib:Org1:history_201", {"library_author"}), ("eve", "lib:Org2:physics_401", {"library_admin"}), - ("grace", "lib:Org1:math_advanced", {"library_collaborator"}), + ("grace", "lib:Org1:math_advanced", {"library_contributor"}), ) @unpack def test_get_user_role_assignments_in_scope( @@ -173,7 +167,7 @@ def test_get_user_role_assignments_in_scope( @data( ("library_admin", "lib:Org1:math_101", {"alice"}), ("library_author", "lib:Org1:history_201", {"bob"}), - ("library_collaborator", "lib:Org1:math_advanced", {"grace", "heidi"}), + ("library_contributor", "lib:Org1:math_advanced", {"grace", "heidi"}), ) @unpack def test_get_user_role_assignments_for_role_in_scope( @@ -203,52 +197,7 @@ def test_get_user_role_assignments_for_role_in_scope( subject=UserData(external_key="alice"), roles=[RoleData( external_key="library_admin", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="publish_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_ADMIN_PERMISSIONS, )], scope=ContentLibraryData(external_key="lib:Org1:math_101"), ), @@ -261,46 +210,7 @@ def test_get_user_role_assignments_for_role_in_scope( subject=UserData(external_key="bob"), roles=[RoleData( external_key="library_author", - permissions=[ - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="publish_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="edit_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="edit_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_AUTHOR_PERMISSIONS, )], scope=ContentLibraryData(external_key="lib:Org1:history_201"), ), @@ -313,52 +223,7 @@ def test_get_user_role_assignments_for_role_in_scope( subject=UserData(external_key="eve"), roles=[RoleData( external_key="library_admin", - permissions=[ - PermissionData( - action=ActionData(external_key="delete_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="publish_library"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_team"), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="manage_library_tags"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="publish_library_content" - ), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="delete_library_collection" - ), - effect="allow", - ), - PermissionData( - action=ActionData(external_key="create_library"), - effect="allow", - ), - PermissionData( - action=ActionData( - external_key="create_library_collection" - ), - effect="allow", - ), - ], + permissions=LIST_LIBRARY_ADMIN_PERMISSIONS, )], scope=ContentLibraryData(external_key="lib:Org2:physics_401"), ), @@ -392,12 +257,12 @@ class TestUserPermissions(UserAssignmentsSetupMixin): ("alice", "delete_library", "lib:Org1:math_101", True), ("bob", "publish_library_content", "lib:Org1:history_201", True), ("eve", "manage_library_team", "lib:Org2:physics_401", True), - ("grace", "edit_library", "lib:Org1:math_advanced", True), + ("grace", "edit_library_content", "lib:Org1:math_advanced", True), ("heidi", "create_library_collection", "lib:Org1:math_advanced", True), ("charlie", "delete_library", "lib:Org1:science_301", False), ("david", "publish_library_content", "lib:Org1:history_201", False), ("mallory", "manage_library_team", "lib:Org1:math_101", False), - ("oscar", "edit_library", "lib:Org4:art_101", False), + ("oscar", "edit_library_content", "lib:Org4:art_101", False), ("peggy", "create_library_collection", "lib:Org2:physics_401", False), ) @unpack diff --git a/openedx_authz/tests/constants.py b/openedx_authz/tests/constants.py new file mode 100644 index 00000000..afb54e21 --- /dev/null +++ b/openedx_authz/tests/constants.py @@ -0,0 +1,141 @@ +""" +Constants for library roles and permissions tests. +""" + +from openedx_authz.api.data import ActionData, PermissionData + +LIST_LIBRARY_ADMIN_PERMISSIONS = [ + PermissionData( + action=ActionData(external_key="view_library"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="manage_library_tags"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="delete_library"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="publish_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="reuse_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="view_library_team"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="manage_library_team"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="create_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="delete_library_collection"), + effect="allow", + ), +] + +LIST_LIBRARY_AUTHOR_PERMISSIONS = [ + PermissionData( + action=ActionData(external_key="view_library"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="manage_library_tags"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="publish_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="reuse_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="view_library_team"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="create_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="delete_library_collection"), + effect="allow", + ), +] + +LIST_LIBRARY_CONTRIBUTOR_PERMISSIONS = [ + PermissionData( + action=ActionData(external_key="view_library"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="manage_library_tags"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="reuse_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="view_library_team"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="create_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="edit_library_collection"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="delete_library_collection"), + effect="allow", + ), +] + +LIST_LIBRARY_USER_PERMISSIONS = [ + PermissionData( + action=ActionData(external_key="view_library"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="reuse_library_content"), + effect="allow", + ), + PermissionData( + action=ActionData(external_key="view_library_team"), + effect="allow", + ), +] diff --git a/openedx_authz/tests/test_commands.py b/openedx_authz/tests/test_commands.py index 75240d2e..16ae986e 100644 --- a/openedx_authz/tests/test_commands.py +++ b/openedx_authz/tests/test_commands.py @@ -11,7 +11,9 @@ from django.core.management import call_command from django.core.management.base import CommandError +from openedx_authz import ROOT_DIRECTORY from openedx_authz.management.commands.enforcement import Command as EnforcementCommand +from openedx_authz.management.commands.load_policies import Command as LoadPoliciesCommand from openedx_authz.tests.test_utils import make_action_key, make_scope_key, make_user_key @@ -194,3 +196,190 @@ def test_interactive_request_error(self, exception: Exception): error_output = self.buffer.getvalue() self.assertIn(f"✗ Error processing request: {str(exception)}", error_output) + + +class LoadPoliciesCommandTests(TestCase): + """ + Tests for the `load_policies` Django management command. + + This test class verifies the behavior of the load_policies command, including: + - Default file path handling + - Clearing existing policies + """ + + def setUp(self): + super().setUp() + self.buffer = io.StringIO() + + @patch('openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer') + @patch('casbin.Enforcer') + @patch('os.path.join') + @patch('click.confirm') + def test_handle_with_default_paths(self, mock_confirm, mock_join, mock_casbin_enforcer, mock_get_enforcer): + """Test handle method with default policy and model paths.""" + # Setup mocks + mock_target_enforcer = Mock() + mock_get_enforcer.return_value = mock_target_enforcer + + mock_source_enforcer = Mock() + mock_casbin_enforcer.return_value = mock_source_enforcer + + policy_path = f"{ROOT_DIRECTORY}/engine/config/authz.policy" + model_path = f"{ROOT_DIRECTORY}/engine/config/model.conf" + + # Define paths that will be joined + mock_join.side_effect = ( + policy_path, + model_path, + ) + + # Create command instance + command = LoadPoliciesCommand() + command.migrate_policies = Mock() + + # Call handle method + command.handle(policy_file_path=None, model_file_path=None, clear_existing=False) + + # Assertions + mock_casbin_enforcer.assert_called_once_with( + model_path, + policy_path, + ) + mock_join.assert_any_call( + ROOT_DIRECTORY, "engine", "config", "authz.policy" + ) + mock_join.assert_any_call( + ROOT_DIRECTORY, "engine", "config", "model.conf" + ) + mock_confirm.assert_not_called() + command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer) + + @patch('openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer') + @patch('casbin.Enforcer') + @patch('click.confirm') + def test_handle_with_custom_paths(self, mock_confirm, mock_casbin_enforcer, mock_get_enforcer): + """Test handle method with custom policy and model paths.""" + # Setup mocks + mock_target_enforcer = Mock() + mock_get_enforcer.return_value = mock_target_enforcer + + mock_source_enforcer = Mock() + mock_casbin_enforcer.return_value = mock_source_enforcer + + # Create command instance + command = LoadPoliciesCommand() + command.migrate_policies = Mock() + + # Custom paths + policy_path = '/custom/path/to/policy.csv' + model_path = '/custom/path/to/model.conf' + + # Call handle method + command.handle(policy_file_path=policy_path, model_file_path=model_path, clear_existing=False) + + # Assertions + mock_casbin_enforcer.assert_called_once_with(model_path, policy_path) + mock_confirm.assert_not_called() + command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer) + + @patch('openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer') + @patch('casbin.Enforcer') + @patch('click.confirm') + @patch('click.style') + def test_handle_clear_existing_roles_confirmed( + self, mock_style, mock_confirm, mock_casbin_enforcer, mock_get_enforcer + ): + """Test handle method with clear_existing and confirmed delete roles.""" + # Setup mocks + mock_target_enforcer = Mock() + mock_get_enforcer.return_value = mock_target_enforcer + + mock_source_enforcer = Mock() + mock_casbin_enforcer.return_value = mock_source_enforcer + + # Setup click mocks + mock_style.return_value = "styled message" + mock_confirm.side_effect = [True, False] # Confirm roles, deny permissions + + # Create command instance + command = LoadPoliciesCommand() + command.migrate_policies = Mock() + command._delete_existing_roles = Mock() + command._delete_permissions_inheritance = Mock() + + # Call handle method + command.handle(policy_file_path=None, model_file_path=None, clear_existing=True) + + # Assertions + mock_target_enforcer.load_policy.assert_called_once() + mock_confirm.assert_called_with(mock_style.return_value, default=False) + command._delete_existing_roles.assert_called_once_with(mock_target_enforcer) + command._delete_permissions_inheritance.assert_not_called() + command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer) + + @patch('openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer') + @patch('casbin.Enforcer') + @patch('click.confirm') + @patch('click.style') + def test_handle_clear_existing_permissions_confirmed( + self, mock_style, mock_confirm, mock_casbin_enforcer, mock_get_enforcer + ): + """Test handle method with clear_existing and confirmed delete permissions.""" + # Setup mocks + mock_target_enforcer = Mock() + mock_get_enforcer.return_value = mock_target_enforcer + + mock_source_enforcer = Mock() + mock_casbin_enforcer.return_value = mock_source_enforcer + + # Setup click mocks + mock_style.return_value = "styled message" + mock_confirm.side_effect = [False, True] # Deny roles, confirm permissions + + # Create command instance + command = LoadPoliciesCommand() + command.migrate_policies = Mock() + command._delete_existing_roles = Mock() + command._delete_permissions_inheritance = Mock() + + # Call handle method + command.handle(policy_file_path=None, model_file_path=None, clear_existing=True) + + # Assertions + mock_target_enforcer.load_policy.assert_called_once() + mock_confirm.assert_called_with(mock_style.return_value, default=False) + command._delete_existing_roles.assert_not_called() + command._delete_permissions_inheritance.assert_called_once_with(mock_target_enforcer) + command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer) + + @patch('openedx_authz.engine.enforcer.AuthzEnforcer.get_enforcer') + @patch('casbin.Enforcer') + @patch('click.confirm') + def test_handle_clear_existing_both_denied(self, mock_confirm, mock_casbin_enforcer, mock_get_enforcer): + """Test handle method with clear_existing but denied deletions.""" + expected_mock_confirm_calls = 2 + # Setup mocks + mock_target_enforcer = Mock() + mock_get_enforcer.return_value = mock_target_enforcer + + mock_source_enforcer = Mock() + mock_casbin_enforcer.return_value = mock_source_enforcer + + # Setup click mocks + mock_confirm.side_effect = [False, False] # Deny both roles and permissions + + # Create command instance + command = LoadPoliciesCommand() + command.migrate_policies = Mock() + command._delete_existing_roles = Mock() + command._delete_permissions_inheritance = Mock() + + # Call handle method + command.handle(policy_file_path=None, model_file_path=None, clear_existing=True) + + # Assertions + mock_target_enforcer.load_policy.assert_called_once() + assert mock_confirm.call_count == expected_mock_confirm_calls + command._delete_existing_roles.assert_not_called() + command._delete_permissions_inheritance.assert_not_called() + command.migrate_policies.assert_called_once_with(mock_source_enforcer, mock_target_enforcer) diff --git a/openedx_authz/tests/test_enforcer.py b/openedx_authz/tests/test_enforcer.py index 83cb1f14..cdc97037 100644 --- a/openedx_authz/tests/test_enforcer.py +++ b/openedx_authz/tests/test_enforcer.py @@ -165,7 +165,7 @@ class TestPolicyLoadingStrategies(PolicyLoadingTestSetupMixin): "role^library_user", "role^library_admin", "role^library_author", - "role^library_collaborator", + "role^library_contributor", ] def setUp(self): diff --git a/openedx_authz/tests/test_engine_utils.py b/openedx_authz/tests/test_engine_utils.py index 8b307e08..89d3b225 100644 --- a/openedx_authz/tests/test_engine_utils.py +++ b/openedx_authz/tests/test_engine_utils.py @@ -75,10 +75,10 @@ def test_migrate_all_file_policies_to_database(self): Expected Result: - All policies from the file are loaded into the database - - The file contains 25 regular policies (p rules) + - The file contains 31 regular policies (p rules) - Policy content matches expected file content """ - expected_policy_count = 25 + expected_policy_count = 31 migrate_policy_between_enforcers(self.source_enforcer, self.target_enforcer) self.target_enforcer.load_policy() @@ -133,10 +133,10 @@ def test_migrate_action_inheritance_from_file(self): Expected Result: - All g2 rules from the file are migrated to database - - The file contains 13 g2 rules defining action hierarchies + - The file contains 10 g2 rules defining action hierarchies - Action inheritance relationships are preserved """ - expected_g2_count = 13 + expected_g2_count = 10 migrate_policy_between_enforcers(self.source_enforcer, self.target_enforcer) self.target_enforcer.load_policy() @@ -150,7 +150,7 @@ def test_migrate_action_inheritance_from_file(self): # Verify a sample of expected g2 rules from the file self.assertIn( - [make_action_key("delete_library"), make_action_key("view_library")], + [make_action_key("delete_library"), make_action_key("edit_library_content")], target_g2, ) self.assertIn( @@ -208,16 +208,16 @@ def test_migrate_complete_file_contents(self): """Test that all policy types from the file are migrated correctly. Expected Result: - - All regular policies (p) are migrated (25 rules) + - All regular policies (p) are migrated (31 rules) - No role assignments (g) - these come from database - - All action inheritance rules (g2) are migrated (13 rules) + - All action inheritance rules (g2) are migrated (10 rules) """ migrate_policy_between_enforcers(self.source_enforcer, self.target_enforcer) self.assertEqual( len(self.target_enforcer.get_policy()), - 25, - "Should have 25 regular policies from file", + 31, + "Should have 31 regular policies from file", ) self.assertEqual( len(self.target_enforcer.get_grouping_policy()), @@ -226,8 +226,8 @@ def test_migrate_complete_file_contents(self): ) self.assertEqual( len(self.target_enforcer.get_named_grouping_policy("g2")), - 13, - "Should have 13 g2 rules from file", + 10, + "Should have 10 g2 rules from file", ) def test_migrate_partial_duplicates(self): @@ -250,8 +250,8 @@ def test_migrate_partial_duplicates(self): target_policies = self.target_enforcer.get_policy() self.assertEqual( len(target_policies), - 25, - "Should have 25 policies total, with no duplicates", + 31, + "Should have 31 policies total, with no duplicates", ) duplicates = ( @@ -279,7 +279,7 @@ def test_migrate_partial_duplicates(self): ), ( make_role_key("library_author"), - make_action_key("edit_library"), + make_action_key("edit_library_content"), make_scope_key("lib", "*"), ), ) @@ -301,8 +301,8 @@ def test_migrate_specific_file_policies(self, role, action, scope): ) @data( - (make_action_key("delete_library"), make_action_key("view_library")), - (make_action_key("edit_library"), make_action_key("view_library")), + (make_action_key("delete_library"), make_action_key("edit_library_content")), + (make_action_key("edit_library_content"), make_action_key("view_library")), (make_action_key("manage_library_team"), make_action_key("view_library_team")), ) @unpack @@ -342,7 +342,7 @@ def test_migrate_preserves_existing_db_policies(self): target_policies = self.target_enforcer.get_policy() self.assertEqual( - len(target_policies), 26, "Should have 25 file policies + 1 custom policy" + len(target_policies), 32, "Should have 31 file policies + 1 custom policy" ) self.assertIn( custom_policy, target_policies, "Custom database policy should be preserved" @@ -384,5 +384,5 @@ def test_migrate_preserves_user_role_assignments_in_db(self): target_policies = self.target_enforcer.get_policy() self.assertEqual( - len(target_policies), 25, "All 25 policies from file should be loaded" + len(target_policies), 31, "All 31 policies from file should be loaded" )