mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 11:29:42 +00:00
Merge pull request #3598 from Infisical/ENG-2755
feat(secret-sync): Secret Key Schema
This commit is contained in:
@@ -2144,6 +2144,7 @@ export const SecretSyncs = {
|
||||
const destinationName = SECRET_SYNC_NAME_MAP[destination];
|
||||
return {
|
||||
initialSyncBehavior: `Specify how Infisical should resolve the initial sync to the ${destinationName} destination.`,
|
||||
keySchema: `Specify the format to use for structuring secret keys in the ${destinationName} destination.`,
|
||||
disableSecretDeletion: `Enable this flag to prevent removal of secrets from the ${destinationName} destination when syncing.`
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import AWS, { AWSError } from "aws-sdk";
|
||||
|
||||
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TAwsParameterStoreSyncWithCredentials } from "./aws-parameter-store-sync-types";
|
||||
@@ -389,6 +390,9 @@ export const AwsParameterStoreSyncFns = {
|
||||
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
||||
const [key, parameter] = entry;
|
||||
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(key in secretMap) || !secretMap[key].value) {
|
||||
parametersToDelete.push(parameter);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
|
||||
import { AwsSecretsManagerSyncMappingBehavior } from "@app/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-enums";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TAwsSecretsManagerSyncWithCredentials } from "./aws-secrets-manager-sync-types";
|
||||
@@ -399,6 +400,9 @@ export const AwsSecretsManagerSyncFns = {
|
||||
if (syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const secretKey of Object.keys(awsSecretsRecord)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(secretKey, syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(secretKey in secretMap) || !secretMap[secretKey].value) {
|
||||
try {
|
||||
await deleteSecret(client, secretKey);
|
||||
|
||||
+4
@@ -7,6 +7,7 @@ import { TAppConnectionDALFactory } from "@app/services/app-connection/app-conne
|
||||
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||
import { isAzureKeyVaultReference } from "@app/services/integration-auth/integration-sync-secret-fns";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { TAzureAppConfigurationSyncWithCredentials } from "./azure-app-configuration-sync-types";
|
||||
@@ -139,6 +140,9 @@ export const azureAppConfigurationSyncFactory = ({
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const key of Object.keys(azureAppConfigSecrets)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
const azureSecret = azureAppConfigSecrets[key];
|
||||
if (
|
||||
!(key in secretMap) ||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
@@ -192,7 +193,9 @@ export const azureKeyVaultSyncFactory = ({ kmsService, appConnectionDAL }: TAzur
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const deleteSecretKey of deleteSecrets.filter(
|
||||
(secret) => !setSecrets.find((setSecret) => setSecret.key === secret)
|
||||
(secret) =>
|
||||
matchesSchema(secret, secretSync.syncOptions.keySchema) &&
|
||||
!setSecrets.find((setSecret) => setSecret.key === secret)
|
||||
)) {
|
||||
await request.delete(`${secretSync.destinationConfig.vaultBaseUrl}/secrets/${deleteSecretKey}?api-version=7.3`, {
|
||||
headers: {
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
TCamundaSyncWithCredentials
|
||||
} from "@app/services/secret-sync/camunda/camunda-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
|
||||
@@ -116,6 +117,9 @@ export const camundaSyncFactory = ({ kmsService, appConnectionDAL }: TCamundaSec
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const secret of Object.keys(camundaSecrets)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(secret, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(secret in secretMap) || !secretMap[secret].value) {
|
||||
try {
|
||||
await deleteCamundaSecret({
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TDatabricksSyncWithCredentials
|
||||
} from "@app/services/secret-sync/databricks/databricks-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
@@ -115,6 +116,9 @@ export const databricksSyncFactory = ({ kmsService, appConnectionDAL }: TDatabri
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const secret of databricksSecretKeys) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(secret.key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(secret.key in secretMap)) {
|
||||
await deleteDatabricksSecrets({
|
||||
key: secret.key,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { getGcpConnectionAuthToken } from "@app/services/app-connection/gcp";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
|
||||
import { SecretSyncError } from "../secret-sync-errors";
|
||||
import { TSecretMap } from "../secret-sync-types";
|
||||
@@ -153,6 +154,9 @@ export const GcpSyncFns = {
|
||||
}
|
||||
|
||||
for await (const key of Object.keys(gcpSecrets)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
try {
|
||||
if (!(key in secretMap) || !secretMap[key].value) {
|
||||
// eslint-disable-next-line no-continue
|
||||
|
||||
@@ -4,6 +4,7 @@ import sodium from "libsodium-wrappers";
|
||||
import { getGitHubClient } from "@app/services/app-connection/github";
|
||||
import { GitHubSyncScope, GitHubSyncVisibility } from "@app/services/secret-sync/github/github-sync-enums";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
@@ -222,6 +223,9 @@ export const GithubSyncFns = {
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const encryptedSecret of encryptedSecrets) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(encryptedSecret.name, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(encryptedSecret.name in secretMap)) {
|
||||
await deleteSecret(client, secretSync, encryptedSecret);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TPostHCVaultVariable
|
||||
} from "@app/services/secret-sync/hc-vault/hc-vault-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const listHCVaultVariables = async ({ instanceUrl, namespace, mount, accessToken, path }: THCVaultListVariables) => {
|
||||
@@ -68,7 +69,7 @@ export const HCVaultSyncFns = {
|
||||
const {
|
||||
connection,
|
||||
destinationConfig: { mount, path },
|
||||
syncOptions: { disableSecretDeletion }
|
||||
syncOptions: { disableSecretDeletion, keySchema }
|
||||
} = secretSync;
|
||||
|
||||
const { namespace } = connection.credentials;
|
||||
@@ -95,6 +96,9 @@ export const HCVaultSyncFns = {
|
||||
if (disableSecretDeletion) return;
|
||||
|
||||
for await (const [key] of Object.entries(variables)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, keySchema)) continue;
|
||||
|
||||
if (!(key in secretMap)) {
|
||||
delete variables[key];
|
||||
tainted = true;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { request } from "@app/lib/config/request";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
@@ -199,6 +200,9 @@ export const HumanitecSyncFns = {
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const humanitecSecret of humanitecSecrets) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(humanitecSecret.key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!secretMap[humanitecSecret.key]) {
|
||||
await deleteSecret(secretSync, humanitecSecret);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
TUpdateOCIVaultVariable
|
||||
} from "@app/services/secret-sync/oci-vault/oci-vault-sync-types";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const listOCIVaultVariables = async ({ provider, compartmentId, vaultId, onlyActive }: TOCIVaultListVariables) => {
|
||||
@@ -211,6 +212,9 @@ export const OCIVaultSyncFns = {
|
||||
|
||||
// Update and delete secrets
|
||||
for await (const [key, variable] of Object.entries(variables)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
// Only update / delete active secrets
|
||||
if (variable.lifecycleState === vault.models.SecretSummary.LifecycleState.Active) {
|
||||
if (key in secretMap && secretMap[key].value.length > 0) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AxiosError } from "axios";
|
||||
import RE2 from "re2";
|
||||
|
||||
import {
|
||||
AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
|
||||
@@ -61,45 +62,63 @@ type TSyncSecretDeps = {
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
};
|
||||
|
||||
// const addAffixes = (secretSync: TSecretSyncWithCredentials, unprocessedSecretMap: TSecretMap) => {
|
||||
// let secretMap = { ...unprocessedSecretMap };
|
||||
//
|
||||
// const { appendSuffix, prependPrefix } = secretSync.syncOptions;
|
||||
//
|
||||
// if (appendSuffix || prependPrefix) {
|
||||
// secretMap = {};
|
||||
// Object.entries(unprocessedSecretMap).forEach(([key, value]) => {
|
||||
// secretMap[`${prependPrefix || ""}${key}${appendSuffix || ""}`] = value;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return secretMap;
|
||||
// };
|
||||
//
|
||||
// const stripAffixes = (secretSync: TSecretSyncWithCredentials, unprocessedSecretMap: TSecretMap) => {
|
||||
// let secretMap = { ...unprocessedSecretMap };
|
||||
//
|
||||
// const { appendSuffix, prependPrefix } = secretSync.syncOptions;
|
||||
//
|
||||
// if (appendSuffix || prependPrefix) {
|
||||
// secretMap = {};
|
||||
// Object.entries(unprocessedSecretMap).forEach(([key, value]) => {
|
||||
// let processedKey = key;
|
||||
//
|
||||
// if (prependPrefix && processedKey.startsWith(prependPrefix)) {
|
||||
// processedKey = processedKey.slice(prependPrefix.length);
|
||||
// }
|
||||
//
|
||||
// if (appendSuffix && processedKey.endsWith(appendSuffix)) {
|
||||
// processedKey = processedKey.slice(0, -appendSuffix.length);
|
||||
// }
|
||||
//
|
||||
// secretMap[processedKey] = value;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// return secretMap;
|
||||
// };
|
||||
// Add schema to secret keys
|
||||
const addSchema = (unprocessedSecretMap: TSecretMap, schema?: string): TSecretMap => {
|
||||
if (!schema) return unprocessedSecretMap;
|
||||
|
||||
const processedSecretMap: TSecretMap = {};
|
||||
|
||||
for (const [key, value] of Object.entries(unprocessedSecretMap)) {
|
||||
const newKey = new RE2("{{secretKey}}").replace(schema, key);
|
||||
processedSecretMap[newKey] = value;
|
||||
}
|
||||
|
||||
return processedSecretMap;
|
||||
};
|
||||
|
||||
// Strip schema from secret keys
|
||||
const stripSchema = (unprocessedSecretMap: TSecretMap, schema?: string): TSecretMap => {
|
||||
if (!schema) return unprocessedSecretMap;
|
||||
|
||||
const [prefix, suffix] = schema.split("{{secretKey}}");
|
||||
|
||||
const strippedMap: TSecretMap = {};
|
||||
|
||||
for (const [key, value] of Object.entries(unprocessedSecretMap)) {
|
||||
if (!key.startsWith(prefix) || !key.endsWith(suffix)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
continue;
|
||||
}
|
||||
|
||||
const strippedKey = key.slice(prefix.length, key.length - suffix.length);
|
||||
strippedMap[strippedKey] = value;
|
||||
}
|
||||
|
||||
return strippedMap;
|
||||
};
|
||||
|
||||
// Checks if a key matches a schema
|
||||
export const matchesSchema = (key: string, schema?: string): boolean => {
|
||||
if (!schema) return true;
|
||||
|
||||
const [prefix, suffix] = schema.split("{{secretKey}}");
|
||||
if (prefix === undefined || suffix === undefined) return true;
|
||||
|
||||
return key.startsWith(prefix) && key.endsWith(suffix);
|
||||
};
|
||||
|
||||
// Filter only for secrets with keys that match the schema
|
||||
const filterForSchema = (secretMap: TSecretMap, schema?: string): TSecretMap => {
|
||||
const filteredMap: TSecretMap = {};
|
||||
|
||||
for (const [key, value] of Object.entries(secretMap)) {
|
||||
if (matchesSchema(key, schema)) {
|
||||
filteredMap[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return filteredMap;
|
||||
};
|
||||
|
||||
export const SecretSyncFns = {
|
||||
syncSecrets: (
|
||||
@@ -107,51 +126,51 @@ export const SecretSyncFns = {
|
||||
secretMap: TSecretMap,
|
||||
{ kmsService, appConnectionDAL }: TSyncSecretDeps
|
||||
): Promise<void> => {
|
||||
// const affixedSecretMap = addAffixes(secretSync, secretMap);
|
||||
const schemaSecretMap = addSchema(secretMap, secretSync.syncOptions.keySchema);
|
||||
|
||||
switch (secretSync.destination) {
|
||||
case SecretSync.AWSParameterStore:
|
||||
return AwsParameterStoreSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return AwsParameterStoreSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AWSSecretsManager:
|
||||
return AwsSecretsManagerSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return AwsSecretsManagerSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GitHub:
|
||||
return GithubSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return GithubSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GCPSecretManager:
|
||||
return GcpSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return GcpSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AzureKeyVault:
|
||||
return azureKeyVaultSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
}).syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AzureAppConfiguration:
|
||||
return azureAppConfigurationSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
}).syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Databricks:
|
||||
return databricksSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
}).syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Humanitec:
|
||||
return HumanitecSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return HumanitecSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.TerraformCloud:
|
||||
return TerraformCloudSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return TerraformCloudSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Camunda:
|
||||
return camundaSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
}).syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Vercel:
|
||||
return VercelSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return VercelSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Windmill:
|
||||
return WindmillSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return WindmillSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.HCVault:
|
||||
return HCVaultSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return HCVaultSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.TeamCity:
|
||||
return TeamCitySyncFns.syncSecrets(secretSync, secretMap);
|
||||
return TeamCitySyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.OCIVault:
|
||||
return OCIVaultSyncFns.syncSecrets(secretSync, secretMap);
|
||||
return OCIVaultSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -226,59 +245,58 @@ export const SecretSyncFns = {
|
||||
);
|
||||
}
|
||||
|
||||
return secretMap;
|
||||
// return stripAffixes(secretSync, secretMap);
|
||||
return stripSchema(filterForSchema(secretMap), secretSync.syncOptions.keySchema);
|
||||
},
|
||||
removeSecrets: (
|
||||
secretSync: TSecretSyncWithCredentials,
|
||||
secretMap: TSecretMap,
|
||||
{ kmsService, appConnectionDAL }: TSyncSecretDeps
|
||||
): Promise<void> => {
|
||||
// const affixedSecretMap = addAffixes(secretSync, secretMap);
|
||||
const schemaSecretMap = addSchema(secretMap, secretSync.syncOptions.keySchema);
|
||||
|
||||
switch (secretSync.destination) {
|
||||
case SecretSync.AWSParameterStore:
|
||||
return AwsParameterStoreSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return AwsParameterStoreSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AWSSecretsManager:
|
||||
return AwsSecretsManagerSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return AwsSecretsManagerSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GitHub:
|
||||
return GithubSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return GithubSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.GCPSecretManager:
|
||||
return GcpSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return GcpSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AzureKeyVault:
|
||||
return azureKeyVaultSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
}).removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.AzureAppConfiguration:
|
||||
return azureAppConfigurationSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
}).removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Databricks:
|
||||
return databricksSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
}).removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Humanitec:
|
||||
return HumanitecSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return HumanitecSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.TerraformCloud:
|
||||
return TerraformCloudSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return TerraformCloudSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Camunda:
|
||||
return camundaSyncFactory({
|
||||
appConnectionDAL,
|
||||
kmsService
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
}).removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Vercel:
|
||||
return VercelSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return VercelSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Windmill:
|
||||
return WindmillSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return WindmillSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.HCVault:
|
||||
return HCVaultSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return HCVaultSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.TeamCity:
|
||||
return TeamCitySyncFns.removeSecrets(secretSync, secretMap);
|
||||
return TeamCitySyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.OCIVault:
|
||||
return OCIVaultSyncFns.removeSecrets(secretSync, secretMap);
|
||||
return OCIVaultSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import RE2 from "re2";
|
||||
import { AnyZodObject, z } from "zod";
|
||||
|
||||
import { SecretSyncsSchema } from "@app/db/schemas/secret-syncs";
|
||||
@@ -24,6 +25,14 @@ const BaseSyncOptionsSchema = <T extends AnyZodObject | undefined = undefined>({
|
||||
? z.nativeEnum(SecretSyncInitialSyncBehavior)
|
||||
: z.literal(SecretSyncInitialSyncBehavior.OverwriteDestination)
|
||||
).describe(SecretSyncs.SYNC_OPTIONS(destination).initialSyncBehavior),
|
||||
keySchema: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine((val) => !val || new RE2(/^(?:[a-zA-Z0-9\-/]*)(?:\{\{secretKey\}\})(?:[a-zA-Z0-9\-/]*)$/).test(val), {
|
||||
message:
|
||||
"Key schema must include one {{secretKey}} and only contain letters, numbers, dashes, slashes, and the {{secretKey}} placeholder."
|
||||
})
|
||||
.describe(SecretSyncs.SYNC_OPTIONS(destination).keySchema),
|
||||
disableSecretDeletion: z.boolean().optional().describe(SecretSyncs.SYNC_OPTIONS(destination).disableSecretDeletion)
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { getTeamCityInstanceUrl } from "@app/services/app-connection/teamcity";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
import {
|
||||
TDeleteTeamCityVariable,
|
||||
@@ -125,6 +126,9 @@ export const TeamCitySyncFns = {
|
||||
const variables = await listTeamCityVariables({ instanceUrl, accessToken, project, buildConfig });
|
||||
|
||||
for await (const [key, variable] of Object.entries(variables)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!(key in secretMap)) {
|
||||
try {
|
||||
await deleteTeamCityVariable({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AxiosResponse } from "axios";
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
@@ -231,6 +232,9 @@ export const TerraformCloudSyncFns = {
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for (const terraformCloudVariable of terraformCloudVariables) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(terraformCloudVariable.key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(secretMap, terraformCloudVariable.key)) {
|
||||
await deleteVariable(secretSync, terraformCloudVariable);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { VercelEnvironmentType } from "./vercel-sync-enums";
|
||||
@@ -290,6 +291,9 @@ export const VercelSyncFns = {
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for await (const vercelSecret of vercelSecrets) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(vercelSecret.key, secretSync.syncOptions.keySchema)) continue;
|
||||
|
||||
if (!secretMap[vercelSecret.key]) {
|
||||
await deleteSecret(secretSync, vercelSecret);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { getWindmillInstanceUrl } from "@app/services/app-connection/windmill";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||
import {
|
||||
TDeleteWindmillVariable,
|
||||
TPostWindmillVariable,
|
||||
@@ -128,7 +129,7 @@ export const WindmillSyncFns = {
|
||||
const {
|
||||
connection,
|
||||
destinationConfig: { path },
|
||||
syncOptions: { disableSecretDeletion }
|
||||
syncOptions: { disableSecretDeletion, keySchema }
|
||||
} = secretSync;
|
||||
|
||||
// url needs to be lowercase
|
||||
@@ -169,6 +170,9 @@ export const WindmillSyncFns = {
|
||||
if (disableSecretDeletion) return;
|
||||
|
||||
for await (const [key, variable] of Object.entries(variables)) {
|
||||
// eslint-disable-next-line no-continue
|
||||
if (!matchesSchema(key, keySchema)) continue;
|
||||
|
||||
if (!(key in secretMap)) {
|
||||
try {
|
||||
await deleteWindmillVariable({
|
||||
|
||||
Reference in New Issue
Block a user