From 95ab9b7b0753467b02949fdf014bd822c65ad763 Mon Sep 17 00:00:00 2001 From: git_admin Date: Mon, 27 Apr 2026 08:18:27 +0000 Subject: [PATCH] Tower: upload cetmix_tower_server 16.0.3.0.1 (via marketplace) --- addons/cetmix_tower_server/tests/test_key.py | 919 +++++++++++++++++++ 1 file changed, 919 insertions(+) create mode 100644 addons/cetmix_tower_server/tests/test_key.py diff --git a/addons/cetmix_tower_server/tests/test_key.py b/addons/cetmix_tower_server/tests/test_key.py new file mode 100644 index 0000000..c633417 --- /dev/null +++ b/addons/cetmix_tower_server/tests/test_key.py @@ -0,0 +1,919 @@ +from odoo.exceptions import AccessError, ValidationError + +from .common import TestTowerCommon + + +class TestTowerKey(TestTowerCommon): + """Test class for tower key.""" + + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Create another manager for testing + cls.manager_2 = cls.Users.create( + { + "name": "Second Manager", + "login": "manager2", + "email": "manager2@test.com", + "groups_id": [(4, cls.env.ref("cetmix_tower_server.group_manager").id)], + } + ) + + # Create test servers + cls.server_1 = cls.Server.create( + { + "name": "Test Server 1", + "ip_v4_address": "192.168.1.1", + "ssh_port": 22, + "ssh_username": "admin", + "ssh_password": "password", + "ssh_auth_mode": "p", + } + ) + cls.server_2 = cls.Server.create( + { + "name": "Test Server 2", + "ip_v4_address": "192.168.1.2", + "ssh_port": 22, + "ssh_username": "admin", + "ssh_password": "password", + "ssh_auth_mode": "p", + } + ) + cls.test_key = cls.Key.create( + {"name": "Test Key", "key_type": "s", "secret_value": "test value"} + ) + + def test_key_creation(self): + """ + Test key creation. + We override create method so need to check if reference is generated properly + """ + + # -- 1-- + # Check new key values + key_one = self.Key.create( + {"name": " test key meme ", "secret_value": "test value", "key_type": "s"} + ) + self.assertEqual( + key_one.reference, "test_key_meme", "Reference must be 'test_key_meme'" + ) + self.assertEqual( + key_one.name, + "test key meme", + "Trailing and leading whitespaces must be removed from name", + ) + + def test_extract_key_strings(self): + """Check if key strings are extracted properly""" + code = ( + "Hey #!cxtower.secret.MEME_KEY!# & Doge #!cxtower.secret.DOGE_KEY !# so " + "like #!cxtower.secret.MEME_KEY!#!\n" + "They make #!memes together." + "And this is another string for the same #!cxtower.secret.MEME_KEY !#" + ) + key_strings = self.Key._extract_key_strings(code) + self.assertEqual(len(key_strings), 3, "Must be 3 key stings") + self.assertIn( + "#!cxtower.secret.MEME_KEY!#", + key_strings, + "Key string must be in key strings", + ) + self.assertIn( + "#!cxtower.secret.DOGE_KEY !#", + key_strings, + "Key string must be in key strings", + ) + self.assertIn( + "#!cxtower.secret.MEME_KEY !#", + key_strings, + "Key string must be in key strings", + ) + + def test_parse_key_string(self): + """Check if key string is parsed correctly""" + + # Test global key + doge_key = self.Key.create( + { + "name": "doge key", + "reference": "DOGE_KEY", + "secret_value": "Doge dog", + "key_type": "s", + } + ) + key_string = "#!cxtower.secret.DOGE_KEY!#" + key_value = self.Key._parse_key_string(key_string) + self.assertEqual(key_value, "Doge dog", "Key value doesn't match") + + # Test the same key string but with some spaces before the key terminator + key_string = "#!cxtower.secret.DOGE_KEY !#" + key_value = self.Key._parse_key_string(key_string) + self.assertEqual(key_value, "Doge dog", "Key value doesn't match") + + # Test partner specific key + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge partner", + "partner_id": self.user_bob.partner_id.id, + } + ) + # compose kwargs + kwargs = { + "partner_id": self.user_bob.partner_id.id, + "server_id": self.server_test_1.id, + } + key_value = self.Key._parse_key_string(key_string, **kwargs) + self.assertEqual(key_value, "Doge partner", "Key value doesn't match") + + # Test server specific key + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge server", + "server_id": self.server_test_1.id, + } + ) + key_value = self.Key._parse_key_string(key_string, **kwargs) + + # Test server and partner specific key + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge server and partner", + "server_id": self.server_test_1.id, + "partner_id": self.user_bob.partner_id.id, + } + ) + key_value = self.Key._parse_key_string(key_string, **kwargs) + self.assertEqual( + key_value, "Doge server and partner", "Key value doesn't match" + ) + + # Test missing key + key_string = "#!cxtower.secret.ANOTHER_KEY!#" + key_value = self.Key._parse_key_string(key_string) + self.assertIsNone(key_value, "Key value must be 'None'") + + # Test missformatted key + key_string = "#!cxtower.ANOTHER_KEY!#" + key_value = self.Key._parse_key_string(key_string) + self.assertIsNone(key_value, "Key value must be 'None'") + + # Test another missformatted key + key_string = "#!cxtower.notasecret.DOGE_KEY!#" + key_value = self.Key._parse_key_string(key_string) + self.assertIsNone(key_value, "Key value must be 'None'") + + def test_resolve_key(self): + """Check generic key resolver""" + self.Key.create( + { + "name": "doge key", + "reference": "DOGE_KEY", + "secret_value": "Doge dog", + "key_type": "s", + } + ) + + # Existing key + key_value = self.Key._resolve_key("secret", "DOGE_KEY") + self.assertEqual(key_value, "Doge dog", "Key value doesn't match") + + # Non existing key + key_value = self.Key._resolve_key("server", "PEPE_KEY") + self.assertIsNone(key_value, "Key value must be 'None'") + + def test_resolve_key_type_secret(self): + """Check 'secret' type key resolver""" + doge_key = self.Key.create( + { + "name": "doge key", + "reference": "DOGE_KEY", + "key_type": "s", + } + ) + + # 1. Test server and partner specific key + server_partner_value = self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge server and partner", + "server_id": self.server_test_1.id, + "partner_id": self.user_bob.partner_id.id, + } + ) + kwargs = { + "partner_id": self.user_bob.partner_id.id, + "server_id": self.server_test_1.id, + } + key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) + self.assertEqual( + key_value, "Doge server and partner", "Key value doesn't match" + ) + + # 2. Global key + doge_key.write({"secret_value": "Doge dog"}) + key_value = self.Key._resolve_key_type_secret("DOGE_KEY") + self.assertEqual(key_value, "Doge dog", "Key value doesn't match") + + # 3. Non existing key + key_value = self.Key._resolve_key_type_secret("PEPE_KEY") + self.assertIsNone(key_value, "Key value must be 'None'") + + # 4. Partner specific key + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge partner", + "partner_id": self.user_bob.partner_id.id, + } + ) + kwargs = { + "partner_id": self.user_bob.partner_id.id, + } + key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) + self.assertEqual(key_value, "Doge partner", "Key value doesn't match") + + # 5. Test server specific key + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge server", + "server_id": self.server_test_1.id, + } + ) + kwargs = { + "server_id": self.server_test_1.id, + } + key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) + self.assertEqual(key_value, "Doge server", "Key value doesn't match") + + # 6. Test with non matching partner. Should return server specific value + kwargs = { + "partner_id": self.user.partner_id.id, + "server_id": self.server_test_1.id, + } + key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) + self.assertEqual(key_value, "Doge server", "Key value doesn't match") + + # 7. Change partner in the server-partner specific value. + # Should return server specific value + server_partner_value.write({"partner_id": self.manager.partner_id.id}) + kwargs = { + "server_id": self.server_test_1.id, + } + key_value = self.Key._resolve_key_type_secret("DOGE_KEY", **kwargs) + self.assertEqual(key_value, "Doge server", "Key value doesn't match") + + # 8. Test with the global key again + key_value = self.Key._resolve_key_type_secret("DOGE_KEY") + self.assertEqual(key_value, "Doge dog", "Key value doesn't match") + + def test_parse_code(self): + """Test code parsing""" + + def check_parsed_code( + code, code_parsed_expected, expected_key_values=None, **kwargs + ): + """Helper function for code parse testing + + Args: + code (Text): code to parse + code_parsed_expected (Text): expected parsed code + expected_key_values (list, optional): key values that are expected + to be returned. Defaults to None. + """ + code_parsed = self.Key._parse_code(code, **kwargs) + self.assertEqual( + code_parsed, + code_parsed_expected, + msg="Parsed code doesn't match expected one", + ) + if expected_key_values: + result = self.Key._parse_code_and_return_key_values(code, **kwargs) + code_parsed = result["code"] + key_values = result["key_values"] + self.assertEqual( + code_parsed, + code_parsed_expected, + msg="Parsed code doesn't match expected one", + ) + self.assertEqual( + len(key_values), + len(expected_key_values), + "Number of key values doesn't match number of expected ones", + ) + for expected_value in expected_key_values: + self.assertIn( + expected_value, + key_values, + f"Value {expected_value} must be in the returned key values", + ) + + # Create new key + self.Key.create( + { + "name": "Meme key", + "reference": "MEME_KEY", + "secret_value": "Pepe Frog", + "key_type": "s", + } + ) + + # Check key parser + + # 1 - single line + + code = "The key to understand this meme is #!cxtower.secret.MEME_KEY!#" + code_parsed_expected = "The key to understand this meme is Pepe Frog" + expected_key_values = ["Pepe Frog"] + check_parsed_code(code, code_parsed_expected, expected_key_values) + + # 2 - multi line + code = "Welcome #!cxtower.secret.MEME_KEY!#\nNew hero of this city!" + code_parsed_expected = "Welcome Pepe Frog\nNew hero of this city!" + expected_key_values = ["Pepe Frog"] + check_parsed_code(code, code_parsed_expected, expected_key_values) + + # 3 - Key not found + code = "Don't mess with #!cxtower.secret.DOGE_LIKE!# He will make you cry" + code_parsed_expected = "Don't mess with None He will make you cry" + expected_key_values = [] + check_parsed_code(code, code_parsed_expected, expected_key_values) + + check_parsed_code(code, code_parsed_expected) + + # 4 - Multi keys + # Create new key + doge_key = self.Key.create( + { + "name": "doge key", + "reference": "DOGE_KEY", + "secret_value": "Doge dog", + "key_type": "s", + } + ) + code = ( + "Hey #!cxtower.secret.MEME_KEY!# & Doge #!cxtower.secret.DOGE_KEY !# so " + "like #!cxtower.secret.MEME_KEY!#!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + code_parsed_expected = ( + "Hey Pepe Frog & Doge Doge dog so " + "like Pepe Frog!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + expected_key_values = ["Pepe Frog", "Doge dog"] + check_parsed_code(code, code_parsed_expected, expected_key_values) + + # 5 - Partner specific key + # Create new key for partner Bob + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge wow", + "partner_id": self.user_bob.partner_id.id, + } + ) + # compose kwargs + kwargs = {"partner_id": self.user_bob.partner_id.id} + code_parsed_expected = ( + "Hey Pepe Frog & Doge Doge wow so " + "like Pepe Frog!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + expected_key_values = ["Pepe Frog", "Doge wow"] + check_parsed_code(code, code_parsed_expected, expected_key_values, **kwargs) + + # 6 - Server specific key + # Create new key for server Test 1 + self.KeyValue.create( + { + "key_id": doge_key.id, + "secret_value": "Doge much", + "server_id": self.server_test_1.id, + } + ) + # compose kwargs + kwargs = { + "partner_id": self.user_bob.partner_id.id, # not needed but may keep it + "server_id": self.server_test_1.id, + } + code_parsed_expected = ( + "Hey Pepe Frog & Doge Doge much so " + "like Pepe Frog!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + expected_key_values = ["Pepe Frog", "Doge much"] + check_parsed_code(code, code_parsed_expected, expected_key_values, **kwargs) + + def test_replace_with_spoiler(self): + """Check if secrets are replaced with spoiler correctly""" + + code = ( + "Hey Pepe Frog & Doge Doge much so " + "like Pepe Frog!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + placeholder = self.Key.SECRET_VALUE_PLACEHOLDER + expected_code = ( + f"Hey {placeholder} & Doge {placeholder} so " + f"like {placeholder}!\n" + "They make #!memes together. Check #!cxtower.secret.MEME_KEY&#!" + "cxtower.secret.DOGE_KEY" + ) + key_values = ["Pepe Frog", "Doge much"] + + result = self.Key._replace_with_spoiler(code, key_values) + self.assertEqual(result, expected_code, "Result doesn't match expected code") + + # -------------------------------------- + # Check with some random key values now + # Original code should rename unchanged + # -------------------------------------- + + key_values = ["Wow much", "No like"] + result = self.Key._replace_with_spoiler(code, key_values) + self.assertEqual(result, code, "Result doesn't match expected code") + + def test_user_access(self): + """Test that regular users have no access to keys""" + user_key = self.Key.with_user(self.user) + + # Create test key + key = self.Key.create( + {"name": "Test Key", "secret_value": "test value", "key_type": "s"} + ) + + # Test CRUD operations + with self.assertRaises(AccessError): + user_key.create( + {"name": "New Key", "secret_value": "secret", "key_type": "s"} + ) + with self.assertRaises(AccessError): + user_key.browse(key.id).read(["name"]) + with self.assertRaises(AccessError): + user_key.browse(key.id).write({"name": "Updated Name"}) + with self.assertRaises(AccessError): + user_key.browse(key.id).unlink() + + def test_manager_read_access(self): + """Test manager read access rules""" + manager_key = self.Key.with_user(self.manager) + + # Create test keys + key_secret = self.Key.create( + {"name": "Secret Key", "secret_value": "secret value", "key_type": "s"} + ) + key_ssh = self.Key.create( + {"name": "SSH Key", "secret_value": "ssh key", "key_type": "k"} + ) + + # Test read access for secret key - should read (all managers can read secrets) + self.assertTrue(manager_key.search([("id", "=", key_secret.id)])) + + # Test read access for SSH key without server access - should not find + self.assertFalse(manager_key.search([("id", "=", key_ssh.id)])) + + # Add manager to server users and set SSH key - should find SSH key + self.write_and_invalidate( + self.server_1, + **{"user_ids": [(4, self.manager.id)], "ssh_key_id": key_ssh.id}, + ) + self.assertTrue(manager_key.search([("id", "=", key_ssh.id)])) + + # Remove key from server - should not find again + self.server_1.write({"ssh_key_id": False}) + self.assertFalse(manager_key.search([("id", "=", key_ssh.id)])) + + # Add as key user - should find both + key_secret.write({"user_ids": [(4, self.manager.id)]}) + key_ssh.write({"user_ids": [(4, self.manager.id)]}) + self.assertTrue(manager_key.search([("id", "=", key_secret.id)])) + self.assertTrue(manager_key.search([("id", "=", key_ssh.id)])) + + def test_manager_write_access(self): + """Test manager write/create access rules""" + manager_key = self.Key.with_user(self.manager) + + # Create test keys as root and ensure manager is not in manager_ids + key_secret = self.Key.create( + { + "name": "Secret Key", + "secret_value": "secret value", + "key_type": "s", + "manager_ids": [(5, 0)], # Clear manager_ids + } + ) + key_ssh = self.Key.create( + { + "name": "SSH Key", + "secret_value": "ssh key", + "key_type": "k", + "manager_ids": [(5, 0)], # Clear manager_ids + } + ) + + # Try write without being manager - should fail + with self.assertRaises(AccessError): + manager_key.browse(key_secret.id).write({"name": "Updated Secret"}) + with self.assertRaises(AccessError): + manager_key.browse(key_ssh.id).write({"name": "Updated SSH"}) + + # Add as key manager - should write to secret + key_secret.write({"manager_ids": [(4, self.manager.id)]}) + manager_key.browse(key_secret.id).write({"name": "Updated Secret"}) + self.assertEqual(key_secret.name, "Updated Secret") + + # Add as server manager and set SSH key - should write to SSH key + self.server_1.write( + {"manager_ids": [(4, self.manager.id)], "ssh_key_id": key_ssh.id} + ) + manager_key.browse(key_ssh.id).write({"name": "Updated SSH"}) + self.assertEqual(key_ssh.name, "Updated SSH") + + def test_manager_create_access(self): + """Test manager create access rules""" + manager_key = self.Key.with_user(self.manager) + manager_2_key = self.Key.with_user(self.manager_2) + + # Try create secret key when not a manager - should fail + with self.assertRaises(AccessError): + manager_2_key.create( + { + "name": "New Secret", + "secret_value": "secret", + "key_type": "s", + "manager_ids": [(5, 0)], # Prevent automatic manager addition + } + ) + + # Try create SSH key when not a server manager - should fail + with self.assertRaises(AccessError): + manager_2_key.create( + { + "name": "New SSH", + "secret_value": "ssh key", + "key_type": "k", + "manager_ids": [(5, 0)], # Prevent automatic manager addition + } + ) + + # Add as server manager - should create SSH key + self.server_1.write({"manager_ids": [(4, self.manager.id)]}) + new_ssh_key = manager_key.create( + {"name": "New SSH", "secret_value": "ssh key", "key_type": "k"} + ) + # Link key to server + self.server_1.write({"ssh_key_id": new_ssh_key.id}) + self.assertTrue(new_ssh_key.exists()) + + def test_manager_unlink_access(self): + """Test manager unlink access rules""" + manager_key = self.Key.with_user(self.manager) + + # Create keys as root + key_secret = self.Key.create( + {"name": "Secret Key", "secret_value": "secret value", "key_type": "s"} + ) + key_ssh = self.Key.create( + {"name": "SSH Key", "secret_value": "ssh key", "key_type": "k"} + ) + # Link SSH key to server + self.server_1.write({"ssh_key_id": key_ssh.id}) + + # Try delete without being manager and creator - should fail + with self.assertRaises(AccessError): + manager_key.browse(key_secret.id).unlink() + with self.assertRaises(AccessError): + manager_key.browse(key_ssh.id).unlink() + + # Add as manager but not creator - should still fail + key_secret.write({"manager_ids": [(4, self.manager.id)]}) + self.server_1.write({"manager_ids": [(4, self.manager.id)]}) + with self.assertRaises(AccessError): + manager_key.browse(key_secret.id).unlink() + with self.assertRaises(AccessError): + manager_key.browse(key_ssh.id).unlink() + + # Create own keys - should delete + own_secret = manager_key.create( + { + "name": "Own Secret", + "secret_value": "secret", + "key_type": "s", + "manager_ids": [(4, self.manager.id)], + } + ) + own_ssh = manager_key.create( + {"name": "Own SSH", "secret_value": "ssh key", "key_type": "k"} + ) + # Link own SSH key to server + self.server_1.write({"ssh_key_id": own_ssh.id}) + + own_secret.unlink() + own_ssh.unlink() + self.assertFalse(own_secret.exists()) + self.assertFalse(own_ssh.exists()) + + def test_root_access(self): + """Test root access rules""" + root_key = self.Key.with_user(self.root) + + # Create + key = root_key.create( + {"name": "Root Key", "secret_value": "root secret", "key_type": "s"} + ) + self.assertTrue(key.exists()) + + # Read + self.assertEqual(root_key.browse(key.id).name, "Root Key") + + # Write + root_key.browse(key.id).write({"name": "Updated Root Key"}) + self.assertEqual(key.name, "Updated Root Key") + + # Delete + key.unlink() + self.assertFalse(key.exists()) + + def test_key_value_user_access(self): + """Test that regular users have no access to key values""" + user_key_value = self.KeyValue.with_user(self.user) + + # Create test key and key value + key = self.Key.create({"name": "Test Key", "key_type": "s"}) + key_value = self.KeyValue.create( + {"key_id": key.id, "secret_value": "test value"} + ) + + # Test CRUD operations + with self.assertRaises(AccessError): + user_key_value.create({"key_id": key.id, "secret_value": "new value"}) + with self.assertRaises(AccessError): + user_key_value.browse(key_value.id).read(["secret_value"]) + with self.assertRaises(AccessError): + user_key_value.browse(key_value.id).write({"secret_value": "updated value"}) + with self.assertRaises(AccessError): + user_key_value.browse(key_value.id).unlink() + + def test_key_value_manager_read_access(self): + """Test manager read access rules for key values""" + manager_key_value = self.KeyValue.with_user(self.manager) + + # Create test key and key values + key = self.Key.create({"name": "Test Key", "key_type": "s"}) + global_value = self.KeyValue.create( + {"key_id": key.id, "secret_value": "global value"} + ) + server_value = self.KeyValue.create( + { + "key_id": key.id, + "secret_value": "server value", + "server_id": self.server_1.id, + } + ) + + # Test read access - should not find without proper access + self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) + self.assertFalse(manager_key_value.search([("id", "=", server_value.id)])) + + # Add as key user - should find global value and server value for that key + key.write({"user_ids": [(4, self.manager.id)]}) + self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) + self.assertTrue(manager_key_value.search([("id", "=", server_value.id)])) + + # Remove from key users + key.write({"user_ids": [(3, self.manager.id)]}) + self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) + self.assertFalse(manager_key_value.search([("id", "=", server_value.id)])) + + # Add as server user - should find server value + self.server_1.write({"user_ids": [(4, self.manager.id)]}) + self.assertTrue(manager_key_value.search([("id", "=", global_value.id)])) + self.assertTrue(manager_key_value.search([("id", "=", server_value.id)])) + + def test_key_value_manager_write_access(self): + """Test manager write/create access rules for key values""" + manager_key_value = self.KeyValue.with_user(self.manager) + + # Create test key and key values + key = self.Key.create({"name": "Test Key", "key_type": "s"}) + global_value = self.KeyValue.create( + {"key_id": key.id, "secret_value": "global value"} + ) + server_value = self.KeyValue.create( + { + "key_id": key.id, + "secret_value": "server value", + "server_id": self.server_1.id, + } + ) + + # Try write without proper access - should fail + with self.assertRaises(AccessError): + manager_key_value.browse(global_value.id).write( + {"secret_value": "new value"} + ) + with self.assertRaises(AccessError): + manager_key_value.browse(server_value.id).write( + {"secret_value": "new value"} + ) + + # Add as key manager - should write to global value + key.write({"manager_ids": [(4, self.manager.id)]}) + manager_key_value.browse(global_value.id).write( + {"secret_value": "updated global"} + ) + self.assertEqual( + global_value._get_secret_value("secret_value"), "updated global" + ) + + # Add as server manager - should write to server value + self.server_1.write({"manager_ids": [(4, self.manager.id)]}) + manager_key_value.browse(server_value.id).write( + {"secret_value": "updated server"} + ) + self.assertEqual( + server_value._get_secret_value("secret_value"), "updated server" + ) + + # Test create access + for_bob = manager_key_value.create( + { + "key_id": key.id, + "secret_value": "for bob", + "partner_id": self.user_bob.partner_id.id, + } + ) + self.assertTrue(for_bob.exists()) + + def test_key_value_manager_unlink_access(self): + """Test manager unlink access rules for key values""" + manager_key_value = self.KeyValue.with_user(self.manager) + + # Create test key and key values + key = self.Key.create({"name": "Test Key", "key_type": "s"}) + + # Create values as root + global_value = self.KeyValue.create( + {"key_id": key.id, "secret_value": "global value"} + ) + server_value = self.KeyValue.create( + { + "key_id": key.id, + "secret_value": "server value", + "server_id": self.server_1.id, + } + ) + + # Try delete without proper access - should fail + with self.assertRaises(AccessError): + manager_key_value.browse(global_value.id).unlink() + with self.assertRaises(AccessError): + manager_key_value.browse(server_value.id).unlink() + + # Add as manager but not creator - should still fail + key.write({"manager_ids": [(4, self.manager.id)]}) + self.server_1.write({"manager_ids": [(4, self.manager.id)]}) + with self.assertRaises(AccessError): + manager_key_value.browse(global_value.id).unlink() + with self.assertRaises(AccessError): + manager_key_value.browse(server_value.id).unlink() + + # Create own values - should delete + own_partner_value = manager_key_value.create( + { + "key_id": key.id, + "secret_value": "own partner", + "partner_id": self.user_bob.partner_id.id, + } + ) + + # Unlink server value first to avoid constraint error + server_value.unlink() + + # Create server value + own_server_value = manager_key_value.create( + { + "key_id": key.id, + "secret_value": "own server", + "server_id": self.server_1.id, + } + ) + + own_partner_value.unlink() + own_server_value.unlink() + self.assertFalse(own_partner_value.exists()) + self.assertFalse(own_server_value.exists()) + + def test_key_value_root_access(self): + """Test root access rules for key values""" + root_key_value = self.KeyValue.with_user(self.root) + + # Create test key + key = self.Key.create({"name": "Test Key", "key_type": "s"}) + + # Create + value = root_key_value.create({"key_id": key.id, "secret_value": "root value"}) + self.assertTrue(value.exists()) + + # Read + self.assertEqual( + root_key_value.browse(value.id)._get_secret_value("secret_value"), + "root value", + ) + + # Write + root_key_value.browse(value.id).write({"secret_value": "updated value"}) + self.assertEqual(value._get_secret_value("secret_value"), "updated value") + + # Delete + value.unlink() + self.assertFalse(value.exists()) + + def test_key_value_global_unique(self): + """Test global value uniqueness""" + + # Try to create a value for the same key + with self.assertRaises(ValidationError): + another_global_value = self.KeyValue.create( + {"key_id": self.test_key.id, "secret_value": "another test value"} + ) + # + another_global_value.unlink() + + def test_key_value_server_unique(self): + """Test server value uniqueness""" + # Create server tight value + + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "server related", + "server_id": self.server_1.id, + } + ) + + # Try create another value for the same server + with self.assertRaises(ValidationError): + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "another server related", + "server_id": self.server_1.id, + } + ) + + def test_key_value_partner_unique(self): + """Test partner value uniqueness""" + # Create partner tight value + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "partner related", + "partner_id": self.user_bob.partner_id.id, + } + ) + + # Try create another value for the same partner + with self.assertRaises(ValidationError): + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "another partner related", + "partner_id": self.user_bob.partner_id.id, + } + ) + + def test_key_value_server_partner_unique(self): + """Test server and partner value uniqueness""" + + # Create server and partner tight value + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "server related", + "server_id": self.server_1.id, + "partner_id": self.user_bob.partner_id.id, + } + ) + + # Try create another value for the same server and partner + with self.assertRaises(ValidationError): + self.KeyValue.create( + { + "key_id": self.test_key.id, + "secret_value": "another server related", + "server_id": self.server_1.id, + "partner_id": self.user_bob.partner_id.id, + } + )