mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-02 17:12:16 +03:00
Enhance user roles to limit camera access (#20024)
* update config for roles and add validator * ensure admin and viewer are never overridden * add class method to user to retrieve all allowed cameras * enforce config roles in auth api endpoints * add camera access api dependency functions * protect review endpoints * protect preview endpoints * rename param name for better fastapi injection matching * remove unneeded * protect export endpoints * protect event endpoints * protect media endpoints * update auth hook for allowed cameras * update default app view * ensure anonymous user always returns all cameras * limit cameras in explore * cameras is already a list * limit cameras in review/history * limit cameras in live view * limit cameras in camera groups * only show face library and classification in sidebar for admin * remove check in delete reviews since admin role is required, no need to check camera access. fixes failing test * pass request with camera access for tests * more async * camera access tests * fix proxy auth tests * allowed cameras for review tests * combine event tests and refactor for camera access * fix post validation for roles * don't limit roles in create user dialog * fix triggers endpoints no need to run require camera access dep since the required role is admin * fix type * create and edit role dialogs * delete role dialog * fix role change dialog * update settings view for roles * i18n changes * minor spacing tweaks * docs * use badges and camera name label component * clarify docs * display all cameras badge for admin and viewer * i18n fix * use validator to prevent reserved and empty roles from being assigned * split users and roles into separate tabs in settings * tweak docs * clarify docs * change icon * don't memoize roles always recalculate on component render
This commit is contained in:
+11
-3
@@ -13,10 +13,13 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import useSWR from "swr";
|
||||
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
||||
import { useIsCustomRole } from "@/hooks/use-is-custom-role";
|
||||
|
||||
function Live() {
|
||||
const { t } = useTranslation(["views/live"]);
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const isCustomRole = useIsCustomRole();
|
||||
|
||||
// selection
|
||||
|
||||
@@ -81,19 +84,22 @@ function Live() {
|
||||
|
||||
// settings
|
||||
|
||||
const allowedCameras = useAllowedCameras();
|
||||
|
||||
const includesBirdseye = useMemo(() => {
|
||||
if (
|
||||
config &&
|
||||
Object.keys(config.camera_groups).length &&
|
||||
cameraGroup &&
|
||||
config.camera_groups[cameraGroup] &&
|
||||
cameraGroup != "default"
|
||||
cameraGroup != "default" &&
|
||||
(!isCustomRole || "birdseye" in allowedCameras)
|
||||
) {
|
||||
return config.camera_groups[cameraGroup].cameras.includes("birdseye");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}, [config, cameraGroup]);
|
||||
}, [config, cameraGroup, allowedCameras, isCustomRole]);
|
||||
|
||||
const cameras = useMemo(() => {
|
||||
if (!config) {
|
||||
@@ -111,13 +117,15 @@ function Live() {
|
||||
.filter(
|
||||
(conf) => conf.enabled_in_config && group.cameras.includes(conf.name),
|
||||
)
|
||||
.filter((cam) => allowedCameras.includes(cam.name))
|
||||
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||
}
|
||||
|
||||
return Object.values(config.cameras)
|
||||
.filter((conf) => conf.ui.dashboard && conf.enabled_in_config)
|
||||
.filter((cam) => allowedCameras.includes(cam.name))
|
||||
.sort((aConf, bConf) => aConf.ui.order - bConf.ui.order);
|
||||
}, [config, cameraGroup]);
|
||||
}, [config, cameraGroup, allowedCameras]);
|
||||
|
||||
const selectedCamera = useMemo(
|
||||
() => cameras.find((cam) => cam.name == selectedCameraName),
|
||||
|
||||
@@ -33,7 +33,8 @@ import CameraSettingsView from "@/views/settings/CameraSettingsView";
|
||||
import ObjectSettingsView from "@/views/settings/ObjectSettingsView";
|
||||
import MotionTunerView from "@/views/settings/MotionTunerView";
|
||||
import MasksAndZonesView from "@/views/settings/MasksAndZonesView";
|
||||
import AuthenticationView from "@/views/settings/AuthenticationView";
|
||||
import UsersView from "@/views/settings/UsersView";
|
||||
import RolesView from "@/views/settings/RolesView";
|
||||
import NotificationView from "@/views/settings/NotificationsSettingsView";
|
||||
import EnrichmentsSettingsView from "@/views/settings/EnrichmentsSettingsView";
|
||||
import UiSettingsView from "@/views/settings/UiSettingsView";
|
||||
@@ -57,6 +58,7 @@ const allSettingsViews = [
|
||||
"triggers",
|
||||
"debug",
|
||||
"users",
|
||||
"roles",
|
||||
"notifications",
|
||||
"frigateplus",
|
||||
] as const;
|
||||
@@ -288,7 +290,8 @@ export default function Settings() {
|
||||
setUnsavedChanges={setUnsavedChanges}
|
||||
/>
|
||||
)}
|
||||
{page == "users" && <AuthenticationView />}
|
||||
{page == "users" && <UsersView />}
|
||||
{page == "roles" && <RolesView />}
|
||||
{page == "notifications" && (
|
||||
<NotificationView setUnsavedChanges={setUnsavedChanges} />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user