mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
feat: added initial sync behavior for azure key integration
This commit is contained in:
@@ -257,14 +257,27 @@ const syncSecretsGCPSecretManager = async ({
|
|||||||
const syncSecretsAzureKeyVault = async ({
|
const syncSecretsAzureKeyVault = async ({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
|
createManySecretsRawFn,
|
||||||
|
updateManySecretsRawFn
|
||||||
}: {
|
}: {
|
||||||
integration: TIntegrations;
|
integration: TIntegrations & {
|
||||||
|
projectId: string;
|
||||||
|
environment: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
secretPath: string;
|
||||||
|
};
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string }>;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
createManySecretsRawFn: (params: TCreateManySecretsRawFn) => Promise<Array<TSecrets & { _id: string }>>;
|
||||||
|
updateManySecretsRawFn: (params: TUpdateManySecretsRawFn) => Promise<Array<TSecrets & { _id: string }>>;
|
||||||
}) => {
|
}) => {
|
||||||
interface GetAzureKeyVaultSecret {
|
interface GetAzureKeyVaultSecret {
|
||||||
id: string; // secret URI
|
id: string; // secret URI
|
||||||
|
value: string;
|
||||||
attributes: {
|
attributes: {
|
||||||
enabled: true;
|
enabled: true;
|
||||||
created: number;
|
created: number;
|
||||||
@@ -361,6 +374,83 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const secretsToAdd: { [key: string]: string } = {};
|
||||||
|
const secretsToUpdate: { [key: string]: string } = {};
|
||||||
|
const secretKeysToRemoveFromDelete = new Set<string>();
|
||||||
|
|
||||||
|
const metadata = IntegrationMetadataSchema.parse(integration.metadata);
|
||||||
|
if (!integration.lastUsed) {
|
||||||
|
Object.keys(res).forEach((key) => {
|
||||||
|
// first time using integration
|
||||||
|
const underscoredKey = key.replace(/-/g, "_");
|
||||||
|
|
||||||
|
// -> apply initial sync behavior
|
||||||
|
switch (metadata.initialSyncBehavior) {
|
||||||
|
case IntegrationInitialSyncBehavior.PREFER_TARGET: {
|
||||||
|
if (!(underscoredKey in secrets)) {
|
||||||
|
secretsToAdd[underscoredKey] = res[key].value;
|
||||||
|
setSecrets.push({
|
||||||
|
key,
|
||||||
|
value: res[key].value
|
||||||
|
});
|
||||||
|
} else if (secrets[underscoredKey]?.value !== res[key].value) {
|
||||||
|
secretsToUpdate[underscoredKey] = res[key].value;
|
||||||
|
const toEditSecretIndex = setSecrets.findIndex((secret) => secret.key === key);
|
||||||
|
if (toEditSecretIndex >= 0) {
|
||||||
|
setSecrets[toEditSecretIndex].value = res[key].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
secretKeysToRemoveFromDelete.add(key);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IntegrationInitialSyncBehavior.PREFER_SOURCE: {
|
||||||
|
if (!(underscoredKey in secrets)) {
|
||||||
|
secretsToAdd[underscoredKey] = res[key].value;
|
||||||
|
setSecrets.push({
|
||||||
|
key,
|
||||||
|
value: res[key].value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
secretKeysToRemoveFromDelete.add(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(secretsToUpdate).length) {
|
||||||
|
await updateManySecretsRawFn({
|
||||||
|
projectId: integration.projectId,
|
||||||
|
environment: integration.environment.slug,
|
||||||
|
path: integration.secretPath,
|
||||||
|
secrets: Object.keys(secretsToUpdate).map((key) => ({
|
||||||
|
secretName: key,
|
||||||
|
secretValue: secretsToUpdate[key],
|
||||||
|
type: SecretType.Shared,
|
||||||
|
secretComment: ""
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(secretsToAdd).length) {
|
||||||
|
await createManySecretsRawFn({
|
||||||
|
projectId: integration.projectId,
|
||||||
|
environment: integration.environment.slug,
|
||||||
|
path: integration.secretPath,
|
||||||
|
secrets: Object.keys(secretsToAdd).map((key) => ({
|
||||||
|
secretName: key,
|
||||||
|
secretValue: secretsToAdd[key],
|
||||||
|
type: SecretType.Shared,
|
||||||
|
secretComment: ""
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const setSecretAzureKeyVault = async ({
|
const setSecretAzureKeyVault = async ({
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
@@ -428,7 +518,7 @@ const syncSecretsAzureKeyVault = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for await (const deleteSecret of deleteSecrets) {
|
for await (const deleteSecret of deleteSecrets.filter((secret) => !secretKeysToRemoveFromDelete.has(secret.key))) {
|
||||||
const { key } = deleteSecret;
|
const { key } = deleteSecret;
|
||||||
await request.delete(`${integration.app}/secrets/${key}?api-version=7.3`, {
|
await request.delete(`${integration.app}/secrets/${key}?api-version=7.3`, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -3512,7 +3602,9 @@ export const syncIntegrationSecrets = async ({
|
|||||||
await syncSecretsAzureKeyVault({
|
await syncSecretsAzureKeyVault({
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken,
|
||||||
|
createManySecretsRawFn,
|
||||||
|
updateManySecretsRawFn
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case Integrations.AWS_PARAMETER_STORE:
|
case Integrations.AWS_PARAMETER_STORE:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|
||||||
import { useCreateIntegration } from "@app/hooks/api";
|
import { useCreateIntegration } from "@app/hooks/api";
|
||||||
|
import { IntegrationSyncBehavior } from "@app/hooks/api/integrations/types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -16,6 +17,18 @@ import {
|
|||||||
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
|
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
|
||||||
|
const initialSyncBehaviors = [
|
||||||
|
{
|
||||||
|
label: "No Import - Overwrite all values in Azure Vault",
|
||||||
|
value: IntegrationSyncBehavior.OVERWRITE_TARGET
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Import - Prefer values from Azure Vault",
|
||||||
|
value: IntegrationSyncBehavior.PREFER_TARGET
|
||||||
|
},
|
||||||
|
{ label: "Import - Prefer values from Infisical", value: IntegrationSyncBehavior.PREFER_SOURCE }
|
||||||
|
];
|
||||||
|
|
||||||
export default function AzureKeyVaultCreateIntegrationPage() {
|
export default function AzureKeyVaultCreateIntegrationPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useCreateIntegration();
|
const { mutateAsync } = useCreateIntegration();
|
||||||
@@ -30,6 +43,9 @@ export default function AzureKeyVaultCreateIntegrationPage() {
|
|||||||
|
|
||||||
const [vaultBaseUrl, setVaultBaseUrl] = useState("");
|
const [vaultBaseUrl, setVaultBaseUrl] = useState("");
|
||||||
const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState("");
|
const [vaultBaseUrlErrorText, setVaultBaseUrlErrorText] = useState("");
|
||||||
|
const [initialSyncBehavior, setInitialSyncBehavior] = useState(
|
||||||
|
IntegrationSyncBehavior.PREFER_SOURCE
|
||||||
|
);
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@@ -59,7 +75,10 @@ export default function AzureKeyVaultCreateIntegrationPage() {
|
|||||||
isActive: true,
|
isActive: true,
|
||||||
app: vaultBaseUrl,
|
app: vaultBaseUrl,
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata: {
|
||||||
|
initialSyncBehavior
|
||||||
|
}
|
||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
@@ -107,6 +126,21 @@ export default function AzureKeyVaultCreateIntegrationPage() {
|
|||||||
onChange={(e) => setVaultBaseUrl(e.target.value)}
|
onChange={(e) => setVaultBaseUrl(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl label="Initial Sync Behavior">
|
||||||
|
<Select
|
||||||
|
value={initialSyncBehavior}
|
||||||
|
onValueChange={(e) => setInitialSyncBehavior(e as IntegrationSyncBehavior)}
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
{initialSyncBehaviors.map((b) => {
|
||||||
|
return (
|
||||||
|
<SelectItem value={b.value} key={`sync-behavior-${b.value}`}>
|
||||||
|
{b.label}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleButtonClick}
|
onClick={handleButtonClick}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
|||||||
Reference in New Issue
Block a user