uiprotect 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of uiprotect might be problematic. Click here for more details.

@@ -0,0 +1,257 @@
1
+ from __future__ import annotations
2
+
3
+ import secrets
4
+ import string
5
+ import uuid
6
+ from typing import Any
7
+ from urllib.parse import urlparse
8
+
9
+ import typer
10
+
11
+ from uiprotect.data import ModelType
12
+
13
+ object_id_mapping: dict[str, str] = {}
14
+
15
+
16
+ def anonymize_data(value: Any, name: str | None = None) -> Any:
17
+ if isinstance(value, list):
18
+ value = anonymize_list(value, name=name)
19
+ elif isinstance(value, dict):
20
+ value = anonymize_dict(value, name=name)
21
+ else:
22
+ value = anonymize_value(value, name=name)
23
+
24
+ return value
25
+
26
+
27
+ def anonymize_user(user_dict: dict[str, Any]) -> dict[str, Any]:
28
+ for index, group_id in enumerate(user_dict.get("groups", [])):
29
+ user_dict["groups"][index] = anonymize_object_id(group_id)
30
+
31
+ user_dict["id"] = anonymize_object_id(user_dict["id"])
32
+
33
+ if "firstName" in user_dict:
34
+ user_dict["firstName"] = random_word().title()
35
+ user_dict["lastName"] = random_word().title()
36
+ user_dict["name"] = f"{user_dict['firstName']} {user_dict['lastName']}"
37
+ user_dict["localUsername"] = random_word()
38
+ user_dict["email"] = f"{user_dict['localUsername']}@example.com"
39
+
40
+ if "cloudAccount" in user_dict and user_dict["cloudAccount"] is not None:
41
+ user_dict["cloudAccount"]["firstName"] = user_dict["firstName"]
42
+ user_dict["cloudAccount"]["lastName"] = user_dict["lastName"]
43
+ user_dict["cloudAccount"]["name"] = user_dict["name"]
44
+ user_dict["cloudAccount"]["email"] = user_dict["email"]
45
+ user_dict["cloudAccount"]["user"] = anonymize_object_id(
46
+ user_dict["cloudAccount"]["user"],
47
+ )
48
+ user_dict["cloudAccount"]["id"] = anonymize_uuid(
49
+ user_dict["cloudAccount"]["id"],
50
+ )
51
+ user_dict["cloudAccount"]["cloudId"] = anonymize_uuid(
52
+ user_dict["cloudAccount"]["cloudId"],
53
+ )
54
+
55
+ camera_order = (user_dict.get("settings") or {}).get("cameraOrder")
56
+ if camera_order is not None:
57
+ for index, camera_id in enumerate(camera_order):
58
+ camera_order[index] = anonymize_object_id(camera_id)
59
+ user_dict["settings"]["cameraOrder"] = camera_order
60
+
61
+ if "allPermissions" in user_dict:
62
+ user_dict["allPermissions"] = anonymize_list(
63
+ user_dict["allPermissions"],
64
+ "allPermissions",
65
+ )
66
+ if "permissions" in user_dict:
67
+ user_dict["permissions"] = anonymize_list(
68
+ user_dict["permissions"],
69
+ "permissions",
70
+ )
71
+
72
+ return user_dict
73
+
74
+
75
+ def anonymize_value(value: Any, name: str | None = None) -> Any:
76
+ if isinstance(value, str):
77
+ if name == "accessKey":
78
+ value = f"{random_number(13)}:{random_hex(24)}:{random_hex(128)}"
79
+ elif name == "credentials":
80
+ value = f"{random_hex(64)}"
81
+ elif name == "privateToken":
82
+ value = f"{random_alphanum(192)}"
83
+ elif name in {"host", "connectionHost", "bindAddr"}:
84
+ value = anonymize_ip(value)
85
+ elif name in {"anonymousDeviceId", "hardwareId"}:
86
+ value = random_identifier()
87
+ elif name in {"rtspAlias", "ssid"}:
88
+ value = random_alphanum(16)
89
+ elif name in {"mac", "server_id"}:
90
+ value = anonymize_peristent_string(value, random_hex(12).upper())
91
+ elif name == "bssid":
92
+ value = anonymize_peristent_string(value, random_seperated_mac())
93
+ elif name in {"latitude", "longitude"}:
94
+ value = "0.0"
95
+ elif name == "name" and value != "Default":
96
+ value = f"{random_word()} {random_word()}".title()
97
+ elif name in {"owner", "user", "camera", "liveview", "authUserId", "event"}:
98
+ value = anonymize_object_id(value)
99
+ elif name == "rtsp":
100
+ value = anonymize_rstp_url(value)
101
+ elif value.startswith("liveview:*:"):
102
+ liveview_id = value.split(":")[-1]
103
+ value = f"liveview:*:{anonymize_object_id(liveview_id)}"
104
+
105
+ return value
106
+
107
+
108
+ def anonymize_dict(obj: dict[str, Any], name: str | None = None) -> dict[str, Any]:
109
+ obj_type = None
110
+ if "modelKey" in obj:
111
+ if obj["modelKey"] in [m.value for m in ModelType]:
112
+ obj_type = ModelType(obj["modelKey"])
113
+ else:
114
+ typer.secho(f"Unknown modelKey: {obj['modelKey']}", fg="yellow")
115
+
116
+ if obj_type == ModelType.USER:
117
+ return anonymize_user(obj)
118
+
119
+ for key, value in obj.items():
120
+ handled = False
121
+ if obj_type is not None or "payload" in obj:
122
+ if key == "id":
123
+ obj[key] = anonymize_object_id(value)
124
+ handled = True
125
+ elif obj_type == ModelType.EVENT:
126
+ if key in {"thumbnail", "heatmap"}:
127
+ obj[key] = anonymize_prefixed_event_id(value)
128
+ handled = True
129
+ elif key == "metadata":
130
+ if "sensorId" in obj[key]:
131
+ obj[key]["sensorId"]["text"] = anonymize_object_id(
132
+ obj[key]["sensorId"]["text"],
133
+ )
134
+ if "sensorName" in obj[key]:
135
+ obj[key]["sensorName"]["text"] = (
136
+ f"{random_word()} {random_word()}".title()
137
+ )
138
+
139
+ if not handled:
140
+ obj[key] = anonymize_data(value, name=key)
141
+
142
+ return obj
143
+
144
+
145
+ def anonymize_list(items: list[Any], name: str | None = None) -> list[Any]:
146
+ for index, value in enumerate(items):
147
+ handled = False
148
+
149
+ if isinstance(value, str) and name in {
150
+ "hosts",
151
+ "smartDetectEvents",
152
+ "camera",
153
+ "cameras",
154
+ }:
155
+ handled = True
156
+ if name == "hosts":
157
+ items[index] = anonymize_ip(items[index])
158
+ elif name in {"smartDetectEvents", "camera", "cameras"}:
159
+ items[index] = anonymize_object_id(value)
160
+
161
+ if not handled:
162
+ items[index] = anonymize_data(value)
163
+
164
+ return items
165
+
166
+
167
+ def anonymize_prefixed_event_id(event_id: str) -> str:
168
+ event_id = event_id[2:]
169
+
170
+ return f"e-{anonymize_object_id(event_id)}"
171
+
172
+
173
+ def anonymize_ip(ip: Any) -> Any:
174
+ if not isinstance(ip, str):
175
+ return ip
176
+
177
+ if ip in {"0.0.0.0", "127.0.0.1", "255.255.255.255"}: # noqa: S104
178
+ return ip
179
+
180
+ return anonymize_peristent_string(ip, random_ip(ip))
181
+
182
+
183
+ def anonymize_uuid(uuid_str: str) -> str:
184
+ return anonymize_peristent_string(uuid_str, random_identifier())
185
+
186
+
187
+ def anonymize_object_id(obj_id: str) -> str:
188
+ return anonymize_peristent_string(obj_id, random_hex(24))
189
+
190
+
191
+ def anonymize_peristent_string(value: str, default: str) -> str:
192
+ if value not in object_id_mapping:
193
+ object_id_mapping[value] = default
194
+
195
+ return object_id_mapping[value]
196
+
197
+
198
+ def anonymize_rstp_url(url: str) -> str:
199
+ parts = urlparse(url)
200
+ port = ""
201
+ if parts.port is not None and parts.port != 554:
202
+ port = f":{parts.port}"
203
+
204
+ return f"{parts.scheme}://{anonymize_ip(url)}{port}/{random_alphanum(16)}"
205
+
206
+
207
+ def random_hex(length: int) -> str:
208
+ return secrets.token_hex(length // 2)
209
+
210
+
211
+ def random_seperated_mac() -> str:
212
+ return ":".join(random_hex(2) for _ in range(6))
213
+
214
+
215
+ def random_str(length: int, choices: str) -> str:
216
+ return "".join(secrets.choice(choices) for _ in range(length))
217
+
218
+
219
+ def random_number(length: int) -> str:
220
+ return random_str(length, string.digits)
221
+
222
+
223
+ def random_word() -> str:
224
+ return random_char(secrets.randbelow(5) + 3)
225
+
226
+
227
+ def random_char(length: int) -> str:
228
+ return random_str(length, string.ascii_letters)
229
+
230
+
231
+ def random_alphanum(length: int) -> str:
232
+ choices = string.ascii_letters + string.ascii_letters.upper() + string.digits
233
+ return random_str(length, choices)
234
+
235
+
236
+ def random_ip(input_ip: str) -> str:
237
+ ip = ""
238
+
239
+ try:
240
+ octals = [int(i) for i in input_ip.split(".")]
241
+ except ValueError:
242
+ pass
243
+ else:
244
+ if octals[0] == 10:
245
+ ip = f"10.{secrets.randbelow(256)}.{secrets.randbelow(256)}.{secrets.randbelow(256)}"
246
+ elif octals[0] == 172 and 16 <= octals[1] <= 31:
247
+ ip = f"172.{secrets.randbelow(16) + 16}.{secrets.randbelow(256)}.{secrets.randbelow(256)}"
248
+ elif octals[0] == 192 and octals[1] == 168:
249
+ ip = f"192.168.{secrets.randbelow(256)}.{secrets.randbelow(256)}"
250
+
251
+ if not ip:
252
+ ip = f"{secrets.randbelow(255) + 1}.{secrets.randbelow(256)}.{secrets.randbelow(256)}.{secrets.randbelow(256)}"
253
+ return ip
254
+
255
+
256
+ def random_identifier() -> str:
257
+ return str(uuid.uuid4())