Skip to content

Commit

Permalink
Fix Google Assistant User with Cloud (#22042)
Browse files Browse the repository at this point in the history
* Fix Google Assistant User with Cloud

* Fix User Agent ID

* respell

* Fix object

* Fix tests

* fix lint

* Fix lint
  • Loading branch information
pvizeli authored and balloob committed Mar 14, 2019
1 parent 3769f58 commit 6a80ffa
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 20 deletions.
10 changes: 10 additions & 0 deletions homeassistant/components/cloud/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import voluptuous as vol

from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.components.alexa import smart_home as alexa_sh
from homeassistant.components.google_assistant import const as ga_c
from homeassistant.const import (
Expand Down Expand Up @@ -136,12 +137,21 @@ async def async_setup(hass, config):
else:
kwargs = {CONF_MODE: DEFAULT_MODE}

# Alexa/Google custom config
alexa_conf = kwargs.pop(CONF_ALEXA, None) or ALEXA_SCHEMA({})
google_conf = kwargs.pop(CONF_GOOGLE_ACTIONS, None) or GACTIONS_SCHEMA({})

# Cloud settings
prefs = CloudPreferences(hass)
await prefs.async_initialize()

# Cloud user
if not prefs.cloud_user:
user = await hass.auth.async_create_system_user(
'Home Assistant Cloud', [GROUP_ID_ADMIN])
await prefs.async_update(cloud_user=user.id)

# Initialize Cloud
websession = hass.helpers.aiohttp_client.async_get_clientsession()
client = CloudClient(hass, prefs, websession, alexa_conf, google_conf)
cloud = hass.data[DOMAIN] = Cloud(client, **kwargs)
Expand Down
12 changes: 8 additions & 4 deletions homeassistant/components/cloud/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,16 @@ async def async_google_message(
if not self._prefs.google_enabled:
return ga.turned_off_response(payload)

cloud = self._hass.data[DOMAIN]
return await ga.async_handle_message(
self._hass, self.google_config,
cloud.claims['cognito:username'], payload
answer = await ga.async_handle_message(
self._hass, self.google_config, self.prefs.cloud_user, payload
)

# Fix AgentUserId
cloud = self._hass.data[DOMAIN]
answer['payload']['agentUserId'] = cloud.claims['cognito:username']

return answer

async def async_webhook_message(
self, payload: Dict[Any, Any]) -> Dict[Any, Any]:
"""Process cloud webhook message to client."""
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/cloud/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
PREF_ENABLE_REMOTE = 'remote_enabled'
PREF_GOOGLE_ALLOW_UNLOCK = 'google_allow_unlock'
PREF_CLOUDHOOKS = 'cloudhooks'
PREF_CLOUD_USER = 'cloud_user'

CONF_ALEXA = 'alexa'
CONF_ALIASES = 'aliases'
Expand Down
14 changes: 11 additions & 3 deletions homeassistant/components/cloud/prefs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Preference management for cloud."""
from .const import (
DOMAIN, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, PREF_ENABLE_REMOTE,
PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS)
PREF_GOOGLE_ALLOW_UNLOCK, PREF_CLOUDHOOKS, PREF_CLOUD_USER)

STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
Expand All @@ -26,21 +26,24 @@ async def async_initialize(self):
PREF_ENABLE_GOOGLE: True,
PREF_ENABLE_REMOTE: False,
PREF_GOOGLE_ALLOW_UNLOCK: False,
PREF_CLOUDHOOKS: {}
PREF_CLOUDHOOKS: {},
PREF_CLOUD_USER: None,
}

self._prefs = prefs

async def async_update(self, *, google_enabled=_UNDEF,
alexa_enabled=_UNDEF, remote_enabled=_UNDEF,
google_allow_unlock=_UNDEF, cloudhooks=_UNDEF):
google_allow_unlock=_UNDEF, cloudhooks=_UNDEF,
cloud_user=_UNDEF):
"""Update user preferences."""
for key, value in (
(PREF_ENABLE_GOOGLE, google_enabled),
(PREF_ENABLE_ALEXA, alexa_enabled),
(PREF_ENABLE_REMOTE, remote_enabled),
(PREF_GOOGLE_ALLOW_UNLOCK, google_allow_unlock),
(PREF_CLOUDHOOKS, cloudhooks),
(PREF_CLOUD_USER, cloud_user),
):
if value is not _UNDEF:
self._prefs[key] = value
Expand Down Expand Up @@ -75,3 +78,8 @@ def google_allow_unlock(self):
def cloudhooks(self):
"""Return the published cloud webhooks."""
return self._prefs.get(PREF_CLOUDHOOKS, {})

@property
def cloud_user(self) -> str:
"""Return ID from Home Assistant Cloud system user."""
return self._prefs.get(PREF_CLOUD_USER)
77 changes: 64 additions & 13 deletions tests/components/cloud/test_init.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
"""Test the cloud component."""
from unittest.mock import MagicMock, patch
from unittest.mock import patch

from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.components import cloud
from homeassistant.components.cloud.const import DOMAIN

from homeassistant.components.cloud.prefs import STORAGE_KEY
from homeassistant.const import (
EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.setup import async_setup_component
from tests.common import mock_coro


async def test_constructor_loads_info_from_config():
async def test_constructor_loads_info_from_config(hass):
"""Test non-dev mode loads info from SERVERS constant."""
hass = MagicMock(data={})

with patch(
"homeassistant.components.cloud.prefs.CloudPreferences."
"async_initialize",
return_value=mock_coro()
):
result = await cloud.async_setup(hass, {
with patch("hass_nabucasa.Cloud.start", return_value=mock_coro()):
result = await async_setup_component(hass, 'cloud', {
'http': {},
'cloud': {
cloud.CONF_MODE: cloud.MODE_DEV,
'cognito_client_id': 'test-cognito_client_id',
Expand Down Expand Up @@ -79,3 +76,57 @@ async def test_startup_shutdown_events(hass, mock_cloud_fixture):
await hass.async_block_till_done()

assert mock_stop.called


async def test_setup_existing_cloud_user(hass, hass_storage):
"""Test setup with API push default data."""
user = await hass.auth.async_create_system_user('Cloud test')
hass_storage[STORAGE_KEY] = {
'version': 1,
'data': {
'cloud_user': user.id
}
}
with patch('hass_nabucasa.Cloud.start', return_value=mock_coro()):
result = await async_setup_component(hass, 'cloud', {
'http': {},
'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

assert hass_storage[STORAGE_KEY]['data']['cloud_user'] == user.id


async def test_setup_setup_cloud_user(hass, hass_storage):
"""Test setup with API push default data."""
hass_storage[STORAGE_KEY] = {
'version': 1,
'data': {
'cloud_user': None
}
}
with patch('hass_nabucasa.Cloud.start', return_value=mock_coro()):
result = await async_setup_component(hass, 'cloud', {
'http': {},
'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

cloud_user = await hass.auth.async_get_user(
hass_storage[STORAGE_KEY]['data']['cloud_user']
)

assert cloud_user
assert cloud_user.groups[0].id == GROUP_ID_ADMIN

0 comments on commit 6a80ffa

Please sign in to comment.