From 51108b8fe9e0718d868a464f4abd0a6c6418ce65 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 27 Jul 2017 21:23:22 +0200 Subject: [PATCH] Hass.io: logo support / timeout handling (#8668) * Disable auth on logo / no timeout for addons update/install * fix tests --- homeassistant/components/hassio.py | 35 +++++++++++++++++++++++++----- tests/components/test_hassio.py | 22 +++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/hassio.py b/homeassistant/components/hassio.py index 4ce60fed014..1ba599c72b4 100644 --- a/homeassistant/components/hassio.py +++ b/homeassistant/components/hassio.py @@ -25,8 +25,15 @@ _LOGGER = logging.getLogger(__name__) DOMAIN = 'hassio' DEPENDENCIES = ['http'] -TIMEOUT = 10 -NO_TIMEOUT = set(['homeassistant/update', 'host/update', 'supervisor/update']) +NO_TIMEOUT = { + re.compile(r'^homeassistant/update$'), re.compile(r'^host/update$'), + re.compile(r'^supervisor/update$'), re.compile(r'^addons/[^/]*/update$'), + re.compile(r'^addons/[^/]*/install$') +} + +NO_AUTH = { + re.compile(r'^panel$'), re.compile(r'^addons/[^/]*/logo$') +} @asyncio.coroutine @@ -71,7 +78,7 @@ class HassIO(object): This method is a coroutine. """ try: - with async_timeout.timeout(TIMEOUT, loop=self.loop): + with async_timeout.timeout(10, loop=self.loop): request = yield from self.websession.get( "http://{}{}".format(self._ip, "/supervisor/ping") ) @@ -97,12 +104,12 @@ class HassIO(object): This method is a coroutine. """ - read_timeout = 0 if path in NO_TIMEOUT else 300 + read_timeout = _get_timeout(path) try: data = None headers = None - with async_timeout.timeout(TIMEOUT, loop=self.loop): + with async_timeout.timeout(10, loop=self.loop): data = yield from request.read() if data: headers = {CONTENT_TYPE: request.content_type} @@ -140,7 +147,7 @@ class HassIOView(HomeAssistantView): @asyncio.coroutine def _handle(self, request, path): """Route data to hassio.""" - if path != 'panel' and not request[KEY_AUTHENTICATED]: + if _need_auth(path) and not request[KEY_AUTHENTICATED]: return web.Response(status=401) client = yield from self.hassio.command_proxy(path, request) @@ -173,3 +180,19 @@ def _create_response_log(client, data): status=client.status, content_type=CONTENT_TYPE_TEXT_PLAIN, ) + + +def _get_timeout(path): + """Return timeout for a url path.""" + for re_path in NO_TIMEOUT: + if re_path.match(path): + return 0 + return 300 + + +def _need_auth(path): + """Return if a path need a auth.""" + for re_path in NO_AUTH: + if re_path.match(path): + return False + return True diff --git a/tests/components/test_hassio.py b/tests/components/test_hassio.py index eb0754fdc0a..ccb56891495 100644 --- a/tests/components/test_hassio.py +++ b/tests/components/test_hassio.py @@ -106,6 +106,28 @@ def test_forward_request_no_auth_for_panel(hassio_client): assert mresp.mock_calls[0][1] == (response, 'data') +@asyncio.coroutine +def test_forward_request_no_auth_for_logo(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/addons/bl_b392/logo') + + # 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_forward_log_request(hassio_client): """Test fetching normal log path."""