Compare commits

...

1 Commits

Author SHA1 Message Date
Petar Petrov 3d36c6f7f8 Allow non-admins to evaluate conditions over the websocket
Drop the admin requirement from the `test_condition` and
`subscribe_condition` websocket commands. Both only evaluate a
condition config and return a read-only result; neither mutates state.

This lets the frontend evaluate dashboard visibility conditions
server-side for everyone, including the non-admin users who view
dashboards. The capability is already available to non-admins through
the `render_template` command, of which a `template` condition is a
subset, so this does not widen the attack surface.
2026-06-29 16:37:08 +03:00
2 changed files with 56 additions and 2 deletions
@@ -1038,7 +1038,6 @@ async def handle_subscribe_trigger(
vol.Optional("variables"): dict,
}
)
@decorators.require_admin
@decorators.async_response
async def handle_test_condition(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
@@ -1101,7 +1100,6 @@ async def handle_test_condition(
vol.Required("condition"): cv.CONDITION_SCHEMA,
}
)
@decorators.require_admin
@decorators.async_response
async def handle_subscribe_condition(
hass: HomeAssistant, connection: ActiveConnection, msg: dict[str, Any]
@@ -2918,6 +2918,32 @@ async def test_test_condition(
assert msg["result"]["result"] is False
async def test_test_condition_non_admin(
hass: HomeAssistant,
websocket_client: MockHAClientWebSocket,
hass_admin_user: MockUser,
) -> None:
"""Test testing a condition does not require admin."""
hass_admin_user.groups = []
hass.states.async_set("hello.world", "paulus")
await websocket_client.send_json_auto_id(
{
"type": "test_condition",
"condition": {
"condition": "state",
"entity_id": "hello.world",
"state": "paulus",
},
}
)
msg = await websocket_client.receive_json()
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
assert msg["result"]["result"] is True
@pytest.mark.parametrize(
("value_template", "expected_template_errors"),
[
@@ -3090,6 +3116,36 @@ async def test_subscribe_condition(
}
async def test_subscribe_condition_non_admin(
hass: HomeAssistant,
websocket_client: MockHAClientWebSocket,
hass_admin_user: MockUser,
) -> None:
"""Test subscribing to a condition does not require admin."""
hass_admin_user.groups = []
hass.states.async_set("hello.world", "paulus")
await websocket_client.send_json_auto_id(
{
"type": "subscribe_condition",
"condition": {
"condition": "state",
"entity_id": "hello.world",
"state": "paulus",
},
}
)
msg = await websocket_client.receive_json()
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
subscription_id = msg["id"]
msg = await websocket_client.receive_json()
assert msg == {"id": subscription_id, "type": "event", "event": {"result": True}}
@pytest.mark.parametrize(
("value_template", "expected_event"),
[