No longer allow invalid slugs or extra keys (#24176)

* No longer allow slugs

* Lint

* Remove HASchema

* Lint

* Fix tests
This commit is contained in:
Paulus Schoutsen 2019-05-30 04:33:26 -07:00 committed by Pascal Vizeli
parent b3d8f8620c
commit 59ce31f44f
6 changed files with 8 additions and 275 deletions

View file

@ -1,6 +1,5 @@
"""Helpers for config validation using voluptuous."""
import inspect
import json
import logging
import os
import re
@ -17,7 +16,7 @@ from pkg_resources import parse_version
import homeassistant.util.dt as dt_util
from homeassistant.const import (
CONF_ABOVE, CONF_ALIAS, CONF_BELOW, CONF_CONDITION, CONF_ENTITY_ID,
CONF_ENTITY_NAMESPACE, CONF_NAME, CONF_PLATFORM, CONF_SCAN_INTERVAL,
CONF_ENTITY_NAMESPACE, CONF_PLATFORM, CONF_SCAN_INTERVAL,
CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_METRIC, CONF_VALUE_TEMPLATE,
CONF_TIMEOUT, ENTITY_MATCH_ALL, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET,
TEMP_CELSIUS, TEMP_FAHRENHEIT, WEEKDAYS, __version__)
@ -29,13 +28,6 @@ from homeassistant.util import slugify as util_slugify
# pylint: disable=invalid-name
TIME_PERIOD_ERROR = "offset {} should be format 'HH:MM' or 'HH:MM:SS'"
OLD_SLUG_VALIDATION = r'^[a-z0-9_]+$'
OLD_ENTITY_ID_VALIDATION = r"^(\w+)\.(\w+)$"
# Keep track of invalid slugs and entity ids found so we can create a
# persistent notification. Rare temporary exception to use a global.
INVALID_SLUGS_FOUND = {}
INVALID_ENTITY_IDS_FOUND = {}
INVALID_EXTRA_KEYS_FOUND = []
# Home Assistant types
@ -176,17 +168,6 @@ def entity_id(value: Any) -> str:
value = string(value).lower()
if valid_entity_id(value):
return value
if re.match(OLD_ENTITY_ID_VALIDATION, value):
# To ease the breaking change, we allow old slugs for now
# Remove after 0.94 or 1.0
fixed = '.'.join(util_slugify(part) for part in value.split('.', 1))
INVALID_ENTITY_IDS_FOUND[value] = fixed
logging.getLogger(__name__).warning(
"Found invalid entity_id %s, please update with %s. This "
"will become a breaking change.",
value, fixed
)
return value
raise vol.Invalid('Entity ID {} is an invalid entity id'.format(value))
@ -377,21 +358,7 @@ def schema_with_slug_keys(value_schema: Union[T, Callable]) -> Callable:
raise vol.Invalid('expected dictionary')
for key in value.keys():
try:
slug(key)
except vol.Invalid:
# To ease the breaking change, we allow old slugs for now
# Remove after 0.94 or 1.0
if re.match(OLD_SLUG_VALIDATION, key):
fixed = util_slugify(key)
INVALID_SLUGS_FOUND[key] = fixed
logging.getLogger(__name__).warning(
"Found invalid slug %s, please update with %s. This "
"will be come a breaking change.",
key, fixed
)
else:
raise
slug(key)
return schema(value)
return verify
@ -656,88 +623,7 @@ def key_dependency(key, dependency):
# Schemas
class HASchema(vol.Schema):
"""Schema class that allows us to mark PREVENT_EXTRA errors as warnings."""
def __call__(self, data):
"""Override __call__ to mark PREVENT_EXTRA as warning."""
try:
return super().__call__(data)
except vol.Invalid as orig_err:
if self.extra != vol.PREVENT_EXTRA:
raise
# orig_error is of type vol.MultipleInvalid (see super __call__)
assert isinstance(orig_err, vol.MultipleInvalid)
# pylint: disable=no-member
# If it fails with PREVENT_EXTRA, try with ALLOW_EXTRA
self.extra = vol.ALLOW_EXTRA
# In case it still fails the following will raise
try:
validated = super().__call__(data)
finally:
self.extra = vol.PREVENT_EXTRA
# This is a legacy config, print warning
extra_key_errs = [err.path[-1] for err in orig_err.errors
if err.error_message == 'extra keys not allowed']
if not extra_key_errs:
# This should not happen (all errors should be extra key
# errors). Let's raise the original error anyway.
raise orig_err
WHITELIST = [
re.compile(CONF_NAME),
re.compile(CONF_PLATFORM),
re.compile('.*_topic'),
]
msg = "Your configuration contains extra keys " \
"that the platform does not support.\n" \
"Please remove "
submsg = ', '.join('[{}]'.format(err) for err in
extra_key_errs)
submsg += '. '
# Add file+line information, if available
if hasattr(data, '__config_file__'):
submsg += " (See {}, line {}). ".format(
data.__config_file__, data.__line__)
# Add configuration source information, if available
if hasattr(data, '__configuration_source__'):
submsg += "\nConfiguration source: {}. ".format(
data.__configuration_source__)
redacted_data = {}
# Print configuration causing the error, but filter any potentially
# sensitive data
for k, v in data.items():
if (any(regex.match(k) for regex in WHITELIST) or
k in extra_key_errs):
redacted_data[k] = v
else:
redacted_data[k] = '<redacted>'
submsg += "\nOffending data: {}".format(
json.dumps(redacted_data))
msg += submsg
logging.getLogger(__name__).warning(msg)
INVALID_EXTRA_KEYS_FOUND.append(submsg)
# Return legacy validated config
return validated
def extend(self, schema, required=None, extra=None):
"""Extend this schema and convert it to HASchema if necessary."""
ret = super().extend(schema, required=required, extra=extra)
if extra is not None:
return ret
return HASchema(ret.schema, required=required, extra=self.extra)
PLATFORM_SCHEMA = HASchema({
PLATFORM_SCHEMA = vol.Schema({
vol.Required(CONF_PLATFORM): string,
vol.Optional(CONF_ENTITY_NAMESPACE): string,
vol.Optional(CONF_SCAN_INTERVAL): time_period