mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 07:26:45 +00:00
Merge pull request #951 from Infisical/gitlab-integration-selfhosted
Extend GitLab integration to support syncing to self-hosted instances of GitLab
This commit is contained in:
@@ -10,11 +10,11 @@ import {
|
|||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
INTEGRATION_BITBUCKET_API_URL,
|
INTEGRATION_BITBUCKET_API_URL,
|
||||||
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_NORTHFLANK_API_URL,
|
INTEGRATION_NORTHFLANK_API_URL,
|
||||||
INTEGRATION_RAILWAY_API_URL,
|
INTEGRATION_RAILWAY_API_URL,
|
||||||
INTEGRATION_SET,
|
INTEGRATION_SET,
|
||||||
INTEGRATION_VERCEL_API_URL,
|
INTEGRATION_VERCEL_API_URL,
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
|
||||||
getIntegrationOptions as getIntegrationOptionsFunc
|
getIntegrationOptions as getIntegrationOptionsFunc
|
||||||
} from "../../variables";
|
} from "../../variables";
|
||||||
import { exchangeRefresh } from "../../integrations";
|
import { exchangeRefresh } from "../../integrations";
|
||||||
@@ -51,7 +51,12 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const oAuthExchange = async (req: Request, res: Response) => {
|
export const oAuthExchange = async (req: Request, res: Response) => {
|
||||||
const { workspaceId, code, integration } = req.body;
|
const {
|
||||||
|
workspaceId,
|
||||||
|
code,
|
||||||
|
integration,
|
||||||
|
url
|
||||||
|
} = req.body;
|
||||||
if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration");
|
if (!INTEGRATION_SET.has(integration)) throw new Error("Failed to validate integration");
|
||||||
|
|
||||||
const environments = req.membership.workspace?.environments || [];
|
const environments = req.membership.workspace?.environments || [];
|
||||||
@@ -63,7 +68,8 @@ export const oAuthExchange = async (req: Request, res: Response) => {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
environment: environments[0].slug
|
environment: environments[0].slug,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
|
|
||||||
await EEAuditLogService.createAuditLog(
|
await EEAuditLogService.createAuditLog(
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { BotService } from "../services";
|
|||||||
import {
|
import {
|
||||||
ALGORITHM_AES_256_GCM,
|
ALGORITHM_AES_256_GCM,
|
||||||
ENCODING_SCHEME_UTF8,
|
ENCODING_SCHEME_UTF8,
|
||||||
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_NETLIFY,
|
INTEGRATION_NETLIFY,
|
||||||
INTEGRATION_VERCEL,
|
INTEGRATION_VERCEL,
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import { BadRequestError, InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
||||||
import { IntegrationAuthMetadata } from "../models/integrationAuth/types";
|
import { IntegrationAuthMetadata } from "../models/integrationAuth/types";
|
||||||
|
|
||||||
interface Update {
|
interface Update {
|
||||||
@@ -36,12 +36,14 @@ export const handleOAuthExchangeHelper = async ({
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
environment
|
environment,
|
||||||
|
url
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
integration: string;
|
integration: string;
|
||||||
code: string;
|
code: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
url?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const bot = await Bot.findOne({
|
const bot = await Bot.findOne({
|
||||||
workspace: workspaceId,
|
workspace: workspaceId,
|
||||||
@@ -53,7 +55,8 @@ export const handleOAuthExchangeHelper = async ({
|
|||||||
// exchange code for access and refresh tokens
|
// exchange code for access and refresh tokens
|
||||||
const res = await exchangeCode({
|
const res = await exchangeCode({
|
||||||
integration,
|
integration,
|
||||||
code
|
code,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
|
|
||||||
const update: Update = {
|
const update: Update = {
|
||||||
@@ -67,6 +70,7 @@ export const handleOAuthExchangeHelper = async ({
|
|||||||
break;
|
break;
|
||||||
case INTEGRATION_NETLIFY:
|
case INTEGRATION_NETLIFY:
|
||||||
update.accountId = res.accountId;
|
update.accountId = res.accountId;
|
||||||
|
break;
|
||||||
case INTEGRATION_GCP_SECRET_MANAGER:
|
case INTEGRATION_GCP_SECRET_MANAGER:
|
||||||
update.metadata = {
|
update.metadata = {
|
||||||
authMethod: "oauth2"
|
authMethod: "oauth2"
|
||||||
|
|||||||
@@ -118,9 +118,11 @@ interface ExchangeCodeBitBucketResponse {
|
|||||||
const exchangeCode = async ({
|
const exchangeCode = async ({
|
||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
|
url
|
||||||
}: {
|
}: {
|
||||||
integration: string;
|
integration: string;
|
||||||
code: string;
|
code: string;
|
||||||
|
url?: string;
|
||||||
}) => {
|
}) => {
|
||||||
let obj = {} as any;
|
let obj = {} as any;
|
||||||
|
|
||||||
@@ -158,6 +160,7 @@ const exchangeCode = async ({
|
|||||||
case INTEGRATION_GITLAB:
|
case INTEGRATION_GITLAB:
|
||||||
obj = await exchangeCodeGitlab({
|
obj = await exchangeCodeGitlab({
|
||||||
code,
|
code,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case INTEGRATION_BITBUCKET:
|
case INTEGRATION_BITBUCKET:
|
||||||
@@ -388,11 +391,17 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
|||||||
* @returns {String} obj2.refreshToken - refresh token for Gitlab API
|
* @returns {String} obj2.refreshToken - refresh token for Gitlab API
|
||||||
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||||
*/
|
*/
|
||||||
const exchangeCodeGitlab = async ({ code }: { code: string }) => {
|
const exchangeCodeGitlab = async ({
|
||||||
|
code,
|
||||||
|
url
|
||||||
|
}: {
|
||||||
|
code: string,
|
||||||
|
url?: string;
|
||||||
|
}) => {
|
||||||
const accessExpiresAt = new Date();
|
const accessExpiresAt = new Date();
|
||||||
const res: ExchangeCodeGitlabResponse = (
|
const res: ExchangeCodeGitlabResponse = (
|
||||||
await standardRequest.post(
|
await standardRequest.post(
|
||||||
INTEGRATION_GITLAB_TOKEN_URL,
|
url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: "authorization_code",
|
grant_type: "authorization_code",
|
||||||
code: code,
|
code: code,
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import {
|
|||||||
INTEGRATION_AZURE_KEY_VAULT,
|
INTEGRATION_AZURE_KEY_VAULT,
|
||||||
INTEGRATION_BITBUCKET,
|
INTEGRATION_BITBUCKET,
|
||||||
INTEGRATION_BITBUCKET_TOKEN_URL,
|
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE,
|
||||||
INTEGRATION_HEROKU,
|
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_GCP_TOKEN_URL,
|
INTEGRATION_GCP_TOKEN_URL,
|
||||||
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE
|
INTEGRATION_GITLAB,
|
||||||
|
INTEGRATION_HEROKU
|
||||||
} from "../variables";
|
} from "../variables";
|
||||||
import {
|
import {
|
||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
@@ -20,13 +20,13 @@ import { IntegrationService } from "../services";
|
|||||||
import {
|
import {
|
||||||
getClientIdAzure,
|
getClientIdAzure,
|
||||||
getClientIdBitBucket,
|
getClientIdBitBucket,
|
||||||
|
getClientIdGCPSecretManager,
|
||||||
getClientIdGitLab,
|
getClientIdGitLab,
|
||||||
getClientSecretAzure,
|
getClientSecretAzure,
|
||||||
getClientSecretBitBucket,
|
getClientSecretBitBucket,
|
||||||
|
getClientSecretGCPSecretManager,
|
||||||
getClientSecretGitLab,
|
getClientSecretGitLab,
|
||||||
getClientSecretHeroku,
|
getClientSecretHeroku,
|
||||||
getClientIdGCPSecretManager,
|
|
||||||
getClientSecretGCPSecretManager,
|
|
||||||
getSiteURL,
|
getSiteURL,
|
||||||
} from "../config";
|
} from "../config";
|
||||||
|
|
||||||
@@ -112,6 +112,7 @@ const exchangeRefresh = async ({
|
|||||||
break;
|
break;
|
||||||
case INTEGRATION_GITLAB:
|
case INTEGRATION_GITLAB:
|
||||||
tokenDetails = await exchangeRefreshGitLab({
|
tokenDetails = await exchangeRefreshGitLab({
|
||||||
|
integrationAuth,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -226,17 +227,21 @@ const exchangeRefreshHeroku = async ({
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const exchangeRefreshGitLab = async ({
|
const exchangeRefreshGitLab = async ({
|
||||||
|
integrationAuth,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
}: {
|
}: {
|
||||||
|
integrationAuth: IIntegrationAuth;
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
const accessExpiresAt = new Date();
|
const accessExpiresAt = new Date();
|
||||||
|
const url = integrationAuth.url;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
}: {
|
}: {
|
||||||
data: RefreshTokenGitLabResponse;
|
data: RefreshTokenGitLabResponse;
|
||||||
} = await standardRequest.post(
|
} = await standardRequest.post(
|
||||||
INTEGRATION_GITLAB_TOKEN_URL,
|
url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: "refresh_token",
|
grant_type: "refresh_token",
|
||||||
refresh_token: refreshToken,
|
refresh_token: refreshToken,
|
||||||
@@ -329,17 +334,17 @@ const exchangeRefreshGCPSecretManager = async ({
|
|||||||
exp: Math.floor(Date.now() / 1000) + 3600,
|
exp: Math.floor(Date.now() / 1000) + 3600,
|
||||||
};
|
};
|
||||||
|
|
||||||
const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: 'RS256' });
|
const token = jwt.sign(payload, serviceAccount.private_key, { algorithm: "RS256" });
|
||||||
|
|
||||||
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await standardRequest.post(
|
const { data }: { data: ServiceAccountAccessTokenGCPSecretManagerResponse } = await standardRequest.post(
|
||||||
INTEGRATION_GCP_TOKEN_URL,
|
INTEGRATION_GCP_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||||
assertion: token
|
assertion: token
|
||||||
}).toString(),
|
}).toString(),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded'
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import jwt from "jsonwebtoken";
|
|
||||||
import {
|
import {
|
||||||
CreateSecretCommand,
|
CreateSecretCommand,
|
||||||
GetSecretValueCommand,
|
GetSecretValueCommand,
|
||||||
@@ -29,8 +28,6 @@ import {
|
|||||||
INTEGRATION_FLYIO_API_URL,
|
INTEGRATION_FLYIO_API_URL,
|
||||||
INTEGRATION_GCP_SECRET_MANAGER,
|
INTEGRATION_GCP_SECRET_MANAGER,
|
||||||
INTEGRATION_GCP_SECRET_MANAGER_URL,
|
INTEGRATION_GCP_SECRET_MANAGER_URL,
|
||||||
INTEGRATION_GCP_TOKEN_URL,
|
|
||||||
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE,
|
|
||||||
INTEGRATION_GITHUB,
|
INTEGRATION_GITHUB,
|
||||||
INTEGRATION_GITLAB,
|
INTEGRATION_GITLAB,
|
||||||
INTEGRATION_GITLAB_API_URL,
|
INTEGRATION_GITLAB_API_URL,
|
||||||
@@ -159,6 +156,7 @@ const syncSecrets = async ({
|
|||||||
break;
|
break;
|
||||||
case INTEGRATION_GITLAB:
|
case INTEGRATION_GITLAB:
|
||||||
await syncSecretsGitLab({
|
await syncSecretsGitLab({
|
||||||
|
integrationAuth,
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
@@ -1816,10 +1814,12 @@ const syncSecretsTravisCI = async ({
|
|||||||
* @param {String} obj.accessToken - access token for GitLab integration
|
* @param {String} obj.accessToken - access token for GitLab integration
|
||||||
*/
|
*/
|
||||||
const syncSecretsGitLab = async ({
|
const syncSecretsGitLab = async ({
|
||||||
|
integrationAuth,
|
||||||
integration,
|
integration,
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
}: {
|
}: {
|
||||||
|
integrationAuth: IIntegrationAuth;
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
secrets: Record<string, { value: string; comment?: string }>;
|
secrets: Record<string, { value: string; comment?: string }>;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
@@ -1829,9 +1829,10 @@ const syncSecretsGitLab = async ({
|
|||||||
value: string;
|
value: string;
|
||||||
environment_scope: string;
|
environment_scope: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gitLabApiUrl = integrationAuth.url ? `${integrationAuth.url}/api` : INTEGRATION_GITLAB_API_URL;
|
||||||
|
|
||||||
const getAllEnvVariables = async (integrationAppId: string, accessToken: string) => {
|
const getAllEnvVariables = async (integrationAppId: string, accessToken: string) => {
|
||||||
const gitLabApiUrl = `${INTEGRATION_GITLAB_API_URL}/v4/projects/${integrationAppId}/variables`;
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: `Bearer ${accessToken}`,
|
Authorization: `Bearer ${accessToken}`,
|
||||||
"Accept-Encoding": "application/json",
|
"Accept-Encoding": "application/json",
|
||||||
@@ -1839,7 +1840,7 @@ const syncSecretsGitLab = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
let allEnvVariables: GitLabSecret[] = [];
|
let allEnvVariables: GitLabSecret[] = [];
|
||||||
let url: string | null = `${gitLabApiUrl}?per_page=100`;
|
let url: string | null = `${gitLabApiUrl}/v4/projects/${integrationAppId}/variables?per_page=100`;
|
||||||
|
|
||||||
while (url) {
|
while (url) {
|
||||||
const response: any = await standardRequest.get(url, { headers });
|
const response: any = await standardRequest.get(url, { headers });
|
||||||
@@ -1867,7 +1868,7 @@ const syncSecretsGitLab = async ({
|
|||||||
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
|
const existingSecret = getSecretsRes.find((s: any) => s.key == key);
|
||||||
if (!existingSecret) {
|
if (!existingSecret) {
|
||||||
await standardRequest.post(
|
await standardRequest.post(
|
||||||
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables`,
|
`${gitLabApiUrl}/v4/projects/${integration?.appId}/variables`,
|
||||||
{
|
{
|
||||||
key: key,
|
key: key,
|
||||||
value: secrets[key].value,
|
value: secrets[key].value,
|
||||||
@@ -1888,7 +1889,7 @@ const syncSecretsGitLab = async ({
|
|||||||
// update secret
|
// update secret
|
||||||
if (secrets[key].value !== existingSecret.value) {
|
if (secrets[key].value !== existingSecret.value) {
|
||||||
await standardRequest.put(
|
await standardRequest.put(
|
||||||
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}?filter[environment_scope]=${integration.targetEnvironment}`,
|
`${gitLabApiUrl}/v4/projects/${integration?.appId}/variables/${existingSecret.key}?filter[environment_scope]=${integration.targetEnvironment}`,
|
||||||
{
|
{
|
||||||
...existingSecret,
|
...existingSecret,
|
||||||
value: secrets[existingSecret.key].value
|
value: secrets[existingSecret.key].value
|
||||||
@@ -1909,7 +1910,7 @@ const syncSecretsGitLab = async ({
|
|||||||
for await (const sec of getSecretsRes) {
|
for await (const sec of getSecretsRes) {
|
||||||
if (!(sec.key in secrets)) {
|
if (!(sec.key in secrets)) {
|
||||||
await standardRequest.delete(
|
await standardRequest.delete(
|
||||||
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}?filter[environment_scope]=${integration.targetEnvironment}`,
|
`${gitLabApiUrl}/v4/projects/${integration?.appId}/variables/${sec.key}?filter[environment_scope]=${integration.targetEnvironment}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ router.post(
|
|||||||
body("workspaceId").exists().trim().notEmpty(),
|
body("workspaceId").exists().trim().notEmpty(),
|
||||||
body("code").exists().trim().notEmpty(),
|
body("code").exists().trim().notEmpty(),
|
||||||
body("integration").exists().trim().notEmpty(),
|
body("integration").exists().trim().notEmpty(),
|
||||||
|
body("url").optional().isString().trim(),
|
||||||
validateRequest,
|
validateRequest,
|
||||||
integrationAuthController.oAuthExchange
|
integrationAuthController.oAuthExchange
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,17 +32,20 @@ class IntegrationService {
|
|||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
environment,
|
environment,
|
||||||
|
url
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
integration: string;
|
integration: string;
|
||||||
code: string;
|
code: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
url?: string;
|
||||||
}) {
|
}) {
|
||||||
return await handleOAuthExchangeHelper({
|
return await handleOAuthExchangeHelper({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
integration,
|
integration,
|
||||||
code,
|
code,
|
||||||
environment,
|
environment,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -367,16 +367,19 @@ export const useAuthorizeIntegration = () => {
|
|||||||
mutationFn: async ({
|
mutationFn: async ({
|
||||||
workspaceId,
|
workspaceId,
|
||||||
code,
|
code,
|
||||||
integration
|
integration,
|
||||||
|
url
|
||||||
}: {
|
}: {
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
code: string;
|
code: string;
|
||||||
integration: string;
|
integration: string;
|
||||||
|
url?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { data: { integrationAuth } } = await apiRequest.post("/api/v1/integration-auth/oauth-token", {
|
const { data: { integrationAuth } } = await apiRequest.post("/api/v1/integration-auth/oauth-token", {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
code,
|
code,
|
||||||
integration
|
integration,
|
||||||
|
url
|
||||||
});
|
});
|
||||||
|
|
||||||
return integrationAuth;
|
return integrationAuth;
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import crypto from "crypto";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { faGoogle } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
import { useGetCloudIntegrations } from "@app/hooks/api";
|
||||||
|
|
||||||
|
import { Button, Card, CardTitle, FormControl, Input } from "../../../components/v2";
|
||||||
|
|
||||||
|
export default function GitLabAuthorizeIntegrationPage() {
|
||||||
|
const { data: cloudIntegrations } = useGetCloudIntegrations();
|
||||||
|
|
||||||
|
const [gitLabURL, setGitLabURL] = useState("");
|
||||||
|
|
||||||
|
const handleIntegrateWithOAuth = () => {
|
||||||
|
if (!cloudIntegrations) return;
|
||||||
|
const integrationOption = cloudIntegrations.find((integration) => integration.slug === "gitlab");
|
||||||
|
|
||||||
|
if (!integrationOption) return;
|
||||||
|
|
||||||
|
const baseURL = gitLabURL.trim() === "" ? "https://gitlab.com" : gitLabURL.trim();
|
||||||
|
|
||||||
|
const csrfToken = crypto.randomBytes(16).toString("hex");
|
||||||
|
localStorage.setItem("latestCSRFToken", csrfToken);
|
||||||
|
|
||||||
|
const state = `${csrfToken}|${gitLabURL.trim() === "" ? "" : gitLabURL.trim()}`;
|
||||||
|
const link = `${baseURL}/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
|
||||||
|
|
||||||
|
window.location.assign(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Card className="max-w-md rounded-md p-8">
|
||||||
|
<CardTitle className="text-center">GitLab Integration</CardTitle>
|
||||||
|
<FormControl label="Self-hosted URL (optional)">
|
||||||
|
<Input
|
||||||
|
placeholder="https://self-hosted-gitlab.com"
|
||||||
|
value={gitLabURL} onChange={(e) => setGitLabURL(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<Button
|
||||||
|
onClick={handleIntegrateWithOAuth}
|
||||||
|
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
|
||||||
|
className="h-11 w-full mx-0 mt-4"
|
||||||
|
>
|
||||||
|
Continue with OAuth
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GitLabAuthorizeIntegrationPage.requireAuth = true;
|
||||||
@@ -95,7 +95,7 @@ export default function GitLabCreateIntegrationPage() {
|
|||||||
integrationAuthId: integrationAuth?._id,
|
integrationAuthId: integrationAuth?._id,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
||||||
appId: targetAppId,
|
appId: String(targetAppId),
|
||||||
sourceEnvironment: selectedSourceEnvironment,
|
sourceEnvironment: selectedSourceEnvironment,
|
||||||
targetEnvironment: targetEnvironment === "" ? "*" : targetEnvironment,
|
targetEnvironment: targetEnvironment === "" ? "*" : targetEnvironment,
|
||||||
targetEnvironmentId: null,
|
targetEnvironmentId: null,
|
||||||
|
|||||||
@@ -15,13 +15,18 @@ export default function GitLabOAuth2CallbackPage() {
|
|||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
// validate state
|
// validate state
|
||||||
if (state !== localStorage.getItem("latestCSRFToken")) return;
|
const [csrfToken, url] = (state as string).split("|", 2);
|
||||||
|
|
||||||
|
if (csrfToken !== localStorage.getItem("latestCSRFToken")) return;
|
||||||
localStorage.removeItem("latestCSRFToken");
|
localStorage.removeItem("latestCSRFToken");
|
||||||
|
|
||||||
const integrationAuth = await mutateAsync({
|
const integrationAuth = await mutateAsync({
|
||||||
workspaceId: localStorage.getItem("projectData.id") as string,
|
workspaceId: localStorage.getItem("projectData.id") as string,
|
||||||
code: code as string,
|
code: code as string,
|
||||||
integration: "gitlab"
|
integration: "gitlab",
|
||||||
|
...(url === "" ? {} : {
|
||||||
|
url
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
router.push(`/integrations/gitlab/create?integrationAuthId=${integrationAuth._id}`);
|
router.push(`/integrations/gitlab/create?integrationAuthId=${integrationAuth._id}`);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
|
|||||||
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
link = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
|
||||||
break;
|
break;
|
||||||
case "gitlab":
|
case "gitlab":
|
||||||
link = `https://gitlab.com/oauth/authorize?client_id=${integrationOption.clientId}&redirect_uri=${window.location.origin}/integrations/gitlab/oauth2/callback&response_type=code&state=${state}`;
|
link = `${window.location.origin}/integrations/gitlab/authorize`;
|
||||||
break;
|
break;
|
||||||
case "render":
|
case "render":
|
||||||
link = `${window.location.origin}/integrations/render/authorize`;
|
link = `${window.location.origin}/integrations/render/authorize`;
|
||||||
|
|||||||
Reference in New Issue
Block a user