From dcd2d428940ef1f46cac738ae2b085dff9c64486 Mon Sep 17 00:00:00 2001
From: Joost Lekkerkerker <joostlek@outlook.com>
Date: Mon, 3 Mar 2025 20:07:07 +0100
Subject: [PATCH] Abort SmartThings flow if default_config is not enabled
 (#139700)

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled

* Abort SmartThings flow if default_config is not enabled
---
 .../components/smartthings/config_flow.py     | 11 +++
 .../components/smartthings/strings.json       |  3 +-
 .../smartthings/test_config_flow.py           | 82 +++++++++++++++++--
 3 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/homeassistant/components/smartthings/config_flow.py b/homeassistant/components/smartthings/config_flow.py
index 02b11b190c9..d2654348527 100644
--- a/homeassistant/components/smartthings/config_flow.py
+++ b/homeassistant/components/smartthings/config_flow.py
@@ -32,6 +32,17 @@ class SmartThingsConfigFlow(AbstractOAuth2FlowHandler, domain=DOMAIN):
         """Extra data that needs to be appended to the authorize url."""
         return {"scope": " ".join(REQUESTED_SCOPES)}
 
+    async def async_step_user(
+        self, user_input: dict[str, Any] | None = None
+    ) -> ConfigFlowResult:
+        """Check we have the cloud integration set up."""
+        if "cloud" not in self.hass.config.components:
+            return self.async_abort(
+                reason="cloud_not_enabled",
+                description_placeholders={"default_config": "default_config"},
+            )
+        return await super().async_step_user(user_input)
+
     async def async_oauth_create_entry(self, data: dict[str, Any]) -> ConfigFlowResult:
         """Create an entry for SmartThings."""
         if not set(data[CONF_TOKEN]["scope"].split()) >= set(SCOPES):
diff --git a/homeassistant/components/smartthings/strings.json b/homeassistant/components/smartthings/strings.json
index 9fd417284af..844ebd12004 100644
--- a/homeassistant/components/smartthings/strings.json
+++ b/homeassistant/components/smartthings/strings.json
@@ -24,7 +24,8 @@
       "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
       "reauth_account_mismatch": "Authenticated account does not match the account to be reauthenticated. Please log in with the correct account and pick the right location.",
       "reauth_location_mismatch": "Authenticated location does not match the location to be reauthenticated. Please log in with the correct account and pick the right location.",
-      "missing_scopes": "Authentication failed. Please make sure you have granted all required permissions."
+      "missing_scopes": "Authentication failed. Please make sure you have granted all required permissions.",
+      "cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml."
     }
   },
   "entity": {
diff --git a/tests/components/smartthings/test_config_flow.py b/tests/components/smartthings/test_config_flow.py
index a16747c1190..7472d7d6b71 100644
--- a/tests/components/smartthings/test_config_flow.py
+++ b/tests/components/smartthings/test_config_flow.py
@@ -28,7 +28,13 @@ from tests.test_util.aiohttp import AiohttpClientMocker
 from tests.typing import ClientSessionGenerator
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.fixture
+def use_cloud(hass: HomeAssistant) -> None:
+    """Set up the cloud component."""
+    hass.config.components.add("cloud")
+
+
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_full_flow(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -100,7 +106,7 @@ async def test_full_flow(
     assert result["result"].unique_id == "397678e5-9995-4a39-9d9f-ae6ba310236c"
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_not_enough_scopes(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -161,7 +167,7 @@ async def test_not_enough_scopes(
     assert result["reason"] == "missing_scopes"
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_duplicate_entry(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -224,6 +230,23 @@ async def test_duplicate_entry(
 
 
 @pytest.mark.usefixtures("current_request_with_host")
+async def test_no_cloud(
+    hass: HomeAssistant,
+    hass_client_no_auth: ClientSessionGenerator,
+    aioclient_mock: AiohttpClientMocker,
+    mock_smartthings: AsyncMock,
+    mock_setup_entry: AsyncMock,
+) -> None:
+    """Check we abort when cloud is not enabled."""
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": SOURCE_USER}
+    )
+
+    assert result["type"] is FlowResultType.ABORT
+    assert result["reason"] == "cloud_not_enabled"
+
+
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_reauthentication(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -285,7 +308,7 @@ async def test_reauthentication(
     }
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_reauthentication_wrong_scopes(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -336,7 +359,7 @@ async def test_reauthentication_wrong_scopes(
     assert result["reason"] == "missing_scopes"
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_reauth_account_mismatch(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -388,6 +411,29 @@ async def test_reauth_account_mismatch(
 
 
 @pytest.mark.usefixtures("current_request_with_host")
+async def test_reauthentication_no_cloud(
+    hass: HomeAssistant,
+    hass_client_no_auth: ClientSessionGenerator,
+    aioclient_mock: AiohttpClientMocker,
+    mock_smartthings: AsyncMock,
+    mock_setup_entry: AsyncMock,
+    mock_config_entry: MockConfigEntry,
+) -> None:
+    """Test SmartThings reauthentication without cloud."""
+    mock_config_entry.add_to_hass(hass)
+
+    result = await mock_config_entry.start_reauth_flow(hass)
+
+    assert result["type"] is FlowResultType.FORM
+    assert result["step_id"] == "reauth_confirm"
+
+    result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
+
+    assert result["type"] is FlowResultType.ABORT
+    assert result["reason"] == "cloud_not_enabled"
+
+
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_migration(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -468,7 +514,7 @@ async def test_migration(
     assert mock_old_config_entry.minor_version == 1
 
 
-@pytest.mark.usefixtures("current_request_with_host")
+@pytest.mark.usefixtures("current_request_with_host", "use_cloud")
 async def test_migration_wrong_location(
     hass: HomeAssistant,
     hass_client_no_auth: ClientSessionGenerator,
@@ -539,3 +585,27 @@ async def test_migration_wrong_location(
     )
     assert mock_old_config_entry.version == 3
     assert mock_old_config_entry.minor_version == 1
+
+
+@pytest.mark.usefixtures("current_request_with_host")
+async def test_migration_no_cloud(
+    hass: HomeAssistant,
+    hass_client_no_auth: ClientSessionGenerator,
+    aioclient_mock: AiohttpClientMocker,
+    mock_smartthings: AsyncMock,
+    mock_old_config_entry: MockConfigEntry,
+) -> None:
+    """Test SmartThings reauthentication with different account."""
+    mock_old_config_entry.add_to_hass(hass)
+
+    await hass.config_entries.async_setup(mock_old_config_entry.entry_id)
+    await hass.async_block_till_done()
+
+    assert mock_old_config_entry.state is ConfigEntryState.SETUP_ERROR
+
+    result = hass.config_entries.flow.async_progress()[0]
+
+    result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
+
+    assert result["type"] is FlowResultType.ABORT
+    assert result["reason"] == "cloud_not_enabled"
-- 
GitLab