diff --git a/src/App.tsx b/src/App.tsx index 86b6b4b23..3d541f768 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import { MdOutlineIntegrationInstructions, MdOutlineSupportAgent } from "react-icons/md"; +import { RiShieldUserFill } from "react-icons/ri"; import { VscJson } from "react-icons/vsc"; import { BrowserRouter, @@ -53,6 +54,7 @@ import { import { ConnectionsPage } from "./pages/Settings/ConnectionsPage"; import { EventQueueStatusPage } from "./pages/Settings/EventQueueStatus"; import { FeatureFlagsPage } from "./pages/Settings/FeatureFlagsPage"; +import { PermissionsPage } from "./pages/Settings/PermissionsPage"; import NotificationSilencedAddPage from "./pages/Settings/notifications/NotificationSilencedAddPage"; import NotificationsPage from "./pages/Settings/notifications/NotificationsPage"; import NotificationRulesPage from "./pages/Settings/notifications/NotificationsRulesPage"; @@ -164,6 +166,13 @@ const settingsNav: SettingsNavigationItems = { featureName: features["settings.connections"], resourceName: tables.connections }, + { + name: "Permissions", + href: "/settings/permissions", + icon: RiShieldUserFill, + featureName: features["settings.permissions"], + resourceName: tables.permissions + }, ...(process.env.NEXT_PUBLIC_AUTH_IS_CLERK === "true" ? [] : [ @@ -397,6 +406,14 @@ export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) { true )} /> + , + tables.permissions, + "read" + )} + /> (url) + ); +} + +export function addPermission(permission: PermissionTable) { + return IncidentCommander.post("/permissions", permission); +} + +export function updatePermission(permission: PermissionTable) { + return IncidentCommander.patch( + `/permissions?id=eq.${permission.id}`, + permission + ); +} + +export function deletePermission(id: string) { + return IncidentCommander.patch(`/permissions?id=eq.${id}`, { + deleted_at: "now()" + }); +} diff --git a/src/api/types/permissions.ts b/src/api/types/permissions.ts new file mode 100644 index 000000000..5e5edb974 --- /dev/null +++ b/src/api/types/permissions.ts @@ -0,0 +1,40 @@ +import { Connection } from "@flanksource-ui/components/Connections/ConnectionFormModal"; +import { ConfigItem } from "./configs"; +import { PlaybookSpec } from "./playbooks"; +import { Topology } from "./topology"; +import { Team, User } from "./users"; + +export type PermissionTable = { + id: string; + description: string; + action: string; + deny?: boolean; + component_id?: string; + config_id?: string; + canary_id?: string; + playbook_id?: string; + created_by: string; + connection_id?: string; + person_id?: string; + team_id?: string; + updated_by: string; + created_at: string; + updated_at: string; + until?: string; + source?: string; +}; + +export type PermissionAPIResponse = PermissionTable & { + // checks: Pick; + catalog: Pick; + component: Pick; + canary: { + id: string; + name: string; + }; + playbook: Pick; + team: Pick; + connection: Pick; + person: User; + createdBy: User; +}; diff --git a/src/components/Permissions/ManagePermissions/Forms/AddPermissionButton.tsx b/src/components/Permissions/ManagePermissions/Forms/AddPermissionButton.tsx new file mode 100644 index 000000000..17e3343b3 --- /dev/null +++ b/src/components/Permissions/ManagePermissions/Forms/AddPermissionButton.tsx @@ -0,0 +1,16 @@ +import { useState } from "react"; +import { AiFillPlusCircle } from "react-icons/ai"; +import PermissionForm from "./PermissionForm"; + +export default function AddPermissionButton() { + const [isOpen, setIsOpen] = useState(false); + + return ( + <> + + setIsOpen(false)} /> + + ); +} diff --git a/src/components/Permissions/ManagePermissions/Forms/DeletePermission.tsx b/src/components/Permissions/ManagePermissions/Forms/DeletePermission.tsx new file mode 100644 index 000000000..eb8be9a11 --- /dev/null +++ b/src/components/Permissions/ManagePermissions/Forms/DeletePermission.tsx @@ -0,0 +1,65 @@ +import { deletePermission } from "@flanksource-ui/api/services/permissions"; +import { + toastError, + toastSuccess +} from "@flanksource-ui/components/Toast/toast"; +import { ConfirmationPromptDialog } from "@flanksource-ui/ui/AlertDialog/ConfirmationPromptDialog"; +import { Button } from "@flanksource-ui/ui/Buttons/Button"; +import { useMutation } from "@tanstack/react-query"; +import { AxiosError } from "axios"; +import { useCallback, useState } from "react"; +import { FaCircleNotch, FaTrash } from "react-icons/fa"; + +export default function DeletePermission({ + permissionId, + onDeleted = () => {} +}: { + permissionId: string; + onDeleted: () => void; +}) { + const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); + + const { mutate: deleteResource, isLoading } = useMutation({ + mutationFn: async (id: string) => { + const res = await deletePermission(id); + return res.data; + }, + onSuccess: (_) => { + toastSuccess("Permission deleted"); + onDeleted(); + }, + onError: (error: AxiosError) => { + toastError(error.message); + } + }); + + const onDeleteResource = useCallback(() => { + setIsConfirmDialogOpen(false); + deleteResource(permissionId); + }, [deleteResource, permissionId]); + + return ( + <> +