apply runtime role changes to jsmpeg streams

The output process never subscribed to config/auth, so role edits from the Roles UI (requires_restart 0) did not reach it and jsmpeg kept streaming cameras that had been removed from a role until restart. The output process now replaces its auth config on config/auth updates, which the camera and birdseye broadcast threads read through the shared config.
This commit is contained in:
Josh Hawkins
2026-10-18 12:16:01 -05:00
parent ab8b41934c
commit 4fbde770d7
2 changed files with 19 additions and 2 deletions
+10 -1
View File
@@ -18,7 +18,7 @@ from ws4py.server.wsgirefserver import (
from frigate.comms.config_updater import ConfigSubscriber
from frigate.comms.detections_updater import DetectionSubscriber, DetectionTypeEnum
from frigate.comms.ws import OriginCheckedWebSocketApplication, WebSocket
from frigate.config import FrigateConfig
from frigate.config import AuthConfig, FrigateConfig
from frigate.config.camera.updater import (
CameraConfigUpdateEnum,
CameraConfigUpdateSubscriber,
@@ -153,6 +153,7 @@ class OutputProcess(FrigateProcess):
],
)
birdseye_config_subscriber = ConfigSubscriber("config/birdseye", exact=True)
auth_config_subscriber = ConfigSubscriber("config/auth", exact=True)
jsmpeg_cameras: dict[str, JsmpegCamera] = {}
birdseye: Birdseye | None = None
@@ -193,6 +194,13 @@ class OutputProcess(FrigateProcess):
self.config.birdseye = birdseye_config
logger.debug("Applied dynamic birdseye config update")
# user changes publish None on this topic, role edits an AuthConfig
_, auth_config = auth_config_subscriber.check_for_update()
if isinstance(auth_config, AuthConfig):
self.config.auth = auth_config
logger.debug("Applied dynamic auth config update")
# drain review updates every iteration, not just when birdseye is
# being consumed, so a dropped end never strands a camera
if birdseye is not None:
@@ -329,6 +337,7 @@ class OutputProcess(FrigateProcess):
config_subscriber.stop()
birdseye_config_subscriber.stop()
auth_config_subscriber.stop()
websocket_server.manager.close_all()
websocket_server.manager.stop()
websocket_server.manager.join()
+9 -1
View File
@@ -3,7 +3,7 @@
import unittest
from types import SimpleNamespace
from frigate.config import FrigateConfig
from frigate.config import AuthConfig, FrigateConfig
from frigate.output.ws_auth import ws_has_camera_access
@@ -55,3 +55,11 @@ class TestWsHasCameraAccess(unittest.TestCase):
self.assertFalse(
ws_has_camera_access(self._make_ws("limited_user"), "birdseye", self.config)
)
def test_replaced_auth_config_revokes_camera(self):
ws = self._make_ws("limited_user")
self.assertTrue(ws_has_camera_access(ws, "front_door", self.config))
self.config.auth = AuthConfig(roles={"limited_user": ["back_door"]})
self.assertFalse(ws_has_camera_access(ws, "front_door", self.config))
self.assertTrue(ws_has_camera_access(ws, "back_door", self.config))