1
mirror of https://github.com/home-assistant/core synced 2024-10-04 07:58:43 +02:00

Add ssl_cipher_list option to rest (#91078)

This commit is contained in:
Michael 2023-04-15 23:22:41 +02:00 committed by GitHub
parent 59dc0ea2e0
commit 323d16cc21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 2 deletions

View File

@ -43,7 +43,9 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import ( from .const import (
CONF_ENCODING, CONF_ENCODING,
CONF_SSL_CIPHER_LIST,
COORDINATOR, COORDINATOR,
DEFAULT_SSL_CIPHER_LIST,
DOMAIN, DOMAIN,
PLATFORM_IDX, PLATFORM_IDX,
REST, REST,
@ -185,6 +187,7 @@ def create_rest_data_from_config(hass: HomeAssistant, config: ConfigType) -> Res
method: str = config[CONF_METHOD] method: str = config[CONF_METHOD]
payload: str | None = config.get(CONF_PAYLOAD) payload: str | None = config.get(CONF_PAYLOAD)
verify_ssl: bool = config[CONF_VERIFY_SSL] verify_ssl: bool = config[CONF_VERIFY_SSL]
ssl_cipher_list: str = config.get(CONF_SSL_CIPHER_LIST, DEFAULT_SSL_CIPHER_LIST)
username: str | None = config.get(CONF_USERNAME) username: str | None = config.get(CONF_USERNAME)
password: str | None = config.get(CONF_PASSWORD) password: str | None = config.get(CONF_PASSWORD)
headers: dict[str, str] | None = config.get(CONF_HEADERS) headers: dict[str, str] | None = config.get(CONF_HEADERS)
@ -218,5 +221,6 @@ def create_rest_data_from_config(hass: HomeAssistant, config: ConfigType) -> Res
params, params,
payload, payload,
verify_ssl, verify_ssl,
ssl_cipher_list,
timeout, timeout,
) )

View File

@ -1,12 +1,16 @@
"""The rest component constants.""" """The rest component constants."""
from homeassistant.util.ssl import SSLCipherList
DOMAIN = "rest" DOMAIN = "rest"
DEFAULT_METHOD = "GET" DEFAULT_METHOD = "GET"
DEFAULT_VERIFY_SSL = True DEFAULT_VERIFY_SSL = True
DEFAULT_SSL_CIPHER_LIST = SSLCipherList.PYTHON_DEFAULT
DEFAULT_FORCE_UPDATE = False DEFAULT_FORCE_UPDATE = False
DEFAULT_ENCODING = "UTF-8" DEFAULT_ENCODING = "UTF-8"
CONF_ENCODING = "encoding" CONF_ENCODING = "encoding"
CONF_SSL_CIPHER_LIST = "ssl_cipher_list"
DEFAULT_BINARY_SENSOR_NAME = "REST Binary Sensor" DEFAULT_BINARY_SENSOR_NAME = "REST Binary Sensor"
DEFAULT_SENSOR_NAME = "REST Sensor" DEFAULT_SENSOR_NAME = "REST Sensor"

View File

@ -9,6 +9,7 @@ import httpx
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import template from homeassistant.helpers import template
from homeassistant.helpers.httpx_client import create_async_httpx_client from homeassistant.helpers.httpx_client import create_async_httpx_client
from homeassistant.util.ssl import SSLCipherList
DEFAULT_TIMEOUT = 10 DEFAULT_TIMEOUT = 10
@ -29,6 +30,7 @@ class RestData:
params: dict[str, str] | None, params: dict[str, str] | None,
data: str | None, data: str | None,
verify_ssl: bool, verify_ssl: bool,
ssl_cipher_list: str,
timeout: int = DEFAULT_TIMEOUT, timeout: int = DEFAULT_TIMEOUT,
) -> None: ) -> None:
"""Initialize the data object.""" """Initialize the data object."""
@ -42,6 +44,7 @@ class RestData:
self._request_data = data self._request_data = data
self._timeout = timeout self._timeout = timeout
self._verify_ssl = verify_ssl self._verify_ssl = verify_ssl
self._ssl_cipher_list = SSLCipherList(ssl_cipher_list)
self._async_client: httpx.AsyncClient | None = None self._async_client: httpx.AsyncClient | None = None
self.data: str | None = None self.data: str | None = None
self.last_exception: Exception | None = None self.last_exception: Exception | None = None
@ -55,7 +58,10 @@ class RestData:
"""Get the latest data from REST service with provided method.""" """Get the latest data from REST service with provided method."""
if not self._async_client: if not self._async_client:
self._async_client = create_async_httpx_client( self._async_client = create_async_httpx_client(
self._hass, verify_ssl=self._verify_ssl, default_encoding=self._encoding self._hass,
verify_ssl=self._verify_ssl,
default_encoding=self._encoding,
ssl_cipher_list=self._ssl_cipher_list,
) )
rendered_headers = template.render_complex(self._headers, parse_result=False) rendered_headers = template.render_complex(self._headers, parse_result=False)

View File

@ -31,14 +31,17 @@ from homeassistant.helpers.template_entity import (
TEMPLATE_ENTITY_BASE_SCHEMA, TEMPLATE_ENTITY_BASE_SCHEMA,
TEMPLATE_SENSOR_BASE_SCHEMA, TEMPLATE_SENSOR_BASE_SCHEMA,
) )
from homeassistant.util.ssl import SSLCipherList
from .const import ( from .const import (
CONF_ENCODING, CONF_ENCODING,
CONF_JSON_ATTRS, CONF_JSON_ATTRS,
CONF_JSON_ATTRS_PATH, CONF_JSON_ATTRS_PATH,
CONF_SSL_CIPHER_LIST,
DEFAULT_ENCODING, DEFAULT_ENCODING,
DEFAULT_FORCE_UPDATE, DEFAULT_FORCE_UPDATE,
DEFAULT_METHOD, DEFAULT_METHOD,
DEFAULT_SSL_CIPHER_LIST,
DEFAULT_VERIFY_SSL, DEFAULT_VERIFY_SSL,
DOMAIN, DOMAIN,
METHODS, METHODS,
@ -58,6 +61,10 @@ RESOURCE_SCHEMA = {
vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_PASSWORD): cv.string,
vol.Optional(CONF_PAYLOAD): cv.string, vol.Optional(CONF_PAYLOAD): cv.string,
vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean,
vol.Optional(
CONF_SSL_CIPHER_LIST,
default=DEFAULT_SSL_CIPHER_LIST,
): vol.In([e.value for e in SSLCipherList]),
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string, vol.Optional(CONF_ENCODING, default=DEFAULT_ENCODING): cv.string,
} }

View File

@ -2,7 +2,7 @@
import asyncio import asyncio
from http import HTTPStatus from http import HTTPStatus
import ssl import ssl
from unittest.mock import MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
import httpx import httpx
import pytest import pytest
@ -30,6 +30,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from homeassistant.util.ssl import SSLCipherList
from tests.common import get_fixture_path from tests.common import get_fixture_path
@ -157,6 +158,44 @@ async def test_setup_encoding(hass: HomeAssistant) -> None:
assert hass.states.get("sensor.mysensor").state == "tack själv" assert hass.states.get("sensor.mysensor").state == "tack själv"
@respx.mock
@pytest.mark.parametrize(
("ssl_cipher_list", "ssl_cipher_list_expected"),
(
("python_default", SSLCipherList.PYTHON_DEFAULT),
("intermediate", SSLCipherList.INTERMEDIATE),
("modern", SSLCipherList.MODERN),
),
)
async def test_setup_ssl_ciphers(
hass: HomeAssistant, ssl_cipher_list: str, ssl_cipher_list_expected: SSLCipherList
) -> None:
"""Test setup with minimum configuration."""
with patch(
"homeassistant.components.rest.data.create_async_httpx_client",
return_value=MagicMock(request=AsyncMock(return_value=respx.MockResponse())),
) as httpx:
assert await async_setup_component(
hass,
SENSOR_DOMAIN,
{
SENSOR_DOMAIN: {
"platform": DOMAIN,
"resource": "http://localhost",
"method": "GET",
"ssl_cipher_list": ssl_cipher_list,
}
},
)
await hass.async_block_till_done()
httpx.assert_called_once_with(
hass,
verify_ssl=True,
default_encoding="UTF-8",
ssl_cipher_list=ssl_cipher_list_expected,
)
@respx.mock @respx.mock
async def test_manual_update(hass: HomeAssistant) -> None: async def test_manual_update(hass: HomeAssistant) -> None:
"""Test setup with minimum configuration.""" """Test setup with minimum configuration."""