Reuse an existing Supervisor system user instead of creating a duplicate (#182304)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Stefan Agner
2026-09-19 11:52:42 +02:00
committed by GitHub
co-authored by Claude Fable 5.1
parent 7af5bcc88a
commit 0527dcd495
2 changed files with 74 additions and 5 deletions
+18 -5
View File
@@ -186,15 +186,28 @@ async def _async_get_or_create_supervisor_user(
if user is None and legacy_user_id is not None:
user = await hass.auth.async_get_user(legacy_user_id)
if user is None:
# The storage naming the Supervisor user may have been lost. Reuse an
# existing Supervisor system user instead of creating a duplicate.
user = next(
(
existing
for existing in await hass.auth.async_get_users()
if existing.system_generated and existing.name == HASSIO_USER_NAME
),
None,
)
if user is None:
user = await hass.auth.async_create_system_user(
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
)
if entry is not None:
hass.config_entries.async_update_entry(
entry,
data={**entry.data, ENTRY_DATA_USER: user.id},
)
if entry is not None and entry.data.get(ENTRY_DATA_USER) != user.id:
hass.config_entries.async_update_entry(
entry,
data={**entry.data, ENTRY_DATA_USER: user.id},
)
# Migrate old Hass.io users to be admin.
if not user.is_admin:
+56
View File
@@ -33,6 +33,7 @@ from probatio import Invalid
import pytest
from homeassistant.auth.const import GROUP_ID_ADMIN
from homeassistant.auth.models import User
from homeassistant.components import frontend, hassio
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
from homeassistant.components.hassio import (
@@ -69,6 +70,7 @@ from homeassistant.components.homeassistant import (
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import HASSIO_USER_NAME
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import device_registry as dr, issue_registry as ir
@@ -444,6 +446,60 @@ async def test_setup_api_existing_hassio_user(
assert hass.auth.async_validate_access_token(access_token) is None
async def _async_assert_supervisor_user_adopted(
hass: HomeAssistant, user: User, human: User
) -> None:
"""Assert setup adopted the existing Supervisor system user."""
entry = hass.config_entries.async_entries(DOMAIN)[0]
assert entry.data[ENTRY_DATA_USER] == user.id
assert entry.data[ENTRY_DATA_USER] != human.id
assert [
existing
for existing in await hass.auth.async_get_users()
if existing.system_generated and existing.name == HASSIO_USER_NAME
] == [user]
async def test_setup_adopts_existing_supervisor_user(hass: HomeAssistant) -> None:
"""Test setup reuses an existing Supervisor system user.
When the config entry data (and legacy store) naming the Supervisor user
is lost, an existing Supervisor system user must be adopted instead of
creating a duplicate. A regular user named alike must not be picked up.
"""
human = await hass.auth.async_create_user(HASSIO_USER_NAME)
user = await hass.auth.async_create_system_user(
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
)
MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN).add_to_hass(hass)
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(hass, DOMAIN, {"hassio": {}})
await hass.async_block_till_done()
await _async_assert_supervisor_user_adopted(hass, user, human)
async def test_setup_adopts_existing_supervisor_user_without_entry(
hass: HomeAssistant,
) -> None:
"""Test setup reuses an existing Supervisor system user without config entry.
Same as above, but with the config entry itself gone: the entry created by
the system flow must name the adopted user.
"""
human = await hass.auth.async_create_user(HASSIO_USER_NAME)
user = await hass.auth.async_create_system_user(
HASSIO_USER_NAME, group_ids=[GROUP_ID_ADMIN]
)
with patch.dict(os.environ, MOCK_ENVIRON):
assert await async_setup_component(hass, DOMAIN, {"hassio": {}})
await hass.async_block_till_done()
await _async_assert_supervisor_user_adopted(hass, user, human)
async def test_setup_migrates_legacy_hassio_store_to_config_entry(
hass: HomeAssistant,
hass_storage: dict[str, Any],