mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-31 08:09:02 +03:00
Merge remote-tracking branch 'origin/master' into dev
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
CI / AMD64 Build (push) Has been cancelled
CI / ARM Build (push) Has been cancelled
CI / Jetson Jetpack 6 (push) Has been cancelled
CI / AMD64 Extra Build (push) Has been cancelled
CI / ARM Extra Build (push) Has been cancelled
CI / Synaptics Build (push) Has been cancelled
CI / Assemble and push default build (push) Has been cancelled
Resolve conflicts in the export pipeline where dev's job-queue refactor met master's chapter-metadata and security work. - Unify chapter support under ChaptersEnum (none / recording_segments / review_items); the realtime stream-copy export selects the per-segment or per-review-item builder by the camera's configured mode. Thread chapters through ExportRecordingsBody -> _build_export_job -> ExportJob -> RecordingExporter. - Keep master's creation_time/comment export metadata and fix a video_path duplication the textual merge introduced in the preview command. - Move the chapters request field to ExportRecordingsBody (the single export endpoint) where it is actually honored. Restore security fixes the automatic merge would have reverted: - frigate/util/services.py: restore the #23493 rename to the public is_go2rtc_arbitrary_exec_allowed so create_config.py's dynamic-source exec guard imports and runs (the merge otherwise left a broken import). - Preserve the export image-path ".." traversal check inside _sanitize_existing_image, applied to single/custom/batch exports. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,16 @@ class TestCheckWsAuthorization(unittest.TestCase):
|
||||
|
||||
DEFAULT_SEPARATOR = ","
|
||||
|
||||
# admin/viewer are reserved and always map to all cameras (empty list);
|
||||
# custom roles map to a specific set of cameras.
|
||||
ROLES_CONFIG = {
|
||||
"admin": [],
|
||||
"viewer": [],
|
||||
"yard": ["front_door", "backyard"],
|
||||
"garage_only": ["garage"],
|
||||
}
|
||||
CAMERA_NAMES = {"front_door", "backyard", "garage"}
|
||||
|
||||
# --- IPC topic blocking (unconditional, regardless of role) ---
|
||||
|
||||
def test_ipc_topic_blocked_for_admin(self):
|
||||
@@ -161,6 +171,124 @@ class TestCheckWsAuthorization(unittest.TestCase):
|
||||
_check_ws_authorization("onConnect", None, self.DEFAULT_SEPARATOR)
|
||||
)
|
||||
|
||||
# --- Camera-scoped PTZ access (non-admin with camera access) ---
|
||||
|
||||
def test_viewer_can_ptz_camera_with_access(self):
|
||||
# viewer maps to all cameras, so PTZ is allowed
|
||||
self.assertTrue(
|
||||
_check_ws_authorization(
|
||||
"front_door/ptz",
|
||||
"viewer",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_custom_role_can_ptz_assigned_camera(self):
|
||||
self.assertTrue(
|
||||
_check_ws_authorization(
|
||||
"front_door/ptz",
|
||||
"yard",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_custom_role_blocked_from_ptz_unassigned_camera(self):
|
||||
self.assertFalse(
|
||||
_check_ws_authorization(
|
||||
"garage/ptz",
|
||||
"yard",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_multiple_roles_union_camera_access_for_ptz(self):
|
||||
# "yard" covers front_door/backyard, "garage_only" covers garage
|
||||
self.assertTrue(
|
||||
_check_ws_authorization(
|
||||
"garage/ptz",
|
||||
"yard,garage_only",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_unknown_role_blocked_from_ptz(self):
|
||||
self.assertFalse(
|
||||
_check_ws_authorization(
|
||||
"front_door/ptz",
|
||||
"nonexistent",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_no_role_header_treated_as_viewer_for_ptz(self):
|
||||
# proxy-only / auth-disabled setups default to the viewer role
|
||||
self.assertTrue(
|
||||
_check_ws_authorization(
|
||||
"front_door/ptz",
|
||||
None,
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_camera_access_does_not_grant_set_commands(self):
|
||||
# camera access enables PTZ only, not config-changing "set" commands
|
||||
self.assertFalse(
|
||||
_check_ws_authorization(
|
||||
"front_door/detect/set",
|
||||
"yard",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_ptz_autotracker_stays_admin_only(self):
|
||||
# ptz_autotracker is a config toggle, not a live-view action
|
||||
self.assertFalse(
|
||||
_check_ws_authorization(
|
||||
"front_door/ptz_autotracker/set",
|
||||
"viewer",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_admin_can_ptz_any_camera_with_config(self):
|
||||
self.assertTrue(
|
||||
_check_ws_authorization(
|
||||
"garage/ptz",
|
||||
"admin",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
def test_ipc_topic_still_blocked_with_camera_access(self):
|
||||
# IPC topics are blocked unconditionally, even with camera access
|
||||
self.assertFalse(
|
||||
_check_ws_authorization(
|
||||
UPDATE_CAMERA_ACTIVITY,
|
||||
"viewer",
|
||||
self.DEFAULT_SEPARATOR,
|
||||
self.ROLES_CONFIG,
|
||||
self.CAMERA_NAMES,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user