Skip to content
4 changes: 4 additions & 0 deletions homeassistant/components/hassio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from homeassistant.loader import bind_hass
from homeassistant.util.dt import utcnow

from .auth import async_setup_auth
from .handler import HassIO, HassioAPIError
from .discovery import async_setup_discovery
from .http import HassIOView
Expand Down Expand Up @@ -280,4 +281,7 @@ async def async_handle_core_service(call):
# Init discovery Hass.io feature
async_setup_discovery(hass, hassio, config)

# Init auth Hass.io feature
async_setup_auth(hass)

return True
68 changes: 68 additions & 0 deletions homeassistant/components/hassio/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"""Implement the auth feature from Hass.io for Add-ons."""
import logging
import os

from aiohttp.web_exceptions import (
Comment thread
balloob marked this conversation as resolved.
HTTPForbidden, HTTPNotFound, HTTPOk, HTTPUnauthorized)

from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.const import KEY_REAL_IP


_LOGGER = logging.getLogger(__name__)

ATTR_USERNAME = 'username'
ATTR_PASSWORD = 'password'


@callback
def async_setup_auth(hass):
"""Auth setup."""
hassio_ip = os.environ['HASSIO'].split(':')[0]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to pre-parse and store that in the view, can just get it on demand?

hassio_auth = HassIOAuth(hass, hassio_ip)

hass.http.register_view(hassio_auth)


class HassIOAuth(HomeAssistantView):
"""Hass.io view to handle base part."""

name = "api:hassio_auth"
url = "/api/hassio_auth"

def __init__(self, hass, hassio_ip):
"""Initialize WebView."""
self.hass = hass
self.hassio_ip = hassio_ip
Comment thread
pvizeli marked this conversation as resolved.
Outdated

async def post(self, request):
Comment thread
pvizeli marked this conversation as resolved.
Outdated
"""Handle new discovery requests."""
if request[KEY_REAL_IP] != self.hassio_ip:
Comment thread
pvizeli marked this conversation as resolved.
Outdated
_LOGGER.error(
"Invalid auth request from %s", request[KEY_REAL_IP])
raise HTTPForbidden()

data = await request.json()
await self._check_login(data[ATTR_USERNAME], data[ATTR_PASSWORD])

def _get_provider(self):
"""Return Homeassistant auth provider."""
for prv in self.hass.auth.auth_provider:
Comment thread
pvizeli marked this conversation as resolved.
Outdated
if prv.type == 'homeassistant':
return prv

_LOGGER.error("Can't find Home Assistant auth.")
raise HTTPNotFound()

async def _check_login(self, username, password):
"""Check User credentials."""
provider = self._get_provider()

Comment thread
pvizeli marked this conversation as resolved.
try:
provider.async_validate_login(username, password)
except HomeAssistantError:
raise HTTPUnauthorized() from None

raise HTTPOk()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is weird. Just return web.Response(status=200) from the view.