mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-19 06:38:21 +03:00
Fix cross-camera auth in timeline and media endpoints (#22522)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* Fix cross-camera authorization bypass in timeline and event media endpoints * formatting
This commit is contained in:
parent
e78da2758d
commit
d11c26970d
@ -732,7 +732,12 @@ def get_recognized_license_plates(
|
||||
|
||||
|
||||
@router.get("/timeline", dependencies=[Depends(allow_any_authenticated())])
|
||||
def timeline(camera: str = "all", limit: int = 100, source_id: Optional[str] = None):
|
||||
def timeline(
|
||||
camera: str = "all",
|
||||
limit: int = 100,
|
||||
source_id: Optional[str] = None,
|
||||
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
|
||||
):
|
||||
clauses = []
|
||||
|
||||
selected_columns = [
|
||||
@ -754,6 +759,9 @@ def timeline(camera: str = "all", limit: int = 100, source_id: Optional[str] = N
|
||||
else:
|
||||
clauses.append((Timeline.source_id.in_(source_ids)))
|
||||
|
||||
# Enforce per-camera access control
|
||||
clauses.append((Timeline.camera << allowed_cameras))
|
||||
|
||||
if len(clauses) == 0:
|
||||
clauses.append((True))
|
||||
|
||||
@ -769,7 +777,10 @@ def timeline(camera: str = "all", limit: int = 100, source_id: Optional[str] = N
|
||||
|
||||
|
||||
@router.get("/timeline/hourly", dependencies=[Depends(allow_any_authenticated())])
|
||||
def hourly_timeline(params: AppTimelineHourlyQueryParameters = Depends()):
|
||||
def hourly_timeline(
|
||||
params: AppTimelineHourlyQueryParameters = Depends(),
|
||||
allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter),
|
||||
):
|
||||
"""Get hourly summary for timeline."""
|
||||
cameras = params.cameras
|
||||
labels = params.labels
|
||||
@ -787,6 +798,9 @@ def hourly_timeline(params: AppTimelineHourlyQueryParameters = Depends()):
|
||||
camera_list = cameras.split(",")
|
||||
clauses.append((Timeline.camera << camera_list))
|
||||
|
||||
# Enforce per-camera access control
|
||||
clauses.append((Timeline.camera << allowed_cameras))
|
||||
|
||||
if labels != "all":
|
||||
label_list = labels.split(",")
|
||||
clauses.append((Timeline.data["label"] << label_list))
|
||||
|
||||
@ -1142,7 +1142,6 @@ async def event_snapshot(
|
||||
|
||||
@router.get(
|
||||
"/events/{event_id}/thumbnail.{extension}",
|
||||
dependencies=[Depends(require_camera_access)],
|
||||
)
|
||||
async def event_thumbnail(
|
||||
request: Request,
|
||||
@ -1344,12 +1343,12 @@ def grid_snapshot(
|
||||
|
||||
@router.get(
|
||||
"/events/{event_id}/snapshot-clean.webp",
|
||||
dependencies=[Depends(require_camera_access)],
|
||||
)
|
||||
def event_snapshot_clean(request: Request, event_id: str, download: bool = False):
|
||||
async def event_snapshot_clean(request: Request, event_id: str, download: bool = False):
|
||||
webp_bytes = None
|
||||
try:
|
||||
event = Event.get(Event.id == event_id)
|
||||
await require_camera_access(event.camera, request=request)
|
||||
snapshot_config = request.app.frigate_config.cameras[event.camera].snapshots
|
||||
if not (snapshot_config.enabled and event.has_snapshot):
|
||||
return JSONResponse(
|
||||
@ -1470,7 +1469,7 @@ def event_snapshot_clean(request: Request, event_id: str, download: bool = False
|
||||
|
||||
|
||||
@router.get(
|
||||
"/events/{event_id}/clip.mp4", dependencies=[Depends(require_camera_access)]
|
||||
"/events/{event_id}/clip.mp4",
|
||||
)
|
||||
async def event_clip(
|
||||
request: Request,
|
||||
@ -1484,6 +1483,8 @@ async def event_clip(
|
||||
content={"success": False, "message": "Event not found"}, status_code=404
|
||||
)
|
||||
|
||||
await require_camera_access(event.camera, request=request)
|
||||
|
||||
if not event.has_clip:
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Clip not available"}, status_code=404
|
||||
@ -1500,9 +1501,9 @@ async def event_clip(
|
||||
|
||||
|
||||
@router.get(
|
||||
"/events/{event_id}/preview.gif", dependencies=[Depends(require_camera_access)]
|
||||
"/events/{event_id}/preview.gif",
|
||||
)
|
||||
def event_preview(request: Request, event_id: str):
|
||||
async def event_preview(request: Request, event_id: str):
|
||||
try:
|
||||
event: Event = Event.get(Event.id == event_id)
|
||||
except DoesNotExist:
|
||||
@ -1510,6 +1511,8 @@ def event_preview(request: Request, event_id: str):
|
||||
content={"success": False, "message": "Event not found"}, status_code=404
|
||||
)
|
||||
|
||||
await require_camera_access(event.camera, request=request)
|
||||
|
||||
start_ts = event.start_time
|
||||
end_ts = start_ts + (
|
||||
min(event.end_time - event.start_time, 20) if event.end_time else 20
|
||||
@ -1854,8 +1857,8 @@ def preview_mp4(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/review/{event_id}/preview", dependencies=[Depends(require_camera_access)])
|
||||
def review_preview(
|
||||
@router.get("/review/{event_id}/preview")
|
||||
async def review_preview(
|
||||
request: Request,
|
||||
event_id: str,
|
||||
format: str = Query(default="gif", enum=["gif", "mp4"]),
|
||||
@ -1868,6 +1871,8 @@ def review_preview(
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
await require_camera_access(review.camera, request=request)
|
||||
|
||||
padding = 8
|
||||
start_ts = review.start_time - padding
|
||||
end_ts = (
|
||||
@ -1881,10 +1886,12 @@ def review_preview(
|
||||
|
||||
|
||||
@router.get(
|
||||
"/preview/{file_name}/thumbnail.jpg", dependencies=[Depends(require_camera_access)]
|
||||
"/preview/{file_name}/thumbnail.jpg",
|
||||
dependencies=[Depends(allow_any_authenticated())],
|
||||
)
|
||||
@router.get(
|
||||
"/preview/{file_name}/thumbnail.webp", dependencies=[Depends(require_camera_access)]
|
||||
"/preview/{file_name}/thumbnail.webp",
|
||||
dependencies=[Depends(allow_any_authenticated())],
|
||||
)
|
||||
def preview_thumbnail(file_name: str):
|
||||
"""Get a thumbnail from the cached preview frames."""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user