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:
Josh Hawkins
2025-09-12 05:19:29 -06:00
committed by GitHub
parent ba650af6f2
commit ed1e3a7c9a
41 changed files with 2286 additions and 739 deletions
+22
View File
@@ -0,0 +1,22 @@
import { useContext } from "react";
import { AuthContext } from "@/context/auth-context";
import useSWR from "swr";
import { FrigateConfig } from "@/types/frigateConfig";
export function useAllowedCameras() {
const { auth } = useContext(AuthContext);
const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
if (
auth.user?.role === "viewer" ||
auth.user?.role === "admin" ||
!auth.isAuthenticated // anonymous port 5000
) {
// return all cameras
return config?.cameras ? Object.keys(config.cameras) : [];
}
return auth.allowedCameras || [];
}
+11
View File
@@ -0,0 +1,11 @@
import { useContext } from "react";
import { AuthContext } from "@/context/auth-context";
export function useIsCustomRole() {
const { auth } = useContext(AuthContext);
return !(
auth.user?.role === "admin" ||
auth.user?.role == "viewer" ||
!auth.isAuthenticated
);
}
+5 -3
View File
@@ -9,6 +9,7 @@ import { LuConstruction } from "react-icons/lu";
import { MdCategory, MdVideoLibrary } from "react-icons/md";
import { TbFaceId } from "react-icons/tb";
import useSWR from "swr";
import { useIsAdmin } from "./use-is-admin";
export const ID_LIVE = 1;
export const ID_REVIEW = 2;
@@ -24,6 +25,7 @@ export default function useNavigation(
const { data: config } = useSWR<FrigateConfig>("config", {
revalidateOnFocus: false,
});
const isAdmin = useIsAdmin();
return useMemo(
() =>
@@ -70,7 +72,7 @@ export default function useNavigation(
icon: TbFaceId,
title: "menu.faceLibrary",
url: "/faces",
enabled: isDesktop && config?.face_recognition.enabled,
enabled: isDesktop && config?.face_recognition.enabled && isAdmin,
},
{
id: ID_CLASSIFICATION,
@@ -78,9 +80,9 @@ export default function useNavigation(
icon: MdCategory,
title: "menu.classification",
url: "/classification",
enabled: isDesktop,
enabled: isDesktop && isAdmin,
},
] as NavData[],
[config?.face_recognition?.enabled, variant],
[config?.face_recognition?.enabled, variant, isAdmin],
);
}