mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
dashboard: fix supervisor auth doing I/O in the event loop (#5807)
This commit is contained in:
parent
2f888ff7c5
commit
ad5f6b5687
1 changed files with 37 additions and 25 deletions
|
@ -13,9 +13,9 @@ import secrets
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any, Callable, TypeVar
|
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
||||||
|
|
||||||
import tornado
|
import tornado
|
||||||
import tornado.concurrent
|
import tornado.concurrent
|
||||||
|
@ -45,12 +45,17 @@ from .util.file import write_file
|
||||||
from .util.subprocess import async_run_system_command
|
from .util.subprocess import async_run_system_command
|
||||||
from .util.text import friendly_name_slugify
|
from .util.text import friendly_name_slugify
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from requests import Response
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
ENV_DEV = "ESPHOME_DASHBOARD_DEV"
|
ENV_DEV = "ESPHOME_DASHBOARD_DEV"
|
||||||
|
|
||||||
|
COOKIE_AUTHENTICATED_YES = b"yes"
|
||||||
|
|
||||||
cookie_authenticated_yes = b"yes"
|
AUTH_COOKIE_NAME = "authenticated"
|
||||||
|
|
||||||
|
|
||||||
settings = DASHBOARD.settings
|
settings = DASHBOARD.settings
|
||||||
|
@ -89,18 +94,18 @@ def authenticated(func: T) -> T:
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
def is_authenticated(request_handler):
|
def is_authenticated(handler: BaseHandler) -> bool:
|
||||||
|
"""Check if the request is authenticated."""
|
||||||
if settings.on_ha_addon:
|
if settings.on_ha_addon:
|
||||||
# Handle ingress - disable auth on ingress port
|
# Handle ingress - disable auth on ingress port
|
||||||
# X-HA-Ingress is automatically stripped on the non-ingress server in nginx
|
# X-HA-Ingress is automatically stripped on the non-ingress server in nginx
|
||||||
header = request_handler.request.headers.get("X-HA-Ingress", "NO")
|
header = handler.request.headers.get("X-HA-Ingress", "NO")
|
||||||
if str(header) == "YES":
|
if str(header) == "YES":
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if settings.using_auth:
|
if settings.using_auth:
|
||||||
return (
|
return handler.get_secure_cookie(AUTH_COOKIE_NAME) == COOKIE_AUTHENTICATED_YES
|
||||||
request_handler.get_secure_cookie("authenticated")
|
|
||||||
== cookie_authenticated_yes
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -860,38 +865,45 @@ class LoginHandler(BaseHandler):
|
||||||
**template_args(),
|
**template_args(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def post_ha_addon_login(self) -> None:
|
def _make_supervisor_auth_request(self) -> Response:
|
||||||
|
"""Make a request to the supervisor auth endpoint."""
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
headers = {
|
headers = {"X-Supervisor-Token": os.getenv("SUPERVISOR_TOKEN")}
|
||||||
"X-Supervisor-Token": os.getenv("SUPERVISOR_TOKEN"),
|
|
||||||
}
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"username": self.get_argument("username", ""),
|
"username": self.get_argument("username", ""),
|
||||||
"password": self.get_argument("password", ""),
|
"password": self.get_argument("password", ""),
|
||||||
}
|
}
|
||||||
|
return requests.post(
|
||||||
|
"http://supervisor/auth", headers=headers, json=data, timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
async def post_ha_addon_login(self) -> None:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
try:
|
try:
|
||||||
req = requests.post(
|
req = await loop.run_in_executor(None, self._make_supervisor_auth_request)
|
||||||
"http://supervisor/auth", headers=headers, json=data, timeout=30
|
|
||||||
)
|
|
||||||
if req.status_code == 200:
|
|
||||||
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
|
|
||||||
self.redirect("/")
|
|
||||||
return
|
|
||||||
except Exception as err: # pylint: disable=broad-except
|
except Exception as err: # pylint: disable=broad-except
|
||||||
_LOGGER.warning("Error during Hass.io auth request: %s", err)
|
_LOGGER.warning("Error during Hass.io auth request: %s", err)
|
||||||
self.set_status(500)
|
self.set_status(500)
|
||||||
self.render_login_page(error="Internal server error")
|
self.render_login_page(error="Internal server error")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if req.status_code == 200:
|
||||||
|
self._set_authenticated()
|
||||||
|
self.redirect("/")
|
||||||
|
return
|
||||||
self.set_status(401)
|
self.set_status(401)
|
||||||
self.render_login_page(error="Invalid username or password")
|
self.render_login_page(error="Invalid username or password")
|
||||||
|
|
||||||
|
def _set_authenticated(self) -> None:
|
||||||
|
"""Set the authenticated cookie."""
|
||||||
|
self.set_secure_cookie(AUTH_COOKIE_NAME, COOKIE_AUTHENTICATED_YES)
|
||||||
|
|
||||||
def post_native_login(self) -> None:
|
def post_native_login(self) -> None:
|
||||||
username = self.get_argument("username", "")
|
username = self.get_argument("username", "")
|
||||||
password = self.get_argument("password", "")
|
password = self.get_argument("password", "")
|
||||||
if settings.check_password(username, password):
|
if settings.check_password(username, password):
|
||||||
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
|
self._set_authenticated()
|
||||||
self.redirect("./")
|
self.redirect("./")
|
||||||
return
|
return
|
||||||
error_str = (
|
error_str = (
|
||||||
|
@ -900,9 +912,9 @@ class LoginHandler(BaseHandler):
|
||||||
self.set_status(401)
|
self.set_status(401)
|
||||||
self.render_login_page(error=error_str)
|
self.render_login_page(error=error_str)
|
||||||
|
|
||||||
def post(self) -> None:
|
async def post(self):
|
||||||
if settings.using_ha_addon_auth:
|
if settings.using_ha_addon_auth:
|
||||||
self.post_ha_addon_login()
|
await self.post_ha_addon_login()
|
||||||
else:
|
else:
|
||||||
self.post_native_login()
|
self.post_native_login()
|
||||||
|
|
||||||
|
@ -910,7 +922,7 @@ class LoginHandler(BaseHandler):
|
||||||
class LogoutHandler(BaseHandler):
|
class LogoutHandler(BaseHandler):
|
||||||
@authenticated
|
@authenticated
|
||||||
def get(self) -> None:
|
def get(self) -> None:
|
||||||
self.clear_cookie("authenticated")
|
self.clear_cookie(AUTH_COOKIE_NAME)
|
||||||
self.redirect("./login")
|
self.redirect("./login")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue