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 socket
import time
import ssl
import requests.certs
import voluptuous as vol
@ -48,6 +49,7 @@ CONF_CERTIFICATE = 'certificate'
CONF_CLIENT_KEY = 'client_key'
CONF_CLIENT_CERT = 'client_cert'
CONF_TLS_INSECURE = 'tls_insecure'
CONF_TLS_VERSION = 'tls_version'
CONF_BIRTH_MESSAGE = 'birth_message'
CONF_WILL_MESSAGE = 'will_message'
@ -67,6 +69,7 @@ DEFAULT_RETAIN = False
DEFAULT_PROTOCOL = PROTOCOL_311
DEFAULT_DISCOVERY = False
DEFAULT_DISCOVERY_PREFIX = 'homeassistant'
DEFAULT_TLS_PROTOCOL = 'auto'
ATTR_TOPIC = 'topic'
ATTR_PAYLOAD = 'payload'
@ -122,6 +125,9 @@ CONFIG_SCHEMA = vol.Schema({
vol.Inclusive(CONF_CLIENT_CERT, 'client_key_auth',
msg=CLIENT_KEY_AUTH_MSG): cv.isfile,
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.All(cv.string, vol.In([PROTOCOL_31, PROTOCOL_311])),
vol.Optional(CONF_EMBEDDED): HBMQTT_CONFIG_SCHEMA,
@ -318,11 +324,27 @@ def async_setup(hass, config):
will_message = conf.get(CONF_WILL_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:
hass.data[DATA_MQTT] = MQTT(
hass, broker, port, client_id, keepalive, username, password,
certificate, client_key, client_cert, tls_insecure, protocol,
will_message, birth_message)
will_message, birth_message, tls_version)
except socket.error:
_LOGGER.exception("Can't connect to the broker. "
"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,
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."""
import paho.mqtt.client as mqtt
@ -409,7 +432,8 @@ class MQTT(object):
if certificate is not None:
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:
self._mqttc.tls_insecure_set(tls_insecure)

View file

@ -4,6 +4,7 @@ from collections import namedtuple, OrderedDict
import unittest
from unittest import mock
import socket
import ssl
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
@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
def test_birth_message(hass):
"""Test sending birth message."""

View file

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