From a2048b4c7a8ce51c4eff2800d0524d8aa981da13 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Sat, 2 May 2020 23:16:45 +0200 Subject: [PATCH] UniFi - Catch controllers running on UniFi OS that don't have a local user configured (#35060) --- homeassistant/components/unifi/config_flow.py | 12 ++++- homeassistant/components/unifi/errors.py | 4 ++ homeassistant/components/unifi/strings.json | 1 + .../components/unifi/translations/en.json | 1 + tests/components/unifi/test_config_flow.py | 47 +++++++++++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/unifi/config_flow.py b/homeassistant/components/unifi/config_flow.py index ad4f3ebf1a1..8c836f77131 100644 --- a/homeassistant/components/unifi/config_flow.py +++ b/homeassistant/components/unifi/config_flow.py @@ -32,7 +32,12 @@ from .const import ( LOGGER, ) from .controller import get_controller -from .errors import AlreadyConfigured, AuthenticationRequired, CannotConnect +from .errors import ( + AlreadyConfigured, + AuthenticationRequired, + CannotConnect, + NoLocalUser, +) DEFAULT_PORT = 8443 DEFAULT_SITE_ID = "default" @@ -129,6 +134,8 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): for site in self.sites.values(): if desc == site["desc"]: + if "role" not in site: + raise NoLocalUser self.config[CONF_SITE_ID] = site["name"] break @@ -147,6 +154,9 @@ class UnifiFlowHandler(config_entries.ConfigFlow, domain=UNIFI_DOMAIN): except AlreadyConfigured: return self.async_abort(reason="already_configured") + except NoLocalUser: + return self.async_abort(reason="no_local_user") + if len(self.sites) == 1: self.desc = next(iter(self.sites.values()))["desc"] return await self.async_step_site(user_input={}) diff --git a/homeassistant/components/unifi/errors.py b/homeassistant/components/unifi/errors.py index c90c4956312..e0da64f245c 100644 --- a/homeassistant/components/unifi/errors.py +++ b/homeassistant/components/unifi/errors.py @@ -22,5 +22,9 @@ class LoginRequired(UnifiException): """Component got logged out.""" +class NoLocalUser(UnifiException): + """No local user.""" + + class UserLevel(UnifiException): """User level too low.""" diff --git a/homeassistant/components/unifi/strings.json b/homeassistant/components/unifi/strings.json index da1d6200ed5..2650066da5f 100644 --- a/homeassistant/components/unifi/strings.json +++ b/homeassistant/components/unifi/strings.json @@ -20,6 +20,7 @@ }, "abort": { "already_configured": "Controller site is already configured", + "no_local_user": "No local user found, configure a local account on controller and try again", "user_privilege": "User needs to be administrator" } }, diff --git a/homeassistant/components/unifi/translations/en.json b/homeassistant/components/unifi/translations/en.json index e49bbbcc50e..ad14f09b300 100644 --- a/homeassistant/components/unifi/translations/en.json +++ b/homeassistant/components/unifi/translations/en.json @@ -2,6 +2,7 @@ "config": { "abort": { "already_configured": "Controller site is already configured", + "no_local_user": "No local user found, configure a local account on controller and try again", "user_privilege": "User needs to be administrator" }, "error": { diff --git a/tests/components/unifi/test_config_flow.py b/tests/components/unifi/test_config_flow.py index 1ca82fb12f1..16777e5d9a9 100644 --- a/tests/components/unifi/test_config_flow.py +++ b/tests/components/unifi/test_config_flow.py @@ -185,6 +185,53 @@ async def test_flow_fails_site_already_configured(hass, aioclient_mock): ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "already_configured" + + +async def test_flow_fails_site_has_no_local_user(hass, aioclient_mock): + """Test config flow.""" + entry = MockConfigEntry( + domain=UNIFI_DOMAIN, data={"controller": {"host": "1.2.3.4", "site": "site_id"}} + ) + entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + UNIFI_DOMAIN, context={"source": "user"} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "user" + + aioclient_mock.get("https://1.2.3.4:1234", status=302) + + aioclient_mock.post( + "https://1.2.3.4:1234/api/login", + json={"data": "login successful", "meta": {"rc": "ok"}}, + headers={"content-type": "application/json"}, + ) + + aioclient_mock.get( + "https://1.2.3.4:1234/api/self/sites", + json={ + "data": [{"desc": "Site name", "name": "site_id"}], + "meta": {"rc": "ok"}, + }, + headers={"content-type": "application/json"}, + ) + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_HOST: "1.2.3.4", + CONF_USERNAME: "username", + CONF_PASSWORD: "password", + CONF_PORT: 1234, + CONF_VERIFY_SSL: True, + }, + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT + assert result["reason"] == "no_local_user" async def test_flow_fails_user_credentials_faulty(hass, aioclient_mock):