From 17cbe0c6ced00bb5ed46499b49fbddf51eba1169 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 22 May 2017 11:00:02 -0700 Subject: [PATCH] Allow fetching hass.io panel without auth (#7714) --- homeassistant/components/hassio.py | 7 +++- tests/components/test_hassio.py | 59 ++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/hassio.py b/homeassistant/components/hassio.py index ed7e13a2969..e33a387eada 100644 --- a/homeassistant/components/hassio.py +++ b/homeassistant/components/hassio.py @@ -16,7 +16,7 @@ from aiohttp.hdrs import CONTENT_TYPE import async_timeout from homeassistant.const import CONTENT_TYPE_TEXT_PLAIN -from homeassistant.components.http import HomeAssistantView +from homeassistant.components.http import HomeAssistantView, KEY_AUTHENTICATED from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.components.frontend import register_built_in_panel @@ -139,7 +139,7 @@ class HassIOView(HomeAssistantView): name = "api:hassio" url = "/api/hassio/{path:.+}" - requires_auth = True + requires_auth = False def __init__(self, hassio): """Initialize a hassio base view.""" @@ -148,6 +148,9 @@ class HassIOView(HomeAssistantView): @asyncio.coroutine def _handle(self, request, path): """Route data to hassio.""" + if path != 'panel' and not request[KEY_AUTHENTICATED]: + return web.Response(status=401) + client = yield from self.hassio.command_proxy(path, request) data = yield from client.read() diff --git a/tests/components/test_hassio.py b/tests/components/test_hassio.py index 658e78b4523..eb0754fdc0a 100644 --- a/tests/components/test_hassio.py +++ b/tests/components/test_hassio.py @@ -5,9 +5,12 @@ from unittest.mock import patch, Mock, MagicMock import pytest +from homeassistant.const import HTTP_HEADER_HA_AUTH from homeassistant.setup import async_setup_component -from tests.common import mock_coro, mock_http_component_app +from tests.common import mock_coro + +API_PASSWORD = 'pass1234' @pytest.fixture @@ -22,10 +25,12 @@ def hassio_env(): @pytest.fixture def hassio_client(hassio_env, hass, test_client): """Create mock hassio http client.""" - app = mock_http_component_app(hass) - hass.loop.run_until_complete(async_setup_component(hass, 'hassio', {})) - hass.http.views['api:hassio'].register(app.router) - yield hass.loop.run_until_complete(test_client(app)) + hass.loop.run_until_complete(async_setup_component(hass, 'hassio', { + 'http': { + 'api_password': API_PASSWORD + } + })) + yield hass.loop.run_until_complete(test_client(hass.http.app)) @asyncio.coroutine @@ -56,7 +61,40 @@ def test_forward_request(hassio_client): Mock(return_value=mock_coro(response))), \ patch('homeassistant.components.hassio._create_response') as mresp: mresp.return_value = 'response' - resp = yield from hassio_client.post('/api/hassio/beer') + resp = yield from hassio_client.post('/api/hassio/beer', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) + + # Check we got right response + assert resp.status == 200 + body = yield from resp.text() + assert body == 'response' + + # Check we forwarded command + assert len(mresp.mock_calls) == 1 + assert mresp.mock_calls[0][1] == (response, 'data') + + +@asyncio.coroutine +def test_auth_required_forward_request(hassio_client): + """Test auth required for normal request.""" + resp = yield from hassio_client.post('/api/hassio/beer') + + # Check we got right response + assert resp.status == 401 + + +@asyncio.coroutine +def test_forward_request_no_auth_for_panel(hassio_client): + """Test no auth needed for .""" + response = MagicMock() + response.read.return_value = mock_coro('data') + + with patch('homeassistant.components.hassio.HassIO.command_proxy', + Mock(return_value=mock_coro(response))), \ + patch('homeassistant.components.hassio._create_response') as mresp: + mresp.return_value = 'response' + resp = yield from hassio_client.get('/api/hassio/panel') # Check we got right response assert resp.status == 200 @@ -79,7 +117,9 @@ def test_forward_log_request(hassio_client): patch('homeassistant.components.hassio.' '_create_response_log') as mresp: mresp.return_value = 'response' - resp = yield from hassio_client.get('/api/hassio/beer/logs') + resp = yield from hassio_client.get('/api/hassio/beer/logs', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) # Check we got right response assert resp.status == 200 @@ -96,5 +136,8 @@ def test_bad_gateway_when_cannot_find_supervisor(hassio_client): """Test we get a bad gateway error if we can't find supervisor.""" with patch('homeassistant.components.hassio.async_timeout.timeout', side_effect=asyncio.TimeoutError): - resp = yield from hassio_client.get('/api/hassio/addons/test/info') + resp = yield from hassio_client.get( + '/api/hassio/addons/test/info', headers={ + HTTP_HEADER_HA_AUTH: API_PASSWORD + }) assert resp.status == 502