Set unique id in aladdin connect config flow (#118798)

This commit is contained in:
Joost Lekkerkerker 2024-06-04 16:00:53 +02:00 committed by GitHub
parent 1eb13b48a2
commit 2ac5f8db06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 208 additions and 22 deletions

View File

@ -4,9 +4,10 @@ from collections.abc import Mapping
import logging
from typing import Any
import voluptuous as vol
import jwt
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 .const import DOMAIN
@ -35,20 +36,33 @@ class AladdinConnectOAuth2FlowHandler(AbstractOAuth2FlowHandler, domain=DOMAIN):
) -> ConfigFlowResult:
"""Dialog that informs the user that reauth is required."""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema({}),
)
return self.async_show_form(step_id="reauth_confirm")
return await self.async_step_user()
async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
"""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(
self.reauth_entry,
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
def logger(self) -> logging.Logger:

View 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,
)

View File

@ -1,10 +1,9 @@
"""Test the Aladdin Connect Garage Door config flow."""
from unittest.mock import patch
from unittest.mock import AsyncMock
import pytest
from homeassistant import config_entries
from homeassistant.components.aladdin_connect.const import (
DOMAIN,
OAUTH2_AUTHORIZE,
@ -14,16 +13,25 @@ from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER, ConfigFlowResult
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
CLIENT_ID = "1234"
CLIENT_SECRET = "5678"
EXAMPLE_TOKEN = (
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhYWFhYWFhYS1iYmJiLWNjY2MtZGRk"
"ZC1lZWVlZWVlZWVlZWUiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsInVzZXJuYW"
"1lIjoidGVzdEB0ZXN0LmNvbSJ9.CTU1YItIrUl8nSM3koJxlFJr5CjLghgc9gS6h45D8dE"
)
@pytest.fixture
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 test_full_flow(
async def _oauth_actions(
hass: HomeAssistant,
result: ConfigFlowResult,
hass_client_no_auth: ClientSessionGenerator,
aioclient_mock: AiohttpClientMocker,
) -> None:
"""Check full flow."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
state = config_entry_oauth2_flow._encode_jwt( # noqa: SLF001
state = config_entry_oauth2_flow._encode_jwt(
hass,
{
"flow_id": result["flow_id"],
@ -69,16 +73,153 @@ async def test_full_flow(
OAUTH2_TOKEN,
json={
"refresh_token": "mock-refresh-token",
"access_token": "mock-access-token",
"access_token": EXAMPLE_TOKEN,
"type": "Bearer",
"expires_in": 60,
},
)
with patch(
"homeassistant.components.aladdin_connect.async_setup_entry", return_value=True
) as mock_setup:
await hass.config_entries.flow.async_configure(result["flow_id"])
async def test_full_flow(
hass: HomeAssistant,
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(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"