Fix google calendar bug where expired tokens are not refreshed (#72994)

This commit is contained in:
Allen Porter 2022-06-03 16:33:12 -07:00 committed by GitHub
parent 04b2223f06
commit bdc41bf22a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 17 additions and 10 deletions

View File

@ -5,7 +5,6 @@ from __future__ import annotations
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
import datetime import datetime
import logging import logging
import time
from typing import Any, cast from typing import Any, cast
import aiohttp import aiohttp
@ -50,12 +49,16 @@ class DeviceAuth(AuthImplementation):
async def async_resolve_external_data(self, external_data: Any) -> dict: async def async_resolve_external_data(self, external_data: Any) -> dict:
"""Resolve a Google API Credentials object to Home Assistant token.""" """Resolve a Google API Credentials object to Home Assistant token."""
creds: Credentials = external_data[DEVICE_AUTH_CREDS] creds: Credentials = external_data[DEVICE_AUTH_CREDS]
delta = creds.token_expiry.replace(tzinfo=datetime.timezone.utc) - dt.utcnow()
_LOGGER.debug(
"Token expires at %s (in %s)", creds.token_expiry, delta.total_seconds()
)
return { return {
"access_token": creds.access_token, "access_token": creds.access_token,
"refresh_token": creds.refresh_token, "refresh_token": creds.refresh_token,
"scope": " ".join(creds.scopes), "scope": " ".join(creds.scopes),
"token_type": "Bearer", "token_type": "Bearer",
"expires_in": creds.token_expiry.timestamp() - time.time(), "expires_in": delta.total_seconds(),
} }

View File

@ -16,7 +16,6 @@ from homeassistant.components.google import CONF_TRACK_NEW, DOMAIN
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
from tests.test_util.aiohttp import AiohttpClientMocker from tests.test_util.aiohttp import AiohttpClientMocker
@ -136,7 +135,10 @@ def token_scopes() -> list[str]:
@pytest.fixture @pytest.fixture
def token_expiry() -> datetime.datetime: def token_expiry() -> datetime.datetime:
"""Expiration time for credentials used in the test.""" """Expiration time for credentials used in the test."""
return utcnow() + datetime.timedelta(days=7) # OAuth library returns an offset-naive timestamp
return datetime.datetime.fromtimestamp(
datetime.datetime.utcnow().timestamp()
) + datetime.timedelta(hours=1)
@pytest.fixture @pytest.fixture

View File

@ -8,6 +8,7 @@ from typing import Any
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from aiohttp.client_exceptions import ClientError from aiohttp.client_exceptions import ClientError
from freezegun.api import FrozenDateTimeFactory
from oauth2client.client import ( from oauth2client.client import (
FlowExchangeError, FlowExchangeError,
OAuth2Credentials, OAuth2Credentials,
@ -94,11 +95,13 @@ async def fire_alarm(hass, point_in_time):
await hass.async_block_till_done() await hass.async_block_till_done()
@pytest.mark.freeze_time("2022-06-03 15:19:59-00:00")
async def test_full_flow_yaml_creds( async def test_full_flow_yaml_creds(
hass: HomeAssistant, hass: HomeAssistant,
mock_code_flow: Mock, mock_code_flow: Mock,
mock_exchange: Mock, mock_exchange: Mock,
component_setup: ComponentSetup, component_setup: ComponentSetup,
freezer: FrozenDateTimeFactory,
) -> None: ) -> None:
"""Test successful creds setup.""" """Test successful creds setup."""
assert await component_setup() assert await component_setup()
@ -115,8 +118,8 @@ async def test_full_flow_yaml_creds(
"homeassistant.components.google.async_setup_entry", return_value=True "homeassistant.components.google.async_setup_entry", return_value=True
) as mock_setup: ) as mock_setup:
# Run one tick to invoke the credential exchange check # Run one tick to invoke the credential exchange check
now = utcnow() freezer.tick(CODE_CHECK_ALARM_TIMEDELTA)
await fire_alarm(hass, now + CODE_CHECK_ALARM_TIMEDELTA) await fire_alarm(hass, datetime.datetime.utcnow())
await hass.async_block_till_done() await hass.async_block_till_done()
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"] flow_id=result["flow_id"]
@ -127,12 +130,11 @@ async def test_full_flow_yaml_creds(
assert "data" in result assert "data" in result
data = result["data"] data = result["data"]
assert "token" in data assert "token" in data
assert 0 < data["token"]["expires_in"] < 8 * 86400
assert ( assert (
datetime.datetime.now().timestamp() data["token"]["expires_in"]
<= data["token"]["expires_at"] == 60 * 60 - CODE_CHECK_ALARM_TIMEDELTA.total_seconds()
< (datetime.datetime.now() + datetime.timedelta(days=8)).timestamp()
) )
assert data["token"]["expires_at"] == 1654273199.0
data["token"].pop("expires_at") data["token"].pop("expires_at")
data["token"].pop("expires_in") data["token"].pop("expires_in")
assert data == { assert data == {