mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-17 18:35:19 +02:00
feat: Implement Role-Based Access Control (RBAC) for search space resources.
-Introduce granular permissions for documents, chats, podcasts, and logs. - Update routes to enforce permission checks for creating, reading, updating, and deleting resources. - Refactor user and search space interactions to align with RBAC model, removing ownership checks in favor of permission validation.
This commit is contained in:
parent
1ed0cb3dfe
commit
e9d32c3516
38 changed files with 5916 additions and 657 deletions
|
|
@ -18,6 +18,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
|
|||
import { Separator } from "@/components/ui/separator";
|
||||
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
||||
import { useLLMPreferences } from "@/hooks/use-llm-configs";
|
||||
import { useUserAccess } from "@/hooks/use-rbac";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function DashboardClientLayout({
|
||||
|
|
@ -60,11 +61,15 @@ export function DashboardClientLayout({
|
|||
}, [activeChatId, isChatPannelOpen]);
|
||||
|
||||
const { loading, error, isOnboardingComplete } = useLLMPreferences(searchSpaceIdNum);
|
||||
const { access, loading: accessLoading } = useUserAccess(searchSpaceIdNum);
|
||||
const [hasCheckedOnboarding, setHasCheckedOnboarding] = useState(false);
|
||||
|
||||
// Skip onboarding check if we're already on the onboarding page
|
||||
const isOnboardingPage = pathname?.includes("/onboard");
|
||||
|
||||
// Only owners should see onboarding - invited members use existing config
|
||||
const isOwner = access?.is_owner ?? false;
|
||||
|
||||
// Translate navigation items
|
||||
const tNavMenu = useTranslations("nav_menu");
|
||||
const translatedNavMain = useMemo(() => {
|
||||
|
|
@ -102,11 +107,13 @@ export function DashboardClientLayout({
|
|||
return;
|
||||
}
|
||||
|
||||
// Only check once after preferences have loaded
|
||||
if (!loading && !hasCheckedOnboarding) {
|
||||
// Wait for both preferences and access data to load
|
||||
if (!loading && !accessLoading && !hasCheckedOnboarding) {
|
||||
const onboardingComplete = isOnboardingComplete();
|
||||
|
||||
if (!onboardingComplete) {
|
||||
// Only redirect to onboarding if user is the owner and onboarding is not complete
|
||||
// Invited members (non-owners) should skip onboarding and use existing config
|
||||
if (!onboardingComplete && isOwner) {
|
||||
router.push(`/dashboard/${searchSpaceId}/onboard`);
|
||||
}
|
||||
|
||||
|
|
@ -114,8 +121,10 @@ export function DashboardClientLayout({
|
|||
}
|
||||
}, [
|
||||
loading,
|
||||
accessLoading,
|
||||
isOnboardingComplete,
|
||||
isOnboardingPage,
|
||||
isOwner,
|
||||
router,
|
||||
searchSpaceId,
|
||||
hasCheckedOnboarding,
|
||||
|
|
@ -145,7 +154,7 @@ export function DashboardClientLayout({
|
|||
}, [chat_id, search_space_id]);
|
||||
|
||||
// Show loading screen while checking onboarding status (only on first load)
|
||||
if (!hasCheckedOnboarding && loading && !isOnboardingPage) {
|
||||
if (!hasCheckedOnboarding && (loading || accessLoading) && !isOnboardingPage) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen space-y-4">
|
||||
<Card className="w-[350px] bg-background/60 backdrop-blur-sm">
|
||||
|
|
|
|||
|
|
@ -52,6 +52,12 @@ export default function DashboardLayout({
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
url: `/dashboard/${search_space_id}/team`,
|
||||
icon: "Users",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: `/dashboard/${search_space_id}/settings`,
|
||||
|
|
|
|||
|
|
@ -1126,7 +1126,7 @@ function LogRowActions({ row, t }: { row: Row<Log>; t: (key: string) => string }
|
|||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteLog(log.id);
|
||||
toast.success(t("log_deleted_success"));
|
||||
// toast.success(t("log_deleted_success"));
|
||||
await refreshLogs();
|
||||
} catch (error) {
|
||||
console.error("Error deleting log:", error);
|
||||
|
|
|
|||
1325
surfsense_web/app/dashboard/[search_space_id]/team/page.tsx
Normal file
1325
surfsense_web/app/dashboard/[search_space_id]/team/page.tsx
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import { AlertCircle, Loader2, Plus, Search, Trash2 } from "lucide-react";
|
||||
import { AlertCircle, Loader2, Plus, Search, Trash2, UserCheck, Users } from "lucide-react";
|
||||
import { motion, type Variants } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
|
@ -22,6 +22,7 @@ import {
|
|||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
|
|
@ -308,16 +309,30 @@ const DashboardPage = () => {
|
|||
>
|
||||
<div className="flex flex-1 flex-col justify-between p-1">
|
||||
<div>
|
||||
<h3 className="font-medium text-lg">{space.name}</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-medium text-lg">{space.name}</h3>
|
||||
{!space.is_owner && (
|
||||
<Badge variant="secondary" className="text-xs font-normal">
|
||||
{t("shared")}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{space.description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 text-xs text-muted-foreground">
|
||||
{/* <span>{space.title}</span> */}
|
||||
<div className="mt-4 flex items-center justify-between text-xs text-muted-foreground">
|
||||
<span>
|
||||
{t("created")} {formatDate(space.created_at)}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
{space.is_owner ? (
|
||||
<UserCheck className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<Users className="h-3.5 w-3.5" />
|
||||
)}
|
||||
<span>{space.member_count}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue