# 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 TestTowerServerLog(TestTowerJetsCommon): """Test the cx.tower.server.log model access rights.""" @classmethod def setUpClass(cls): super().setUpClass() # Create test server logs cls.server_log_1 = cls.ServerLog.create( { "name": "Test Log 1", "server_id": cls.server_test_1.id, "log_type": "file", "access_level": "1", } ) cls.server_log_2 = cls.ServerLog.create( { "name": "Test Log 2", "server_id": cls.server_test_1.id, "log_type": "file", "access_level": "1", } ) # Create additional server for testing cls.server_2 = cls.Server.create( { "name": "Test Server 2", "ip_v4_address": "localhost", "ssh_username": "test2", "ssh_password": "test2", "ssh_port": 22, "user_ids": [(6, 0, [])], "manager_ids": [(6, 0, [])], } ) # Use pre-created jet_template_test and jet_test from TestTowerJetsCommon # Ensure jet_template_test has server_test_1 in server_ids cls.jet_template_test.write({"server_ids": [(4, cls.server_test_1.id)]}) # Create server logs linked to Jet cls.server_log_jet_1 = cls.ServerLog.create( { "name": "Test Jet Log 1", "server_id": cls.server_test_1.id, "jet_id": cls.jet_test.id, "log_type": "file", "access_level": "1", } ) cls.server_log_jet_2 = cls.ServerLog.create( { "name": "Test Jet Log 2", "server_id": cls.server_test_1.id, "jet_id": cls.jet_test.id, "log_type": "file", "access_level": "2", } ) # Create server logs linked to Jet Template cls.server_log_jet_template_1 = cls.ServerLog.create( { "name": "Test Jet Template Log 1", "server_id": cls.server_test_1.id, "jet_template_id": cls.jet_template_test.id, "log_type": "file", "access_level": "1", } ) cls.server_log_jet_template_2 = cls.ServerLog.create( { "name": "Test Jet Template Log 2", "server_id": cls.server_test_1.id, "jet_template_id": cls.jet_template_test.id, "log_type": "file", "access_level": "2", } ) def test_user_access(self): """Test user access to server logs""" # Add user to server's user_ids self.server_test_1.write( { "user_ids": [(6, 0, [self.user.id])], } ) # Case 1: User should be able to read when: # - access_level == "1" # - user is in server's user_ids recs = self.ServerLog.with_user(self.user).search( [("id", "in", [self.server_log_1.id, self.server_log_2.id])] ) self.assertEqual( len(recs), 2, "User should be able to read all logs with access_level '1'" " when in user_ids", ) # Case 2: User should not be able to read when not in server's user_ids self.server_test_1.write( { "user_ids": [(5, 0, 0)], # Remove all users } ) recs = self.ServerLog.with_user(self.user).search( [("id", "=", self.server_log_1.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read when not in server's user_ids", ) # Case 3: User should not be able to read when access_level > "1" self.server_test_1.write( { "user_ids": [(6, 0, [self.user.id])], } ) high_access_log = ( self.ServerLog.with_user(self.user) .sudo() .create( { "name": "High Access Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "2", } ) ) recs = self.ServerLog.with_user(self.user).search( [("id", "=", high_access_log.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read logs with access_level > '1'", ) def test_manager_access(self): """Test manager access to server logs""" # Add manager to server's manager_ids self.server_test_1.write( { "manager_ids": [(6, 0, [self.manager.id])], } ) # Case 1: Manager should be able to read when: # - access_level <= "2" # - manager is in server's manager_ids recs = self.ServerLog.with_user(self.manager).search( [("id", "in", [self.server_log_1.id, self.server_log_2.id])] ) self.assertEqual( len(recs), 2, "Manager should be able to read all logs when in manager_ids", ) # Case 2: Manager should be able to create and write when: # - access_level <= "2" # - manager is in server's manager_ids try: new_log = self.ServerLog.with_user(self.manager).create( { "name": "Manager Test Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "2", } ) except AccessError: self.fail( "Manager should be able to create logs when in server's manager_ids" ) try: new_log.write({"name": "Updated Name"}) except AccessError: self.fail( "Manager should be able to write logs when in server's manager_ids" ) self.assertEqual(new_log.name, "Updated Name") # Case 3: Manager should be able to unlink when: # - access_level <= "2" # - created by manager # - manager is in server's manager_ids try: new_log.unlink() except AccessError: self.fail( "Manager should be able to unlink own logs when in server's manager_ids" ) # Case 4: Manager should not be able to unlink logs created by others with self.assertRaises(AccessError): self.server_log_1.with_user(self.manager).unlink() # Case 5: Manager should not be able to access logs with access_level > "2" high_access_log = ( self.ServerLog.with_user(self.manager) .sudo() .create( { "name": "High Access Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "3", } ) ) recs = self.ServerLog.with_user(self.manager).search( [("id", "=", high_access_log.id)] ) self.assertEqual( len(recs), 0, "Manager should not be able to read logs with access_level > '2'", ) def test_root_access(self): """Test root user unrestricted access""" # Create test logs with various conditions test_logs = self.ServerLog.with_user(self.root).create( [ { "name": f"Root Test Log {level}", "server_id": self.server_test_1.id, "log_type": "file", "access_level": level, } for level in ["1", "2", "3"] ] ) # Root should be able to read all logs regardless of conditions recs = self.ServerLog.with_user(self.root).search([("id", "in", test_logs.ids)]) self.assertEqual( len(recs), 3, "Root should have unrestricted read access to all logs", ) # Root should be able to write all logs try: for log in test_logs: log.write({"name": "Updated by Root"}) except AccessError: self.fail("Root should be able to write any logs") # Root should be able to unlink all logs try: test_logs.unlink() except AccessError: self.fail("Root should be able to unlink any logs") def test_log_text_access_restrictions(self): """Test log_text field access controls""" test_log = self.ServerLog.create( { "name": "Access Test Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "1", "log_text": "
Test content
", } ) # 1. Verify read access for all roles for user in (self.root, self.manager, self.user): content = test_log.with_user(user).log_text self.assertEqual( content, "Test content
", f"{user.name} should read log_text" ) # 2. Verify write prohibition for all roles for user in (self.root, self.manager, self.user): with self.assertRaises( AccessError, msg=f"{user.name} shouldn't modify log_text" ): test_log.with_user(user).write({"log_text": "Modified
"}) def test_log_text_refresh_mechanism(self): """Test log_text can only be updated via refresh action""" test_log = self.ServerLog.create( { "name": "Refresh Test Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "1", "log_text": "Initial
", } ) # 1. Direct write attempts should fail with self.assertRaises(AccessError): test_log.sudo().write({"log_text": "Illegal Update
"}) # 2. Verify refresh action updates content original_content = test_log.log_text test_log.action_update_log() self.assertNotEqual( test_log.log_text, original_content, "action_update_log() should update log_text", ) def test_log_text_copy(self): """Duplicating a log must NOT keep the log output""" original = self.ServerLog.create( { "name": "Original Log", "server_id": self.server_test_1.id, "log_type": "file", "access_level": "1", "log_text": "Original content
", } ) copied = original.copy() # log_text must be cleared because copy=False self.assertFalse(copied.log_text, "Copied log must not keep log_text") self.assertNotEqual(copied.id, original.id) self.assertTrue(bool(copied.name)) def test_jet_user_access(self): """Test user access to server logs via Jet""" # Set user to jet's user_ids (replaces any existing users) self.jet_test.write({"user_ids": [(6, 0, [self.user.id])]}) # Case 1: User should be able to read when: # - access_level == "1" # - user is in jet's user_ids recs = self.ServerLog.with_user(self.user).search( [("id", "in", [self.server_log_jet_1.id, self.server_log_jet_2.id])] ) self.assertEqual( len(recs), 1, "User should be able to read logs with access_level '1'" " when in jet's user_ids", ) self.assertEqual(recs.id, self.server_log_jet_1.id) # Case 2: User should not be able to read when not in jet's user_ids self.jet_test.write({"user_ids": [(5, 0, 0)]}) # Remove all users recs = self.ServerLog.with_user(self.user).search( [("id", "=", self.server_log_jet_1.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read when not in jet's user_ids", ) # Case 3: User should not be able to read when access_level > "1" # Set user back to jet's user_ids self.jet_test.write({"user_ids": [(6, 0, [self.user.id])]}) recs = self.ServerLog.with_user(self.user).search( [("id", "=", self.server_log_jet_2.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read logs with access_level > '1'", ) def test_jet_manager_access(self): """Test manager access to server logs via Jet""" # Set manager to jet's manager_ids (replaces any existing managers) self.jet_test.write({"manager_ids": [(6, 0, [self.manager.id])]}) # Case 1: Manager should be able to read when: # - access_level <= "2" # - manager is in jet's user_ids or manager_ids recs = self.ServerLog.with_user(self.manager).search( [("id", "in", [self.server_log_jet_1.id, self.server_log_jet_2.id])] ) self.assertEqual( len(recs), 2, "Manager should be able to read all logs when in jet's manager_ids", ) # Case 2: Manager should be able to create and write when: # - access_level <= "2" # - manager is in jet's manager_ids try: new_log = self.ServerLog.with_user(self.manager).create( { "name": "Manager Jet Test Log", "server_id": self.server_test_1.id, "jet_id": self.jet_test.id, "log_type": "file", "access_level": "2", } ) except AccessError: self.fail("Manager should be able to create logs when in jet's manager_ids") try: new_log.write({"name": "Updated Jet Name"}) except AccessError: self.fail("Manager should be able to write logs when in jet's manager_ids") self.assertEqual(new_log.name, "Updated Jet Name") # Case 3: Manager should be able to unlink when: # - access_level <= "2" # - created by manager # - manager is in jet's manager_ids try: new_log.unlink() except AccessError: self.fail( "Manager should be able to unlink own logs when in jet's manager_ids" ) # Case 4: Manager should not be able to unlink logs created by others with self.assertRaises(AccessError): self.server_log_jet_1.with_user(self.manager).unlink() # Case 5: Manager should not be able to access logs with access_level > "2" high_access_log = ( self.ServerLog.with_user(self.manager) .sudo() .create( { "name": "High Access Jet Log", "server_id": self.server_test_1.id, "jet_id": self.jet_test.id, "log_type": "file", "access_level": "3", } ) ) recs = self.ServerLog.with_user(self.manager).search( [("id", "=", high_access_log.id)] ) self.assertEqual( len(recs), 0, "Manager should not be able to read logs with access_level > '2'", ) # Case 6: Manager should be able to read when in jet's user_ids # Remove managers and add manager to jet's user_ids self.jet_test.write( { "manager_ids": [(5, 0, 0)], # Remove managers "user_ids": [(6, 0, [self.manager.id])], # Set to users } ) recs = self.ServerLog.with_user(self.manager).search( [("id", "in", [self.server_log_jet_1.id, self.server_log_jet_2.id])] ) self.assertEqual( len(recs), 2, "Manager should be able to read when in jet's user_ids", ) def test_jet_template_user_access(self): """Test user access to server logs via Jet Template""" # Set user to jet template's user_ids (replaces any existing users) self.jet_template_test.write({"user_ids": [(6, 0, [self.user.id])]}) # Case 1: User should be able to read when: # - access_level == "1" # - user is in jet template's user_ids recs = self.ServerLog.with_user(self.user).search( [ ( "id", "in", [ self.server_log_jet_template_1.id, self.server_log_jet_template_2.id, ], ) ] ) self.assertEqual( len(recs), 1, "User should be able to read logs with access_level '1'" " when in jet template's user_ids", ) self.assertEqual(recs.id, self.server_log_jet_template_1.id) # Case 2: User should not be able to read when not in jet template's user_ids self.jet_template_test.write({"user_ids": [(5, 0, 0)]}) # Remove all users recs = self.ServerLog.with_user(self.user).search( [("id", "=", self.server_log_jet_template_1.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read when not in jet template's user_ids", ) # Case 3: User should not be able to read when access_level > "1" # Set user back to jet template's user_ids self.jet_template_test.write({"user_ids": [(6, 0, [self.user.id])]}) recs = self.ServerLog.with_user(self.user).search( [("id", "=", self.server_log_jet_template_2.id)] ) self.assertEqual( len(recs), 0, "User should not be able to read logs with access_level > '1'", ) def test_jet_template_manager_access(self): """Test manager access to server logs via Jet Template""" # Set manager to jet template's manager_ids (replaces any existing managers) self.jet_template_test.write({"manager_ids": [(6, 0, [self.manager.id])]}) # Case 1: Manager should be able to read when: # - access_level <= "2" # - manager is in jet template's user_ids or manager_ids recs = self.ServerLog.with_user(self.manager).search( [ ( "id", "in", [ self.server_log_jet_template_1.id, self.server_log_jet_template_2.id, ], ) ] ) self.assertEqual( len(recs), 2, "Manager should be able to read all logs when" " in jet template's manager_ids", ) # Case 2: Manager should be able to create and write when: # - access_level <= "2" # - manager is in jet template's manager_ids try: new_log = self.ServerLog.with_user(self.manager).create( { "name": "Manager Jet Template Test Log", "server_id": self.server_test_1.id, "jet_template_id": self.jet_template_test.id, "log_type": "file", "access_level": "2", } ) except AccessError: self.fail( "Manager should be able to create logs when " "in jet template's manager_ids" ) try: new_log.write({"name": "Updated Jet Template Name"}) except AccessError: self.fail( "Manager should be able to write logs when " "in jet template's manager_ids" ) self.assertEqual(new_log.name, "Updated Jet Template Name") # Case 3: Manager should be able to unlink when: # - access_level <= "2" # - created by manager # - manager is in jet template's manager_ids try: new_log.unlink() except AccessError: self.fail( "Manager should be able to unlink own logs" " when in jet template's manager_ids" ) # Case 4: Manager should not be able to unlink logs created by others with self.assertRaises(AccessError): self.server_log_jet_template_1.with_user(self.manager).unlink() # Case 5: Manager should not be able to access logs with access_level > "2" high_access_log = ( self.ServerLog.with_user(self.manager) .sudo() .create( { "name": "High Access Jet Template Log", "server_id": self.server_test_1.id, "jet_template_id": self.jet_template_test.id, "log_type": "file", "access_level": "3", } ) ) recs = self.ServerLog.with_user(self.manager).search( [("id", "=", high_access_log.id)] ) self.assertEqual( len(recs), 0, "Manager should not be able to read logs with access_level > '2'", ) # Case 6: Manager should be able to read when in jet template's user_ids # Remove managers and add manager to jet template's user_ids self.jet_template_test.write( { "manager_ids": [(5, 0, 0)], # Remove managers "user_ids": [(6, 0, [self.manager.id])], # Set to users } ) recs = self.ServerLog.with_user(self.manager).search( [ ( "id", "in", [ self.server_log_jet_template_1.id, self.server_log_jet_template_2.id, ], ) ] ) self.assertEqual( len(recs), 2, "Manager should be able to read when in jet template's user_ids", )