mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 12:26:38 +00:00
Merge pull request #755 from zwkee/integration/bitbucket
BitBucket Integration
This commit is contained in:
@@ -47,11 +47,13 @@ CLIENT_ID_VERCEL=
|
|||||||
CLIENT_ID_NETLIFY=
|
CLIENT_ID_NETLIFY=
|
||||||
CLIENT_ID_GITHUB=
|
CLIENT_ID_GITHUB=
|
||||||
CLIENT_ID_GITLAB=
|
CLIENT_ID_GITLAB=
|
||||||
|
CLIENT_ID_BITBUCKET=
|
||||||
CLIENT_SECRET_HEROKU=
|
CLIENT_SECRET_HEROKU=
|
||||||
CLIENT_SECRET_VERCEL=
|
CLIENT_SECRET_VERCEL=
|
||||||
CLIENT_SECRET_NETLIFY=
|
CLIENT_SECRET_NETLIFY=
|
||||||
CLIENT_SECRET_GITHUB=
|
CLIENT_SECRET_GITHUB=
|
||||||
CLIENT_SECRET_GITLAB=
|
CLIENT_SECRET_GITLAB=
|
||||||
|
CLIENT_SECRET_BITBUCKET=
|
||||||
CLIENT_SLUG_VERCEL=
|
CLIENT_SLUG_VERCEL=
|
||||||
|
|
||||||
# Sentry (optional) for monitoring errors
|
# Sentry (optional) for monitoring errors
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export const getClientIdNetlify = async () => (await client.getSecret("CLIENT_ID
|
|||||||
export const getClientIdGitHub = async () => (await client.getSecret("CLIENT_ID_GITHUB")).secretValue;
|
export const getClientIdGitHub = async () => (await client.getSecret("CLIENT_ID_GITHUB")).secretValue;
|
||||||
export const getClientIdGitLab = async () => (await client.getSecret("CLIENT_ID_GITLAB")).secretValue;
|
export const getClientIdGitLab = async () => (await client.getSecret("CLIENT_ID_GITLAB")).secretValue;
|
||||||
export const getClientIdGoogle = async () => (await client.getSecret("CLIENT_ID_GOOGLE")).secretValue;
|
export const getClientIdGoogle = async () => (await client.getSecret("CLIENT_ID_GOOGLE")).secretValue;
|
||||||
|
export const getClientIdBitBucket = async () => (await client.getSecret("CLIENT_ID_BITBUCKET")).secretValue;
|
||||||
export const getClientSecretAzure = async () => (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue;
|
export const getClientSecretAzure = async () => (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue;
|
||||||
export const getClientSecretHeroku = async () => (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue;
|
export const getClientSecretHeroku = async () => (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue;
|
||||||
export const getClientSecretVercel = async () => (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue;
|
export const getClientSecretVercel = async () => (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue;
|
||||||
@@ -44,6 +45,7 @@ export const getClientSecretNetlify = async () => (await client.getSecret("CLIEN
|
|||||||
export const getClientSecretGitHub = async () => (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue;
|
export const getClientSecretGitHub = async () => (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue;
|
||||||
export const getClientSecretGitLab = async () => (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue;
|
export const getClientSecretGitLab = async () => (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue;
|
||||||
export const getClientSecretGoogle = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE")).secretValue;
|
export const getClientSecretGoogle = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE")).secretValue;
|
||||||
|
export const getClientSecretBitBucket = async () => (await client.getSecret("CLIENT_SECRET_BITBUCKET")).secretValue;
|
||||||
export const getClientSlugVercel = async () => (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue;
|
export const getClientSlugVercel = async () => (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue;
|
||||||
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
||||||
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { IntegrationService } from "../../services";
|
|||||||
import {
|
import {
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
|
INTEGRATION_BITBUCKET_API_URL,
|
||||||
INTEGRATION_RAILWAY_API_URL,
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
INTEGRATION_SET,
|
INTEGRATION_SET,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
@@ -141,12 +142,14 @@ export const saveIntegrationAccessToken = async (req: Request, res: Response) =>
|
|||||||
*/
|
*/
|
||||||
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
||||||
const teamId = req.query.teamId as string;
|
const teamId = req.query.teamId as string;
|
||||||
|
const workspaceSlug = req.query.workspaceSlug as string;
|
||||||
|
|
||||||
const apps = await getApps({
|
const apps = await getApps({
|
||||||
integrationAuth: req.integrationAuth,
|
integrationAuth: req.integrationAuth,
|
||||||
accessToken: req.accessToken,
|
accessToken: req.accessToken,
|
||||||
accessId: req.accessId,
|
accessId: req.accessId,
|
||||||
...(teamId && { teamId })
|
...(teamId && { teamId }),
|
||||||
|
...(workspaceSlug && { workspaceSlug })
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
@@ -382,6 +385,66 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of workspaces allowed for Bitbucket integration
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getIntegrationAuthBitBucketWorkspaces = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
|
interface WorkspaceResponse {
|
||||||
|
size: number;
|
||||||
|
page: number;
|
||||||
|
pageLen: number;
|
||||||
|
next: string;
|
||||||
|
previous: string;
|
||||||
|
values: Array<Workspace>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Workspace {
|
||||||
|
type: string;
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
is_private: boolean;
|
||||||
|
created_on: string;
|
||||||
|
updated_on: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaces: Workspace[] = [];
|
||||||
|
let hasNextPage = true;
|
||||||
|
let workspaceUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/workspaces`
|
||||||
|
|
||||||
|
while (hasNextPage) {
|
||||||
|
const { data }: { data: WorkspaceResponse } = await standardRequest.get(
|
||||||
|
workspaceUrl,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${req.accessToken}`,
|
||||||
|
"Accept-Encoding": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data?.values.length > 0) {
|
||||||
|
data.values.forEach((workspace) => {
|
||||||
|
workspaces.push(workspace)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.next) {
|
||||||
|
workspaceUrl = data.next
|
||||||
|
} else {
|
||||||
|
hasNextPage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(200).send({
|
||||||
|
workspaces
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete integration authorization with id [integrationAuthId]
|
* Delete integration authorization with id [integrationAuthId]
|
||||||
* @param req
|
* @param req
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import { ssoController } from "../../controllers/v1";
|
|||||||
import { authLimiter } from "../../../helpers/rateLimiter";
|
import { authLimiter } from "../../../helpers/rateLimiter";
|
||||||
import {
|
import {
|
||||||
ACCEPTED,
|
ACCEPTED,
|
||||||
OWNER,
|
ADMIN,
|
||||||
ADMIN
|
OWNER
|
||||||
} from "../../../variables";
|
} from "../../../variables";
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
membership as v1MembershipRouter,
|
membership as v1MembershipRouter,
|
||||||
organization as v1OrganizationRouter,
|
organization as v1OrganizationRouter,
|
||||||
password as v1PasswordRouter,
|
password as v1PasswordRouter,
|
||||||
|
secretImport as v1SecretImportRouter,
|
||||||
secret as v1SecretRouter,
|
secret as v1SecretRouter,
|
||||||
secretScanning as v1SecretScanningRouter,
|
secretScanning as v1SecretScanningRouter,
|
||||||
secretsFolder as v1SecretsFolder,
|
secretsFolder as v1SecretsFolder,
|
||||||
@@ -43,8 +44,7 @@ import {
|
|||||||
userAction as v1UserActionRouter,
|
userAction as v1UserActionRouter,
|
||||||
user as v1UserRouter,
|
user as v1UserRouter,
|
||||||
webhooks as v1WebhooksRouter,
|
webhooks as v1WebhooksRouter,
|
||||||
workspace as v1WorkspaceRouter,
|
workspace as v1WorkspaceRouter
|
||||||
secretImport as v1SecretImportRouter
|
|
||||||
} from "./routes/v1";
|
} from "./routes/v1";
|
||||||
import {
|
import {
|
||||||
auth as v2AuthRouter,
|
auth as v2AuthRouter,
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ import {
|
|||||||
INTEGRATION_AWS_PARAMETER_STORE,
|
INTEGRATION_AWS_PARAMETER_STORE,
|
||||||
INTEGRATION_AWS_SECRET_MANAGER,
|
INTEGRATION_AWS_SECRET_MANAGER,
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
|
INTEGRATION_BITBUCKET_API_URL,
|
||||||
INTEGRATION_CHECKLY,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_CHECKLY_API_URL,
|
INTEGRATION_CHECKLY_API_URL,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_CIRCLECI_API_URL,
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
|
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
|
||||||
|
INTEGRATION_CODEFRESH,
|
||||||
|
INTEGRATION_CODEFRESH_API_URL,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
@@ -31,10 +35,7 @@ import {
|
|||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_TRAVISCI_API_URL,
|
INTEGRATION_TRAVISCI_API_URL,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL
|
||||||
INTEGRATION_CODEFRESH,
|
|
||||||
INTEGRATION_CODEFRESH_API_URL
|
|
||||||
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
interface App {
|
interface App {
|
||||||
@@ -57,11 +58,13 @@ const getApps = async ({
|
|||||||
accessToken,
|
accessToken,
|
||||||
accessId,
|
accessId,
|
||||||
teamId,
|
teamId,
|
||||||
|
workspaceSlug,
|
||||||
}: {
|
}: {
|
||||||
integrationAuth: IIntegrationAuth;
|
integrationAuth: IIntegrationAuth;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
accessId?: string;
|
accessId?: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
|
workspaceSlug?: string;
|
||||||
}) => {
|
}) => {
|
||||||
let apps: App[] = [];
|
let apps: App[] = [];
|
||||||
switch (integrationAuth.integration) {
|
switch (integrationAuth.integration) {
|
||||||
@@ -148,6 +151,12 @@ const getApps = async ({
|
|||||||
accountId: accessId
|
accountId: accessId
|
||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_BITBUCKET:
|
||||||
|
apps = await getAppsBitBucket({
|
||||||
|
accessToken,
|
||||||
|
workspaceSlug
|
||||||
|
});
|
||||||
|
break;
|
||||||
case INTEGRATION_CODEFRESH:
|
case INTEGRATION_CODEFRESH:
|
||||||
apps = await getAppsCodefresh({
|
apps = await getAppsCodefresh({
|
||||||
accessToken,
|
accessToken,
|
||||||
@@ -729,6 +738,79 @@ const getAppsCloudflarePages = async ({
|
|||||||
return apps;
|
return apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of repositories for the BitBucket integration based on provided BitBucket workspace
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {String} obj.accessToken - access token for BitBucket API
|
||||||
|
* @param {String} obj.workspaceSlug - Workspace identifier for fetching BitBucket repositories
|
||||||
|
* @returns {Object[]} apps - BitBucket repositories
|
||||||
|
* @returns {String} apps.name - name of BitBucket repository
|
||||||
|
*/
|
||||||
|
const getAppsBitBucket = async ({
|
||||||
|
accessToken,
|
||||||
|
workspaceSlug,
|
||||||
|
}: {
|
||||||
|
accessToken: string;
|
||||||
|
workspaceSlug?: string;
|
||||||
|
}) => {
|
||||||
|
interface RepositoriesResponse {
|
||||||
|
size: number;
|
||||||
|
page: number;
|
||||||
|
pageLen: number;
|
||||||
|
next: string;
|
||||||
|
previous: string;
|
||||||
|
values: Array<Repository>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Repository {
|
||||||
|
type: string;
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
is_private: boolean;
|
||||||
|
created_on: string;
|
||||||
|
updated_on: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!workspaceSlug) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const repositories: Repository[] = [];
|
||||||
|
let hasNextPage = true;
|
||||||
|
let repositoriesUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/repositories/${workspaceSlug}`
|
||||||
|
|
||||||
|
while (hasNextPage) {
|
||||||
|
const { data }: { data: RepositoriesResponse } = await standardRequest.get(
|
||||||
|
repositoriesUrl,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data?.values.length > 0) {
|
||||||
|
data.values.forEach((repository) => {
|
||||||
|
repositories.push(repository)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.next) {
|
||||||
|
repositoriesUrl = data.next
|
||||||
|
} else {
|
||||||
|
hasNextPage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const apps = repositories.map((repository) => {
|
||||||
|
return {
|
||||||
|
name: repository.name,
|
||||||
|
appId: repository.uuid,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return apps;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of projects for Supabase integration
|
* Return list of projects for Supabase integration
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { standardRequest } from "../config/request";
|
|||||||
import {
|
import {
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
@@ -15,11 +17,13 @@ import {
|
|||||||
} from "../variables";
|
} from "../variables";
|
||||||
import {
|
import {
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
|
getClientIdBitBucket,
|
||||||
getClientIdGitHub,
|
getClientIdGitHub,
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
getClientIdNetlify,
|
getClientIdNetlify,
|
||||||
getClientIdVercel,
|
getClientIdVercel,
|
||||||
getClientSecretAzure,
|
getClientSecretAzure,
|
||||||
|
getClientSecretBitBucket,
|
||||||
getClientSecretGitHub,
|
getClientSecretGitHub,
|
||||||
getClientSecretGitLab,
|
getClientSecretGitLab,
|
||||||
getClientSecretHeroku,
|
getClientSecretHeroku,
|
||||||
@@ -78,6 +82,15 @@ interface ExchangeCodeGitlabResponse {
|
|||||||
created_at: number;
|
created_at: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ExchangeCodeBitBucketResponse {
|
||||||
|
access_token: string;
|
||||||
|
token_type: string;
|
||||||
|
expires_in: number;
|
||||||
|
refresh_token: string;
|
||||||
|
scopes: string;
|
||||||
|
state: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for OAuth2
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for OAuth2
|
||||||
* code-token exchange for integration named [integration]
|
* code-token exchange for integration named [integration]
|
||||||
@@ -129,6 +142,12 @@ const exchangeCode = async ({
|
|||||||
obj = await exchangeCodeGitlab({
|
obj = await exchangeCodeGitlab({
|
||||||
code,
|
code,
|
||||||
});
|
});
|
||||||
|
break;
|
||||||
|
case INTEGRATION_BITBUCKET:
|
||||||
|
obj = await exchangeCodeBitBucket({
|
||||||
|
code,
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
@@ -347,4 +366,43 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for BitBucket
|
||||||
|
* code-token exchange
|
||||||
|
* @param {Object} obj1
|
||||||
|
* @param {Object} obj1.code - code for code-token exchange
|
||||||
|
* @returns {Object} obj2
|
||||||
|
* @returns {String} obj2.accessToken - access token for BitBucket API
|
||||||
|
* @returns {String} obj2.refreshToken - refresh token for BitBucket API
|
||||||
|
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||||
|
*/
|
||||||
|
const exchangeCodeBitBucket = async ({ code }: { code: string }) => {
|
||||||
|
const accessExpiresAt = new Date();
|
||||||
|
const res: ExchangeCodeBitBucketResponse = (
|
||||||
|
await standardRequest.post(
|
||||||
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
code: code,
|
||||||
|
client_id: await getClientIdBitBucket(),
|
||||||
|
client_secret: await getClientSecretBitBucket(),
|
||||||
|
redirect_uri: `${await getSiteURL()}/integrations/bitbucket/oauth2/callback`,
|
||||||
|
} as any),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).data;
|
||||||
|
|
||||||
|
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: res.access_token,
|
||||||
|
refreshToken: res.refresh_token,
|
||||||
|
accessExpiresAt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export { exchangeCode };
|
export { exchangeCode };
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { standardRequest } from "../config/request";
|
|||||||
import { IIntegrationAuth } from "../models";
|
import { IIntegrationAuth } from "../models";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_HEROKU,
|
INTEGRATION_HEROKU,
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
@@ -13,8 +15,10 @@ import {
|
|||||||
import { IntegrationService } from "../services";
|
import { IntegrationService } from "../services";
|
||||||
import {
|
import {
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
|
getClientIdBitBucket,
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
getClientSecretAzure,
|
getClientSecretAzure,
|
||||||
|
getClientSecretBitBucket,
|
||||||
getClientSecretGitLab,
|
getClientSecretGitLab,
|
||||||
getClientSecretHeroku,
|
getClientSecretHeroku,
|
||||||
getSiteURL,
|
getSiteURL,
|
||||||
@@ -46,6 +50,15 @@ interface RefreshTokenGitLabResponse {
|
|||||||
created_at: number;
|
created_at: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RefreshTokenBitBucketResponse {
|
||||||
|
access_token: string;
|
||||||
|
token_type: string;
|
||||||
|
expires_in: number;
|
||||||
|
refresh_token: string;
|
||||||
|
scopes: string;
|
||||||
|
state: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return new access token by exchanging refresh token [refreshToken] for integration
|
* Return new access token by exchanging refresh token [refreshToken] for integration
|
||||||
* named [integration]
|
* named [integration]
|
||||||
@@ -83,6 +96,11 @@ const exchangeRefresh = async ({
|
|||||||
refreshToken,
|
refreshToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case INTEGRATION_BITBUCKET:
|
||||||
|
tokenDetails = await exchangeRefreshBitBucket({
|
||||||
|
refreshToken,
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Failed to exchange token for incompatible integration");
|
throw new Error("Failed to exchange token for incompatible integration");
|
||||||
}
|
}
|
||||||
@@ -218,4 +236,46 @@ const exchangeRefreshGitLab = async ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return new access token by exchanging refresh token [refreshToken] for the
|
||||||
|
* BitBucket integration
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {String} obj.refreshToken - refresh token to use to get new access token for BitBucket
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const exchangeRefreshBitBucket = async ({
|
||||||
|
refreshToken,
|
||||||
|
}: {
|
||||||
|
refreshToken: string;
|
||||||
|
}) => {
|
||||||
|
const accessExpiresAt = new Date();
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
data: RefreshTokenBitBucketResponse;
|
||||||
|
} = await standardRequest.post(
|
||||||
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
client_id: await getClientIdBitBucket(),
|
||||||
|
client_secret: await getClientSecretBitBucket(),
|
||||||
|
redirect_uri: `${await getSiteURL()}/integrations/bitbucket/oauth2/callback`,
|
||||||
|
} as any),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Accept-Encoding": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: data.access_token,
|
||||||
|
refreshToken: data.refresh_token,
|
||||||
|
accessExpiresAt,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export { exchangeRefresh };
|
export { exchangeRefresh };
|
||||||
|
|||||||
@@ -14,12 +14,16 @@ import {
|
|||||||
INTEGRATION_AWS_PARAMETER_STORE,
|
INTEGRATION_AWS_PARAMETER_STORE,
|
||||||
INTEGRATION_AWS_SECRET_MANAGER,
|
INTEGRATION_AWS_SECRET_MANAGER,
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
|
INTEGRATION_BITBUCKET_API_URL,
|
||||||
INTEGRATION_CHECKLY,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_CHECKLY_API_URL,
|
INTEGRATION_CHECKLY_API_URL,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_CIRCLECI_API_URL,
|
INTEGRATION_CIRCLECI_API_URL,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
|
INTEGRATION_CLOUDFLARE_PAGES_API_URL,
|
||||||
|
INTEGRATION_CODEFRESH,
|
||||||
|
INTEGRATION_CODEFRESH_API_URL,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
@@ -41,9 +45,7 @@ import {
|
|||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_TRAVISCI_API_URL,
|
INTEGRATION_TRAVISCI_API_URL,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL
|
||||||
INTEGRATION_CODEFRESH,
|
|
||||||
INTEGRATION_CODEFRESH_API_URL
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { standardRequest } from "../config/request";
|
import { standardRequest } from "../config/request";
|
||||||
|
|
||||||
@@ -180,34 +182,6 @@ const syncSecrets = async ({
|
|||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_FLYIO:
|
|
||||||
await syncSecretsFlyio({
|
|
||||||
integration,
|
|
||||||
secrets,
|
|
||||||
accessToken,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case INTEGRATION_CIRCLECI:
|
|
||||||
await syncSecretsCircleCI({
|
|
||||||
integration,
|
|
||||||
secrets,
|
|
||||||
accessToken,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case INTEGRATION_TRAVISCI:
|
|
||||||
await syncSecretsTravisCI({
|
|
||||||
integration,
|
|
||||||
secrets,
|
|
||||||
accessToken,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case INTEGRATION_SUPABASE:
|
|
||||||
await syncSecretsSupabase({
|
|
||||||
integration,
|
|
||||||
secrets,
|
|
||||||
accessToken,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case INTEGRATION_CHECKLY:
|
case INTEGRATION_CHECKLY:
|
||||||
await syncSecretsCheckly({
|
await syncSecretsCheckly({
|
||||||
integration,
|
integration,
|
||||||
@@ -239,7 +213,14 @@ const syncSecrets = async ({
|
|||||||
accessToken,
|
accessToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
case INTEGRATION_BITBUCKET:
|
||||||
|
await syncSecretsBitBucket({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -706,8 +687,6 @@ const syncSecretsVercel = async ({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// return secret.target.includes(integration.targetEnvironment);
|
|
||||||
|
|
||||||
const res: { [key: string]: VercelSecret } = {};
|
const res: { [key: string]: VercelSecret } = {};
|
||||||
|
|
||||||
for await (const vercelSecret of vercelSecrets) {
|
for await (const vercelSecret of vercelSecrets) {
|
||||||
@@ -1935,7 +1914,7 @@ const syncSecretsCloudflarePages = async ({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.data.result['deployment_configs'][integration.targetEnvironment]['env_vars'];
|
.data.result["deployment_configs"][integration.targetEnvironment]["env_vars"];
|
||||||
|
|
||||||
// copy the secrets object, so we can set deleted keys to null
|
// copy the secrets object, so we can set deleted keys to null
|
||||||
const secretsObj: any = { ...secrets };
|
const secretsObj: any = { ...secrets };
|
||||||
@@ -1975,6 +1954,121 @@ const syncSecretsCloudflarePages = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Sync/push [secrets] to BitBucket repo with name [integration.app]
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {IIntegration} obj.integration - integration details
|
||||||
|
* @param {IIntegrationAuth} obj.integrationAuth - integration auth details
|
||||||
|
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||||
|
* @param {String} obj.accessToken - access token for BitBucket integration
|
||||||
|
*/
|
||||||
|
const syncSecretsBitBucket = async ({
|
||||||
|
integration,
|
||||||
|
secrets,
|
||||||
|
accessToken,
|
||||||
|
}: {
|
||||||
|
integration: IIntegration;
|
||||||
|
secrets: any;
|
||||||
|
accessToken: string;
|
||||||
|
}) => {
|
||||||
|
interface VariablesResponse {
|
||||||
|
size: number;
|
||||||
|
page: number;
|
||||||
|
pageLen: number;
|
||||||
|
next: string;
|
||||||
|
previous: string;
|
||||||
|
values: Array<BitbucketVariable>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BitbucketVariable {
|
||||||
|
type: string;
|
||||||
|
uuid: string;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
secured: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const res: { [key: string]: BitbucketVariable } = {};
|
||||||
|
|
||||||
|
let hasNextPage = true;
|
||||||
|
let variablesUrl = `${INTEGRATION_BITBUCKET_API_URL}/2.0/repositories/${integration.targetEnvironmentId}/${integration.appId}/pipelines_config/variables`
|
||||||
|
|
||||||
|
while (hasNextPage) {
|
||||||
|
const { data }: { data: VariablesResponse } = await standardRequest.get(
|
||||||
|
variablesUrl,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (data?.values.length > 0) {
|
||||||
|
data.values.forEach((variable) => {
|
||||||
|
res[variable.key] = variable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.next) {
|
||||||
|
variablesUrl = data.next
|
||||||
|
} else {
|
||||||
|
hasNextPage = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const key of Object.keys(secrets)) {
|
||||||
|
if (key in res) {
|
||||||
|
// update existing secret
|
||||||
|
await standardRequest.put(
|
||||||
|
`${variablesUrl}/${res[key].uuid}`,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
value: secrets[key],
|
||||||
|
secured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// create new secret
|
||||||
|
await standardRequest.post(
|
||||||
|
variablesUrl,
|
||||||
|
{
|
||||||
|
key,
|
||||||
|
value: secrets[key],
|
||||||
|
secured: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
"Accept": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for await (const key of Object.keys(res)) {
|
||||||
|
if (!(key in secrets)) {
|
||||||
|
// delete secret
|
||||||
|
await standardRequest.delete(
|
||||||
|
`${variablesUrl}/${res[key].uuid}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
"Accept": "application/json",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
* Sync/push [secrets] to Codefresh with name [integration.app]
|
* Sync/push [secrets] to Codefresh with name [integration.app]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {IIntegration} obj.integration - integration details
|
* @param {IIntegration} obj.integration - integration details
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import {
|
|||||||
INTEGRATION_AWS_PARAMETER_STORE,
|
INTEGRATION_AWS_PARAMETER_STORE,
|
||||||
INTEGRATION_AWS_SECRET_MANAGER,
|
INTEGRATION_AWS_SECRET_MANAGER,
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_CHECKLY,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
|
INTEGRATION_CODEFRESH,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
@@ -17,8 +19,7 @@ import {
|
|||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_SUPABASE,
|
INTEGRATION_SUPABASE,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL
|
||||||
INTEGRATION_CODEFRESH
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
export interface IIntegration {
|
export interface IIntegration {
|
||||||
@@ -56,7 +57,8 @@ export interface IIntegration {
|
|||||||
| "checkly"
|
| "checkly"
|
||||||
| "hashicorp-vault"
|
| "hashicorp-vault"
|
||||||
| "cloudflare-pages"
|
| "cloudflare-pages"
|
||||||
| "codefresh";
|
| "bitbucket"
|
||||||
|
| "codefresh"
|
||||||
integrationAuth: Types.ObjectId;
|
integrationAuth: Types.ObjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +148,7 @@ const integrationSchema = new Schema<IIntegration>(
|
|||||||
INTEGRATION_CHECKLY,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_HASHICORP_VAULT,
|
INTEGRATION_HASHICORP_VAULT,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_CODEFRESH
|
INTEGRATION_CODEFRESH
|
||||||
],
|
],
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
INTEGRATION_AWS_PARAMETER_STORE,
|
INTEGRATION_AWS_PARAMETER_STORE,
|
||||||
INTEGRATION_AWS_SECRET_MANAGER,
|
INTEGRATION_AWS_SECRET_MANAGER,
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_CIRCLECI,
|
INTEGRATION_CIRCLECI,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
|
INTEGRATION_CODEFRESH,
|
||||||
INTEGRATION_FLYIO,
|
INTEGRATION_FLYIO,
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
@@ -19,14 +21,32 @@ import {
|
|||||||
INTEGRATION_RENDER,
|
INTEGRATION_RENDER,
|
||||||
INTEGRATION_SUPABASE,
|
INTEGRATION_SUPABASE,
|
||||||
INTEGRATION_TRAVISCI,
|
INTEGRATION_TRAVISCI,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL
|
||||||
INTEGRATION_CODEFRESH
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
|
|
||||||
export interface IIntegrationAuth extends Document {
|
export interface IIntegrationAuth extends Document {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
workspace: Types.ObjectId;
|
workspace: Types.ObjectId;
|
||||||
integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'railway' | 'flyio' | 'azure-key-vault' | 'laravel-forge' | 'circleci' | 'travisci' | 'supabase' | 'aws-parameter-store' | 'aws-secret-manager' | 'checkly' | 'cloudflare-pages' | 'codefresh';
|
integration:
|
||||||
|
| "heroku"
|
||||||
|
| "vercel"
|
||||||
|
| "netlify"
|
||||||
|
| "github"
|
||||||
|
| "gitlab"
|
||||||
|
| "render"
|
||||||
|
| "railway"
|
||||||
|
| "flyio"
|
||||||
|
| "azure-key-vault"
|
||||||
|
| "laravel-forge"
|
||||||
|
| "circleci"
|
||||||
|
| "travisci"
|
||||||
|
| "supabase"
|
||||||
|
| "aws-parameter-store"
|
||||||
|
| "aws-secret-manager"
|
||||||
|
| "checkly"
|
||||||
|
| "cloudflare-pages"
|
||||||
|
| "codefresh"
|
||||||
|
| "bitbucket";
|
||||||
teamId: string;
|
teamId: string;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
url: string;
|
url: string;
|
||||||
@@ -72,6 +92,7 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
|
|||||||
INTEGRATION_SUPABASE,
|
INTEGRATION_SUPABASE,
|
||||||
INTEGRATION_HASHICORP_VAULT,
|
INTEGRATION_HASHICORP_VAULT,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_CODEFRESH
|
INTEGRATION_CODEFRESH
|
||||||
],
|
],
|
||||||
required: true,
|
required: true,
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ router.get(
|
|||||||
}),
|
}),
|
||||||
param("integrationAuthId"),
|
param("integrationAuthId"),
|
||||||
query("teamId"),
|
query("teamId"),
|
||||||
|
query("workspaceSlug"),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
integrationAuthController.getIntegrationAuthApps
|
integrationAuthController.getIntegrationAuthApps
|
||||||
);
|
);
|
||||||
@@ -141,6 +142,19 @@ router.get(
|
|||||||
integrationAuthController.getIntegrationAuthRailwayServices
|
integrationAuthController.getIntegrationAuthRailwayServices
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
"/:integrationAuthId/bitbucket/workspaces",
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: [AUTH_MODE_JWT],
|
||||||
|
}),
|
||||||
|
requireIntegrationAuthorizationAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
}),
|
||||||
|
param("integrationAuthId").exists().isString(),
|
||||||
|
validateRequest,
|
||||||
|
integrationAuthController.getIntegrationAuthBitBucketWorkspaces
|
||||||
|
);
|
||||||
|
|
||||||
router.delete(
|
router.delete(
|
||||||
"/:integrationAuthId",
|
"/:integrationAuthId",
|
||||||
requireAuth({
|
requireAuth({
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ const ALGORITHM = "aes-256-gcm";
|
|||||||
const BLOCK_SIZE_BYTES = 16;
|
const BLOCK_SIZE_BYTES = 16;
|
||||||
|
|
||||||
export default class AesGCM {
|
export default class AesGCM {
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
static encrypt(
|
static encrypt(
|
||||||
text: string,
|
text: string,
|
||||||
secret: string
|
secret: string
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { Types } from "mongoose";
|
|||||||
import { AuthData } from "../interfaces/middleware";
|
import { AuthData } from "../interfaces/middleware";
|
||||||
import {
|
import {
|
||||||
AuthProvider,
|
AuthProvider,
|
||||||
|
MembershipOrg,
|
||||||
|
Organization,
|
||||||
ServiceAccount,
|
ServiceAccount,
|
||||||
ServiceTokenData,
|
ServiceTokenData,
|
||||||
User,
|
User
|
||||||
Organization,
|
|
||||||
MembershipOrg
|
|
||||||
} from "../models";
|
} from "../models";
|
||||||
import { createToken } from "../helpers/auth";
|
import { createToken } from "../helpers/auth";
|
||||||
import {
|
import {
|
||||||
@@ -18,8 +18,8 @@ import {
|
|||||||
getJwtProviderAuthSecret,
|
getJwtProviderAuthSecret,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
import { getSSOConfigHelper } from "../ee/helpers/organizations";
|
import { getSSOConfigHelper } from "../ee/helpers/organizations";
|
||||||
import { OrganizationNotFoundError, InternalServerError } from "./errors";
|
import { InternalServerError, OrganizationNotFoundError } from "./errors";
|
||||||
import { MEMBER, INVITED } from "../variables";
|
import { INVITED, MEMBER } from "../variables";
|
||||||
import { getSiteURL } from "../config";
|
import { getSiteURL } from "../config";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
|
getClientIdBitBucket,
|
||||||
getClientIdGitHub,
|
getClientIdGitHub,
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
getClientIdHeroku,
|
getClientIdHeroku,
|
||||||
@@ -26,6 +27,7 @@ export const INTEGRATION_SUPABASE = "supabase";
|
|||||||
export const INTEGRATION_CHECKLY = "checkly";
|
export const INTEGRATION_CHECKLY = "checkly";
|
||||||
export const INTEGRATION_HASHICORP_VAULT = "hashicorp-vault";
|
export const INTEGRATION_HASHICORP_VAULT = "hashicorp-vault";
|
||||||
export const INTEGRATION_CLOUDFLARE_PAGES = "cloudflare-pages";
|
export const INTEGRATION_CLOUDFLARE_PAGES = "cloudflare-pages";
|
||||||
|
export const INTEGRATION_BITBUCKET = "bitbucket";
|
||||||
export const INTEGRATION_CODEFRESH = "codefresh";
|
export const INTEGRATION_CODEFRESH = "codefresh";
|
||||||
export const INTEGRATION_SET = new Set([
|
export const INTEGRATION_SET = new Set([
|
||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
@@ -43,6 +45,7 @@ export const INTEGRATION_SET = new Set([
|
|||||||
INTEGRATION_CHECKLY,
|
INTEGRATION_CHECKLY,
|
||||||
INTEGRATION_HASHICORP_VAULT,
|
INTEGRATION_HASHICORP_VAULT,
|
||||||
INTEGRATION_CLOUDFLARE_PAGES,
|
INTEGRATION_CLOUDFLARE_PAGES,
|
||||||
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_CODEFRESH
|
INTEGRATION_CODEFRESH
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -58,6 +61,7 @@ export const INTEGRATION_NETLIFY_TOKEN_URL = "https://api.netlify.com/oauth/toke
|
|||||||
export const INTEGRATION_GITHUB_TOKEN_URL =
|
export const INTEGRATION_GITHUB_TOKEN_URL =
|
||||||
"https://github.com/login/oauth/access_token";
|
"https://github.com/login/oauth/access_token";
|
||||||
export const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
|
export const INTEGRATION_GITLAB_TOKEN_URL = "https://gitlab.com/oauth/token";
|
||||||
|
export const INTEGRATION_BITBUCKET_TOKEN_URL = "https://bitbucket.org/site/oauth2/access_token"
|
||||||
|
|
||||||
// integration apps endpoints
|
// integration apps endpoints
|
||||||
export const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
|
export const INTEGRATION_HEROKU_API_URL = "https://api.heroku.com";
|
||||||
@@ -73,6 +77,7 @@ export const INTEGRATION_SUPABASE_API_URL = "https://api.supabase.com";
|
|||||||
export const INTEGRATION_LARAVELFORGE_API_URL = "https://forge.laravel.com";
|
export const INTEGRATION_LARAVELFORGE_API_URL = "https://forge.laravel.com";
|
||||||
export const INTEGRATION_CHECKLY_API_URL = "https://api.checklyhq.com";
|
export const INTEGRATION_CHECKLY_API_URL = "https://api.checklyhq.com";
|
||||||
export const INTEGRATION_CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com";
|
export const INTEGRATION_CLOUDFLARE_PAGES_API_URL = "https://api.cloudflare.com";
|
||||||
|
export const INTEGRATION_BITBUCKET_API_URL = "https://api.bitbucket.org";
|
||||||
export const INTEGRATION_CODEFRESH_API_URL = "https://g.codefresh.io/api";
|
export const INTEGRATION_CODEFRESH_API_URL = "https://g.codefresh.io/api";
|
||||||
|
|
||||||
export const getIntegrationOptions = async () => {
|
export const getIntegrationOptions = async () => {
|
||||||
@@ -249,6 +254,15 @@ export const getIntegrationOptions = async () => {
|
|||||||
clientId: "",
|
clientId: "",
|
||||||
docsLink: ""
|
docsLink: ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "BitBucket",
|
||||||
|
slug: "bitbucket",
|
||||||
|
image: "BitBucket.png",
|
||||||
|
isAvailable: true,
|
||||||
|
type: "oauth",
|
||||||
|
clientId: await getClientIdBitBucket(),
|
||||||
|
docsLink: ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Codefresh",
|
name: "Codefresh",
|
||||||
slug: "codefresh",
|
slug: "codefresh",
|
||||||
@@ -257,7 +271,7 @@ export const getIntegrationOptions = async () => {
|
|||||||
type: "pat",
|
type: "pat",
|
||||||
clientId: "",
|
clientId: "",
|
||||||
docsLink: "",
|
docsLink: "",
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return INTEGRATION_OPTIONS;
|
return INTEGRATION_OPTIONS;
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 184 KiB |
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
title: "BitBucket"
|
||||||
|
description: "How to sync secrets from Infisical to BitBucket"
|
||||||
|
---
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
|
||||||
|
- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
|
||||||
|
|
||||||
|
## Navigate to your project's integrations tab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Authorize Infisical for BitBucket
|
||||||
|
|
||||||
|
Press on the BitBucket tile and grant Infisical access to your BitBucket account.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<Info>
|
||||||
|
If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
|
||||||
|
Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
|
||||||
|
</Info>
|
||||||
|
|
||||||
|
## Start integration
|
||||||
|
|
||||||
|
Select which Infisical environment secrets you want to sync to which BitBucket repo and press start integration to start syncing secrets to the repo.
|
||||||
|
|
||||||
|

|
||||||
@@ -28,6 +28,7 @@ Missing an integration? [Throw in a request](https://github.com/Infisical/infisi
|
|||||||
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
| [AWS Parameter Store](/integrations/cloud/aws-parameter-store) | Cloud | Available |
|
||||||
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
| [AWS Secret Manager](/integrations/cloud/aws-secret-manager) | Cloud | Available |
|
||||||
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
|
| [Azure Key Vault](/integrations/cloud/azure-key-vault) | Cloud | Available |
|
||||||
|
| [BitBucket](/integrations/cicd/bitbucket) | CI/CD | Available |
|
||||||
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
| [GitHub Actions](/integrations/cicd/githubactions) | CI/CD | Available |
|
||||||
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
|
| [GitLab](/integrations/cicd/gitlab) | CI/CD | Available |
|
||||||
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
|
| [CircleCI](/integrations/cicd/circleci) | CI/CD | Available |
|
||||||
|
|||||||
+2
-1
@@ -222,7 +222,8 @@
|
|||||||
"integrations/cicd/githubactions",
|
"integrations/cicd/githubactions",
|
||||||
"integrations/cicd/gitlab",
|
"integrations/cicd/gitlab",
|
||||||
"integrations/cicd/circleci",
|
"integrations/cicd/circleci",
|
||||||
"integrations/cicd/travisci"
|
"integrations/cicd/travisci",
|
||||||
|
"integrations/cicd/bitbucket"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -107,6 +107,14 @@ Other environment variables are listed below to increase the functionality of yo
|
|||||||
<ParamField query="CLIENT_SLUG_VERCEL" type="string" default="none" optional>
|
<ParamField query="CLIENT_SLUG_VERCEL" type="string" default="none" optional>
|
||||||
OAuth2 slug for Vercel integration
|
OAuth2 slug for Vercel integration
|
||||||
</ParamField>
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_ID_BITBUCKET" type="string" default="none" optional>
|
||||||
|
OAuth2 client ID for BitBucket integration
|
||||||
|
</ParamField>
|
||||||
|
|
||||||
|
<ParamField query="CLIENT_SECRET_BITBUCKET" type="string" default="none" optional>
|
||||||
|
OAuth2 client secret for BitBucket integration
|
||||||
|
</ParamField>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Auth Integrations">
|
<Tab title="Auth Integrations">
|
||||||
To integrate with external auth providers, provide value for the related keys
|
To integrate with external auth providers, provide value for the related keys
|
||||||
|
|||||||
@@ -3,26 +3,27 @@ interface Mapping {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const integrationSlugNameMapping: Mapping = {
|
const integrationSlugNameMapping: Mapping = {
|
||||||
'azure-key-vault': 'Azure Key Vault',
|
"azure-key-vault": "Azure Key Vault",
|
||||||
'aws-parameter-store': 'AWS Parameter Store',
|
"aws-parameter-store": "AWS Parameter Store",
|
||||||
'aws-secret-manager': 'AWS Secret Manager',
|
"aws-secret-manager": "AWS Secret Manager",
|
||||||
'heroku': 'Heroku',
|
heroku: "Heroku",
|
||||||
'vercel': 'Vercel',
|
vercel: "Vercel",
|
||||||
'netlify': 'Netlify',
|
netlify: "Netlify",
|
||||||
'github': 'GitHub',
|
github: "GitHub",
|
||||||
'gitlab': 'GitLab',
|
gitlab: "GitLab",
|
||||||
'render': 'Render',
|
render: "Render",
|
||||||
'laravel-forge': "Laravel Forge",
|
"laravel-forge": "Laravel Forge",
|
||||||
'railway': 'Railway',
|
railway: "Railway",
|
||||||
'flyio': 'Fly.io',
|
flyio: "Fly.io",
|
||||||
'circleci': 'CircleCI',
|
circleci: "CircleCI",
|
||||||
'travisci': 'TravisCI',
|
travisci: "TravisCI",
|
||||||
'supabase': 'Supabase',
|
supabase: "Supabase",
|
||||||
'checkly': 'Checkly',
|
checkly: "Checkly",
|
||||||
'hashicorp-vault': 'Vault',
|
"hashicorp-vault": "Vault",
|
||||||
'cloudflare-pages': 'Cloudflare Pages',
|
"cloudflare-pages": "Cloudflare Pages",
|
||||||
'codefresh': 'Codefresh'
|
"codefresh": "Codefresh",
|
||||||
}
|
bitbucket: "BitBucket"
|
||||||
|
};
|
||||||
|
|
||||||
const envMapping: Mapping = {
|
const envMapping: Mapping = {
|
||||||
Development: "dev",
|
Development: "dev",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -1,6 +1,7 @@
|
|||||||
export {
|
export {
|
||||||
useDeleteIntegrationAuth,
|
useDeleteIntegrationAuth,
|
||||||
useGetIntegrationAuthApps,
|
useGetIntegrationAuthApps,
|
||||||
|
useGetIntegrationAuthBitBucketWorkspaces,
|
||||||
useGetIntegrationAuthById,
|
useGetIntegrationAuthById,
|
||||||
useGetIntegrationAuthRailwayEnvironments,
|
useGetIntegrationAuthRailwayEnvironments,
|
||||||
useGetIntegrationAuthRailwayServices,
|
useGetIntegrationAuthRailwayServices,
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|||||||
import { apiRequest } from "@app/config/request";
|
import { apiRequest } from "@app/config/request";
|
||||||
|
|
||||||
import { workspaceKeys } from "../workspace/queries";
|
import { workspaceKeys } from "../workspace/queries";
|
||||||
import { App, Environment, IntegrationAuth, Service, Team } from "./types";
|
import { App, BitBucketWorkspace, Environment, IntegrationAuth, Service, Team } from "./types";
|
||||||
|
|
||||||
const integrationAuthKeys = {
|
const integrationAuthKeys = {
|
||||||
getIntegrationAuthById: (integrationAuthId: string) =>
|
getIntegrationAuthById: (integrationAuthId: string) =>
|
||||||
[{ integrationAuthId }, "integrationAuth"] as const,
|
[{ integrationAuthId }, "integrationAuth"] as const,
|
||||||
getIntegrationAuthApps: (integrationAuthId: string, teamId?: string) =>
|
getIntegrationAuthApps: (integrationAuthId: string, teamId?: string, workspaceSlug?: string) =>
|
||||||
[{ integrationAuthId, teamId }, "integrationAuthApps"] as const,
|
[{ integrationAuthId, teamId, workspaceSlug }, "integrationAuthApps"] as const,
|
||||||
getIntegrationAuthTeams: (integrationAuthId: string) =>
|
getIntegrationAuthTeams: (integrationAuthId: string) =>
|
||||||
[{ integrationAuthId }, "integrationAuthTeams"] as const,
|
[{ integrationAuthId }, "integrationAuthTeams"] as const,
|
||||||
getIntegrationAuthVercelBranches: ({
|
getIntegrationAuthVercelBranches: ({
|
||||||
@@ -33,7 +33,9 @@ const integrationAuthKeys = {
|
|||||||
}: {
|
}: {
|
||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
}) => [{ integrationAuthId, appId }, "integrationAuthRailwayServices"] as const
|
}) => [{ integrationAuthId, appId }, "integrationAuthRailwayServices"] as const,
|
||||||
|
getIntegrationAuthBitBucketWorkspaces: (integrationAuthId: string) =>
|
||||||
|
[{ integrationAuthId }, "integrationAuthBitbucketWorkspaces"] as const,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchIntegrationAuthById = async (integrationAuthId: string) => {
|
const fetchIntegrationAuthById = async (integrationAuthId: string) => {
|
||||||
@@ -45,12 +47,22 @@ const fetchIntegrationAuthById = async (integrationAuthId: string) => {
|
|||||||
|
|
||||||
const fetchIntegrationAuthApps = async ({
|
const fetchIntegrationAuthApps = async ({
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
teamId
|
teamId,
|
||||||
|
workspaceSlug
|
||||||
}: {
|
}: {
|
||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
|
workspaceSlug?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const searchParams = new URLSearchParams(teamId ? { teamId } : undefined);
|
const params: Record<string, string> = {}
|
||||||
|
if (teamId) {
|
||||||
|
params.teamId = teamId
|
||||||
|
}
|
||||||
|
if (workspaceSlug) {
|
||||||
|
params.workspaceSlug = workspaceSlug
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchParams = new URLSearchParams(params);
|
||||||
const { data } = await apiRequest.get<{ apps: App[] }>(
|
const { data } = await apiRequest.get<{ apps: App[] }>(
|
||||||
`/api/v1/integration-auth/${integrationAuthId}/apps`,
|
`/api/v1/integration-auth/${integrationAuthId}/apps`,
|
||||||
{ params: searchParams }
|
{ params: searchParams }
|
||||||
@@ -129,6 +141,13 @@ const fetchIntegrationAuthRailwayServices = async ({
|
|||||||
return services;
|
return services;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchIntegrationAuthBitBucketWorkspaces = async (integrationAuthId: string) => {
|
||||||
|
const { data: { workspaces } } = await apiRequest.get<{ workspaces: BitBucketWorkspace[] }>(
|
||||||
|
`/api/v1/integration-auth/${integrationAuthId}/bitbucket/workspaces`
|
||||||
|
);
|
||||||
|
return workspaces;
|
||||||
|
};
|
||||||
|
|
||||||
export const useGetIntegrationAuthById = (integrationAuthId: string) => {
|
export const useGetIntegrationAuthById = (integrationAuthId: string) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId),
|
queryKey: integrationAuthKeys.getIntegrationAuthById(integrationAuthId),
|
||||||
@@ -139,17 +158,20 @@ export const useGetIntegrationAuthById = (integrationAuthId: string) => {
|
|||||||
|
|
||||||
export const useGetIntegrationAuthApps = ({
|
export const useGetIntegrationAuthApps = ({
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
teamId
|
teamId,
|
||||||
|
workspaceSlug,
|
||||||
}: {
|
}: {
|
||||||
integrationAuthId: string;
|
integrationAuthId: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
|
workspaceSlug?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: integrationAuthKeys.getIntegrationAuthApps(integrationAuthId, teamId),
|
queryKey: integrationAuthKeys.getIntegrationAuthApps(integrationAuthId, teamId, workspaceSlug),
|
||||||
queryFn: () =>
|
queryFn: () =>
|
||||||
fetchIntegrationAuthApps({
|
fetchIntegrationAuthApps({
|
||||||
integrationAuthId,
|
integrationAuthId,
|
||||||
teamId
|
teamId,
|
||||||
|
workspaceSlug
|
||||||
}),
|
}),
|
||||||
enabled: true
|
enabled: true
|
||||||
});
|
});
|
||||||
@@ -226,6 +248,13 @@ export const useGetIntegrationAuthRailwayServices = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetIntegrationAuthBitBucketWorkspaces = (integrationAuthId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: integrationAuthKeys.getIntegrationAuthBitBucketWorkspaces(integrationAuthId),
|
||||||
|
queryFn: () => fetchIntegrationAuthBitBucketWorkspaces(integrationAuthId),
|
||||||
|
enabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useDeleteIntegrationAuth = () => {
|
export const useDeleteIntegrationAuth = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -29,3 +29,9 @@ export type Service = {
|
|||||||
name: string;
|
name: string;
|
||||||
serviceId: string;
|
serviceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type BitBucketWorkspace = {
|
||||||
|
uuid: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import queryString from "query-string";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardTitle,
|
||||||
|
FormControl,
|
||||||
|
Input,
|
||||||
|
Select,
|
||||||
|
SelectItem
|
||||||
|
} from "../../../components/v2";
|
||||||
|
import {
|
||||||
|
useGetIntegrationAuthApps,
|
||||||
|
useGetIntegrationAuthBitBucketWorkspaces,
|
||||||
|
useGetIntegrationAuthById,
|
||||||
|
} from "../../../hooks/api/integrationAuth";
|
||||||
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
|
import createIntegration from "../../api/integrations/createIntegration";
|
||||||
|
|
||||||
|
export default function BitBucketCreateIntegrationPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [targetAppId, setTargetAppId] = useState("");
|
||||||
|
const [targetEnvironmentId, setTargetEnvironmentId] = useState("");
|
||||||
|
|
||||||
|
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState("");
|
||||||
|
const [secretPath, setSecretPath] = useState("/");
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const { integrationAuthId } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
|
const { data: integrationAuth } = useGetIntegrationAuthById((integrationAuthId as string) ?? "");
|
||||||
|
const { data: workspace } = useGetWorkspaceById(localStorage.getItem("projectData.id") ?? "");
|
||||||
|
const { data: targetEnvironments } = useGetIntegrationAuthBitBucketWorkspaces((integrationAuthId as string) ?? "");
|
||||||
|
const { data: integrationAuthApps } = useGetIntegrationAuthApps({
|
||||||
|
integrationAuthId: (integrationAuthId as string) ?? "",
|
||||||
|
workspaceSlug: targetEnvironmentId
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (workspace) {
|
||||||
|
setSelectedSourceEnvironment(workspace.environments[0].slug);
|
||||||
|
}
|
||||||
|
}, [workspace]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (integrationAuthApps) {
|
||||||
|
if (integrationAuthApps.length > 0) {
|
||||||
|
setTargetAppId(integrationAuthApps[0].appId as string);
|
||||||
|
} else {
|
||||||
|
setTargetAppId("none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [integrationAuthApps]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (targetEnvironments) {
|
||||||
|
if (targetEnvironments.length > 0) {
|
||||||
|
setTargetEnvironmentId(targetEnvironments[0].slug);
|
||||||
|
} else {
|
||||||
|
setTargetEnvironmentId("none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [targetEnvironments]);
|
||||||
|
|
||||||
|
const handleButtonClick = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
if (!integrationAuth?._id) return;
|
||||||
|
|
||||||
|
const targetApp = integrationAuthApps?.find(
|
||||||
|
(integrationAuthApp) => integrationAuthApp.appId === targetAppId
|
||||||
|
);
|
||||||
|
const targetEnvironment = targetEnvironments?.find(
|
||||||
|
(environment) => environment.slug === targetEnvironmentId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!targetApp || !targetApp.appId || !targetEnvironment) return;
|
||||||
|
|
||||||
|
await createIntegration({
|
||||||
|
integrationAuthId: integrationAuth?._id,
|
||||||
|
isActive: true,
|
||||||
|
app: targetApp.name,
|
||||||
|
appId: targetApp.appId,
|
||||||
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
|
targetEnvironment: targetEnvironment.name,
|
||||||
|
targetEnvironmentId: targetEnvironment.slug,
|
||||||
|
targetService: null,
|
||||||
|
targetServiceId: null,
|
||||||
|
owner: null,
|
||||||
|
path: null,
|
||||||
|
region: null,
|
||||||
|
secretPath
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
|
||||||
|
router.push(`/integrations/${localStorage.getItem("projectData.id")}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return integrationAuth &&
|
||||||
|
workspace &&
|
||||||
|
selectedSourceEnvironment &&
|
||||||
|
integrationAuthApps &&
|
||||||
|
targetEnvironments ? (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Card className="max-w-md rounded-md p-8">
|
||||||
|
<CardTitle className="text-center">BitBucket Integration</CardTitle>
|
||||||
|
<FormControl label="Project Environment" className="mt-4">
|
||||||
|
<Select
|
||||||
|
value={selectedSourceEnvironment}
|
||||||
|
onValueChange={(val) => setSelectedSourceEnvironment(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
>
|
||||||
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
|
<SelectItem
|
||||||
|
value={sourceEnvironment.slug}
|
||||||
|
key={`source-environment-${sourceEnvironment.slug}`}
|
||||||
|
>
|
||||||
|
{sourceEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="Secrets Path">
|
||||||
|
<Input
|
||||||
|
value={secretPath}
|
||||||
|
onChange={(evt) => setSecretPath(evt.target.value)}
|
||||||
|
placeholder="Provide a path, default is /"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="BitBucket Workspace">
|
||||||
|
<Select
|
||||||
|
value={targetEnvironmentId}
|
||||||
|
onValueChange={(val) => setTargetEnvironmentId(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
isDisabled={targetEnvironments.length === 0}
|
||||||
|
>
|
||||||
|
{targetEnvironments.length > 0 ? (
|
||||||
|
targetEnvironments.map((targetEnvironment) => (
|
||||||
|
<SelectItem
|
||||||
|
value={targetEnvironment.slug as string}
|
||||||
|
key={`target-environment-${targetEnvironment.slug as string}`}
|
||||||
|
>
|
||||||
|
{targetEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem value="none" key="target-environment-none">
|
||||||
|
No workspaces found
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl label="BitBucket Repo">
|
||||||
|
<Select
|
||||||
|
value={targetAppId}
|
||||||
|
onValueChange={(val) => setTargetAppId(val)}
|
||||||
|
className="w-full border border-mineshaft-500"
|
||||||
|
>
|
||||||
|
{integrationAuthApps.length > 0 ? (
|
||||||
|
integrationAuthApps.map((integrationAuthApp) => (
|
||||||
|
<SelectItem
|
||||||
|
value={integrationAuthApp.appId as string}
|
||||||
|
key={`target-app-${integrationAuthApp.appId as string}`}
|
||||||
|
>
|
||||||
|
{integrationAuthApp.name}
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<SelectItem value="none" key="target-app-none">
|
||||||
|
No repositories found
|
||||||
|
</SelectItem>
|
||||||
|
)}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={handleButtonClick}
|
||||||
|
color="mineshaft"
|
||||||
|
className="mt-4"
|
||||||
|
isLoading={isLoading}
|
||||||
|
isDisabled={integrationAuthApps.length === 0}
|
||||||
|
>
|
||||||
|
Create Integration
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBucketCreateIntegrationPage.requireAuth = true;
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import queryString from "query-string";
|
||||||
|
|
||||||
|
import AuthorizeIntegration from "../../../api/integrations/authorizeIntegration";
|
||||||
|
|
||||||
|
export default function BitBucketOAuth2CallbackPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { code, state } = queryString.parse(router.asPath.split("?")[1]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// validate state
|
||||||
|
if (state !== localStorage.getItem("latestCSRFToken")) return;
|
||||||
|
localStorage.removeItem("latestCSRFToken");
|
||||||
|
|
||||||
|
const integrationAuth = await AuthorizeIntegration({
|
||||||
|
workspaceId: localStorage.getItem("projectData.id") as string,
|
||||||
|
code: code as string,
|
||||||
|
integration: "bitbucket"
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(`/integrations/bitbucket/create?integrationAuthId=${integrationAuth._id}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
|
||||||
|
BitBucketOAuth2CallbackPage.requireAuth = true;
|
||||||
@@ -92,10 +92,12 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
|
|||||||
case "cloudflare-pages":
|
case "cloudflare-pages":
|
||||||
link = `${window.location.origin}/integrations/cloudflare-pages/authorize`;
|
link = `${window.location.origin}/integrations/cloudflare-pages/authorize`;
|
||||||
break;
|
break;
|
||||||
case "codefresh":
|
case "bitbucket":
|
||||||
|
link = `https://bitbucket.org/site/oauth2/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${window.location.origin}/integrations/bitbucket/oauth2/callback&state=${state}`;
|
||||||
|
break;
|
||||||
|
case "codefresh":
|
||||||
link = `${window.location.origin}/integrations/codefresh/authorize`;
|
link = `${window.location.origin}/integrations/codefresh/authorize`;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -105,7 +105,8 @@ export const IntegrationsSection = ({
|
|||||||
{(integration.integration === "vercel" ||
|
{(integration.integration === "vercel" ||
|
||||||
integration.integration === "netlify" ||
|
integration.integration === "netlify" ||
|
||||||
integration.integration === "railway" ||
|
integration.integration === "railway" ||
|
||||||
integration.integration === "gitlab") && (
|
integration.integration === "gitlab" ||
|
||||||
|
integration.integration === "bitbucket") && (
|
||||||
<div className="ml-4 flex flex-col">
|
<div className="ml-4 flex flex-col">
|
||||||
<FormLabel label="Target Environment" />
|
<FormLabel label="Target Environment" />
|
||||||
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
<div className="rounded-md border border-mineshaft-700 bg-mineshaft-900 px-3 py-2 font-inter text-sm text-bunker-200">
|
||||||
|
|||||||
Reference in New Issue
Block a user