mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Set unique id in aladdin connect config flow (#118798)
This commit is contained in:
parent
1eb13b48a2
commit
2ac5f8db06
@ -4,9 +4,10 @@ from collections.abc import Mapping
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import jwt
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
|
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
|
||||||
|
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
|
||||||
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
|
from homeassistant.helpers.config_entry_oauth2_flow import AbstractOAuth2FlowHandler
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
@ -35,20 +36,33 @@ class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
|
|||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Dialog that informs the user that reauth is required."""
|
"""Dialog that informs the user that reauth is required."""
|
||||||
if user_input is None:
|
if user_input is None:
|
||||||
return self.async_show_form(
|
return self.async_show_form(step_id="reauth_confirm")
|
||||||
step_id="reauth_confirm",
|
|
||||||
data_schema=vol.Schema({}),
|
|
||||||
)
|
|
||||||
return await self.async_step_user()
|
return await self.async_step_user()
|
||||||
|
|
||||||
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
|
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
|
||||||
"""Create an oauth config entry or update existing entry for reauth."""
|
"""Create an oauth config entry or update existing entry for reauth."""
|
||||||
if self.reauth_entry:
|
token_payload = jwt.decode(
|
||||||
|
data[CONF_TOKEN][CONF_ACCESS_TOKEN], options={"verify_signature": False}
|
||||||
|
)
|
||||||
|
if not self.reauth_entry:
|
||||||
|
await self.async_set_unique_id(token_payload["sub"])
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=token_payload["username"],
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.reauth_entry.unique_id == token_payload["username"]:
|
||||||
return self.async_update_reload_and_abort(
|
return self.async_update_reload_and_abort(
|
||||||
self.reauth_entry,
|
self.reauth_entry,
|
||||||
data=data,
|
data=data,
|
||||||
|
unique_id=token_payload["sub"],
|
||||||
)
|
)
|
||||||
return await super().async_oauth_create_entry(data)
|
if self.reauth_entry.unique_id == token_payload["sub"]:
|
||||||
|
return self.async_update_reload_and_abort(self.reauth_entry, data=data)
|
||||||
|
|
||||||
|
return self.async_abort(reason="wrong_account")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logger(self) -> logging.Logger:
|
def logger(self) -> logging.Logger:
|
||||||
|
31
tests/components/aladdin_connect/conftest.py
Normal file
31
tests/components/aladdin_connect/conftest.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""Test fixtures for the Aladdin Connect Garage Door integration."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.aladdin_connect import DOMAIN
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
"""Override async_setup_entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
yield mock_setup_entry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
|
"""Return an Aladdin Connect config entry."""
|
||||||
|
return MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={},
|
||||||
|
title="test@test.com",
|
||||||
|
unique_id="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
|
||||||
|
version=2,
|
||||||
|
)
|
@ -1,10 +1,9 @@
|
|||||||
"""Test the Aladdin Connect Garage Door config flow."""
|
"""Test the Aladdin Connect Garage Door config flow."""
|
||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import config_entries
|
|
||||||
from homeassistant.components.aladdin_connect.const import (
|
from homeassistant.components.aladdin_connect.const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
OAUTH2_AUTHORIZE,
|
OAUTH2_AUTHORIZE,
|
||||||
@ -14,16 +13,25 @@ from homeassistant.components.application_credentials import (
|
|||||||
ClientCredential,
|
ClientCredential,
|
||||||
async_import_client_credential,
|
async_import_client_credential,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER, ConfigFlowResult
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
from homeassistant.helpers import config_entry_oauth2_flow
|
from homeassistant.helpers import config_entry_oauth2_flow
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
CLIENT_ID = "1234"
|
CLIENT_ID = "1234"
|
||||||
CLIENT_SECRET = "5678"
|
CLIENT_SECRET = "5678"
|
||||||
|
|
||||||
|
EXAMPLE_TOKEN = (
|
||||||
|
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYWFhYWFhYS1iYmJiLWNjY2MtZGRk"
|
||||||
|
"ZC1lZWVlZWVlZWVlZWUiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsInVzZXJuYW"
|
||||||
|
"1lIjoidGVzdEB0ZXN0LmNvbSJ9.CTU1YItIrUl8nSM3koJxlFJr5CjLghgc9gS6h45D8dE"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def setup_credentials(hass: HomeAssistant) -> None:
|
async def setup_credentials(hass: HomeAssistant) -> None:
|
||||||
@ -36,17 +44,13 @@ async def setup_credentials(hass: HomeAssistant) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("current_request_with_host", "setup_credentials")
|
async def _oauth_actions(
|
||||||
async def test_full_flow(
|
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
result: ConfigFlowResult,
|
||||||
hass_client_no_auth: ClientSessionGenerator,
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
aioclient_mock: AiohttpClientMocker,
|
aioclient_mock: AiohttpClientMocker,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Check full flow."""
|
state = config_entry_oauth2_flow._encode_jwt(
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
|
||||||
)
|
|
||||||
state = config_entry_oauth2_flow._encode_jwt( # noqa: SLF001
|
|
||||||
hass,
|
hass,
|
||||||
{
|
{
|
||||||
"flow_id": result["flow_id"],
|
"flow_id": result["flow_id"],
|
||||||
@ -69,16 +73,153 @@ async def test_full_flow(
|
|||||||
OAUTH2_TOKEN,
|
OAUTH2_TOKEN,
|
||||||
json={
|
json={
|
||||||
"refresh_token": "mock-refresh-token",
|
"refresh_token": "mock-refresh-token",
|
||||||
"access_token": "mock-access-token",
|
"access_token": EXAMPLE_TOKEN,
|
||||||
"type": "Bearer",
|
"type": "Bearer",
|
||||||
"expires_in": 60,
|
"expires_in": 60,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
|
async def test_full_flow(
|
||||||
) as mock_setup:
|
hass: HomeAssistant,
|
||||||
await hass.config_entries.flow.async_configure(result["flow_id"])
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
current_request_with_host: None,
|
||||||
|
setup_credentials: None,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Check full flow."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["title"] == "test@test.com"
|
||||||
|
assert result["data"]["token"]["access_token"] == EXAMPLE_TOKEN
|
||||||
|
assert result["data"]["token"]["refresh_token"] == "mock-refresh-token"
|
||||||
|
assert result["result"].unique_id == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
|
|
||||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||||
assert len(mock_setup.mock_calls) == 1
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_duplicate_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
current_request_with_host: None,
|
||||||
|
setup_credentials: None,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort with duplicate entry."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauth(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
current_request_with_host: None,
|
||||||
|
setup_credentials: None,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test reauthentication."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": SOURCE_REAUTH,
|
||||||
|
"entry_id": mock_config_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=mock_config_entry.data,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "reauth_successful"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauth_wrong_account(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
current_request_with_host: None,
|
||||||
|
setup_credentials: None,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test reauthentication with wrong account."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={},
|
||||||
|
title="test@test.com",
|
||||||
|
unique_id="aaaaaaaa-bbbb-ffff-dddd-eeeeeeeeeeee",
|
||||||
|
version=2,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": SOURCE_REAUTH,
|
||||||
|
"entry_id": config_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=config_entry.data,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "wrong_account"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reauth_old_account(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
hass_client_no_auth: ClientSessionGenerator,
|
||||||
|
aioclient_mock: AiohttpClientMocker,
|
||||||
|
current_request_with_host: None,
|
||||||
|
setup_credentials: None,
|
||||||
|
mock_setup_entry: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test reauthentication with old account."""
|
||||||
|
config_entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={},
|
||||||
|
title="test@test.com",
|
||||||
|
unique_id="test@test.com",
|
||||||
|
version=2,
|
||||||
|
)
|
||||||
|
config_entry.add_to_hass(hass)
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={
|
||||||
|
"source": SOURCE_REAUTH,
|
||||||
|
"entry_id": config_entry.entry_id,
|
||||||
|
},
|
||||||
|
data=config_entry.data,
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "reauth_confirm"
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
|
||||||
|
await _oauth_actions(hass, result, hass_client_no_auth, aioclient_mock)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_configure(result["flow_id"])
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "reauth_successful"
|
||||||
|
assert config_entry.unique_id == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user