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:
RenierM26
2023-03-30 10:39:58 -04:00
committed by Paulus Schoutsen
co-authored by Paulus Schoutsen
parent fc67a147ce
commit e877fd6682
9 changed files with 535 additions and 217 deletions
+24 -2
View File
@@ -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(
+7 -1
View File
@@ -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
+218 -24
View File
@@ -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"