diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 016d5162d238..afd6065cb987 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -7,6 +7,7 @@ from datetime import timedelta import functools as ft import json import logging +from urllib.parse import quote import pychromecast from pychromecast.controllers.homeassistant import HomeAssistantController @@ -472,7 +473,7 @@ class CastDevice(MediaPlayerEntity): media_id = async_sign_path( self.hass, refresh_token.id, - media_id, + quote(media_id), timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 3267c9cc70e7..382758194832 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -1,6 +1,7 @@ """Authentication for HTTP component.""" import logging import secrets +from urllib.parse import unquote from aiohttp import hdrs from aiohttp.web import middleware @@ -30,11 +31,16 @@ def async_sign_path(hass, refresh_token_id, path, expiration): now = dt_util.utcnow() encoded = jwt.encode( - {"iss": refresh_token_id, "path": path, "iat": now, "exp": now + expiration}, + { + "iss": refresh_token_id, + "path": unquote(path), + "iat": now, + "exp": now + expiration, + }, secret, algorithm="HS256", ) - return f"{path}?{SIGN_QUERY_PARAM}=" f"{encoded.decode()}" + return f"{path}?{SIGN_QUERY_PARAM}={encoded.decode()}" @callback diff --git a/homeassistant/components/media_source/__init__.py b/homeassistant/components/media_source/__init__.py index 0ef5d460580f..5b027a99bf9c 100644 --- a/homeassistant/components/media_source/__init__.py +++ b/homeassistant/components/media_source/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import timedelta +from urllib.parse import quote import voluptuous as vol @@ -123,7 +124,7 @@ async def websocket_resolve_media(hass, connection, msg): url = async_sign_path( hass, connection.refresh_token_id, - url, + quote(url), timedelta(seconds=msg["expires"]), ) diff --git a/tests/components/media_source/test_init.py b/tests/components/media_source/test_init.py index 0dda9f67fbe3..d8ee73ebc2f1 100644 --- a/tests/components/media_source/test_init.py +++ b/tests/components/media_source/test_init.py @@ -1,5 +1,6 @@ """Test Media Source initialization.""" from unittest.mock import patch +from urllib.parse import quote import pytest @@ -45,7 +46,7 @@ async def test_async_browse_media(hass): media = await media_source.async_browse_media(hass, "") assert isinstance(media, media_source.models.BrowseMediaSource) assert media.title == "media/" - assert len(media.children) == 1 + assert len(media.children) == 2 # Test invalid media content with pytest.raises(ValueError): @@ -133,14 +134,15 @@ async def test_websocket_browse_media(hass, hass_ws_client): assert msg["error"]["message"] == "test" -async def test_websocket_resolve_media(hass, hass_ws_client): +@pytest.mark.parametrize("filename", ["test.mp3", "Epic Sax Guy 10 Hours.mp4"]) +async def test_websocket_resolve_media(hass, hass_ws_client, filename): """Test browse media websocket.""" assert await async_setup_component(hass, const.DOMAIN, {}) await hass.async_block_till_done() client = await hass_ws_client(hass) - media = media_source.models.PlayMedia("/media/local/test.mp3", "audio/mpeg") + media = media_source.models.PlayMedia(f"/media/local/{filename}", "audio/mpeg") with patch( "homeassistant.components.media_source.async_resolve_media", @@ -150,7 +152,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client): { "id": 1, "type": "media_source/resolve_media", - "media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/test.mp3", + "media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/{filename}", } ) @@ -158,7 +160,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client): assert msg["success"] assert msg["id"] == 1 - assert msg["result"]["url"].startswith(media.url) + assert msg["result"]["url"].startswith(quote(media.url)) assert msg["result"]["mime_type"] == media.mime_type with patch( diff --git a/tests/components/media_source/test_local_source.py b/tests/components/media_source/test_local_source.py index e3e2a3f1617d..aff4f92be022 100644 --- a/tests/components/media_source/test_local_source.py +++ b/tests/components/media_source/test_local_source.py @@ -95,5 +95,8 @@ async def test_media_view(hass, hass_client): resp = await client.get("/media/local/test.mp3") assert resp.status == 200 + resp = await client.get("/media/local/Epic Sax Guy 10 Hours.mp4") + assert resp.status == 200 + resp = await client.get("/media/recordings/test.mp3") assert resp.status == 200 diff --git a/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 b/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 new file mode 100644 index 000000000000..23bd6ccc5648 --- /dev/null +++ b/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 @@ -0,0 +1 @@ +I play the sax