From 7bbcab8ccbacc3a8f79c0890b4a73aeb438941b0 Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Wed, 25 Sep 2024 10:31:26 +0300 Subject: [PATCH] feat: add permissions page with ability to manage permissions Fixes #2246 chore: enable hiding resource column fix: make minor adjustments to resource display --- src/App.tsx | 17 ++ src/api/services/permissions.ts | 98 ++++++++++ src/api/types/permissions.ts | 40 ++++ .../Forms/AddPermissionButton.tsx | 16 ++ .../Forms/DeletePermission.tsx | 65 +++++++ .../FormikPermissionSelectResourceFields.tsx | 68 +++++++ .../Forms/PermissionForm.tsx | 183 ++++++++++++++++++ .../Forms/PermissionResource.tsx | 26 +++ .../Forms/PermissionSubjectControls.tsx | 57 ++++++ .../Permissions/PermissionsTable.tsx | 142 ++++++++++++++ .../Permissions/PermissionsView.tsx | 74 +++++++ src/context/UserAccessContext/permissions.ts | 3 +- src/pages/Settings/PermissionsPage.tsx | 60 ++++++ src/services/permissions/features.ts | 3 +- 14 files changed, 850 insertions(+), 2 deletions(-) create mode 100644 src/api/services/permissions.ts create mode 100644 src/api/types/permissions.ts create mode 100644 src/components/Permissions/ManagePermissions/Forms/AddPermissionButton.tsx create mode 100644 src/components/Permissions/ManagePermissions/Forms/DeletePermission.tsx create mode 100644 src/components/Permissions/ManagePermissions/Forms/FormikPermissionSelectResourceFields.tsx create mode 100644 src/components/Permissions/ManagePermissions/Forms/PermissionForm.tsx create mode 100644 src/components/Permissions/ManagePermissions/Forms/PermissionResource.tsx create mode 100644 src/components/Permissions/ManagePermissions/Forms/PermissionSubjectControls.tsx create mode 100644 src/components/Permissions/PermissionsTable.tsx create mode 100644 src/components/Permissions/PermissionsView.tsx create mode 100644 src/pages/Settings/PermissionsPage.tsx 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 ( + <> +