feature secret rotations v2

This commit is contained in:
Scott Wilson
2025-03-31 20:34:48 -07:00
parent da094383b8
commit 766c1242fd
275 changed files with 9692 additions and 783 deletions
@@ -366,7 +366,7 @@ export const SecretDetailSidebar = ({
>
<div className="flex items-center gap-2">
<InfisicalSecretInput
isReadOnly={isReadOnly || !isAllowed}
isReadOnly={isReadOnly || !isAllowed || secret?.isRotatedSecret}
environment={environment}
secretPath={secretPath}
key="secret-value"
@@ -881,21 +881,23 @@ export const SecretDetailSidebar = ({
</div>
</div>
</div>
<div
className={`flex items-center justify-center ${version === secretVersion.length ? "hidden" : ""}`}
>
<Tooltip content="Restore Secret Value">
<IconButton
ariaLabel="Restore"
variant="outline_bg"
size="sm"
className="h-8 w-8 rounded-md"
onClick={() => setValue("value", secretValue)}
>
<FontAwesomeIcon icon={faArrowRotateRight} />
</IconButton>
</Tooltip>
</div>
{!secret?.isRotatedSecret && (
<div
className={`flex items-center justify-center ${version === secretVersion.length ? "hidden" : ""}`}
>
<Tooltip content="Restore Secret Value">
<IconButton
ariaLabel="Restore"
variant="outline_bg"
size="sm"
className="h-8 w-8 rounded-md"
onClick={() => setValue("value", secretValue)}
>
<FontAwesomeIcon icon={faArrowRotateRight} />
</IconButton>
</Tooltip>
</div>
)}
</div>
)
)}
@@ -48,6 +48,8 @@ import {
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
import { Blur } from "@app/components/v2/Blur";
import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faKey, faRotate } from "@fortawesome/free-solid-svg-icons";
import {
FontAwesomeSpriteName,
formSchema,
@@ -91,6 +93,7 @@ export const SecretItem = memo(
}: Props) => {
const { currentWorkspace } = useWorkspace();
const { permission } = useProjectPermission();
const { isRotatedSecret } = secret;
const {
handleSubmit,
@@ -151,7 +154,6 @@ export const SecretItem = memo(
secretTags: selectedTagSlugs
})
);
const { secretValueHidden } = secret;
const [isSecValueCopied, setIsSecValueCopied] = useToggle(false);
@@ -221,29 +223,43 @@ export const SecretItem = memo(
<div
className={twMerge(
"border-b border-mineshaft-600 bg-mineshaft-800 shadow-none hover:bg-mineshaft-700",
isDirty && "border-primary-400/50"
isDirty && "border-primary-400/50",
isRotatedSecret && "bg-mineshaft-700/60"
)}
>
<div className="group flex">
<div
className={twMerge(
"flex h-11 w-11 items-center justify-center px-4 py-3",
"flex h-11 w-11 items-center justify-center px-4 py-3 text-mineshaft-300",
isDirty && "text-primary"
)}
>
<Checkbox
id={`checkbox-${secret.id}`}
isChecked={isSelected}
onCheckedChange={() => onToggleSecretSelect(secret)}
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
/>
<FontAwesomeSymbol
className={twMerge(
"ml-3 block h-3.5 w-3.5 group-hover:hidden",
isSelected && "hidden"
)}
symbolName={FontAwesomeSpriteName.SecretKey}
/>
{secret.isRotatedSecret ? (
<div className="relative">
<FontAwesomeIcon icon={faKey} size="xs" className={twMerge("ml-3 h-3.5 w-3.5")} />
<FontAwesomeIcon
icon={faRotate}
size="xs"
className="absolute -bottom-[0.05rem] -right-[0.2rem] text-mineshaft-400"
/>
</div>
) : (
<>
<Checkbox
id={`checkbox-${secret.id}`}
isChecked={isSelected}
onCheckedChange={() => onToggleSecretSelect(secret)}
className={twMerge("ml-3 hidden group-hover:flex", isSelected && "flex")}
/>
<FontAwesomeSymbol
className={twMerge(
"ml-3 block h-3.5 w-3.5 group-hover:hidden",
isSelected && "hidden"
)}
symbolName={FontAwesomeSpriteName.SecretKey}
/>
</>
)}
</div>
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
<Controller
@@ -252,7 +268,7 @@ export const SecretItem = memo(
render={({ field, fieldState: { error } }) => (
<Input
autoComplete="off"
isReadOnly={isReadOnly}
isReadOnly={isReadOnly || isRotatedSecret}
autoCapitalization={currentWorkspace?.autoCapitalization}
variant="plain"
isDisabled={isOverriden}
@@ -294,7 +310,7 @@ export const SecretItem = memo(
control={control}
render={({ field }) => (
<InfisicalSecretInput
isReadOnly={isReadOnly}
isReadOnly={isReadOnly || isRotatedSecret}
key="secret-value"
isVisible={isVisible}
environment={environment}
@@ -582,7 +598,7 @@ export const SecretItem = memo(
secretTags: selectedTagSlugs
})}
renderTooltip
allowedLabel="Delete"
allowedLabel={isRotatedSecret ? "Cannot Delete Rotated Secret" : "Delete"} // just using label for isRotatedSecret, disabled below
>
{(isAllowed) => (
<IconButton
@@ -592,7 +608,7 @@ export const SecretItem = memo(
size="md"
className="p-0 opacity-0 group-hover:opacity-100"
onClick={() => onDeleteSecret(secret)}
isDisabled={!isAllowed}
isDisabled={!isAllowed || isRotatedSecret}
>
<FontAwesomeSymbol
symbolName={FontAwesomeSpriteName.Close}
@@ -79,7 +79,8 @@ export const SecretListView = ({
skipMultilineEncoding,
newKey,
secretId,
secretMetadata
secretMetadata,
isRotatedSecret
}: Partial<{
value: string;
comment: string;
@@ -90,6 +91,7 @@ export const SecretListView = ({
newKey: string;
secretId: string;
secretMetadata?: { key: string; value: string }[];
isRotatedSecret?: boolean;
}> = {}
) => {
if (operation === "delete") {
@@ -110,14 +112,16 @@ export const SecretListView = ({
workspaceId,
secretPath,
secretKey: key,
secretValue: value || "",
...(!isRotatedSecret && {
newSecretName: newKey,
secretValue: value || ""
}),
type,
tagIds: tags,
secretComment: comment,
secretReminderRepeatDays: reminderRepeatDays,
secretReminderNote: reminderNote,
skipMultilineEncoding,
newSecretName: newKey,
secretMetadata
});
return;
@@ -213,7 +217,8 @@ export const SecretListView = ({
secretId: orgSecret.id,
newKey: hasKeyChanged ? key : undefined,
skipMultilineEncoding: modSecret.skipMultilineEncoding,
secretMetadata
secretMetadata,
isRotatedSecret: orgSecret.isRotatedSecret
});
if (cb) cb();
}
@@ -1,3 +1,7 @@
import { faLock, faRotate } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { FontAwesomeSymbol, Input, Tooltip } from "@app/components/v2";
import { Blur } from "@app/components/v2/Blur";
@@ -5,9 +9,10 @@ import { FontAwesomeSpriteName } from "./SecretListView.utils";
type Props = {
count: number;
isRotationView?: boolean;
};
export const SecretNoAccessListView = ({ count }: Props) => {
export const SecretNoAccessListView = ({ count, isRotationView }: Props) => {
return (
<>
{Array.from(Array(count)).map((_, i) => (
@@ -17,15 +22,34 @@ export const SecretNoAccessListView = ({ count }: Props) => {
content="You do not have permission to view this secret"
key={`no-access-secret-${i + 1}`}
>
<div className="flex border-b border-mineshaft-600 bg-mineshaft-800 shadow-none hover:bg-mineshaft-700">
<div
className={twMerge(
"flex border-b border-mineshaft-600 shadow-none hover:bg-mineshaft-700",
isRotationView ? "bg-mineshaft-700/60" : "bg-mineshaft-800"
)}
>
<div className="flex h-11 w-11 items-center justify-center px-4 py-3">
<FontAwesomeSymbol
className="ml-3 block h-3.5 w-3.5"
symbolName={FontAwesomeSpriteName.KeyLock}
/>
{isRotationView ? (
<div className="relative">
<FontAwesomeIcon
icon={faLock}
size="xs"
className={twMerge("ml-3 h-3.5 w-3.5")}
/>
<FontAwesomeIcon
icon={faRotate}
size="xs"
className="absolute -bottom-[0.05rem] -right-[0.2rem] text-mineshaft-400"
/>
</div>
) : (
<FontAwesomeSymbol
className="ml-3 block h-3.5 w-3.5"
symbolName={FontAwesomeSpriteName.KeyLock}
/>
)}
</div>
<div className="flex h-11 w-80 flex-shrink-0 items-center px-4 py-2">
<div className="ml-[0.05rem] flex h-11 w-80 flex-shrink-0 items-center border-r border-mineshaft-600 px-4 py-2">
<Input
autoComplete="off"
isReadOnly
@@ -35,7 +59,7 @@ export const SecretNoAccessListView = ({ count }: Props) => {
className="w-full px-0 blur-sm placeholder:text-red-500 focus:text-bunker-100 focus:ring-transparent"
/>
</div>
<Blur />
<Blur className="pl-8" />
</div>
</Tooltip>
))}