Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions keepercommander/service/config/file_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'

Expand Down Expand Up @@ -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)}")
raise ValidationError(f"Failed to decrypt configuration file: {str(e)}")
43 changes: 36 additions & 7 deletions unit-tests/service/test_service_config.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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')
Expand Down Expand Up @@ -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
)
)
Loading