mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 12:47:08 +00:00
RFC: Create a secrets file and enable HTTP password by default (#9685)
* Create a secret and enable password by default * Comment out api password secret * Lint/fix tests
This commit is contained in:
parent
8db4641455
commit
75f902f57e
@ -23,7 +23,7 @@ from homeassistant.const import (
|
|||||||
from homeassistant.core import callback, DOMAIN as CONF_CORE
|
from homeassistant.core import callback, DOMAIN as CONF_CORE
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.loader import get_component, get_platform
|
from homeassistant.loader import get_component, get_platform
|
||||||
from homeassistant.util.yaml import load_yaml
|
from homeassistant.util.yaml import load_yaml, SECRET_YAML
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import dt as date_util, location as loc_util
|
from homeassistant.util import dt as date_util, location as loc_util
|
||||||
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
from homeassistant.util.unit_system import IMPERIAL_SYSTEM, METRIC_SYSTEM
|
||||||
@ -70,8 +70,8 @@ frontend:
|
|||||||
config:
|
config:
|
||||||
|
|
||||||
http:
|
http:
|
||||||
# Uncomment this to add a password (recommended!)
|
# Secrets are defined in the file secrets.yaml
|
||||||
# api_password: PASSWORD
|
# api_password: !secret http_password
|
||||||
# Uncomment this if you are using SSL/TLS, running in Docker container, etc.
|
# Uncomment this if you are using SSL/TLS, running in Docker container, etc.
|
||||||
# base_url: example.duckdns.org:8123
|
# base_url: example.duckdns.org:8123
|
||||||
|
|
||||||
@ -111,6 +111,11 @@ group: !include groups.yaml
|
|||||||
automation: !include automations.yaml
|
automation: !include automations.yaml
|
||||||
script: !include scripts.yaml
|
script: !include scripts.yaml
|
||||||
"""
|
"""
|
||||||
|
DEFAULT_SECRETS = """
|
||||||
|
# Use this file to store secrets like usernames and passwords.
|
||||||
|
# Learn more at https://home-assistant.io/docs/configuration/secrets/
|
||||||
|
http_password: welcome
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
PACKAGES_CONFIG_SCHEMA = vol.Schema({
|
PACKAGES_CONFIG_SCHEMA = vol.Schema({
|
||||||
@ -181,6 +186,7 @@ def create_default_config(config_dir, detect_location=True):
|
|||||||
CONFIG_PATH as CUSTOMIZE_CONFIG_PATH)
|
CONFIG_PATH as CUSTOMIZE_CONFIG_PATH)
|
||||||
|
|
||||||
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
config_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||||
|
secret_path = os.path.join(config_dir, SECRET_YAML)
|
||||||
version_path = os.path.join(config_dir, VERSION_FILE)
|
version_path = os.path.join(config_dir, VERSION_FILE)
|
||||||
group_yaml_path = os.path.join(config_dir, GROUP_CONFIG_PATH)
|
group_yaml_path = os.path.join(config_dir, GROUP_CONFIG_PATH)
|
||||||
automation_yaml_path = os.path.join(config_dir, AUTOMATION_CONFIG_PATH)
|
automation_yaml_path = os.path.join(config_dir, AUTOMATION_CONFIG_PATH)
|
||||||
@ -209,7 +215,7 @@ def create_default_config(config_dir, detect_location=True):
|
|||||||
# Writing files with YAML does not create the most human readable results
|
# Writing files with YAML does not create the most human readable results
|
||||||
# So we're hard coding a YAML template.
|
# So we're hard coding a YAML template.
|
||||||
try:
|
try:
|
||||||
with open(config_path, 'w') as config_file:
|
with open(config_path, 'wt') as config_file:
|
||||||
config_file.write("homeassistant:\n")
|
config_file.write("homeassistant:\n")
|
||||||
|
|
||||||
for attr, _, _, description in DEFAULT_CORE_CONFIG:
|
for attr, _, _, description in DEFAULT_CORE_CONFIG:
|
||||||
@ -221,6 +227,9 @@ def create_default_config(config_dir, detect_location=True):
|
|||||||
|
|
||||||
config_file.write(DEFAULT_CONFIG)
|
config_file.write(DEFAULT_CONFIG)
|
||||||
|
|
||||||
|
with open(secret_path, 'wt') as secret_file:
|
||||||
|
secret_file.write(DEFAULT_SECRETS)
|
||||||
|
|
||||||
with open(version_path, 'wt') as version_file:
|
with open(version_path, 'wt') as version_file:
|
||||||
version_file.write(__version__)
|
version_file.write(__version__)
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ from homeassistant.exceptions import HomeAssistantError
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_SECRET_NAMESPACE = 'homeassistant'
|
_SECRET_NAMESPACE = 'homeassistant'
|
||||||
_SECRET_YAML = 'secrets.yaml'
|
SECRET_YAML = 'secrets.yaml'
|
||||||
__SECRET_CACHE = {} # type: Dict
|
__SECRET_CACHE = {} # type: Dict
|
||||||
|
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ def _include_dir_merge_named_yaml(loader: SafeLineLoader,
|
|||||||
mapping = OrderedDict() # type: OrderedDict
|
mapping = OrderedDict() # type: OrderedDict
|
||||||
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
for fname in _find_files(loc, '*.yaml'):
|
for fname in _find_files(loc, '*.yaml'):
|
||||||
if os.path.basename(fname) == _SECRET_YAML:
|
if os.path.basename(fname) == SECRET_YAML:
|
||||||
continue
|
continue
|
||||||
loaded_yaml = load_yaml(fname)
|
loaded_yaml = load_yaml(fname)
|
||||||
if isinstance(loaded_yaml, dict):
|
if isinstance(loaded_yaml, dict):
|
||||||
@ -146,7 +146,7 @@ def _include_dir_list_yaml(loader: SafeLineLoader,
|
|||||||
"""Load multiple files from directory as a list."""
|
"""Load multiple files from directory as a list."""
|
||||||
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
loc = os.path.join(os.path.dirname(loader.name), node.value)
|
||||||
return [load_yaml(f) for f in _find_files(loc, '*.yaml')
|
return [load_yaml(f) for f in _find_files(loc, '*.yaml')
|
||||||
if os.path.basename(f) != _SECRET_YAML]
|
if os.path.basename(f) != SECRET_YAML]
|
||||||
|
|
||||||
|
|
||||||
def _include_dir_merge_list_yaml(loader: SafeLineLoader,
|
def _include_dir_merge_list_yaml(loader: SafeLineLoader,
|
||||||
@ -156,7 +156,7 @@ def _include_dir_merge_list_yaml(loader: SafeLineLoader,
|
|||||||
node.value) # type: str
|
node.value) # type: str
|
||||||
merged_list = [] # type: List
|
merged_list = [] # type: List
|
||||||
for fname in _find_files(loc, '*.yaml'):
|
for fname in _find_files(loc, '*.yaml'):
|
||||||
if os.path.basename(fname) == _SECRET_YAML:
|
if os.path.basename(fname) == SECRET_YAML:
|
||||||
continue
|
continue
|
||||||
loaded_yaml = load_yaml(fname)
|
loaded_yaml = load_yaml(fname)
|
||||||
if isinstance(loaded_yaml, list):
|
if isinstance(loaded_yaml, list):
|
||||||
@ -216,7 +216,7 @@ def _env_var_yaml(loader: SafeLineLoader,
|
|||||||
|
|
||||||
def _load_secret_yaml(secret_path: str) -> Dict:
|
def _load_secret_yaml(secret_path: str) -> Dict:
|
||||||
"""Load the secrets yaml from path."""
|
"""Load the secrets yaml from path."""
|
||||||
secret_path = os.path.join(secret_path, _SECRET_YAML)
|
secret_path = os.path.join(secret_path, SECRET_YAML)
|
||||||
if secret_path in __SECRET_CACHE:
|
if secret_path in __SECRET_CACHE:
|
||||||
return __SECRET_CACHE[secret_path]
|
return __SECRET_CACHE[secret_path]
|
||||||
|
|
||||||
@ -264,6 +264,8 @@ def _secret_yaml(loader: SafeLineLoader,
|
|||||||
_LOGGER.debug("Secret %s retrieved from keyring", node.value)
|
_LOGGER.debug("Secret %s retrieved from keyring", node.value)
|
||||||
return pwd
|
return pwd
|
||||||
|
|
||||||
|
global credstash # pylint: disable=invalid-name
|
||||||
|
|
||||||
if credstash:
|
if credstash:
|
||||||
try:
|
try:
|
||||||
pwd = credstash.getSecret(node.value, table=_SECRET_NAMESPACE)
|
pwd = credstash.getSecret(node.value, table=_SECRET_NAMESPACE)
|
||||||
@ -272,6 +274,9 @@ def _secret_yaml(loader: SafeLineLoader,
|
|||||||
return pwd
|
return pwd
|
||||||
except credstash.ItemNotFound:
|
except credstash.ItemNotFound:
|
||||||
pass
|
pass
|
||||||
|
except Exception: # pylint: disable=broad-except
|
||||||
|
# Catch if package installed and no config
|
||||||
|
credstash = None
|
||||||
|
|
||||||
_LOGGER.error("Secret %s not defined", node.value)
|
_LOGGER.error("Secret %s not defined", node.value)
|
||||||
raise HomeAssistantError(node.value)
|
raise HomeAssistantError(node.value)
|
||||||
|
@ -16,6 +16,7 @@ from homeassistant.const import (
|
|||||||
CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__,
|
CONF_TIME_ZONE, CONF_ELEVATION, CONF_CUSTOMIZE, __version__,
|
||||||
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT)
|
CONF_UNIT_SYSTEM_METRIC, CONF_UNIT_SYSTEM_IMPERIAL, CONF_TEMPERATURE_UNIT)
|
||||||
from homeassistant.util import location as location_util, dt as dt_util
|
from homeassistant.util import location as location_util, dt as dt_util
|
||||||
|
from homeassistant.util.yaml import SECRET_YAML
|
||||||
from homeassistant.util.async import run_coroutine_threadsafe
|
from homeassistant.util.async import run_coroutine_threadsafe
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.components.config.group import (
|
from homeassistant.components.config.group import (
|
||||||
@ -32,6 +33,7 @@ from tests.common import (
|
|||||||
|
|
||||||
CONFIG_DIR = get_test_config_dir()
|
CONFIG_DIR = get_test_config_dir()
|
||||||
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
|
YAML_PATH = os.path.join(CONFIG_DIR, config_util.YAML_CONFIG_FILE)
|
||||||
|
SECRET_PATH = os.path.join(CONFIG_DIR, SECRET_YAML)
|
||||||
VERSION_PATH = os.path.join(CONFIG_DIR, config_util.VERSION_FILE)
|
VERSION_PATH = os.path.join(CONFIG_DIR, config_util.VERSION_FILE)
|
||||||
GROUP_PATH = os.path.join(CONFIG_DIR, GROUP_CONFIG_PATH)
|
GROUP_PATH = os.path.join(CONFIG_DIR, GROUP_CONFIG_PATH)
|
||||||
AUTOMATIONS_PATH = os.path.join(CONFIG_DIR, AUTOMATIONS_CONFIG_PATH)
|
AUTOMATIONS_PATH = os.path.join(CONFIG_DIR, AUTOMATIONS_CONFIG_PATH)
|
||||||
@ -62,6 +64,9 @@ class TestConfig(unittest.TestCase):
|
|||||||
if os.path.isfile(YAML_PATH):
|
if os.path.isfile(YAML_PATH):
|
||||||
os.remove(YAML_PATH)
|
os.remove(YAML_PATH)
|
||||||
|
|
||||||
|
if os.path.isfile(SECRET_PATH):
|
||||||
|
os.remove(SECRET_PATH)
|
||||||
|
|
||||||
if os.path.isfile(VERSION_PATH):
|
if os.path.isfile(VERSION_PATH):
|
||||||
os.remove(VERSION_PATH)
|
os.remove(VERSION_PATH)
|
||||||
|
|
||||||
@ -85,6 +90,7 @@ class TestConfig(unittest.TestCase):
|
|||||||
config_util.create_default_config(CONFIG_DIR, False)
|
config_util.create_default_config(CONFIG_DIR, False)
|
||||||
|
|
||||||
assert os.path.isfile(YAML_PATH)
|
assert os.path.isfile(YAML_PATH)
|
||||||
|
assert os.path.isfile(SECRET_PATH)
|
||||||
assert os.path.isfile(VERSION_PATH)
|
assert os.path.isfile(VERSION_PATH)
|
||||||
assert os.path.isfile(GROUP_PATH)
|
assert os.path.isfile(GROUP_PATH)
|
||||||
assert os.path.isfile(AUTOMATIONS_PATH)
|
assert os.path.isfile(AUTOMATIONS_PATH)
|
||||||
|
@ -302,7 +302,7 @@ class TestSecrets(unittest.TestCase):
|
|||||||
config_dir = get_test_config_dir()
|
config_dir = get_test_config_dir()
|
||||||
yaml.clear_secret_cache()
|
yaml.clear_secret_cache()
|
||||||
self._yaml_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
self._yaml_path = os.path.join(config_dir, YAML_CONFIG_FILE)
|
||||||
self._secret_path = os.path.join(config_dir, yaml._SECRET_YAML)
|
self._secret_path = os.path.join(config_dir, yaml.SECRET_YAML)
|
||||||
self._sub_folder_path = os.path.join(config_dir, 'subFolder')
|
self._sub_folder_path = os.path.join(config_dir, 'subFolder')
|
||||||
self._unrelated_path = os.path.join(config_dir, 'unrelated')
|
self._unrelated_path = os.path.join(config_dir, 'unrelated')
|
||||||
|
|
||||||
@ -351,7 +351,7 @@ class TestSecrets(unittest.TestCase):
|
|||||||
def test_secret_overrides_parent(self):
|
def test_secret_overrides_parent(self):
|
||||||
"""Test loading current directory secret overrides the parent."""
|
"""Test loading current directory secret overrides the parent."""
|
||||||
expected = {'api_password': 'override'}
|
expected = {'api_password': 'override'}
|
||||||
load_yaml(os.path.join(self._sub_folder_path, yaml._SECRET_YAML),
|
load_yaml(os.path.join(self._sub_folder_path, yaml.SECRET_YAML),
|
||||||
'http_pw: override')
|
'http_pw: override')
|
||||||
self._yaml = load_yaml(os.path.join(self._sub_folder_path, 'sub.yaml'),
|
self._yaml = load_yaml(os.path.join(self._sub_folder_path, 'sub.yaml'),
|
||||||
'http:\n'
|
'http:\n'
|
||||||
@ -365,7 +365,7 @@ class TestSecrets(unittest.TestCase):
|
|||||||
|
|
||||||
def test_secrets_from_unrelated_fails(self):
|
def test_secrets_from_unrelated_fails(self):
|
||||||
"""Test loading secrets from unrelated folder fails."""
|
"""Test loading secrets from unrelated folder fails."""
|
||||||
load_yaml(os.path.join(self._unrelated_path, yaml._SECRET_YAML),
|
load_yaml(os.path.join(self._unrelated_path, yaml.SECRET_YAML),
|
||||||
'test: failure')
|
'test: failure')
|
||||||
with self.assertRaises(HomeAssistantError):
|
with self.assertRaises(HomeAssistantError):
|
||||||
load_yaml(os.path.join(self._sub_folder_path, 'sub.yaml'),
|
load_yaml(os.path.join(self._sub_folder_path, 'sub.yaml'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user