mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Dont substitute user/pass for relative stream urls on generic camera (#74201)
Co-authored-by: Dave T <davet2001@users.noreply.github.com>
This commit is contained in:
parent
57114c1a55
commit
737a1fd9fa
@ -228,7 +228,13 @@ class GenericCamera(Camera):
|
||||
try:
|
||||
stream_url = self._stream_source.async_render(parse_result=False)
|
||||
url = yarl.URL(stream_url)
|
||||
if not url.user and not url.password and self._username and self._password:
|
||||
if (
|
||||
not url.user
|
||||
and not url.password
|
||||
and self._username
|
||||
and self._password
|
||||
and url.is_absolute()
|
||||
):
|
||||
url = url.with_user(self._username).with_password(self._password)
|
||||
return str(url)
|
||||
except TemplateError as err:
|
||||
|
@ -150,6 +150,12 @@ async def async_test_still(
|
||||
except TemplateError as err:
|
||||
_LOGGER.warning("Problem rendering template %s: %s", url, err)
|
||||
return {CONF_STILL_IMAGE_URL: "template_error"}, None
|
||||
try:
|
||||
yarl_url = yarl.URL(url)
|
||||
except ValueError:
|
||||
return {CONF_STILL_IMAGE_URL: "malformed_url"}, None
|
||||
if not yarl_url.is_absolute():
|
||||
return {CONF_STILL_IMAGE_URL: "relative_url"}, None
|
||||
verify_ssl = info[CONF_VERIFY_SSL]
|
||||
auth = generate_auth(info)
|
||||
try:
|
||||
@ -222,7 +228,12 @@ async def async_test_stream(
|
||||
if info.get(CONF_USE_WALLCLOCK_AS_TIMESTAMPS):
|
||||
stream_options[CONF_USE_WALLCLOCK_AS_TIMESTAMPS] = True
|
||||
|
||||
url = yarl.URL(stream_source)
|
||||
try:
|
||||
url = yarl.URL(stream_source)
|
||||
except ValueError:
|
||||
return {CONF_STREAM_SOURCE: "malformed_url"}
|
||||
if not url.is_absolute():
|
||||
return {CONF_STREAM_SOURCE: "relative_url"}
|
||||
if not url.user and not url.password:
|
||||
username = info.get(CONF_USERNAME)
|
||||
password = info.get(CONF_PASSWORD)
|
||||
|
@ -6,6 +6,8 @@
|
||||
"unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.",
|
||||
"no_still_image_or_stream_url": "You must specify at least a still image or stream URL",
|
||||
"invalid_still_image": "URL did not return a valid still image",
|
||||
"malformed_url": "Malformed URL",
|
||||
"relative_url": "Relative URLs are not allowed",
|
||||
"stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)",
|
||||
"stream_http_not_found": "HTTP 404 Not found while trying to connect to stream",
|
||||
"template_error": "Error rendering template. Review log for more info.",
|
||||
@ -75,6 +77,8 @@
|
||||
"unable_still_load": "[%key:component::generic::config::error::unable_still_load%]",
|
||||
"no_still_image_or_stream_url": "[%key:component::generic::config::error::no_still_image_or_stream_url%]",
|
||||
"invalid_still_image": "[%key:component::generic::config::error::invalid_still_image%]",
|
||||
"malformed_url": "[%key:component::generic::config::error::malformed_url%]",
|
||||
"relative_url": "[%key:component::generic::config::error::relative_url%]",
|
||||
"template_error": "[%key:component::generic::config::error::template_error%]",
|
||||
"timeout": "[%key:component::generic::config::error::timeout%]",
|
||||
"stream_no_route_to_host": "[%key:component::generic::config::error::stream_no_route_to_host%]",
|
||||
|
@ -1,20 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"no_devices_found": "No devices found on the network",
|
||||
"single_instance_allowed": "Already configured. Only a single configuration possible."
|
||||
},
|
||||
"error": {
|
||||
"already_exists": "A camera with these URL settings already exists.",
|
||||
"invalid_still_image": "URL did not return a valid still image",
|
||||
"malformed_url": "Malformed URL",
|
||||
"no_still_image_or_stream_url": "You must specify at least a still image or stream URL",
|
||||
"relative_url": "Relative URLs are not allowed",
|
||||
"stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)",
|
||||
"stream_http_not_found": "HTTP 404 Not found while trying to connect to stream",
|
||||
"stream_io_error": "Input/Output error while trying to connect to stream. Wrong RTSP transport protocol?",
|
||||
"stream_no_route_to_host": "Could not find host while trying to connect to stream",
|
||||
"stream_no_video": "Stream has no video",
|
||||
"stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?",
|
||||
"stream_unauthorised": "Authorisation failed while trying to connect to stream",
|
||||
"template_error": "Error rendering template. Review log for more info.",
|
||||
"timeout": "Timeout while loading URL",
|
||||
"unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.",
|
||||
@ -50,14 +49,12 @@
|
||||
"error": {
|
||||
"already_exists": "A camera with these URL settings already exists.",
|
||||
"invalid_still_image": "URL did not return a valid still image",
|
||||
"malformed_url": "Malformed URL",
|
||||
"no_still_image_or_stream_url": "You must specify at least a still image or stream URL",
|
||||
"stream_file_not_found": "File not found while trying to connect to stream (is ffmpeg installed?)",
|
||||
"stream_http_not_found": "HTTP 404 Not found while trying to connect to stream",
|
||||
"relative_url": "Relative URLs are not allowed",
|
||||
"stream_io_error": "Input/Output error while trying to connect to stream. Wrong RTSP transport protocol?",
|
||||
"stream_no_route_to_host": "Could not find host while trying to connect to stream",
|
||||
"stream_no_video": "Stream has no video",
|
||||
"stream_not_permitted": "Operation not permitted while trying to connect to stream. Wrong RTSP transport protocol?",
|
||||
"stream_unauthorised": "Authorisation failed while trying to connect to stream",
|
||||
"template_error": "Error rendering template. Review log for more info.",
|
||||
"timeout": "Timeout while loading URL",
|
||||
"unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.",
|
||||
|
@ -180,33 +180,43 @@ async def test_form_only_still_sample(hass, user_flow, image_file):
|
||||
|
||||
@respx.mock
|
||||
@pytest.mark.parametrize(
|
||||
("template", "url", "expected_result"),
|
||||
("template", "url", "expected_result", "expected_errors"),
|
||||
[
|
||||
# Test we can handle templates in strange parts of the url, #70961.
|
||||
(
|
||||
"http://localhost:812{{3}}/static/icons/favicon-apple-180x180.png",
|
||||
"http://localhost:8123/static/icons/favicon-apple-180x180.png",
|
||||
data_entry_flow.RESULT_TYPE_CREATE_ENTRY,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"{% if 1 %}https://bla{% else %}https://yo{% endif %}",
|
||||
"https://bla/",
|
||||
data_entry_flow.RESULT_TYPE_CREATE_ENTRY,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"http://{{example.org",
|
||||
"http://example.org",
|
||||
data_entry_flow.RESULT_TYPE_FORM,
|
||||
{"still_image_url": "template_error"},
|
||||
),
|
||||
(
|
||||
"invalid1://invalid:4\\1",
|
||||
"invalid1://invalid:4%5c1",
|
||||
data_entry_flow.RESULT_TYPE_CREATE_ENTRY,
|
||||
data_entry_flow.RESULT_TYPE_FORM,
|
||||
{"still_image_url": "malformed_url"},
|
||||
),
|
||||
(
|
||||
"relative/urls/are/not/allowed.jpg",
|
||||
"relative/urls/are/not/allowed.jpg",
|
||||
data_entry_flow.RESULT_TYPE_FORM,
|
||||
{"still_image_url": "relative_url"},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_still_template(
|
||||
hass, user_flow, fakeimgbytes_png, template, url, expected_result
|
||||
hass, user_flow, fakeimgbytes_png, template, url, expected_result, expected_errors
|
||||
) -> None:
|
||||
"""Test we can handle various templates."""
|
||||
respx.get(url).respond(stream=fakeimgbytes_png)
|
||||
@ -220,6 +230,7 @@ async def test_still_template(
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result2["type"] == expected_result
|
||||
assert result2.get("errors") == expected_errors
|
||||
|
||||
|
||||
@respx.mock
|
||||
@ -514,8 +525,29 @@ async def test_options_template_error(hass, fakeimgbytes_png, mock_create_stream
|
||||
result4["flow_id"],
|
||||
user_input=data,
|
||||
)
|
||||
assert result5.get("type") == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result5["errors"] == {"stream_source": "template_error"}
|
||||
|
||||
assert result5.get("type") == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result5["errors"] == {"stream_source": "template_error"}
|
||||
|
||||
# verify that an relative stream url is rejected.
|
||||
data[CONF_STILL_IMAGE_URL] = "http://127.0.0.1/testurl/1"
|
||||
data[CONF_STREAM_SOURCE] = "relative/stream.mjpeg"
|
||||
result6 = await hass.config_entries.options.async_configure(
|
||||
result5["flow_id"],
|
||||
user_input=data,
|
||||
)
|
||||
assert result6.get("type") == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result6["errors"] == {"stream_source": "relative_url"}
|
||||
|
||||
# verify that an malformed stream url is rejected.
|
||||
data[CONF_STILL_IMAGE_URL] = "http://127.0.0.1/testurl/1"
|
||||
data[CONF_STREAM_SOURCE] = "http://example.com:45:56"
|
||||
result7 = await hass.config_entries.options.async_configure(
|
||||
result6["flow_id"],
|
||||
user_input=data,
|
||||
)
|
||||
assert result7.get("type") == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result7["errors"] == {"stream_source": "malformed_url"}
|
||||
|
||||
|
||||
async def test_slug(hass, caplog):
|
||||
@ -528,6 +560,10 @@ async def test_slug(hass, caplog):
|
||||
assert result is None
|
||||
assert "Syntax error in" in caplog.text
|
||||
|
||||
result = slug(hass, "http://example.com:999999999999/stream")
|
||||
assert result is None
|
||||
assert "Syntax error in" in caplog.text
|
||||
|
||||
|
||||
@respx.mock
|
||||
async def test_options_only_stream(hass, fakeimgbytes_png, mock_create_stream):
|
||||
|
Loading…
x
Reference in New Issue
Block a user