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
@@ -0,0 +1,96 @@
import { ReactNode } from "react";
import { faRotate } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format } from "date-fns";
import { Modal, ModalContent, Spinner } from "@app/components/v2";
import { SECRET_ROTATION_MAP } from "@app/helpers/secretRotationsV2";
import {
SecretRotation,
TSecretRotationV2,
useViewSecretRotationV2GeneratedCredentials
} from "@app/hooks/api/secretRotationsV2";
import { ViewSqlRotationGeneratedCredentials } from "./shared";
type Props = {
secretRotation?: TSecretRotationV2;
isOpen: boolean;
onOpenChange: (isOpen: boolean) => void;
};
type ContentProps = {
secretRotation: TSecretRotationV2;
};
const Content = ({ secretRotation }: ContentProps) => {
const { id: rotationId, type, nextRotationAt } = secretRotation;
const { data: generatedCredentialsResponse, isPending } =
useViewSecretRotationV2GeneratedCredentials({
rotationId,
type
});
if (isPending) {
return (
<div className="flex h-full flex-col items-center justify-center py-2.5">
<Spinner size="lg" className="text-mineshaft-500" />
<p className="mt-4 text-sm text-mineshaft-400">Loading generated credentials...</p>
</div>
);
}
let Component: ReactNode;
switch (generatedCredentialsResponse!.type) {
case SecretRotation.PostgresCredentials:
case SecretRotation.MsSqlCredentials:
Component = (
<ViewSqlRotationGeneratedCredentials
generatedCredentialsResponse={generatedCredentialsResponse!}
/>
);
break;
default:
throw new Error("Unhandled View Generated Credential Rotation Type");
}
return (
<div className="flex flex-col gap-y-4">
{Component}
{nextRotationAt && (
<div className="flex items-center gap-x-1.5 text-sm text-mineshaft-300">
<FontAwesomeIcon icon={faRotate} className="text-mineshaft-400" />
<span>
Next rotation occurs on: {format(nextRotationAt, "MM/dd/yyyy")} at{" "}
{format(nextRotationAt, "h:mm aa")}
</span>
</div>
)}
</div>
);
};
export const ViewSecretRotationV2GeneratedCredentialsModal = ({
isOpen,
onOpenChange,
secretRotation
}: Props) => {
if (!secretRotation) return null;
const rotationType = SECRET_ROTATION_MAP[secretRotation.type].name;
return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent
onOpenAutoFocus={(event) => {
event.preventDefault();
}}
title="Generated Credentials"
subTitle={`View the current and retired ${rotationType}.`}
>
<Content secretRotation={secretRotation} />
</ModalContent>
</Modal>
);
};
@@ -0,0 +1 @@
export * from "./ViewSecretRotationV2GeneratedCredentials";
@@ -0,0 +1,55 @@
import { useReducer } from "react";
import { faCheck, faCopy, faEyeSlash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { GenericFieldLabel, IconButton, Tooltip } from "@app/components/v2";
import { useTimedReset } from "@app/hooks";
type Props = {
children?: string;
label: string;
isSensitive?: boolean;
};
export const CredentialDisplay = ({ children, label, isSensitive }: Props) => {
const [showCredential, toggleShowCredential] = useReducer((prev) => !prev, !isSensitive);
const [, isCopyingCredential, setCopyCredential] = useTimedReset<string>({
initialState: "Copy ID to clipboard"
});
return (
<GenericFieldLabel label={label}>
{children ? (
<div className="flex w-full items-center gap-1 text-mineshaft-100">
{showCredential ? children : "****************************"}
<Tooltip content={`Copy ${label}`}>
<IconButton
onClick={() => {
setCopyCredential(children);
navigator.clipboard.writeText(children);
}}
ariaLabel="Copy credential"
variant="plain"
size="xs"
>
<FontAwesomeIcon icon={isCopyingCredential ? faCheck : faCopy} />
</IconButton>
</Tooltip>
{isSensitive && (
<Tooltip content={`Show ${label}`}>
<IconButton
variant="plain"
size="xs"
onClick={toggleShowCredential}
ariaLabel={`${showCredential ? "Hide" : "Show"} credential`}
>
<FontAwesomeIcon icon={faEyeSlash} />
</IconButton>
</Tooltip>
)}
</div>
) : null}
</GenericFieldLabel>
);
};
@@ -0,0 +1,57 @@
import { faCheck, faClockRotateLeft } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CredentialDisplay } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials/shared/CredentialDisplay";
import { TViewSecretRotationGeneratedCredentialsResponse } from "@app/hooks/api/secretRotationsV2";
type Props = {
generatedCredentialsResponse: TViewSecretRotationGeneratedCredentialsResponse;
};
export const ViewSqlRotationGeneratedCredentials = ({
generatedCredentialsResponse: { generatedCredentials, activeIndex }
}: Props) => {
const inactiveIndex = activeIndex === 0 ? 1 : 0;
const activeCredentials = generatedCredentials[activeIndex];
const inactiveCredentials = generatedCredentials[inactiveIndex];
return (
<>
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faCheck} className="mr-1.5 text-green" />
Current Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The active credential set currently mapped to the rotation secrets.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
<CredentialDisplay label="Username">{activeCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{activeCredentials?.password}
</CredentialDisplay>
</div>
</div>
<div className="flex flex-col gap-2">
<div className="w-full border-b border-mineshaft-600">
<span className="text-sm text-mineshaft-100">
<FontAwesomeIcon icon={faClockRotateLeft} className="mr-1.5 text-yellow" />
Retired Credentials
</span>
</div>
<p className="text-sm text-mineshaft-300">
The retired credential set that will be revoked during the next rotation cycle.
</p>
<div className="flex flex-col gap-x-8 gap-y-2 rounded border border-mineshaft-600 bg-mineshaft-700 p-2">
<CredentialDisplay label="Username">{inactiveCredentials?.username}</CredentialDisplay>
<CredentialDisplay isSensitive label="Password">
{inactiveCredentials?.password}
</CredentialDisplay>
</div>
</div>
</>
);
};
@@ -0,0 +1 @@
export * from "./ViewSqlRotationGeneratedCredentials";