mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-25 16:04:41 +03:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2f1a6b87e | ||
|
|
5eb5d662f5 |
+16
-7
@@ -45,6 +45,7 @@ from frigate.genai.prompts import (
|
||||
)
|
||||
from frigate.genai.utils import build_assistant_message_for_conversation
|
||||
from frigate.jobs.vlm_watch import (
|
||||
VLMWatchJob,
|
||||
get_vlm_watch_job,
|
||||
start_vlm_watch_job,
|
||||
stop_vlm_watch_job,
|
||||
@@ -765,7 +766,7 @@ async def _execute_tool_internal(
|
||||
elif tool_name == "start_camera_watch":
|
||||
return await _execute_start_camera_watch(request, arguments)
|
||||
elif tool_name == "stop_camera_watch":
|
||||
return _execute_stop_camera_watch()
|
||||
return _execute_stop_camera_watch(request)
|
||||
elif tool_name == "get_profile_status":
|
||||
return _execute_get_profile_status(request)
|
||||
elif tool_name == "get_recap":
|
||||
@@ -819,6 +820,7 @@ async def _execute_start_camera_watch(
|
||||
dispatcher=request.app.dispatcher,
|
||||
labels=labels,
|
||||
zones=zones,
|
||||
username=request.headers.get("remote-user", ""),
|
||||
)
|
||||
except RuntimeError as e:
|
||||
logger.exception("Failed to start VLM watch job: %s", e)
|
||||
@@ -834,7 +836,18 @@ async def _execute_start_camera_watch(
|
||||
}
|
||||
|
||||
|
||||
def _execute_stop_camera_watch() -> dict[str, Any]:
|
||||
def _can_cancel_watch_job(request: Request, job: VLMWatchJob) -> bool:
|
||||
"""Admins can cancel any watch job; other users only jobs they started."""
|
||||
role = request.headers.get("remote-role", "viewer")
|
||||
username = request.headers.get("remote-user", "")
|
||||
return role == "admin" or username == job.username
|
||||
|
||||
|
||||
def _execute_stop_camera_watch(request: Request) -> dict[str, Any]:
|
||||
job = get_vlm_watch_job()
|
||||
if job is not None and not _can_cancel_watch_job(request, job):
|
||||
return {"success": False, "message": "Not authorized to cancel this watch job."}
|
||||
|
||||
cancelled = stop_vlm_watch_job()
|
||||
if cancelled:
|
||||
return {"success": True, "message": "Watch job cancelled."}
|
||||
@@ -1548,11 +1561,7 @@ async def cancel_vlm_monitor(request: Request) -> JSONResponse:
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
role = request.headers.get("remote-role", "viewer")
|
||||
username = request.headers.get("remote-user", "")
|
||||
|
||||
# Admin can cancel any job; other users can only cancel jobs they started.
|
||||
if role != "admin" and username != job.username:
|
||||
if not _can_cancel_watch_job(request, job):
|
||||
return JSONResponse(
|
||||
content={
|
||||
"success": False,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Tests for camera watch ownership in the chat tools."""
|
||||
|
||||
import asyncio
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from frigate.api.chat import _execute_start_camera_watch, _execute_stop_camera_watch
|
||||
from frigate.jobs.vlm_watch import VLMWatchJob
|
||||
|
||||
|
||||
def _request(user: str, role: str) -> SimpleNamespace:
|
||||
return SimpleNamespace(headers={"remote-user": user, "remote-role": role})
|
||||
|
||||
|
||||
class TestStopCameraWatch(unittest.TestCase):
|
||||
def setUp(self):
|
||||
job = VLMWatchJob(camera="front", condition="person", username="alice")
|
||||
patch("frigate.api.chat.get_vlm_watch_job", return_value=job).start()
|
||||
self.stop = patch(
|
||||
"frigate.api.chat.stop_vlm_watch_job", return_value=True
|
||||
).start()
|
||||
self.addCleanup(patch.stopall)
|
||||
|
||||
def test_other_user_cannot_cancel(self):
|
||||
result = _execute_stop_camera_watch(_request("bob", "viewer"))
|
||||
|
||||
self.assertFalse(result["success"])
|
||||
self.stop.assert_not_called()
|
||||
|
||||
def test_creator_can_cancel(self):
|
||||
result = _execute_stop_camera_watch(_request("alice", "viewer"))
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
self.stop.assert_called_once()
|
||||
|
||||
def test_admin_can_cancel(self):
|
||||
result = _execute_stop_camera_watch(_request("bob", "admin"))
|
||||
|
||||
self.assertTrue(result["success"])
|
||||
self.stop.assert_called_once()
|
||||
|
||||
|
||||
class TestStartCameraWatch(unittest.TestCase):
|
||||
@patch("frigate.api.chat.require_camera_access", new_callable=AsyncMock)
|
||||
@patch("frigate.api.chat.start_vlm_watch_job", return_value="job1")
|
||||
def test_records_caller_as_owner(self, start, _access):
|
||||
request = _request("alice", "viewer")
|
||||
request.app = MagicMock()
|
||||
request.app.frigate_config.cameras = {"front": None}
|
||||
request.app.genai_manager.chat_client.supports_vision = True
|
||||
|
||||
asyncio.run(
|
||||
_execute_start_camera_watch(
|
||||
request, {"camera": "front", "condition": "person"}
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(start.call_args.kwargs["username"], "alice")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user