# Copyright (C) 2025 Cetmix OÜ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.exceptions import AccessError from .common_jets import TestTowerJetsCommon class TestTowerJetDependencyAccess(TestTowerJetsCommon): """ Test access rules for Jet Dependency model """ # ====================== # Manager Read Access Tests # ====================== def test_manager_read_access_both_user_ids(self): """Test Manager: Read when in user_ids of both jets""" _, _, dependency = self._create_jet_dependency( "Jet 1", "jet_1", "Jet 2", "jet_2", jet_user_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 1, "Manager should read when in user_ids of both jets", ) self.assertIn( dependency, records, "Manager should get exactly the dependency record we searched for", ) def test_manager_read_access_both_manager_ids(self): """Test Manager: Read when in manager_ids of both jets""" _, _, dependency = self._create_jet_dependency( "Jet Manager 1", "jet_manager_1", "Jet Manager 2", "jet_manager_2", jet_manager_ids=[(4, self.manager.id)], depends_on_manager_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 1, "Manager should read when in manager_ids of both jets", ) self.assertIn( dependency, records, "Manager should get exactly the dependency record we searched for", ) def test_manager_read_access_jet_user_depends_manager(self): """Test Manager: Read when in user_ids of jet and manager_ids of depends""" _, _, dependency = self._create_jet_dependency( "Jet User", "jet_user", "Depends Manager", "depends_manager", jet_user_ids=[(4, self.manager.id)], depends_on_manager_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 1, "Manager should read when in user_ids of jet and manager_ids of depends", ) self.assertIn( dependency, records, "Manager should get exactly the dependency record we searched for", ) def test_manager_read_access_jet_manager_depends_user(self): """Test Manager: Read when in manager_ids of jet and user_ids of depends""" _, _, dependency = self._create_jet_dependency( "Jet Manager", "jet_manager", "Depends User", "depends_user", jet_manager_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 1, "Manager should read when in manager_ids of jet and user_ids of depends", ) self.assertIn( dependency, records, "Manager should get exactly the dependency record we searched for", ) def test_manager_read_no_access_jet_only(self): """Test Manager: No read when in jet but NOT in depends on jet""" _, _, dependency = self._create_jet_dependency( "Jet Has Access", "jet_has_access", "Depends No Access", "depends_no_access", jet_user_ids=[(4, self.manager.id)], depends_on_user_ids=[(5, 0, 0)], depends_on_manager_ids=[(5, 0, 0)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 0, "Manager should not read when not in depends_on" " jet user_ids or manager_ids", ) def test_manager_read_no_access_depends_only(self): """Test Manager: No read when in depends on jet but NOT in jet""" _, _, dependency = self._create_jet_dependency( "Jet No Access", "jet_no_access", "Depends Has Access", "depends_has_access", jet_user_ids=[(5, 0, 0)], jet_manager_ids=[(5, 0, 0)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) records = self.JetDependency.with_user(self.manager).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 0, "Manager should not read when not in jet user_ids or manager_ids", ) # ====================== # Manager CRUD Access Tests # ====================== def test_manager_write_access(self): """ Test Manager: Write access when in manager_ids of jet AND user_ids or manager_ids of depends. """ # Test with depends_on user_ids (same conditions as create test, # but tests write access on existing record) _, _, dependency1 = self._create_jet_dependency( "Write Jet Manager", "write_jet_manager", "Depends User", "depends_user", jet_manager_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) # Verify manager can access the dependency (write permissions allow read access) try: dependency1.invalidate_recordset() dependency1.with_user(self.manager).read(["jet_id", "jet_depends_on_id"]) # Perform an actual write: switch to an alternative valid depends_on jet depends_on_jet_alt = self._create_jet( "Depends User Alt", "depends_user_alt", template=self.jet_template_tower_core, user_ids=[(4, self.manager.id)], server_user_ids=[(4, self.manager.id)], ) dependency1.with_user(self.manager).write( {"jet_depends_on_id": depends_on_jet_alt.id} ) except AccessError: self.fail( "Manager should be able to write when in jet manager_ids " "AND depends_on user_ids" ) # Test with depends_on manager_ids - use different templates # to avoid duplicate template dependency _, _, dependency2 = self._create_jet_dependency( "Write Jet Manager 2", "write_jet_manager_2", "Depends Manager", "depends_manager", jet_manager_ids=[(4, self.manager.id)], depends_on_manager_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], jet_template=self.jet_template_nginx, # Use different template to avoid duplicate depends_on_template=self.jet_template_docker, ) try: dependency2.invalidate_recordset() dependency2.with_user(self.manager).read(["jet_id", "jet_depends_on_id"]) # Perform an actual write: switch to an alternative valid depends_on jet depends_on_jet_alt2 = self._create_jet( "Depends Manager Alt", "depends_manager_alt", template=self.jet_template_docker, manager_ids=[(4, self.manager.id)], server_user_ids=[(4, self.manager.id)], ) dependency2.with_user(self.manager).write( {"jet_depends_on_id": depends_on_jet_alt2.id} ) except AccessError: self.fail( "Manager should be able to write when in jet manager_ids" " AND depends_on manager_ids" ) def test_manager_create_access(self): """ Test Manager: Create when in manager_ids of jet AND user_ids or manager_ids of depends. """ # Try to create dependency as manager # (helper ensures proper template dependency) try: _, _, dependency = self._create_jet_dependency( "Create Jet", "create_jet", "Create Depends", "create_depends", jet_manager_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], with_user=self.manager, jet_template=self.jet_template_test, depends_on_template=self.jet_template_tower_core, ) records = self.JetDependency.search([("id", "=", dependency.id)]) self.assertIn( dependency, records, "Manager should be able to create dependency", ) except AccessError: self.fail("Manager should be able to create when in jet manager_ids") def test_manager_create_forbidden_not_in_jet_manager_ids(self): """Test Manager: Cannot create when not in jet manager_ids""" # Should not be able to create (manager not in jet manager_ids) self.assertRaises( AccessError, lambda: self._create_jet_dependency( "No Create Jet", "no_create_jet", "No Create Depends", "no_create_depends", jet_user_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], with_user=self.manager, jet_template=self.jet_template_test, depends_on_template=self.jet_template_tower_core, ), ) def test_manager_create_forbidden_not_in_depends(self): """ Test Manager: Cannot create when in jet manager_ids but NOT in depends. """ # Should not be able to create (manager has no access to depends) self.assertRaises( AccessError, lambda: self._create_jet_dependency( "Create Jet", "create_jet", "No Depends Access", "no_depends_access", jet_manager_ids=[(4, self.manager.id)], depends_on_user_ids=[(5, 0, 0)], depends_on_manager_ids=[(5, 0, 0)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], with_user=self.manager, jet_template=self.jet_template_test, depends_on_template=self.jet_template_tower_core, ), ) def test_manager_unlink_access(self): """ Test Manager: Delete when in manager_ids of jet AND user_ids or manager_ids of depends. """ _, _, dependency = self._create_jet_dependency( "Delete Jet", "delete_jet", "Delete Depends", "delete_depends", jet_manager_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], with_user=self.manager, ) # Refresh dependency in manager context to ensure access dependency.invalidate_recordset() dependency = dependency.with_user(self.manager) try: dependency.unlink() records = self.JetDependency.search([("id", "=", dependency.id)]) self.assertEqual( len(records), 0, "Manager should be able to delete dependency", ) except AccessError: self.fail("Manager should be able to delete dependency") def test_manager_unlink_forbidden_not_in_jet_manager_ids(self): """Test Manager: Cannot delete when not in jet manager_ids""" _, _, dependency = self._create_jet_dependency( "No Delete Jet", "no_delete_jet", "No Delete Depends", "no_delete_depends", jet_user_ids=[(4, self.manager.id)], depends_on_user_ids=[(4, self.manager.id)], jet_server_user_ids=[(4, self.manager.id)], depends_on_server_user_ids=[(4, self.manager.id)], ) self.assertRaises(AccessError, dependency.with_user(self.manager).unlink) # ====================== # Root Access Tests # ====================== def test_root_full_access(self): """Test Root: Full CRUD access regardless of access restrictions""" # Root can create dependency via helper regardless of access _, _, dependency = self._create_jet_dependency( "Root Jet", "root_jet", "Root Depends", "root_depends", jet_user_ids=[(5, 0, 0)], jet_manager_ids=[(5, 0, 0)], depends_on_user_ids=[(5, 0, 0)], depends_on_manager_ids=[(5, 0, 0)], with_user=self.root, jet_template=self.jet_template_test, depends_on_template=self.jet_template_tower_core, ) # Root can read records = self.JetDependency.with_user(self.root).search( [("id", "=", dependency.id)] ) self.assertIn(dependency, records, "Root should be able to read") # Root can write: switch depends_on to another valid jet depends_on_jet_alt = self._create_jet( "Root Depends Alt", "root_depends_alt", template=self.jet_template_tower_core, ) dependency.invalidate_recordset() dependency.with_user(self.root).write( {"jet_depends_on_id": depends_on_jet_alt.id} ) # Root can delete dependency.with_user(self.root).unlink() records = self.JetDependency.with_user(self.root).search( [("id", "=", dependency.id)] ) self.assertEqual( len(records), 0, "Root should be able to delete dependency", )