mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-09-30 20:06:51 +03:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
743fef2782 | ||
|
|
17e1d5e094 |
+12
-17
@@ -127,23 +127,18 @@ def require_admin_by_default():
|
||||
if path.startswith(EXEMPT_PREFIXES):
|
||||
return
|
||||
|
||||
# Dynamic camera path exemption:
|
||||
# Any path whose first segment matches a configured camera name should
|
||||
# bypass the global admin requirement. These endpoints enforce access
|
||||
# via route-level dependencies (e.g. require_camera_access) to ensure
|
||||
# per-camera authorization. This allows non-admin authenticated users
|
||||
# (e.g. viewer role) to access camera-specific resources without
|
||||
# needing admin privileges.
|
||||
try:
|
||||
if path.startswith("/"):
|
||||
first_segment = path.split("/", 2)[1]
|
||||
if (
|
||||
first_segment
|
||||
and first_segment in request.app.frigate_config.cameras
|
||||
):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
# Camera routes enforce per-camera access via route-level dependencies
|
||||
# (e.g. require_camera_access). Match on the route template, not the raw
|
||||
# path, so a camera named like another namespace (e.g. "faces") can't
|
||||
# waive the admin check for that namespace's routes.
|
||||
route = request.scope.get("route")
|
||||
if (
|
||||
route is not None
|
||||
and route.path.startswith("/{camera_name}")
|
||||
and request.path_params.get("camera_name")
|
||||
in request.app.frigate_config.cameras
|
||||
):
|
||||
return
|
||||
|
||||
# For all other paths, require admin role
|
||||
# Internal port requests have admin role set automatically
|
||||
|
||||
@@ -144,7 +144,9 @@ class BaseTestHttp(unittest.TestCase):
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def create_app(self, stats=None, event_metadata_publisher=None):
|
||||
def create_app(
|
||||
self, stats=None, event_metadata_publisher=None, enforce_default_admin=False
|
||||
):
|
||||
from frigate.api.auth import get_allowed_cameras_for_filter, get_current_user
|
||||
|
||||
app = create_fastapi_app(
|
||||
@@ -158,7 +160,7 @@ class BaseTestHttp(unittest.TestCase):
|
||||
event_metadata_publisher,
|
||||
None,
|
||||
DebugReplayManager(),
|
||||
enforce_default_admin=False,
|
||||
enforce_default_admin=enforce_default_admin,
|
||||
)
|
||||
|
||||
# Default test mocks for authentication
|
||||
|
||||
@@ -47,6 +47,25 @@ class TestHttpApp(BaseTestHttp):
|
||||
assert response.status_code == 200
|
||||
assert response.json()["front_door"]["usage_percent"] == 25.0
|
||||
|
||||
def test_camera_name_collision_keeps_admin_default(self):
|
||||
self.minimal_config["cameras"]["faces"] = self.minimal_config["cameras"].pop(
|
||||
"front_door"
|
||||
)
|
||||
app = super().create_app(enforce_default_admin=True)
|
||||
viewer = {"remote-user": "viewer", "remote-role": "viewer"}
|
||||
|
||||
with AuthTestClient(app) as client:
|
||||
assert client.get("/faces", headers=viewer).status_code == 403
|
||||
assert client.get("/faces").status_code == 200
|
||||
assert (
|
||||
client.post("/faces/train/person/classify", headers=viewer).status_code
|
||||
== 403
|
||||
)
|
||||
|
||||
# Camera routes for the same name stay reachable by viewers
|
||||
response = client.get("/faces/recordings/summary", headers=viewer)
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_config_set_in_memory_replaces_objects_track_list(self):
|
||||
self.minimal_config["cameras"]["front_door"]["objects"] = {
|
||||
"track": ["person", "car"],
|
||||
|
||||
Reference in New Issue
Block a user