mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 19:28:09 +00:00
Fix errors and complete v1 full-loop of GitHub integration with repository secrets
This commit is contained in:
@@ -50,9 +50,11 @@ SMTP_PASSWORD=
|
|||||||
CLIENT_ID_HEROKU=
|
CLIENT_ID_HEROKU=
|
||||||
CLIENT_ID_VERCEL=
|
CLIENT_ID_VERCEL=
|
||||||
CLIENT_ID_NETLIFY=
|
CLIENT_ID_NETLIFY=
|
||||||
|
CLIENT_ID_GITHUB=
|
||||||
CLIENT_SECRET_HEROKU=
|
CLIENT_SECRET_HEROKU=
|
||||||
CLIENT_SECRET_VERCEL=
|
CLIENT_SECRET_VERCEL=
|
||||||
CLIENT_SECRET_NETLIFY=
|
CLIENT_SECRET_NETLIFY=
|
||||||
|
CLIENT_SECRET_GITHUB=
|
||||||
|
|
||||||
# Sentry (optional) for monitoring errors
|
# Sentry (optional) for monitoring errors
|
||||||
SENTRY_DSN=
|
SENTRY_DSN=
|
||||||
|
|||||||
Generated
-7
@@ -3921,7 +3921,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
"fsevents": "~2.3.2",
|
|
||||||
"glob-parent": "~5.1.2",
|
"glob-parent": "~5.1.2",
|
||||||
"is-binary-path": "~2.1.0",
|
"is-binary-path": "~2.1.0",
|
||||||
"is-glob": "~4.0.1",
|
"is-glob": "~4.0.1",
|
||||||
@@ -5133,7 +5132,6 @@
|
|||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"neo-async": "^2.6.0",
|
"neo-async": "^2.6.0",
|
||||||
"source-map": "^0.6.1",
|
"source-map": "^0.6.1",
|
||||||
"uglify-js": "^3.1.4",
|
|
||||||
"wordwrap": "^1.0.0"
|
"wordwrap": "^1.0.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -5791,7 +5789,6 @@
|
|||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"anymatch": "^3.0.3",
|
"anymatch": "^3.0.3",
|
||||||
"fb-watchman": "^2.0.0",
|
"fb-watchman": "^2.0.0",
|
||||||
"fsevents": "^2.3.2",
|
|
||||||
"graceful-fs": "^4.2.9",
|
"graceful-fs": "^4.2.9",
|
||||||
"jest-regex-util": "^29.2.0",
|
"jest-regex-util": "^29.2.0",
|
||||||
"jest-util": "^29.3.1",
|
"jest-util": "^29.3.1",
|
||||||
@@ -6595,11 +6592,9 @@
|
|||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.11.0.tgz",
|
||||||
"integrity": "sha512-9l9n4Nk2BYZzljW3vHah3Z0rfS5npKw6ktnkmFgTcnzaXH1DRm3pDl6VMHu84EVb1lzmSaJC4OzWZqTkB5i2wg==",
|
"integrity": "sha512-9l9n4Nk2BYZzljW3vHah3Z0rfS5npKw6ktnkmFgTcnzaXH1DRm3pDl6VMHu84EVb1lzmSaJC4OzWZqTkB5i2wg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/credential-providers": "^3.186.0",
|
|
||||||
"bson": "^4.7.0",
|
"bson": "^4.7.0",
|
||||||
"denque": "^2.1.0",
|
"denque": "^2.1.0",
|
||||||
"mongodb-connection-string-url": "^2.5.4",
|
"mongodb-connection-string-url": "^2.5.4",
|
||||||
"saslprep": "^1.0.3",
|
|
||||||
"socks": "^2.7.1"
|
"socks": "^2.7.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -7682,7 +7677,6 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@colors/colors": "1.5.0",
|
|
||||||
"string-width": "^4.2.0"
|
"string-width": "^4.2.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -8478,7 +8472,6 @@
|
|||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"encoding": "^0.1.13",
|
|
||||||
"minipass": "^3.1.6",
|
"minipass": "^3.1.6",
|
||||||
"minipass-sized": "^1.0.3",
|
"minipass-sized": "^1.0.3",
|
||||||
"minizlib": "^2.1.2"
|
"minizlib": "^2.1.2"
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
|
|||||||
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
|
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
|
||||||
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
|
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
|
||||||
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
|
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
|
||||||
const CLIENT_ID_GITHUB =
|
const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!;
|
||||||
process.env.CLIENT_ID_GITHUB! || 'e787fc24bcec43ecd5d5';
|
|
||||||
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
|
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
|
||||||
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
|
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
|
||||||
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB! || '407f32da788f63559abd662c6de08bb2911ca8ae';
|
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!;
|
||||||
const CLIENT_SLUG_VERCEL= process.env.CLIENT_SLUG_VERCEL!;
|
const CLIENT_SLUG_VERCEL= process.env.CLIENT_SLUG_VERCEL!;
|
||||||
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
|
||||||
const POSTHOG_PROJECT_API_KEY =
|
const POSTHOG_PROJECT_API_KEY =
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ import {
|
|||||||
INTEGRATION_GITHUB_API_URL
|
INTEGRATION_GITHUB_API_URL
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
|
|
||||||
|
interface GitHubApp {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return list of names of apps for integration named [integration]
|
* Return list of names of apps for integration named [integration]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@@ -186,13 +190,17 @@ const getAppsGithub = async ({
|
|||||||
auth: accessToken
|
auth: accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
const repos = await octokit.request(
|
const repos = (await octokit.request(
|
||||||
'GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}',
|
'GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}',
|
||||||
{}
|
{}
|
||||||
);
|
)).data;
|
||||||
apps = repos.map((a: any) => {
|
|
||||||
a.name;
|
apps = repos
|
||||||
});
|
.filter((a:any) => a.permissions.admin === true)
|
||||||
|
.map((a: any) => ({
|
||||||
|
name: a.name
|
||||||
|
})
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ import {
|
|||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
INTEGRATION_NETLIFY_TOKEN_URL,
|
INTEGRATION_NETLIFY_TOKEN_URL,
|
||||||
INTEGRATION_GITHUB_TOKEN_URL,
|
INTEGRATION_GITHUB_TOKEN_URL,
|
||||||
|
INTEGRATION_GITHUB_API_URL,
|
||||||
ACTION_PUSH_TO_HEROKU
|
ACTION_PUSH_TO_HEROKU
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SITE_URL,
|
SITE_URL,
|
||||||
CLIENT_ID_VERCEL,
|
CLIENT_ID_VERCEL,
|
||||||
CLIENT_ID_NETLIFY,
|
CLIENT_ID_NETLIFY,
|
||||||
CLIENT_ID_GITHUB,
|
CLIENT_ID_GITHUB,
|
||||||
|
CLIENT_SECRET_HEROKU,
|
||||||
CLIENT_SECRET_VERCEL,
|
CLIENT_SECRET_VERCEL,
|
||||||
CLIENT_SECRET_NETLIFY,
|
CLIENT_SECRET_NETLIFY,
|
||||||
CLIENT_SECRET_GITHUB
|
CLIENT_SECRET_GITHUB
|
||||||
@@ -117,43 +118,13 @@ const exchangeCode = async ({
|
|||||||
* @returns {String} obj2.refreshToken - refresh token for Heroku API
|
* @returns {String} obj2.refreshToken - refresh token for Heroku API
|
||||||
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
|
||||||
*/
|
*/
|
||||||
<<<<<<< HEAD
|
|
||||||
const exchangeCodeHeroku = async ({ code }: { code: string }) => {
|
|
||||||
let res: ExchangeCodeHerokuResponse;
|
|
||||||
const accessExpiresAt = new Date();
|
|
||||||
try {
|
|
||||||
res = (
|
|
||||||
await axios.post(
|
|
||||||
INTEGRATION_HEROKU_TOKEN_URL,
|
|
||||||
new URLSearchParams({
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
code: code,
|
|
||||||
client_secret: OAUTH_CLIENT_SECRET_HEROKU
|
|
||||||
} as any)
|
|
||||||
)
|
|
||||||
).data;
|
|
||||||
|
|
||||||
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in);
|
|
||||||
} catch (err) {
|
|
||||||
Sentry.setUser(null);
|
|
||||||
Sentry.captureException(err);
|
|
||||||
throw new Error('Failed OAuth2 code-token exchange with Heroku');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
accessToken: res.access_token,
|
|
||||||
refreshToken: res.refresh_token,
|
|
||||||
accessExpiresAt
|
|
||||||
};
|
|
||||||
};
|
|
||||||
=======
|
|
||||||
const exchangeCodeHeroku = async ({
|
const exchangeCodeHeroku = async ({
|
||||||
code
|
code
|
||||||
}: {
|
}: {
|
||||||
code: string;
|
code: string;
|
||||||
}) => {
|
}) => {
|
||||||
let res: ExchangeCodeHerokuResponse;
|
let res: ExchangeCodeHerokuResponse;
|
||||||
let accessExpiresAt = new Date();
|
const accessExpiresAt = new Date();
|
||||||
try {
|
try {
|
||||||
res = (await axios.post(
|
res = (await axios.post(
|
||||||
INTEGRATION_HEROKU_TOKEN_URL,
|
INTEGRATION_HEROKU_TOKEN_URL,
|
||||||
@@ -179,7 +150,6 @@ const exchangeCodeHeroku = async ({
|
|||||||
accessExpiresAt
|
accessExpiresAt
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
>>>>>>> 5444382d5ae1fabf1107434a856b58b9f09c67f6
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
|
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
|
||||||
@@ -286,7 +256,6 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
|||||||
*/
|
*/
|
||||||
const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
||||||
let res: ExchangeCodeGithubResponse;
|
let res: ExchangeCodeGithubResponse;
|
||||||
let res2;
|
|
||||||
try {
|
try {
|
||||||
res = (
|
res = (
|
||||||
await axios.get(INTEGRATION_GITHUB_TOKEN_URL, {
|
await axios.get(INTEGRATION_GITHUB_TOKEN_URL, {
|
||||||
@@ -295,29 +264,21 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
|||||||
client_secret: CLIENT_SECRET_GITHUB,
|
client_secret: CLIENT_SECRET_GITHUB,
|
||||||
code: code,
|
code: code,
|
||||||
redirect_uri: `${SITE_URL}/github`
|
redirect_uri: `${SITE_URL}/github`
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
res2 = (
|
|
||||||
await axios.get(INTEGRATION_GITHUB_TOKEN_URL, {
|
|
||||||
params: {
|
|
||||||
Authorization: `Bearer ${res.access_token}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
).data;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed OAuth2 code-token exchange with Github');
|
throw new Error('Failed OAuth2 code-token exchange with Github');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Check actual response and fix next line
|
|
||||||
const accountId = res2.user;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessToken: res.access_token,
|
accessToken: res.access_token,
|
||||||
user: accountId,
|
|
||||||
refreshToken: null,
|
refreshToken: null,
|
||||||
accessExpiresAt: null
|
accessExpiresAt: null
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,18 +52,6 @@ const exchangeRefreshHeroku = async ({
|
|||||||
}: {
|
}: {
|
||||||
refreshToken: string;
|
refreshToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
<<<<<<< HEAD
|
|
||||||
let accessToken;
|
|
||||||
try {
|
|
||||||
const res = await axios.post(
|
|
||||||
INTEGRATION_HEROKU_TOKEN_URL,
|
|
||||||
new URLSearchParams({
|
|
||||||
grant_type: 'refresh_token',
|
|
||||||
refresh_token: refreshToken,
|
|
||||||
client_secret: OAUTH_CLIENT_SECRET_HEROKU
|
|
||||||
} as any)
|
|
||||||
);
|
|
||||||
=======
|
|
||||||
let accessToken;
|
let accessToken;
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(
|
const res = await axios.post(
|
||||||
@@ -74,7 +62,6 @@ const exchangeRefreshHeroku = async ({
|
|||||||
client_secret: CLIENT_SECRET_HEROKU
|
client_secret: CLIENT_SECRET_HEROKU
|
||||||
} as any)
|
} as any)
|
||||||
);
|
);
|
||||||
>>>>>>> 5444382d5ae1fabf1107434a856b58b9f09c67f6
|
|
||||||
|
|
||||||
accessToken = res.data.access_token;
|
accessToken = res.data.access_token;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import { Octokit } from '@octokit/rest';
|
import { Octokit } from '@octokit/rest';
|
||||||
import * as sodium from 'libsodium-wrappers';
|
// import * as sodium from 'libsodium-wrappers';
|
||||||
|
import sodium from 'libsodium-wrappers';
|
||||||
|
// const sodium = require('libsodium-wrappers');
|
||||||
import { IIntegration, IIntegrationAuth } from '../models';
|
import { IIntegration, IIntegrationAuth } from '../models';
|
||||||
import {
|
import {
|
||||||
INTEGRATION_HEROKU,
|
INTEGRATION_HEROKU,
|
||||||
@@ -66,11 +68,11 @@ const syncSecrets = async ({
|
|||||||
case INTEGRATION_GITHUB:
|
case INTEGRATION_GITHUB:
|
||||||
await syncSecretsGitHub({
|
await syncSecretsGitHub({
|
||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
@@ -486,7 +488,7 @@ const syncSecretsNetlify = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync/push [secrets] to GitHub site [app]
|
* Sync/push [secrets] to GitHub [repo]
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
* @param {IIntegration} obj.integration - integration details
|
* @param {IIntegration} obj.integration - integration details
|
||||||
* @param {IIntegrationAuth} obj.integrationAuth - integration auth details
|
* @param {IIntegrationAuth} obj.integrationAuth - integration auth details
|
||||||
@@ -494,106 +496,110 @@ const syncSecretsNetlify = async ({
|
|||||||
*/
|
*/
|
||||||
const syncSecretsGitHub = async ({
|
const syncSecretsGitHub = async ({
|
||||||
integration,
|
integration,
|
||||||
integrationAuth,
|
|
||||||
secrets,
|
secrets,
|
||||||
accessToken
|
accessToken
|
||||||
}: {
|
}: {
|
||||||
integration: IIntegration;
|
integration: IIntegration;
|
||||||
integrationAuth: IIntegrationAuth;
|
|
||||||
secrets: any;
|
secrets: any;
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const deleteSecrets: Array<string> = [];
|
|
||||||
|
interface GitHubRepoKey {
|
||||||
|
key_id: string;
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GitHubSecret {
|
||||||
|
name: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GitHubSecretRes {
|
||||||
|
[index: string]: GitHubSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSecrets: GitHubSecret[] = [];
|
||||||
|
|
||||||
const octokit = new Octokit({
|
const octokit = new Octokit({
|
||||||
auth: accessToken
|
auth: accessToken
|
||||||
});
|
});
|
||||||
|
|
||||||
const loggedInUser = await octokit.request('GET /user', {});
|
const user = (await octokit.request('GET /user', {})).data;
|
||||||
// TODO: Check loggedInUser.login == repo owner
|
|
||||||
const repoPublicKey = await octokit.request(
|
const repoPublicKey: GitHubRepoKey = (await octokit.request(
|
||||||
'GET /repos/{owner}/{repo}/actions/secrets/public-key',
|
'GET /repos/{owner}/{repo}/actions/secrets/public-key',
|
||||||
{
|
{
|
||||||
owner: loggedInUser.login,
|
owner: user.login,
|
||||||
repo: integration.app
|
repo: integration.app
|
||||||
}
|
}
|
||||||
).key;
|
)).data;
|
||||||
|
|
||||||
const userRepos = await octokit.request('GET /user/repos', {});
|
// // Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
||||||
|
const encryptedSecrets: GitHubSecretRes = (await octokit.request(
|
||||||
// Get local copy of decrypted secrets. We cannot decrypt them as we dont have access to GH private key
|
|
||||||
const encryptedSecrets = await octokit.request(
|
|
||||||
'GET /repos/{owner}/{repo}/actions/secrets',
|
'GET /repos/{owner}/{repo}/actions/secrets',
|
||||||
{
|
{
|
||||||
owner: loggedInUser.name,
|
owner: user.login,
|
||||||
repo: integration.app
|
repo: integration.app
|
||||||
}
|
}
|
||||||
);
|
))
|
||||||
|
.data
|
||||||
Object.keys(secrets).map((key) => {
|
.secrets
|
||||||
if (!(key in encryptedSecrets)) {
|
.reduce((obj: any, secret: any) => ({
|
||||||
deleteSecrets.push(key);
|
...obj,
|
||||||
}
|
[secret.name]: secret
|
||||||
});
|
}), {});
|
||||||
|
|
||||||
if (!Object.values(userRepos).includes(integration.app)) {
|
Object.keys(encryptedSecrets).map(async (key) => {
|
||||||
if (deleteSecrets.length == 0) {
|
if (!(key in secrets)) {
|
||||||
throw new Error('Failed to sync secrets to Github');
|
await octokit.request(
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sync/push all secrets
|
|
||||||
for (const i in secrets) {
|
|
||||||
let encryptedSecret;
|
|
||||||
sodium.ready.then(() => {
|
|
||||||
// Convert Secret & Base64 key to Uint8Array.
|
|
||||||
const binkey = sodium.from_base64(
|
|
||||||
repoPublicKey,
|
|
||||||
sodium.base64_variants.ORIGINAL
|
|
||||||
);
|
|
||||||
const binsec = sodium.from_string(secrets[i]);
|
|
||||||
|
|
||||||
//Encrypt the secret using LibSodium
|
|
||||||
const encBytes = sodium.crypto_box_seal(binsec, binkey);
|
|
||||||
|
|
||||||
// Convert encrypted Uint8Array to Base64
|
|
||||||
encryptedSecret = sodium.to_base64(
|
|
||||||
encBytes,
|
|
||||||
sodium.base64_variants.ORIGINAL
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await octokit.request(
|
|
||||||
'PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
|
||||||
{
|
|
||||||
owner: loggedInUser.login,
|
|
||||||
repo: integration.app,
|
|
||||||
secret_name: Object.keys(secrets[i]),
|
|
||||||
encrypted_value: encryptedSecret,
|
|
||||||
key_id: '' //TODO: Not sure if we need this? https://docs.github.com/en/rest/actions/secrets?apiVersion=2022-11-28#create-or-update-a-repository-secret
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete secrets
|
|
||||||
if (deleteSecrets.length > 0) {
|
|
||||||
for (const i in deleteSecrets) {
|
|
||||||
const res = await octokit.request(
|
|
||||||
'DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
'DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
||||||
{
|
{
|
||||||
owner: loggedInUser.login,
|
owner: user.login,
|
||||||
repo: integration.app,
|
repo: integration.app,
|
||||||
secret_name: secrets[i]
|
secret_name: key
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
Object.keys(secrets).map((key) => {
|
||||||
|
// let encryptedSecret;
|
||||||
|
sodium.ready.then(async () => {
|
||||||
|
// convert secret & base64 key to Uint8Array.
|
||||||
|
const binkey = sodium.from_base64(
|
||||||
|
repoPublicKey.key,
|
||||||
|
sodium.base64_variants.ORIGINAL
|
||||||
|
);
|
||||||
|
const binsec = sodium.from_string(secrets[key]);
|
||||||
|
|
||||||
|
// encrypt secret using libsodium
|
||||||
|
const encBytes = sodium.crypto_box_seal(binsec, binkey);
|
||||||
|
|
||||||
|
// convert encrypted Uint8Array to base64
|
||||||
|
const encryptedSecret = sodium.to_base64(
|
||||||
|
encBytes,
|
||||||
|
sodium.base64_variants.ORIGINAL
|
||||||
|
);
|
||||||
|
|
||||||
|
await octokit.request(
|
||||||
|
'PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}',
|
||||||
|
{
|
||||||
|
owner: user.login,
|
||||||
|
repo: integration.app,
|
||||||
|
secret_name: key,
|
||||||
|
encrypted_value: encryptedSecret,
|
||||||
|
key_id: repoPublicKey.key_id
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
throw new Error('Failed to sync secrets to Github');
|
throw new Error('Failed to sync secrets to GitHub');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { syncSecrets };
|
export { syncSecrets };
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CLIENT_ID_HEROKU,
|
CLIENT_ID_HEROKU,
|
||||||
CLIENT_ID_NETLIFY,
|
CLIENT_ID_NETLIFY,
|
||||||
|
CLIENT_ID_GITHUB,
|
||||||
CLIENT_SLUG_VERCEL
|
CLIENT_SLUG_VERCEL
|
||||||
} from '../config';
|
} from '../config';
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ const INTEGRATION_GITHUB_TOKEN_URL =
|
|||||||
const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com';
|
const INTEGRATION_HEROKU_API_URL = 'https://api.heroku.com';
|
||||||
const INTEGRATION_VERCEL_API_URL = 'https://api.vercel.com';
|
const INTEGRATION_VERCEL_API_URL = 'https://api.vercel.com';
|
||||||
const INTEGRATION_NETLIFY_API_URL = 'https://api.netlify.com';
|
const INTEGRATION_NETLIFY_API_URL = 'https://api.netlify.com';
|
||||||
const INTEGRATION_GITHUB_API_URL = ' https://api.github.com/';
|
const INTEGRATION_GITHUB_API_URL = 'https://api.github.com';
|
||||||
|
|
||||||
const INTEGRATION_OPTIONS = [
|
const INTEGRATION_OPTIONS = [
|
||||||
{
|
{
|
||||||
@@ -62,6 +63,16 @@ const INTEGRATION_OPTIONS = [
|
|||||||
clientId: CLIENT_ID_NETLIFY,
|
clientId: CLIENT_ID_NETLIFY,
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'GitHub',
|
||||||
|
slug: 'github',
|
||||||
|
image: 'GitHub',
|
||||||
|
isAvailable: true,
|
||||||
|
type: 'oauth2',
|
||||||
|
clientId: CLIENT_ID_GITHUB,
|
||||||
|
docsLink: ''
|
||||||
|
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Google Cloud Platform',
|
name: 'Google Cloud Platform',
|
||||||
slug: 'gcp',
|
slug: 'gcp',
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ const Integration = ({
|
|||||||
context: integrationContext ? reverseContextNetlifyMapping[integrationContext] : null,
|
context: integrationContext ? reverseContextNetlifyMapping[integrationContext] : null,
|
||||||
siteId
|
siteId
|
||||||
});
|
});
|
||||||
|
|
||||||
router.reload();
|
router.reload();
|
||||||
}}
|
}}
|
||||||
color="mineshaft"
|
color="mineshaft"
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ export default function Integrations() {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const handleIntegrationOption = async ({ integrationOption }) => {
|
const handleIntegrationOption = async ({ integrationOption }) => {
|
||||||
|
|
||||||
|
console.log('handleIntegrationOption', integrationOption);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// generate CSRF token for OAuth2 code-token exchange integrations
|
// generate CSRF token for OAuth2 code-token exchange integrations
|
||||||
@@ -137,16 +139,11 @@ export default function Integrations() {
|
|||||||
window.location = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`;
|
window.location = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`;
|
||||||
break;
|
break;
|
||||||
case 'Netlify':
|
case 'Netlify':
|
||||||
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
|
|
||||||
break;
|
|
||||||
case 'Github':
|
|
||||||
window.location = `https://github.com.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
|
|
||||||
break;
|
|
||||||
case 'Fly.io':
|
|
||||||
console.log('fly.io');
|
|
||||||
setIntegrationAccessTokenDialogOpen(true);
|
|
||||||
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/netlify`;
|
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/netlify`;
|
||||||
break;
|
break;
|
||||||
|
case 'GitHub':
|
||||||
|
window.location = `https://github.com/login/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=repo&redirect_uri=${window.location.origin}/github&state=${state}`;
|
||||||
|
break;
|
||||||
// case 'Fly.io':
|
// case 'Fly.io':
|
||||||
// console.log('fly.io');
|
// console.log('fly.io');
|
||||||
// setIntegrationAccessTokenDialogOpen(true);
|
// setIntegrationAccessTokenDialogOpen(true);
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
Reference in New Issue
Block a user