mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 13:27:22 +00:00
final release
This commit is contained in:
@@ -367,17 +367,7 @@ export const registerSyncSecretsEndpoints = <T extends TSecretSync, I extends TS
|
|||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
importBehavior: z
|
importBehavior: z
|
||||||
.nativeEnum(SecretSyncImportBehavior)
|
.nativeEnum(SecretSyncImportBehavior)
|
||||||
.describe(SecretSyncs.IMPORT_SECRETS(destination).importBehavior),
|
.describe(SecretSyncs.IMPORT_SECRETS(destination).importBehavior)
|
||||||
filterForSchema: z
|
|
||||||
.enum(["true", "false"])
|
|
||||||
.optional()
|
|
||||||
.default("false")
|
|
||||||
.transform((v) => v === "true"),
|
|
||||||
stripSchema: z
|
|
||||||
.enum(["true", "false"])
|
|
||||||
.optional()
|
|
||||||
.default("false")
|
|
||||||
.transform((v) => v === "true")
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({ secretSync: responseSchema })
|
200: z.object({ secretSync: responseSchema })
|
||||||
@@ -386,15 +376,13 @@ export const registerSyncSecretsEndpoints = <T extends TSecretSync, I extends TS
|
|||||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const { syncId } = req.params;
|
const { syncId } = req.params;
|
||||||
const { importBehavior, filterForSchema, stripSchema } = req.query;
|
const { importBehavior } = req.query;
|
||||||
|
|
||||||
const secretSync = (await server.services.secretSync.triggerSecretSyncImportSecretsById(
|
const secretSync = (await server.services.secretSync.triggerSecretSyncImportSecretsById(
|
||||||
{
|
{
|
||||||
syncId,
|
syncId,
|
||||||
destination,
|
destination,
|
||||||
importBehavior,
|
importBehavior
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
},
|
},
|
||||||
req.permission
|
req.permission
|
||||||
)) as T;
|
)) as T;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import AWS, { AWSError } from "aws-sdk";
|
|||||||
|
|
||||||
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
|
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { TAwsParameterStoreSyncWithCredentials } from "./aws-parameter-store-sync-types";
|
import { TAwsParameterStoreSyncWithCredentials } from "./aws-parameter-store-sync-types";
|
||||||
@@ -389,6 +390,9 @@ export const AwsParameterStoreSyncFns = {
|
|||||||
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
for (const entry of Object.entries(awsParameterStoreSecretsRecord)) {
|
||||||
const [key, parameter] = entry;
|
const [key, parameter] = entry;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(key, syncOptions.keySchema)) continue;
|
||||||
|
|
||||||
if (!(key in secretMap) || !secretMap[key].value) {
|
if (!(key in secretMap) || !secretMap[key].value) {
|
||||||
parametersToDelete.push(parameter);
|
parametersToDelete.push(parameter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
import { getAwsConnectionConfig } from "@app/services/app-connection/aws/aws-connection-fns";
|
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 { 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 { 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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { TAwsSecretsManagerSyncWithCredentials } from "./aws-secrets-manager-sync-types";
|
import { TAwsSecretsManagerSyncWithCredentials } from "./aws-secrets-manager-sync-types";
|
||||||
@@ -399,6 +400,9 @@ export const AwsSecretsManagerSyncFns = {
|
|||||||
if (syncOptions.disableSecretDeletion) return;
|
if (syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const secretKey of Object.keys(awsSecretsRecord)) {
|
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) {
|
if (!(secretKey in secretMap) || !secretMap[secretKey].value) {
|
||||||
try {
|
try {
|
||||||
await deleteSecret(client, secretKey);
|
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 { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||||
import { isAzureKeyVaultReference } from "@app/services/integration-auth/integration-sync-secret-fns";
|
import { isAzureKeyVaultReference } from "@app/services/integration-auth/integration-sync-secret-fns";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { TAzureAppConfigurationSyncWithCredentials } from "./azure-app-configuration-sync-types";
|
import { TAzureAppConfigurationSyncWithCredentials } from "./azure-app-configuration-sync-types";
|
||||||
@@ -139,6 +140,9 @@ export const azureAppConfigurationSyncFactory = ({
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const key of Object.keys(azureAppConfigSecrets)) {
|
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];
|
const azureSecret = azureAppConfigSecrets[key];
|
||||||
if (
|
if (
|
||||||
!(key in secretMap) ||
|
!(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 { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
import { getAzureConnectionAccessToken } from "@app/services/app-connection/azure-key-vault";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { SecretSyncError } from "../secret-sync-errors";
|
import { SecretSyncError } from "../secret-sync-errors";
|
||||||
@@ -192,7 +193,9 @@ export const azureKeyVaultSyncFactory = ({ kmsService, appConnectionDAL }: TAzur
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const deleteSecretKey of deleteSecrets.filter(
|
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`, {
|
await request.delete(`${secretSync.destinationConfig.vaultBaseUrl}/secrets/${deleteSecretKey}?api-version=7.3`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
TCamundaSyncWithCredentials
|
TCamundaSyncWithCredentials
|
||||||
} from "@app/services/secret-sync/camunda/camunda-sync-types";
|
} from "@app/services/secret-sync/camunda/camunda-sync-types";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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";
|
import { TSecretMap } from "../secret-sync-types";
|
||||||
|
|
||||||
@@ -116,6 +117,9 @@ export const camundaSyncFactory = ({ kmsService, appConnectionDAL }: TCamundaSec
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const secret of Object.keys(camundaSecrets)) {
|
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) {
|
if (!(secret in secretMap) || !secretMap[secret].value) {
|
||||||
try {
|
try {
|
||||||
await deleteCamundaSecret({
|
await deleteCamundaSecret({
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
TDatabricksSyncWithCredentials
|
TDatabricksSyncWithCredentials
|
||||||
} from "@app/services/secret-sync/databricks/databricks-sync-types";
|
} from "@app/services/secret-sync/databricks/databricks-sync-types";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
|
|
||||||
import { TSecretMap } from "../secret-sync-types";
|
import { TSecretMap } from "../secret-sync-types";
|
||||||
@@ -115,6 +116,9 @@ export const databricksSyncFactory = ({ kmsService, appConnectionDAL }: TDatabri
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const secret of databricksSecretKeys) {
|
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)) {
|
if (!(secret.key in secretMap)) {
|
||||||
await deleteDatabricksSecrets({
|
await deleteDatabricksSecrets({
|
||||||
key: secret.key,
|
key: secret.key,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { request } from "@app/lib/config/request";
|
|||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { getGcpConnectionAuthToken } from "@app/services/app-connection/gcp";
|
import { getGcpConnectionAuthToken } from "@app/services/app-connection/gcp";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
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 { SecretSyncError } from "../secret-sync-errors";
|
||||||
import { TSecretMap } from "../secret-sync-types";
|
import { TSecretMap } from "../secret-sync-types";
|
||||||
@@ -153,6 +154,9 @@ export const GcpSyncFns = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for await (const key of Object.keys(gcpSecrets)) {
|
for await (const key of Object.keys(gcpSecrets)) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(key, secretSync.syncOptions.keySchema)) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!(key in secretMap) || !secretMap[key].value) {
|
if (!(key in secretMap) || !secretMap[key].value) {
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import sodium from "libsodium-wrappers";
|
|||||||
import { getGitHubClient } from "@app/services/app-connection/github";
|
import { getGitHubClient } from "@app/services/app-connection/github";
|
||||||
import { GitHubSyncScope, GitHubSyncVisibility } from "@app/services/secret-sync/github/github-sync-enums";
|
import { GitHubSyncScope, GitHubSyncVisibility } from "@app/services/secret-sync/github/github-sync-enums";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
@@ -222,6 +223,9 @@ export const GithubSyncFns = {
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const encryptedSecret of encryptedSecrets) {
|
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)) {
|
if (!(encryptedSecret.name in secretMap)) {
|
||||||
await deleteSecret(client, secretSync, encryptedSecret);
|
await deleteSecret(client, secretSync, encryptedSecret);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
TPostHCVaultVariable
|
TPostHCVaultVariable
|
||||||
} from "@app/services/secret-sync/hc-vault/hc-vault-sync-types";
|
} from "@app/services/secret-sync/hc-vault/hc-vault-sync-types";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
const listHCVaultVariables = async ({ instanceUrl, namespace, mount, accessToken, path }: THCVaultListVariables) => {
|
const listHCVaultVariables = async ({ instanceUrl, namespace, mount, accessToken, path }: THCVaultListVariables) => {
|
||||||
@@ -68,7 +69,7 @@ export const HCVaultSyncFns = {
|
|||||||
const {
|
const {
|
||||||
connection,
|
connection,
|
||||||
destinationConfig: { mount, path },
|
destinationConfig: { mount, path },
|
||||||
syncOptions: { disableSecretDeletion }
|
syncOptions: { disableSecretDeletion, keySchema }
|
||||||
} = secretSync;
|
} = secretSync;
|
||||||
|
|
||||||
const { namespace } = connection.credentials;
|
const { namespace } = connection.credentials;
|
||||||
@@ -95,6 +96,9 @@ export const HCVaultSyncFns = {
|
|||||||
if (disableSecretDeletion) return;
|
if (disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const [key] of Object.entries(variables)) {
|
for await (const [key] of Object.entries(variables)) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(key, keySchema)) continue;
|
||||||
|
|
||||||
if (!(key in secretMap)) {
|
if (!(key in secretMap)) {
|
||||||
delete variables[key];
|
delete variables[key];
|
||||||
tainted = true;
|
tainted = true;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { request } from "@app/lib/config/request";
|
|||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
@@ -199,6 +200,9 @@ export const HumanitecSyncFns = {
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const humanitecSecret of humanitecSecrets) {
|
for await (const humanitecSecret of humanitecSecrets) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(humanitecSecret.key, secretSync.syncOptions.keySchema)) continue;
|
||||||
|
|
||||||
if (!secretMap[humanitecSecret.key]) {
|
if (!secretMap[humanitecSecret.key]) {
|
||||||
await deleteSecret(secretSync, humanitecSecret);
|
await deleteSecret(secretSync, humanitecSecret);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
TUpdateOCIVaultVariable
|
TUpdateOCIVaultVariable
|
||||||
} from "@app/services/secret-sync/oci-vault/oci-vault-sync-types";
|
} from "@app/services/secret-sync/oci-vault/oci-vault-sync-types";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
const listOCIVaultVariables = async ({ provider, compartmentId, vaultId, onlyActive }: TOCIVaultListVariables) => {
|
const listOCIVaultVariables = async ({ provider, compartmentId, vaultId, onlyActive }: TOCIVaultListVariables) => {
|
||||||
@@ -211,6 +212,9 @@ export const OCIVaultSyncFns = {
|
|||||||
|
|
||||||
// Update and delete secrets
|
// Update and delete secrets
|
||||||
for await (const [key, variable] of Object.entries(variables)) {
|
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
|
// Only update / delete active secrets
|
||||||
if (variable.lifecycleState === vault.models.SecretSummary.LifecycleState.Active) {
|
if (variable.lifecycleState === vault.models.SecretSummary.LifecycleState.Active) {
|
||||||
if (key in secretMap && secretMap[key].value.length > 0) {
|
if (key in secretMap && secretMap[key].value.length > 0) {
|
||||||
|
|||||||
@@ -61,11 +61,6 @@ type TSyncSecretDeps = {
|
|||||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TSyncSecretConfig {
|
|
||||||
filterForSchema?: boolean;
|
|
||||||
stripSchema?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add schema to secret keys
|
// Add schema to secret keys
|
||||||
const addSchema = (unprocessedSecretMap: TSecretMap, schema?: string): TSecretMap => {
|
const addSchema = (unprocessedSecretMap: TSecretMap, schema?: string): TSecretMap => {
|
||||||
if (!schema) return unprocessedSecretMap;
|
if (!schema) return unprocessedSecretMap;
|
||||||
@@ -101,17 +96,22 @@ const stripSchema = (unprocessedSecretMap: TSecretMap, schema?: string): TSecret
|
|||||||
return strippedMap;
|
return strippedMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Filter only for secrets with keys that match the schema
|
// Checks if a key matches a schema
|
||||||
const filterForSchema = (secretMap: TSecretMap, schema?: string): TSecretMap => {
|
export const matchesSchema = (key: string, schema?: string): boolean => {
|
||||||
if (!schema) return secretMap;
|
if (!schema) return true;
|
||||||
|
|
||||||
const [prefix, suffix] = schema.split("{{secretKey}}");
|
const [prefix, suffix] = schema.split("{{secretKey}}");
|
||||||
if (prefix === undefined || suffix === undefined) return secretMap;
|
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 = {};
|
const filteredMap: TSecretMap = {};
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(secretMap)) {
|
for (const [key, value] of Object.entries(secretMap)) {
|
||||||
if (key.startsWith(prefix) && key.endsWith(suffix)) {
|
if (matchesSchema(key, schema)) {
|
||||||
filteredMap[key] = value;
|
filteredMap[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,8 +178,7 @@ export const SecretSyncFns = {
|
|||||||
},
|
},
|
||||||
getSecrets: async (
|
getSecrets: async (
|
||||||
secretSync: TSecretSyncWithCredentials,
|
secretSync: TSecretSyncWithCredentials,
|
||||||
{ kmsService, appConnectionDAL }: TSyncSecretDeps,
|
{ kmsService, appConnectionDAL }: TSyncSecretDeps
|
||||||
config?: TSyncSecretConfig
|
|
||||||
): Promise<TSecretMap> => {
|
): Promise<TSecretMap> => {
|
||||||
let secretMap: TSecretMap;
|
let secretMap: TSecretMap;
|
||||||
switch (secretSync.destination) {
|
switch (secretSync.destination) {
|
||||||
@@ -245,17 +244,7 @@ export const SecretSyncFns = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let processedSecretMap = secretMap;
|
return stripSchema(filterForSchema(secretMap), secretSync.syncOptions.keySchema);
|
||||||
|
|
||||||
if (config?.filterForSchema) {
|
|
||||||
processedSecretMap = filterForSchema(processedSecretMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config?.stripSchema) {
|
|
||||||
return stripSchema(processedSecretMap, secretSync.syncOptions.keySchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
return processedSecretMap;
|
|
||||||
},
|
},
|
||||||
removeSecrets: (
|
removeSecrets: (
|
||||||
secretSync: TSecretSyncWithCredentials,
|
secretSync: TSecretSyncWithCredentials,
|
||||||
|
|||||||
@@ -319,12 +319,9 @@ export const secretSyncQueueFactory = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(andrey): Possibly add a "stripSchema" parameter for imports?
|
|
||||||
const $importSecrets = async (
|
const $importSecrets = async (
|
||||||
secretSync: TSecretSyncWithCredentials,
|
secretSync: TSecretSyncWithCredentials,
|
||||||
importBehavior: SecretSyncImportBehavior,
|
importBehavior: SecretSyncImportBehavior
|
||||||
filterForSchema: boolean,
|
|
||||||
stripSchema: boolean
|
|
||||||
): Promise<TSecretMap> => {
|
): Promise<TSecretMap> => {
|
||||||
const { projectId, environment, folder } = secretSync;
|
const { projectId, environment, folder } = secretSync;
|
||||||
|
|
||||||
@@ -333,17 +330,10 @@ export const secretSyncQueueFactory = ({
|
|||||||
"Invalid Secret Sync source configuration: folder no longer exists. Please update source environment and secret path."
|
"Invalid Secret Sync source configuration: folder no longer exists. Please update source environment and secret path."
|
||||||
);
|
);
|
||||||
|
|
||||||
const importedSecrets = await SecretSyncFns.getSecrets(
|
const importedSecrets = await SecretSyncFns.getSecrets(secretSync, {
|
||||||
secretSync,
|
|
||||||
{
|
|
||||||
appConnectionDAL,
|
appConnectionDAL,
|
||||||
kmsService
|
kmsService
|
||||||
},
|
});
|
||||||
{
|
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!Object.keys(importedSecrets).length) return {};
|
if (!Object.keys(importedSecrets).length) return {};
|
||||||
|
|
||||||
@@ -454,9 +444,7 @@ export const secretSyncQueueFactory = ({
|
|||||||
secretSyncWithCredentials,
|
secretSyncWithCredentials,
|
||||||
initialSyncBehavior === SecretSyncInitialSyncBehavior.ImportPrioritizeSource
|
initialSyncBehavior === SecretSyncInitialSyncBehavior.ImportPrioritizeSource
|
||||||
? SecretSyncImportBehavior.PrioritizeSource
|
? SecretSyncImportBehavior.PrioritizeSource
|
||||||
: SecretSyncImportBehavior.PrioritizeDestination,
|
: SecretSyncImportBehavior.PrioritizeDestination
|
||||||
false,
|
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Object.entries(importedSecretMap).forEach(([key, secretData]) => {
|
Object.entries(importedSecretMap).forEach(([key, secretData]) => {
|
||||||
@@ -548,7 +536,7 @@ export const secretSyncQueueFactory = ({
|
|||||||
|
|
||||||
const $handleImportSecretsJob = async (job: TSecretSyncImportSecretsDTO) => {
|
const $handleImportSecretsJob = async (job: TSecretSyncImportSecretsDTO) => {
|
||||||
const {
|
const {
|
||||||
data: { syncId, auditLogInfo, importBehavior, filterForSchema, stripSchema }
|
data: { syncId, auditLogInfo, importBehavior }
|
||||||
} = job;
|
} = job;
|
||||||
|
|
||||||
const secretSync = await secretSyncDAL.findById(syncId);
|
const secretSync = await secretSyncDAL.findById(syncId);
|
||||||
@@ -586,9 +574,7 @@ export const secretSyncQueueFactory = ({
|
|||||||
credentials
|
credentials
|
||||||
}
|
}
|
||||||
} as TSecretSyncWithCredentials,
|
} as TSecretSyncWithCredentials,
|
||||||
importBehavior,
|
importBehavior
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
);
|
);
|
||||||
|
|
||||||
isSuccess = true;
|
isSuccess = true;
|
||||||
|
|||||||
@@ -231,8 +231,6 @@ export type TQueueSecretSyncImportSecretsByIdDTO = {
|
|||||||
syncId: string;
|
syncId: string;
|
||||||
importBehavior: SecretSyncImportBehavior;
|
importBehavior: SecretSyncImportBehavior;
|
||||||
auditLogInfo?: AuditLogInfo;
|
auditLogInfo?: AuditLogInfo;
|
||||||
filterForSchema: boolean;
|
|
||||||
stripSchema: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTriggerSecretSyncImportSecretsByIdDTO = {
|
export type TTriggerSecretSyncImportSecretsByIdDTO = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { getTeamCityInstanceUrl } from "@app/services/app-connection/teamcity";
|
import { getTeamCityInstanceUrl } from "@app/services/app-connection/teamcity";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
import {
|
import {
|
||||||
TDeleteTeamCityVariable,
|
TDeleteTeamCityVariable,
|
||||||
@@ -125,6 +126,9 @@ export const TeamCitySyncFns = {
|
|||||||
const variables = await listTeamCityVariables({ instanceUrl, accessToken, project, buildConfig });
|
const variables = await listTeamCityVariables({ instanceUrl, accessToken, project, buildConfig });
|
||||||
|
|
||||||
for await (const [key, variable] of Object.entries(variables)) {
|
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)) {
|
if (!(key in secretMap)) {
|
||||||
try {
|
try {
|
||||||
await deleteTeamCityVariable({
|
await deleteTeamCityVariable({
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AxiosResponse } from "axios";
|
|||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||||
@@ -231,6 +232,9 @@ export const TerraformCloudSyncFns = {
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for (const terraformCloudVariable of terraformCloudVariables) {
|
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)) {
|
if (!Object.prototype.hasOwnProperty.call(secretMap, terraformCloudVariable.key)) {
|
||||||
await deleteVariable(secretSync, terraformCloudVariable);
|
await deleteVariable(secretSync, terraformCloudVariable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
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 { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
|
|
||||||
import { VercelEnvironmentType } from "./vercel-sync-enums";
|
import { VercelEnvironmentType } from "./vercel-sync-enums";
|
||||||
@@ -290,6 +291,9 @@ export const VercelSyncFns = {
|
|||||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const vercelSecret of vercelSecrets) {
|
for await (const vercelSecret of vercelSecrets) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(vercelSecret.key, secretSync.syncOptions.keySchema)) continue;
|
||||||
|
|
||||||
if (!secretMap[vercelSecret.key]) {
|
if (!secretMap[vercelSecret.key]) {
|
||||||
await deleteSecret(secretSync, vercelSecret);
|
await deleteSecret(secretSync, vercelSecret);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { getWindmillInstanceUrl } from "@app/services/app-connection/windmill";
|
import { getWindmillInstanceUrl } from "@app/services/app-connection/windmill";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||||
|
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
|
||||||
import {
|
import {
|
||||||
TDeleteWindmillVariable,
|
TDeleteWindmillVariable,
|
||||||
TPostWindmillVariable,
|
TPostWindmillVariable,
|
||||||
@@ -128,7 +129,7 @@ export const WindmillSyncFns = {
|
|||||||
const {
|
const {
|
||||||
connection,
|
connection,
|
||||||
destinationConfig: { path },
|
destinationConfig: { path },
|
||||||
syncOptions: { disableSecretDeletion }
|
syncOptions: { disableSecretDeletion, keySchema }
|
||||||
} = secretSync;
|
} = secretSync;
|
||||||
|
|
||||||
// url needs to be lowercase
|
// url needs to be lowercase
|
||||||
@@ -169,6 +170,9 @@ export const WindmillSyncFns = {
|
|||||||
if (disableSecretDeletion) return;
|
if (disableSecretDeletion) return;
|
||||||
|
|
||||||
for await (const [key, variable] of Object.entries(variables)) {
|
for await (const [key, variable] of Object.entries(variables)) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
if (!matchesSchema(key, keySchema)) continue;
|
||||||
|
|
||||||
if (!(key in secretMap)) {
|
if (!(key in secretMap)) {
|
||||||
try {
|
try {
|
||||||
await deleteWindmillVariable({
|
await deleteWindmillVariable({
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import {
|
|||||||
ModalClose,
|
ModalClose,
|
||||||
ModalContent,
|
ModalContent,
|
||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem
|
||||||
Switch
|
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import { SECRET_SYNC_IMPORT_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
import { SECRET_SYNC_IMPORT_BEHAVIOR_MAP, SECRET_SYNC_MAP } from "@app/helpers/secretSyncs";
|
||||||
import {
|
import {
|
||||||
@@ -32,20 +31,13 @@ type ContentProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const FormSchema = z.object({
|
const FormSchema = z.object({
|
||||||
importBehavior: z.nativeEnum(SecretSyncImportBehavior),
|
importBehavior: z.nativeEnum(SecretSyncImportBehavior)
|
||||||
filterForSchema: z.boolean(),
|
|
||||||
stripSchema: z.boolean()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type TFormData = z.infer<typeof FormSchema>;
|
type TFormData = z.infer<typeof FormSchema>;
|
||||||
|
|
||||||
const Content = ({ secretSync, onComplete }: ContentProps) => {
|
const Content = ({ secretSync, onComplete }: ContentProps) => {
|
||||||
const {
|
const { id: syncId, destination, projectId } = secretSync;
|
||||||
id: syncId,
|
|
||||||
destination,
|
|
||||||
projectId,
|
|
||||||
syncOptions: { keySchema }
|
|
||||||
} = secretSync;
|
|
||||||
const destinationName = SECRET_SYNC_MAP[destination].name;
|
const destinationName = SECRET_SYNC_MAP[destination].name;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -53,28 +45,18 @@ const Content = ({ secretSync, onComplete }: ContentProps) => {
|
|||||||
control,
|
control,
|
||||||
formState: { isSubmitting, isDirty }
|
formState: { isSubmitting, isDirty }
|
||||||
} = useForm<TFormData>({
|
} = useForm<TFormData>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: zodResolver(FormSchema)
|
||||||
defaultValues: {
|
|
||||||
filterForSchema: false,
|
|
||||||
stripSchema: false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const triggerImportSecrets = useTriggerSecretSyncImportSecrets();
|
const triggerImportSecrets = useTriggerSecretSyncImportSecrets();
|
||||||
|
|
||||||
const handleTriggerImportSecrets = async ({
|
const handleTriggerImportSecrets = async ({ importBehavior }: TFormData) => {
|
||||||
importBehavior,
|
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
}: TFormData) => {
|
|
||||||
try {
|
try {
|
||||||
await triggerImportSecrets.mutateAsync({
|
await triggerImportSecrets.mutateAsync({
|
||||||
syncId,
|
syncId,
|
||||||
destination,
|
destination,
|
||||||
importBehavior,
|
importBehavior,
|
||||||
projectId,
|
projectId
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
});
|
});
|
||||||
|
|
||||||
createNotification({
|
createNotification({
|
||||||
@@ -151,64 +133,6 @@ const Content = ({ secretSync, onComplete }: ContentProps) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{keySchema && (
|
|
||||||
<>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="filterForSchema"
|
|
||||||
defaultValue
|
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
tooltipClassName="max-w-md"
|
|
||||||
tooltipText={
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<p>
|
|
||||||
If enabled, Infisical will only import destination secrets that match your key
|
|
||||||
schema:
|
|
||||||
</p>
|
|
||||||
<code className="text-mineshaft-300">{keySchema}</code>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
label="Filter Keys for Schema"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Switch
|
|
||||||
id="filter-for-schema"
|
|
||||||
thumbClassName="bg-mineshaft-800"
|
|
||||||
isChecked={value}
|
|
||||||
onCheckedChange={onChange}
|
|
||||||
>
|
|
||||||
Only import destination secrets that match schema
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="stripSchema"
|
|
||||||
defaultValue
|
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
|
||||||
<FormControl
|
|
||||||
tooltipClassName="max-w-md"
|
|
||||||
tooltipText="If enabled, Infisical will strip secret keys according to your schema. Keys that do not match the schema will not be affected."
|
|
||||||
label="Strip Schema"
|
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
>
|
|
||||||
<Switch
|
|
||||||
id="strip-schema"
|
|
||||||
thumbClassName="bg-mineshaft-800"
|
|
||||||
isChecked={value}
|
|
||||||
onCheckedChange={onChange}
|
|
||||||
>
|
|
||||||
Strip schema from imported secret keys
|
|
||||||
</Switch>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div className="mt-8 flex w-full items-center justify-between gap-2">
|
<div className="mt-8 flex w-full items-center justify-between gap-2">
|
||||||
<ModalClose asChild>
|
<ModalClose asChild>
|
||||||
<Button colorSchema="secondary" variant="plain">
|
<Button colorSchema="secondary" variant="plain">
|
||||||
|
|||||||
@@ -86,12 +86,10 @@ export const useTriggerSecretSyncImportSecrets = () => {
|
|||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
syncId,
|
syncId,
|
||||||
destination,
|
destination,
|
||||||
importBehavior,
|
importBehavior
|
||||||
filterForSchema,
|
|
||||||
stripSchema
|
|
||||||
}: TTriggerSecretSyncImportSecretsDTO) => {
|
}: TTriggerSecretSyncImportSecretsDTO) => {
|
||||||
const { data } = await apiRequest.post(
|
const { data } = await apiRequest.post(
|
||||||
`/api/v1/secret-syncs/${destination}/${syncId}/import-secrets?importBehavior=${importBehavior}&filterForSchema=${filterForSchema}&stripSchema=${stripSchema}`
|
`/api/v1/secret-syncs/${destination}/${syncId}/import-secrets?importBehavior=${importBehavior}`
|
||||||
);
|
);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -82,8 +82,6 @@ export type TTriggerSecretSyncImportSecretsDTO = {
|
|||||||
syncId: string;
|
syncId: string;
|
||||||
importBehavior: SecretSyncImportBehavior;
|
importBehavior: SecretSyncImportBehavior;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
filterForSchema: boolean;
|
|
||||||
stripSchema: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTriggerSecretSyncRemoveSecretsDTO = {
|
export type TTriggerSecretSyncRemoveSecretsDTO = {
|
||||||
|
|||||||
Reference in New Issue
Block a user