122 lines
3.6 KiB
Python
122 lines
3.6 KiB
Python
"""Tests for the Feature 09 permission gate's audit logging."""
|
|||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
import re
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from homeassistant.exceptions import Unauthorized
|
||
|
|
|
||
|
|
from conftest import wire_entry_to_registry
|
||
|
|
|
||
|
|
from custom_components.nestquest import async_setup_entry
|
||
|
|
from custom_components.nestquest.const import (
|
||
|
|
CONF_ADMIN_USER_IDS,
|
||
|
|
DOMAIN,
|
||
|
|
SERVICE_MANAGE_CHILD,
|
||
|
|
)
|
||
|
|
|
||
|
|
ADMIN_ID = "admin-1"
|
||
|
|
ADMIN_CTX = {"user_id": ADMIN_ID}
|
||
|
|
_TIMESTAMP = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}")
|
||
|
|
|
||
|
|
|
||
|
|
async def _set_up(hass, make_entry):
|
||
|
|
entry = wire_entry_to_registry(
|
||
|
|
make_entry(data={CONF_ADMIN_USER_IDS: [ADMIN_ID]}), hass.registry
|
||
|
|
)
|
||
|
|
assert await async_setup_entry(hass, entry) is True
|
||
|
|
return entry
|
||
|
|
|
||
|
|
|
||
|
|
async def test_denied_call_logs_warning_with_fields(
|
||
|
|
hass, make_entry, caplog
|
||
|
|
) -> None:
|
||
|
|
entry = await _set_up(hass, make_entry)
|
||
|
|
with pytest.raises(Unauthorized):
|
||
|
|
await hass.services.call(
|
||
|
|
DOMAIN,
|
||
|
|
SERVICE_MANAGE_CHILD,
|
||
|
|
{"action": "create", "display_name": "Ada"},
|
||
|
|
context={"user_id": "kiosk-panel"},
|
||
|
|
)
|
||
|
|
denials = [
|
||
|
|
record
|
||
|
|
for record in caplog.records
|
||
|
|
if record.levelno == logging.WARNING
|
||
|
|
and "denied" in record.getMessage()
|
||
|
|
]
|
||
|
|
assert len(denials) == 1
|
||
|
|
message = denials[0].getMessage()
|
||
|
|
assert SERVICE_MANAGE_CHILD in message
|
||
|
|
assert "kiosk-panel" in message
|
||
|
|
assert _TIMESTAMP.search(message), message
|
||
|
|
|
||
|
|
|
||
|
|
async def test_denied_no_context_logs_user_none(
|
||
|
|
hass, make_entry, caplog
|
||
|
|
) -> None:
|
||
|
|
entry = await _set_up(hass, make_entry)
|
||
|
|
with pytest.raises(Unauthorized):
|
||
|
|
await hass.services.call(
|
||
|
|
DOMAIN,
|
||
|
|
SERVICE_MANAGE_CHILD,
|
||
|
|
{"action": "create", "display_name": "Ada"},
|
||
|
|
)
|
||
|
|
denials = [
|
||
|
|
record
|
||
|
|
for record in caplog.records
|
||
|
|
if record.levelno == logging.WARNING
|
||
|
|
and "denied" in record.getMessage()
|
||
|
|
]
|
||
|
|
assert len(denials) == 1
|
||
|
|
message = denials[0].getMessage()
|
||
|
|
assert "none" in message
|
||
|
|
assert _TIMESTAMP.search(message), message
|
||
|
|
|
||
|
|
|
||
|
|
async def test_successful_admin_call_logs_debug_with_fields(
|
||
|
|
hass, make_entry, caplog
|
||
|
|
) -> None:
|
||
|
|
caplog.set_level(logging.DEBUG, logger="custom_components.nestquest")
|
||
|
|
entry = await _set_up(hass, make_entry)
|
||
|
|
await hass.services.call(
|
||
|
|
DOMAIN,
|
||
|
|
SERVICE_MANAGE_CHILD,
|
||
|
|
{"action": "create", "display_name": "Ada"},
|
||
|
|
context=ADMIN_CTX,
|
||
|
|
)
|
||
|
|
allowed = [
|
||
|
|
record
|
||
|
|
for record in caplog.records
|
||
|
|
if record.levelno == logging.DEBUG
|
||
|
|
and "allowed" in record.getMessage()
|
||
|
|
]
|
||
|
|
assert len(allowed) == 1
|
||
|
|
message = allowed[0].getMessage()
|
||
|
|
assert SERVICE_MANAGE_CHILD in message
|
||
|
|
assert ADMIN_ID in message
|
||
|
|
assert _TIMESTAMP.search(message), message
|
||
|
|
|
||
|
|
|
||
|
|
async def test_denied_log_carries_no_personal_data_beyond_user_id(
|
||
|
|
hass, make_entry, caplog
|
||
|
|
) -> None:
|
||
|
|
"""The log line names the caller's user id only: the payload's
|
||
|
|
display name must not leak into the audit trail."""
|
||
|
|
entry = await _set_up(hass, make_entry)
|
||
|
|
with pytest.raises(Unauthorized):
|
||
|
|
await hass.services.call(
|
||
|
|
DOMAIN,
|
||
|
|
SERVICE_MANAGE_CHILD,
|
||
|
|
{"action": "create", "display_name": "Ada"},
|
||
|
|
context={"user_id": "kiosk-panel"},
|
||
|
|
)
|
||
|
|
denials = [
|
||
|
|
record.getMessage()
|
||
|
|
for record in caplog.records
|
||
|
|
if record.levelno == logging.WARNING and "denied" in record.getMessage()
|
||
|
|
]
|
||
|
|
assert denials, "expected a denial log line"
|
||
|
|
assert "Ada" not in denials[0]
|
||
|
|
assert "display_name" not in denials[0]
|