diff --git a/backend/src/server/lib/schemas.ts b/backend/src/server/lib/schemas.ts index 00651d2cc..d0750926d 100644 --- a/backend/src/server/lib/schemas.ts +++ b/backend/src/server/lib/schemas.ts @@ -43,8 +43,6 @@ export const GenericResourceNameSchema = z export const BaseSecretNameSchema = z.string().trim().min(1); export const SecretNameSchema = BaseSecretNameSchema.refine( - (el) => !el.includes(" "), - "Secret name cannot contain spaces." -) - .refine((el) => !el.includes(":"), "Secret name cannot contain colon.") - .refine((el) => !el.includes("/"), "Secret name cannot contain forward slash."); + (el) => !el.includes(":"), + "Secret name cannot contain colon." +).refine((el) => !el.includes("/"), "Secret name cannot contain forward slash."); diff --git a/frontend/src/components/v2/Input/Input.tsx b/frontend/src/components/v2/Input/Input.tsx index d325719e6..fa3982abd 100644 --- a/frontend/src/components/v2/Input/Input.tsx +++ b/frontend/src/components/v2/Input/Input.tsx @@ -12,6 +12,7 @@ type Props = { isReadOnly?: boolean; autoCapitalization?: boolean; containerClassName?: string; + warning?: ReactNode; }; const inputVariants = cva( @@ -84,6 +85,7 @@ export const Input = forwardRef( size = "md", isReadOnly, autoCapitalization, + warning, ...props }, ref @@ -116,10 +118,11 @@ export const Input = forwardRef( onInput={handleInput} className={twMerge( leftIcon ? "pl-10" : "pl-2.5", - rightIcon ? "pr-10" : "pr-2.5", + rightIcon || warning ? "pr-10" : "pr-2.5", inputVariants({ className, isError, size, isRounded, variant }) )} /> + {Boolean(warning) && !rightIcon && warning} {rightIcon && {rightIcon}} ); diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx index d4f8bbafe..7c993ee8b 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/CreateSecretForm/CreateSecretForm.tsx @@ -1,7 +1,7 @@ import { ClipboardEvent, useRef } from "react"; import { Controller, useForm } from "react-hook-form"; import { subject } from "@casl/ability"; -import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons"; +import { faTriangleExclamation, faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; @@ -12,7 +12,8 @@ import { FilterableSelect, FormControl, Input, - PasswordGenerator + PasswordGenerator, + Tooltip } from "@app/components/v2"; import { CreatableSelect } from "@app/components/v2/CreatableSelect"; import { InfisicalSecretInput } from "@app/components/v2/InfisicalSecretInput"; @@ -217,6 +218,29 @@ export const CreateSecretForm = ({ secretPath = "/", onClose }: Props) => { // @ts-expect-error this is for multiple ref single component secretKeyInputRef.current = e; }} + warning={ + secretKey?.includes(" ") ? ( + + Secret key contains whitespaces. +
+
If this is the desired format, you need to provide it as{" "} + + {encodeURIComponent(secretKey.trim())} + {" "} + when making API requests. + + } + > + +
+ ) : undefined + } placeholder="Type your secret name" onPaste={handlePaste} autoCapitalization={currentProject?.autoCapitalization} diff --git a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx index 723d4c3e2..9fb20d3b8 100644 --- a/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx +++ b/frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretRenameRow.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; import { subject } from "@casl/ability"; -import { faCheck, faClose, faCopy } from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faClose, faCopy, faWarning } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { zodResolver } from "@hookform/resolvers/zod"; import { AnimatePresence, motion } from "framer-motion"; @@ -78,6 +78,7 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath } control, reset, trigger, + watch, getValues, formState: { isDirty, isSubmitting, errors } } = useForm({ @@ -144,14 +145,35 @@ function SecretRenameRow({ environments, getSecretByKey, secretKey, secretPath } setIsSecNameCopied.on(); }; + const currentSecretValue = watch("key"); + return (
- + Key + {currentSecretValue?.trim()?.includes(" ") && + currentSecretValue?.trim() !== secretKey && ( + + Secret key contains whitespaces. +
+
If this is the desired format, you need to provide it as{" "} + + {encodeURIComponent(secretKey.trim())} + {" "} + when making API requests. +
+ } + > + + + )} + Secret key contains whitespaces. +
+
If this is the desired format, you need to provide it as{" "} + + {encodeURIComponent(secretKey.trim())} + {" "} + when making API requests. + + } + > + + + ) : undefined + } placeholder="Type your secret name" onPaste={handlePaste} autoCapitalization={autoCapitalize} diff --git a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx index 6191e2a18..8e40ef40d 100644 --- a/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx +++ b/frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretItem.tsx @@ -43,7 +43,7 @@ import { twMerge } from "tailwind-merge"; import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types"; import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faEyeSlash, faKey, faRotate } from "@fortawesome/free-solid-svg-icons"; +import { faEyeSlash, faKey, faRotate, faWarning } from "@fortawesome/free-solid-svg-icons"; import { PendingAction } from "@app/hooks/api/secretFolders/types"; import { format } from "date-fns"; import { CreateReminderForm } from "@app/pages/secret-manager/SecretDashboardPage/components/SecretListView/CreateReminderForm"; @@ -487,6 +487,30 @@ export const SecretItem = memo( placeholder={error?.message} isError={Boolean(error)} onKeyUp={() => trigger("key")} + warning={ + field?.value !== (originalSecret.originalKey || originalSecret.key) && + field.value?.includes(" ") ? ( + + Secret key contains whitespaces. +
+
If this is the desired format, you need to provide it as{" "} + + {encodeURIComponent(field.value.trim())} + {" "} + when making API requests. + + } + > + +
+ ) : undefined + } {...field} className="w-full px-0 placeholder:text-red-500 focus:text-bunker-100 focus:ring-transparent" />