diff --git a/homeassistant/components/hassio/const.py b/homeassistant/components/hassio/const.py index c539169ebe3..964f94bfb41 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 be2806716a7..6b8004f7664 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 435de6d1edf..f69be17a9e7 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,13 +40,27 @@ def hassio_client(hassio_env, hass, aiohttp_client, legacy_auth): 'api_password': API_PASSWORD } })) + + +@pytest.fixture +def hassio_client(hassio_stubs, hass, hass_client): + """Return a Hass.io HTTP client.""" + yield hass.loop.run_until_complete(hass_client()) + + +@pytest.fixture +def hassio_noauth_client(hassio_stubs, hass, aiohttp_client): + """Return a Hass.io HTTP client without auth.""" yield hass.loop.run_until_complete(aiohttp_client(hass.http.app)) @pytest.fixture def hassio_handler(hass, aioclient_mock): """Create mock hassio handler.""" - websession = hass.helpers.aiohttp_client.async_get_clientsession() + async def get_client_session(): + return hass.helpers.aiohttp_client.async_get_clientsession() + + websession = hass.loop.run_until_complete(get_client_session()) with patch.dict(os.environ, {'HASSIO_TOKEN': HASSIO_TOKEN}): yield HassIO(hass.loop, websession, "127.0.0.1") diff --git a/tests/components/hassio/test_auth.py b/tests/components/hassio/test_auth.py index fdf3230dedc..ed34ea96b49 100644 --- a/tests/components/hassio/test_auth.py +++ b/tests/components/hassio/test_auth.py @@ -4,14 +4,12 @@ from unittest.mock import patch, Mock from homeassistant.const import HTTP_HEADER_HA_AUTH from homeassistant.exceptions import HomeAssistantError -from tests.common import mock_coro, register_auth_provider +from tests.common import mock_coro from . import API_PASSWORD async def test_login_success(hass, hassio_client): """Test no auth needed for .""" - await register_auth_provider(hass, {'type': 'homeassistant'}) - with patch('homeassistant.auth.providers.homeassistant.' 'HassAuthProvider.async_validate_login', Mock(return_value=mock_coro())) as mock_login: @@ -34,8 +32,6 @@ async def test_login_success(hass, hassio_client): async def test_login_error(hass, hassio_client): """Test no auth needed for error.""" - await register_auth_provider(hass, {'type': 'homeassistant'}) - with patch('homeassistant.auth.providers.homeassistant.' 'HassAuthProvider.async_validate_login', Mock(side_effect=HomeAssistantError())) as mock_login: @@ -58,8 +54,6 @@ async def test_login_error(hass, hassio_client): async def test_login_no_data(hass, hassio_client): """Test auth with no data -> error.""" - await register_auth_provider(hass, {'type': 'homeassistant'}) - with patch('homeassistant.auth.providers.homeassistant.' 'HassAuthProvider.async_validate_login', Mock(side_effect=HomeAssistantError())) as mock_login: @@ -77,8 +71,6 @@ async def test_login_no_data(hass, hassio_client): async def test_login_no_username(hass, hassio_client): """Test auth with no username in data -> error.""" - await register_auth_provider(hass, {'type': 'homeassistant'}) - with patch('homeassistant.auth.providers.homeassistant.' 'HassAuthProvider.async_validate_login', Mock(side_effect=HomeAssistantError())) as mock_login: @@ -100,8 +92,6 @@ async def test_login_no_username(hass, hassio_client): async def test_login_success_extra(hass, hassio_client): """Test auth with extra data.""" - await register_auth_provider(hass, {'type': 'homeassistant'}) - with patch('homeassistant.auth.providers.homeassistant.' 'HassAuthProvider.async_validate_login', Mock(return_value=mock_coro())) as mock_login: diff --git a/tests/components/hassio/test_http.py b/tests/components/hassio/test_http.py index 07db126312b..3f58c6e697e 100644 --- a/tests/components/hassio/test_http.py +++ b/tests/components/hassio/test_http.py @@ -40,9 +40,10 @@ 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 +136,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 f5bf0b8a4f8..8b3b057bfc0 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."""