Fix onvif reauth when device returns a http 401/403 error (#92690)

This commit is contained in:
J. Nick Koston 2023-05-06 17:12:24 -05:00 committed by GitHub
parent d66305ddd3
commit d05724a42a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 3 deletions

View File

@ -1,5 +1,6 @@
"""The ONVIF integration.""" """The ONVIF integration."""
import asyncio import asyncio
from http import HTTPStatus
import logging import logging
from httpx import RequestError from httpx import RequestError
@ -56,7 +57,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except ONVIFError as err: except ONVIFError as err:
await device.device.close() await device.device.close()
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
f"Could not setup camera {device.device.host}:{device.device.port}: {err}" f"Could not setup camera {device.device.host}:{device.device.port}: {stringify_onvif_error(err)}"
) from err
except TransportError as err:
await device.device.close()
stringified_onvif_error = stringify_onvif_error(err)
if err.status_code in (
HTTPStatus.UNAUTHORIZED.value,
HTTPStatus.FORBIDDEN.value,
):
raise ConfigEntryAuthFailed(
f"Auth Failed: {stringified_onvif_error}"
) from err
raise ConfigEntryNotReady(
f"Could not setup camera {device.device.host}:{device.device.port}: {stringified_onvif_error}"
) from err ) from err
except asyncio.CancelledError as err: except asyncio.CancelledError as err:
# After https://github.com/agronholm/anyio/issues/374 is resolved # After https://github.com/agronholm/anyio/issues/374 is resolved

View File

@ -142,10 +142,14 @@ class OnvifFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
hass.async_create_task(hass.config_entries.async_reload(entry_id)) hass.async_create_task(hass.config_entries.async_reload(entry_id))
return self.async_abort(reason="reauth_successful") return self.async_abort(reason="reauth_successful")
username = (user_input or {}).get(CONF_USERNAME) or entry.data[CONF_USERNAME]
return self.async_show_form( return self.async_show_form(
step_id="reauth_confirm", step_id="reauth_confirm",
data_schema=vol.Schema( data_schema=vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str} {
vol.Required(CONF_USERNAME, default=username): str,
vol.Required(CONF_PASSWORD): str,
}
), ),
errors=errors, errors=errors,
description_placeholders=description_placeholders, description_placeholders=description_placeholders,

View File

@ -47,6 +47,7 @@
}, },
"reauth_confirm": { "reauth_confirm": {
"title": "Reauthenticate the ONVIF device", "title": "Reauthenticate the ONVIF device",
"description": "Some devices will reject authentication if the time is out of sync by more than 5 seconds. If authentication is unsuccessful, verify the time on the device is correct and try again.",
"data": { "data": {
"username": "[%key:common::config_flow::data::username%]", "username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]" "password": "[%key:common::config_flow::data::password%]"

View File

@ -5,7 +5,7 @@ from homeassistant import config_entries, data_entry_flow
from homeassistant.components import dhcp from homeassistant.components import dhcp
from homeassistant.components.onvif import DOMAIN, config_flow from homeassistant.components.onvif import DOMAIN, config_flow
from homeassistant.config_entries import SOURCE_DHCP from homeassistant.config_entries import SOURCE_DHCP
from homeassistant.const import CONF_HOST from homeassistant.const import CONF_HOST, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
@ -710,6 +710,14 @@ async def test_discovered_by_dhcp_does_not_update_if_no_matching_entry(
assert result["reason"] == "no_devices_found" assert result["reason"] == "no_devices_found"
def _get_schema_default(schema, key_name):
"""Iterate schema to find a key."""
for schema_key in schema:
if schema_key == key_name:
return schema_key.default()
raise KeyError(f"{key_name} not found in schema")
async def test_form_reauth(hass: HomeAssistant) -> None: async def test_form_reauth(hass: HomeAssistant) -> None:
"""Test reauthenticate.""" """Test reauthenticate."""
entry, _, _ = await setup_onvif_integration(hass) entry, _, _ = await setup_onvif_integration(hass)
@ -721,6 +729,10 @@ async def test_form_reauth(hass: HomeAssistant) -> None:
) )
assert result["type"] == FlowResultType.FORM assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "reauth_confirm" assert result["step_id"] == "reauth_confirm"
assert (
_get_schema_default(result["data_schema"].schema, CONF_USERNAME)
== entry.data[CONF_USERNAME]
)
with patch( with patch(
"homeassistant.components.onvif.config_flow.get_device" "homeassistant.components.onvif.config_flow.get_device"