MQTT embedded broker has to set its own password. (#15929)
parent
34e1f1b6da
commit
d0e4c95bbc
|
@ -32,7 +32,8 @@ from homeassistant.util.async_ import (
|
|||
from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_STOP, CONF_VALUE_TEMPLATE, CONF_USERNAME,
|
||||
CONF_PASSWORD, CONF_PORT, CONF_PROTOCOL, CONF_PAYLOAD)
|
||||
from homeassistant.components.mqtt.server import HBMQTT_CONFIG_SCHEMA
|
||||
|
||||
from .server import HBMQTT_CONFIG_SCHEMA
|
||||
|
||||
REQUIREMENTS = ['paho-mqtt==1.3.1']
|
||||
|
||||
|
@ -306,7 +307,8 @@ async def _async_setup_server(hass: HomeAssistantType,
|
|||
return None
|
||||
|
||||
success, broker_config = \
|
||||
await server.async_start(hass, conf.get(CONF_EMBEDDED))
|
||||
await server.async_start(
|
||||
hass, conf.get(CONF_PASSWORD), conf.get(CONF_EMBEDDED))
|
||||
|
||||
if not success:
|
||||
return None
|
||||
|
@ -349,6 +351,16 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool:
|
|||
if CONF_EMBEDDED not in conf and CONF_BROKER in conf:
|
||||
broker_config = None
|
||||
else:
|
||||
if (conf.get(CONF_PASSWORD) is None and
|
||||
config.get('http') is not None and
|
||||
config['http'].get('api_password') is not None):
|
||||
_LOGGER.error("Starting from 0.77, embedded MQTT broker doesn't"
|
||||
" use api_password as default password any more."
|
||||
" Please set password configuration. See https://"
|
||||
"home-assistant.io/docs/mqtt/broker#embedded-broker"
|
||||
" for details")
|
||||
return False
|
||||
|
||||
broker_config = await _async_setup_server(hass, config)
|
||||
|
||||
if CONF_BROKER in conf:
|
||||
|
|
|
@ -27,27 +27,29 @@ HBMQTT_CONFIG_SCHEMA = vol.Any(None, vol.Schema({
|
|||
})
|
||||
}, extra=vol.ALLOW_EXTRA))
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_start(hass, server_config):
|
||||
def async_start(hass, password, server_config):
|
||||
"""Initialize MQTT Server.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
from hbmqtt.broker import Broker, BrokerException
|
||||
|
||||
passwd = tempfile.NamedTemporaryFile()
|
||||
try:
|
||||
passwd = tempfile.NamedTemporaryFile()
|
||||
|
||||
if server_config is None:
|
||||
server_config, client_config = generate_config(hass, passwd)
|
||||
server_config, client_config = generate_config(
|
||||
hass, passwd, password)
|
||||
else:
|
||||
client_config = None
|
||||
|
||||
broker = Broker(server_config, hass.loop)
|
||||
yield from broker.start()
|
||||
except BrokerException:
|
||||
logging.getLogger(__name__).exception("Error initializing MQTT server")
|
||||
_LOGGER.exception("Error initializing MQTT server")
|
||||
return False, None
|
||||
finally:
|
||||
passwd.close()
|
||||
|
@ -63,9 +65,10 @@ def async_start(hass, server_config):
|
|||
return True, client_config
|
||||
|
||||
|
||||
def generate_config(hass, passwd):
|
||||
def generate_config(hass, passwd, password):
|
||||
"""Generate a configuration based on current Home Assistant instance."""
|
||||
from homeassistant.components.mqtt import PROTOCOL_311
|
||||
from . import PROTOCOL_311
|
||||
|
||||
config = {
|
||||
'listeners': {
|
||||
'default': {
|
||||
|
@ -79,29 +82,26 @@ def generate_config(hass, passwd):
|
|||
},
|
||||
},
|
||||
'auth': {
|
||||
'allow-anonymous': hass.config.api.api_password is None
|
||||
'allow-anonymous': password is None
|
||||
},
|
||||
'plugins': ['auth_anonymous'],
|
||||
}
|
||||
|
||||
if hass.config.api.api_password:
|
||||
if password:
|
||||
username = 'homeassistant'
|
||||
password = hass.config.api.api_password
|
||||
|
||||
# Encrypt with what hbmqtt uses to verify
|
||||
from passlib.apps import custom_app_context
|
||||
|
||||
passwd.write(
|
||||
'homeassistant:{}\n'.format(
|
||||
custom_app_context.encrypt(
|
||||
hass.config.api.api_password)).encode('utf-8'))
|
||||
custom_app_context.encrypt(password)).encode('utf-8'))
|
||||
passwd.flush()
|
||||
|
||||
config['auth']['password-file'] = passwd.name
|
||||
config['plugins'].append('auth_file')
|
||||
else:
|
||||
username = None
|
||||
password = None
|
||||
|
||||
client_config = ('localhost', 1883, username, password, None, PROTOCOL_311)
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import sys
|
|||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.setup import setup_component
|
||||
import homeassistant.components.mqtt as mqtt
|
||||
|
||||
|
@ -19,9 +20,6 @@ class TestMQTT:
|
|||
def setup_method(self, method):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
setup_component(self.hass, 'http', {
|
||||
'api_password': 'super_secret'
|
||||
})
|
||||
|
||||
def teardown_method(self, method):
|
||||
"""Stop everything that was started."""
|
||||
|
@ -32,14 +30,36 @@ class TestMQTT:
|
|||
@patch('hbmqtt.broker.Broker', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker.start', Mock(return_value=mock_coro()))
|
||||
@patch('homeassistant.components.mqtt.MQTT')
|
||||
def test_creating_config_with_http_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started and subscribe/publish msg."""
|
||||
def test_creating_config_with_http_pass_only(self, mock_mqtt):
|
||||
"""Test if the MQTT server failed starts.
|
||||
|
||||
Since 0.77, MQTT server has to setup its own password.
|
||||
If user has api_password but don't have mqtt.password, MQTT component
|
||||
will fail to start
|
||||
"""
|
||||
mock_mqtt().async_connect.return_value = mock_coro(True)
|
||||
self.hass.bus.listen_once = MagicMock()
|
||||
password = 'super_secret'
|
||||
assert not setup_component(self.hass, mqtt.DOMAIN, {
|
||||
'http': {'api_password': 'http_secret'}
|
||||
})
|
||||
|
||||
self.hass.config.api = MagicMock(api_password=password)
|
||||
assert setup_component(self.hass, mqtt.DOMAIN, {})
|
||||
@patch('passlib.apps.custom_app_context', Mock(return_value=''))
|
||||
@patch('tempfile.NamedTemporaryFile', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker.start', Mock(return_value=mock_coro()))
|
||||
@patch('homeassistant.components.mqtt.MQTT')
|
||||
def test_creating_config_with_pass_and_no_http_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started with password.
|
||||
|
||||
Since 0.77, MQTT server has to setup its own password.
|
||||
"""
|
||||
mock_mqtt().async_connect.return_value = mock_coro(True)
|
||||
self.hass.bus.listen_once = MagicMock()
|
||||
password = 'mqtt_secret'
|
||||
|
||||
assert setup_component(self.hass, mqtt.DOMAIN, {
|
||||
mqtt.DOMAIN: {CONF_PASSWORD: password},
|
||||
})
|
||||
assert mock_mqtt.called
|
||||
from pprint import pprint
|
||||
pprint(mock_mqtt.mock_calls)
|
||||
|
@ -51,8 +71,33 @@ class TestMQTT:
|
|||
@patch('hbmqtt.broker.Broker', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker.start', Mock(return_value=mock_coro()))
|
||||
@patch('homeassistant.components.mqtt.MQTT')
|
||||
def test_creating_config_with_http_no_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started and subscribe/publish msg."""
|
||||
def test_creating_config_with_pass_and_http_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started with password.
|
||||
|
||||
Since 0.77, MQTT server has to setup its own password.
|
||||
"""
|
||||
mock_mqtt().async_connect.return_value = mock_coro(True)
|
||||
self.hass.bus.listen_once = MagicMock()
|
||||
password = 'mqtt_secret'
|
||||
|
||||
self.hass.config.api = MagicMock(api_password='api_password')
|
||||
assert setup_component(self.hass, mqtt.DOMAIN, {
|
||||
'http': {'api_password': 'http_secret'},
|
||||
mqtt.DOMAIN: {CONF_PASSWORD: password},
|
||||
})
|
||||
assert mock_mqtt.called
|
||||
from pprint import pprint
|
||||
pprint(mock_mqtt.mock_calls)
|
||||
assert mock_mqtt.mock_calls[1][1][5] == 'homeassistant'
|
||||
assert mock_mqtt.mock_calls[1][1][6] == password
|
||||
|
||||
@patch('passlib.apps.custom_app_context', Mock(return_value=''))
|
||||
@patch('tempfile.NamedTemporaryFile', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker', Mock(return_value=MagicMock()))
|
||||
@patch('hbmqtt.broker.Broker.start', Mock(return_value=mock_coro()))
|
||||
@patch('homeassistant.components.mqtt.MQTT')
|
||||
def test_creating_config_without_pass(self, mock_mqtt):
|
||||
"""Test if the MQTT server gets started without password."""
|
||||
mock_mqtt().async_connect.return_value = mock_coro(True)
|
||||
self.hass.bus.listen_once = MagicMock()
|
||||
|
||||
|
|
Loading…
Reference in New Issue