mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
Store unique user configurations for HomeLink integration (#159111)
This commit is contained in:
@@ -5,6 +5,7 @@ from typing import Any
|
|||||||
|
|
||||||
import botocore.exceptions
|
import botocore.exceptions
|
||||||
from homelink.auth.srp_auth import SRPAuth
|
from homelink.auth.srp_auth import SRPAuth
|
||||||
|
import jwt
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigFlowResult
|
from homeassistant.config_entries import ConfigFlowResult
|
||||||
@@ -38,8 +39,6 @@ class SRPFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
|||||||
"""Ask for username and password."""
|
"""Ask for username and password."""
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
self._async_abort_entries_match({CONF_EMAIL: user_input[CONF_EMAIL]})
|
|
||||||
|
|
||||||
srp_auth = SRPAuth()
|
srp_auth = SRPAuth()
|
||||||
try:
|
try:
|
||||||
tokens = await self.hass.async_add_executor_job(
|
tokens = await self.hass.async_add_executor_job(
|
||||||
@@ -48,12 +47,17 @@ class SRPFlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
|||||||
user_input[CONF_PASSWORD],
|
user_input[CONF_PASSWORD],
|
||||||
)
|
)
|
||||||
except botocore.exceptions.ClientError:
|
except botocore.exceptions.ClientError:
|
||||||
_LOGGER.exception("Error authenticating homelink account")
|
|
||||||
errors["base"] = "srp_auth_failed"
|
errors["base"] = "srp_auth_failed"
|
||||||
except Exception:
|
except Exception:
|
||||||
_LOGGER.exception("An unexpected error occurred")
|
_LOGGER.exception("An unexpected error occurred")
|
||||||
errors["base"] = "unknown"
|
errors["base"] = "unknown"
|
||||||
else:
|
else:
|
||||||
|
access_token = jwt.decode(
|
||||||
|
tokens["AuthenticationResult"]["AccessToken"],
|
||||||
|
options={"verify_signature": False},
|
||||||
|
)
|
||||||
|
await self.async_set_unique_id(access_token["sub"])
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
self.external_data = {"tokens": tokens}
|
self.external_data = {"tokens": tokens}
|
||||||
return await self.async_step_creation()
|
return await self.async_step_creation()
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
"""Makes requests to the state server and stores the resulting data so that the buttons can access it."""
|
"""Establish MQTT connection and listen for event data."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
|
||||||
from homelink.model.device import Device
|
from homelink.model.device import Device
|
||||||
@@ -14,8 +13,6 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.util.ssl import get_default_context
|
from homeassistant.util.ssl import get_default_context
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
type HomeLinkConfigEntry = ConfigEntry[HomeLinkCoordinator]
|
type HomeLinkConfigEntry = ConfigEntry[HomeLinkCoordinator]
|
||||||
type EventCallback = Callable[[HomeLinkEventData], None]
|
type EventCallback = Callable[[HomeLinkEventData], None]
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,17 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
import jwt
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
TEST_CREDENTIALS = {CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"}
|
||||||
|
|
||||||
|
TEST_ACCESS_JWT = jwt.encode({"sub": "some-uuid"}, key="secret")
|
||||||
|
|
||||||
|
|
||||||
async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
async def setup_integration(hass: HomeAssistant, entry: MockConfigEntry) -> None:
|
||||||
"""Set up the homelink integration for testing."""
|
"""Set up the homelink integration for testing."""
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.gentex_homelink import DOMAIN
|
from homeassistant.components.gentex_homelink import DOMAIN
|
||||||
|
|
||||||
|
from . import TEST_ACCESS_JWT
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ def mock_srp_auth() -> Generator[AsyncMock]:
|
|||||||
instance = mock_srp_auth.return_value
|
instance = mock_srp_auth.return_value
|
||||||
instance.async_get_access_token.return_value = {
|
instance.async_get_access_token.return_value = {
|
||||||
"AuthenticationResult": {
|
"AuthenticationResult": {
|
||||||
"AccessToken": "access",
|
"AccessToken": TEST_ACCESS_JWT,
|
||||||
"RefreshToken": "refresh",
|
"RefreshToken": "refresh",
|
||||||
"TokenType": "bearer",
|
"TokenType": "bearer",
|
||||||
"ExpiresIn": 3600,
|
"ExpiresIn": 3600,
|
||||||
@@ -60,6 +62,8 @@ def mock_device() -> AsyncMock:
|
|||||||
def mock_config_entry() -> MockConfigEntry:
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
"""Mock setup entry."""
|
"""Mock setup entry."""
|
||||||
return MockConfigEntry(
|
return MockConfigEntry(
|
||||||
|
unique_id="some-uuid",
|
||||||
|
version=1,
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={
|
data={
|
||||||
"auth_implementation": "gentex_homelink",
|
"auth_implementation": "gentex_homelink",
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.gentex_homelink.const import DOMAIN
|
from homeassistant.components.gentex_homelink.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_USER
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
|
||||||
|
from . import TEST_ACCESS_JWT, TEST_CREDENTIALS, setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_full_flow(
|
async def test_full_flow(
|
||||||
hass: HomeAssistant, mock_srp_auth: AsyncMock, mock_setup_entry: AsyncMock
|
hass: HomeAssistant, mock_srp_auth: AsyncMock, mock_setup_entry: AsyncMock
|
||||||
@@ -26,13 +29,13 @@ async def test_full_flow(
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
|
user_input=TEST_CREDENTIALS,
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["data"] == {
|
assert result["data"] == {
|
||||||
"auth_implementation": "gentex_homelink",
|
"auth_implementation": "gentex_homelink",
|
||||||
"token": {
|
"token": {
|
||||||
"access_token": "access",
|
"access_token": TEST_ACCESS_JWT,
|
||||||
"refresh_token": "refresh",
|
"refresh_token": "refresh",
|
||||||
"expires_in": 3600,
|
"expires_in": 3600,
|
||||||
"token_type": "bearer",
|
"token_type": "bearer",
|
||||||
@@ -40,6 +43,31 @@ async def test_full_flow(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert result["title"] == "SRPAuth"
|
assert result["title"] == "SRPAuth"
|
||||||
|
assert result["result"].unique_id == "some-uuid"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unique_configurations(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_srp_auth: AsyncMock,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Check full flow."""
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert not result["errors"]
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input=TEST_CREDENTIALS,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -69,7 +97,7 @@ async def test_exceptions(
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
|
user_input=TEST_CREDENTIALS,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
@@ -79,6 +107,6 @@ async def test_exceptions(
|
|||||||
|
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
user_input={CONF_EMAIL: "test@test.com", CONF_PASSWORD: "SomePassword"},
|
user_input=TEST_CREDENTIALS,
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|||||||
Reference in New Issue
Block a user