mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 01:11:51 -04:00
Use the hassio-provided Supervisor user for Unix socket auth (#182302)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
Copilot Autofix powered by AI
parent
1ca85dc3bc
commit
cf3580a8e4
@@ -18,6 +18,7 @@ from homeassistant.auth.const import GROUP_ID_ADMIN
|
||||
from homeassistant.auth.models import User
|
||||
from homeassistant.components import frontend
|
||||
from homeassistant.components.homeassistant import async_set_stop_handler
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.components.onboarding import async_is_onboarded
|
||||
from homeassistant.config_entries import SOURCE_SYSTEM, ConfigEntry
|
||||
from homeassistant.const import EVENT_CORE_CONFIG_UPDATE, HASSIO_USER_NAME, Platform
|
||||
@@ -57,7 +58,6 @@ from .const import (
|
||||
ADDONS_COORDINATOR,
|
||||
DATA_COMPONENT,
|
||||
DATA_HASSIO_HOST,
|
||||
DATA_HASSIO_SUPERVISOR_USER,
|
||||
DATA_KEY_SUPERVISOR_ISSUES,
|
||||
DOMAIN,
|
||||
ENTRY_DATA_USER,
|
||||
@@ -340,7 +340,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
if legacy_data is not None:
|
||||
legacy_user_id = legacy_data.get("hassio_user")
|
||||
|
||||
hass.data[DATA_HASSIO_SUPERVISOR_USER] = await _async_get_or_create_supervisor_user(
|
||||
hass.data[DATA_SUPERVISOR_USER] = await _async_get_or_create_supervisor_user(
|
||||
hass, entry, legacy_user_id
|
||||
)
|
||||
|
||||
@@ -386,7 +386,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
remove_legacy_store = True
|
||||
|
||||
# Async setup runs first unconditionally and always populates this field
|
||||
user = hass.data[DATA_HASSIO_SUPERVISOR_USER]
|
||||
user = hass.data[DATA_SUPERVISOR_USER]
|
||||
if entry.data.get(ENTRY_DATA_USER) != user.id:
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
|
||||
@@ -15,12 +15,15 @@ import probatio
|
||||
|
||||
from homeassistant.auth.providers import homeassistant as auth_ha
|
||||
from homeassistant.components.http import KEY_HASS, KEY_HASS_USER, HomeAssistantView
|
||||
from homeassistant.components.http.const import is_supervisor_unix_socket_request
|
||||
from homeassistant.components.http.const import (
|
||||
DATA_SUPERVISOR_USER,
|
||||
is_supervisor_unix_socket_request,
|
||||
)
|
||||
from homeassistant.components.http.data_validator import RequestDataValidator
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
|
||||
from .const import ATTR_ADDON, ATTR_PASSWORD, ATTR_USERNAME, DATA_HASSIO_SUPERVISOR_USER
|
||||
from .const import ATTR_ADDON, ATTR_PASSWORD, ATTR_USERNAME
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -41,7 +44,7 @@ class HassIOBaseAuth(HomeAssistantView):
|
||||
|
||||
def _check_access(self, request: web.Request) -> None:
|
||||
"""Check if this call is from Supervisor."""
|
||||
user = self.hass.data.get(DATA_HASSIO_SUPERVISOR_USER)
|
||||
user = self.hass.data.get(DATA_SUPERVISOR_USER)
|
||||
if user is None:
|
||||
raise HTTPServiceUnavailable
|
||||
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import HASSIO_USER_NAME
|
||||
|
||||
from .const import (
|
||||
DATA_HASSIO_SUPERVISOR_USER,
|
||||
DEFAULT_UPDATE_OPTIONS,
|
||||
DOMAIN,
|
||||
ENTRY_DATA_USER,
|
||||
)
|
||||
from .const import DEFAULT_UPDATE_OPTIONS, DOMAIN, ENTRY_DATA_USER
|
||||
|
||||
|
||||
class HassIoConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@@ -23,7 +19,7 @@ class HassIoConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
data: dict[str, Any] = {}
|
||||
if (user := self.hass.data.get(DATA_HASSIO_SUPERVISOR_USER)) is not None:
|
||||
if (user := self.hass.data.get(DATA_SUPERVISOR_USER)) is not None:
|
||||
data[ENTRY_DATA_USER] = user.id
|
||||
|
||||
return self.async_create_entry(
|
||||
|
||||
@@ -20,8 +20,6 @@ if TYPE_CHECKING:
|
||||
SupervisorInfo,
|
||||
)
|
||||
|
||||
from homeassistant.auth.models import User
|
||||
|
||||
from .coordinator import (
|
||||
HassioAddOnDataUpdateCoordinator,
|
||||
HassioMainDataUpdateCoordinator,
|
||||
@@ -153,7 +151,6 @@ DATA_KEY_SUPERVISOR_ISSUES: HassKey[SupervisorIssuesCoordinator] = HassKey(
|
||||
)
|
||||
DATA_KEY_MOUNTS = "mounts"
|
||||
DATA_HASSIO_HOST: HassKey[str] = HassKey("hassio_host")
|
||||
DATA_HASSIO_SUPERVISOR_USER: HassKey[User] = HassKey("hassio_supervisor_user")
|
||||
|
||||
ENTRY_DATA_USER = "user"
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ from typing import Any, Final
|
||||
import probatio
|
||||
|
||||
from homeassistant.components.network import async_get_source_ip
|
||||
from homeassistant.const import (
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
EVENT_HOMEASSISTANT_STOP,
|
||||
HASSIO_USER_NAME,
|
||||
)
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv, issue_registry as ir
|
||||
@@ -48,6 +44,7 @@ from .const import ( # noqa: F401
|
||||
CONF_TRUSTED_PROXIES,
|
||||
CONF_USE_X_FORWARDED_FOR,
|
||||
CONF_USE_X_FRAME_OPTIONS,
|
||||
DATA_SUPERVISOR_USER,
|
||||
DEFAULT_CORS,
|
||||
DOMAIN,
|
||||
KEY_HASS_REFRESH_TOKEN_ID,
|
||||
@@ -215,11 +212,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
|
||||
async def start_supervisor_unix_socket(*_: Any) -> None:
|
||||
"""Start the Unix socket after the Supervisor user is available."""
|
||||
if any(
|
||||
user
|
||||
for user in await hass.auth.async_get_users()
|
||||
if user.system_generated and user.name == HASSIO_USER_NAME
|
||||
):
|
||||
if hass.data.get(DATA_SUPERVISOR_USER) is not None:
|
||||
await server.async_start_supervisor_unix_socket()
|
||||
else:
|
||||
_LOGGER.error("Supervisor user not found; not starting Unix socket")
|
||||
|
||||
@@ -22,7 +22,6 @@ from yarl import URL
|
||||
from homeassistant.auth import jwt_wrapper
|
||||
from homeassistant.auth.const import GROUP_ID_READ_ONLY
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.const import HASSIO_USER_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.http import current_request
|
||||
from homeassistant.helpers.json import json_bytes
|
||||
@@ -30,6 +29,7 @@ from homeassistant.helpers.storage import Store
|
||||
|
||||
from .auth_util import async_user_not_allowed_do_auth
|
||||
from .const import (
|
||||
DATA_SUPERVISOR_USER,
|
||||
KEY_AUTHENTICATED,
|
||||
KEY_HASS_REFRESH_TOKEN_ID,
|
||||
KEY_HASS_USER,
|
||||
@@ -94,7 +94,7 @@ def async_sign_path(
|
||||
return f"{url.path}?{url.query_string}"
|
||||
|
||||
|
||||
async def async_setup_auth( # noqa: C901
|
||||
async def async_setup_auth(
|
||||
hass: HomeAssistant,
|
||||
app: Application,
|
||||
) -> None:
|
||||
@@ -187,40 +187,30 @@ async def async_setup_auth( # noqa: C901
|
||||
request[KEY_HASS_REFRESH_TOKEN_ID] = refresh_token.id
|
||||
return True
|
||||
|
||||
supervisor_user_id: str | None = None
|
||||
|
||||
async def async_authenticate_supervisor_unix_socket(request: Request) -> bool:
|
||||
"""Authenticate a request from a Unix socket as the Supervisor user.
|
||||
|
||||
The Unix Socket is dedicated and only available to Supervisor. To
|
||||
avoid the extra overhead and round trips for the authentication and
|
||||
refresh tokens, we directly authenticate requests from the socket as
|
||||
the Supervisor user.
|
||||
the Supervisor user provided by the hassio integration. The user is
|
||||
looked up in the auth store so a user removed at runtime is not
|
||||
authenticated any longer.
|
||||
"""
|
||||
nonlocal supervisor_user_id
|
||||
if (supervisor_user := hass.data.get(DATA_SUPERVISOR_USER)) is None or (
|
||||
user := await hass.auth.async_get_user(supervisor_user.id)
|
||||
) is None:
|
||||
# The Unix socket should not be serving before the hassio integration
|
||||
# has provided the Supervisor user. If we get here, something is wrong.
|
||||
_LOGGER.error(
|
||||
"Supervisor user not found; cannot authenticate Unix socket request"
|
||||
)
|
||||
raise HTTPInternalServerError
|
||||
|
||||
# Fast path: use cached user ID
|
||||
if supervisor_user_id is not None:
|
||||
if user := await hass.auth.async_get_user(supervisor_user_id):
|
||||
request[KEY_HASS_USER] = user
|
||||
return True
|
||||
supervisor_user_id = None
|
||||
|
||||
# Slow path: find the Supervisor user by name
|
||||
for user in await hass.auth.async_get_users():
|
||||
if user.system_generated and user.name == HASSIO_USER_NAME:
|
||||
supervisor_user_id = user.id
|
||||
# Not setting KEY_HASS_REFRESH_TOKEN_ID since Supervisor user
|
||||
# doesn't use refresh tokens.
|
||||
request[KEY_HASS_USER] = user
|
||||
return True
|
||||
|
||||
# The Unix socket should not be serving before the hassio integration
|
||||
# has created the Supervisor user. If we get here, something is wrong.
|
||||
_LOGGER.error(
|
||||
"Supervisor user not found; cannot authenticate Unix socket request"
|
||||
)
|
||||
raise HTTPInternalServerError
|
||||
# Not setting KEY_HASS_REFRESH_TOKEN_ID since Supervisor user
|
||||
# doesn't use refresh tokens.
|
||||
request[KEY_HASS_USER] = user
|
||||
return True
|
||||
|
||||
@middleware
|
||||
async def auth_middleware(
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
"""HTTP specific constants."""
|
||||
|
||||
from typing import Final
|
||||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from aiohttp.web import Request
|
||||
|
||||
from homeassistant.helpers.http import KEY_AUTHENTICATED, KEY_HASS # noqa: F401
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.auth.models import User
|
||||
|
||||
DOMAIN: Final = "http"
|
||||
|
||||
@@ -12,6 +16,11 @@ KEY_HASS_USER: Final = "hass_user"
|
||||
KEY_HASS_REFRESH_TOKEN_ID: Final = "hass_refresh_token_id"
|
||||
KEY_SUPERVISOR_UNIX_SOCKET: Final = "ha_supervisor_unix_socket"
|
||||
|
||||
# System user used to authenticate requests over the Supervisor Unix socket.
|
||||
# Set by the hassio integration during its setup; the Unix socket is only
|
||||
# started once it is available.
|
||||
DATA_SUPERVISOR_USER: HassKey[User] = HassKey("hassio_supervisor_user")
|
||||
|
||||
CONF_SERVER_HOST: Final = "server_host"
|
||||
CONF_SERVER_PORT: Final = "server_port"
|
||||
CONF_BASE_URL: Final = "base_url"
|
||||
|
||||
@@ -10,7 +10,7 @@ import pytest
|
||||
|
||||
from homeassistant.auth.providers.homeassistant import InvalidAuth
|
||||
from homeassistant.components.hassio.auth import HassIOBaseAuth
|
||||
from homeassistant.components.hassio.const import DATA_HASSIO_SUPERVISOR_USER
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ async def test_check_access_unix_socket_or_missing_peername(
|
||||
expectation: AbstractContextManager,
|
||||
) -> None:
|
||||
"""Test _check_access handles Unix socket requests and missing peername."""
|
||||
user = hass.data.get(DATA_HASSIO_SUPERVISOR_USER)
|
||||
user = hass.data.get(DATA_SUPERVISOR_USER)
|
||||
assert user is not None
|
||||
|
||||
auth_view = HassIOBaseAuth(hass)
|
||||
|
||||
@@ -5,10 +5,10 @@ from unittest.mock import patch
|
||||
from homeassistant.auth.const import GROUP_ID_ADMIN
|
||||
from homeassistant.components.hassio import DOMAIN
|
||||
from homeassistant.components.hassio.const import (
|
||||
DATA_HASSIO_SUPERVISOR_USER,
|
||||
DEFAULT_UPDATE_OPTIONS,
|
||||
ENTRY_DATA_USER,
|
||||
)
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
@@ -53,7 +53,7 @@ async def test_config_flow_uses_bootstrap_user(hass: HomeAssistant) -> None:
|
||||
user = await hass.auth.async_create_system_user(
|
||||
"Supervisor", group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
hass.data[DATA_HASSIO_SUPERVISOR_USER] = user
|
||||
hass.data[DATA_SUPERVISOR_USER] = user
|
||||
|
||||
with (
|
||||
patch("homeassistant.components.hassio.async_setup", return_value=True),
|
||||
|
||||
@@ -54,7 +54,6 @@ from homeassistant.components.hassio import (
|
||||
hostname_from_addon_slug,
|
||||
)
|
||||
from homeassistant.components.hassio.const import (
|
||||
DATA_HASSIO_SUPERVISOR_USER,
|
||||
DATA_KEY_SUPERVISOR_ISSUES,
|
||||
DEFAULT_UPDATE_OPTIONS,
|
||||
ENTRY_DATA_USER,
|
||||
@@ -68,6 +67,7 @@ from homeassistant.components.homeassistant import (
|
||||
DOMAIN as HOMEASSISTANT_DOMAIN,
|
||||
SERVICE_UPDATE_ENTITY,
|
||||
)
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import HASSIO_USER_NAME
|
||||
@@ -368,7 +368,7 @@ async def test_setup_api_push_api_data_default(
|
||||
supervisor_client.homeassistant.set_options.assert_called_once_with(
|
||||
HomeAssistantOptions(ssl=False, port=80, refresh_token=None)
|
||||
)
|
||||
hassio_user = hass.data[DATA_HASSIO_SUPERVISOR_USER]
|
||||
hassio_user = hass.data[DATA_SUPERVISOR_USER]
|
||||
assert hassio_user.system_generated
|
||||
assert len(hassio_user.groups) == 1
|
||||
assert hassio_user.groups[0].id == GROUP_ID_ADMIN
|
||||
|
||||
@@ -28,6 +28,7 @@ from homeassistant.components.http.auth import (
|
||||
async_sign_path,
|
||||
async_user_not_allowed_do_auth,
|
||||
)
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.components.http.forwarded import async_setup_forwarded
|
||||
from homeassistant.components.http.request_context import (
|
||||
current_request,
|
||||
@@ -766,7 +767,7 @@ async def test_unix_socket_auth_with_supervisor_user(
|
||||
supervisor_user = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
await hass.auth.async_create_refresh_token(supervisor_user)
|
||||
hass.data[DATA_SUPERVISOR_USER] = supervisor_user
|
||||
|
||||
await async_setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
@@ -798,16 +799,17 @@ async def test_unix_socket_auth_without_supervisor_user(
|
||||
assert req.status == HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
|
||||
async def test_unix_socket_auth_caches_user_id(
|
||||
async def test_unix_socket_auth_removed_user(
|
||||
hass: HomeAssistant,
|
||||
app: web.Application,
|
||||
aiohttp_client: ClientSessionGenerator,
|
||||
) -> None:
|
||||
"""Test that Unix socket auth caches the Supervisor user ID."""
|
||||
"""Test that Unix socket requests fail once the Supervisor user was removed."""
|
||||
supervisor_user = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
await hass.auth.async_create_refresh_token(supervisor_user)
|
||||
hass.data[DATA_SUPERVISOR_USER] = supervisor_user
|
||||
await hass.auth.async_remove_user(supervisor_user)
|
||||
|
||||
await async_setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
@@ -816,20 +818,37 @@ async def test_unix_socket_auth_caches_user_id(
|
||||
"homeassistant.components.http.auth.is_supervisor_unix_socket_request",
|
||||
return_value=True,
|
||||
):
|
||||
# First request triggers user lookup
|
||||
req = await client.get("/")
|
||||
assert req.status == HTTPStatus.OK
|
||||
assert req.status == HTTPStatus.INTERNAL_SERVER_ERROR
|
||||
|
||||
# Second request should use cached user ID
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.http.auth.is_supervisor_unix_socket_request",
|
||||
return_value=True,
|
||||
),
|
||||
patch.object(
|
||||
hass.auth, "async_get_users", wraps=hass.auth.async_get_users
|
||||
) as mock_get_users,
|
||||
|
||||
async def test_unix_socket_auth_uses_provided_user(
|
||||
hass: HomeAssistant,
|
||||
app: web.Application,
|
||||
aiohttp_client: ClientSessionGenerator,
|
||||
) -> None:
|
||||
"""Test that Unix socket auth uses the user provided by hassio, not a name match.
|
||||
|
||||
A stale duplicate system user named Supervisor can be left behind in the
|
||||
auth store; requests must be authenticated as the user hassio actually uses.
|
||||
"""
|
||||
stale_user = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
supervisor_user = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
|
||||
)
|
||||
hass.data[DATA_SUPERVISOR_USER] = supervisor_user
|
||||
|
||||
await async_setup_auth(hass, app)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.http.auth.is_supervisor_unix_socket_request",
|
||||
return_value=True,
|
||||
):
|
||||
req = await client.get("/")
|
||||
assert req.status == HTTPStatus.OK
|
||||
mock_get_users.assert_not_called()
|
||||
assert req.status == HTTPStatus.OK
|
||||
data = await req.json()
|
||||
assert data["user_id"] == supervisor_user.id
|
||||
assert data["user_id"] != stale_user.id
|
||||
|
||||
@@ -31,7 +31,11 @@ from homeassistant.components.http.config import (
|
||||
async_get_and_load_store,
|
||||
default_server_port,
|
||||
)
|
||||
from homeassistant.components.http.const import ENV_SETUP_PORT, ENV_SUPERVISOR
|
||||
from homeassistant.components.http.const import (
|
||||
DATA_SUPERVISOR_USER,
|
||||
ENV_SETUP_PORT,
|
||||
ENV_SUPERVISOR,
|
||||
)
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, HASSIO_USER_NAME, SERVER_PORT
|
||||
from homeassistant.core import CoreState, HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
@@ -958,7 +962,7 @@ async def test_unix_socket_started_with_supervisor(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test unix socket is started when running under Supervisor."""
|
||||
await hass.auth.async_create_system_user(
|
||||
hass.data[DATA_SUPERVISOR_USER] = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=["system-admin"]
|
||||
)
|
||||
socket_path = tmp_path / "core.sock"
|
||||
@@ -1027,7 +1031,7 @@ async def test_supervisor_http_config_view(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test the HTTP config view is registered and served over the socket only."""
|
||||
await hass.auth.async_create_system_user(
|
||||
hass.data[DATA_SUPERVISOR_USER] = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=["system-admin"]
|
||||
)
|
||||
socket_path = tmp_path / "core.sock"
|
||||
|
||||
@@ -7,6 +7,7 @@ from aiohttp import WSMsgType, web
|
||||
import pytest
|
||||
|
||||
from homeassistant.auth.providers.homeassistant import HassAuthProvider
|
||||
from homeassistant.components.http.const import DATA_SUPERVISOR_USER
|
||||
from homeassistant.components.websocket_api import DOMAIN
|
||||
from homeassistant.components.websocket_api.auth import (
|
||||
TYPE_AUTH,
|
||||
@@ -436,8 +437,8 @@ async def test_unix_socket_auth_bypass(
|
||||
hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator
|
||||
) -> None:
|
||||
"""Test that Unix socket connections skip websocket auth phase."""
|
||||
# Create the Supervisor system user
|
||||
await hass.auth.async_create_system_user(
|
||||
# Create the Supervisor system user, as the hassio integration would
|
||||
hass.data[DATA_SUPERVISOR_USER] = await hass.auth.async_create_system_user(
|
||||
HASSIO_USER_NAME, group_ids=["system-admin"]
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user