diff --git a/homeassistant/auth/auth_store.py b/homeassistant/auth/auth_store.py index fb4700c806f..c170344746e 100644 --- a/homeassistant/auth/auth_store.py +++ b/homeassistant/auth/auth_store.py @@ -28,7 +28,8 @@ class AuthStore: """Initialize the auth store.""" self.hass = hass self._users = None # type: Optional[Dict[str, models.User]] - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY, + private=True) async def async_get_users(self) -> List[models.User]: """Retrieve all users.""" diff --git a/homeassistant/auth/mfa_modules/notify.py b/homeassistant/auth/mfa_modules/notify.py index 84f9de614c1..03be4c74d32 100644 --- a/homeassistant/auth/mfa_modules/notify.py +++ b/homeassistant/auth/mfa_modules/notify.py @@ -85,7 +85,7 @@ class NotifyAuthModule(MultiFactorAuthModule): super().__init__(hass, config) self._user_settings = None # type: Optional[_UsersDict] self._user_store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY) + STORAGE_VERSION, STORAGE_KEY, private=True) self._include = config.get(CONF_INCLUDE, []) self._exclude = config.get(CONF_EXCLUDE, []) self._message_template = config[CONF_MESSAGE] diff --git a/homeassistant/auth/mfa_modules/totp.py b/homeassistant/auth/mfa_modules/totp.py index 625cc0302e1..9b5896ef666 100644 --- a/homeassistant/auth/mfa_modules/totp.py +++ b/homeassistant/auth/mfa_modules/totp.py @@ -67,7 +67,7 @@ class TotpAuthModule(MultiFactorAuthModule): super().__init__(hass, config) self._users = None # type: Optional[Dict[str, str]] self._user_store = hass.helpers.storage.Store( - STORAGE_VERSION, STORAGE_KEY) + STORAGE_VERSION, STORAGE_KEY, private=True) @property def input_schema(self) -> vol.Schema: diff --git a/homeassistant/auth/providers/homeassistant.py b/homeassistant/auth/providers/homeassistant.py index c743a5b7f65..8710e7c60bc 100644 --- a/homeassistant/auth/providers/homeassistant.py +++ b/homeassistant/auth/providers/homeassistant.py @@ -52,7 +52,8 @@ class Data: def __init__(self, hass: HomeAssistant) -> None: """Initialize the user data store.""" self.hass = hass - self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + self._store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY, + private=True) self._data = None # type: Optional[Dict[str, Any]] async def async_load(self) -> None: diff --git a/homeassistant/components/onboarding/__init__.py b/homeassistant/components/onboarding/__init__.py index 52d18b9a870..376575e3440 100644 --- a/homeassistant/components/onboarding/__init__.py +++ b/homeassistant/components/onboarding/__init__.py @@ -23,7 +23,8 @@ def async_is_onboarded(hass): async def async_setup(hass, config): """Set up the onboarding component.""" - store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY) + store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY, + private=True) data = await store.async_load() if data is None: diff --git a/homeassistant/helpers/storage.py b/homeassistant/helpers/storage.py index 95e6925b2a4..7d867b50ec2 100644 --- a/homeassistant/helpers/storage.py +++ b/homeassistant/helpers/storage.py @@ -46,11 +46,12 @@ async def async_migrator(hass, old_path, store, *, class Store: """Class to help storing data.""" - def __init__(self, hass, version: int, key: str): + def __init__(self, hass, version: int, key: str, private: bool = False): """Initialize storage class.""" self.version = version self.key = key self.hass = hass + self._private = private self._data = None self._unsub_delay_listener = None self._unsub_stop_listener = None @@ -186,7 +187,7 @@ class Store: os.makedirs(os.path.dirname(path)) _LOGGER.debug('Writing data for %s', self.key) - json.save_json(path, data) + json.save_json(path, data, self._private) async def _async_migrate_func(self, old_version, old_data): """Migrate to the new version.""" diff --git a/homeassistant/util/json.py b/homeassistant/util/json.py index 8ecfebd5b33..40f25689148 100644 --- a/homeassistant/util/json.py +++ b/homeassistant/util/json.py @@ -3,6 +3,7 @@ import logging from typing import Union, List, Dict import json +import os from homeassistant.exceptions import HomeAssistantError @@ -38,14 +39,17 @@ def load_json(filename: str, default: Union[List, Dict, None] = None) \ return {} if default is None else default -def save_json(filename: str, data: Union[List, Dict]) -> None: +def save_json(filename: str, data: Union[List, Dict], + private: bool = False) -> None: """Save JSON data to a file. Returns True on success. """ try: json_data = json.dumps(data, sort_keys=True, indent=4) - with open(filename, 'w', encoding='utf-8') as fdesc: + mode = 0o600 if private else 0o644 + with open(os.open(filename, os.O_WRONLY | os.O_CREAT, mode), + 'w', encoding='utf-8') as fdesc: fdesc.write(json_data) except TypeError as error: _LOGGER.exception('Failed to serialize to JSON: %s', diff --git a/tests/components/emulated_hue/test_init.py b/tests/components/emulated_hue/test_init.py index 2f443eb5d6e..d416dd08e05 100644 --- a/tests/components/emulated_hue/test_init.py +++ b/tests/components/emulated_hue/test_init.py @@ -16,24 +16,25 @@ def test_config_google_home_entity_id_to_number(): handle = mop() with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '2' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '1': 'light.test2', - '2': 'light.test', - } + with patch('homeassistant.util.json.os.open', return_value=0): + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '1': 'light.test2', + '2': 'light.test', + } - number = conf.entity_id_to_number('light.test') - assert number == '2' - assert handle.write.call_count == 1 + number = conf.entity_id_to_number('light.test') + assert number == '2' + assert handle.write.call_count == 1 - number = conf.entity_id_to_number('light.test2') - assert number == '1' - assert handle.write.call_count == 1 + number = conf.entity_id_to_number('light.test2') + assert number == '1' + assert handle.write.call_count == 1 - entity_id = conf.number_to_entity_id('1') - assert entity_id == 'light.test2' + entity_id = conf.number_to_entity_id('1') + assert entity_id == 'light.test2' def test_config_google_home_entity_id_to_number_altered(): @@ -46,24 +47,25 @@ def test_config_google_home_entity_id_to_number_altered(): handle = mop() with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '22' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '21': 'light.test2', - '22': 'light.test', - } + with patch('homeassistant.util.json.os.open', return_value=0): + number = conf.entity_id_to_number('light.test') + assert number == '22' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '21': 'light.test2', + '22': 'light.test', + } - number = conf.entity_id_to_number('light.test') - assert number == '22' - assert handle.write.call_count == 1 + number = conf.entity_id_to_number('light.test') + assert number == '22' + assert handle.write.call_count == 1 - number = conf.entity_id_to_number('light.test2') - assert number == '21' - assert handle.write.call_count == 1 + number = conf.entity_id_to_number('light.test2') + assert number == '21' + assert handle.write.call_count == 1 - entity_id = conf.number_to_entity_id('21') - assert entity_id == 'light.test2' + entity_id = conf.number_to_entity_id('21') + assert entity_id == 'light.test2' def test_config_google_home_entity_id_to_number_empty(): @@ -76,23 +78,24 @@ def test_config_google_home_entity_id_to_number_empty(): handle = mop() with patch('homeassistant.util.json.open', mop, create=True): - number = conf.entity_id_to_number('light.test') - assert number == '1' - assert handle.write.call_count == 1 - assert json.loads(handle.write.mock_calls[0][1][0]) == { - '1': 'light.test', - } + with patch('homeassistant.util.json.os.open', return_value=0): + number = conf.entity_id_to_number('light.test') + assert number == '1' + assert handle.write.call_count == 1 + assert json.loads(handle.write.mock_calls[0][1][0]) == { + '1': 'light.test', + } - number = conf.entity_id_to_number('light.test') - assert number == '1' - assert handle.write.call_count == 1 + number = conf.entity_id_to_number('light.test') + assert number == '1' + assert handle.write.call_count == 1 - number = conf.entity_id_to_number('light.test2') - assert number == '2' - assert handle.write.call_count == 2 + number = conf.entity_id_to_number('light.test2') + assert number == '2' + assert handle.write.call_count == 2 - entity_id = conf.number_to_entity_id('2') - assert entity_id == 'light.test2' + entity_id = conf.number_to_entity_id('2') + assert entity_id == 'light.test2' def test_config_alexa_entity_id_to_number():