From 68ca5feaec1f0b045591d694cbc4a712864e9275 Mon Sep 17 00:00:00 2001 From: git_admin Date: Mon, 27 Apr 2026 08:18:21 +0000 Subject: [PATCH] Tower: upload cetmix_tower_server 16.0.3.0.1 (via marketplace) --- .../tests/test_jet_dependency_access.py | 420 ++++++++++++++++++ 1 file changed, 420 insertions(+) create mode 100644 addons/cetmix_tower_server/tests/test_jet_dependency_access.py diff --git a/addons/cetmix_tower_server/tests/test_jet_dependency_access.py b/addons/cetmix_tower_server/tests/test_jet_dependency_access.py new file mode 100644 index 0000000..43c5489 --- /dev/null +++ b/addons/cetmix_tower_server/tests/test_jet_dependency_access.py @@ -0,0 +1,420 @@ +# 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", + )