mirror of
https://github.com/home-assistant/core.git
synced 2026-09-28 02:18:10 -04:00
Use auth token in Ezviz (#54663)
* Initial commit * Revert "Initial commit" This reverts commit 452027f1a3c1be186cedd4115cea6928917c9467. * Change ezviz to token auth * Bump API version. * Add fix for token expired. Fix options update and unload. * Fix tests (PLATFORM to PLATFORM_BY_TYPE) * Uses and stores token only, added reauth step when token expires. * Add tests MFA code exceptions. * Fix tests. * Remove redundant try/except blocks. * Rebase fixes. * Fix errors in reauth config flow * Implement recommendations * Fix typing error in config_flow * Fix tests after rebase, readd camera check on init * Change to platform setup * Cleanup init. * Test for MFA required under user form * Remove useless if block. * Fix formating after rebase * Fix formating. * No longer stored in the repository --------- Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
committed by
Paulus Schoutsen
co-authored by
Paulus Schoutsen
parent
fc67a147ce
commit
e877fd6682
@@ -3,8 +3,11 @@ from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.ezviz.const import (
|
||||
ATTR_SERIAL,
|
||||
ATTR_TYPE_CAMERA,
|
||||
ATTR_TYPE_CLOUD,
|
||||
CONF_FFMPEG_ARGUMENTS,
|
||||
CONF_RFSESSION_ID,
|
||||
CONF_SESSION_ID,
|
||||
DEFAULT_FFMPEG_ARGUMENTS,
|
||||
DEFAULT_TIMEOUT,
|
||||
DOMAIN,
|
||||
@@ -22,8 +25,8 @@ from homeassistant.core import HomeAssistant
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
ENTRY_CONFIG = {
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_SESSION_ID: "test-username",
|
||||
CONF_RFSESSION_ID: "test-password",
|
||||
CONF_URL: "apiieu.ezvizlife.com",
|
||||
CONF_TYPE: ATTR_TYPE_CLOUD,
|
||||
}
|
||||
@@ -46,6 +49,18 @@ USER_INPUT = {
|
||||
CONF_TYPE: ATTR_TYPE_CLOUD,
|
||||
}
|
||||
|
||||
USER_INPUT_CAMERA_VALIDATE = {
|
||||
ATTR_SERIAL: "C666666",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_USERNAME: "test-username",
|
||||
}
|
||||
|
||||
USER_INPUT_CAMERA = {
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_TYPE: ATTR_TYPE_CAMERA,
|
||||
}
|
||||
|
||||
DISCOVERY_INFO = {
|
||||
ATTR_SERIAL: "C666666",
|
||||
CONF_USERNAME: None,
|
||||
@@ -59,6 +74,13 @@ TEST = {
|
||||
CONF_IP_ADDRESS: "127.0.0.1",
|
||||
}
|
||||
|
||||
API_LOGIN_RETURN_VALIDATE = {
|
||||
CONF_SESSION_ID: "fake_token",
|
||||
CONF_RFSESSION_ID: "fake_rf_token",
|
||||
CONF_URL: "apiieu.ezvizlife.com",
|
||||
CONF_TYPE: ATTR_TYPE_CLOUD,
|
||||
}
|
||||
|
||||
|
||||
def _patch_async_setup_entry(return_value=True):
|
||||
return patch(
|
||||
|
||||
@@ -5,6 +5,12 @@ from pyezviz import EzvizClient
|
||||
from pyezviz.test_cam_rtsp import TestRTSPAuth
|
||||
import pytest
|
||||
|
||||
ezviz_login_token_return = {
|
||||
"session_id": "fake_token",
|
||||
"rf_session_id": "fake_rf_token",
|
||||
"api_url": "apiieu.ezvizlife.com",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_ffmpeg(hass):
|
||||
@@ -42,7 +48,7 @@ def ezviz_config_flow(hass):
|
||||
"1",
|
||||
)
|
||||
|
||||
instance.login = MagicMock(return_value=True)
|
||||
instance.login = MagicMock(return_value=ezviz_login_token_return)
|
||||
instance.get_detection_sensibility = MagicMock(return_value=True)
|
||||
|
||||
yield mock_ezviz
|
||||
|
||||
@@ -3,6 +3,7 @@ from unittest.mock import patch
|
||||
|
||||
from pyezviz.exceptions import (
|
||||
AuthTestResultFailed,
|
||||
EzvizAuthVerificationCode,
|
||||
HTTPError,
|
||||
InvalidHost,
|
||||
InvalidURL,
|
||||
@@ -12,13 +13,16 @@ from pyezviz.exceptions import (
|
||||
from homeassistant.components.ezviz.const import (
|
||||
ATTR_SERIAL,
|
||||
ATTR_TYPE_CAMERA,
|
||||
ATTR_TYPE_CLOUD,
|
||||
CONF_FFMPEG_ARGUMENTS,
|
||||
DEFAULT_FFMPEG_ARGUMENTS,
|
||||
DEFAULT_TIMEOUT,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_INTEGRATION_DISCOVERY, SOURCE_USER
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_INTEGRATION_DISCOVERY,
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_USER,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_CUSTOMIZE,
|
||||
CONF_IP_ADDRESS,
|
||||
@@ -32,8 +36,8 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from . import (
|
||||
API_LOGIN_RETURN_VALIDATE,
|
||||
DISCOVERY_INFO,
|
||||
USER_INPUT,
|
||||
USER_INPUT_VALIDATE,
|
||||
_patch_async_setup_entry,
|
||||
init_integration,
|
||||
@@ -59,7 +63,7 @@ async def test_user_form(hass: HomeAssistant, ezviz_config_flow) -> None:
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test-username"
|
||||
assert result["data"] == {**USER_INPUT}
|
||||
assert result["data"] == {**API_LOGIN_RETURN_VALIDATE}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@@ -78,7 +82,11 @@ async def test_user_custom_url(hass: HomeAssistant, ezviz_config_flow) -> None:
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: "test-user", CONF_PASSWORD: "test-pass", CONF_URL: "customize"},
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
CONF_URL: CONF_CUSTOMIZE,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
@@ -90,21 +98,58 @@ async def test_user_custom_url(hass: HomeAssistant, ezviz_config_flow) -> None:
|
||||
result["flow_id"],
|
||||
{CONF_URL: "test-user"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == {
|
||||
CONF_PASSWORD: "test-pass",
|
||||
CONF_TYPE: ATTR_TYPE_CLOUD,
|
||||
CONF_URL: "test-user",
|
||||
CONF_USERNAME: "test-user",
|
||||
}
|
||||
assert result["data"] == API_LOGIN_RETURN_VALIDATE
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_step_discovery_abort_if_cloud_account_missing(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
async def test_async_step_reauth(hass, ezviz_config_flow):
|
||||
"""Test the reauth step."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with _patch_async_setup_entry() as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT_VALIDATE,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test-username"
|
||||
assert result["data"] == {**API_LOGIN_RETURN_VALIDATE}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_REAUTH}, data=USER_INPUT_VALIDATE
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "reauth_successful"
|
||||
|
||||
|
||||
async def test_step_discovery_abort_if_cloud_account_missing(hass):
|
||||
"""Test discovery and confirm step, abort if cloud account was removed."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -127,11 +172,21 @@ async def test_step_discovery_abort_if_cloud_account_missing(
|
||||
assert result["reason"] == "ezviz_cloud_account_missing"
|
||||
|
||||
|
||||
async def test_step_reauth_abort_if_cloud_account_missing(hass):
|
||||
"""Test reauth and confirm step, abort if cloud account was removed."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_REAUTH}, data=USER_INPUT_VALIDATE
|
||||
)
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "ezviz_cloud_account_missing"
|
||||
|
||||
|
||||
async def test_async_step_integration_discovery(
|
||||
hass: HomeAssistant, ezviz_config_flow, ezviz_test_rtsp_config_flow
|
||||
) -> None:
|
||||
hass, ezviz_config_flow, ezviz_test_rtsp_config_flow
|
||||
):
|
||||
"""Test discovery and confirm step."""
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS", []):
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS_BY_TYPE", []):
|
||||
await init_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -189,11 +244,14 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
|
||||
async def test_user_form_exception(hass: HomeAssistant, ezviz_config_flow) -> None:
|
||||
"""Test we handle exception on user form."""
|
||||
ezviz_config_flow.side_effect = PyEzvizError
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
ezviz_config_flow.side_effect = PyEzvizError
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
@@ -215,6 +273,17 @@ async def test_user_form_exception(hass: HomeAssistant, ezviz_config_flow) -> No
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "invalid_host"}
|
||||
|
||||
ezviz_config_flow.side_effect = EzvizAuthVerificationCode
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT_VALIDATE,
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "mfa_required"}
|
||||
|
||||
ezviz_config_flow.side_effect = HTTPError
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
@@ -224,7 +293,7 @@ async def test_user_form_exception(hass: HomeAssistant, ezviz_config_flow) -> No
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
ezviz_config_flow.side_effect = Exception
|
||||
|
||||
@@ -242,7 +311,7 @@ async def test_discover_exception_step1(
|
||||
ezviz_config_flow,
|
||||
) -> None:
|
||||
"""Test we handle unexpected exception on discovery."""
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS", []):
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS_BY_TYPE", []):
|
||||
await init_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -295,7 +364,21 @@ async def test_discover_exception_step1(
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
assert result["errors"] == {"base": "invalid_host"}
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
ezviz_config_flow.side_effect = EzvizAuthVerificationCode
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-user",
|
||||
CONF_PASSWORD: "test-pass",
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "confirm"
|
||||
assert result["errors"] == {"base": "mfa_required"}
|
||||
|
||||
ezviz_config_flow.side_effect = Exception
|
||||
|
||||
@@ -317,7 +400,7 @@ async def test_discover_exception_step3(
|
||||
ezviz_test_rtsp_config_flow,
|
||||
) -> None:
|
||||
"""Test we handle unexpected exception on discovery."""
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS", []):
|
||||
with patch("homeassistant.components.ezviz.PLATFORMS_BY_TYPE", []):
|
||||
await init_integration(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
@@ -423,7 +506,18 @@ async def test_user_custom_url_exception(
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user_custom_url"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
ezviz_config_flow.side_effect = EzvizAuthVerificationCode
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_URL: "test-user"},
|
||||
)
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user_custom_url"
|
||||
assert result["errors"] == {"base": "mfa_required"}
|
||||
|
||||
ezviz_config_flow.side_effect = Exception
|
||||
|
||||
@@ -434,3 +528,103 @@ async def test_user_custom_url_exception(
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "unknown"
|
||||
|
||||
|
||||
async def test_async_step_reauth_exception(hass, ezviz_config_flow):
|
||||
"""Test the reauth step exceptions."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "user"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with _patch_async_setup_entry() as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
USER_INPUT_VALIDATE,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test-username"
|
||||
assert result["data"] == {**API_LOGIN_RETURN_VALIDATE}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_REAUTH}, data=USER_INPUT_VALIDATE
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {}
|
||||
|
||||
ezviz_config_flow.side_effect = InvalidURL()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "invalid_host"}
|
||||
|
||||
ezviz_config_flow.side_effect = InvalidHost()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "invalid_host"}
|
||||
|
||||
ezviz_config_flow.side_effect = EzvizAuthVerificationCode()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "mfa_required"}
|
||||
|
||||
ezviz_config_flow.side_effect = PyEzvizError()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["step_id"] == "reauth_confirm"
|
||||
assert result["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
ezviz_config_flow.side_effect = Exception()
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_USERNAME: "test-username",
|
||||
CONF_PASSWORD: "test-password",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "unknown"
|
||||
|
||||
Reference in New Issue
Block a user