Fix double space quoting in WebDAV (#140364)

This commit is contained in:
Jan-Philipp Benecke 2025-03-11 14:06:44 +01:00 committed by GitHub
parent bc6d342919
commit d2124db3ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 154 additions and 7 deletions

View File

@ -13,7 +13,11 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
from .const import CONF_BACKUP_PATH, DATA_BACKUP_AGENT_LISTENERS, DOMAIN from .const import CONF_BACKUP_PATH, DATA_BACKUP_AGENT_LISTENERS, DOMAIN
from .helpers import async_create_client, async_ensure_path_exists from .helpers import (
async_create_client,
async_ensure_path_exists,
async_migrate_wrong_folder_path,
)
type WebDavConfigEntry = ConfigEntry[Client] type WebDavConfigEntry = ConfigEntry[Client]
@ -46,10 +50,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: WebDavConfigEntry) -> bo
translation_key="cannot_connect", translation_key="cannot_connect",
) )
path = entry.data.get(CONF_BACKUP_PATH, "/")
await async_migrate_wrong_folder_path(client, path)
# Ensure the backup directory exists # Ensure the backup directory exists
if not await async_ensure_path_exists( if not await async_ensure_path_exists(client, path):
client, entry.data.get(CONF_BACKUP_PATH, "/")
):
raise ConfigEntryNotReady( raise ConfigEntryNotReady(
translation_domain=DOMAIN, translation_domain=DOMAIN,
translation_key="cannot_access_or_create_backup_path", translation_key="cannot_access_or_create_backup_path",

View File

@ -1,10 +1,18 @@
"""Helper functions for the WebDAV component.""" """Helper functions for the WebDAV component."""
import logging
from aiowebdav2.client import Client, ClientOptions from aiowebdav2.client import Client, ClientOptions
from aiowebdav2.exceptions import WebDavError
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@callback @callback
def async_create_client( def async_create_client(
@ -36,3 +44,24 @@ async def async_ensure_path_exists(client: Client, path: str) -> bool:
return False return False
return True return True
async def async_migrate_wrong_folder_path(client: Client, path: str) -> None:
"""Migrate the wrong encoded folder path to the correct one."""
wrong_path = path.replace(" ", "%20")
if await client.check(wrong_path):
try:
await client.move(wrong_path, path)
except WebDavError as err:
raise ConfigEntryNotReady(
translation_domain=DOMAIN,
translation_key="failed_to_migrate_folder",
translation_placeholders={
"wrong_path": wrong_path,
"correct_path": path,
},
) from err
_LOGGER.debug(
"Migrated wrong encoded folder path from %s to %s", wrong_path, path
)

View File

@ -8,5 +8,5 @@
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aiowebdav2"], "loggers": ["aiowebdav2"],
"quality_scale": "bronze", "quality_scale": "bronze",
"requirements": ["aiowebdav2==0.4.1"] "requirements": ["aiowebdav2==0.4.2"]
} }

View File

@ -36,6 +36,9 @@
}, },
"cannot_access_or_create_backup_path": { "cannot_access_or_create_backup_path": {
"message": "Cannot access or create backup path. Please check the path and permissions." "message": "Cannot access or create backup path. Please check the path and permissions."
},
"failed_to_migrate_folder": {
"message": "Failed to migrate wrong encoded folder \"{wrong_path}\" to \"{correct_path}\"."
} }
} }
} }

2
requirements_all.txt generated
View File

@ -422,7 +422,7 @@ aiowaqi==3.1.0
aiowatttime==0.1.1 aiowatttime==0.1.1
# homeassistant.components.webdav # homeassistant.components.webdav
aiowebdav2==0.4.1 aiowebdav2==0.4.2
# homeassistant.components.webostv # homeassistant.components.webostv
aiowebostv==0.7.3 aiowebostv==0.7.3

View File

@ -404,7 +404,7 @@ aiowaqi==3.1.0
aiowatttime==0.1.1 aiowatttime==0.1.1
# homeassistant.components.webdav # homeassistant.components.webdav
aiowebdav2==0.4.1 aiowebdav2==0.4.2
# homeassistant.components.webostv # homeassistant.components.webostv
aiowebostv==0.7.3 aiowebostv==0.7.3

View File

@ -1 +1,14 @@
"""Tests for the WebDAV integration.""" """Tests for the WebDAV integration."""
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_integration(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
"""Set up the WebDAV integration for testing."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -62,4 +62,5 @@ def mock_webdav_client() -> Generator[AsyncMock]:
mock.download_iter.side_effect = _download_mock mock.download_iter.side_effect = _download_mock
mock.upload_iter.return_value = None mock.upload_iter.return_value = None
mock.clean.return_value = None mock.clean.return_value = None
mock.move.return_value = None
yield mock yield mock

View File

@ -0,0 +1,96 @@
"""Test WebDAV component setup."""
from unittest.mock import AsyncMock
from aiowebdav2.exceptions import WebDavError
import pytest
from homeassistant.components.webdav.const import CONF_BACKUP_PATH, DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME
from homeassistant.core import HomeAssistant
from . import setup_integration
from tests.common import MockConfigEntry
async def test_migrate_wrong_path(
hass: HomeAssistant, webdav_client: AsyncMock
) -> None:
"""Test migration of wrong encoded folder path."""
webdav_client.list_with_properties.return_value = [
{"/wrong%20path": []},
]
config_entry = MockConfigEntry(
title="user@webdav.demo",
domain=DOMAIN,
data={
CONF_URL: "https://webdav.demo",
CONF_USERNAME: "user",
CONF_PASSWORD: "supersecretpassword",
CONF_BACKUP_PATH: "/wrong path",
},
entry_id="01JKXV07ASC62D620DGYNG2R8H",
)
await setup_integration(hass, config_entry)
webdav_client.move.assert_called_once_with("/wrong%20path", "/wrong path")
async def test_migrate_non_wrong_path(
hass: HomeAssistant, webdav_client: AsyncMock
) -> None:
"""Test no migration of correct folder path."""
webdav_client.list_with_properties.return_value = [
{"/correct path": []},
]
webdav_client.check.side_effect = lambda path: path == "/correct path"
config_entry = MockConfigEntry(
title="user@webdav.demo",
domain=DOMAIN,
data={
CONF_URL: "https://webdav.demo",
CONF_USERNAME: "user",
CONF_PASSWORD: "supersecretpassword",
CONF_BACKUP_PATH: "/correct path",
},
entry_id="01JKXV07ASC62D620DGYNG2R8H",
)
await setup_integration(hass, config_entry)
webdav_client.move.assert_not_called()
async def test_migrate_error(
hass: HomeAssistant,
webdav_client: AsyncMock,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test migration of wrong encoded folder path with error."""
webdav_client.list_with_properties.return_value = [
{"/wrong%20path": []},
]
webdav_client.move.side_effect = WebDavError("Failed to move")
config_entry = MockConfigEntry(
title="user@webdav.demo",
domain=DOMAIN,
data={
CONF_URL: "https://webdav.demo",
CONF_USERNAME: "user",
CONF_PASSWORD: "supersecretpassword",
CONF_BACKUP_PATH: "/wrong path",
},
entry_id="01JKXV07ASC62D620DGYNG2R8H",
)
await setup_integration(hass, config_entry)
assert config_entry.state is ConfigEntryState.SETUP_RETRY
assert (
'Failed to migrate wrong encoded folder "/wrong%20path" to "/wrong path"'
in caplog.text
)