mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #4210 from Infisical/feat/bitbucketSecretSync
Add Bitbucket Secret Sync
This commit is contained in:
@@ -2506,6 +2506,11 @@ export const SecretSyncs = {
|
||||
SUPABASE: {
|
||||
projectId: "The ID of the Supabase project to sync secrets to.",
|
||||
projectName: "The name of the Supabase project to sync secrets to."
|
||||
},
|
||||
BITBUCKET: {
|
||||
workspaceSlug: "The Bitbucket Workspace slug to sync secrets to.",
|
||||
repositorySlug: "The Bitbucket Repository slug to sync secrets to.",
|
||||
environmentId: "The Bitbucket Deployment Environment uuid to sync secrets to."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -85,4 +85,40 @@ export const registerBitbucketConnectionRouter = async (server: FastifyZodProvid
|
||||
return { repositories };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/environments`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
querystring: z.object({
|
||||
workspaceSlug: z.string().min(1).max(255),
|
||||
repositorySlug: z.string().min(1).max(255)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
environments: z.object({ slug: z.string(), name: z.string(), uuid: z.string() }).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const {
|
||||
params: { connectionId },
|
||||
query: { workspaceSlug, repositorySlug }
|
||||
} = req;
|
||||
|
||||
const environments = await server.services.appConnection.bitbucket.listEnvironments(
|
||||
{ connectionId, workspaceSlug, repositorySlug },
|
||||
req.permission
|
||||
);
|
||||
|
||||
return { environments };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import {
|
||||
BitbucketSyncSchema,
|
||||
CreateBitbucketSyncSchema,
|
||||
UpdateBitbucketSyncSchema
|
||||
} from "@app/services/secret-sync/bitbucket";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
|
||||
|
||||
export const registerBitbucketSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.Bitbucket,
|
||||
server,
|
||||
responseSchema: BitbucketSyncSchema,
|
||||
createSchema: CreateBitbucketSyncSchema,
|
||||
updateSchema: UpdateBitbucketSyncSchema
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { registerAwsSecretsManagerSyncRouter } from "./aws-secrets-manager-sync-
|
||||
import { registerAzureAppConfigurationSyncRouter } from "./azure-app-configuration-sync-router";
|
||||
import { registerAzureDevOpsSyncRouter } from "./azure-devops-sync-router";
|
||||
import { registerAzureKeyVaultSyncRouter } from "./azure-key-vault-sync-router";
|
||||
import { registerBitbucketSyncRouter } from "./bitbucket-sync-router";
|
||||
import { registerCamundaSyncRouter } from "./camunda-sync-router";
|
||||
import { registerChecklySyncRouter } from "./checkly-sync-router";
|
||||
import { registerCloudflarePagesSyncRouter } from "./cloudflare-pages-sync-router";
|
||||
@@ -57,5 +58,6 @@ export const SECRET_SYNC_REGISTER_ROUTER_MAP: Record<SecretSync, (server: Fastif
|
||||
[SecretSync.Supabase]: registerSupabaseSyncRouter,
|
||||
[SecretSync.Zabbix]: registerZabbixSyncRouter,
|
||||
[SecretSync.Railway]: registerRailwaySyncRouter,
|
||||
[SecretSync.Checkly]: registerChecklySyncRouter
|
||||
[SecretSync.Checkly]: registerChecklySyncRouter,
|
||||
[SecretSync.Bitbucket]: registerBitbucketSyncRouter
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from "@app/services/secret-sync/azure-app-configuration";
|
||||
import { AzureDevOpsSyncListItemSchema, AzureDevOpsSyncSchema } from "@app/services/secret-sync/azure-devops";
|
||||
import { AzureKeyVaultSyncListItemSchema, AzureKeyVaultSyncSchema } from "@app/services/secret-sync/azure-key-vault";
|
||||
import { BitbucketSyncListItemSchema, BitbucketSyncSchema } from "@app/services/secret-sync/bitbucket";
|
||||
import { CamundaSyncListItemSchema, CamundaSyncSchema } from "@app/services/secret-sync/camunda";
|
||||
import { ChecklySyncListItemSchema, ChecklySyncSchema } from "@app/services/secret-sync/checkly/checkly-sync-schemas";
|
||||
import {
|
||||
@@ -75,7 +76,8 @@ const SecretSyncSchema = z.discriminatedUnion("destination", [
|
||||
SupabaseSyncSchema,
|
||||
ZabbixSyncSchema,
|
||||
RailwaySyncSchema,
|
||||
ChecklySyncSchema
|
||||
ChecklySyncSchema,
|
||||
BitbucketSyncSchema
|
||||
]);
|
||||
|
||||
const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
@@ -102,11 +104,11 @@ const SecretSyncOptionsSchema = z.discriminatedUnion("destination", [
|
||||
GitLabSyncListItemSchema,
|
||||
CloudflarePagesSyncListItemSchema,
|
||||
CloudflareWorkersSyncListItemSchema,
|
||||
|
||||
ZabbixSyncListItemSchema,
|
||||
RailwaySyncListItemSchema,
|
||||
ChecklySyncListItemSchema,
|
||||
SupabaseSyncListItemSchema
|
||||
SupabaseSyncListItemSchema,
|
||||
BitbucketSyncListItemSchema
|
||||
]);
|
||||
|
||||
export const registerSecretSyncRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { BitbucketConnectionMethod } from "./bitbucket-connection-enums";
|
||||
import {
|
||||
TBitbucketConnection,
|
||||
TBitbucketConnectionConfig,
|
||||
TBitbucketEnvironment,
|
||||
TBitbucketRepo,
|
||||
TBitbucketWorkspace
|
||||
} from "./bitbucket-connection-types";
|
||||
@@ -21,11 +22,15 @@ export const getBitbucketConnectionListItem = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const createAuthHeader = (email: string, apiToken: string): string => {
|
||||
return `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`;
|
||||
};
|
||||
|
||||
export const getBitbucketUser = async ({ email, apiToken }: { email: string; apiToken: string }) => {
|
||||
try {
|
||||
const { data } = await request.get<{ username: string }>(`${IntegrationUrls.BITBUCKET_API_URL}/2.0/user`, {
|
||||
headers: {
|
||||
Authorization: `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`,
|
||||
Authorization: createAuthHeader(email, apiToken),
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
@@ -57,7 +62,7 @@ export const listBitbucketWorkspaces = async (appConnection: TBitbucketConnectio
|
||||
const { email, apiToken } = appConnection.credentials;
|
||||
|
||||
const headers = {
|
||||
Authorization: `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`,
|
||||
Authorization: createAuthHeader(email, apiToken),
|
||||
Accept: "application/json"
|
||||
};
|
||||
|
||||
@@ -89,7 +94,7 @@ export const listBitbucketRepositories = async (appConnection: TBitbucketConnect
|
||||
const { email, apiToken } = appConnection.credentials;
|
||||
|
||||
const headers = {
|
||||
Authorization: `Basic ${Buffer.from(`${email}:${apiToken}`).toString("base64")}`,
|
||||
Authorization: createAuthHeader(email, apiToken),
|
||||
Accept: "application/json"
|
||||
};
|
||||
|
||||
@@ -115,3 +120,43 @@ export const listBitbucketRepositories = async (appConnection: TBitbucketConnect
|
||||
|
||||
return allRepos;
|
||||
};
|
||||
|
||||
export const listBitbucketEnvironments = async (
|
||||
appConnection: TBitbucketConnection,
|
||||
workspaceSlug: string,
|
||||
repositorySlug: string
|
||||
) => {
|
||||
const { email, apiToken } = appConnection.credentials;
|
||||
|
||||
const headers = {
|
||||
Authorization: createAuthHeader(email, apiToken),
|
||||
Accept: "application/json"
|
||||
};
|
||||
|
||||
const environments: TBitbucketEnvironment[] = [];
|
||||
let hasNextPage = true;
|
||||
|
||||
let environmentsUrl = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${encodeURIComponent(workspaceSlug)}/${encodeURIComponent(repositorySlug)}/environments?pagelen=100`;
|
||||
|
||||
let iterationCount = 0;
|
||||
// Limit to 10 iterations, fetching at most 10 * 100 = 1000 environments
|
||||
while (hasNextPage && iterationCount < 10) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { data }: { data: { values: TBitbucketEnvironment[]; next: string } } = await request.get(environmentsUrl, {
|
||||
headers
|
||||
});
|
||||
|
||||
if (data?.values.length > 0) {
|
||||
environments.push(...data.values);
|
||||
}
|
||||
|
||||
if (data.next) {
|
||||
environmentsUrl = data.next;
|
||||
} else {
|
||||
hasNextPage = false;
|
||||
}
|
||||
iterationCount += 1;
|
||||
}
|
||||
|
||||
return environments;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
import { AppConnection } from "../app-connection-enums";
|
||||
import { listBitbucketRepositories, listBitbucketWorkspaces } from "./bitbucket-connection-fns";
|
||||
import { TBitbucketConnection, TGetBitbucketRepositoriesDTO } from "./bitbucket-connection-types";
|
||||
import {
|
||||
listBitbucketEnvironments,
|
||||
listBitbucketRepositories,
|
||||
listBitbucketWorkspaces
|
||||
} from "./bitbucket-connection-fns";
|
||||
import {
|
||||
TBitbucketConnection,
|
||||
TGetBitbucketEnvironmentsDTO,
|
||||
TGetBitbucketRepositoriesDTO
|
||||
} from "./bitbucket-connection-types";
|
||||
|
||||
type TGetAppConnectionFunc = (
|
||||
app: AppConnection,
|
||||
@@ -26,8 +34,18 @@ export const bitbucketConnectionService = (getAppConnection: TGetAppConnectionFu
|
||||
return repositories;
|
||||
};
|
||||
|
||||
const listEnvironments = async (
|
||||
{ connectionId, workspaceSlug, repositorySlug }: TGetBitbucketEnvironmentsDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const appConnection = await getAppConnection(AppConnection.Bitbucket, connectionId, actor);
|
||||
const environments = await listBitbucketEnvironments(appConnection, workspaceSlug, repositorySlug);
|
||||
return environments;
|
||||
};
|
||||
|
||||
return {
|
||||
listWorkspaces,
|
||||
listRepositories
|
||||
listRepositories,
|
||||
listEnvironments
|
||||
};
|
||||
};
|
||||
|
||||
@@ -38,3 +38,20 @@ export type TBitbucketRepo = {
|
||||
full_name: string; // workspace-slug/repo-slug
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type TGetBitbucketEnvironmentsDTO = {
|
||||
connectionId: string;
|
||||
workspaceSlug: string;
|
||||
repositorySlug: string;
|
||||
};
|
||||
|
||||
export type TBitbucketEnvironment = {
|
||||
uuid: string;
|
||||
slug: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type TBitbucketEnvironmentsResponse = {
|
||||
values: TBitbucketEnvironment[];
|
||||
next?: string;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const BITBUCKET_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Bitbucket",
|
||||
destination: SecretSync.Bitbucket,
|
||||
connection: AppConnection.Bitbucket,
|
||||
canImportSecrets: false
|
||||
};
|
||||
222
backend/src/services/secret-sync/bitbucket/bitbucket-sync-fns.ts
Normal file
222
backend/src/services/secret-sync/bitbucket/bitbucket-sync-fns.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { request } from "@app/lib/config/request";
|
||||
import { createAuthHeader } from "@app/services/app-connection/bitbucket";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import {
|
||||
TBitbucketListVariables,
|
||||
TBitbucketSyncWithCredentials,
|
||||
TBitbucketVariable,
|
||||
TDeleteBitbucketVariable,
|
||||
TPutBitbucketVariable
|
||||
} from "@app/services/secret-sync/bitbucket/bitbucket-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";
|
||||
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
|
||||
const buildVariablesUrl = (workspace: string, repository: string, environment?: string, uuid?: string): string => {
|
||||
const baseUrl = `${IntegrationUrls.BITBUCKET_API_URL}/2.0/repositories/${encodeURIComponent(workspace)}/${encodeURIComponent(repository)}`;
|
||||
|
||||
if (environment) {
|
||||
return `${baseUrl}/deployments_config/environments/${environment}/variables/${uuid || ""}`;
|
||||
}
|
||||
|
||||
return `${baseUrl}/pipelines_config/variables/${uuid || ""}`;
|
||||
};
|
||||
|
||||
const listVariables = async ({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
authHeader
|
||||
}: TBitbucketListVariables): Promise<TBitbucketVariable[]> => {
|
||||
const url = buildVariablesUrl(workspaceSlug, repositorySlug, environmentId);
|
||||
|
||||
const { data } = await request.get<{ values: TBitbucketVariable[] }>(url, {
|
||||
headers: {
|
||||
Authorization: authHeader,
|
||||
Accept: "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
return data.values;
|
||||
};
|
||||
|
||||
const upsertVariable = async ({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
key,
|
||||
value,
|
||||
existingVariables,
|
||||
authHeader
|
||||
}: {
|
||||
workspaceSlug: string;
|
||||
repositorySlug: string;
|
||||
environmentId?: string;
|
||||
key: string;
|
||||
value: string;
|
||||
existingVariables: TBitbucketVariable[];
|
||||
authHeader: string;
|
||||
}) => {
|
||||
const existingVariable = existingVariables.find((variable) => variable.key === key);
|
||||
const requestData = { key, value, secured: true };
|
||||
const headers = {
|
||||
Authorization: authHeader,
|
||||
"Content-Type": "application/json"
|
||||
};
|
||||
|
||||
if (existingVariable) {
|
||||
const url = buildVariablesUrl(workspaceSlug, repositorySlug, environmentId, existingVariable.uuid);
|
||||
return request.put(url, requestData, { headers });
|
||||
}
|
||||
|
||||
const url = buildVariablesUrl(workspaceSlug, repositorySlug, environmentId);
|
||||
return request.post(url, requestData, { headers });
|
||||
};
|
||||
|
||||
const putVariables = async ({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
secretMap,
|
||||
authHeader
|
||||
}: TPutBitbucketVariable & { secretMap: TSecretMap; authHeader: string }) => {
|
||||
const existingVariables = await listVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
authHeader
|
||||
});
|
||||
|
||||
const promises = Object.entries(secretMap).map(([key, { value }]) =>
|
||||
upsertVariable({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
key,
|
||||
value,
|
||||
existingVariables,
|
||||
authHeader
|
||||
})
|
||||
);
|
||||
|
||||
return Promise.all(promises);
|
||||
};
|
||||
|
||||
const deleteVariables = async ({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
keys,
|
||||
authHeader
|
||||
}: TDeleteBitbucketVariable) => {
|
||||
const existingVariables = await listVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
authHeader
|
||||
});
|
||||
|
||||
const variablesToDelete = existingVariables.filter((variable) => keys.includes(variable.key));
|
||||
const promises = variablesToDelete.map((variable) => {
|
||||
const url = buildVariablesUrl(workspaceSlug, repositorySlug, environmentId, variable.uuid);
|
||||
return request.delete(url, {
|
||||
headers: { Authorization: authHeader }
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
};
|
||||
|
||||
export const BitbucketSyncFns = {
|
||||
syncSecrets: async (secretSync: TBitbucketSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const {
|
||||
connection,
|
||||
environment,
|
||||
destinationConfig: { workspaceSlug, repositorySlug, environmentId }
|
||||
} = secretSync;
|
||||
|
||||
const { email, apiToken } = connection.credentials;
|
||||
const authHeader = createAuthHeader(email, apiToken);
|
||||
|
||||
try {
|
||||
await putVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
secretMap,
|
||||
authHeader
|
||||
});
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({ error });
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
try {
|
||||
const existingVariables = await listVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
authHeader
|
||||
});
|
||||
|
||||
const keysToDelete = existingVariables
|
||||
.map((variable) => variable.key)
|
||||
.filter(
|
||||
(secret) =>
|
||||
matchesSchema(secret, environment?.slug || "", secretSync.syncOptions.keySchema) && !(secret in secretMap)
|
||||
);
|
||||
|
||||
if (keysToDelete.length > 0) {
|
||||
await deleteVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
keys: keysToDelete,
|
||||
authHeader
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({ error });
|
||||
}
|
||||
},
|
||||
|
||||
removeSecrets: async (secretSync: TBitbucketSyncWithCredentials, secretMap: TSecretMap) => {
|
||||
const {
|
||||
connection,
|
||||
destinationConfig: { workspaceSlug, repositorySlug, environmentId }
|
||||
} = secretSync;
|
||||
|
||||
const { email, apiToken } = connection.credentials;
|
||||
const authHeader = createAuthHeader(email, apiToken);
|
||||
|
||||
try {
|
||||
const existingVariables = await listVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
authHeader
|
||||
});
|
||||
|
||||
const keysToRemove = existingVariables.map((variable) => variable.key).filter((secret) => secret in secretMap);
|
||||
|
||||
if (keysToRemove.length > 0) {
|
||||
await deleteVariables({
|
||||
workspaceSlug,
|
||||
repositorySlug,
|
||||
environmentId,
|
||||
keys: keysToRemove,
|
||||
authHeader
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({ error });
|
||||
}
|
||||
},
|
||||
|
||||
getSecrets: async (secretSync: TBitbucketSyncWithCredentials): Promise<TSecretMap> => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
const BitbucketSyncDestinationConfigSchema = z.object({
|
||||
repositorySlug: z.string().describe(SecretSyncs.DESTINATION_CONFIG.BITBUCKET.repositorySlug),
|
||||
environmentId: z.string().optional().describe(SecretSyncs.DESTINATION_CONFIG.BITBUCKET.environmentId),
|
||||
workspaceSlug: z.string().describe(SecretSyncs.DESTINATION_CONFIG.BITBUCKET.workspaceSlug)
|
||||
});
|
||||
|
||||
const BitbucketSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const BitbucketSyncSchema = BaseSecretSyncSchema(SecretSync.Bitbucket, BitbucketSyncOptionsConfig).extend({
|
||||
destination: z.literal(SecretSync.Bitbucket),
|
||||
destinationConfig: BitbucketSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateBitbucketSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.Bitbucket,
|
||||
BitbucketSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: BitbucketSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateBitbucketSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.Bitbucket,
|
||||
BitbucketSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: BitbucketSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const BitbucketSyncListItemSchema = z.object({
|
||||
name: z.literal("Bitbucket"),
|
||||
connection: z.literal(AppConnection.Bitbucket),
|
||||
destination: z.literal(SecretSync.Bitbucket),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { TBitbucketConnection } from "@app/services/app-connection/bitbucket";
|
||||
|
||||
import { BitbucketSyncListItemSchema, BitbucketSyncSchema, CreateBitbucketSyncSchema } from "./bitbucket-sync-schemas";
|
||||
|
||||
export type TBitbucketSync = z.infer<typeof BitbucketSyncSchema>;
|
||||
|
||||
export type TBitbucketSyncInput = z.infer<typeof CreateBitbucketSyncSchema>;
|
||||
|
||||
export type TBitbucketSyncListItem = z.infer<typeof BitbucketSyncListItemSchema>;
|
||||
|
||||
export type TBitbucketSyncWithCredentials = TBitbucketSync & {
|
||||
connection: TBitbucketConnection;
|
||||
};
|
||||
|
||||
export type TBitbucketVariable = {
|
||||
key: string;
|
||||
value?: string;
|
||||
// Secure variables values are not returned by the API neither are they shown in Bitbucket UI
|
||||
secured: boolean;
|
||||
uuid: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type TBitbucketListVariables = {
|
||||
workspaceSlug: string;
|
||||
repositorySlug: string;
|
||||
environmentId?: string;
|
||||
authHeader: string;
|
||||
};
|
||||
|
||||
export type TPutBitbucketVariable = {
|
||||
authHeader: string;
|
||||
workspaceSlug: string;
|
||||
repositorySlug: string;
|
||||
environmentId?: string;
|
||||
};
|
||||
|
||||
export type TDeleteBitbucketVariable = {
|
||||
authHeader: string;
|
||||
workspaceSlug: string;
|
||||
repositorySlug: string;
|
||||
environmentId?: string;
|
||||
keys: string[];
|
||||
};
|
||||
|
||||
export type TBitbucketConnectionCredentials = {
|
||||
authHeader: string;
|
||||
};
|
||||
4
backend/src/services/secret-sync/bitbucket/index.ts
Normal file
4
backend/src/services/secret-sync/bitbucket/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from "./bitbucket-sync-constants";
|
||||
export * from "./bitbucket-sync-fns";
|
||||
export * from "./bitbucket-sync-schemas";
|
||||
export * from "./bitbucket-sync-types";
|
||||
@@ -25,7 +25,8 @@ export enum SecretSync {
|
||||
Supabase = "supabase",
|
||||
Zabbix = "zabbix",
|
||||
Railway = "railway",
|
||||
Checkly = "checkly"
|
||||
Checkly = "checkly",
|
||||
Bitbucket = "bitbucket"
|
||||
}
|
||||
|
||||
export enum SecretSyncInitialSyncBehavior {
|
||||
|
||||
@@ -28,6 +28,7 @@ import { ONEPASS_SYNC_LIST_OPTION, OnePassSyncFns } from "./1password";
|
||||
import { AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION, azureAppConfigurationSyncFactory } from "./azure-app-configuration";
|
||||
import { AZURE_DEVOPS_SYNC_LIST_OPTION, azureDevOpsSyncFactory } from "./azure-devops";
|
||||
import { AZURE_KEY_VAULT_SYNC_LIST_OPTION, azureKeyVaultSyncFactory } from "./azure-key-vault";
|
||||
import { BITBUCKET_SYNC_LIST_OPTION, BitbucketSyncFns } from "./bitbucket";
|
||||
import { CAMUNDA_SYNC_LIST_OPTION, camundaSyncFactory } from "./camunda";
|
||||
import { CHECKLY_SYNC_LIST_OPTION } from "./checkly/checkly-sync-constants";
|
||||
import { ChecklySyncFns } from "./checkly/checkly-sync-fns";
|
||||
@@ -80,7 +81,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.Supabase]: SUPABASE_SYNC_LIST_OPTION,
|
||||
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION,
|
||||
[SecretSync.Railway]: RAILWAY_SYNC_LIST_OPTION,
|
||||
[SecretSync.Checkly]: CHECKLY_SYNC_LIST_OPTION
|
||||
[SecretSync.Checkly]: CHECKLY_SYNC_LIST_OPTION,
|
||||
[SecretSync.Bitbucket]: BITBUCKET_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretSyncOptions = () => {
|
||||
@@ -258,6 +260,8 @@ export const SecretSyncFns = {
|
||||
return ChecklySyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Supabase:
|
||||
return SupabaseSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Bitbucket:
|
||||
return BitbucketSyncFns.syncSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -365,6 +369,9 @@ export const SecretSyncFns = {
|
||||
case SecretSync.Supabase:
|
||||
secretMap = await SupabaseSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.Bitbucket:
|
||||
secretMap = await BitbucketSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -452,6 +459,8 @@ export const SecretSyncFns = {
|
||||
return ChecklySyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Supabase:
|
||||
return SupabaseSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
case SecretSync.Bitbucket:
|
||||
return BitbucketSyncFns.removeSecrets(secretSync, schemaSecretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
|
||||
@@ -28,7 +28,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.Supabase]: "Supabase",
|
||||
[SecretSync.Zabbix]: "Zabbix",
|
||||
[SecretSync.Railway]: "Railway",
|
||||
[SecretSync.Checkly]: "Checkly"
|
||||
[SecretSync.Checkly]: "Checkly",
|
||||
[SecretSync.Bitbucket]: "Bitbucket"
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
@@ -58,7 +59,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.Supabase]: AppConnection.Supabase,
|
||||
[SecretSync.Zabbix]: AppConnection.Zabbix,
|
||||
[SecretSync.Railway]: AppConnection.Railway,
|
||||
[SecretSync.Checkly]: AppConnection.Checkly
|
||||
[SecretSync.Checkly]: AppConnection.Checkly,
|
||||
[SecretSync.Bitbucket]: AppConnection.Bitbucket
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
@@ -88,5 +90,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
|
||||
[SecretSync.Supabase]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Zabbix]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Railway]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Checkly]: SecretSyncPlanType.Regular
|
||||
[SecretSync.Checkly]: SecretSyncPlanType.Regular,
|
||||
[SecretSync.Bitbucket]: SecretSyncPlanType.Regular
|
||||
};
|
||||
|
||||
@@ -72,6 +72,12 @@ import {
|
||||
TAzureKeyVaultSyncListItem,
|
||||
TAzureKeyVaultSyncWithCredentials
|
||||
} from "./azure-key-vault";
|
||||
import {
|
||||
TBitbucketSync,
|
||||
TBitbucketSyncInput,
|
||||
TBitbucketSyncListItem,
|
||||
TBitbucketSyncWithCredentials
|
||||
} from "./bitbucket/bitbucket-sync-types";
|
||||
import {
|
||||
TChecklySync,
|
||||
TChecklySyncInput,
|
||||
@@ -166,7 +172,8 @@ export type TSecretSync =
|
||||
| TZabbixSync
|
||||
| TRailwaySync
|
||||
| TChecklySync
|
||||
| TSupabaseSync;
|
||||
| TSupabaseSync
|
||||
| TBitbucketSync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
| TAwsParameterStoreSyncWithCredentials
|
||||
@@ -195,7 +202,8 @@ export type TSecretSyncWithCredentials =
|
||||
| TZabbixSyncWithCredentials
|
||||
| TRailwaySyncWithCredentials
|
||||
| TChecklySyncWithCredentials
|
||||
| TSupabaseSyncWithCredentials;
|
||||
| TSupabaseSyncWithCredentials
|
||||
| TBitbucketSyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
| TAwsParameterStoreSyncInput
|
||||
@@ -224,7 +232,8 @@ export type TSecretSyncInput =
|
||||
| TZabbixSyncInput
|
||||
| TRailwaySyncInput
|
||||
| TChecklySyncInput
|
||||
| TSupabaseSyncInput;
|
||||
| TSupabaseSyncInput
|
||||
| TBitbucketSyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
| TAwsParameterStoreSyncListItem
|
||||
@@ -253,7 +262,8 @@ export type TSecretSyncListItem =
|
||||
| TZabbixSyncListItem
|
||||
| TRailwaySyncListItem
|
||||
| TChecklySyncListItem
|
||||
| TSupabaseSyncListItem;
|
||||
| TSupabaseSyncListItem
|
||||
| TBitbucketSyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
canImportSecrets: boolean;
|
||||
|
||||
Reference in New Issue
Block a user