mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-09 15:28:58 +00:00
misc: revisions to import secret flow
This commit is contained in:
@@ -7,6 +7,7 @@ import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
|||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import {
|
import {
|
||||||
ExternalMigrationProviders,
|
ExternalMigrationProviders,
|
||||||
|
VaultImportStatus,
|
||||||
VaultMappingType
|
VaultMappingType
|
||||||
} from "@app/services/external-migration/external-migration-types";
|
} from "@app/services/external-migration/external-migration-types";
|
||||||
|
|
||||||
@@ -277,19 +278,19 @@ export const registerExternalMigrationRouter = async (server: FastifyZodProvider
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
message: z.string()
|
status: z.nativeEnum(VaultImportStatus)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
await server.services.migration.importVaultSecrets({
|
const result = await server.services.migration.importVaultSecrets({
|
||||||
actor: req.permission,
|
actor: req.permission,
|
||||||
auditLogInfo: req.auditLogInfo,
|
auditLogInfo: req.auditLogInfo,
|
||||||
...req.body
|
...req.body
|
||||||
});
|
});
|
||||||
|
|
||||||
return { message: "Successfully imported vault secrets" };
|
return result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ import {
|
|||||||
TConfigureExternalMigrationDTO,
|
TConfigureExternalMigrationDTO,
|
||||||
THasCustomVaultMigrationDTO,
|
THasCustomVaultMigrationDTO,
|
||||||
TImportEnvKeyDataDTO,
|
TImportEnvKeyDataDTO,
|
||||||
TImportVaultDataDTO
|
TImportVaultDataDTO,
|
||||||
|
VaultImportStatus
|
||||||
} from "./external-migration-types";
|
} from "./external-migration-types";
|
||||||
|
|
||||||
type TExternalMigrationServiceFactoryDep = {
|
type TExternalMigrationServiceFactoryDep = {
|
||||||
@@ -487,41 +488,49 @@ export const externalMigrationServiceFactory = ({
|
|||||||
|
|
||||||
const vaultSecrets = await getHCVaultSecretsForPath(vaultNamespace, vaultSecretPath, connection, gatewayService);
|
const vaultSecrets = await getHCVaultSecretsForPath(vaultNamespace, vaultSecretPath, connection, gatewayService);
|
||||||
|
|
||||||
const secretOperation = await secretService.createManySecretsRaw({
|
try {
|
||||||
actorId: actor.id,
|
const secretOperation = await secretService.createManySecretsRaw({
|
||||||
actor: actor.type,
|
actorId: actor.id,
|
||||||
actorAuthMethod: actor.authMethod,
|
actor: actor.type,
|
||||||
actorOrgId: actor.orgId,
|
actorAuthMethod: actor.authMethod,
|
||||||
secretPath,
|
actorOrgId: actor.orgId,
|
||||||
environment,
|
secretPath,
|
||||||
projectId,
|
environment,
|
||||||
secrets: Object.entries(vaultSecrets).map(([secretKey, secretValue]) => ({
|
|
||||||
secretKey,
|
|
||||||
secretValue
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secretOperation.type === SecretProtectionType.Approval) {
|
|
||||||
await auditLogService.createAuditLog({
|
|
||||||
projectId,
|
projectId,
|
||||||
...auditLogInfo,
|
secrets: Object.entries(vaultSecrets).map(([secretKey, secretValue]) => ({
|
||||||
event: {
|
secretKey,
|
||||||
type: EventType.SECRET_APPROVAL_REQUEST,
|
secretValue
|
||||||
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 { 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"}`
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,11 @@ export enum ExternalMigrationProviders {
|
|||||||
EnvKey = "env-key"
|
EnvKey = "env-key"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum VaultImportStatus {
|
||||||
|
Imported = "imported",
|
||||||
|
ApprovalRequired = "approval_required"
|
||||||
|
}
|
||||||
|
|
||||||
export type TConfigureExternalMigrationDTO = {
|
export type TConfigureExternalMigrationDTO = {
|
||||||
platform: ExternalMigrationProviders;
|
platform: ExternalMigrationProviders;
|
||||||
connectionId: string | null;
|
connectionId: string | null;
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import { externalMigrationQueryKeys } from "./queries";
|
|||||||
import {
|
import {
|
||||||
ExternalMigrationProviders,
|
ExternalMigrationProviders,
|
||||||
TExternalMigrationConfig,
|
TExternalMigrationConfig,
|
||||||
TImportVaultSecretsDTO
|
TImportVaultSecretsDTO,
|
||||||
|
VaultImportStatus
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
export const useImportEnvKey = () => {
|
export const useImportEnvKey = () => {
|
||||||
@@ -100,9 +101,9 @@ export const useUpdateExternalMigrationConfig = (platform: ExternalMigrationProv
|
|||||||
export const useImportVaultSecrets = () => {
|
export const useImportVaultSecrets = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation<{ message: string }, object, TImportVaultSecretsDTO>({
|
return useMutation<{ status: VaultImportStatus }, object, TImportVaultSecretsDTO>({
|
||||||
mutationFn: async (dto) => {
|
mutationFn: async (dto) => {
|
||||||
const { data } = await apiRequest.post<{ message: string }>(
|
const { data } = await apiRequest.post<{ status: VaultImportStatus }>(
|
||||||
"/api/v3/external-migration/vault/import-secrets",
|
"/api/v3/external-migration/vault/import-secrets",
|
||||||
dto
|
dto
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ export enum ExternalMigrationProviders {
|
|||||||
EnvKey = "env-key"
|
EnvKey = "env-key"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum VaultImportStatus {
|
||||||
|
Imported = "imported",
|
||||||
|
ApprovalRequired = "approval_required"
|
||||||
|
}
|
||||||
|
|
||||||
export type TExternalMigrationConfig = {
|
export type TExternalMigrationConfig = {
|
||||||
id: string;
|
id: string;
|
||||||
orgId: string;
|
orgId: string;
|
||||||
|
|||||||
+13
-6
@@ -78,7 +78,7 @@ import {
|
|||||||
} from "@app/hooks/api/dashboard/queries";
|
} from "@app/hooks/api/dashboard/queries";
|
||||||
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
||||||
import { useGetExternalMigrationConfig, useImportVaultSecrets } from "@app/hooks/api/migration";
|
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 { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
|
||||||
import { PendingAction } from "@app/hooks/api/secretFolders/types";
|
import { PendingAction } from "@app/hooks/api/secretFolders/types";
|
||||||
import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries";
|
import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries";
|
||||||
@@ -672,7 +672,7 @@ export const ActionBar = ({
|
|||||||
|
|
||||||
const handleVaultImport = async (vaultPath: string, namespace: string) => {
|
const handleVaultImport = async (vaultPath: string, namespace: string) => {
|
||||||
try {
|
try {
|
||||||
await importVaultSecrets({
|
const result = await importVaultSecrets({
|
||||||
projectId,
|
projectId,
|
||||||
environment,
|
environment,
|
||||||
secretPath,
|
secretPath,
|
||||||
@@ -680,10 +680,17 @@ export const ActionBar = ({
|
|||||||
vaultSecretPath: vaultPath
|
vaultSecretPath: vaultPath
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
if (result.status === VaultImportStatus.ApprovalRequired) {
|
||||||
type: "success",
|
createNotification({
|
||||||
text: "Successfully imported secrets from HashiCorp Vault"
|
type: "info",
|
||||||
});
|
text: "Secret change request created successfully. Awaiting approval."
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createNotification({
|
||||||
|
type: "success",
|
||||||
|
text: "Successfully imported secrets from HashiCorp Vault"
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Vault import error:", err);
|
console.error("Vault import error:", err);
|
||||||
const error = err as AxiosError<{ message?: string }>;
|
const error = err as AxiosError<{ message?: string }>;
|
||||||
|
|||||||
+1
-5
@@ -88,13 +88,9 @@ const Content = ({ onClose, environment, secretPath, onImport }: ContentProps) =
|
|||||||
<div className="space-y-1.5 text-xs leading-relaxed">
|
<div className="space-y-1.5 text-xs leading-relaxed">
|
||||||
<p>
|
<p>
|
||||||
Select a Vault namespace and secret path to import secrets into the current
|
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>.
|
<code className="text-xs">{secretPath}</code>.
|
||||||
</p>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user