diff --git a/keepercommander/service/config/file_handler.py b/keepercommander/service/config/file_handler.py index e7b4a798a..88419194d 100644 --- a/keepercommander/service/config/file_handler.py +++ b/keepercommander/service/config/file_handler.py @@ -17,6 +17,13 @@ from ..decorators.logging import logger from ..util.exceptions import ValidationError + +SERVICE_CONFIG_RECORD_TITLES = ( + 'Commander Service Mode Config', + 'Commander Service Mode', +) + + class ConfigFormatHandler: def __init__(self, config_dir: Path, messages: Dict, validation_messages: Dict): self.config_dir = config_dir @@ -50,13 +57,14 @@ def get_config_format(self, save_type: str = None) -> str: from ..core.globals import get_current_params if params := get_current_params(): - if self.cli_handler.download_config_from_vault(params, 'Commander Service Mode Config', self.config_dir): - if json_path.exists(): - self.encrypt_config_file(json_path, self.config_dir) - return 'json' - if yaml_path.exists(): - self.encrypt_config_file(yaml_path, self.config_dir) - return 'yaml' + for title in SERVICE_CONFIG_RECORD_TITLES: + if self.cli_handler.download_config_from_vault(params, title, self.config_dir): + if json_path.exists(): + self.encrypt_config_file(json_path, self.config_dir) + return 'json' + if yaml_path.exists(): + self.encrypt_config_file(yaml_path, self.config_dir) + return 'yaml' return self._get_format_input() if save_type == 'create' else 'json' @@ -195,4 +203,4 @@ def decrypt_config_file(encrypted_content: bytes, config_dir: Path) -> str: hashed_key = sha256(private_key.encode('utf-8')).digest() return decrypt_aes_v2(encrypted_content, hashed_key).decode('utf-8') except Exception as e: - raise ValidationError(f"Failed to decrypt configuration file: {str(e)}") \ No newline at end of file + raise ValidationError(f"Failed to decrypt configuration file: {str(e)}") diff --git a/unit-tests/service/test_service_config.py b/unit-tests/service/test_service_config.py index 9ca4f3eeb..dd221f67d 100644 --- a/unit-tests/service/test_service_config.py +++ b/unit-tests/service/test_service_config.py @@ -1,7 +1,10 @@ import unittest +import tempfile +from pathlib import Path from unittest.mock import patch, MagicMock import json from keepercommander.params import KeeperParams +from keepercommander.service.config.file_handler import ConfigFormatHandler from keepercommander.service.config.service_config import ServiceConfig from keepercommander.service.util.exceptions import ValidationError @@ -43,12 +46,12 @@ def test_save_config_success(self): """Test successful configuration save.""" with patch.object(self.service_config.format_handler, 'get_config_format') as mock_format, \ patch.object(self.service_config.format_handler, '_save_json') as mock_save_json: - + mock_format.return_value = 'json' mock_save_json.return_value = self.service_config.config_path - + result = self.service_config.save_config(self.test_config) - + mock_format.assert_called_once() mock_save_json.assert_called_once() self.assertEqual(result, self.service_config.config_path) @@ -57,13 +60,39 @@ def test_save_config_io_error(self): """Test configuration save with IO error.""" with patch.object(self.service_config.format_handler, 'get_config_format') as mock_format, \ patch.object(self.service_config.format_handler, '_save_json') as mock_save_json: - + mock_format.return_value = 'json' mock_save_json.side_effect = IOError("Test error") - + with self.assertRaises(ValidationError): self.service_config.save_config(self.test_config) - + + def test_get_config_format_falls_back_to_legacy_vault_title(self): + """Test config recovery from the legacy Service Mode record title.""" + with tempfile.TemporaryDirectory() as temp_dir: + config_dir = Path(temp_dir) + handler = ConfigFormatHandler(config_dir, {}, {}) + params = MagicMock(spec=KeeperParams) + download_calls = [] + + def download_side_effect(_params, title, _config_dir): + download_calls.append(title) + if title == 'Commander Service Mode': + (config_dir / 'service_config.json').write_text('{}') + return True + return False + + handler.cli_handler = MagicMock() + handler.cli_handler.download_config_from_vault.side_effect = download_side_effect + + with patch('keepercommander.service.core.globals.get_current_params', return_value=params), \ + patch.object(handler, 'encrypt_config_file') as mock_encrypt: + format_type = handler.get_config_format() + + self.assertEqual(format_type, 'json') + self.assertEqual(download_calls, ['Commander Service Mode Config', 'Commander Service Mode']) + mock_encrypt.assert_called_once_with(config_dir / 'service_config.json', config_dir) + @unittest.skip @patch('pathlib.Path.exists') @patch('pathlib.Path.read_text') @@ -131,4 +160,4 @@ def test_update_or_add_record(self, mock_record_handler): self.service_config.update_or_add_record(params) mock_record_handler.update_or_add_record.assert_called_once_with( params, self.service_config.title, self.service_config.config_path - ) \ No newline at end of file + )