WIP: Hass.io sent token to supervisor (#15536)

Hass.io sent token to supervisor
pull/15353/merge
Paulus Schoutsen 2018-07-23 14:14:57 +02:00 committed by GitHub
parent 4e7dbf9ce5
commit 8213b1476f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 114 additions and 73 deletions

View File

@ -27,6 +27,8 @@ _LOGGER = logging.getLogger(__name__)
DOMAIN = 'hassio'
DEPENDENCIES = ['http']
STORAGE_KEY = DOMAIN
STORAGE_VERSION = 1
CONF_FRONTEND_REPO = 'development_repo'
@ -167,6 +169,21 @@ def async_setup(hass, config):
_LOGGER.error("Not connected with Hass.io")
return False
store = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY)
data = yield from store.async_load()
if data is None:
data = {}
if 'hassio_user' in data:
user = yield from hass.auth.async_get_user(data['hassio_user'])
refresh_token = list(user.refresh_tokens.values())[0]
else:
user = yield from hass.auth.async_create_system_user('Hass.io')
refresh_token = yield from hass.auth.async_create_refresh_token(user)
data['hassio_user'] = user.id
yield from store.async_save(data)
# This overrides the normal API call that would be forwarded
development_repo = config.get(DOMAIN, {}).get(CONF_FRONTEND_REPO)
if development_repo is not None:
@ -186,8 +203,13 @@ def async_setup(hass, config):
embed_iframe=True,
)
if 'http' in config:
yield from hassio.update_hass_api(config['http'])
# Temporary. No refresh token tells supervisor to use API password.
if hass.auth.active:
token = refresh_token.token
else:
token = None
yield from hassio.update_hass_api(config.get('http', {}), token)
if 'homeassistant' in config:
yield from hassio.update_hass_timezone(config['homeassistant'])

View File

@ -23,10 +23,9 @@ X_HASSIO = 'X-HASSIO-KEY'
def _api_bool(funct):
"""Return a boolean."""
@asyncio.coroutine
def _wrapper(*argv, **kwargs):
async def _wrapper(*argv, **kwargs):
"""Wrap function."""
data = yield from funct(*argv, **kwargs)
data = await funct(*argv, **kwargs)
return data and data['result'] == "ok"
return _wrapper
@ -34,10 +33,9 @@ def _api_bool(funct):
def _api_data(funct):
"""Return data of an api."""
@asyncio.coroutine
def _wrapper(*argv, **kwargs):
async def _wrapper(*argv, **kwargs):
"""Wrap function."""
data = yield from funct(*argv, **kwargs)
data = await funct(*argv, **kwargs)
if data and data['result'] == "ok":
return data['data']
return None
@ -94,24 +92,23 @@ class HassIO:
return self.send_command("/homeassistant/check", timeout=300)
@_api_bool
def update_hass_api(self, http_config):
"""Update Home Assistant API data on Hass.io.
This method return a coroutine.
"""
async def update_hass_api(self, http_config, refresh_token):
"""Update Home Assistant API data on Hass.io."""
port = http_config.get(CONF_SERVER_PORT) or SERVER_PORT
options = {
'ssl': CONF_SSL_CERTIFICATE in http_config,
'port': port,
'password': http_config.get(CONF_API_PASSWORD),
'watchdog': True,
'refresh_token': refresh_token,
}
if CONF_SERVER_HOST in http_config:
options['watchdog'] = False
_LOGGER.warning("Don't use 'server_host' options with Hass.io")
return self.send_command("/homeassistant/options", payload=options)
return await self.send_command("/homeassistant/options",
payload=options)
@_api_bool
def update_hass_timezone(self, core_config):

View File

@ -3,8 +3,11 @@ import asyncio
import os
from unittest.mock import patch, Mock
import pytest
from homeassistant.setup import async_setup_component
from homeassistant.components.hassio import async_check_config
from homeassistant.components.hassio import (
STORAGE_KEY, async_check_config)
from tests.common import mock_coro
@ -15,20 +18,28 @@ MOCK_ENVIRON = {
}
@asyncio.coroutine
def test_setup_api_ping(hass, aioclient_mock):
"""Test setup with API ping."""
@pytest.fixture(autouse=True)
def mock_all(aioclient_mock):
"""Mock all setup requests."""
aioclient_mock.post(
"http://127.0.0.1/homeassistant/options", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.post(
"http://127.0.0.1/supervisor/options", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
@asyncio.coroutine
def test_setup_api_ping(hass, aioclient_mock):
"""Test setup with API ping."""
with patch.dict(os.environ, MOCK_ENVIRON):
result = yield from async_setup_component(hass, 'hassio', {})
assert result
assert aioclient_mock.call_count == 2
assert aioclient_mock.call_count == 3
assert hass.components.hassio.get_homeassistant_version() == "10.0"
assert hass.components.hassio.is_hassio()
@ -36,14 +47,6 @@ def test_setup_api_ping(hass, aioclient_mock):
@asyncio.coroutine
def test_setup_api_push_api_data(hass, aioclient_mock):
"""Test setup with API push."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
aioclient_mock.post(
"http://127.0.0.1/homeassistant/options", json={'result': 'ok'})
with patch.dict(os.environ, MOCK_ENVIRON):
result = yield from async_setup_component(hass, 'hassio', {
'http': {
@ -64,14 +67,6 @@ def test_setup_api_push_api_data(hass, aioclient_mock):
@asyncio.coroutine
def test_setup_api_push_api_data_server_host(hass, aioclient_mock):
"""Test setup with API push with active server host."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
aioclient_mock.post(
"http://127.0.0.1/homeassistant/options", json={'result': 'ok'})
with patch.dict(os.environ, MOCK_ENVIRON):
result = yield from async_setup_component(hass, 'hassio', {
'http': {
@ -90,19 +85,12 @@ def test_setup_api_push_api_data_server_host(hass, aioclient_mock):
assert not aioclient_mock.mock_calls[1][2]['watchdog']
@asyncio.coroutine
def test_setup_api_push_api_data_default(hass, aioclient_mock):
async def test_setup_api_push_api_data_default(hass, aioclient_mock,
hass_storage):
"""Test setup with API push default data."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
aioclient_mock.post(
"http://127.0.0.1/homeassistant/options", json={'result': 'ok'})
with patch.dict(os.environ, MOCK_ENVIRON):
result = yield from async_setup_component(hass, 'hassio', {
with patch.dict(os.environ, MOCK_ENVIRON), \
patch('homeassistant.auth.AuthManager.active', return_value=True):
result = await async_setup_component(hass, 'hassio', {
'http': {},
'hassio': {}
})
@ -112,19 +100,61 @@ def test_setup_api_push_api_data_default(hass, aioclient_mock):
assert not aioclient_mock.mock_calls[1][2]['ssl']
assert aioclient_mock.mock_calls[1][2]['password'] is None
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
refresh_token = aioclient_mock.mock_calls[1][2]['refresh_token']
hassio_user = await hass.auth.async_get_user(
hass_storage[STORAGE_KEY]['data']['hassio_user']
)
assert hassio_user is not None
assert hassio_user.system_generated
assert refresh_token in hassio_user.refresh_tokens
async def test_setup_api_push_api_data_no_auth(hass, aioclient_mock,
hass_storage):
"""Test setup with API push default data."""
with patch.dict(os.environ, MOCK_ENVIRON):
result = await async_setup_component(hass, 'hassio', {
'http': {},
'hassio': {}
})
assert result
assert aioclient_mock.call_count == 3
assert not aioclient_mock.mock_calls[1][2]['ssl']
assert aioclient_mock.mock_calls[1][2]['password'] is None
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
assert aioclient_mock.mock_calls[1][2]['refresh_token'] is None
async def test_setup_api_existing_hassio_user(hass, aioclient_mock,
hass_storage):
"""Test setup with API push default data."""
user = await hass.auth.async_create_system_user('Hass.io test')
token = await hass.auth.async_create_refresh_token(user)
hass_storage[STORAGE_KEY] = {
'version': 1,
'data': {
'hassio_user': user.id
}
}
with patch.dict(os.environ, MOCK_ENVIRON), \
patch('homeassistant.auth.AuthManager.active', return_value=True):
result = await async_setup_component(hass, 'hassio', {
'http': {},
'hassio': {}
})
assert result
assert aioclient_mock.call_count == 3
assert not aioclient_mock.mock_calls[1][2]['ssl']
assert aioclient_mock.mock_calls[1][2]['password'] is None
assert aioclient_mock.mock_calls[1][2]['port'] == 8123
assert aioclient_mock.mock_calls[1][2]['refresh_token'] == token.token
@asyncio.coroutine
def test_setup_core_push_timezone(hass, aioclient_mock):
"""Test setup with API push default data."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
aioclient_mock.post(
"http://127.0.0.1/supervisor/options", json={'result': 'ok'})
with patch.dict(os.environ, MOCK_ENVIRON):
result = yield from async_setup_component(hass, 'hassio', {
'hassio': {},
@ -134,21 +164,13 @@ def test_setup_core_push_timezone(hass, aioclient_mock):
})
assert result
assert aioclient_mock.call_count == 3
assert aioclient_mock.mock_calls[1][2]['timezone'] == "testzone"
assert aioclient_mock.call_count == 4
assert aioclient_mock.mock_calls[2][2]['timezone'] == "testzone"
@asyncio.coroutine
def test_setup_hassio_no_additional_data(hass, aioclient_mock):
"""Test setup with API push default data."""
aioclient_mock.get(
"http://127.0.0.1/supervisor/ping", json={'result': 'ok'})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={
'result': 'ok', 'data': {'last_version': '10.0'}})
aioclient_mock.get(
"http://127.0.0.1/homeassistant/info", json={'result': 'ok'})
with patch.dict(os.environ, MOCK_ENVIRON), \
patch.dict(os.environ, {'HASSIO_TOKEN': "123456"}):
result = yield from async_setup_component(hass, 'hassio', {
@ -156,7 +178,7 @@ def test_setup_hassio_no_additional_data(hass, aioclient_mock):
})
assert result
assert aioclient_mock.call_count == 2
assert aioclient_mock.call_count == 3
assert aioclient_mock.mock_calls[-1][3]['X-HASSIO-KEY'] == "123456"
@ -234,14 +256,14 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
'hassio', 'addon_stdin', {'addon': 'test', 'input': 'test'})
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 4
assert aioclient_mock.call_count == 5
assert aioclient_mock.mock_calls[-1][2] == 'test'
yield from hass.services.async_call('hassio', 'host_shutdown', {})
yield from hass.services.async_call('hassio', 'host_reboot', {})
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 6
assert aioclient_mock.call_count == 7
yield from hass.services.async_call('hassio', 'snapshot_full', {})
yield from hass.services.async_call('hassio', 'snapshot_partial', {
@ -251,7 +273,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
})
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 8
assert aioclient_mock.call_count == 9
assert aioclient_mock.mock_calls[-1][2] == {
'addons': ['test'], 'folders': ['ssl'], 'password': "123456"}
@ -267,7 +289,7 @@ def test_service_calls(hassio_env, hass, aioclient_mock):
})
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 10
assert aioclient_mock.call_count == 11
assert aioclient_mock.mock_calls[-1][2] == {
'addons': ['test'], 'folders': ['ssl'], 'homeassistant': False,
'password': "123456"
@ -289,17 +311,17 @@ def test_service_calls_core(hassio_env, hass, aioclient_mock):
yield from hass.services.async_call('homeassistant', 'stop')
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 1
assert aioclient_mock.call_count == 2
yield from hass.services.async_call('homeassistant', 'check_config')
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 2
assert aioclient_mock.call_count == 3
yield from hass.services.async_call('homeassistant', 'restart')
yield from hass.async_block_till_done()
assert aioclient_mock.call_count == 4
assert aioclient_mock.call_count == 5
@asyncio.coroutine