Quote media_source paths (#49054)

* Quote path in async_sign_path

* Address review comments, add tests

* Update tests/testing_config/media/Epic Sax Guy 10 Hours.mp4

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Erik Montnemery 2021-04-12 18:32:12 +02:00 committed by GitHub
parent dbb771e19c
commit f5545badac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 23 additions and 9 deletions

View File

@ -7,6 +7,7 @@ from datetime import timedelta
import functools as ft import functools as ft
import json import json
import logging import logging
from urllib.parse import quote
import pychromecast import pychromecast
from pychromecast.controllers.homeassistant import HomeAssistantController from pychromecast.controllers.homeassistant import HomeAssistantController
@ -472,7 +473,7 @@ class CastDevice(MediaPlayerEntity):
media_id = async_sign_path( media_id = async_sign_path(
self.hass, self.hass,
refresh_token.id, refresh_token.id,
media_id, quote(media_id),
timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME),
) )

View File

@ -1,6 +1,7 @@
"""Authentication for HTTP component.""" """Authentication for HTTP component."""
import logging import logging
import secrets import secrets
from urllib.parse import unquote
from aiohttp import hdrs from aiohttp import hdrs
from aiohttp.web import middleware from aiohttp.web import middleware
@ -30,11 +31,16 @@ def async_sign_path(hass, refresh_token_id, path, expiration):
now = dt_util.utcnow() now = dt_util.utcnow()
encoded = jwt.encode( 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, secret,
algorithm="HS256", algorithm="HS256",
) )
return f"{path}?{SIGN_QUERY_PARAM}=" f"{encoded.decode()}" return f"{path}?{SIGN_QUERY_PARAM}={encoded.decode()}"
@callback @callback

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta from datetime import timedelta
from urllib.parse import quote
import voluptuous as vol import voluptuous as vol
@ -123,7 +124,7 @@ async def websocket_resolve_media(hass, connection, msg):
url = async_sign_path( url = async_sign_path(
hass, hass,
connection.refresh_token_id, connection.refresh_token_id,
url, quote(url),
timedelta(seconds=msg["expires"]), timedelta(seconds=msg["expires"]),
) )

View File

@ -1,5 +1,6 @@
"""Test Media Source initialization.""" """Test Media Source initialization."""
from unittest.mock import patch from unittest.mock import patch
from urllib.parse import quote
import pytest import pytest
@ -45,7 +46,7 @@ async def test_async_browse_media(hass):
media = await media_source.async_browse_media(hass, "") media = await media_source.async_browse_media(hass, "")
assert isinstance(media, media_source.models.BrowseMediaSource) assert isinstance(media, media_source.models.BrowseMediaSource)
assert media.title == "media/" assert media.title == "media/"
assert len(media.children) == 1 assert len(media.children) == 2
# Test invalid media content # Test invalid media content
with pytest.raises(ValueError): with pytest.raises(ValueError):
@ -133,14 +134,15 @@ async def test_websocket_browse_media(hass, hass_ws_client):
assert msg["error"]["message"] == "test" 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.""" """Test browse media websocket."""
assert await async_setup_component(hass, const.DOMAIN, {}) assert await async_setup_component(hass, const.DOMAIN, {})
await hass.async_block_till_done() await hass.async_block_till_done()
client = await hass_ws_client(hass) 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( with patch(
"homeassistant.components.media_source.async_resolve_media", "homeassistant.components.media_source.async_resolve_media",
@ -150,7 +152,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client):
{ {
"id": 1, "id": 1,
"type": "media_source/resolve_media", "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["success"]
assert msg["id"] == 1 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 assert msg["result"]["mime_type"] == media.mime_type
with patch( with patch(

View File

@ -95,5 +95,8 @@ async def test_media_view(hass, hass_client):
resp = await client.get("/media/local/test.mp3") resp = await client.get("/media/local/test.mp3")
assert resp.status == 200 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") resp = await client.get("/media/recordings/test.mp3")
assert resp.status == 200 assert resp.status == 200

View File

@ -0,0 +1 @@
I play the sax