1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00

Add reauth flow to Skybell (#75682)

Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Robert Hillis 2022-08-25 09:24:09 -04:00 committed by GitHub
parent 8ddee30787
commit 4f526a9212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 16 deletions

View File

@ -11,7 +11,7 @@ from homeassistant.components.repairs.models import IssueSeverity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType
@ -59,8 +59,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
try:
devices = await api.async_initialize()
except SkybellAuthenticationException:
return False
except SkybellAuthenticationException as ex:
raise ConfigEntryAuthFailed from ex
except SkybellException as ex:
raise ConfigEntryNotReady(f"Unable to connect to Skybell service: {ex}") from ex

View File

@ -1,6 +1,7 @@
"""Config flow for Skybell integration."""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from aioskybell import Skybell, exceptions
@ -17,6 +18,39 @@ from .const import DOMAIN
class SkybellFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Skybell."""
reauth_email: str
async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
"""Handle a reauthorization flow request."""
self.reauth_email = entry_data[CONF_EMAIL]
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(
self, user_input: dict[str, str] | None = None
) -> FlowResult:
"""Handle user's reauth credentials."""
errors = {}
if user_input:
password = user_input[CONF_PASSWORD]
entry_id = self.context["entry_id"]
if entry := self.hass.config_entries.async_get_entry(entry_id):
_, error = await self._async_validate_input(self.reauth_email, password)
if error is None:
self.hass.config_entries.async_update_entry(
entry,
data=entry.data | user_input,
)
await self.hass.config_entries.async_reload(entry.entry_id)
return self.async_abort(reason="reauth_successful")
errors["base"] = error
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema({vol.Required(CONF_PASSWORD): str}),
description_placeholders={CONF_EMAIL: self.reauth_email},
errors=errors,
)
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:

View File

@ -6,6 +6,13 @@
"email": "[%key:common::config_flow::data::email%]",
"password": "[%key:common::config_flow::data::password%]"
}
},
"reauth_confirm": {
"description": "Please update your password for {email}",
"title": "[%key:common::config_flow::title::reauth%]",
"data": {
"password": "[%key:common::config_flow::data::password%]"
}
}
},
"error": {

View File

@ -15,6 +15,13 @@
"email": "Email",
"password": "Password"
}
},
"reauth_confirm": {
"description": "Please update your password for {email}",
"title": "Reauthenticate Integration",
"data": {
"password": "Password"
}
}
}
},

View File

@ -3,12 +3,20 @@ from unittest.mock import patch
from aioskybell import exceptions
from homeassistant import config_entries
from homeassistant.components.skybell.const import DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_PASSWORD, CONF_SOURCE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import CONF_CONFIG_FLOW, _patch_skybell, _patch_skybell_devices
from . import (
CONF_CONFIG_FLOW,
PASSWORD,
USER_ID,
_patch_skybell,
_patch_skybell_devices,
)
from tests.common import MockConfigEntry
@ -20,16 +28,9 @@ def _patch_setup_entry() -> None:
)
def _patch_setup() -> None:
return patch(
"homeassistant.components.skybell.async_setup",
return_value=True,
)
async def test_flow_user(hass: HomeAssistant) -> None:
"""Test that the user step works."""
with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry(), _patch_setup():
with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry():
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
)
@ -45,6 +46,7 @@ async def test_flow_user(hass: HomeAssistant) -> None:
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "user"
assert result["data"] == CONF_CONFIG_FLOW
assert result["result"].unique_id == USER_ID
async def test_flow_user_already_configured(hass: HomeAssistant) -> None:
@ -55,10 +57,10 @@ async def test_flow_user_already_configured(hass: HomeAssistant) -> None:
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
)
with _patch_skybell(), _patch_skybell_devices():
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
@ -99,3 +101,69 @@ async def test_flow_user_unknown_error(hass: HomeAssistant) -> None:
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
assert result["errors"] == {"base": "unknown"}
async def test_step_reauth(hass: HomeAssistant) -> None:
"""Test the reauth flow."""
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_CONFIG_FLOW)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
CONF_SOURCE: config_entries.SOURCE_REAUTH,
"entry_id": entry.entry_id,
"unique_id": entry.unique_id,
},
data=entry.data,
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "reauth_confirm"
with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry():
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_PASSWORD: PASSWORD},
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "reauth_successful"
async def test_step_reauth_failed(hass: HomeAssistant) -> None:
"""Test the reauth flow fails and recovers."""
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_CONFIG_FLOW)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={
CONF_SOURCE: config_entries.SOURCE_REAUTH,
"entry_id": entry.entry_id,
"unique_id": entry.unique_id,
},
data=entry.data,
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "reauth_confirm"
with patch("homeassistant.components.skybell.Skybell.async_login") as skybell_mock:
skybell_mock.side_effect = exceptions.SkybellAuthenticationException(hass)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_PASSWORD: PASSWORD},
)
assert result["type"] == FlowResultType.FORM
assert result["errors"] == {"base": "invalid_auth"}
with _patch_skybell(), _patch_skybell_devices(), _patch_setup_entry():
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_PASSWORD: PASSWORD},
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "reauth_successful"