misc: revisions to import secret flow

This commit is contained in:
Sheen Capadngan
2025-10-15 01:28:30 +08:00
parent 43498573c5
commit d3b4ef6088
7 changed files with 74 additions and 50 deletions

View File

@@ -7,6 +7,7 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import {
ExternalMigrationProviders,
VaultImportStatus,
VaultMappingType
} from "@app/services/external-migration/external-migration-types";
@@ -277,19 +278,19 @@ export const registerExternalMigrationRouter = async (server: FastifyZodProvider
}),
response: {
200: z.object({
message: z.string()
status: z.nativeEnum(VaultImportStatus)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
await server.services.migration.importVaultSecrets({
const result = await server.services.migration.importVaultSecrets({
actor: req.permission,
auditLogInfo: req.auditLogInfo,
...req.body
});
return { message: "Successfully imported vault secrets" };
return result;
}
});

View File

@@ -43,7 +43,8 @@ import {
TConfigureExternalMigrationDTO,
THasCustomVaultMigrationDTO,
TImportEnvKeyDataDTO,
TImportVaultDataDTO
TImportVaultDataDTO,
VaultImportStatus
} from "./external-migration-types";
type TExternalMigrationServiceFactoryDep = {
@@ -487,41 +488,49 @@ export const externalMigrationServiceFactory = ({
const vaultSecrets = await getHCVaultSecretsForPath(vaultNamespace, vaultSecretPath, connection, gatewayService);
const secretOperation = await secretService.createManySecretsRaw({
actorId: actor.id,
actor: actor.type,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
secretPath,
environment,
projectId,
secrets: Object.entries(vaultSecrets).map(([secretKey, secretValue]) => ({
secretKey,
secretValue
}))
});
if (secretOperation.type === SecretProtectionType.Approval) {
await auditLogService.createAuditLog({
try {
const secretOperation = await secretService.createManySecretsRaw({
actorId: actor.id,
actor: actor.type,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
secretPath,
environment,
projectId,
...auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath,
environment,
secrets: Object.entries(vaultSecrets).map(([secretKey]) => ({
secretKey
})),
eventType: SecretApprovalEvent.CreateMany
}
}
secrets: Object.entries(vaultSecrets).map(([secretKey, secretValue]) => ({
secretKey,
secretValue
}))
});
return { approval: secretOperation.approval };
if (secretOperation.type === SecretProtectionType.Approval) {
await auditLogService.createAuditLog({
projectId,
...auditLogInfo,
event: {
type: EventType.SECRET_APPROVAL_REQUEST,
metadata: {
committedBy: secretOperation.approval.committerUserId,
secretApprovalRequestId: secretOperation.approval.id,
secretApprovalRequestSlug: secretOperation.approval.slug,
secretPath,
environment,
secrets: Object.entries(vaultSecrets).map(([secretKey]) => ({
secretKey
})),
eventType: SecretApprovalEvent.CreateMany
}
}
});
return { status: VaultImportStatus.ApprovalRequired };
}
return { status: VaultImportStatus.Imported };
} catch (error) {
throw new BadRequestError({
message: `Failed to import Vault secrets. ${error instanceof Error ? error.message : "Unknown error"}`
});
}
};

View File

@@ -122,6 +122,11 @@ export enum ExternalMigrationProviders {
EnvKey = "env-key"
}
export enum VaultImportStatus {
Imported = "imported",
ApprovalRequired = "approval_required"
}
export type TConfigureExternalMigrationDTO = {
platform: ExternalMigrationProviders;
connectionId: string | null;

View File

@@ -9,7 +9,8 @@ import { externalMigrationQueryKeys } from "./queries";
import {
ExternalMigrationProviders,
TExternalMigrationConfig,
TImportVaultSecretsDTO
TImportVaultSecretsDTO,
VaultImportStatus
} from "./types";
export const useImportEnvKey = () => {
@@ -100,9 +101,9 @@ export const useUpdateExternalMigrationConfig = (platform: ExternalMigrationProv
export const useImportVaultSecrets = () => {
const queryClient = useQueryClient();
return useMutation<{ message: string }, object, TImportVaultSecretsDTO>({
return useMutation<{ status: VaultImportStatus }, object, TImportVaultSecretsDTO>({
mutationFn: async (dto) => {
const { data } = await apiRequest.post<{ message: string }>(
const { data } = await apiRequest.post<{ status: VaultImportStatus }>(
"/api/v3/external-migration/vault/import-secrets",
dto
);

View File

@@ -3,6 +3,11 @@ export enum ExternalMigrationProviders {
EnvKey = "env-key"
}
export enum VaultImportStatus {
Imported = "imported",
ApprovalRequired = "approval_required"
}
export type TExternalMigrationConfig = {
id: string;
orgId: string;

View File

@@ -78,7 +78,7 @@ import {
} from "@app/hooks/api/dashboard/queries";
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
import { useGetExternalMigrationConfig, useImportVaultSecrets } from "@app/hooks/api/migration";
import { ExternalMigrationProviders } from "@app/hooks/api/migration/types";
import { ExternalMigrationProviders, VaultImportStatus } from "@app/hooks/api/migration/types";
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
import { PendingAction } from "@app/hooks/api/secretFolders/types";
import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries";
@@ -672,7 +672,7 @@ export const ActionBar = ({
const handleVaultImport = async (vaultPath: string, namespace: string) => {
try {
await importVaultSecrets({
const result = await importVaultSecrets({
projectId,
environment,
secretPath,
@@ -680,10 +680,17 @@ export const ActionBar = ({
vaultSecretPath: vaultPath
});
createNotification({
type: "success",
text: "Successfully imported secrets from HashiCorp Vault"
});
if (result.status === VaultImportStatus.ApprovalRequired) {
createNotification({
type: "info",
text: "Secret change request created successfully. Awaiting approval."
});
} else {
createNotification({
type: "success",
text: "Successfully imported secrets from HashiCorp Vault"
});
}
} catch (err) {
console.error("Vault import error:", err);
const error = err as AxiosError<{ message?: string }>;

View File

@@ -88,13 +88,9 @@ const Content = ({ onClose, environment, secretPath, onImport }: ContentProps) =
<div className="space-y-1.5 text-xs leading-relaxed">
<p>
Select a Vault namespace and secret path to import secrets into the current
environment (<code className="text-xs">{environment}</code>) at path{" "}
Infisical environment (<code className="text-xs">{environment}</code>) at path{" "}
<code className="text-xs">{secretPath}</code>.
</p>
<p>
<strong>Note:</strong> Existing secrets with the same key will be overwritten.
Secrets will be imported from the selected Vault path.
</p>
</div>
</div>
</div>