Introduce new redact helper (#64579)

pull/64587/head
Paulus Schoutsen 2022-01-20 14:02:47 -08:00 committed by GitHub
parent 53aed22d5c
commit 520ba0a82e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 129 additions and 145 deletions

View File

@ -1,40 +1,27 @@
"""Diagnostics support for AirVisual."""
from __future__ import annotations
from types import MappingProxyType
from typing import Any
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_STATE
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONF_CITY, CONF_COUNTRY, DOMAIN
CONF_COORDINATES = "coordinates"
@callback
def _async_redact_data(data: MappingProxyType | dict) -> dict[str, Any]:
"""Redact sensitive data in a dict."""
redacted = {**data}
for key, value in redacted.items():
if key in (
CONF_API_KEY,
CONF_CITY,
CONF_COORDINATES,
CONF_COUNTRY,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_STATE,
):
redacted[key] = REDACTED
elif isinstance(value, dict):
redacted[key] = _async_redact_data(value)
return redacted
TO_REDACT = {
CONF_API_KEY,
CONF_CITY,
CONF_COORDINATES,
CONF_COUNTRY,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_STATE,
}
async def async_get_config_entry_diagnostics(
@ -46,8 +33,8 @@ async def async_get_config_entry_diagnostics(
return {
"entry": {
"title": entry.title,
"data": _async_redact_data(entry.data),
"options": _async_redact_data(entry.options),
"data": async_redact_data(entry.data, TO_REDACT),
"options": async_redact_data(entry.options, TO_REDACT),
},
"data": _async_redact_data(coordinator.data["data"]),
"data": async_redact_data(coordinator.data["data"], TO_REDACT),
}

View File

@ -1,13 +1,12 @@
"""Diagnostics support for Ambient PWS."""
from __future__ import annotations
from types import MappingProxyType
from typing import Any
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from . import AmbientStation
from .const import CONF_APP_KEY, DOMAIN
@ -20,31 +19,17 @@ CONF_MAC_ADDRESS = "mac_address"
CONF_MAC_ADDRESS_CAMEL = "macAddress"
CONF_TZ = "tz"
@callback
def _async_redact_data(data: MappingProxyType | dict) -> dict[str, Any]:
"""Redact sensitive data in a dict."""
redacted = {**data}
for key, value in redacted.items():
if key in (
CONF_API_KEY,
CONF_API_KEY_CAMEL,
CONF_APP_KEY,
CONF_APP_KEY_CAMEL,
CONF_DEVICE_ID_CAMEL,
CONF_LOCATION,
CONF_MAC_ADDRESS,
CONF_MAC_ADDRESS_CAMEL,
CONF_TZ,
):
redacted[key] = REDACTED
elif isinstance(value, dict):
redacted[key] = _async_redact_data(value)
elif isinstance(value, list):
redacted[key] = [_async_redact_data(item) for item in value]
return redacted
TO_REDACT = {
CONF_API_KEY,
CONF_API_KEY_CAMEL,
CONF_APP_KEY,
CONF_APP_KEY_CAMEL,
CONF_DEVICE_ID_CAMEL,
CONF_LOCATION,
CONF_MAC_ADDRESS,
CONF_MAC_ADDRESS_CAMEL,
CONF_TZ,
}
async def async_get_config_entry_diagnostics(
@ -56,7 +41,7 @@ async def async_get_config_entry_diagnostics(
return {
"entry": {
"title": entry.title,
"data": _async_redact_data(entry.data),
"data": async_redact_data(entry.data, TO_REDACT),
},
"stations": _async_redact_data(ambient.stations),
"stations": async_redact_data(ambient.stations, TO_REDACT),
}

View File

@ -22,8 +22,9 @@ from homeassistant.util.json import (
)
from .const import DOMAIN, REDACTED, DiagnosticsSubType, DiagnosticsType
from .util import async_redact_data
__all__ = ["REDACTED"]
__all__ = ["REDACTED", "async_redact_data"]
_LOGGER = logging.getLogger(__name__)

View File

@ -0,0 +1,28 @@
"""Diagnostic utilities."""
from __future__ import annotations
from collections.abc import Iterable, Mapping
from typing import Any
from homeassistant.core import callback
from .const import REDACTED
@callback
def async_redact_data(data: Mapping, to_redact: Iterable[Any]) -> dict[str, Any]:
"""Redact sensitive data in a dict."""
if not isinstance(data, (Mapping, list)):
return data
redacted = {**data}
for key, value in redacted.items():
if key in to_redact:
redacted[key] = REDACTED
elif isinstance(value, dict):
redacted[key] = async_redact_data(value, to_redact)
elif isinstance(value, list):
redacted[key] = [async_redact_data(item, to_redact) for item in value]
return redacted

View File

@ -1,13 +1,15 @@
"""Diagnostics support for Evil Genius Labs."""
from __future__ import annotations
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from . import EvilGeniusUpdateCoordinator
from .const import DOMAIN
TO_REDACT = {"wiFiSsidDefault", "wiFiSSID"}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
@ -16,10 +18,6 @@ async def async_get_config_entry_diagnostics(
coordinator: EvilGeniusUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]
return {
"info": {
**coordinator.info,
"wiFiSsidDefault": REDACTED,
"wiFiSSID": REDACTED,
},
"info": async_redact_data(coordinator.info, TO_REDACT),
"data": coordinator.data,
}

View File

@ -1,13 +1,25 @@
"""Diagnostics support for Netatmo."""
from __future__ import annotations
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from .const import DATA_HANDLER, DOMAIN
from .data_handler import NetatmoDataHandler
TO_REDACT = {
"access_token",
"refresh_token",
"restricted_access_token",
"restricted_refresh_token",
"webhook_id",
"lat_ne",
"lat_sw",
"lon_ne",
"lon_sw",
}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
@ -17,28 +29,13 @@ async def async_get_config_entry_diagnostics(
DATA_HANDLER
]
diagnostics_data = {
"info": {
**config_entry.as_dict(),
"webhook_registered": data_handler.webhook,
},
"data": data_handler.data,
return {
"info": async_redact_data(
{
**config_entry.as_dict(),
"webhook_registered": data_handler.webhook,
},
TO_REDACT,
),
"data": async_redact_data(data_handler.data, TO_REDACT),
}
if "token" in diagnostics_data["info"]["data"]:
diagnostics_data["info"]["data"]["token"]["access_token"] = REDACTED
diagnostics_data["info"]["data"]["token"]["refresh_token"] = REDACTED
diagnostics_data["info"]["data"]["token"]["restricted_access_token"] = REDACTED
diagnostics_data["info"]["data"]["token"]["restricted_refresh_token"] = REDACTED
if "webhook_id" in diagnostics_data["info"]["data"]:
diagnostics_data["info"]["data"]["webhook_id"] = REDACTED
if "weather_areas" in diagnostics_data["info"].get("options", {}):
for area in diagnostics_data["info"]["options"]["weather_areas"]:
for attr in ("lat_ne", "lat_sw", "lon_ne", "lon_sw"):
diagnostics_data["info"]["options"]["weather_areas"][area][
attr
] = REDACTED
return diagnostics_data

View File

@ -1,42 +1,25 @@
"""Diagnostics support for Renault."""
from __future__ import annotations
from types import MappingProxyType
from typing import Any
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntry
from . import RenaultHub
from .const import CONF_KAMEREON_ACCOUNT_ID, DOMAIN
TO_REDACT = (
TO_REDACT = {
CONF_KAMEREON_ACCOUNT_ID,
CONF_PASSWORD,
CONF_USERNAME,
"radioCode",
"registrationNumber",
"vin",
)
@callback
def _async_redact_data(data: MappingProxyType | dict) -> dict[str, Any]:
"""Redact sensitive data in a dict."""
redacted = {**data}
for key, value in redacted.items():
if key in TO_REDACT:
redacted[key] = REDACTED
elif isinstance(value, dict):
redacted[key] = _async_redact_data(value)
elif isinstance(value, list):
redacted[key] = [_async_redact_data(item) for item in value]
return redacted
}
async def async_get_config_entry_diagnostics(
@ -48,10 +31,10 @@ async def async_get_config_entry_diagnostics(
return {
"entry": {
"title": entry.title,
"data": _async_redact_data(entry.data),
"data": async_redact_data(entry.data, TO_REDACT),
},
"vehicles": [
_async_redact_data(vehicle.details.raw_data)
async_redact_data(vehicle.details.raw_data, TO_REDACT)
for vehicle in renault_hub.vehicles.values()
],
}
@ -65,5 +48,7 @@ async def async_get_device_diagnostics(
vin = next(iter(device.identifiers))[1]
return {
"details": _async_redact_data(renault_hub.vehicles[vin].details.raw_data),
"details": async_redact_data(
renault_hub.vehicles[vin].details.raw_data, TO_REDACT
),
}

View File

@ -1,4 +1,6 @@
"""Test AirVisual diagnostics."""
from homeassistant.components.diagnostics import REDACTED
from tests.components.diagnostics import get_diagnostics_for_config_entry
@ -8,18 +10,18 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_airvisua
"entry": {
"title": "Mock Title",
"data": {
"api_key": "**REDACTED**",
"api_key": REDACTED,
"integration_type": "Geographical Location by Latitude/Longitude",
"latitude": "**REDACTED**",
"longitude": "**REDACTED**",
"latitude": REDACTED,
"longitude": REDACTED,
},
"options": {
"show_on_map": True,
},
},
"data": {
"city": "**REDACTED**",
"country": "**REDACTED**",
"city": REDACTED,
"country": REDACTED,
"current": {
"weather": {
"ts": "2021-09-03T21:00:00.000Z",
@ -39,9 +41,9 @@ async def test_entry_diagnostics(hass, config_entry, hass_client, setup_airvisua
},
},
"location": {
"coordinates": "**REDACTED**",
"coordinates": REDACTED,
"type": "Point",
},
"state": "**REDACTED**",
"state": REDACTED,
},
}

View File

@ -1,5 +1,6 @@
"""Test Ambient PWS diagnostics."""
from homeassistant.components.ambient_station import DOMAIN
from homeassistant.components.diagnostics import REDACTED
from tests.components.diagnostics import get_diagnostics_for_config_entry
@ -12,14 +13,14 @@ async def test_entry_diagnostics(
ambient.stations = station_data
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"data": {"api_key": "**REDACTED**", "app_key": "**REDACTED**"},
"data": {"api_key": REDACTED, "app_key": REDACTED},
"title": "Mock Title",
},
"stations": {
"devices": [
{
"apiKey": "**REDACTED**",
"info": {"location": "**REDACTED**", "name": "Side Yard"},
"apiKey": REDACTED,
"info": {"location": REDACTED, "name": "Side Yard"},
"lastData": {
"baromabsin": 25.016,
"baromrelin": 29.953,
@ -27,7 +28,7 @@ async def test_entry_diagnostics(
"dailyrainin": 0,
"date": "2022-01-19T22:38:00.000Z",
"dateutc": 1642631880000,
"deviceId": "**REDACTED**",
"deviceId": REDACTED,
"dewPoint": 17.75,
"dewPointin": 37,
"eventrainin": 0,
@ -43,14 +44,14 @@ async def test_entry_diagnostics(
"tempf": 21,
"tempinf": 70.9,
"totalrainin": 35.398,
"tz": "**REDACTED**",
"tz": REDACTED,
"uv": 0,
"weeklyrainin": 0,
"winddir": 25,
"windgustmph": 1.1,
"windspeedmph": 0.2,
},
"macAddress": "**REDACTED**",
"macAddress": REDACTED,
}
],
"method": "subscribe",

View File

@ -1,6 +1,7 @@
"""Test the Netatmo diagnostics."""
from unittest.mock import AsyncMock, patch
from homeassistant.components.diagnostics import REDACTED
from homeassistant.setup import async_setup_component
from .common import fake_post_request
@ -34,11 +35,9 @@ async def test_entry_diagnostics(hass, hass_client, config_entry):
"data": {
"auth_implementation": "cloud",
"token": {
"access_token": "**REDACTED**",
"restricted_access_token": "**REDACTED**",
"access_token": REDACTED,
"expires_in": 60,
"refresh_token": "**REDACTED**",
"restricted_refresh_token": "**REDACTED**",
"refresh_token": REDACTED,
"scope": [
"read_station",
"read_camera",
@ -54,7 +53,7 @@ async def test_entry_diagnostics(hass, hass_client, config_entry):
],
"type": "Bearer",
},
"webhook_id": "**REDACTED**",
"webhook_id": REDACTED,
},
"disabled_by": None,
"domain": "netatmo",
@ -62,19 +61,19 @@ async def test_entry_diagnostics(hass, hass_client, config_entry):
"weather_areas": {
"Home avg": {
"area_name": "Home avg",
"lat_ne": "**REDACTED**",
"lat_sw": "**REDACTED**",
"lon_ne": "**REDACTED**",
"lon_sw": "**REDACTED**",
"lat_ne": REDACTED,
"lat_sw": REDACTED,
"lon_ne": REDACTED,
"lon_sw": REDACTED,
"mode": "avg",
"show_on_map": False,
},
"Home max": {
"area_name": "Home max",
"lat_ne": "**REDACTED**",
"lat_sw": "**REDACTED**",
"lon_ne": "**REDACTED**",
"lon_sw": "**REDACTED**",
"lat_ne": REDACTED,
"lat_sw": REDACTED,
"lon_ne": REDACTED,
"lon_sw": REDACTED,
"mode": "max",
"show_on_map": True,
},

View File

@ -1,6 +1,7 @@
"""Test Renault diagnostics."""
import pytest
from homeassistant.components.diagnostics import REDACTED
from homeassistant.components.renault import DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
@ -14,7 +15,7 @@ from tests.components.diagnostics import (
pytestmark = pytest.mark.usefixtures("patch_renault_account", "patch_get_vehicles")
VEHICLE_DETAILS = {
"vin": "**REDACTED**",
"vin": REDACTED,
"registrationDate": "2017-08-01",
"firstRegistrationDate": "2017-08-01",
"engineType": "5AQ",
@ -52,7 +53,7 @@ VEHICLE_DETAILS = {
},
"version": {"code": "INT MB 10R"},
"energy": {"code": "ELEC", "label": "ELECTRIQUE", "group": "019"},
"registrationNumber": "**REDACTED**",
"registrationNumber": REDACTED,
"vcd": "SYTINC/SKTPOU/SAND41/FDIU1/SSESM/MAPSUP/SSCALL/SAND88/SAND90/SQKDRO/SDIFPA/FACBA2/PRLEX1/SSRCAR/CABDO2/TCU0G2/SWALBO/EVTEC1/STANDA/X10/B10/EA2/MB/ELEC/DG/TEMP/TR4X2/RV/ABS/CAREG/LAC/VT003/CPE/RET03/SPROJA/RALU16/CEAVRH/AIRBA1/SERIE/DRA/DRAP08/HARM02/ATAR/TERQG/SFBANA/KM/DPRPN/AVREPL/SSDECA/ASRESP/RDAR02/ALEVA/CACBL2/SOP02C/CTHAB2/TRNOR/LVAVIP/LVAREL/SASURV/KTGREP/SGSCHA/APL03/ALOUCC/CMAR3P/NAV3G5/RAD37A/BVEL/AUTAUG/RNORM/ISOFIX/EQPEUR/HRGM01/SDPCLV/TLFRAN/SPRODI/SAN613/SSAPEX/GENEV1/ELC1/SANCML/PE2012/PHAS1/SAN913/045KWH/BT4AR1/VEC153/X101VE/NBT017/5AQ",
"assets": [
{
@ -130,7 +131,7 @@ VEHICLE_DETAILS = {
"deliveryDate": "2017-08-11",
"retrievedFromDhs": False,
"engineEnergyType": "ELEC",
"radioCode": "**REDACTED**",
"radioCode": REDACTED,
}
@ -146,10 +147,10 @@ async def test_entry_diagnostics(
assert await get_diagnostics_for_config_entry(hass, hass_client, config_entry) == {
"entry": {
"data": {
"kamereon_account_id": "**REDACTED**",
"kamereon_account_id": REDACTED,
"locale": "fr_FR",
"password": "**REDACTED**",
"username": "**REDACTED**",
"password": REDACTED,
"username": REDACTED,
},
"title": "Mock Title",
},