Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions framework/configstore/rdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2830,8 +2830,6 @@ func (s *RDBConfigStore) GetTeamsPaginated(ctx context.Context, params TeamsQuer
offset := params.Offset
if limit <= 0 {
limit = 25
} else if limit > 100 {
limit = 100
}
Comment thread
impoiler marked this conversation as resolved.
if offset < 0 {
offset = 0
Expand Down
7 changes: 6 additions & 1 deletion ui/app/_fallbacks/enterprise/lib/contexts/rbacContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export enum RbacResource {
ModelProvider = "ModelProvider",
Plugins = "Plugins",
MCPGateway = "MCPGateway",
MCPToolGroups = "MCPToolGroups",
MCPLogs = "MCPLogs",
AdaptiveRouter = "AdaptiveRouter",
AuditLogs = "AuditLogs",
Customers = "Customers",
Expand All @@ -26,6 +28,9 @@ export enum RbacResource {
PromptRepository = "PromptRepository",
PromptDeploymentStrategy = "PromptDeploymentStrategy",
AccessProfiles = "AccessProfiles",
APIKeys = "APIKeys",
Inference = "Inference",
Metrics = "Metrics",
}

// RBAC Operation Names (must match backend definitions)
Expand Down Expand Up @@ -81,4 +86,4 @@ export function useRbacContext() {
};
}
return context;
}
}
14 changes: 10 additions & 4 deletions ui/app/workspace/config/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { createFileRoute, Outlet, useChildMatches } from "@tanstack/react-router";
import { createFileRoute, Outlet, useChildMatches, useLocation } from "@tanstack/react-router";
import FullPageLoader from "@/components/fullPageLoader";
import { NoPermissionView } from "@/components/noPermissionView";
import { useGetCoreConfigQuery } from "@/lib/store";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import ConfigPage from "./page";

function RouteComponent() {
const hasConfigAccess = useRbac(RbacResource.Settings, RbacOperation.View);
const { isLoading } = useGetCoreConfigQuery({ fromDB: true }, { skip: !hasConfigAccess });
const pathname = useLocation({ select: (l) => l.pathname });
const hasSettingsAccess = useRbac(RbacResource.Settings, RbacOperation.View);
const hasAPIKeysAccess = useRbac(RbacResource.APIKeys, RbacOperation.View);
const childMatches = useChildMatches();

if (!hasConfigAccess) {
const isAPIKeysRoute = pathname.startsWith("/workspace/config/api-keys");
const requiredAccess = isAPIKeysRoute ? hasAPIKeysAccess : hasSettingsAccess;

const { isLoading } = useGetCoreConfigQuery({ fromDB: true }, { skip: !requiredAccess });

if (!requiredAccess) {
return <NoPermissionView entity="configuration" />;
}

Expand Down
45 changes: 24 additions & 21 deletions ui/app/workspace/logs/views/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -472,26 +472,29 @@ export const createColumns = (
},
}));

const actionsColumn: ColumnDef<LogEntry> = {
id: "actions",
size: 72,
cell: ({ row }) => {
const log = row.original;
return (
<Button
variant="outline"
size="icon"
data-testid="log-delete-btn"
aria-label="Delete log"
className="text-secondary-foreground/30 hover:bg-destructive/10 hover:text-destructive border-destructive/10"
onClick={() => onDelete(log)}
disabled={!hasDeleteAccess}
>
<Trash2 strokeWidth={1.5} />
</Button>
);
},
};
const actionsColumn: ColumnDef<LogEntry>[] = hasDeleteAccess
? [
{
id: "actions",
size: 72,
cell: ({ row }) => {
const log = row.original;
return (
<Button
variant="outline"
size="icon"
data-testid="log-delete-btn"
aria-label="Delete log"
className="text-destructive/60 border-destructive/60 hover:text-destructive hover:bg-destructive/10"
onClick={() => onDelete(log)}
>
<Trash2 strokeWidth={1.5} />
</Button>
);
},
},
]
: [];

return [...baseColumns, ...metadataColumns, actionsColumn];
return [...baseColumns, ...metadataColumns, ...actionsColumn];
};
12 changes: 11 additions & 1 deletion ui/app/workspace/mcp-logs/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { NoPermissionView } from "@/components/noPermissionView";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import { createFileRoute } from "@tanstack/react-router";
import MCPLogsPage from "./page";

function RouteComponent() {
const hasViewMCPLogsAccess = useRbac(RbacResource.MCPLogs, RbacOperation.View);
if (!hasViewMCPLogsAccess) {
return <NoPermissionView entity="mcp logs" />;
}
return <MCPLogsPage />;
}

export const Route = createFileRoute("/workspace/mcp-logs")({
component: MCPLogsPage,
component: RouteComponent,
});
4 changes: 2 additions & 2 deletions ui/app/workspace/mcp-logs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function MCPLogsPage() {
const [error, setError] = useState<string | null>(null);
const [showEmptyState, setShowEmptyState] = useState(false);
const hasCheckedEmptyState = useRef(false);
const hasDeleteAccess = useRbac(RbacResource.Logs, RbacOperation.Delete);
const hasDeleteAccess = useRbac(RbacResource.MCPLogs, RbacOperation.Delete);

const [deleteLogs] = useDeleteMCPLogsMutation();
// Lazy query kept only for handleLogNavigate (fetches adjacent pages on demand)
Expand Down Expand Up @@ -448,4 +448,4 @@ export default function MCPLogsPage() {
)}
</div>
);
}
}
47 changes: 25 additions & 22 deletions ui/app/workspace/mcp-logs/views/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Status, StatusBarColors, Statuses } from "@/lib/constants/logs";
import type { MCPToolLogEntry } from "@/lib/types/logs";
import { ColumnDef } from "@tanstack/react-table";
import { ColumnDef, Row } from "@tanstack/react-table";
import { format, isValid } from "date-fns";
import { ArrowUpDown, Trash2 } from "lucide-react";

Expand Down Expand Up @@ -95,24 +95,27 @@ export const createMCPColumns = (
return <div className="font-mono text-sm">{isValidNumber ? `${cost.toFixed(4)}` : "N/A"}</div>;
},
},
{
id: "actions",
size: 72,
cell: ({ row }) => {
const log = row.original;
return (
<Button
variant="outline"
size="icon"
data-testid="log-delete-btn"
aria-label="Delete log"
className="text-secondary-foreground/30 hover:bg-destructive/10 hover:text-destructive border-destructive/10"
onClick={() => void handleDelete(log)}
disabled={!hasDeleteAccess}
>
<Trash2 />
</Button>
);
},
},
];
...(hasDeleteAccess
? [
{
id: "actions",
size: 72,
cell: ({ row }: { row: Row<MCPToolLogEntry> }) => {
const log = row.original;
return (
<Button
variant="outline"
size="icon"
data-testid="log-delete-btn"
aria-label="Delete log"
className="text-destructive/60 border-destructive/60 hover:text-destructive hover:bg-destructive/10"
onClick={() => void handleDelete(log)}
>
<Trash2 />
</Button>
);
},
},
]
: []),
];
4 changes: 2 additions & 2 deletions ui/app/workspace/mcp-tool-groups/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import MCPToolGroupsPage from "./page";

function RouteComponent() {
const hasMCPGatewayAccess = useRbac(RbacResource.MCPGateway, RbacOperation.View);
if (!hasMCPGatewayAccess) {
const hasMCPToolGroupsAccess = useRbac(RbacResource.MCPToolGroups, RbacOperation.View);
if (!hasMCPToolGroupsAccess) {
return <NoPermissionView entity="MCP tool groups" />;
}
return <MCPToolGroupsPage />;
Expand Down
6 changes: 3 additions & 3 deletions ui/app/workspace/providers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
import { KnownProvider, ModelProviderName, ProviderStatus } from "@/lib/types/config";
import { cn } from "@/lib/utils";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import { AlertCircle } from "lucide-react";
import { useNavigate } from "@tanstack/react-router";
import { AlertCircle } from "lucide-react";
import { useQueryState } from "nuqs";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
Expand Down Expand Up @@ -240,15 +240,15 @@ export default function Providers() {
})}
</div>
)}
<div className="pb-4">
{hasProviderCreateAccess ? <div className="pb-4">
<AddProviderDropdown
disabled={!hasProviderCreateAccess}
existingInSidebar={existingInSidebarNames}
knownProviders={knownProviders}
onSelectKnownProvider={handleSelectKnownProvider}
onAddCustomProvider={() => setShowCustomProviderSheet(true)}
/>
</div>
</div> : null}
</div>
</div>
</TooltipProvider>
Expand Down
64 changes: 33 additions & 31 deletions ui/app/workspace/providers/views/modelProviderKeysTableView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export default function ModelProviderKeysTableView({ provider, className, header
<div className="flex items-center gap-2">Configured {entityLabelPlural}</div>
<div className="flex items-center gap-2">
{headerActions}
{!isKeyless && (
{!isKeyless && hasUpdateProviderAccess ? (
<Button
disabled={!hasUpdateProviderAccess}
data-testid="add-key-btn"
Expand All @@ -117,7 +117,7 @@ export default function ModelProviderKeysTableView({ provider, className, header
<PlusIcon className="h-4 w-4" />
Add new {entityLabel}
</Button>
)}
) : null}
</div>
</CardTitle>
</CardHeader>
Expand Down Expand Up @@ -152,7 +152,7 @@ export default function ModelProviderKeysTableView({ provider, className, header
key={key.id}
data-testid={`key-row-${key.name}`}
className="text-sm transition-colors hover:bg-white"
onClick={() => {}}
onClick={() => { }}
>
<TableCell>
<div className="flex items-center space-x-2">
Expand Down Expand Up @@ -258,34 +258,36 @@ export default function ModelProviderKeysTableView({ provider, className, header
</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end space-x-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button onClick={(e) => e.stopPropagation()} variant="ghost">
<EllipsisIcon className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setShowAddNewKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasUpdateProviderAccess}
>
<PencilIcon className="mr-1 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
onClick={() => {
setShowDeleteKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasDeleteProviderAccess}
>
<TrashIcon className="mr-1 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{hasUpdateProviderAccess || hasDeleteProviderAccess ?
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button onClick={(e) => e.stopPropagation()} variant="ghost">
<EllipsisIcon className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setShowAddNewKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasUpdateProviderAccess}
>
<PencilIcon className="mr-1 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
onClick={() => {
setShowDeleteKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasDeleteProviderAccess}
>
<TrashIcon className="mr-1 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu> : null
}
</div>
</TableCell>
</TableRow>
Expand Down
Loading
Loading