mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 16:52:38 +00:00
feature secret rotations v2
This commit is contained in:
@@ -14,7 +14,8 @@ import {
|
||||
faFolderPlus,
|
||||
faKey,
|
||||
faList,
|
||||
faPlus
|
||||
faPlus,
|
||||
faRotate
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, useNavigate, useRouter, useSearch } from "@tanstack/react-router";
|
||||
@@ -23,6 +24,11 @@ import { twMerge } from "tailwind-merge";
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { CreateSecretRotationV2Modal } from "@app/components/secret-rotations-v2";
|
||||
import { DeleteSecretRotationV2Modal } from "@app/components/secret-rotations-v2/DeleteSecretRotationV2Modal";
|
||||
import { EditSecretRotationV2Modal } from "@app/components/secret-rotations-v2/EditSecretRotationV2Modal";
|
||||
import { RotateSecretRotationV2Modal } from "@app/components/secret-rotations-v2/RotateSecretRotationV2Modal";
|
||||
import { ViewSecretRotationV2GeneratedCredentialsModal } from "@app/components/secret-rotations-v2/ViewSecretRotationV2GeneratedCredentials";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
@@ -57,6 +63,7 @@ import {
|
||||
useSubscription,
|
||||
useWorkspace
|
||||
} from "@app/context";
|
||||
import { ProjectPermissionSecretRotationActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { useDebounce, usePagination, usePopUp, useResetPageHelper } from "@app/hooks";
|
||||
import {
|
||||
useCreateFolder,
|
||||
@@ -71,9 +78,16 @@ import { DashboardSecretsOrderBy } from "@app/hooks/api/dashboard/types";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { useUpdateFolderBatch } from "@app/hooks/api/secretFolders/queries";
|
||||
import { TUpdateFolderBatchDTO } from "@app/hooks/api/secretFolders/types";
|
||||
import { TSecretRotationV2 } from "@app/hooks/api/secretRotationsV2";
|
||||
import { SecretType, SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/types";
|
||||
import { ProjectType, ProjectVersion } from "@app/hooks/api/workspace/types";
|
||||
import { useDynamicSecretOverview, useFolderOverview, useSecretOverview } from "@app/hooks/utils";
|
||||
import {
|
||||
useDynamicSecretOverview,
|
||||
useFolderOverview,
|
||||
useSecretOverview,
|
||||
useSecretRotationOverview
|
||||
} from "@app/hooks/utils";
|
||||
import { SecretOverviewSecretRotationRow } from "@app/pages/secret-manager/OverviewPage/components/SecretOverviewSecretRotationRow";
|
||||
|
||||
import { CreateDynamicSecretForm } from "../SecretDashboardPage/components/ActionBar/CreateDynamicSecretForm";
|
||||
import { FolderForm } from "../SecretDashboardPage/components/ActionBar/FolderForm";
|
||||
@@ -99,7 +113,8 @@ enum RowType {
|
||||
Folder = "folder",
|
||||
DynamicSecret = "dynamic",
|
||||
Secret = "secret",
|
||||
Import = "import"
|
||||
Import = "import",
|
||||
SecretRotation = "rotation"
|
||||
}
|
||||
|
||||
type Filter = {
|
||||
@@ -110,7 +125,8 @@ const DEFAULT_FILTER_STATE = {
|
||||
[RowType.Folder]: true,
|
||||
[RowType.DynamicSecret]: true,
|
||||
[RowType.Secret]: true,
|
||||
[RowType.Import]: true
|
||||
[RowType.Import]: true,
|
||||
[RowType.SecretRotation]: true
|
||||
};
|
||||
|
||||
export const OverviewPage = () => {
|
||||
@@ -195,6 +211,15 @@ export const OverviewPage = () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
const userAvailableSecretRotationEnvs = userAvailableEnvs.filter((env) =>
|
||||
permission.can(
|
||||
ProjectPermissionSecretRotationActions.Create,
|
||||
subject(ProjectPermissionSub.SecretRotation, {
|
||||
environment: env.slug,
|
||||
secretPath
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const [visibleEnvs, setVisibleEnvs] = useState(userAvailableEnvs);
|
||||
|
||||
@@ -224,6 +249,7 @@ export const OverviewPage = () => {
|
||||
includeDynamicSecrets: filter.dynamic,
|
||||
includeSecrets: filter.secret,
|
||||
includeImports: filter.import,
|
||||
includeSecretRotations: filter.rotation,
|
||||
search: debouncedSearchFilter,
|
||||
limit,
|
||||
offset
|
||||
@@ -235,15 +261,18 @@ export const OverviewPage = () => {
|
||||
secrets,
|
||||
folders,
|
||||
dynamicSecrets,
|
||||
secretRotations,
|
||||
totalFolderCount,
|
||||
totalSecretCount,
|
||||
totalDynamicSecretCount,
|
||||
totalSecretRotationCount,
|
||||
totalImportCount,
|
||||
totalCount = 0,
|
||||
totalUniqueFoldersInPage,
|
||||
totalUniqueSecretsInPage,
|
||||
totalUniqueSecretImportsInPage,
|
||||
totalUniqueDynamicSecretsInPage
|
||||
totalUniqueDynamicSecretsInPage,
|
||||
totalUniqueSecretRotationsInPage
|
||||
} = overview ?? {};
|
||||
|
||||
const secretImportsShaped = secretImports
|
||||
@@ -273,6 +302,13 @@ export const OverviewPage = () => {
|
||||
const { dynamicSecretNames, isDynamicSecretPresentInEnv } =
|
||||
useDynamicSecretOverview(dynamicSecrets);
|
||||
|
||||
const {
|
||||
secretRotationNames,
|
||||
isSecretRotationPresentInEnv,
|
||||
getSecretRotationByName,
|
||||
getSecretRotationStatusesByName
|
||||
} = useSecretRotationOverview(secretRotations);
|
||||
|
||||
const { secKeys, getEnvSecretKeyCount } = useSecretOverview(
|
||||
secrets?.concat(secretImportsShaped) || []
|
||||
);
|
||||
@@ -301,6 +337,11 @@ export const OverviewPage = () => {
|
||||
"misc",
|
||||
"updateFolder",
|
||||
"addDynamicSecret",
|
||||
"addSecretRotation",
|
||||
"editSecretRotation",
|
||||
"rotateSecretRotation",
|
||||
"viewSecretRotationGeneratedCredentials",
|
||||
"deleteSecretRotation",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
@@ -841,6 +882,21 @@ export const OverviewPage = () => {
|
||||
<span>Dynamic Secrets</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
handleToggleRowType(RowType.SecretRotation);
|
||||
}}
|
||||
icon={
|
||||
filter[RowType.SecretRotation] && <FontAwesomeIcon icon={faCheckCircle} />
|
||||
}
|
||||
iconPos="right"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<FontAwesomeIcon icon={faRotate} className="text-mineshaft-400" />
|
||||
<span>Secret Rotations</span>
|
||||
</div>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -952,6 +1008,29 @@ export const OverviewPage = () => {
|
||||
Add Dynamic Secret
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content={
|
||||
userAvailableSecretRotationEnvs.length === 0 ? "Access restricted" : ""
|
||||
}
|
||||
>
|
||||
<Button
|
||||
leftIcon={<FontAwesomeIcon icon={faRotate} className="pr-2" />}
|
||||
onClick={() => {
|
||||
if (subscription?.secretRotation) {
|
||||
handlePopUpOpen("addSecretRotation");
|
||||
handlePopUpClose("misc");
|
||||
return;
|
||||
}
|
||||
handlePopUpOpen("upgradePlan");
|
||||
}}
|
||||
isDisabled={userAvailableSecretRotationEnvs.length === 0}
|
||||
variant="outline_bg"
|
||||
className="h-10 text-left"
|
||||
isFullWidth
|
||||
>
|
||||
Add Secret Rotation
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -1141,6 +1220,29 @@ export const OverviewPage = () => {
|
||||
key={`overview-${dynamicSecretName}-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
{secretRotationNames.map((secretRotationName, index) => (
|
||||
<SecretOverviewSecretRotationRow
|
||||
secretRotationName={secretRotationName}
|
||||
isSecretRotationInEnv={isSecretRotationPresentInEnv}
|
||||
environments={visibleEnvs}
|
||||
getSecretRotationByName={getSecretRotationByName}
|
||||
getSecretRotationStatusesByName={getSecretRotationStatusesByName}
|
||||
key={`overview-${secretRotationName}-${index + 1}`}
|
||||
scrollOffset={scrollOffset}
|
||||
onEdit={(secretRotation) =>
|
||||
handlePopUpOpen("editSecretRotation", secretRotation)
|
||||
}
|
||||
onRotate={(secretRotation) =>
|
||||
handlePopUpOpen("rotateSecretRotation", secretRotation)
|
||||
}
|
||||
onViewGeneratedCredentials={(secretRotation) =>
|
||||
handlePopUpOpen("viewSecretRotationGeneratedCredentials", secretRotation)
|
||||
}
|
||||
onDelete={(secretRotation) =>
|
||||
handlePopUpOpen("deleteSecretRotation", secretRotation)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{secKeys.map((key, index) => (
|
||||
<SecretOverviewTableRow
|
||||
isSelected={Boolean(selectedEntries.secret[key])}
|
||||
@@ -1165,7 +1267,8 @@ export const OverviewPage = () => {
|
||||
(totalUniqueFoldersInPage || 0) -
|
||||
(totalUniqueDynamicSecretsInPage || 0) -
|
||||
(totalUniqueSecretsInPage || 0) -
|
||||
(totalUniqueSecretImportsInPage || 0),
|
||||
(totalUniqueSecretImportsInPage || 0) -
|
||||
(totalUniqueSecretRotationsInPage || 0),
|
||||
0
|
||||
)}
|
||||
/>
|
||||
@@ -1206,6 +1309,7 @@ export const OverviewPage = () => {
|
||||
secretCount={totalSecretCount}
|
||||
folderCount={totalFolderCount}
|
||||
importCount={totalImportCount}
|
||||
secretRotationCount={totalSecretRotationCount}
|
||||
/>
|
||||
}
|
||||
className="rounded-b-md border-t border-solid border-t-mineshaft-600"
|
||||
@@ -1277,6 +1381,34 @@ export const OverviewPage = () => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<CreateSecretRotationV2Modal
|
||||
secretPath={secretPath}
|
||||
environments={userAvailableSecretRotationEnvs}
|
||||
isOpen={popUp.addSecretRotation.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("addSecretRotation", isOpen)}
|
||||
/>
|
||||
<EditSecretRotationV2Modal
|
||||
isOpen={popUp.editSecretRotation.isOpen}
|
||||
secretRotation={popUp.editSecretRotation.data as TSecretRotationV2}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("editSecretRotation", isOpen)}
|
||||
/>
|
||||
<RotateSecretRotationV2Modal
|
||||
isOpen={popUp.rotateSecretRotation.isOpen}
|
||||
secretRotation={popUp.rotateSecretRotation.data as TSecretRotationV2}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("rotateSecretRotation", isOpen)}
|
||||
/>
|
||||
<ViewSecretRotationV2GeneratedCredentialsModal
|
||||
isOpen={popUp.viewSecretRotationGeneratedCredentials.isOpen}
|
||||
secretRotation={popUp.viewSecretRotationGeneratedCredentials.data as TSecretRotationV2}
|
||||
onOpenChange={(isOpen) =>
|
||||
handlePopUpToggle("viewSecretRotationGeneratedCredentials", isOpen)
|
||||
}
|
||||
/>
|
||||
<DeleteSecretRotationV2Modal
|
||||
isOpen={popUp.deleteSecretRotation.isOpen}
|
||||
secretRotation={popUp.deleteSecretRotation.data as TSecretRotationV2}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("deleteSecretRotation", isOpen)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user