From fd21d6cc9d4003b2fd5d40979ffee3f1b4f57588 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 17 Dec 2018 11:27:03 +0100 Subject: [PATCH] Add Hass.io user headers to supervisor proxy --- homeassistant/components/hassio/const.py | 2 ++ homeassistant/components/hassio/http.py | 13 ++++++++++--- tests/components/hassio/conftest.py | 11 ++++++++++- tests/components/hassio/test_http.py | 21 +++++++++++++++++++-- tests/test_util/aiohttp.py | 5 +++++ 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index c539169ebe34..964f94bfb41c 100644 --- a/homeassistant/components/hassio/const.py +++ b/homeassistant/components/hassio/const.py @@ -10,3 +10,5 @@ ATTR_USERNAME = 'username' ATTR_PASSWORD = 'password' X_HASSIO = 'X-HASSIO-KEY' +X_HASS_USER_ID = 'X-HASS-USER-ID' +X_HASS_IS_ADMIN = 'X-HASS-IS-ADMIN' diff --git a/homeassistant/components/hassio/http.py b/homeassistant/components/hassio/http.py index be2806716a76..6b8004f76644 100644 --- a/homeassistant/components/hassio/http.py +++ b/homeassistant/components/hassio/http.py @@ -17,7 +17,7 @@ from aiohttp.web_exceptions import HTTPBadGateway from homeassistant.components.http import KEY_AUTHENTICATED, HomeAssistantView -from .const import X_HASSIO +from .const import X_HASSIO, X_HASS_USER_ID, X_HASS_IS_ADMIN _LOGGER = logging.getLogger(__name__) @@ -75,9 +75,16 @@ class HassIOView(HomeAssistantView): read_timeout = _get_timeout(path) hass = request.app['hass'] + data = None + headers = { + X_HASSIO: os.environ.get('HASSIO_TOKEN', ""), + } + user = request.get('hass_user') + if user is not None: + headers[X_HASS_USER_ID] = request['hass_user'].id + headers[X_HASS_IS_ADMIN] = str(int(request['hass_user'].is_admin)) + try: - data = None - headers = {X_HASSIO: os.environ.get('HASSIO_TOKEN', "")} with async_timeout.timeout(10, loop=hass.loop): data = await request.read() if data: diff --git a/tests/components/hassio/conftest.py b/tests/components/hassio/conftest.py index 435de6d1edf9..17a26b4fd2f4 100644 --- a/tests/components/hassio/conftest.py +++ b/tests/components/hassio/conftest.py @@ -27,7 +27,7 @@ def hassio_env(): @pytest.fixture -def hassio_client(hassio_env, hass, aiohttp_client, legacy_auth): +def hassio_stubs(hassio_env, hass, hass_client, aioclient_mock): """Create mock hassio http client.""" with patch('homeassistant.components.hassio.HassIO.update_hass_api', Mock(return_value=mock_coro({"result": "ok"}))), \ @@ -40,6 +40,15 @@ def hassio_client(hassio_env, hass, aiohttp_client, legacy_auth): 'api_password': API_PASSWORD } })) + + +@pytest.fixture +def hassio_client(hassio_stubs, hass, hass_client): + yield hass.loop.run_until_complete(hass_client()) + + +@pytest.fixture +def hassio_noauth_client(hassio_stubs, hass, aiohttp_client): yield hass.loop.run_until_complete(aiohttp_client(hass.http.app)) diff --git a/tests/components/hassio/test_http.py b/tests/components/hassio/test_http.py index 07db126312b0..8fca88d5fda5 100644 --- a/tests/components/hassio/test_http.py +++ b/tests/components/hassio/test_http.py @@ -40,9 +40,9 @@ def test_forward_request(hassio_client): 'build_type', [ 'supervisor/info', 'homeassistant/update', 'host/info' ]) -def test_auth_required_forward_request(hassio_client, build_type): +def test_auth_required_forward_request(hassio_noauth_client, build_type): """Test auth required for normal request.""" - resp = yield from hassio_client.post("/api/hassio/{}".format(build_type)) + resp = yield from hassio_noauth_client.post("/api/hassio/{}".format(build_type)) # Check we got right response assert resp.status == 401 @@ -135,3 +135,20 @@ def test_bad_gateway_when_cannot_find_supervisor(hassio_client): HTTP_HEADER_HA_AUTH: API_PASSWORD }) assert resp.status == 502 + + +async def test_forwarding_user_info(hassio_client, hass_admin_user, + aioclient_mock): + """Test that we forward user info correctly.""" + aioclient_mock.get('http://127.0.0.1/hello') + + resp = await hassio_client.get('/api/hassio/hello') + + # Check we got right response + assert resp.status == 200 + + assert len(aioclient_mock.mock_calls) == 1 + + req_headers = aioclient_mock.mock_calls[0][-1] + req_headers['X-HASS-USER-ID'] == hass_admin_user.id + req_headers['X-HASS-IS-ADMIN'] == '1' diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index f5bf0b8a4f8f..8b3b057bfc0d 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -182,6 +182,11 @@ class AiohttpClientMockResponse: """Return yarl of URL.""" return self._url + @property + def content_type(self): + """Return yarl of URL.""" + return self._headers.get('content-type') + @property def content(self): """Return content."""