From 6bb55ce79e2bf34edfc80ed1580e6decdb39e30c Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Wed, 7 Aug 2024 21:11:03 +1000 Subject: [PATCH] Add missing application credential to Tesla Fleet (#123271) Co-authored-by: Franck Nijhof --- .../components/tesla_fleet/__init__.py | 10 ++++++- .../tesla_fleet/application_credentials.py | 27 ++++++++++--------- .../components/tesla_fleet/config_flow.py | 17 +++++++++++- homeassistant/components/tesla_fleet/const.py | 5 ++++ tests/components/tesla_fleet/__init__.py | 14 ++++++++++ tests/components/tesla_fleet/conftest.py | 19 ------------- .../tesla_fleet/test_config_flow.py | 5 ++-- 7 files changed, 61 insertions(+), 36 deletions(-) diff --git a/homeassistant/components/tesla_fleet/__init__.py b/homeassistant/components/tesla_fleet/__init__.py index 8257bf75cd0..4eac1168674 100644 --- a/homeassistant/components/tesla_fleet/__init__.py +++ b/homeassistant/components/tesla_fleet/__init__.py @@ -13,6 +13,7 @@ from tesla_fleet_api.exceptions import ( TeslaFleetError, ) +from homeassistant.components.application_credentials import ClientCredential from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN, Platform from homeassistant.core import HomeAssistant @@ -26,7 +27,9 @@ from homeassistant.helpers.config_entry_oauth2_flow import ( import homeassistant.helpers.config_validation as cv from homeassistant.helpers.device_registry import DeviceInfo -from .const import DOMAIN, LOGGER, MODELS +from .application_credentials import TeslaOAuth2Implementation +from .config_flow import OAuth2FlowHandler +from .const import CLIENT_ID, DOMAIN, LOGGER, MODELS, NAME from .coordinator import ( TeslaFleetEnergySiteInfoCoordinator, TeslaFleetEnergySiteLiveCoordinator, @@ -51,6 +54,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) - scopes = token["scp"] region = token["ou_code"].lower() + OAuth2FlowHandler.async_register_implementation( + hass, + TeslaOAuth2Implementation(hass, DOMAIN, ClientCredential(CLIENT_ID, "", NAME)), + ) + implementation = await async_get_config_entry_implementation(hass, entry) oauth_session = OAuth2Session(hass, entry, implementation) refresh_lock = asyncio.Lock() diff --git a/homeassistant/components/tesla_fleet/application_credentials.py b/homeassistant/components/tesla_fleet/application_credentials.py index fda9fce8cec..32e16cc9244 100644 --- a/homeassistant/components/tesla_fleet/application_credentials.py +++ b/homeassistant/components/tesla_fleet/application_credentials.py @@ -5,15 +5,17 @@ import hashlib import secrets from typing import Any -from homeassistant.components.application_credentials import ClientCredential +from homeassistant.components.application_credentials import ( + AuthImplementation, + AuthorizationServer, + ClientCredential, +) from homeassistant.core import HomeAssistant from homeassistant.helpers import config_entry_oauth2_flow -from .const import DOMAIN, SCOPES +from .const import AUTHORIZE_URL, DOMAIN, SCOPES, TOKEN_URL -CLIENT_ID = "71b813eb-4a2e-483a-b831-4dec5cb9bf0d" -AUTHORIZE_URL = "https://auth.tesla.com/oauth2/v3/authorize" -TOKEN_URL = "https://auth.tesla.com/oauth2/v3/token" +AUTH_SERVER = AuthorizationServer(AUTHORIZE_URL, TOKEN_URL) async def async_get_auth_implementation( @@ -23,15 +25,16 @@ async def async_get_auth_implementation( return TeslaOAuth2Implementation( hass, DOMAIN, + credential, ) -class TeslaOAuth2Implementation(config_entry_oauth2_flow.LocalOAuth2Implementation): +class TeslaOAuth2Implementation(AuthImplementation): """Tesla Fleet API Open Source Oauth2 implementation.""" - _name = "Tesla Fleet API" - - def __init__(self, hass: HomeAssistant, domain: str) -> None: + def __init__( + self, hass: HomeAssistant, domain: str, credential: ClientCredential + ) -> None: """Initialize local auth implementation.""" self.hass = hass self._domain = domain @@ -45,10 +48,8 @@ class TeslaOAuth2Implementation(config_entry_oauth2_flow.LocalOAuth2Implementati super().__init__( hass, domain, - CLIENT_ID, - "", # Implementation has no client secret - AUTHORIZE_URL, - TOKEN_URL, + credential, + AUTH_SERVER, ) @property diff --git a/homeassistant/components/tesla_fleet/config_flow.py b/homeassistant/components/tesla_fleet/config_flow.py index ad6ba8817c9..c09ea78177f 100644 --- a/homeassistant/components/tesla_fleet/config_flow.py +++ b/homeassistant/components/tesla_fleet/config_flow.py @@ -8,10 +8,12 @@ from typing import Any import jwt +from homeassistant.components.application_credentials import ClientCredential from homeassistant.config_entries import ConfigEntry, ConfigFlowResult from homeassistant.helpers import config_entry_oauth2_flow -from .const import DOMAIN, LOGGER +from .application_credentials import TeslaOAuth2Implementation +from .const import CLIENT_ID, DOMAIN, LOGGER, NAME class OAuth2FlowHandler( @@ -27,6 +29,19 @@ class OAuth2FlowHandler( """Return logger.""" return LOGGER + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> ConfigFlowResult: + """Handle a flow start.""" + self.async_register_implementation( + self.hass, + TeslaOAuth2Implementation( + self.hass, DOMAIN, ClientCredential(CLIENT_ID, "", NAME) + ), + ) + + return await super().async_step_user() + async def async_oauth_create_entry( self, data: dict[str, Any], diff --git a/homeassistant/components/tesla_fleet/const.py b/homeassistant/components/tesla_fleet/const.py index ae622d2266c..9d78716a13e 100644 --- a/homeassistant/components/tesla_fleet/const.py +++ b/homeassistant/components/tesla_fleet/const.py @@ -13,6 +13,11 @@ CONF_REFRESH_TOKEN = "refresh_token" LOGGER = logging.getLogger(__package__) +NAME = "Home Assistant" +CLIENT_ID = "71b813eb-4a2e-483a-b831-4dec5cb9bf0d" +AUTHORIZE_URL = "https://auth.tesla.com/oauth2/v3/authorize" +TOKEN_URL = "https://auth.tesla.com/oauth2/v3/token" + SCOPES = [ Scope.OPENID, Scope.OFFLINE_ACCESS, diff --git a/tests/components/tesla_fleet/__init__.py b/tests/components/tesla_fleet/__init__.py index d5df0d0a2ed..78159402bff 100644 --- a/tests/components/tesla_fleet/__init__.py +++ b/tests/components/tesla_fleet/__init__.py @@ -4,9 +4,15 @@ from unittest.mock import patch from syrupy import SnapshotAssertion +from homeassistant.components.application_credentials import ( + ClientCredential, + async_import_client_credential, +) +from homeassistant.components.tesla_fleet.const import CLIENT_ID, DOMAIN from homeassistant.const import Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er +from homeassistant.setup import async_setup_component from tests.common import MockConfigEntry @@ -18,6 +24,14 @@ async def setup_platform( ) -> None: """Set up the Tesla Fleet platform.""" + assert await async_setup_component(hass, "application_credentials", {}) + await async_import_client_credential( + hass, + DOMAIN, + ClientCredential(CLIENT_ID, "", "Home Assistant"), + DOMAIN, + ) + config_entry.add_to_hass(hass) if platforms is None: diff --git a/tests/components/tesla_fleet/conftest.py b/tests/components/tesla_fleet/conftest.py index ade2f6eb0a9..7d60ae5e174 100644 --- a/tests/components/tesla_fleet/conftest.py +++ b/tests/components/tesla_fleet/conftest.py @@ -10,14 +10,7 @@ from unittest.mock import AsyncMock, patch import jwt import pytest -from homeassistant.components.application_credentials import ( - ClientCredential, - async_import_client_credential, -) -from homeassistant.components.tesla_fleet.application_credentials import CLIENT_ID from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES -from homeassistant.core import HomeAssistant -from homeassistant.setup import async_setup_component from .const import LIVE_STATUS, PRODUCTS, SITE_INFO, VEHICLE_DATA, VEHICLE_ONLINE @@ -71,18 +64,6 @@ def normal_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry: ) -@pytest.fixture(autouse=True) -async def setup_credentials(hass: HomeAssistant) -> None: - """Fixture to setup credentials.""" - assert await async_setup_component(hass, "application_credentials", {}) - await async_import_client_credential( - hass, - DOMAIN, - ClientCredential(CLIENT_ID, ""), - DOMAIN, - ) - - @pytest.fixture(autouse=True) def mock_products() -> Generator[AsyncMock]: """Mock Tesla Fleet Api products method.""" diff --git a/tests/components/tesla_fleet/test_config_flow.py b/tests/components/tesla_fleet/test_config_flow.py index 334d8902fc7..bd1c7d7c2b8 100644 --- a/tests/components/tesla_fleet/test_config_flow.py +++ b/tests/components/tesla_fleet/test_config_flow.py @@ -5,12 +5,13 @@ from urllib.parse import parse_qs, urlparse import pytest -from homeassistant.components.tesla_fleet.application_credentials import ( +from homeassistant.components.tesla_fleet.const import ( AUTHORIZE_URL, CLIENT_ID, + DOMAIN, + SCOPES, TOKEN_URL, ) -from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType