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
4 changes: 2 additions & 2 deletions src/api/services/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IncidentCommander } from "../axios";
import { resolvePostGrestRequestWithPagination } from "../resolve";
import { PermissionAPIResponse, PermissionTable } from "../types/permissions";
import { PermissionsSummary, PermissionTable } from "../types/permissions";

export type FetchPermissionsInput = {
componentId?: string;
Expand Down Expand Up @@ -74,7 +74,7 @@ export function fetchPermissions(

const url = `/permissions_summary?${queryParam}&select=${selectFields.join(",")}&deleted_at=is.null&limit=${pageSize}&offset=${pageIndex * pageSize}`;
return resolvePostGrestRequestWithPagination(
IncidentCommander.get<PermissionAPIResponse[]>(url, {
IncidentCommander.get<PermissionsSummary[]>(url, {
headers: {
Prefer: "count=exact"
}
Expand Down
3 changes: 2 additions & 1 deletion src/api/types/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ export type PermissionTable = {
playbook_id?: string;
};

export type PermissionAPIResponse = PermissionTable & {
export type PermissionsSummary = PermissionTable & {
// checks: Pick<HealthCheck, "id" | "name" | "type" | "status">;
catalog: Pick<ConfigItem, "id" | "name" | "type" | "config_class">;
component: Pick<Topology, "id" | "name" | "icon">;
source?: string;
canary: {
id: string;
name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} 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 { useMutation, useQueryClient } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { useCallback, useState } from "react";
import { FaCircleNotch, FaTrash } from "react-icons/fa";
Expand All @@ -18,6 +18,7 @@ export default function DeletePermission({
onDeleted: () => void;
}) {
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false);
const queryClient = useQueryClient();

const { mutate: deleteResource, isLoading } = useMutation({
mutationFn: async (id: string) => {
Expand All @@ -26,6 +27,7 @@ export default function DeletePermission({
},
onSuccess: (_) => {
toastSuccess("Permission deleted");
queryClient.invalidateQueries({ queryKey: ["permissions_summary"] });
onDeleted();
},
onError: (error: AxiosError) => {
Expand Down
100 changes: 64 additions & 36 deletions src/components/Permissions/ManagePermissions/Forms/PermissionForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useUser } from "@flanksource-ui/context";
import { tables } from "@flanksource-ui/context/UserAccessContext/permissions";
import { Button } from "@flanksource-ui/ui/Buttons/Button";
import { Modal } from "@flanksource-ui/ui/Modal";
import { useMutation } from "@tanstack/react-query";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { AxiosError } from "axios";
import clsx from "clsx";
import { Form, Formik, useFormikContext } from "formik";
Expand All @@ -36,7 +36,7 @@ type PermissionFormProps = {
data?: Partial<PermissionTable>;
};

function PermissionActionDropdown() {
function PermissionActionDropdown({ isDisabled }: { isDisabled?: boolean }) {
const { values } = useFormikContext<Partial<PermissionTable>>();

const resourceType = useMemo<ResourceType | undefined>(() => {
Expand All @@ -63,17 +63,68 @@ function PermissionActionDropdown() {
options={availableActions}
name="action"
label="Action"
isDisabled={isDisabled}
/>
);
}

function PermissionFormContent({
isResourceIdProvided,
agentOptions,
source
}: {
isResourceIdProvided: boolean;
agentOptions: { label: string; value: string }[];
source?: string;
}) {
const isReadOnly = source === "KubernetesCRD";

return (
<div className="flex flex-col gap-3 p-4">
<PermissionsSubjectControls />
{isResourceIdProvided ? (
<div className="flex flex-col gap-2">
<label className="text-sm font-semibold">Resource</label>
<PermissionResource />
</div>
) : (
<FormikPermissionSelectResourceFields />
)}
<PermissionActionDropdown isDisabled={isReadOnly} />
<FormikCheckbox name="deny" label="Deny" disabled={isReadOnly} />
<div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
<div className="space-y-3">
<FormikKeyValueMapField
name="tags"
label="Tags"
hint="Permission will apply only to resources matching these tags"
/>
<FormikSelectDropdown
name="agents"
label="Agents"
options={agentOptions}
isMulti
placeholder="Select agents..."
isDisabled={isReadOnly}
/>
</div>
</div>
<FormikTextArea
name="description"
label="Description"
disabled={isReadOnly}
/>
</div>
);
}

export default function PermissionForm({
onClose,
isOpen = false,
data
}: PermissionFormProps) {
const isResourceIdProvided = useMemo(() => {
return (
return !!(
data?.component_id ||
data?.config_id ||
data?.canary_id ||
Expand All @@ -85,6 +136,7 @@ export default function PermissionForm({
}, [data]);

const { user } = useUser();
const queryClient = useQueryClient();

const { data: agents } = useAllAgentNamesQuery({});

Expand All @@ -106,8 +158,8 @@ export default function PermissionForm({
return res.data;
},
onSuccess: () => {
// do something
toastSuccess("Permission added");
queryClient.invalidateQueries({ queryKey: ["permissions_summary"] });
onClose();
},
onError: (error: AxiosError) => {
Expand All @@ -126,8 +178,8 @@ export default function PermissionForm({
return res.data;
},
onSuccess: () => {
// do something
toastSuccess("Permission updated");
queryClient.invalidateQueries({ queryKey: ["permissions_summary"] });
onClose();
},
onError: (error: AxiosError) => {
Expand Down Expand Up @@ -187,42 +239,18 @@ export default function PermissionForm({
}}
>
<Form className="flex flex-1 flex-col gap-2 overflow-y-auto">
<div className="flex flex-1 flex-col gap-3 p-4">
<PermissionsSubjectControls />
{isResourceIdProvided ? (
<div className="flex flex-col gap-2">
<label className="text-sm font-semibold">Resource</label>
<PermissionResource />
</div>
) : (
<FormikPermissionSelectResourceFields />
)}
<PermissionActionDropdown />
<FormikCheckbox name="deny" label="Deny" />
<div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
<div className="space-y-3">
<FormikKeyValueMapField
name="tags"
label="Tags"
hint="Permission will apply only to resources matching these tags"
/>
<FormikSelectDropdown
name="agents"
label="Agents"
options={agentOptions}
isMulti
placeholder="Select agents..."
/>
</div>
</div>
<FormikTextArea name="description" label="Description" />
<div className="flex flex-1 flex-col overflow-y-auto">
<PermissionFormContent
isResourceIdProvided={isResourceIdProvided}
agentOptions={agentOptions}
source={data?.source}
/>
</div>
<CanEditResource
id={data?.id}
name={"Permission"}
resourceType={"permissions"}
source={data?.source}
className="flex flex-col"
className="flex items-center bg-gray-100 px-5 py-4"
>
<AuthorizationAccessCheck
resource={tables.permissions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function PermissionsSubjectControls() {
<div>
<div className="flex w-full flex-row">
<Switch
options={["Team", "Person", "Notification", "Role", "Playbook"]}
options={["Person", "Team", "Notification", "Role", "Playbook"]}
className="w-auto"
itemsClassName=""
defaultValue="Go Template"
Expand Down
16 changes: 12 additions & 4 deletions src/components/Permissions/PermissionsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PermissionAPIResponse } from "@flanksource-ui/api/types/permissions";
import { PermissionsSummary } from "@flanksource-ui/api/types/permissions";
import { Avatar } from "@flanksource-ui/ui/Avatar";
import { Icon } from "@flanksource-ui/ui/Icons/Icon";
import { MRTDateCell } from "@flanksource-ui/ui/MRTDataTable/Cells/MRTDateCells";
Expand All @@ -13,12 +13,13 @@ import { permissionsActionsList } from "./PermissionsView";
import { BsBan } from "react-icons/bs";
import { Link } from "react-router-dom";
import { Badge } from "@flanksource-ui/ui/Badge/Badge";
import CRDSource from "../Settings/CRDSource";

const formatTagText = (key: string, value: string): string => {
return `${key}: ${value}`;
};

const permissionsTableColumns: MRT_ColumnDef<PermissionAPIResponse>[] = [
const permissionsTableColumns: MRT_ColumnDef<PermissionsSummary>[] = [
{
header: "Subject",
size: 100,
Expand Down Expand Up @@ -253,17 +254,24 @@ const permissionsTableColumns: MRT_ColumnDef<PermissionAPIResponse>[] = [
size: 40,
Cell: ({ row }) => {
const createdBy = row.original.createdBy;
const source = row.original.source;

if (source?.toLowerCase() === "KubernetesCRD".toLowerCase()) {
const id = row.original.id;
return <CRDSource source={source} id={id} showMinimal />;
}

return <Avatar user={createdBy} />;
}
}
];

type PermissionsTableProps = {
permissions: PermissionAPIResponse[];
permissions: PermissionsSummary[];
isLoading: boolean;
pageCount: number;
totalEntries: number;
handleRowClick?: (row: PermissionAPIResponse) => void;
handleRowClick?: (row: PermissionsSummary) => void;
hideResourceColumn?: boolean;
};

Expand Down
4 changes: 2 additions & 2 deletions src/components/Permissions/PermissionsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
FetchPermissionsInput
} from "@flanksource-ui/api/services/permissions";
import {
PermissionAPIResponse,
PermissionsSummary,
PermissionTable
} from "@flanksource-ui/api/types/permissions";
import useReactTablePaginationState from "@flanksource-ui/ui/DataTable/Hooks/useReactTablePaginationState";
Expand Down Expand Up @@ -81,7 +81,7 @@ export default function PermissionsView({
onRefetch
}: PermissionsViewProps) {
const [selectedPermission, setSelectedPermission] =
useState<PermissionAPIResponse>();
useState<PermissionsSummary>();
const { pageSize, pageIndex } = useReactTablePaginationState();
const [isPermissionModalOpen, setIsPermissionModalOpen] = useState(false);

Expand Down
Loading