KNX ConfigFlow: Validate contents of knxkeys file (#84411)

pull/84607/head^2
Matthias Alphart 2022-12-27 21:00:19 +01:00 committed by GitHub
parent c99025be26
commit a752232de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 44 deletions

View File

@ -440,18 +440,37 @@ class KNXCommonFlow(ABC, FlowHandler):
) -> FlowResult: ) -> FlowResult:
"""Configure secure knxkeys used to authenticate.""" """Configure secure knxkeys used to authenticate."""
errors = {} errors = {}
description_placeholders = {}
if user_input is not None: if user_input is not None:
connection_type = self.new_entry_data[CONF_KNX_CONNECTION_TYPE]
storage_key = CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME] storage_key = CONST_KNX_STORAGE_KEY + user_input[CONF_KNX_KNXKEY_FILENAME]
try: try:
await load_keyring( keyring = await load_keyring(
path=self.hass.config.path(STORAGE_DIR, storage_key), path=self.hass.config.path(STORAGE_DIR, storage_key),
password=user_input[CONF_KNX_KNXKEY_PASSWORD], password=user_input[CONF_KNX_KNXKEY_PASSWORD],
) )
except FileNotFoundError: except FileNotFoundError:
errors[CONF_KNX_KNXKEY_FILENAME] = "file_not_found" errors[CONF_KNX_KNXKEY_FILENAME] = "keyfile_not_found"
except InvalidSecureConfiguration: except InvalidSecureConfiguration:
errors[CONF_KNX_KNXKEY_PASSWORD] = "invalid_signature" errors[CONF_KNX_KNXKEY_PASSWORD] = "keyfile_invalid_signature"
else:
if (
connection_type == CONF_KNX_TUNNELING_TCP_SECURE
and self._selected_tunnel is not None
):
tunnel_endpoints = []
if host_ia := self._selected_tunnel.individual_address:
tunnel_endpoints = keyring.get_tunnel_interfaces_by_host(
host=host_ia
)
if not tunnel_endpoints:
errors["base"] = "keyfile_no_tunnel_for_host"
description_placeholders = {CONF_HOST: str(host_ia)}
if connection_type == CONF_KNX_ROUTING_SECURE:
if not (keyring.backbone is not None and keyring.backbone.key):
errors["base"] = "keyfile_no_backbone_key"
if not errors: if not errors:
self.new_entry_data |= KNXConfigEntryData( self.new_entry_data |= KNXConfigEntryData(
@ -463,10 +482,7 @@ class KNXCommonFlow(ABC, FlowHandler):
user_id=None, user_id=None,
user_password=None, user_password=None,
) )
if ( if connection_type == CONF_KNX_ROUTING_SECURE:
self.new_entry_data[CONF_KNX_CONNECTION_TYPE]
== CONF_KNX_ROUTING_SECURE
):
title = ( title = (
"Secure Routing as" "Secure Routing as"
f" {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}" f" {self.new_entry_data[CONF_KNX_INDIVIDUAL_ADDRESS]}"
@ -488,7 +504,10 @@ class KNXCommonFlow(ABC, FlowHandler):
} }
return self.async_show_form( return self.async_show_form(
step_id="secure_knxkeys", data_schema=vol.Schema(fields), errors=errors step_id="secure_knxkeys",
data_schema=vol.Schema(fields),
errors=errors,
description_placeholders=description_placeholders,
) )
async def async_step_routing(self, user_input: dict | None = None) -> FlowResult: async def async_step_routing(self, user_input: dict | None = None) -> FlowResult:

View File

@ -2,18 +2,21 @@
"config": { "config": {
"step": { "step": {
"connection_type": { "connection_type": {
"title": "KNX connection",
"description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.",
"data": { "data": {
"connection_type": "KNX Connection Type" "connection_type": "KNX Connection Type"
} }
}, },
"tunnel": { "tunnel": {
"title": "Tunnel",
"description": "Please select a gateway from the list.", "description": "Please select a gateway from the list.",
"data": { "data": {
"gateway": "KNX Tunnel Connection" "gateway": "KNX Tunnel Connection"
} }
}, },
"manual_tunnel": { "manual_tunnel": {
"title": "Tunnel settings",
"description": "Please enter the connection information of your tunneling device.", "description": "Please enter the connection information of your tunneling device.",
"data": { "data": {
"tunneling_type": "KNX Tunneling Type", "tunneling_type": "KNX Tunneling Type",
@ -30,6 +33,7 @@
} }
}, },
"secure_key_source": { "secure_key_source": {
"title": "KNX IP-Secure",
"description": "Select how you want to configure KNX/IP Secure.", "description": "Select how you want to configure KNX/IP Secure.",
"menu_options": { "menu_options": {
"secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys",
@ -38,6 +42,7 @@
} }
}, },
"secure_knxkeys": { "secure_knxkeys": {
"title": "Keyfile",
"description": "Please enter the information for your `.knxkeys` file.", "description": "Please enter the information for your `.knxkeys` file.",
"data": { "data": {
"knxkeys_filename": "The filename of your `.knxkeys` file (including extension)", "knxkeys_filename": "The filename of your `.knxkeys` file (including extension)",
@ -49,6 +54,7 @@
} }
}, },
"secure_tunnel_manual": { "secure_tunnel_manual": {
"title": "Secure tunnelling",
"description": "Please enter your IP secure information.", "description": "Please enter your IP secure information.",
"data": { "data": {
"user_id": "User ID", "user_id": "User ID",
@ -62,6 +68,7 @@
} }
}, },
"secure_routing_manual": { "secure_routing_manual": {
"title": "Secure routing",
"description": "Please enter your IP secure information.", "description": "Please enter your IP secure information.",
"data": { "data": {
"backbone_key": "Backbone key", "backbone_key": "Backbone key",
@ -73,6 +80,7 @@
} }
}, },
"routing": { "routing": {
"title": "Routing",
"description": "Please configure the routing options.", "description": "Please configure the routing options.",
"data": { "data": {
"individual_address": "Individual address", "individual_address": "Individual address",
@ -96,8 +104,10 @@
"invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.",
"invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'",
"invalid_ip_address": "Invalid IPv4 address.", "invalid_ip_address": "Invalid IPv4 address.",
"invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.",
"file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/", "keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.",
"keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.",
"keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/",
"no_router_discovered": "No KNXnet/IP router was discovered on the network.", "no_router_discovered": "No KNXnet/IP router was discovered on the network.",
"no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.",
"unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway."
@ -106,12 +116,14 @@
"options": { "options": {
"step": { "step": {
"options_init": { "options_init": {
"title": "KNX Settings",
"menu_options": { "menu_options": {
"connection_type": "Configure KNX interface", "connection_type": "Configure KNX interface",
"communication_settings": "Communication settings" "communication_settings": "Communication settings"
} }
}, },
"communication_settings": { "communication_settings": {
"title": "Communication settings",
"data": { "data": {
"state_updater": "State updater", "state_updater": "State updater",
"rate_limit": "Rate limit" "rate_limit": "Rate limit"
@ -122,18 +134,21 @@
} }
}, },
"connection_type": { "connection_type": {
"title": "[%key:component::knx::config::step::connection_type::title%]",
"description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.", "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.",
"data": { "data": {
"connection_type": "KNX Connection Type" "connection_type": "KNX Connection Type"
} }
}, },
"tunnel": { "tunnel": {
"title": "[%key:component::knx::config::step::tunnel::title%]",
"description": "[%key:component::knx::config::step::tunnel::description%]", "description": "[%key:component::knx::config::step::tunnel::description%]",
"data": { "data": {
"gateway": "[%key:component::knx::config::step::tunnel::data::gateway%]" "gateway": "[%key:component::knx::config::step::tunnel::data::gateway%]"
} }
}, },
"manual_tunnel": { "manual_tunnel": {
"title": "[%key:component::knx::config::step::manual_tunnel::title%]",
"description": "[%key:component::knx::config::step::manual_tunnel::description%]", "description": "[%key:component::knx::config::step::manual_tunnel::description%]",
"data": { "data": {
"tunneling_type": "[%key:component::knx::config::step::manual_tunnel::data::tunneling_type%]", "tunneling_type": "[%key:component::knx::config::step::manual_tunnel::data::tunneling_type%]",
@ -150,6 +165,7 @@
} }
}, },
"secure_key_source": { "secure_key_source": {
"title": "[%key:component::knx::config::step::secure_key_source::title%]",
"description": "[%key:component::knx::config::step::secure_key_source::description%]", "description": "[%key:component::knx::config::step::secure_key_source::description%]",
"menu_options": { "menu_options": {
"secure_knxkeys": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_knxkeys%]", "secure_knxkeys": "[%key:component::knx::config::step::secure_key_source::menu_options::secure_knxkeys%]",
@ -158,6 +174,7 @@
} }
}, },
"secure_knxkeys": { "secure_knxkeys": {
"title": "[%key:component::knx::config::step::secure_knxkeys::title%]",
"description": "[%key:component::knx::config::step::secure_knxkeys::description%]", "description": "[%key:component::knx::config::step::secure_knxkeys::description%]",
"data": { "data": {
"knxkeys_filename": "[%key:component::knx::config::step::secure_knxkeys::data::knxkeys_filename%]", "knxkeys_filename": "[%key:component::knx::config::step::secure_knxkeys::data::knxkeys_filename%]",
@ -169,6 +186,7 @@
} }
}, },
"secure_tunnel_manual": { "secure_tunnel_manual": {
"title": "[%key:component::knx::config::step::secure_tunnel_manual::title%]",
"description": "[%key:component::knx::config::step::secure_tunnel_manual::description%]", "description": "[%key:component::knx::config::step::secure_tunnel_manual::description%]",
"data": { "data": {
"user_id": "[%key:component::knx::config::step::secure_tunnel_manual::data::user_id%]", "user_id": "[%key:component::knx::config::step::secure_tunnel_manual::data::user_id%]",
@ -182,6 +200,7 @@
} }
}, },
"secure_routing_manual": { "secure_routing_manual": {
"title": "[%key:component::knx::config::step::secure_routing_manual::title%]",
"description": "[%key:component::knx::config::step::secure_routing_manual::description%]", "description": "[%key:component::knx::config::step::secure_routing_manual::description%]",
"data": { "data": {
"backbone_key": "[%key:component::knx::config::step::secure_routing_manual::data::backbone_key%]", "backbone_key": "[%key:component::knx::config::step::secure_routing_manual::data::backbone_key%]",
@ -193,6 +212,7 @@
} }
}, },
"routing": { "routing": {
"title": "[%key:component::knx::config::step::routing::title%]",
"description": "[%key:component::knx::config::step::routing::description%]", "description": "[%key:component::knx::config::step::routing::description%]",
"data": { "data": {
"individual_address": "[%key:component::knx::config::step::routing::data::individual_address%]", "individual_address": "[%key:component::knx::config::step::routing::data::individual_address%]",
@ -212,8 +232,10 @@
"invalid_backbone_key": "[%key:component::knx::config::error::invalid_backbone_key%]", "invalid_backbone_key": "[%key:component::knx::config::error::invalid_backbone_key%]",
"invalid_individual_address": "[%key:component::knx::config::error::invalid_individual_address%]", "invalid_individual_address": "[%key:component::knx::config::error::invalid_individual_address%]",
"invalid_ip_address": "[%key:component::knx::config::error::invalid_ip_address%]", "invalid_ip_address": "[%key:component::knx::config::error::invalid_ip_address%]",
"invalid_signature": "[%key:component::knx::config::error::invalid_signature%]", "keyfile_no_backbone_key": "[%key:component::knx::config::error::keyfile_no_backbone_key%]",
"file_not_found": "[%key:component::knx::config::error::file_not_found%]", "keyfile_invalid_signature": "[%key:component::knx::config::error::keyfile_invalid_signature%]",
"keyfile_no_tunnel_for_host": "[%key:component::knx::config::error::keyfile_no_tunnel_for_host%]",
"keyfile_not_found": "[%key:component::knx::config::error::keyfile_not_found%]",
"no_router_discovered": "[%key:component::knx::config::error::no_router_discovered%]", "no_router_discovered": "[%key:component::knx::config::error::no_router_discovered%]",
"no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]", "no_tunnel_discovered": "[%key:component::knx::config::error::no_tunnel_discovered%]",
"unsupported_tunnel_type": "[%key:component::knx::config::error::unsupported_tunnel_type%]" "unsupported_tunnel_type": "[%key:component::knx::config::error::unsupported_tunnel_type%]"

View File

@ -6,11 +6,13 @@
}, },
"error": { "error": {
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/",
"invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.",
"invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'",
"invalid_ip_address": "Invalid IPv4 address.", "invalid_ip_address": "Invalid IPv4 address.",
"invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.",
"keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.",
"keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.",
"keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/",
"no_router_discovered": "No KNXnet/IP router was discovered on the network.", "no_router_discovered": "No KNXnet/IP router was discovered on the network.",
"no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.",
"unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway."
@ -20,7 +22,8 @@
"data": { "data": {
"connection_type": "KNX Connection Type" "connection_type": "KNX Connection Type"
}, },
"description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.",
"title": "KNX connection"
}, },
"manual_tunnel": { "manual_tunnel": {
"data": { "data": {
@ -36,7 +39,8 @@
"port": "Port of the KNX/IP tunneling device.", "port": "Port of the KNX/IP tunneling device.",
"route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections."
}, },
"description": "Please enter the connection information of your tunneling device." "description": "Please enter the connection information of your tunneling device.",
"title": "Tunnel settings"
}, },
"routing": { "routing": {
"data": { "data": {
@ -50,7 +54,8 @@
"individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`",
"local_ip": "Leave blank to use auto-discovery." "local_ip": "Leave blank to use auto-discovery."
}, },
"description": "Please configure the routing options." "description": "Please configure the routing options.",
"title": "Routing"
}, },
"secure_key_source": { "secure_key_source": {
"description": "Select how you want to configure KNX/IP Secure.", "description": "Select how you want to configure KNX/IP Secure.",
@ -58,7 +63,8 @@
"secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys",
"secure_routing_manual": "Configure IP secure backbone key manually", "secure_routing_manual": "Configure IP secure backbone key manually",
"secure_tunnel_manual": "Configure IP secure credentials manually" "secure_tunnel_manual": "Configure IP secure credentials manually"
} },
"title": "KNX IP-Secure"
}, },
"secure_knxkeys": { "secure_knxkeys": {
"data": { "data": {
@ -69,7 +75,8 @@
"knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`", "knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`",
"knxkeys_password": "This was set when exporting the file from ETS." "knxkeys_password": "This was set when exporting the file from ETS."
}, },
"description": "Please enter the information for your `.knxkeys` file." "description": "Please enter the information for your `.knxkeys` file.",
"title": "Keyfile"
}, },
"secure_routing_manual": { "secure_routing_manual": {
"data": { "data": {
@ -80,7 +87,8 @@
"backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'",
"sync_latency_tolerance": "Default is 1000." "sync_latency_tolerance": "Default is 1000."
}, },
"description": "Please enter your IP secure information." "description": "Please enter your IP secure information.",
"title": "Secure routing"
}, },
"secure_tunnel_manual": { "secure_tunnel_manual": {
"data": { "data": {
@ -93,24 +101,28 @@
"user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.",
"user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS."
}, },
"description": "Please enter your IP secure information." "description": "Please enter your IP secure information.",
"title": "Secure tunnelling"
}, },
"tunnel": { "tunnel": {
"data": { "data": {
"gateway": "KNX Tunnel Connection" "gateway": "KNX Tunnel Connection"
}, },
"description": "Please select a gateway from the list." "description": "Please select a gateway from the list.",
"title": "Tunnel"
} }
} }
}, },
"options": { "options": {
"error": { "error": {
"cannot_connect": "Failed to connect", "cannot_connect": "Failed to connect",
"file_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/",
"invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.", "invalid_backbone_key": "Invalid backbone key. 32 hexadecimal numbers expected.",
"invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'", "invalid_individual_address": "Value does not match pattern for KNX individual address.\n'area.line.device'",
"invalid_ip_address": "Invalid IPv4 address.", "invalid_ip_address": "Invalid IPv4 address.",
"invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.", "keyfile_invalid_signature": "The password to decrypt the `.knxkeys` file is wrong.",
"keyfile_no_backbone_key": "The `.knxkeys` file does not contain a backbone key for secure routing.",
"keyfile_no_tunnel_for_host": "The `.knxkeys` file does not contain credentials for host `{host}`.",
"keyfile_not_found": "The specified `.knxkeys` file was not found in the path config/.storage/knx/",
"no_router_discovered": "No KNXnet/IP router was discovered on the network.", "no_router_discovered": "No KNXnet/IP router was discovered on the network.",
"no_tunnel_discovered": "Could not find a KNX tunneling server on your network.", "no_tunnel_discovered": "Could not find a KNX tunneling server on your network.",
"unsupported_tunnel_type": "Selected tunnelling type not supported by gateway." "unsupported_tunnel_type": "Selected tunnelling type not supported by gateway."
@ -124,13 +136,15 @@
"data_description": { "data_description": {
"rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40", "rate_limit": "Maximum outgoing telegrams per second.\n`0` to disable limit. Recommended: 0 or 20 to 40",
"state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options." "state_updater": "Set default for reading states from the KNX Bus. When disabled, Home Assistant will not actively retrieve entity states from the KNX Bus. Can be overridden by `sync_state` entity options."
} },
"title": "Communication settings"
}, },
"connection_type": { "connection_type": {
"data": { "data": {
"connection_type": "KNX Connection Type" "connection_type": "KNX Connection Type"
}, },
"description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing." "description": "Please enter the connection type we should use for your KNX connection. \n AUTOMATIC - The integration takes care of the connectivity to your KNX Bus by performing a gateway scan. \n TUNNELING - The integration will connect to your KNX bus via tunneling. \n ROUTING - The integration will connect to your KNX bus via routing.",
"title": "KNX connection"
}, },
"manual_tunnel": { "manual_tunnel": {
"data": { "data": {
@ -146,13 +160,15 @@
"port": "Port of the KNX/IP tunneling device.", "port": "Port of the KNX/IP tunneling device.",
"route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections." "route_back": "Enable if your KNXnet/IP tunneling server is behind NAT. Only applies for UDP connections."
}, },
"description": "Please enter the connection information of your tunneling device." "description": "Please enter the connection information of your tunneling device.",
"title": "Tunnel settings"
}, },
"options_init": { "options_init": {
"menu_options": { "menu_options": {
"communication_settings": "Communication settings", "communication_settings": "Communication settings",
"connection_type": "Configure KNX interface" "connection_type": "Configure KNX interface"
} },
"title": "KNX Settings"
}, },
"routing": { "routing": {
"data": { "data": {
@ -166,7 +182,8 @@
"individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`", "individual_address": "KNX address to be used by Home Assistant, e.g. `0.0.4`",
"local_ip": "Leave blank to use auto-discovery." "local_ip": "Leave blank to use auto-discovery."
}, },
"description": "Please configure the routing options." "description": "Please configure the routing options.",
"title": "Routing"
}, },
"secure_key_source": { "secure_key_source": {
"description": "Select how you want to configure KNX/IP Secure.", "description": "Select how you want to configure KNX/IP Secure.",
@ -174,7 +191,8 @@
"secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys", "secure_knxkeys": "Use a `.knxkeys` file containing IP secure keys",
"secure_routing_manual": "Configure IP secure backbone key manually", "secure_routing_manual": "Configure IP secure backbone key manually",
"secure_tunnel_manual": "Configure IP secure credentials manually" "secure_tunnel_manual": "Configure IP secure credentials manually"
} },
"title": "KNX IP-Secure"
}, },
"secure_knxkeys": { "secure_knxkeys": {
"data": { "data": {
@ -185,7 +203,8 @@
"knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`", "knxkeys_filename": "The file is expected to be found in your config directory in `.storage/knx/`.\nIn Home Assistant OS this would be `/config/.storage/knx/`\nExample: `my_project.knxkeys`",
"knxkeys_password": "This was set when exporting the file from ETS." "knxkeys_password": "This was set when exporting the file from ETS."
}, },
"description": "Please enter the information for your `.knxkeys` file." "description": "Please enter the information for your `.knxkeys` file.",
"title": "Keyfile"
}, },
"secure_routing_manual": { "secure_routing_manual": {
"data": { "data": {
@ -196,7 +215,8 @@
"backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'", "backbone_key": "Can be seen in the 'Security' report of an ETS project. Eg. '00112233445566778899AABBCCDDEEFF'",
"sync_latency_tolerance": "Default is 1000." "sync_latency_tolerance": "Default is 1000."
}, },
"description": "Please enter your IP secure information." "description": "Please enter your IP secure information.",
"title": "Secure routing"
}, },
"secure_tunnel_manual": { "secure_tunnel_manual": {
"data": { "data": {
@ -209,13 +229,15 @@
"user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.", "user_id": "This is often tunnel number +1. So 'Tunnel 2' would have User-ID '3'.",
"user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS." "user_password": "Password for the specific tunnel connection set in the 'Properties' panel of the tunnel in ETS."
}, },
"description": "Please enter your IP secure information." "description": "Please enter your IP secure information.",
"title": "Secure tunnelling"
}, },
"tunnel": { "tunnel": {
"data": { "data": {
"gateway": "KNX Tunnel Connection" "gateway": "KNX Tunnel Connection"
}, },
"description": "Please select a gateway from the list." "description": "Please select a gateway from the list.",
"title": "Tunnel"
} }
} }
} }

View File

@ -1,10 +1,11 @@
"""Test the KNX config flow.""" """Test the KNX config flow."""
from unittest.mock import patch from unittest.mock import Mock, patch
import pytest import pytest
from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration from xknx.exceptions.exception import CommunicationError, InvalidSecureConfiguration
from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT from xknx.io import DEFAULT_MCAST_GRP, DEFAULT_MCAST_PORT
from xknx.io.gateway_scanner import GatewayDescriptor from xknx.io.gateway_scanner import GatewayDescriptor
from xknx.telegram import IndividualAddress
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.knx.config_flow import ( from homeassistant.components.knx.config_flow import (
@ -44,6 +45,8 @@ from homeassistant.data_entry_flow import FlowResult, FlowResultType
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
GATEWAY_INDIVIDUAL_ADDRESS = IndividualAddress("1.0.0")
@pytest.fixture(name="knx_setup") @pytest.fixture(name="knx_setup")
def fixture_knx_setup(): def fixture_knx_setup():
@ -63,6 +66,7 @@ def _gateway_descriptor(
"""Get mock gw descriptor.""" """Get mock gw descriptor."""
descriptor = GatewayDescriptor( descriptor = GatewayDescriptor(
name="Test", name="Test",
individual_address=GATEWAY_INDIVIDUAL_ADDRESS,
ip_addr=ip, ip_addr=ip,
port=port, port=port,
local_interface="eth0", local_interface="eth0",
@ -352,9 +356,25 @@ async def test_routing_secure_keyfile(
assert result4["step_id"] == "secure_knxkeys" assert result4["step_id"] == "secure_knxkeys"
assert not result4["errors"] assert not result4["errors"]
# test file without backbone key
with patch( with patch(
"homeassistant.components.knx.config_flow.load_keyring", return_value=True "homeassistant.components.knx.config_flow.load_keyring"
): ) as mock_load_keyring:
mock_keyring = Mock()
mock_keyring.backbone.key = None
mock_load_keyring.return_value = mock_keyring
secure_knxkeys = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys",
CONF_KNX_KNXKEY_PASSWORD: "password",
},
)
assert secure_knxkeys["type"] == FlowResultType.FORM
assert secure_knxkeys["errors"] == {"base": "keyfile_no_backbone_key"}
# test valid file
with patch("homeassistant.components.knx.config_flow.load_keyring"):
routing_secure_knxkeys = await hass.config_entries.flow.async_configure( routing_secure_knxkeys = await hass.config_entries.flow.async_configure(
result4["flow_id"], result4["flow_id"],
{ {
@ -976,8 +996,11 @@ async def test_configure_secure_knxkeys(hass: HomeAssistant, knx_setup):
assert not result["errors"] assert not result["errors"]
with patch( with patch(
"homeassistant.components.knx.config_flow.load_keyring", return_value=True "homeassistant.components.knx.config_flow.load_keyring"
): ) as mock_load_keyring:
mock_keyring = Mock()
mock_keyring.get_tunnel_interfaces_by_host.return_value = ["stub"]
mock_load_keyring.return_value = mock_keyring
secure_knxkeys = await hass.config_entries.flow.async_configure( secure_knxkeys = await hass.config_entries.flow.async_configure(
result["flow_id"], result["flow_id"],
{ {
@ -1031,7 +1054,7 @@ async def test_configure_secure_knxkeys_file_not_found(hass: HomeAssistant):
) )
assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["type"] == FlowResultType.FORM
assert secure_knxkeys["errors"] assert secure_knxkeys["errors"]
assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_FILENAME] == "file_not_found" assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_FILENAME] == "keyfile_not_found"
async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant): async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant):
@ -1059,7 +1082,39 @@ async def test_configure_secure_knxkeys_invalid_signature(hass: HomeAssistant):
) )
assert secure_knxkeys["type"] == FlowResultType.FORM assert secure_knxkeys["type"] == FlowResultType.FORM
assert secure_knxkeys["errors"] assert secure_knxkeys["errors"]
assert secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD] == "invalid_signature" assert (
secure_knxkeys["errors"][CONF_KNX_KNXKEY_PASSWORD]
== "keyfile_invalid_signature"
)
async def test_configure_secure_knxkeys_no_tunnel_for_host(hass: HomeAssistant):
"""Test configure secure knxkeys but file was not found."""
menu_step = await _get_menu_step(hass)
result = await hass.config_entries.flow.async_configure(
menu_step["flow_id"],
{"next_step_id": "secure_knxkeys"},
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "secure_knxkeys"
assert not result["errors"]
with patch(
"homeassistant.components.knx.config_flow.load_keyring"
) as mock_load_keyring:
mock_keyring = Mock()
mock_keyring.get_tunnel_interfaces_by_host.return_value = []
mock_load_keyring.return_value = mock_keyring
secure_knxkeys = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_KNX_KNXKEY_FILENAME: "testcase.knxkeys",
CONF_KNX_KNXKEY_PASSWORD: "password",
},
)
assert secure_knxkeys["type"] == FlowResultType.FORM
assert secure_knxkeys["errors"] == {"base": "keyfile_no_tunnel_for_host"}
async def test_options_flow_connection_type( async def test_options_flow_connection_type(
@ -1185,9 +1240,7 @@ async def test_options_flow_secure_manual_to_keyfile(
assert result4["step_id"] == "secure_knxkeys" assert result4["step_id"] == "secure_knxkeys"
assert not result4["errors"] assert not result4["errors"]
with patch( with patch("homeassistant.components.knx.config_flow.load_keyring"):
"homeassistant.components.knx.config_flow.load_keyring", return_value=True
):
secure_knxkeys = await hass.config_entries.options.async_configure( secure_knxkeys = await hass.config_entries.options.async_configure(
result4["flow_id"], result4["flow_id"],
{ {