Fix Google Assistant User with Cloud (#22042)
* Fix Google Assistant User with Cloud * Fix User Agent ID * respell * Fix object * Fix tests * fix lint * Fix lint
This commit is contained in:
parent
3769f5893a
commit
6a80ffa8cc
5 changed files with 94 additions and 20 deletions
|
@ -3,6 +3,7 @@ import logging
|
||||||
|
|
||||||
import voluptuous as vol
|
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.alexa import smart_home as alexa_sh
|
||||||
from homeassistant.components.google_assistant import const as ga_c
|
from homeassistant.components.google_assistant import const as ga_c
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -136,12 +137,21 @@ async def async_setup(hass, config):
|
||||||
else:
|
else:
|
||||||
kwargs = {CONF_MODE: DEFAULT_MODE}
|
kwargs = {CONF_MODE: DEFAULT_MODE}
|
||||||
|
|
||||||
|
# Alexa/Google custom config
|
||||||
alexa_conf = kwargs.pop(CONF_ALEXA, None) or ALEXA_SCHEMA({})
|
alexa_conf = kwargs.pop(CONF_ALEXA, None) or ALEXA_SCHEMA({})
|
||||||
google_conf = kwargs.pop(CONF_GOOGLE_ACTIONS, None) or GACTIONS_SCHEMA({})
|
google_conf = kwargs.pop(CONF_GOOGLE_ACTIONS, None) or GACTIONS_SCHEMA({})
|
||||||
|
|
||||||
|
# Cloud settings
|
||||||
prefs = CloudPreferences(hass)
|
prefs = CloudPreferences(hass)
|
||||||
await prefs.async_initialize()
|
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()
|
websession = hass.helpers.aiohttp_client.async_get_clientsession()
|
||||||
client = CloudClient(hass, prefs, websession, alexa_conf, google_conf)
|
client = CloudClient(hass, prefs, websession, alexa_conf, google_conf)
|
||||||
cloud = hass.data[DOMAIN] = Cloud(client, **kwargs)
|
cloud = hass.data[DOMAIN] = Cloud(client, **kwargs)
|
||||||
|
|
|
@ -136,12 +136,16 @@ class CloudClient(Interface):
|
||||||
if not self._prefs.google_enabled:
|
if not self._prefs.google_enabled:
|
||||||
return ga.turned_off_response(payload)
|
return ga.turned_off_response(payload)
|
||||||
|
|
||||||
cloud = self._hass.data[DOMAIN]
|
answer = await ga.async_handle_message(
|
||||||
return await ga.async_handle_message(
|
self._hass, self.google_config, self.prefs.cloud_user, payload
|
||||||
self._hass, self.google_config,
|
|
||||||
cloud.claims['cognito:username'], payload
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fix AgentUserId
|
||||||
|
cloud = self._hass.data[DOMAIN]
|
||||||
|
answer['payload']['agentUserId'] = cloud.claims['cognito:username']
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
async def async_webhook_message(
|
async def async_webhook_message(
|
||||||
self, payload: Dict[Any, Any]) -> Dict[Any, Any]:
|
self, payload: Dict[Any, Any]) -> Dict[Any, Any]:
|
||||||
"""Process cloud webhook message to client."""
|
"""Process cloud webhook message to client."""
|
||||||
|
|
|
@ -7,6 +7,7 @@ PREF_ENABLE_GOOGLE = 'google_enabled'
|
||||||
PREF_ENABLE_REMOTE = 'remote_enabled'
|
PREF_ENABLE_REMOTE = 'remote_enabled'
|
||||||
PREF_GOOGLE_ALLOW_UNLOCK = 'google_allow_unlock'
|
PREF_GOOGLE_ALLOW_UNLOCK = 'google_allow_unlock'
|
||||||
PREF_CLOUDHOOKS = 'cloudhooks'
|
PREF_CLOUDHOOKS = 'cloudhooks'
|
||||||
|
PREF_CLOUD_USER = 'cloud_user'
|
||||||
|
|
||||||
CONF_ALEXA = 'alexa'
|
CONF_ALEXA = 'alexa'
|
||||||
CONF_ALIASES = 'aliases'
|
CONF_ALIASES = 'aliases'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Preference management for cloud."""
|
"""Preference management for cloud."""
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN, PREF_ENABLE_ALEXA, PREF_ENABLE_GOOGLE, PREF_ENABLE_REMOTE,
|
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_KEY = DOMAIN
|
||||||
STORAGE_VERSION = 1
|
STORAGE_VERSION = 1
|
||||||
|
@ -26,14 +26,16 @@ class CloudPreferences:
|
||||||
PREF_ENABLE_GOOGLE: True,
|
PREF_ENABLE_GOOGLE: True,
|
||||||
PREF_ENABLE_REMOTE: False,
|
PREF_ENABLE_REMOTE: False,
|
||||||
PREF_GOOGLE_ALLOW_UNLOCK: False,
|
PREF_GOOGLE_ALLOW_UNLOCK: False,
|
||||||
PREF_CLOUDHOOKS: {}
|
PREF_CLOUDHOOKS: {},
|
||||||
|
PREF_CLOUD_USER: None,
|
||||||
}
|
}
|
||||||
|
|
||||||
self._prefs = prefs
|
self._prefs = prefs
|
||||||
|
|
||||||
async def async_update(self, *, google_enabled=_UNDEF,
|
async def async_update(self, *, google_enabled=_UNDEF,
|
||||||
alexa_enabled=_UNDEF, remote_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."""
|
"""Update user preferences."""
|
||||||
for key, value in (
|
for key, value in (
|
||||||
(PREF_ENABLE_GOOGLE, google_enabled),
|
(PREF_ENABLE_GOOGLE, google_enabled),
|
||||||
|
@ -41,6 +43,7 @@ class CloudPreferences:
|
||||||
(PREF_ENABLE_REMOTE, remote_enabled),
|
(PREF_ENABLE_REMOTE, remote_enabled),
|
||||||
(PREF_GOOGLE_ALLOW_UNLOCK, google_allow_unlock),
|
(PREF_GOOGLE_ALLOW_UNLOCK, google_allow_unlock),
|
||||||
(PREF_CLOUDHOOKS, cloudhooks),
|
(PREF_CLOUDHOOKS, cloudhooks),
|
||||||
|
(PREF_CLOUD_USER, cloud_user),
|
||||||
):
|
):
|
||||||
if value is not _UNDEF:
|
if value is not _UNDEF:
|
||||||
self._prefs[key] = value
|
self._prefs[key] = value
|
||||||
|
@ -75,3 +78,8 @@ class CloudPreferences:
|
||||||
def cloudhooks(self):
|
def cloudhooks(self):
|
||||||
"""Return the published cloud webhooks."""
|
"""Return the published cloud webhooks."""
|
||||||
return self._prefs.get(PREF_CLOUDHOOKS, {})
|
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)
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
"""Test the cloud component."""
|
"""Test the cloud component."""
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.auth.const import GROUP_ID_ADMIN
|
||||||
EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START)
|
|
||||||
from homeassistant.components import cloud
|
from homeassistant.components import cloud
|
||||||
from homeassistant.components.cloud.const import DOMAIN
|
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
|
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."""
|
"""Test non-dev mode loads info from SERVERS constant."""
|
||||||
hass = MagicMock(data={})
|
with patch("hass_nabucasa.Cloud.start", return_value=mock_coro()):
|
||||||
|
result = await async_setup_component(hass, 'cloud', {
|
||||||
with patch(
|
'http': {},
|
||||||
"homeassistant.components.cloud.prefs.CloudPreferences."
|
|
||||||
"async_initialize",
|
|
||||||
return_value=mock_coro()
|
|
||||||
):
|
|
||||||
result = await cloud.async_setup(hass, {
|
|
||||||
'cloud': {
|
'cloud': {
|
||||||
cloud.CONF_MODE: cloud.MODE_DEV,
|
cloud.CONF_MODE: cloud.MODE_DEV,
|
||||||
'cognito_client_id': 'test-cognito_client_id',
|
'cognito_client_id': 'test-cognito_client_id',
|
||||||
|
@ -79,3 +76,57 @@ async def test_startup_shutdown_events(hass, mock_cloud_fixture):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert mock_stop.called
|
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue