Fix Google Mail expired authorization (#102735)
* Fix Google Mail expired authorization * add test * raise HomeAssistantError * handle in api module * uno mas
This commit is contained in:
parent
ba7dbc5927
commit
7dbe0c3a48
4 changed files with 49 additions and 22 deletions
|
@ -1,12 +1,9 @@
|
|||
"""Support for Google Mail."""
|
||||
from __future__ import annotations
|
||||
|
||||
from aiohttp.client_exceptions import ClientError, ClientResponseError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||
from homeassistant.const import CONF_NAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv, discovery
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
OAuth2Session,
|
||||
|
@ -35,16 +32,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
implementation = await async_get_config_entry_implementation(hass, entry)
|
||||
session = OAuth2Session(hass, entry, implementation)
|
||||
auth = AsyncConfigEntryAuth(session)
|
||||
try:
|
||||
await auth.check_and_refresh_token()
|
||||
except ClientResponseError as err:
|
||||
if 400 <= err.status < 500:
|
||||
raise ConfigEntryAuthFailed(
|
||||
"OAuth session is not valid, reauth required"
|
||||
) from err
|
||||
raise ConfigEntryNotReady from err
|
||||
except ClientError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
await auth.check_and_refresh_token()
|
||||
hass.data[DOMAIN][entry.entry_id] = auth
|
||||
|
||||
hass.async_create_task(
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
"""API for Google Mail bound to Home Assistant OAuth."""
|
||||
from aiohttp.client_exceptions import ClientError, ClientResponseError
|
||||
from google.auth.exceptions import RefreshError
|
||||
from google.oauth2.credentials import Credentials
|
||||
from googleapiclient.discovery import Resource, build
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_ACCESS_TOKEN
|
||||
from homeassistant.exceptions import (
|
||||
ConfigEntryAuthFailed,
|
||||
ConfigEntryNotReady,
|
||||
HomeAssistantError,
|
||||
)
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
|
||||
|
||||
|
@ -24,14 +31,30 @@ class AsyncConfigEntryAuth:
|
|||
|
||||
async def check_and_refresh_token(self) -> str:
|
||||
"""Check the token."""
|
||||
await self.oauth_session.async_ensure_token_valid()
|
||||
try:
|
||||
await self.oauth_session.async_ensure_token_valid()
|
||||
except (RefreshError, ClientResponseError, ClientError) as ex:
|
||||
if (
|
||||
self.oauth_session.config_entry.state
|
||||
is ConfigEntryState.SETUP_IN_PROGRESS
|
||||
):
|
||||
if isinstance(ex, ClientResponseError) and 400 <= ex.status < 500:
|
||||
raise ConfigEntryAuthFailed(
|
||||
"OAuth session is not valid, reauth required"
|
||||
) from ex
|
||||
raise ConfigEntryNotReady from ex
|
||||
if (
|
||||
isinstance(ex, RefreshError)
|
||||
or hasattr(ex, "status")
|
||||
and ex.status == 400
|
||||
):
|
||||
self.oauth_session.config_entry.async_start_reauth(
|
||||
self.oauth_session.hass
|
||||
)
|
||||
raise HomeAssistantError(ex) from ex
|
||||
return self.access_token
|
||||
|
||||
async def get_resource(self) -> Resource:
|
||||
"""Get current resource."""
|
||||
try:
|
||||
credentials = Credentials(await self.check_and_refresh_token())
|
||||
except RefreshError as ex:
|
||||
self.oauth_session.config_entry.async_start_reauth(self.oauth_session.hass)
|
||||
raise ex
|
||||
credentials = Credentials(await self.check_and_refresh_token())
|
||||
return build("gmail", "v1", credentials=credentials)
|
||||
|
|
|
@ -73,8 +73,13 @@ async def test_expired_token_refresh_success(
|
|||
http.HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
ConfigEntryState.SETUP_RETRY,
|
||||
),
|
||||
(
|
||||
time.time() - 3600,
|
||||
http.HTTPStatus.BAD_REQUEST,
|
||||
ConfigEntryState.SETUP_ERROR,
|
||||
),
|
||||
],
|
||||
ids=["failure_requires_reauth", "transient_failure"],
|
||||
ids=["failure_requires_reauth", "transient_failure", "revoked_auth"],
|
||||
)
|
||||
async def test_expired_token_refresh_failure(
|
||||
hass: HomeAssistant,
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
"""Services tests for the Google Mail integration."""
|
||||
from unittest.mock import patch
|
||||
|
||||
from aiohttp.client_exceptions import ClientResponseError
|
||||
from google.auth.exceptions import RefreshError
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.google_mail import DOMAIN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .conftest import BUILD, SENSOR, TOKEN, ComponentSetup
|
||||
|
||||
|
@ -57,13 +59,22 @@ async def test_set_vacation(
|
|||
assert len(mock_client.mock_calls) == 5
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("side_effect"),
|
||||
(
|
||||
(RefreshError,),
|
||||
(ClientResponseError("", (), status=400),),
|
||||
),
|
||||
)
|
||||
async def test_reauth_trigger(
|
||||
hass: HomeAssistant, setup_integration: ComponentSetup
|
||||
hass: HomeAssistant,
|
||||
setup_integration: ComponentSetup,
|
||||
side_effect,
|
||||
) -> None:
|
||||
"""Test reauth is triggered after a refresh error during service call."""
|
||||
await setup_integration()
|
||||
|
||||
with patch(TOKEN, side_effect=RefreshError), pytest.raises(RefreshError):
|
||||
with patch(TOKEN, side_effect=side_effect), pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"set_vacation",
|
||||
|
|
Loading…
Add table
Reference in a new issue