requested changes

This commit is contained in:
Daniel Hougaard
2025-02-08 04:27:17 +04:00
parent 1b5b937db5
commit 8d52011173
27 changed files with 173 additions and 35 deletions
@@ -28,6 +28,7 @@ import { githubConnectionService } from "@app/services/app-connection/github/git
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TAppConnectionDALFactory } from "./app-connection-dal"; import { TAppConnectionDALFactory } from "./app-connection-dal";
import { ValidateAzureAppConfigurationConnectionCredentialsSchema } from "./azure-app-configuration";
import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault"; import { ValidateAzureKeyVaultConnectionCredentialsSchema } from "./azure-key-vault";
import { ValidateGcpConnectionCredentialsSchema } from "./gcp"; import { ValidateGcpConnectionCredentialsSchema } from "./gcp";
import { gcpConnectionService } from "./gcp/gcp-connection-service"; import { gcpConnectionService } from "./gcp/gcp-connection-service";
@@ -45,7 +46,7 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.GitHub]: ValidateGitHubConnectionCredentialsSchema, [AppConnection.GitHub]: ValidateGitHubConnectionCredentialsSchema,
[AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema, [AppConnection.GCP]: ValidateGcpConnectionCredentialsSchema,
[AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema, [AppConnection.AzureKeyVault]: ValidateAzureKeyVaultConnectionCredentialsSchema,
[AppConnection.AzureAppConfiguration]: ValidateAzureKeyVaultConnectionCredentialsSchema [AppConnection.AzureAppConfiguration]: ValidateAzureAppConfigurationConnectionCredentialsSchema
}; };
export const appConnectionServiceFactory = ({ export const appConnectionServiceFactory = ({
@@ -63,7 +63,6 @@ export const SanitizedAzureAppConfigurationConnectionSchema = z.discriminatedUni
BaseAzureAppConfigurationConnectionSchema.extend({ BaseAzureAppConfigurationConnectionSchema.extend({
method: z.literal(AzureAppConfigurationConnectionMethod.OAuth), method: z.literal(AzureAppConfigurationConnectionMethod.OAuth),
credentials: AzureAppConfigurationConnectionOAuthOutputCredentialsSchema.pick({ credentials: AzureAppConfigurationConnectionOAuthOutputCredentialsSchema.pick({
resource: true,
tenantId: true tenantId: true
}) })
}) })
@@ -63,7 +63,6 @@ export const SanitizedAzureKeyVaultConnectionSchema = z.discriminatedUnion("meth
BaseAzureKeyVaultConnectionSchema.extend({ BaseAzureKeyVaultConnectionSchema.extend({
method: z.literal(AzureKeyVaultConnectionMethod.OAuth), method: z.literal(AzureKeyVaultConnectionMethod.OAuth),
credentials: AzureKeyVaultConnectionOAuthOutputCredentialsSchema.pick({ credentials: AzureKeyVaultConnectionOAuthOutputCredentialsSchema.pick({
resource: true,
tenantId: true tenantId: true
}) })
}) })
@@ -19,6 +19,7 @@ type TAzureAppConfigurationSecretSyncFactoryDeps = {
interface AzureAppConfigKeyValue { interface AzureAppConfigKeyValue {
key: string; key: string;
value: string; value: string;
label?: string;
} }
export const azureAppConfigurationSecretSyncFactory = ({ export const azureAppConfigurationSecretSyncFactory = ({
@@ -78,7 +79,9 @@ export const azureAppConfigurationSecretSyncFactory = ({
secretSync.destinationConfig.label ? `&label=${secretSync.destinationConfig.label}` : "&label=%00" secretSync.destinationConfig.label ? `&label=${secretSync.destinationConfig.label}` : "&label=%00"
}`; }`;
const azureAppConfigSecrets = Object.fromEntries( const azureAppConfigValuesUrlAllSecrets = `/kv?api-version=2023-11-01`;
const azureAppConfigSecretsLabeled = Object.fromEntries(
( (
await $getCompleteAzureAppConfigValues( await $getCompleteAzureAppConfigValues(
accessToken, accessToken,
@@ -88,9 +91,25 @@ export const azureAppConfigurationSecretSyncFactory = ({
).map((entry) => [entry.key, entry.value]) ).map((entry) => [entry.key, entry.value])
); );
const azureAppConfigSecrets = Object.fromEntries(
(
await $getCompleteAzureAppConfigValues(
accessToken,
secretSync.destinationConfig.configurationUrl,
azureAppConfigValuesUrlAllSecrets
)
).map((entry) => [
entry.key,
{
value: entry.value,
label: entry.label
}
])
);
// add the secrets to azure app config, that are in infisical // add the secrets to azure app config, that are in infisical
for await (const key of Object.keys(secretMap)) { for await (const key of Object.keys(secretMap)) {
if (!(key in azureAppConfigSecrets) || secretMap[key]?.value !== azureAppConfigSecrets[key]) { if (!(key in azureAppConfigSecretsLabeled) || secretMap[key]?.value !== azureAppConfigSecretsLabeled[key]) {
await request.put( await request.put(
`${secretSync.destinationConfig.configurationUrl}/kv/${key}?api-version=2023-11-01`, `${secretSync.destinationConfig.configurationUrl}/kv/${key}?api-version=2023-11-01`,
{ {
@@ -117,14 +136,14 @@ export const azureAppConfigurationSecretSyncFactory = ({
} }
} }
// delete the secrets that are in azure app config, but not in infisical
for await (const key of Object.keys(azureAppConfigSecrets)) { for await (const key of Object.keys(azureAppConfigSecrets)) {
if (!(key in secretMap) || secretMap[key] === null) { const azureSecret = azureAppConfigSecrets[key];
if (!(key in secretMap) || secretMap[key] === null || azureSecret.label !== secretSync.destinationConfig.label) {
await $deleteAzureSecret( await $deleteAzureSecret(
accessToken, accessToken,
secretSync.destinationConfig.configurationUrl, secretSync.destinationConfig.configurationUrl,
key, key,
secretSync.destinationConfig.label azureSecret.label // use the secret's actual label for deletion
); );
} }
} }
@@ -7,6 +7,7 @@ import { getAzureConnectionAccessToken } from "@app/services/app-connection/azur
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types"; import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
import { SecretSyncError } from "../secret-sync-errors";
import { GetAzureKeyVaultSecret, TAzureKeyVaultSyncWithCredentials } from "./azure-key-vault-sync-types"; import { GetAzureKeyVaultSecret, TAzureKeyVaultSyncWithCredentials } from "./azure-key-vault-sync-types";
type TAzureKeyVaultSecretSyncFactoryDeps = { type TAzureKeyVaultSecretSyncFactoryDeps = {
@@ -127,6 +128,7 @@ export const azureKeyVaultSecretSyncFactory = ({
const setSecretAzureKeyVault = async ({ key, value }: { key: string; value: string }) => { const setSecretAzureKeyVault = async ({ key, value }: { key: string; value: string }) => {
let isSecretSet = false; let isSecretSet = false;
let syncError: Error | null = null;
let maxTries = 6; let maxTries = 6;
if (disabledAzureKeyVaultSecretKeys.includes(key)) return; if (disabledAzureKeyVaultSecretKeys.includes(key)) return;
@@ -147,6 +149,7 @@ export const azureKeyVaultSecretSyncFactory = ({
isSecretSet = true; isSecretSet = true;
} catch (err) { } catch (err) {
syncError = err as Error;
if (err instanceof AxiosError) { if (err instanceof AxiosError) {
// eslint-disable-next-line // eslint-disable-next-line
if (err.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") { if (err.response?.data?.error?.innererror?.code === "ObjectIsDeletedButRecoverable") {
@@ -174,7 +177,10 @@ export const azureKeyVaultSecretSyncFactory = ({
} }
if (!isSecretSet) { if (!isSecretSet) {
throw new Error(`Failed to set secret ${key}`); throw new SecretSyncError({
error: syncError,
secretKey: key
});
} }
}; };
@@ -0,0 +1,4 @@
---
title: "Available"
openapi: "GET /api/v1/app-connections/azure-app-configuration/available"
---
@@ -0,0 +1,10 @@
---
title: "Create"
openapi: "POST /api/v1/app-connections/azure-app-configuration"
---
<Note>
Azure App Configuration Connections must be created through the Infisical UI.
Check out the configuration docs for [Azure App Configuration Connections](/integrations/app-connections/azure-app-configuration) for a step-by-step
guide.
</Note>
@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/app-connections/azure-app-configuration/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/app-connections/azure-app-configuration/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/app-connections/azure-app-configuration/connection-name/{connectionName}"
---
@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/app-connections/azure-app-configuration"
---
@@ -0,0 +1,10 @@
---
title: "Update"
openapi: "PATCH /api/v1/app-connections/azure-app-configuration/{connectionId}"
---
<Note>
Azure App Configuration Connections must be updated through the Infisical UI.
Check out the configuration docs for [Azure App Configuration Connections](/integrations/app-connections/azure-app-configuration) for a step-by-step
guide.
</Note>
@@ -0,0 +1,4 @@
---
title: "Available"
openapi: "GET /api/v1/app-connections/azure-key-vault/available"
---
@@ -0,0 +1,10 @@
---
title: "Create"
openapi: "POST /api/v1/app-connections/azure-key-vault"
---
<Note>
Azure Key Vault Connections must be created through the Infisical UI.
Check out the configuration docs for [Azure Key Vault Connections](/integrations/app-connections/azure-key-vault) for a step-by-step
guide.
</Note>
@@ -0,0 +1,4 @@
---
title: "Delete"
openapi: "DELETE /api/v1/app-connections/azure-key-vault/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by ID"
openapi: "GET /api/v1/app-connections/azure-key-vault/{connectionId}"
---
@@ -0,0 +1,4 @@
---
title: "Get by Name"
openapi: "GET /api/v1/app-connections/azure-key-vault/connection-name/{connectionName}"
---
@@ -0,0 +1,4 @@
---
title: "List"
openapi: "GET /api/v1/app-connections/azure-key-vault"
---
@@ -0,0 +1,10 @@
---
title: "Update"
openapi: "PATCH /api/v1/app-connections/azure-key-vault/{connectionId}"
---
<Note>
Azure Key Vault Connections must be updated through the Infisical UI.
Check out the configuration docs for [Azure Key Vault Connections](/integrations/app-connections/azure-key-vault) for a step-by-step
guide.
</Note>
@@ -80,7 +80,7 @@ Infisical currently only supports one method for connecting to Azure, which is O
</Step> </Step>
<Step title="Grant Access"> <Step title="Grant Access">
You will then be redirected to Azure to grant Infisical access to your Azure account (organization and repo privileges). Once granted, You will then be redirected to Azure to grant Infisical access to your Azure account. Once granted,
you will redirect you back to Infisical's App Connections page. ![Azure App Configuration you will redirect you back to Infisical's App Connections page. ![Azure App Configuration
Authorization](/images/app-connections/azure/grant-access.png) Authorization](/images/app-connections/azure/grant-access.png)
</Step> </Step>
@@ -32,7 +32,7 @@ Infisical currently only supports one method for connecting to Azure, which is O
</Step> </Step>
<Step title="Assign API permissions to the application"> <Step title="Assign API permissions to the application">
For the Azure Connection to work with both Key Vault, you need to assign multiple permissions to the application. For the Azure Connection to work with Key Vault, you need to assign multiple permissions to the application.
#### Azure Key Vault permissions #### Azure Key Vault permissions
@@ -79,7 +79,7 @@ Infisical currently only supports one method for connecting to Azure, which is O
</Step> </Step>
<Step title="Grant Access"> <Step title="Grant Access">
You will then be redirected to Azure to grant Infisical access to your Azure account (organization and repo privileges). Once granted, You will then be redirected to Azure to grant Infisical access to your Azure account. Once granted,
you will redirect you back to Infisical's App Connections page. ![Azure Key Vault you will redirect you back to Infisical's App Connections page. ![Azure Key Vault
Authorization](/images/app-connections/azure/grant-access.png) Authorization](/images/app-connections/azure/grant-access.png)
</Step> </Step>
@@ -12,8 +12,7 @@ description: "Learn how to configure an Azure App Configuration Sync for Infisic
The Azure App Configuration Secret Sync requires the following permissions to be set on the user / service principal The Azure App Configuration Secret Sync requires the following permissions to be set on the user / service principal
for Infisical to sync secrets to Azure App Configuration: `Read Key-Value`, `Write Key-Value`, `Delete Key-Value`. for Infisical to sync secrets to Azure App Configuration: `Read Key-Value`, `Write Key-Value`, `Delete Key-Value`.
Any role with these permissions would work such as the **App Configuration Data Owner** role. Alternatively, you can use the Any role with these permissions would work such as the **App Configuration Data Owner** role. Alternatively, you can use the **App Configuration Data Contributor** role for read/write access.
**App Configuration Data Reader** role for read-only access or **App Configuration Data Contributor** role for read/write access.
</Note> </Note>
<Tabs> <Tabs>
@@ -44,11 +43,12 @@ description: "Learn how to configure an Azure App Configuration Sync for Infisic
5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**. 5. Configure the **Sync Options** to specify how secrets should be synced, then click **Next**.
![Configure Options](/images/secret-syncs/azure-app-configuration/app-config-options.png) ![Configure Options](/images/secret-syncs/azure-app-configuration/app-config-options.png)
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync. - **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical. - **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
<Note> - **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over Secrets Manager when keys conflict.
Azure App Configuration does not support importing secrets. - **Import Secrets (Prioritize Azure App Configuration)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Secrets Manager over Infisical when keys conflict.
</Note>
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only. - **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
6. Configure the **Details** of your Azure App Configuration Sync, then click **Next**. 6. Configure the **Details** of your Azure App Configuration Sync, then click **Next**.
@@ -49,9 +49,8 @@ description: "Learn how to configure a Azure Key Vault Sync for Infisical."
- **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync. - **Initial Sync Behavior**: Determines how Infisical should resolve the initial sync.
- **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical. - **Overwrite Destination Secrets**: Removes any secrets at the destination endpoint not present in Infisical.
<Note> - **Import Secrets (Prioritize Infisical)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Infisical over Secrets Manager when keys conflict.
Azure Key Vault does not support importing secrets. - **Import Secrets (Prioritize Azure Key Vault)**: Imports secrets from the destination endpoint before syncing, prioritizing values from Secrets Manager over Infisical when keys conflict.
</Note>
- **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only. - **Auto-Sync Enabled**: If enabled, secrets will automatically be synced from the source location when changes occur. Disable to enforce manual syncing only.
6. Configure the **Details** of your Azure Key Vault Sync, then click **Next**. 6. Configure the **Details** of your Azure Key Vault Sync, then click **Next**.
+25 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "Infisical", "name": "Infisical",
"openapi": "https://2eb781d275f5.ngrok.app/api/docs/json", "openapi": "https://app.infisical.com/api/docs/json",
"logo": { "logo": {
"dark": "/logo/dark.svg", "dark": "/logo/dark.svg",
"light": "/logo/light.svg", "light": "/logo/light.svg",
@@ -847,6 +847,30 @@
"api-reference/endpoints/app-connections/gcp/update", "api-reference/endpoints/app-connections/gcp/update",
"api-reference/endpoints/app-connections/gcp/delete" "api-reference/endpoints/app-connections/gcp/delete"
] ]
},
{
"group": "Azure Key Vault",
"pages": [
"api-reference/endpoints/app-connections/azure-key-vault/list",
"api-reference/endpoints/app-connections/azure-key-vault/available",
"api-reference/endpoints/app-connections/azure-key-vault/get-by-id",
"api-reference/endpoints/app-connections/azure-key-vault/get-by-name",
"api-reference/endpoints/app-connections/azure-key-vault/create",
"api-reference/endpoints/app-connections/azure-key-vault/update",
"api-reference/endpoints/app-connections/azure-key-vault/delete"
]
},
{
"group": "Azure App Configuration",
"pages": [
"api-reference/endpoints/app-connections/azure-app-configuration/list",
"api-reference/endpoints/app-connections/azure-app-configuration/available",
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-id",
"api-reference/endpoints/app-connections/azure-app-configuration/get-by-name",
"api-reference/endpoints/app-connections/azure-app-configuration/create",
"api-reference/endpoints/app-connections/azure-app-configuration/update",
"api-reference/endpoints/app-connections/azure-app-configuration/delete"
]
} }
] ]
}, },
@@ -8,6 +8,7 @@ import {
AzureAppConfigurationConnectionMethod, AzureAppConfigurationConnectionMethod,
AzureKeyVaultConnectionMethod, AzureKeyVaultConnectionMethod,
GitHubConnectionMethod, GitHubConnectionMethod,
TAzureAppConfigurationConnection,
TAzureKeyVaultConnection, TAzureKeyVaultConnection,
TGitHubConnection, TGitHubConnection,
useCreateAppConnection, useCreateAppConnection,
@@ -15,20 +16,25 @@ import {
} from "@app/hooks/api/appConnections"; } from "@app/hooks/api/appConnections";
import { AppConnection } from "@app/hooks/api/appConnections/enums"; import { AppConnection } from "@app/hooks/api/appConnections/enums";
type GithubFormData = Pick<TGitHubConnection, "name" | "method" | "description"> & { type BaseFormData = {
returnUrl?: string; returnUrl?: string;
connectionId?: string; connectionId?: string;
}; };
type AzureFormData = Pick<TGitHubConnection, "name" | "method" | "description"> & { type GithubFormData = BaseFormData & Pick<TGitHubConnection, "name" | "method" | "description">;
returnUrl?: string;
connectionId?: string; type AzureKeyVaultFormData = BaseFormData &
} & Pick<TAzureKeyVaultConnection["credentials"], "tenantId">; Pick<TAzureKeyVaultConnection, "name" | "method" | "description"> &
Pick<TAzureKeyVaultConnection["credentials"], "tenantId">;
type AzureAppConfigurationFormData = BaseFormData &
Pick<TAzureAppConfigurationConnection, "name" | "method" | "description"> &
Pick<TAzureAppConfigurationConnection["credentials"], "tenantId">;
type FormDataMap = { type FormDataMap = {
[AppConnection.GitHub]: GithubFormData & { app: AppConnection.GitHub }; [AppConnection.GitHub]: GithubFormData & { app: AppConnection.GitHub };
[AppConnection.AzureKeyVault]: AzureFormData & { app: AppConnection.AzureKeyVault }; [AppConnection.AzureKeyVault]: AzureKeyVaultFormData & { app: AppConnection.AzureKeyVault };
[AppConnection.AzureAppConfiguration]: AzureFormData & { [AppConnection.AzureAppConfiguration]: AzureAppConfigurationFormData & {
app: AppConnection.AzureAppConfiguration; app: AppConnection.AzureAppConfiguration;
}; };
}; };
@@ -105,7 +111,8 @@ export const OAuthCallbackPage = () => {
app: AppConnection.AzureKeyVault, app: AppConnection.AzureKeyVault,
connectionId, connectionId,
credentials: { credentials: {
code: code as string code: code as string,
tenantId: formData.tenantId
} }
}); });
} else { } else {
@@ -122,7 +129,7 @@ export const OAuthCallbackPage = () => {
} }
} catch (err: any) { } catch (err: any) {
createNotification({ createNotification({
title: `Failed to ${connectionId ? "update" : "add"} Azure Connection`, title: `Failed to ${connectionId ? "update" : "add"} Azure Key Vault Connection`,
text: err?.message, text: err?.message,
type: "error" type: "error"
}); });
@@ -152,7 +159,8 @@ export const OAuthCallbackPage = () => {
app: AppConnection.AzureAppConfiguration, app: AppConnection.AzureAppConfiguration,
connectionId, connectionId,
credentials: { credentials: {
code: code as string code: code as string,
tenantId: formData.tenantId
} }
}); });
} else { } else {
@@ -162,13 +170,14 @@ export const OAuthCallbackPage = () => {
description, description,
method: AzureAppConfigurationConnectionMethod.OAuth, method: AzureAppConfigurationConnectionMethod.OAuth,
credentials: { credentials: {
code: code as string code: code as string,
tenantId: formData.tenantId
} }
}); });
} }
} catch (err: any) { } catch (err: any) {
createNotification({ createNotification({
title: `Failed to ${connectionId ? "update" : "add"} Azure Connection`, title: `Failed to ${connectionId ? "update" : "add"} Azure App Configuration Connection`,
text: err?.message, text: err?.message,
type: "error" type: "error"
}); });
@@ -45,7 +45,8 @@ export const AzureAppConfigurationConnectionForm = ({ appConnection }: Props) =>
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: appConnection defaultValues: appConnection
? { ? {
...appConnection ...appConnection,
tenantId: appConnection.credentials.tenantId
} }
: { : {
app: AppConnection.AzureAppConfiguration, app: AppConnection.AzureAppConfiguration,
@@ -45,7 +45,8 @@ export const AzureKeyVaultConnectionForm = ({ appConnection }: Props) => {
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: appConnection defaultValues: appConnection
? { ? {
...appConnection ...appConnection,
tenantId: appConnection.credentials.tenantId
} }
: { : {
app: AppConnection.AzureKeyVault, app: AppConnection.AzureKeyVault,