Skip to content

Commit

Permalink
Don't use keyset (#17984)
Browse files Browse the repository at this point in the history
  • Loading branch information
balloob committed Oct 29, 2018
1 parent a91d894 commit 1e03f94
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 80 deletions.
68 changes: 3 additions & 65 deletions homeassistant/components/cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/cloud/
"""
import asyncio
from datetime import datetime, timedelta
import json
import logging
import os

import aiohttp
import async_timeout
import voluptuous as vol

from homeassistant.const import (
EVENT_HOMEASSISTANT_START, CONF_REGION, CONF_MODE, CONF_NAME)
from homeassistant.helpers import entityfilter, config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import dt as dt_util
from homeassistant.components.alexa import smart_home as alexa_sh
from homeassistant.components.google_assistant import helpers as ga_h
Expand Down Expand Up @@ -129,7 +125,6 @@ def __init__(self, hass, mode, alexa, google_actions,
self._google_actions = google_actions
self._gactions_config = None
self._prefs = None
self.jwt_keyset = None
self.id_token = None
self.access_token = None
self.refresh_token = None
Expand Down Expand Up @@ -262,13 +257,6 @@ async def async_start(self, _):
}
self._prefs = prefs

success = await self._fetch_jwt_keyset()

# Fetching keyset can fail if internet is not up yet.
if not success:
self.hass.helpers.event.async_call_later(5, self.async_start)
return

def load_config():
"""Load config."""
# Ensure config dir exists
Expand All @@ -288,14 +276,6 @@ def load_config():
if info is None:
return

# Validate tokens
try:
for token in 'id_token', 'access_token':
self._decode_claims(info[token])
except ValueError as err: # Raised when token is invalid
_LOGGER.warning("Found invalid token %s: %s", token, err)
return

self.id_token = info['id_token']
self.access_token = info['access_token']
self.refresh_token = info['refresh_token']
Expand All @@ -311,49 +291,7 @@ async def update_preferences(self, *, google_enabled=_UNDEF,
self._prefs[STORAGE_ENABLE_ALEXA] = alexa_enabled
await self._store.async_save(self._prefs)

async def _fetch_jwt_keyset(self):
"""Fetch the JWT keyset for the Cognito instance."""
session = async_get_clientsession(self.hass)
url = ("https://cognito-idp.us-east-1.amazonaws.com/"
"{}/.well-known/jwks.json".format(self.user_pool_id))

try:
with async_timeout.timeout(10, loop=self.hass.loop):
req = await session.get(url)
self.jwt_keyset = await req.json()

return True

except (asyncio.TimeoutError, aiohttp.ClientError) as err:
_LOGGER.error("Error fetching Cognito keyset: %s", err)
return False

def _decode_claims(self, token):
def _decode_claims(self, token): # pylint: disable=no-self-use
"""Decode the claims in a token."""
from jose import jwt, exceptions as jose_exceptions
try:
header = jwt.get_unverified_header(token)
except jose_exceptions.JWTError as err:
raise ValueError(str(err)) from None
kid = header.get('kid')

if kid is None:
raise ValueError("No kid in header")

# Locate the key for this kid
key = None
for key_dict in self.jwt_keyset['keys']:
if key_dict['kid'] == kid:
key = key_dict
break
if not key:
raise ValueError(
"Unable to locate kid ({}) in keyset".format(kid))

try:
return jwt.decode(
token, key, audience=self.cognito_client_id, options={
'verify_exp': False,
})
except jose_exceptions.JWTError as err:
raise ValueError(str(err)) from None
from jose import jwt
return jwt.get_unverified_claims(token)
25 changes: 10 additions & 15 deletions tests/components/cloud/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ def test_constructor_loads_info_from_constant():
'google_actions_sync_url': 'test-google_actions_sync_url',
'subscription_info_url': 'test-subscription-info-url'
}
}), patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
return_value=mock_coro(True)):
}):
result = yield from cloud.async_setup(hass, {
'cloud': {cloud.CONF_MODE: 'beer'}
})
Expand All @@ -54,17 +53,15 @@ def test_constructor_loads_info_from_config():
"""Test non-dev mode loads info from SERVERS constant."""
hass = MagicMock(data={})

with patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
return_value=mock_coro(True)):
result = yield from cloud.async_setup(hass, {
'cloud': {
cloud.CONF_MODE: cloud.MODE_DEV,
'cognito_client_id': 'test-cognito_client_id',
'user_pool_id': 'test-user_pool_id',
'region': 'test-region',
'relayer': 'test-relayer',
}
})
result = yield from cloud.async_setup(hass, {
'cloud': {
cloud.CONF_MODE: cloud.MODE_DEV,
'cognito_client_id': 'test-cognito_client_id',
'user_pool_id': 'test-user_pool_id',
'region': 'test-region',
'relayer': 'test-relayer',
}
})
assert result

cl = hass.data['cloud']
Expand All @@ -89,8 +86,6 @@ async def test_initialize_loads_info(mock_os, hass):
cl.iot.connect.return_value = mock_coro()

with patch('homeassistant.components.cloud.open', mopen, create=True), \
patch('homeassistant.components.cloud.Cloud._fetch_jwt_keyset',
return_value=mock_coro(True)), \
patch('homeassistant.components.cloud.Cloud._decode_claims'):
await cl.async_start(None)

Expand Down

0 comments on commit 1e03f94

Please sign in to comment.