From 14bca5a052d3b12a9384cff4c0c98b97be16d3b1 Mon Sep 17 00:00:00 2001 From: Thomas55555 <59625598+Thomas55555@users.noreply.github.com> Date: Mon, 5 Jan 2026 00:32:38 +0100 Subject: [PATCH] Make verify_ssl configurable in remote calendar (#160216) --- .../components/remote_calendar/config_flow.py | 5 +++-- .../components/remote_calendar/coordinator.py | 6 ++++-- .../components/remote_calendar/strings.json | 6 ++++-- tests/components/remote_calendar/conftest.py | 9 +++++++-- .../components/remote_calendar/test_config_flow.py | 14 +++++++++++++- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/remote_calendar/config_flow.py b/homeassistant/components/remote_calendar/config_flow.py index 3f835b5d82b..0e23ecfc8d1 100644 --- a/homeassistant/components/remote_calendar/config_flow.py +++ b/homeassistant/components/remote_calendar/config_flow.py @@ -8,7 +8,7 @@ from httpx import HTTPError, InvalidURL, TimeoutException import voluptuous as vol from homeassistant.config_entries import ConfigFlow, ConfigFlowResult -from homeassistant.const import CONF_URL +from homeassistant.const import CONF_URL, CONF_VERIFY_SSL from homeassistant.helpers.httpx_client import get_async_client from .client import get_calendar @@ -21,6 +21,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema( { vol.Required(CONF_CALENDAR_NAME): str, vol.Required(CONF_URL): str, + vol.Required(CONF_VERIFY_SSL, default=True): bool, } ) @@ -48,7 +49,7 @@ class RemoteCalendarConfigFlow(ConfigFlow, domain=DOMAIN): "webcal://", "https://", 1 ) self._async_abort_entries_match({CONF_URL: user_input[CONF_URL]}) - client = get_async_client(self.hass) + client = get_async_client(self.hass, verify_ssl=user_input[CONF_VERIFY_SSL]) try: res = await get_calendar(client, user_input[CONF_URL]) if res.status_code == HTTPStatus.FORBIDDEN: diff --git a/homeassistant/components/remote_calendar/coordinator.py b/homeassistant/components/remote_calendar/coordinator.py index 7a7abe37b89..2d592c3cb9b 100644 --- a/homeassistant/components/remote_calendar/coordinator.py +++ b/homeassistant/components/remote_calendar/coordinator.py @@ -7,7 +7,7 @@ from httpx import HTTPError, InvalidURL, TimeoutException from ical.calendar import Calendar from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_URL +from homeassistant.const import CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from homeassistant.helpers.httpx_client import get_async_client from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -42,7 +42,9 @@ class RemoteCalendarDataUpdateCoordinator(DataUpdateCoordinator[Calendar]): config_entry=config_entry, always_update=True, ) - self._client = get_async_client(hass) + self._client = get_async_client( + hass, verify_ssl=config_entry.data.get(CONF_VERIFY_SSL, True) + ) self._url = config_entry.data[CONF_URL] async def _async_update_data(self) -> Calendar: diff --git a/homeassistant/components/remote_calendar/strings.json b/homeassistant/components/remote_calendar/strings.json index ed0648562c2..a987ca956f1 100644 --- a/homeassistant/components/remote_calendar/strings.json +++ b/homeassistant/components/remote_calendar/strings.json @@ -13,11 +13,13 @@ "user": { "data": { "calendar_name": "Calendar name", - "url": "Calendar URL" + "url": "Calendar URL", + "verify_ssl": "[%key:common::config_flow::data::verify_ssl%]" }, "data_description": { "calendar_name": "The name of the calendar shown in the UI.", - "url": "The URL of the remote calendar." + "url": "The URL of the remote calendar.", + "verify_ssl": "Enable SSL certificate verification for secure connections." }, "description": "Please choose a name for the calendar to be imported" } diff --git a/tests/components/remote_calendar/conftest.py b/tests/components/remote_calendar/conftest.py index bf5184bbf54..75a6a69da71 100644 --- a/tests/components/remote_calendar/conftest.py +++ b/tests/components/remote_calendar/conftest.py @@ -9,7 +9,7 @@ import urllib import pytest from homeassistant.components.remote_calendar.const import CONF_CALENDAR_NAME, DOMAIN -from homeassistant.const import CONF_URL +from homeassistant.const import CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from tests.common import MockConfigEntry @@ -41,7 +41,12 @@ async def set_time_zone(hass: HomeAssistant, time_zone: str): def mock_config_entry() -> MockConfigEntry: """Fixture for mock configuration entry.""" return MockConfigEntry( - domain=DOMAIN, data={CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL} + domain=DOMAIN, + data={ + CONF_CALENDAR_NAME: CALENDAR_NAME, + CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, + }, ) diff --git a/tests/components/remote_calendar/test_config_flow.py b/tests/components/remote_calendar/test_config_flow.py index 3619251654d..0d7b36fc64d 100644 --- a/tests/components/remote_calendar/test_config_flow.py +++ b/tests/components/remote_calendar/test_config_flow.py @@ -6,7 +6,7 @@ import respx from homeassistant.components.remote_calendar.const import CONF_CALENDAR_NAME, DOMAIN from homeassistant.config_entries import SOURCE_USER -from homeassistant.const import CONF_URL +from homeassistant.const import CONF_URL, CONF_VERIFY_SSL from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -35,6 +35,7 @@ async def test_form_import_ics(hass: HomeAssistant, ics_content: str) -> None: user_input={ CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, }, ) assert result2["type"] is FlowResultType.CREATE_ENTRY @@ -42,6 +43,7 @@ async def test_form_import_ics(hass: HomeAssistant, ics_content: str) -> None: assert result2["data"] == { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, } @@ -71,6 +73,7 @@ async def test_form_import_webcal(hass: HomeAssistant, ics_content: str) -> None assert result2["data"] == { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, } @@ -116,6 +119,7 @@ async def test_form_invalid_url( { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, }, ) assert result3["type"] is FlowResultType.CREATE_ENTRY @@ -123,6 +127,7 @@ async def test_form_invalid_url( assert result3["data"] == { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, } @@ -209,6 +214,7 @@ async def test_form_http_status_error( { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, }, ) assert result3["type"] is FlowResultType.CREATE_ENTRY @@ -216,6 +222,7 @@ async def test_form_http_status_error( assert result3["data"] == { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, } @@ -238,6 +245,7 @@ async def test_no_valid_calendar(hass: HomeAssistant, ics_content: str) -> None: user_input={ CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, }, ) @@ -254,6 +262,7 @@ async def test_no_valid_calendar(hass: HomeAssistant, ics_content: str) -> None: { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: False, }, ) assert result3["type"] is FlowResultType.CREATE_ENTRY @@ -261,6 +270,7 @@ async def test_no_valid_calendar(hass: HomeAssistant, ics_content: str) -> None: assert result3["data"] == { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: False, } @@ -282,6 +292,7 @@ async def test_duplicate_name( { CONF_CALENDAR_NAME: CALENDAR_NAME, CONF_URL: "http://other-calendar.com", + CONF_VERIFY_SSL: True, }, ) await hass.async_block_till_done() @@ -308,6 +319,7 @@ async def test_duplicate_url( { CONF_CALENDAR_NAME: "new name", CONF_URL: CALENDER_URL, + CONF_VERIFY_SSL: True, }, ) await hass.async_block_till_done()