Be able to select mqtt:tls_version for Python < 3.6 (#6442)

* Be able to select tls_version

* This test should always assert this value, not only in 3.6

* Disable linting on future property (py36)

* Only allow TLS 1.0, 1.1 and 1.2

* Fix line length issue

* Fix check config tests

* Allow auto as a TLS version
This commit is contained in:
Dennis de Greef 2017-03-13 06:02:59 +01:00 committed by Paulus Schoutsen
parent 0aa8933df6
commit 5183cb5903
3 changed files with 75 additions and 3 deletions

View file

@ -9,6 +9,7 @@ import logging
import os import os
import socket import socket
import time import time
import ssl
import requests.certs import requests.certs
import voluptuous as vol import voluptuous as vol
@ -48,6 +49,7 @@ CONF_CERTIFICATE = 'certificate'
CONF_CLIENT_KEY = 'client_key' CONF_CLIENT_KEY = 'client_key'
CONF_CLIENT_CERT = 'client_cert' CONF_CLIENT_CERT = 'client_cert'
CONF_TLS_INSECURE = 'tls_insecure' CONF_TLS_INSECURE = 'tls_insecure'
CONF_TLS_VERSION = 'tls_version'
CONF_BIRTH_MESSAGE = 'birth_message' CONF_BIRTH_MESSAGE = 'birth_message'
CONF_WILL_MESSAGE = 'will_message' CONF_WILL_MESSAGE = 'will_message'
@ -67,6 +69,7 @@ DEFAULT_RETAIN = False
DEFAULT_PROTOCOL = PROTOCOL_311 DEFAULT_PROTOCOL = PROTOCOL_311
DEFAULT_DISCOVERY = False DEFAULT_DISCOVERY = False
DEFAULT_DISCOVERY_PREFIX = 'homeassistant' DEFAULT_DISCOVERY_PREFIX = 'homeassistant'
DEFAULT_TLS_PROTOCOL = 'auto'
ATTR_TOPIC = 'topic' ATTR_TOPIC = 'topic'
ATTR_PAYLOAD = 'payload' ATTR_PAYLOAD = 'payload'
@ -122,6 +125,9 @@ CONFIG_SCHEMA = vol.Schema({
vol.Inclusive(CONF_CLIENT_CERT, 'client_key_auth', vol.Inclusive(CONF_CLIENT_CERT, 'client_key_auth',
msg=CLIENT_KEY_AUTH_MSG): cv.isfile, msg=CLIENT_KEY_AUTH_MSG): cv.isfile,
vol.Optional(CONF_TLS_INSECURE): cv.boolean, vol.Optional(CONF_TLS_INSECURE): cv.boolean,
vol.Optional(CONF_TLS_VERSION,
default=DEFAULT_TLS_PROTOCOL): vol.Any('auto', '1.0',
'1.1', '1.2'),
vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL): vol.Optional(CONF_PROTOCOL, default=DEFAULT_PROTOCOL):
vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])), vol.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])),
vol.Optional(CONF_EMBEDDED): HBMQTT_CONFIG_SCHEMA, vol.Optional(CONF_EMBEDDED): HBMQTT_CONFIG_SCHEMA,
@ -318,11 +324,27 @@ def async_setup(hass, config):
will_message = conf.get(CONF_WILL_MESSAGE) will_message = conf.get(CONF_WILL_MESSAGE)
birth_message = conf.get(CONF_BIRTH_MESSAGE) birth_message = conf.get(CONF_BIRTH_MESSAGE)
# Be able to override versions other than TLSv1.0 under Python3.6
conf_tls_version = conf.get(CONF_TLS_VERSION)
if conf_tls_version == '1.2':
tls_version = ssl.PROTOCOL_TLSv1_2
elif conf_tls_version == '1.1':
tls_version = ssl.PROTOCOL_TLSv1_1
elif conf_tls_version == '1.0':
tls_version = ssl.PROTOCOL_TLSv1
else:
import sys
# Python3.6 supports automatic negotiation of highest TLS version
if sys.hexversion >= 0x03060000:
tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member
else:
tls_version = ssl.PROTOCOL_TLSv1
try: try:
hass.data[DATA_MQTT] = MQTT( hass.data[DATA_MQTT] = MQTT(
hass, broker, port, client_id, keepalive, username, password, hass, broker, port, client_id, keepalive, username, password,
certificate, client_key, client_cert, tls_insecure, protocol, certificate, client_key, client_cert, tls_insecure, protocol,
will_message, birth_message) will_message, birth_message, tls_version)
except socket.error: except socket.error:
_LOGGER.exception("Can't connect to the broker. " _LOGGER.exception("Can't connect to the broker. "
"Please check your settings and the broker itself") "Please check your settings and the broker itself")
@ -380,7 +402,8 @@ class MQTT(object):
def __init__(self, hass, broker, port, client_id, keepalive, username, def __init__(self, hass, broker, port, client_id, keepalive, username,
password, certificate, client_key, client_cert, password, certificate, client_key, client_cert,
tls_insecure, protocol, will_message, birth_message): tls_insecure, protocol, will_message, birth_message,
tls_version):
"""Initialize Home Assistant MQTT client.""" """Initialize Home Assistant MQTT client."""
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
@ -409,7 +432,8 @@ class MQTT(object):
if certificate is not None: if certificate is not None:
self._mqttc.tls_set( self._mqttc.tls_set(
certificate, certfile=client_cert, keyfile=client_key) certificate, certfile=client_cert,
keyfile=client_key, tls_version=tls_version)
if tls_insecure is not None: if tls_insecure is not None:
self._mqttc.tls_insecure_set(tls_insecure) self._mqttc.tls_insecure_set(tls_insecure)

View file

@ -4,6 +4,7 @@ from collections import namedtuple, OrderedDict
import unittest import unittest
from unittest import mock from unittest import mock
import socket import socket
import ssl
import voluptuous as vol import voluptuous as vol
@ -409,6 +410,52 @@ def test_setup_uses_certificate_not_on_mqtts_port(hass):
assert mock_MQTT.mock_calls[0][1][7] != mqttsCertificateBundle assert mock_MQTT.mock_calls[0][1][7] != mqttsCertificateBundle
@asyncio.coroutine
def test_setup_without_tls_config_uses_tlsv1_under_python36(hass):
"""Test setup defaults to TLSv1 under python3.6."""
test_broker_cfg = {mqtt.DOMAIN: {mqtt.CONF_BROKER: 'test-broker'}}
with mock.patch('homeassistant.components.mqtt.MQTT') as mock_MQTT:
yield from async_setup_component(hass, mqtt.DOMAIN, test_broker_cfg)
assert mock_MQTT.called
import sys
if sys.hexversion >= 0x03060000:
expectedTlsVersion = ssl.PROTOCOL_TLS # pylint: disable=no-member
else:
expectedTlsVersion = ssl.PROTOCOL_TLSv1
assert mock_MQTT.mock_calls[0][1][14] == expectedTlsVersion
@asyncio.coroutine
def test_setup_with_tls_config_uses_tls_version1_2(hass):
"""Test setup uses specified TLS version."""
test_broker_cfg = {mqtt.DOMAIN: {mqtt.CONF_BROKER: 'test-broker',
'tls_version': '1.2'}}
with mock.patch('homeassistant.components.mqtt.MQTT') as mock_MQTT:
yield from async_setup_component(hass, mqtt.DOMAIN, test_broker_cfg)
assert mock_MQTT.called
assert mock_MQTT.mock_calls[0][1][14] == ssl.PROTOCOL_TLSv1_2
@asyncio.coroutine
def test_setup_with_tls_config_of_v1_under_python36_only_uses_v1(hass):
"""Test setup uses TLSv1.0 if explicitly chosen."""
test_broker_cfg = {mqtt.DOMAIN: {mqtt.CONF_BROKER: 'test-broker',
'tls_version': '1.0'}}
with mock.patch('homeassistant.components.mqtt.MQTT') as mock_MQTT:
yield from async_setup_component(hass, mqtt.DOMAIN, test_broker_cfg)
assert mock_MQTT.called
assert mock_MQTT.mock_calls[0][1][14] == ssl.PROTOCOL_TLSv1
@asyncio.coroutine @asyncio.coroutine
def test_birth_message(hass): def test_birth_message(hass):
"""Test sending birth message.""" """Test sending birth message."""

View file

@ -108,6 +108,7 @@ class TestCheckConfig(unittest.TestCase):
'protocol': '3.1.1', 'protocol': '3.1.1',
'discovery': False, 'discovery': False,
'discovery_prefix': 'homeassistant', 'discovery_prefix': 'homeassistant',
'tls_version': 'auto',
}, },
'light': []}, 'light': []},
res['components'] res['components']