mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-03 05:16:50 +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:
@@ -1,5 +1,5 @@
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Button } from "../ui/button";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -7,22 +7,23 @@ import {
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../ui/dialog";
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../ui/select";
|
||||
} from "@/components/ui/select";
|
||||
import { useState } from "react";
|
||||
import { LuShield, LuUser } from "react-icons/lu";
|
||||
|
||||
type RoleChangeDialogProps = {
|
||||
show: boolean;
|
||||
username: string;
|
||||
currentRole: "admin" | "viewer";
|
||||
onSave: (role: "admin" | "viewer") => void;
|
||||
currentRole: string;
|
||||
availableRoles: string[];
|
||||
onSave: (role: string) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
@@ -30,13 +31,12 @@ export default function RoleChangeDialog({
|
||||
show,
|
||||
username,
|
||||
currentRole,
|
||||
availableRoles,
|
||||
onSave,
|
||||
onCancel,
|
||||
}: RoleChangeDialogProps) {
|
||||
const { t } = useTranslation(["views/settings"]);
|
||||
const [selectedRole, setSelectedRole] = useState<"admin" | "viewer">(
|
||||
currentRole,
|
||||
);
|
||||
const [selectedRole, setSelectedRole] = useState<string>(currentRole);
|
||||
|
||||
return (
|
||||
<Dialog open={show} onOpenChange={onCancel}>
|
||||
@@ -73,31 +73,46 @@ export default function RoleChangeDialog({
|
||||
</span>
|
||||
: {t("users.dialog.changeRole.roleInfo.viewerDesc")}
|
||||
</li>
|
||||
{availableRoles
|
||||
.filter((role) => role !== "admin" && role !== "viewer")
|
||||
.map((role) => (
|
||||
<li key={role}>
|
||||
<span className="font-medium">{role}</span>:{" "}
|
||||
{t("users.dialog.changeRole.roleInfo.customDesc")}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
value={selectedRole}
|
||||
onValueChange={(value) =>
|
||||
setSelectedRole(value as "admin" | "viewer")
|
||||
}
|
||||
>
|
||||
<Select value={selectedRole} onValueChange={setSelectedRole}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder={t("users.dialog.changeRole.select")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="admin" className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<LuShield className="size-4 text-primary" />
|
||||
<span>{t("role.admin", { ns: "common" })}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="viewer" className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<LuUser className="size-4 text-primary" />
|
||||
<span>{t("role.viewer", { ns: "common" })}</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
{availableRoles.map((role) => (
|
||||
<SelectItem
|
||||
key={role}
|
||||
value={role}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{role === "admin" ? (
|
||||
<LuShield className="size-4 text-primary" />
|
||||
) : role === "viewer" ? (
|
||||
<LuUser className="size-4 text-primary" />
|
||||
) : (
|
||||
<LuUser className="size-4 text-muted-foreground" />
|
||||
)}
|
||||
<span>
|
||||
{role === "admin"
|
||||
? t("role.admin", { ns: "common" })
|
||||
: role === "viewer"
|
||||
? t("role.viewer", { ns: "common" })
|
||||
: role}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -108,6 +123,7 @@ export default function RoleChangeDialog({
|
||||
<Button
|
||||
className="flex flex-1"
|
||||
aria-label={t("button.cancel", { ns: "common" })}
|
||||
variant="outline"
|
||||
onClick={onCancel}
|
||||
type="button"
|
||||
>
|
||||
@@ -118,6 +134,7 @@ export default function RoleChangeDialog({
|
||||
aria-label={t("button.save", { ns: "common" })}
|
||||
className="flex flex-1"
|
||||
onClick={() => onSave(selectedRole)}
|
||||
type="button"
|
||||
disabled={selectedRole === currentRole}
|
||||
>
|
||||
{t("button.save", { ns: "common" })}
|
||||
|
||||
Reference in New Issue
Block a user