mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-08 14:25:41 +03:00
fix camera group access for non admin users
changes from previous PR wrongly included users from the standard viewer role (but excluded custom viewer roles)
This commit is contained in:
parent
5919b56ffb
commit
890ae6ae50
@ -78,7 +78,7 @@ import { useStreamingSettings } from "@/context/streaming-settings-provider";
|
|||||||
import { Trans, useTranslation } from "react-i18next";
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
import { CameraNameLabel } from "../camera/FriendlyNameLabel";
|
import { CameraNameLabel } from "../camera/FriendlyNameLabel";
|
||||||
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
||||||
import { useIsCustomRole } from "@/hooks/use-is-custom-role";
|
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||||
|
|
||||||
type CameraGroupSelectorProps = {
|
type CameraGroupSelectorProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -88,7 +88,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
|
|||||||
const { t } = useTranslation(["components/camera"]);
|
const { t } = useTranslation(["components/camera"]);
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const allowedCameras = useAllowedCameras();
|
const allowedCameras = useAllowedCameras();
|
||||||
const isCustomRole = useIsCustomRole();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
// tooltip
|
// tooltip
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
|
|||||||
const allGroups = Object.entries(config.camera_groups);
|
const allGroups = Object.entries(config.camera_groups);
|
||||||
|
|
||||||
// If custom role, filter out groups where user has no accessible cameras
|
// If custom role, filter out groups where user has no accessible cameras
|
||||||
if (isCustomRole) {
|
if (!isAdmin) {
|
||||||
return allGroups
|
return allGroups
|
||||||
.filter(([, groupConfig]) => {
|
.filter(([, groupConfig]) => {
|
||||||
// Check if user has access to at least one camera in this group
|
// Check if user has access to at least one camera in this group
|
||||||
@ -136,7 +136,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return allGroups.sort((a, b) => a[1].order - b[1].order);
|
return allGroups.sort((a, b) => a[1].order - b[1].order);
|
||||||
}, [config, allowedCameras, isCustomRole]);
|
}, [config, allowedCameras, isAdmin]);
|
||||||
|
|
||||||
// add group
|
// add group
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
|
|||||||
activeGroup={group}
|
activeGroup={group}
|
||||||
setGroup={setGroup}
|
setGroup={setGroup}
|
||||||
deleteGroup={deleteGroup}
|
deleteGroup={deleteGroup}
|
||||||
isCustomRole={isCustomRole}
|
isAdmin={isAdmin}
|
||||||
/>
|
/>
|
||||||
<Scroller className={`${isMobile ? "whitespace-nowrap" : ""}`}>
|
<Scroller className={`${isMobile ? "whitespace-nowrap" : ""}`}>
|
||||||
<div
|
<div
|
||||||
@ -221,7 +221,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{!isCustomRole && (
|
{isAdmin && (
|
||||||
<Button
|
<Button
|
||||||
className="bg-secondary text-muted-foreground"
|
className="bg-secondary text-muted-foreground"
|
||||||
aria-label={t("group.add")}
|
aria-label={t("group.add")}
|
||||||
@ -245,7 +245,7 @@ type NewGroupDialogProps = {
|
|||||||
activeGroup?: string;
|
activeGroup?: string;
|
||||||
setGroup: (value: string | undefined, replace?: boolean | undefined) => void;
|
setGroup: (value: string | undefined, replace?: boolean | undefined) => void;
|
||||||
deleteGroup: () => void;
|
deleteGroup: () => void;
|
||||||
isCustomRole?: boolean;
|
isAdmin?: boolean;
|
||||||
};
|
};
|
||||||
function NewGroupDialog({
|
function NewGroupDialog({
|
||||||
open,
|
open,
|
||||||
@ -254,7 +254,7 @@ function NewGroupDialog({
|
|||||||
activeGroup,
|
activeGroup,
|
||||||
setGroup,
|
setGroup,
|
||||||
deleteGroup,
|
deleteGroup,
|
||||||
isCustomRole,
|
isAdmin,
|
||||||
}: NewGroupDialogProps) {
|
}: NewGroupDialogProps) {
|
||||||
const { t } = useTranslation(["components/camera"]);
|
const { t } = useTranslation(["components/camera"]);
|
||||||
const { mutate: updateConfig } = useSWR<FrigateConfig>("config");
|
const { mutate: updateConfig } = useSWR<FrigateConfig>("config");
|
||||||
@ -390,7 +390,7 @@ function NewGroupDialog({
|
|||||||
>
|
>
|
||||||
<Title>{t("group.label")}</Title>
|
<Title>{t("group.label")}</Title>
|
||||||
<Description className="sr-only">{t("group.edit")}</Description>
|
<Description className="sr-only">{t("group.edit")}</Description>
|
||||||
{!isCustomRole && (
|
{isAdmin && (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute",
|
"absolute",
|
||||||
@ -422,7 +422,7 @@ function NewGroupDialog({
|
|||||||
group={group}
|
group={group}
|
||||||
onDeleteGroup={() => onDeleteGroup(group[0])}
|
onDeleteGroup={() => onDeleteGroup(group[0])}
|
||||||
onEditGroup={() => onEditGroup(group)}
|
onEditGroup={() => onEditGroup(group)}
|
||||||
isReadOnly={isCustomRole}
|
isReadOnly={!isAdmin}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -677,7 +677,7 @@ export function CameraGroupEdit({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const allowedCameras = useAllowedCameras();
|
const allowedCameras = useAllowedCameras();
|
||||||
const isCustomRole = useIsCustomRole();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
const [openCamera, setOpenCamera] = useState<string | null>();
|
const [openCamera, setOpenCamera] = useState<string | null>();
|
||||||
|
|
||||||
@ -867,7 +867,7 @@ export function CameraGroupEdit({
|
|||||||
<FormMessage />
|
<FormMessage />
|
||||||
{[
|
{[
|
||||||
...(birdseyeConfig?.enabled &&
|
...(birdseyeConfig?.enabled &&
|
||||||
(!isCustomRole || "birdseye" in allowedCameras)
|
(isAdmin || "birdseye" in allowedCameras)
|
||||||
? ["birdseye"]
|
? ["birdseye"]
|
||||||
: []),
|
: []),
|
||||||
...Object.keys(config?.cameras ?? {})
|
...Object.keys(config?.cameras ?? {})
|
||||||
|
|||||||
@ -14,12 +14,12 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
||||||
import { useIsCustomRole } from "@/hooks/use-is-custom-role";
|
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||||
|
|
||||||
function Live() {
|
function Live() {
|
||||||
const { t } = useTranslation(["views/live"]);
|
const { t } = useTranslation(["views/live"]);
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const isCustomRole = useIsCustomRole();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
// selection
|
// selection
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ function Live() {
|
|||||||
|
|
||||||
const includesBirdseye = useMemo(() => {
|
const includesBirdseye = useMemo(() => {
|
||||||
// Restricted users should never have access to birdseye
|
// Restricted users should never have access to birdseye
|
||||||
if (isCustomRole) {
|
if (!isAdmin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ function Live() {
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}, [config, cameraGroup, isCustomRole]);
|
}, [config, cameraGroup, isAdmin]);
|
||||||
|
|
||||||
const cameras = useMemo(() => {
|
const cameras = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
|
|||||||
@ -54,7 +54,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { EmptyCard } from "@/components/card/EmptyCard";
|
import { EmptyCard } from "@/components/card/EmptyCard";
|
||||||
import { BsFillCameraVideoOffFill } from "react-icons/bs";
|
import { BsFillCameraVideoOffFill } from "react-icons/bs";
|
||||||
import { AuthContext } from "@/context/auth-context";
|
import { AuthContext } from "@/context/auth-context";
|
||||||
import { useIsCustomRole } from "@/hooks/use-is-custom-role";
|
import { useIsAdmin } from "@/hooks/use-is-admin";
|
||||||
|
|
||||||
type LiveDashboardViewProps = {
|
type LiveDashboardViewProps = {
|
||||||
cameras: CameraConfig[];
|
cameras: CameraConfig[];
|
||||||
@ -661,10 +661,10 @@ export default function LiveDashboardView({
|
|||||||
function NoCameraView() {
|
function NoCameraView() {
|
||||||
const { t } = useTranslation(["views/live"]);
|
const { t } = useTranslation(["views/live"]);
|
||||||
const { auth } = useContext(AuthContext);
|
const { auth } = useContext(AuthContext);
|
||||||
const isCustomRole = useIsCustomRole();
|
const isAdmin = useIsAdmin();
|
||||||
|
|
||||||
// Check if this is a restricted user with no cameras in this group
|
// Check if this is a restricted user with no cameras in this group
|
||||||
const isRestricted = isCustomRole && auth.isAuthenticated;
|
const isRestricted = !isAdmin && auth.isAuthenticated;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full items-center justify-center">
|
<div className="flex size-full items-center justify-center">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user