mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-04 19:11:14 +03:00
try both onvif WS-Security password encodings when probing in the add camera wizard
This commit is contained in:
parent
08be019bed
commit
418c701f24
@ -529,6 +529,68 @@ def _extract_fps(r_frame_rate: str) -> float | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _build_digest_transport(username: str, password: str) -> AsyncTransport:
|
||||||
|
"""Build a zeep transport backed by an httpx client using HTTP digest auth."""
|
||||||
|
auth = httpx.DigestAuth(username, password)
|
||||||
|
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
||||||
|
return AsyncTransport(client=client)
|
||||||
|
|
||||||
|
|
||||||
|
async def _connect_onvif_camera(
|
||||||
|
host: str,
|
||||||
|
port: int,
|
||||||
|
username: str,
|
||||||
|
password: str,
|
||||||
|
wsdl_base: str | None,
|
||||||
|
auth_type: str,
|
||||||
|
) -> ONVIFCamera:
|
||||||
|
"""Connect to an ONVIF device, trying both WS-Security password encodings.
|
||||||
|
|
||||||
|
Cameras disagree on whether the WS-Security UsernameToken should carry a
|
||||||
|
hashed PasswordDigest or a plaintext PasswordText. The wizard can't know
|
||||||
|
which a given camera expects, so we try PasswordDigest first (the common
|
||||||
|
case) and fall back to PasswordText when the device rejects the token. This
|
||||||
|
is independent of auth_type, which controls HTTP transport-level auth.
|
||||||
|
"""
|
||||||
|
first_error: Fault | None = None
|
||||||
|
|
||||||
|
# encrypt=True -> PasswordDigest, encrypt=False -> PasswordText
|
||||||
|
for encrypt in (True, False):
|
||||||
|
onvif_camera = ONVIFCamera(
|
||||||
|
host,
|
||||||
|
port,
|
||||||
|
username or "",
|
||||||
|
password or "",
|
||||||
|
wsdl_dir=wsdl_base,
|
||||||
|
encrypt=encrypt,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await onvif_camera.update_xaddrs()
|
||||||
|
except Fault as e:
|
||||||
|
# A SOAP fault here is how a camera signals the wrong password
|
||||||
|
# encoding, so retry with the other encoding before giving up.
|
||||||
|
logger.debug(
|
||||||
|
"ONVIF connect with %s rejected, trying alternate encoding",
|
||||||
|
"PasswordDigest" if encrypt else "PasswordText",
|
||||||
|
)
|
||||||
|
if first_error is None:
|
||||||
|
first_error = e
|
||||||
|
continue
|
||||||
|
|
||||||
|
if auth_type == "digest" and username and password:
|
||||||
|
transport = _build_digest_transport(username, password)
|
||||||
|
for service in ("devicemgmt", "media", "ptz"):
|
||||||
|
if hasattr(onvif_camera, service):
|
||||||
|
getattr(onvif_camera, service).zeep_client.transport = transport
|
||||||
|
logger.debug("Configured digest authentication")
|
||||||
|
|
||||||
|
return onvif_camera
|
||||||
|
|
||||||
|
# Both encodings failed authentication; surface the original fault.
|
||||||
|
raise first_error
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/onvif/probe",
|
"/onvif/probe",
|
||||||
dependencies=[Depends(require_role(["admin"]))],
|
dependencies=[Depends(require_role(["admin"]))],
|
||||||
@ -605,34 +667,10 @@ async def onvif_probe(
|
|||||||
except Exception:
|
except Exception:
|
||||||
wsdl_base = None
|
wsdl_base = None
|
||||||
|
|
||||||
onvif_camera = ONVIFCamera(
|
onvif_camera = await _connect_onvif_camera(
|
||||||
host, port, username or "", password or "", wsdl_dir=wsdl_base
|
host, port, username, password, wsdl_base, auth_type
|
||||||
)
|
)
|
||||||
|
|
||||||
# Configure digest authentication if requested
|
|
||||||
if auth_type == "digest" and username and password:
|
|
||||||
# Create httpx client with digest auth
|
|
||||||
auth = httpx.DigestAuth(username, password)
|
|
||||||
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
|
||||||
|
|
||||||
# Replace the transport in the zeep client
|
|
||||||
transport = AsyncTransport(client=client)
|
|
||||||
|
|
||||||
# Update the xaddr before setting transport
|
|
||||||
await onvif_camera.update_xaddrs()
|
|
||||||
|
|
||||||
# Replace transport in all services
|
|
||||||
if hasattr(onvif_camera, "devicemgmt"):
|
|
||||||
onvif_camera.devicemgmt.zeep_client.transport = transport
|
|
||||||
if hasattr(onvif_camera, "media"):
|
|
||||||
onvif_camera.media.zeep_client.transport = transport
|
|
||||||
if hasattr(onvif_camera, "ptz"):
|
|
||||||
onvif_camera.ptz.zeep_client.transport = transport
|
|
||||||
|
|
||||||
logger.debug("Configured digest authentication")
|
|
||||||
else:
|
|
||||||
await onvif_camera.update_xaddrs()
|
|
||||||
|
|
||||||
# Get device information
|
# Get device information
|
||||||
device_info = {
|
device_info = {
|
||||||
"manufacturer": "Unknown",
|
"manufacturer": "Unknown",
|
||||||
@ -644,10 +682,9 @@ async def onvif_probe(
|
|||||||
|
|
||||||
# Update transport for device service if digest auth
|
# Update transport for device service if digest auth
|
||||||
if auth_type == "digest" and username and password:
|
if auth_type == "digest" and username and password:
|
||||||
auth = httpx.DigestAuth(username, password)
|
device_service.zeep_client.transport = _build_digest_transport(
|
||||||
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
username, password
|
||||||
transport = AsyncTransport(client=client)
|
)
|
||||||
device_service.zeep_client.transport = transport
|
|
||||||
|
|
||||||
device_info_resp = await device_service.GetDeviceInformation()
|
device_info_resp = await device_service.GetDeviceInformation()
|
||||||
manufacturer = getattr(device_info_resp, "Manufacturer", None) or (
|
manufacturer = getattr(device_info_resp, "Manufacturer", None) or (
|
||||||
@ -685,10 +722,9 @@ async def onvif_probe(
|
|||||||
|
|
||||||
# Update transport for media service if digest auth
|
# Update transport for media service if digest auth
|
||||||
if auth_type == "digest" and username and password:
|
if auth_type == "digest" and username and password:
|
||||||
auth = httpx.DigestAuth(username, password)
|
media_service.zeep_client.transport = _build_digest_transport(
|
||||||
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
username, password
|
||||||
transport = AsyncTransport(client=client)
|
)
|
||||||
media_service.zeep_client.transport = transport
|
|
||||||
|
|
||||||
profiles = await media_service.GetProfiles()
|
profiles = await media_service.GetProfiles()
|
||||||
profiles_count = len(profiles) if profiles else 0
|
profiles_count = len(profiles) if profiles else 0
|
||||||
@ -720,10 +756,9 @@ async def onvif_probe(
|
|||||||
|
|
||||||
# Update transport for PTZ service if digest auth
|
# Update transport for PTZ service if digest auth
|
||||||
if auth_type == "digest" and username and password:
|
if auth_type == "digest" and username and password:
|
||||||
auth = httpx.DigestAuth(username, password)
|
ptz_service.zeep_client.transport = _build_digest_transport(
|
||||||
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
username, password
|
||||||
transport = AsyncTransport(client=client)
|
)
|
||||||
ptz_service.zeep_client.transport = transport
|
|
||||||
|
|
||||||
# Check if PTZ service is available
|
# Check if PTZ service is available
|
||||||
try:
|
try:
|
||||||
@ -876,10 +911,9 @@ async def onvif_probe(
|
|||||||
|
|
||||||
# Update transport for media service if digest auth
|
# Update transport for media service if digest auth
|
||||||
if auth_type == "digest" and username and password:
|
if auth_type == "digest" and username and password:
|
||||||
auth = httpx.DigestAuth(username, password)
|
media_service.zeep_client.transport = _build_digest_transport(
|
||||||
client = httpx.AsyncClient(auth=auth, timeout=10.0)
|
username, password
|
||||||
transport = AsyncTransport(client=client)
|
)
|
||||||
media_service.zeep_client.transport = transport
|
|
||||||
|
|
||||||
if profiles_count and media_service:
|
if profiles_count and media_service:
|
||||||
for p in profiles or []:
|
for p in profiles or []:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user