From 5671cd5cef83ea6dad59a68abd1567d44b591fd3 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sat, 5 Apr 2025 22:57:46 -0700 Subject: [PATCH] Begin ssh host permissions --- .../ProjectRoleModifySection.utils.tsx | 16 +- .../components/SshPermissionConditions.tsx | 180 ++++++++++++++++++ .../SshHostsPage/components/SshHostsTable.tsx | 4 - 3 files changed, 195 insertions(+), 5 deletions(-) create mode 100644 frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx index 4e578df94..9bbf61dfa 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx @@ -205,6 +205,7 @@ export const projectRoleFormSchema = z.object({ ), [ProjectPermissionSub.SshCertificates]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.SshCertificateTemplates]: GeneralPolicyActionSchema.array().default([]), + [ProjectPermissionSub.SshHosts]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.SecretApproval]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.SecretRollback]: SecretRollbackPolicyActionSchema.array().default([]), [ProjectPermissionSub.Project]: WorkspacePolicyActionSchema.array().default([]), @@ -291,7 +292,11 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { ProjectPermissionSub.SecretApproval, ProjectPermissionSub.Tags, ProjectPermissionSub.SecretRotation, - ProjectPermissionSub.Kms + ProjectPermissionSub.Kms, + ProjectPermissionSub.SshHosts, + ProjectPermissionSub.SshCertificateTemplates, + ProjectPermissionSub.SshCertificateAuthorities, + ProjectPermissionSub.SshCertificates ].includes(subject) ) { // from above statement we are sure it won't be undefined @@ -860,6 +865,15 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { { label: "Remove", value: "delete" } ] }, + [ProjectPermissionSub.SshHosts]: { + title: "SSH Hosts", + actions: [ + { label: "Read", value: "read" }, + { label: "Create", value: "create" }, + { label: "Modify", value: "edit" }, + { label: "Remove", value: "delete" } + ] + }, [ProjectPermissionSub.PkiCollections]: { title: "PKI Collections", actions: [ diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx new file mode 100644 index 000000000..46a92c73b --- /dev/null +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/SshPermissionConditions.tsx @@ -0,0 +1,180 @@ +import { Controller, useFieldArray, useFormContext } from "react-hook-form"; +import { faInfoCircle, faPlus, faTrash, faWarning } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { + Button, + FormControl, + IconButton, + Input, + Select, + SelectItem, + Tooltip +} from "@app/components/v2"; +import { PermissionConditionOperators } from "@app/context/ProjectPermissionContext/types"; + +import { + getConditionOperatorHelperInfo, + renderOperatorSelectItems +} from "./PermissionConditionHelpers"; +import { TFormSchema } from "./ProjectRoleModifySection.utils"; + +type Props = { + position?: number; + isDisabled?: boolean; +}; + +export const SshPermissionConditions = ({ position = 0, isDisabled }: Props) => { + const { + control, + watch, + setValue, + formState: { errors } + } = useFormContext(); + const items = useFieldArray({ + control, + name: `permissions.secrets.${position}.conditions` + }); + + const conditionErrorMessage = + errors?.permissions?.secrets?.[position]?.conditions?.message || + errors?.permissions?.secrets?.[position]?.conditions?.root?.message; + + return ( +
+

Conditions

+

+ Conditions determine when a policy will be applied (always if no conditions are present). +

+

+ All conditions must evaluate to true for the policy to take effect. +

+
+ {items.fields.map((el, index) => { + const condition = watch(`permissions.secrets.${position}.conditions.${index}`) as { + lhs: string; + rhs: string; + operator: string; + }; + return ( +
+
+ ( + + + + )} + /> +
+
+ ( + + + + )} + /> +
+ + + +
+
+
+ ( + + + + )} + /> +
+
+ items.remove(index)} + > + + +
+
+ ); + })} +
+ {conditionErrorMessage && ( +
+ + {conditionErrorMessage} +
+ )} +
+ +
+
+ ); +}; diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx index dbd1f9a6a..9cbf478ae 100644 --- a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx @@ -30,14 +30,10 @@ type Props = { ) => void; }; -// TODO: something to do with allowed principals - export const SshHostsTable = ({ handlePopUpOpen }: Props) => { const { currentWorkspace } = useWorkspace(); const { data, isPending } = useListWorkspaceSshHosts(currentWorkspace?.id || ""); - console.log("hosts data: ", data); - return (