mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Finish adding support for self-hosted GitLab integration
This commit is contained in:
@@ -10,11 +10,11 @@ import {
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
INTEGRATION_BITBUCKET_API_URL,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
INTEGRATION_NORTHFLANK_API_URL,
|
||||
INTEGRATION_RAILWAY_API_URL,
|
||||
INTEGRATION_SET,
|
||||
INTEGRATION_VERCEL_API_URL,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
getIntegrationOptions as getIntegrationOptionsFunc
|
||||
} from "../../variables";
|
||||
import { exchangeRefresh } from "../../integrations";
|
||||
@@ -51,7 +51,12 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
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");
|
||||
|
||||
const environments = req.membership.workspace?.environments || [];
|
||||
@@ -63,7 +68,8 @@ export const oAuthExchange = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
integration,
|
||||
code,
|
||||
environment: environments[0].slug
|
||||
environment: environments[0].slug,
|
||||
url
|
||||
});
|
||||
|
||||
await EEAuditLogService.createAuditLog(
|
||||
|
||||
@@ -5,11 +5,11 @@ import { BotService } from "../services";
|
||||
import {
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
INTEGRATION_NETLIFY,
|
||||
INTEGRATION_VERCEL,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
} from "../variables";
|
||||
import { BadRequestError, InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
||||
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
||||
import { IntegrationAuthMetadata } from "../models/integrationAuth/types";
|
||||
|
||||
interface Update {
|
||||
@@ -36,12 +36,14 @@ export const handleOAuthExchangeHelper = async ({
|
||||
workspaceId,
|
||||
integration,
|
||||
code,
|
||||
environment
|
||||
environment,
|
||||
url
|
||||
}: {
|
||||
workspaceId: string;
|
||||
integration: string;
|
||||
code: string;
|
||||
environment: string;
|
||||
url?: string;
|
||||
}) => {
|
||||
const bot = await Bot.findOne({
|
||||
workspace: workspaceId,
|
||||
@@ -53,7 +55,8 @@ export const handleOAuthExchangeHelper = async ({
|
||||
// exchange code for access and refresh tokens
|
||||
const res = await exchangeCode({
|
||||
integration,
|
||||
code
|
||||
code,
|
||||
url
|
||||
});
|
||||
|
||||
const update: Update = {
|
||||
@@ -67,6 +70,7 @@ export const handleOAuthExchangeHelper = async ({
|
||||
break;
|
||||
case INTEGRATION_NETLIFY:
|
||||
update.accountId = res.accountId;
|
||||
break;
|
||||
case INTEGRATION_GCP_SECRET_MANAGER:
|
||||
update.metadata = {
|
||||
authMethod: "oauth2"
|
||||
|
||||
@@ -118,9 +118,11 @@ interface ExchangeCodeBitBucketResponse {
|
||||
const exchangeCode = async ({
|
||||
integration,
|
||||
code,
|
||||
url
|
||||
}: {
|
||||
integration: string;
|
||||
code: string;
|
||||
url?: string;
|
||||
}) => {
|
||||
let obj = {} as any;
|
||||
|
||||
@@ -158,6 +160,7 @@ const exchangeCode = async ({
|
||||
case INTEGRATION_GITLAB:
|
||||
obj = await exchangeCodeGitlab({
|
||||
code,
|
||||
url
|
||||
});
|
||||
break;
|
||||
case INTEGRATION_BITBUCKET:
|
||||
@@ -388,11 +391,17 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
||||
* @returns {String} obj2.refreshToken - refresh token for Gitlab API
|
||||
* @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 res: ExchangeCodeGitlabResponse = (
|
||||
await standardRequest.post(
|
||||
INTEGRATION_GITLAB_TOKEN_URL,
|
||||
url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
code: code,
|
||||
|
||||
@@ -5,11 +5,11 @@ import {
|
||||
INTEGRATION_AZURE_KEY_VAULT,
|
||||
INTEGRATION_BITBUCKET,
|
||||
INTEGRATION_BITBUCKET_TOKEN_URL,
|
||||
INTEGRATION_GITLAB,
|
||||
INTEGRATION_HEROKU,
|
||||
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
INTEGRATION_GCP_TOKEN_URL,
|
||||
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE
|
||||
INTEGRATION_GITLAB,
|
||||
INTEGRATION_HEROKU
|
||||
} from "../variables";
|
||||
import {
|
||||
INTEGRATION_AZURE_TOKEN_URL,
|
||||
@@ -20,13 +20,13 @@ import { IntegrationService } from "../services";
|
||||
import {
|
||||
getClientIdAzure,
|
||||
getClientIdBitBucket,
|
||||
getClientIdGCPSecretManager,
|
||||
getClientIdGitLab,
|
||||
getClientSecretAzure,
|
||||
getClientSecretBitBucket,
|
||||
getClientSecretGCPSecretManager,
|
||||
getClientSecretGitLab,
|
||||
getClientSecretHeroku,
|
||||
getClientIdGCPSecretManager,
|
||||
getClientSecretGCPSecretManager,
|
||||
getSiteURL,
|
||||
} from "../config";
|
||||
|
||||
@@ -112,6 +112,7 @@ const exchangeRefresh = async ({
|
||||
break;
|
||||
case INTEGRATION_GITLAB:
|
||||
tokenDetails = await exchangeRefreshGitLab({
|
||||
integrationAuth,
|
||||
refreshToken,
|
||||
});
|
||||
break;
|
||||
@@ -226,17 +227,21 @@ const exchangeRefreshHeroku = async ({
|
||||
* @returns
|
||||
*/
|
||||
const exchangeRefreshGitLab = async ({
|
||||
integrationAuth,
|
||||
refreshToken,
|
||||
}: {
|
||||
integrationAuth: IIntegrationAuth;
|
||||
refreshToken: string;
|
||||
}) => {
|
||||
const accessExpiresAt = new Date();
|
||||
const url = integrationAuth.url;
|
||||
|
||||
const {
|
||||
data,
|
||||
}: {
|
||||
data: RefreshTokenGitLabResponse;
|
||||
} = await standardRequest.post(
|
||||
INTEGRATION_GITLAB_TOKEN_URL,
|
||||
url ? `${url}/oauth/token` : INTEGRATION_GITLAB_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: refreshToken,
|
||||
@@ -329,17 +334,17 @@ const exchangeRefreshGCPSecretManager = async ({
|
||||
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(
|
||||
INTEGRATION_GCP_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
assertion: token
|
||||
}).toString(),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import {
|
||||
CreateSecretCommand,
|
||||
GetSecretValueCommand,
|
||||
@@ -29,8 +28,6 @@ import {
|
||||
INTEGRATION_FLYIO_API_URL,
|
||||
INTEGRATION_GCP_SECRET_MANAGER,
|
||||
INTEGRATION_GCP_SECRET_MANAGER_URL,
|
||||
INTEGRATION_GCP_TOKEN_URL,
|
||||
INTEGRATION_GCP_CLOUD_PLATFORM_SCOPE,
|
||||
INTEGRATION_GITHUB,
|
||||
INTEGRATION_GITLAB,
|
||||
INTEGRATION_GITLAB_API_URL,
|
||||
|
||||
@@ -48,6 +48,7 @@ router.post(
|
||||
body("workspaceId").exists().trim().notEmpty(),
|
||||
body("code").exists().trim().notEmpty(),
|
||||
body("integration").exists().trim().notEmpty(),
|
||||
body("url").optional().isString().trim(),
|
||||
validateRequest,
|
||||
integrationAuthController.oAuthExchange
|
||||
);
|
||||
|
||||
@@ -32,17 +32,20 @@ class IntegrationService {
|
||||
integration,
|
||||
code,
|
||||
environment,
|
||||
url
|
||||
}: {
|
||||
workspaceId: string;
|
||||
integration: string;
|
||||
code: string;
|
||||
environment: string;
|
||||
url?: string;
|
||||
}) {
|
||||
return await handleOAuthExchangeHelper({
|
||||
workspaceId,
|
||||
integration,
|
||||
code,
|
||||
environment,
|
||||
url
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -367,16 +367,19 @@ export const useAuthorizeIntegration = () => {
|
||||
mutationFn: async ({
|
||||
workspaceId,
|
||||
code,
|
||||
integration
|
||||
integration,
|
||||
url
|
||||
}: {
|
||||
workspaceId: string;
|
||||
code: string;
|
||||
integration: string;
|
||||
url?: string;
|
||||
}) => {
|
||||
const { data: { integrationAuth } } = await apiRequest.post("/api/v1/integration-auth/oauth-token", {
|
||||
workspaceId,
|
||||
code,
|
||||
integration
|
||||
integration,
|
||||
url
|
||||
});
|
||||
|
||||
return integrationAuth;
|
||||
|
||||
@@ -14,18 +14,20 @@ export default function GitLabAuthorizeIntegrationPage() {
|
||||
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 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);
|
||||
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 (
|
||||
|
||||
@@ -95,7 +95,7 @@ export default function GitLabCreateIntegrationPage() {
|
||||
integrationAuthId: integrationAuth?._id,
|
||||
isActive: true,
|
||||
app: integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId)?.name,
|
||||
appId: targetAppId,
|
||||
appId: String(targetAppId),
|
||||
sourceEnvironment: selectedSourceEnvironment,
|
||||
targetEnvironment: targetEnvironment === "" ? "*" : targetEnvironment,
|
||||
targetEnvironmentId: null,
|
||||
|
||||
@@ -15,14 +15,18 @@ export default function GitLabOAuth2CallbackPage() {
|
||||
(async () => {
|
||||
try {
|
||||
// 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");
|
||||
|
||||
// TODO: self-hosted url somewhere here?
|
||||
const integrationAuth = await mutateAsync({
|
||||
workspaceId: localStorage.getItem("projectData.id") as string,
|
||||
code: code as string,
|
||||
integration: "gitlab"
|
||||
integration: "gitlab",
|
||||
...(url === "" ? {} : {
|
||||
url
|
||||
})
|
||||
});
|
||||
|
||||
router.push(`/integrations/gitlab/create?integrationAuthId=${integrationAuth._id}`);
|
||||
|
||||
Reference in New Issue
Block a user