Skip to content

Commit

Permalink
Always set hass_user (#18844)
Browse files Browse the repository at this point in the history
  • Loading branch information
balloob committed Nov 30, 2018
1 parent 8f50180 commit d014517
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 10 deletions.
18 changes: 14 additions & 4 deletions homeassistant/components/http/auth.py
Expand Up @@ -45,6 +45,7 @@ def setup_auth(app, trusted_networks, use_auth,
support_legacy=False, api_password=None):
"""Create auth middleware for the app."""
old_auth_warning = set()
legacy_auth = (not use_auth or support_legacy) and api_password

@middleware
async def auth_middleware(request, handler):
Expand All @@ -60,7 +61,6 @@ async def auth_middleware(request, handler):
request.path, request[KEY_REAL_IP])
old_auth_warning.add(request.path)

legacy_auth = (not use_auth or support_legacy) and api_password
if (hdrs.AUTHORIZATION in request.headers and
await async_validate_auth_header(
request, api_password if legacy_auth else None)):
Expand Down Expand Up @@ -91,6 +91,11 @@ async def auth_middleware(request, handler):
app['hass'])

elif _is_trusted_ip(request, trusted_networks):
users = await app['hass'].auth.async_get_users()
for user in users:
if user.is_owner:
request['hass_user'] = user
break
authenticated = True

elif not use_auth and api_password is None:
Expand Down Expand Up @@ -136,8 +141,9 @@ async def async_validate_auth_header(request, api_password=None):
# If no space in authorization header
return False

hass = request.app['hass']

if auth_type == 'Bearer':
hass = request.app['hass']
refresh_token = await hass.auth.async_validate_access_token(auth_val)
if refresh_token is None:
return False
Expand All @@ -157,8 +163,12 @@ async def async_validate_auth_header(request, api_password=None):
if username != 'homeassistant':
return False

return hmac.compare_digest(api_password.encode('utf-8'),
password.encode('utf-8'))
if not hmac.compare_digest(api_password.encode('utf-8'),
password.encode('utf-8')):
return False

request['hass_user'] = await legacy_api_password.async_get_user(hass)
return True

return False

Expand Down
6 changes: 6 additions & 0 deletions tests/components/conftest.py
Expand Up @@ -88,6 +88,12 @@ def hass_access_token(hass, hass_admin_user):
yield hass.auth.async_create_access_token(refresh_token)


@pytest.fixture
def hass_owner_user(hass, local_auth):
"""Return a Home Assistant admin user."""
return MockUser(is_owner=True).add_to_hass(hass)


@pytest.fixture
def hass_admin_user(hass, local_auth):
"""Return a Home Assistant admin user."""
Expand Down
65 changes: 59 additions & 6 deletions tests/components/http/test_auth.py
Expand Up @@ -7,6 +7,7 @@
from aiohttp import BasicAuth, web
from aiohttp.web_exceptions import HTTPUnauthorized

from homeassistant.auth.providers import legacy_api_password
from homeassistant.components.http.auth import setup_auth, async_sign_path
from homeassistant.components.http.const import KEY_AUTHENTICATED
from homeassistant.components.http.real_ip import setup_real_ip
Expand Down Expand Up @@ -84,29 +85,40 @@ async def test_access_without_password(app, aiohttp_client):


async def test_access_with_password_in_header(app, aiohttp_client,
legacy_auth):
legacy_auth, hass):
"""Test access with password in header."""
setup_auth(app, [], False, api_password=API_PASSWORD)
client = await aiohttp_client(app)
user = await legacy_api_password.async_get_user(hass)

req = await client.get(
'/', headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
assert req.status == 200
assert await req.json() == {
'refresh_token_id': None,
'user_id': user.id,
}

req = await client.get(
'/', headers={HTTP_HEADER_HA_AUTH: 'wrong-pass'})
assert req.status == 401


async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth):
async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth,
hass):
"""Test access with password in URL."""
setup_auth(app, [], False, api_password=API_PASSWORD)
client = await aiohttp_client(app)
user = await legacy_api_password.async_get_user(hass)

resp = await client.get('/', params={
'api_password': API_PASSWORD
})
assert resp.status == 200
assert await resp.json() == {
'refresh_token_id': None,
'user_id': user.id,
}

resp = await client.get('/')
assert resp.status == 401
Expand All @@ -117,15 +129,20 @@ async def test_access_with_password_in_query(app, aiohttp_client, legacy_auth):
assert resp.status == 401


async def test_basic_auth_works(app, aiohttp_client):
async def test_basic_auth_works(app, aiohttp_client, hass, legacy_auth):
"""Test access with basic authentication."""
setup_auth(app, [], False, api_password=API_PASSWORD)
client = await aiohttp_client(app)
user = await legacy_api_password.async_get_user(hass)

req = await client.get(
'/',
auth=BasicAuth('homeassistant', API_PASSWORD))
assert req.status == 200
assert await req.json() == {
'refresh_token_id': None,
'user_id': user.id,
}

req = await client.get(
'/',
Expand All @@ -145,7 +162,7 @@ async def test_basic_auth_works(app, aiohttp_client):
assert req.status == 401


async def test_access_with_trusted_ip(app2, aiohttp_client):
async def test_access_with_trusted_ip(app2, aiohttp_client, hass_owner_user):
"""Test access with an untrusted ip address."""
setup_auth(app2, TRUSTED_NETWORKS, False, api_password='some-pass')

Expand All @@ -163,6 +180,10 @@ async def test_access_with_trusted_ip(app2, aiohttp_client):
resp = await client.get('/')
assert resp.status == 200, \
"{} should be trusted".format(remote_addr)
assert await resp.json() == {
'refresh_token_id': None,
'user_id': hass_owner_user.id,
}


async def test_auth_active_access_with_access_token_in_header(
Expand All @@ -171,18 +192,32 @@ async def test_auth_active_access_with_access_token_in_header(
token = hass_access_token
setup_auth(app, [], True, api_password=None)
client = await aiohttp_client(app)
refresh_token = await hass.auth.async_validate_access_token(
hass_access_token)

req = await client.get(
'/', headers={'Authorization': 'Bearer {}'.format(token)})
assert req.status == 200
assert await req.json() == {
'refresh_token_id': refresh_token.id,
'user_id': refresh_token.user.id,
}

req = await client.get(
'/', headers={'AUTHORIZATION': 'Bearer {}'.format(token)})
assert req.status == 200
assert await req.json() == {
'refresh_token_id': refresh_token.id,
'user_id': refresh_token.user.id,
}

req = await client.get(
'/', headers={'authorization': 'Bearer {}'.format(token)})
assert req.status == 200
assert await req.json() == {
'refresh_token_id': refresh_token.id,
'user_id': refresh_token.user.id,
}

req = await client.get(
'/', headers={'Authorization': token})
Expand All @@ -200,7 +235,8 @@ async def test_auth_active_access_with_access_token_in_header(
assert req.status == 401


async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client):
async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client,
hass_owner_user):
"""Test access with an untrusted ip address."""
setup_auth(app2, TRUSTED_NETWORKS, True, api_password=None)

Expand All @@ -218,6 +254,10 @@ async def test_auth_active_access_with_trusted_ip(app2, aiohttp_client):
resp = await client.get('/')
assert resp.status == 200, \
"{} should be trusted".format(remote_addr)
assert await resp.json() == {
'refresh_token_id': None,
'user_id': hass_owner_user.id,
}


async def test_auth_active_blocked_api_password_access(
Expand All @@ -242,24 +282,37 @@ async def test_auth_active_blocked_api_password_access(


async def test_auth_legacy_support_api_password_access(
app, aiohttp_client, legacy_auth):
app, aiohttp_client, legacy_auth, hass):
"""Test access using api_password if auth.support_legacy."""
setup_auth(app, [], True, support_legacy=True, api_password=API_PASSWORD)
client = await aiohttp_client(app)
user = await legacy_api_password.async_get_user(hass)

req = await client.get(
'/', headers={HTTP_HEADER_HA_AUTH: API_PASSWORD})
assert req.status == 200
assert await req.json() == {
'refresh_token_id': None,
'user_id': user.id,
}

resp = await client.get('/', params={
'api_password': API_PASSWORD
})
assert resp.status == 200
assert await resp.json() == {
'refresh_token_id': None,
'user_id': user.id,
}

req = await client.get(
'/',
auth=BasicAuth('homeassistant', API_PASSWORD))
assert req.status == 200
assert await req.json() == {
'refresh_token_id': None,
'user_id': user.id,
}


async def test_auth_access_signed_path(
Expand Down

0 comments on commit d014517

Please sign in to comment.