Auto recover mqtt config entry secret if Mosquitto add-on was re-installed (#124514)

Auto recover mqtt config entry secret if Mosquitto add-on is re-installed
This commit is contained in:
Jan Bouwhuis 2024-08-26 09:40:25 +02:00 committed by GitHub
parent 76ebb0df08
commit 65216df3a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 132 additions and 0 deletions

View File

@ -401,6 +401,36 @@ class FlowHandler(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult: ) -> ConfigFlowResult:
"""Handle re-authentication with MQTT broker.""" """Handle re-authentication with MQTT broker."""
self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
if is_hassio(self.hass):
# Check if entry setup matches the add-on discovery config
addon_manager = get_addon_manager(self.hass)
try:
addon_discovery_config = (
await addon_manager.async_get_addon_discovery_info()
)
except AddonError:
# Follow manual flow if we have an error
pass
else:
# Check if the addon secrets need to be renewed.
# This will repair the config entry,
# in case the official Mosquitto Broker addon was re-installed.
if (
entry_data[CONF_BROKER] == addon_discovery_config[CONF_HOST]
and entry_data[CONF_PORT] == addon_discovery_config[CONF_PORT]
and entry_data.get(CONF_USERNAME)
== (username := addon_discovery_config.get(CONF_USERNAME))
and entry_data.get(CONF_PASSWORD)
!= (password := addon_discovery_config.get(CONF_PASSWORD))
):
_LOGGER.info(
"Executing autorecovery %s add-on secrets",
addon_manager.addon_name,
)
return await self.async_step_reauth_confirm(
user_input={CONF_USERNAME: username, CONF_PASSWORD: password}
)
return await self.async_step_reauth_confirm() return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm( async def async_step_reauth_confirm(

View File

@ -1587,6 +1587,108 @@ async def test_step_reauth(
await hass.async_block_till_done() await hass.async_block_till_done()
@pytest.mark.parametrize("discovery_info", [{"config": ADD_ON_DISCOVERY_INFO.copy()}])
@pytest.mark.usefixtures(
"mqtt_client_mock", "mock_reload_after_entry_update", "supervisor", "addon_running"
)
async def test_step_hassio_reauth(
hass: HomeAssistant, mock_try_connection: MagicMock, addon_info: AsyncMock
) -> None:
"""Test that the reauth step works in case the Mosquitto broker add-on was re-installed."""
# Set up entry data based on the discovery data, but with a stale password
entry_data = {
mqtt.CONF_BROKER: "core-mosquitto",
CONF_PORT: 1883,
CONF_USERNAME: "mock-user",
CONF_PASSWORD: "stale-secret",
}
addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry
config_entry = MockConfigEntry(domain=mqtt.DOMAIN, data=entry_data)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
assert config_entry.data.get(CONF_PASSWORD) == "stale-secret"
# Start reauth flow
mock_try_connection.reset_mock()
mock_try_connection.return_value = True
config_entry.async_start_reauth(hass)
await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 0
# Assert the entry is updated automatically
assert config_entry.data.get(CONF_PASSWORD) == "mock-pass"
mock_try_connection.assert_called_once_with(
{
"broker": "core-mosquitto",
"port": 1883,
"username": "mock-user",
"password": "mock-pass",
}
)
@pytest.mark.parametrize(
("discovery_info", "discovery_info_side_effect", "broker"),
[
({"config": ADD_ON_DISCOVERY_INFO.copy()}, AddonError, "core-mosquitto"),
({"config": ADD_ON_DISCOVERY_INFO.copy()}, None, "broker-not-addon"),
],
)
@pytest.mark.usefixtures(
"mqtt_client_mock", "mock_reload_after_entry_update", "supervisor", "addon_running"
)
async def test_step_hassio_reauth_no_discovery_info(
hass: HomeAssistant,
mock_try_connection: MagicMock,
addon_info: AsyncMock,
broker: str,
) -> None:
"""Test hassio reauth flow defaults to manual flow.
Test that the reauth step defaults to
normal reauth flow if fetching add-on discovery info failed,
or the broker is not the add-on.
"""
# Set up entry data based on the discovery data, but with a stale password
entry_data = {
mqtt.CONF_BROKER: broker,
CONF_PORT: 1883,
CONF_USERNAME: "mock-user",
CONF_PASSWORD: "wrong-pass",
}
addon_info["hostname"] = "core-mosquitto"
# Prepare the config entry
config_entry = MockConfigEntry(domain=mqtt.DOMAIN, data=entry_data)
config_entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(config_entry.entry_id)
assert config_entry.data.get(CONF_PASSWORD) == "wrong-pass"
# Start reauth flow
mock_try_connection.reset_mock()
mock_try_connection.return_value = True
config_entry.async_start_reauth(hass)
await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
result = flows[0]
assert result["step_id"] == "reauth_confirm"
assert result["context"]["source"] == "reauth"
# Assert the entry is not updated
assert config_entry.data.get(CONF_PASSWORD) == "wrong-pass"
mock_try_connection.assert_not_called()
async def test_options_user_connection_fails( async def test_options_user_connection_fails(
hass: HomeAssistant, mock_try_connection_time_out: MagicMock hass: HomeAssistant, mock_try_connection_time_out: MagicMock
) -> None: ) -> None: