8000 Use credentials_hash for smartcamera rtsp url (#1293) · ryenitcher/python-kasa@652b4e0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 652b4e0

Browse files
authored
Use credentials_hash for smartcamera rtsp url (python-kasa#1293)
1 parent f2ba233 commit 652b4e0

File tree

2 files changed

+60
-9
lines changed

2 files changed

+60
-9
lines changed

kasa/smartcamera/modules/camera.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
from __future__ import annotations
44

5+
import base64
6+
import logging
57
from urllib.parse import quote_plus
68

79
from ...credentials import Credentials
810
from ...device_type import DeviceType
911
from ...feature import Feature
12+
from ...json import loads as json_loads
1013
from ..smartcameramodule import SmartCameraModule
1114

15+
_LOGGER = logging.getLogger(__name__)
16+
1217
LOCAL_STREAMING_PORT = 554
1318

1419

@@ -38,6 +43,27 @@ def is_on(self) -> bool:
3843
"""Return the device id."""
3944
return self.data["lens_mask_info"]["enabled"] == "off"
4045

46+
def _get_credentials(self) -> Credentials | None:
47+
"""Get credentials from ."""
48+
config = self._device.config
49+
if credentials := config.credentials:
50+
return credentials
51+
52+
if credentials_hash := config.credentials_hash:
53+
try:
54+
decoded = json_loads(
55+
base64.b64decode(credentials_hash.encode()).decode()
56+
)
57+
except Exception:
58+
_LOGGER.warning(
59+
"Unable to deserialize credentials_hash: %s", credentials_hash
60+
)
61+
return None
62+
if (username := decoded.get("un")) and (password := decoded.get("pwd")):
63+
return Credentials(username, password)
64+
65+
return None
66+
4167
def stream_rtsp_url(self, credentials: Credentials | None = None) -> str | None:
4268
"""Return the local rtsp streaming url.
4369
@@ -51,7 +77,8 @@ def stream_rtsp_url(self, credentials: Credentials | None = None) -> str | None:
5177
return None
5278
dev = self._device
5379
if not credentials:
54-
credentials = dev.credentials
80+
credentials = self._get_credentials()
81+
5582
if not credentials or not credentials.username or not credentials.password:
5683
return None
5784
username = quote_plus(credentials.username)

tests/smartcamera/test_smartcamera.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
import base64
6+
import json
57
from datetime import UTC, datetime
68
from unittest.mock import patch
79

@@ -35,17 +37,41 @@ async def test_stream_rtsp_url(dev: Device):
3537
url = camera_module.stream_rtsp_url(Credentials("foo", "bar"))
3638
assert url == "rtsp://foo:bar@127.0.0.123:554/stream1"
3739

38-
with patch.object(
39-
dev.protocol._transport, "_credentials", Credentials("bar", "foo")
40-
):
40+
with patch.object(dev.config, "credentials", Credentials("bar", "foo")):
4141
url = camera_module.stream_rtsp_url()
4242
assert url == "rtsp://bar:foo@127.0.0.123:554/stream1"
4343

44-
with patch.object(dev.protocol._transport, "_credentials", Credentials("bar", "")):
44+
with patch.object(dev.config, "credentials", Credentials("bar", "")):
4545
url = camera_module.stream_rtsp_url()
4646
assert url is None
4747

48-
with patch.object(dev.protocol._transport, "_credentials", Credentials("", "Foo")):
48+
with patch.object(dev.config, "credentials", Credentials("", "Foo")):
49+
url = camera_module.stream_rtsp_url()
50+
assert url is None
51+
52+
# Test with credentials_hash
53+
cred = json.dumps({"un": "bar", "pwd": "foobar"})
54+
cred_hash = base64.b64encode(cred.encode()).decode()
55+
with (
56+
patch.object(dev.config, "credentials", None),
57+
patch.object(dev.config, "credentials_hash", cred_hash),
58+
):
59+
url = camera_module.stream_rtsp_url()
60+
assert url == "rtsp://bar:foobar@127.0.0.123:554/stream1"
61+
62+
# Test with invalid credentials_hash
63+
with (
64+
patch.object(dev.config, "credentials", None),
65+
patch.object(dev.config, "credentials_hash", b"238472871"),
66+
):
67+
url = camera_module.stream_rtsp_url()
68+
assert url is None
69+
70+
# Test with no credentials
71+
with (
72+
patch.object(dev.config, "credentials", None),
73+
patch.object(dev.config, "credentials_hash", None),
74+
):
4975
url = camera_module.stream_rtsp_url()
5076
assert url is None
5177

@@ -54,9 +80,7 @@ async def test_stream_rtsp_url(dev: Device):
5480
await dev.update()
5581
url = camera_module.stream_rtsp_url(Credentials("foo", "bar"))
5682
assert url is None
57-
with patch.object(
58-
dev.protocol._transport, "_credentials", Credentials("bar", "foo")
59-
):
83+
with patch.object(dev.config, "credentials", Credentials("bar", "foo")):
6084
url = camera_module.stream_rtsp_url()
6185
assert url is None
6286

0 commit comments

Comments
 (0)
0