From d119bbe4efc39eefca6f3c26ae125fd1e99e188a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 27 Apr 2026 10:56:47 -0400 Subject: [PATCH] Require admin for configurator.configure service (#169230) Co-authored-by: Claude Opus 4.7 (1M context) --- .../components/configurator/__init__.py | 11 +++++++-- tests/components/configurator/test_init.py | 24 +++++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/configurator/__init__.py b/homeassistant/components/configurator/__init__.py index ce383eec1c18..063fce654faf 100644 --- a/homeassistant/components/configurator/__init__.py +++ b/homeassistant/components/configurator/__init__.py @@ -14,6 +14,8 @@ from datetime import datetime import functools as ft from typing import Any +import voluptuous as vol + from homeassistant.const import ATTR_ENTITY_PICTURE, ATTR_FRIENDLY_NAME from homeassistant.core import ( HassJob, @@ -24,6 +26,7 @@ from homeassistant.core import ( from homeassistant.helpers import config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id from homeassistant.helpers.event import async_call_later +from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType from homeassistant.util.async_ import run_callback_threadsafe @@ -149,8 +152,12 @@ class Configurator: self._requests: dict[ str, tuple[str, list[dict[str, str]], ConfiguratorCallback | None] ] = {} - hass.services.async_register( - DOMAIN, SERVICE_CONFIGURE, self.async_handle_service_call + async_register_admin_service( + hass, + DOMAIN, + SERVICE_CONFIGURE, + self.async_handle_service_call, + schema=vol.Schema({}, extra=vol.ALLOW_EXTRA), ) @async_callback diff --git a/tests/components/configurator/test_init.py b/tests/components/configurator/test_init.py index da0c80586754..5fabcbd9954b 100644 --- a/tests/components/configurator/test_init.py +++ b/tests/components/configurator/test_init.py @@ -6,10 +6,11 @@ import pytest from homeassistant.components import configurator from homeassistant.const import ATTR_FRIENDLY_NAME -from homeassistant.core import HomeAssistant +from homeassistant.core import Context, HomeAssistant +from homeassistant.exceptions import Unauthorized from homeassistant.util import dt as dt_util -from tests.common import async_fire_time_changed +from tests.common import MockUser, async_fire_time_changed @pytest.mark.parametrize( @@ -130,3 +131,22 @@ async def test_request_done_fail_silently_on_bad_request_id( ) -> None: """Test that request_done fails silently with a bad request id.""" configurator.async_request_done(hass, 2016) + + +@pytest.mark.parametrize( + "ignore_missing_translations", ["component.configurator.services.configure."] +) +async def test_configure_service_requires_admin( + hass: HomeAssistant, hass_read_only_user: MockUser +) -> None: + """Test the configure service requires admin.""" + request_id = configurator.async_request_config(hass, "Test Request", lambda _: None) + + with pytest.raises(Unauthorized): + await hass.services.async_call( + configurator.DOMAIN, + configurator.SERVICE_CONFIGURE, + {configurator.ATTR_CONFIGURE_ID: request_id}, + context=Context(user_id=hass_read_only_user.id), + blocking=True, + )