2018-10-10 12:07:51 +00:00
|
|
|
"""Implement the auth feature from Hass.io for Add-ons."""
|
2019-12-08 15:33:22 +00:00
|
|
|
from ipaddress import ip_address
|
2019-02-14 15:01:46 +00:00
|
|
|
import logging
|
2018-10-10 12:07:51 +00:00
|
|
|
import os
|
|
|
|
|
|
|
|
from aiohttp import web
|
2020-08-25 12:22:50 +00:00
|
|
|
from aiohttp.web_exceptions import HTTPNotFound, HTTPUnauthorized
|
2019-12-08 15:33:22 +00:00
|
|
|
import voluptuous as vol
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
from homeassistant.auth.models import User
|
2020-08-25 12:22:50 +00:00
|
|
|
from homeassistant.auth.providers import homeassistant as auth_ha
|
2018-10-10 12:07:51 +00:00
|
|
|
from homeassistant.components.http import HomeAssistantView
|
2020-08-11 20:57:50 +00:00
|
|
|
from homeassistant.components.http.const import KEY_HASS_USER
|
2018-10-10 12:07:51 +00:00
|
|
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
2020-04-08 16:47:38 +00:00
|
|
|
from homeassistant.const import HTTP_OK
|
2021-04-23 08:34:02 +00:00
|
|
|
from homeassistant.core import HomeAssistant, callback
|
2019-12-08 15:33:22 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2019-02-14 15:01:46 +00:00
|
|
|
from .const import ATTR_ADDON, ATTR_PASSWORD, ATTR_USERNAME
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2018-10-11 08:55:38 +00:00
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2018-10-10 12:07:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
2021-04-23 08:34:02 +00:00
|
|
|
def async_setup_auth_view(hass: HomeAssistant, user: User):
|
2018-10-10 12:07:51 +00:00
|
|
|
"""Auth setup."""
|
2020-01-14 22:49:56 +00:00
|
|
|
hassio_auth = HassIOAuth(hass, user)
|
|
|
|
hassio_password_reset = HassIOPasswordReset(hass, user)
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
hass.http.register_view(hassio_auth)
|
|
|
|
hass.http.register_view(hassio_password_reset)
|
2018-10-10 12:07:51 +00:00
|
|
|
|
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
class HassIOBaseAuth(HomeAssistantView):
|
|
|
|
"""Hass.io view to handle auth requests."""
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2021-04-23 08:34:02 +00:00
|
|
|
def __init__(self, hass: HomeAssistant, user: User):
|
2018-10-10 12:07:51 +00:00
|
|
|
"""Initialize WebView."""
|
|
|
|
self.hass = hass
|
2020-01-14 22:49:56 +00:00
|
|
|
self.user = user
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
def _check_access(self, request: web.Request):
|
|
|
|
"""Check if this call is from Supervisor."""
|
|
|
|
# Check caller IP
|
2019-07-31 19:25:30 +00:00
|
|
|
hassio_ip = os.environ["HASSIO"].split(":")[0]
|
2020-08-11 20:57:50 +00:00
|
|
|
if ip_address(request.transport.get_extra_info("peername")[0]) != ip_address(
|
|
|
|
hassio_ip
|
|
|
|
):
|
|
|
|
_LOGGER.error("Invalid auth request from %s", request.remote)
|
2020-01-14 22:49:56 +00:00
|
|
|
raise HTTPUnauthorized()
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
# Check caller token
|
|
|
|
if request[KEY_HASS_USER].id != self.user.id:
|
|
|
|
_LOGGER.error("Invalid auth request from %s", request[KEY_HASS_USER].name)
|
|
|
|
raise HTTPUnauthorized()
|
2018-10-10 12:07:51 +00:00
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
|
|
|
|
class HassIOAuth(HassIOBaseAuth):
|
|
|
|
"""Hass.io view to handle auth requests."""
|
|
|
|
|
|
|
|
name = "api:hassio:auth"
|
|
|
|
url = "/api/hassio_auth"
|
|
|
|
|
2020-08-25 12:22:50 +00:00
|
|
|
@RequestDataValidator(
|
|
|
|
vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(ATTR_USERNAME): cv.string,
|
|
|
|
vol.Required(ATTR_PASSWORD): cv.string,
|
|
|
|
vol.Required(ATTR_ADDON): cv.string,
|
|
|
|
},
|
|
|
|
extra=vol.ALLOW_EXTRA,
|
|
|
|
)
|
|
|
|
)
|
2020-01-14 22:49:56 +00:00
|
|
|
async def post(self, request, data):
|
|
|
|
"""Handle auth requests."""
|
|
|
|
self._check_access(request)
|
2020-08-25 12:22:50 +00:00
|
|
|
provider = auth_ha.async_get_provider(request.app["hass"])
|
2018-10-10 12:07:51 +00:00
|
|
|
|
|
|
|
try:
|
2020-08-25 12:22:50 +00:00
|
|
|
await provider.async_validate_login(
|
|
|
|
data[ATTR_USERNAME], data[ATTR_PASSWORD]
|
|
|
|
)
|
|
|
|
except auth_ha.InvalidAuth:
|
2021-01-08 16:28:22 +00:00
|
|
|
raise HTTPNotFound() from None
|
2020-01-14 22:49:56 +00:00
|
|
|
|
2020-08-25 12:22:50 +00:00
|
|
|
return web.Response(status=HTTP_OK)
|
|
|
|
|
2020-01-14 22:49:56 +00:00
|
|
|
|
|
|
|
class HassIOPasswordReset(HassIOBaseAuth):
|
|
|
|
"""Hass.io view to handle password reset requests."""
|
|
|
|
|
|
|
|
name = "api:hassio:auth:password:reset"
|
|
|
|
url = "/api/hassio_auth/password_reset"
|
|
|
|
|
2020-08-25 12:22:50 +00:00
|
|
|
@RequestDataValidator(
|
|
|
|
vol.Schema(
|
|
|
|
{
|
|
|
|
vol.Required(ATTR_USERNAME): cv.string,
|
|
|
|
vol.Required(ATTR_PASSWORD): cv.string,
|
|
|
|
},
|
|
|
|
extra=vol.ALLOW_EXTRA,
|
|
|
|
)
|
|
|
|
)
|
2020-01-14 22:49:56 +00:00
|
|
|
async def post(self, request, data):
|
|
|
|
"""Handle password reset requests."""
|
|
|
|
self._check_access(request)
|
2020-08-25 12:22:50 +00:00
|
|
|
provider = auth_ha.async_get_provider(request.app["hass"])
|
2020-01-14 22:49:56 +00:00
|
|
|
|
|
|
|
try:
|
2020-08-25 12:22:50 +00:00
|
|
|
await provider.async_change_password(
|
|
|
|
data[ATTR_USERNAME], data[ATTR_PASSWORD]
|
2020-01-14 22:49:56 +00:00
|
|
|
)
|
2020-08-28 11:50:32 +00:00
|
|
|
except auth_ha.InvalidUser as err:
|
|
|
|
raise HTTPNotFound() from err
|
2020-08-25 12:22:50 +00:00
|
|
|
|
|
|
|
return web.Response(status=HTTP_OK)
|