From 794071449abe949ae99221d90c8003c5f06ca098 Mon Sep 17 00:00:00 2001 From: tronikos Date: Wed, 30 Aug 2023 21:36:07 -0700 Subject: [PATCH] Opower MFA fixes (#99317) opower mfa fixes --- .../components/opower/config_flow.py | 24 +++++++++---------- .../components/opower/coordinator.py | 2 +- homeassistant/components/opower/manifest.json | 2 +- homeassistant/components/opower/strings.json | 11 ++++++--- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/opower/test_config_flow.py | 4 ++-- 7 files changed, 25 insertions(+), 22 deletions(-) diff --git a/homeassistant/components/opower/config_flow.py b/homeassistant/components/opower/config_flow.py index 9f2ec56423d..d456fc536e5 100644 --- a/homeassistant/components/opower/config_flow.py +++ b/homeassistant/components/opower/config_flow.py @@ -5,7 +5,13 @@ from collections.abc import Mapping import logging from typing import Any -from opower import CannotConnect, InvalidAuth, Opower, get_supported_utility_names +from opower import ( + CannotConnect, + InvalidAuth, + Opower, + get_supported_utility_names, + select_utility, +) import voluptuous as vol from homeassistant import config_entries @@ -20,9 +26,7 @@ _LOGGER = logging.getLogger(__name__) STEP_USER_DATA_SCHEMA = vol.Schema( { - vol.Required(CONF_UTILITY): vol.In( - get_supported_utility_names(supports_mfa=True) - ), + vol.Required(CONF_UTILITY): vol.In(get_supported_utility_names()), vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str, } @@ -38,7 +42,7 @@ async def _validate_login( login_data[CONF_UTILITY], login_data[CONF_USERNAME], login_data[CONF_PASSWORD], - login_data.get(CONF_TOTP_SECRET, None), + login_data.get(CONF_TOTP_SECRET), ) errors: dict[str, str] = {} try: @@ -50,12 +54,6 @@ async def _validate_login( return errors -@callback -def _supports_mfa(utility: str) -> bool: - """Return whether the utility supports MFA.""" - return utility not in get_supported_utility_names(supports_mfa=False) - - class OpowerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Opower.""" @@ -78,7 +76,7 @@ class OpowerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): CONF_USERNAME: user_input[CONF_USERNAME], } ) - if _supports_mfa(user_input[CONF_UTILITY]): + if select_utility(user_input[CONF_UTILITY]).accepts_mfa(): self.utility_info = user_input return await self.async_step_mfa() @@ -154,7 +152,7 @@ class OpowerConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): vol.Required(CONF_USERNAME): self.reauth_entry.data[CONF_USERNAME], vol.Required(CONF_PASSWORD): str, } - if _supports_mfa(self.reauth_entry.data[CONF_UTILITY]): + if select_utility(self.reauth_entry.data[CONF_UTILITY]).accepts_mfa(): schema[vol.Optional(CONF_TOTP_SECRET)] = str return self.async_show_form( step_id="reauth_confirm", diff --git a/homeassistant/components/opower/coordinator.py b/homeassistant/components/opower/coordinator.py index 1410b62b7b6..5ce35e949af 100644 --- a/homeassistant/components/opower/coordinator.py +++ b/homeassistant/components/opower/coordinator.py @@ -55,7 +55,7 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]): entry_data[CONF_UTILITY], entry_data[CONF_USERNAME], entry_data[CONF_PASSWORD], - entry_data.get(CONF_TOTP_SECRET, None), + entry_data.get(CONF_TOTP_SECRET), ) async def _async_update_data( diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index fb4ff5153ec..05e89ea96d4 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -7,5 +7,5 @@ "documentation": "https://www.home-assistant.io/integrations/opower", "iot_class": "cloud_polling", "loggers": ["opower"], - "requirements": ["opower==0.0.32"] + "requirements": ["opower==0.0.33"] } diff --git a/homeassistant/components/opower/strings.json b/homeassistant/components/opower/strings.json index ac931bf9308..362e6cd7596 100644 --- a/homeassistant/components/opower/strings.json +++ b/homeassistant/components/opower/strings.json @@ -5,8 +5,13 @@ "data": { "utility": "Utility name", "username": "[%key:common::config_flow::data::username%]", - "password": "[%key:common::config_flow::data::password%]", - "totp_secret": "TOTP Secret (only for some utilities, see documentation)" + "password": "[%key:common::config_flow::data::password%]" + } + }, + "mfa": { + "description": "The TOTP secret below is not one of the 6 digit time-based numeric codes. It is a string of around 16 characters containing the shared secret that enables your authenticator app to generate the correct time-based code at the appropriate time. See the documentation.", + "data": { + "totp_secret": "TOTP Secret" } }, "reauth_confirm": { @@ -14,7 +19,7 @@ "data": { "username": "[%key:common::config_flow::data::username%]", "password": "[%key:common::config_flow::data::password%]", - "totp_secret": "TOTP Secret (only for some utilities, see documentation)" + "totp_secret": "TOTP Secret" } } }, diff --git a/requirements_all.txt b/requirements_all.txt index 35394fafa95..56f75109423 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1374,7 +1374,7 @@ openwrt-luci-rpc==1.1.16 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.0.32 +opower==0.0.33 # homeassistant.components.oralb oralb-ble==0.17.6 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 40b6c4ace56..69e6a9043a1 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1040,7 +1040,7 @@ openerz-api==0.2.0 openhomedevice==2.2.0 # homeassistant.components.opower -opower==0.0.32 +opower==0.0.33 # homeassistant.components.oralb oralb-ble==0.17.6 diff --git a/tests/components/opower/test_config_flow.py b/tests/components/opower/test_config_flow.py index 0391e42ca16..f9ae457a80e 100644 --- a/tests/components/opower/test_config_flow.py +++ b/tests/components/opower/test_config_flow.py @@ -300,7 +300,7 @@ async def test_form_valid_reauth( assert result["reason"] == "reauth_successful" await hass.async_block_till_done() - assert hass.config_entries.async_entries(DOMAIN)[0].data == { + assert mock_config_entry.data == { "utility": "Pacific Gas and Electric Company (PG&E)", "username": "test-username", "password": "test-password2", @@ -350,7 +350,7 @@ async def test_form_valid_reauth_with_mfa( assert result["reason"] == "reauth_successful" await hass.async_block_till_done() - assert hass.config_entries.async_entries(DOMAIN)[0].data == { + assert mock_config_entry.data == { "utility": "Consolidated Edison (ConEd)", "username": "test-username", "password": "test-password2",