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:
Robert Hillis 2023-10-30 07:36:34 -04:00 committed by GitHub
parent ba7dbc5927
commit 7dbe0c3a48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 22 deletions

View file

@ -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(

View file

@ -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)

View file

@ -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,

View file

@ -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",