mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 21:06:19 +00:00
Fix ESPHome reauth not being triggered on incorrect password (#152911)
This commit is contained in:
@@ -57,6 +57,7 @@ from .manager import async_replace_device
|
||||
|
||||
ERROR_REQUIRES_ENCRYPTION_KEY = "requires_encryption_key"
|
||||
ERROR_INVALID_ENCRYPTION_KEY = "invalid_psk"
|
||||
ERROR_INVALID_PASSWORD_AUTH = "invalid_auth"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ZERO_NOISE_PSK = "MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA="
|
||||
@@ -137,6 +138,11 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
self._password = ""
|
||||
return await self._async_authenticate_or_add()
|
||||
|
||||
if error == ERROR_INVALID_PASSWORD_AUTH or (
|
||||
error is None and self._device_info and self._device_info.uses_password
|
||||
):
|
||||
return await self.async_step_authenticate()
|
||||
|
||||
if error is None and entry_data.get(CONF_NOISE_PSK):
|
||||
# Device was configured with encryption but now connects without it.
|
||||
# Check if it's the same device before offering to remove encryption.
|
||||
@@ -690,13 +696,15 @@ class EsphomeFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
cli = APIClient(
|
||||
host,
|
||||
port or DEFAULT_PORT,
|
||||
"",
|
||||
self._password or "",
|
||||
zeroconf_instance=zeroconf_instance,
|
||||
noise_psk=noise_psk,
|
||||
)
|
||||
try:
|
||||
await cli.connect()
|
||||
self._device_info = await cli.device_info()
|
||||
except InvalidAuthAPIError:
|
||||
return ERROR_INVALID_PASSWORD_AUTH
|
||||
except RequiresEncryptionAPIError:
|
||||
return ERROR_REQUIRES_ENCRYPTION_KEY
|
||||
except InvalidEncryptionKeyAPIError as ex:
|
||||
|
||||
@@ -372,6 +372,9 @@ class ESPHomeManager:
|
||||
"""Subscribe to states and list entities on successful API login."""
|
||||
try:
|
||||
await self._on_connect()
|
||||
except InvalidAuthAPIError as err:
|
||||
_LOGGER.warning("Authentication failed for %s: %s", self.host, err)
|
||||
await self._start_reauth_and_disconnect()
|
||||
except APIConnectionError as err:
|
||||
_LOGGER.warning(
|
||||
"Error getting setting up connection for %s: %s", self.host, err
|
||||
@@ -641,7 +644,14 @@ class ESPHomeManager:
|
||||
if self.reconnect_logic:
|
||||
await self.reconnect_logic.stop()
|
||||
return
|
||||
await self._start_reauth_and_disconnect()
|
||||
|
||||
async def _start_reauth_and_disconnect(self) -> None:
|
||||
"""Start reauth flow and stop reconnection attempts."""
|
||||
self.entry.async_start_reauth(self.hass)
|
||||
await self.cli.disconnect()
|
||||
if self.reconnect_logic:
|
||||
await self.reconnect_logic.stop()
|
||||
|
||||
async def _handle_dynamic_encryption_key(
|
||||
self, device_info: EsphomeDeviceInfo
|
||||
|
||||
@@ -1184,6 +1184,42 @@ async def test_reauth_attempt_to_change_mac_aborts(
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_zeroconf", "mock_setup_entry")
|
||||
async def test_reauth_password_changed(
|
||||
hass: HomeAssistant, mock_client: APIClient
|
||||
) -> None:
|
||||
"""Test reauth when password has changed."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={CONF_HOST: "127.0.0.1", CONF_PORT: 6053, CONF_PASSWORD: "old_password"},
|
||||
unique_id="11:22:33:44:55:aa",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_client.connect.side_effect = InvalidAuthAPIError("Invalid password")
|
||||
|
||||
result = await entry.start_reauth_flow(hass)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "authenticate"
|
||||
assert result["description_placeholders"] == {
|
||||
"name": "Mock Title",
|
||||
}
|
||||
|
||||
mock_client.connect.side_effect = None
|
||||
mock_client.connect.return_value = None
|
||||
mock_client.device_info.return_value = DeviceInfo(
|
||||
uses_password=True, name="test", mac_address="11:22:33:44:55:aa"
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={CONF_PASSWORD: "new_password"}
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
assert entry.data[CONF_PASSWORD] == "new_password"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry", "mock_zeroconf")
|
||||
async def test_reauth_fixed_via_dashboard(
|
||||
hass: HomeAssistant,
|
||||
@@ -1239,7 +1275,7 @@ async def test_reauth_fixed_via_dashboard_add_encryption_remove_password(
|
||||
) -> None:
|
||||
"""Test reauth fixed automatically via dashboard with password removed."""
|
||||
mock_client.device_info.side_effect = (
|
||||
InvalidAuthAPIError,
|
||||
InvalidEncryptionKeyAPIError("Wrong key", "test"),
|
||||
DeviceInfo(uses_password=False, name="test", mac_address="11:22:33:44:55:aa"),
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
from aioesphomeapi import APIClient, DeviceInfo, InvalidAuthAPIError
|
||||
from aioesphomeapi import APIClient, DeviceInfo, InvalidEncryptionKeyAPIError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.esphome import CONF_NOISE_PSK, DOMAIN, dashboard
|
||||
@@ -194,7 +194,7 @@ async def test_new_dashboard_fix_reauth(
|
||||
) -> None:
|
||||
"""Test config entries waiting for reauth are triggered."""
|
||||
mock_client.device_info.side_effect = (
|
||||
InvalidAuthAPIError,
|
||||
InvalidEncryptionKeyAPIError("Wrong key", "test"),
|
||||
DeviceInfo(uses_password=False, name="test", mac_address="11:22:33:44:55:AA"),
|
||||
)
|
||||
|
||||
|
||||
@@ -1455,6 +1455,37 @@ async def test_no_reauth_wrong_mac(
|
||||
)
|
||||
|
||||
|
||||
async def test_auth_error_during_on_connect_triggers_reauth(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
) -> None:
|
||||
"""Test that InvalidAuthAPIError during on_connect triggers reauth."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="11:22:33:44:55:aa",
|
||||
data={
|
||||
CONF_HOST: "test.local",
|
||||
CONF_PORT: 6053,
|
||||
CONF_PASSWORD: "wrong_password",
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
mock_client.device_info_and_list_entities = AsyncMock(
|
||||
side_effect=InvalidAuthAPIError("Invalid password!")
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
flows = hass.config_entries.flow.async_progress(DOMAIN)
|
||||
assert len(flows) == 1
|
||||
assert flows[0]["context"]["source"] == "reauth"
|
||||
assert flows[0]["context"]["entry_id"] == entry.entry_id
|
||||
assert mock_client.disconnect.call_count >= 1
|
||||
|
||||
|
||||
async def test_entry_missing_unique_id(
|
||||
hass: HomeAssistant,
|
||||
mock_client: APIClient,
|
||||
|
||||
Reference in New Issue
Block a user