Update github integration org/envs support

This commit is contained in:
Tuan Dang
2024-03-15 18:45:55 -07:00
parent f77942c702
commit 5f99e58674
5 changed files with 125 additions and 103 deletions
@@ -410,6 +410,7 @@ export const integrationAuthServiceFactory = ({
} }
}); });
if (!data) return []; if (!data) return [];
return data.map(({ login: name, id: orgId }) => ({ name, orgId: String(orgId) })); return data.map(({ login: name, id: orgId }) => ({ name, orgId: String(orgId) }));
}; };
@@ -1124,10 +1124,6 @@ const syncSecretsGitHub = async ({
selected_repositories_url?: string | undefined; selected_repositories_url?: string | undefined;
} }
interface GitHubSecretRes {
[index: string]: GitHubSecret;
}
const octokit = new Octokit({ const octokit = new Octokit({
auth: accessToken auth: accessToken
}); });
@@ -1152,7 +1148,7 @@ const syncSecretsGitHub = async ({
const { data } = await octokit.request( const { data } = await octokit.request(
"GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key", "GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key",
{ {
repository_id: Number(integration.targetServiceId), repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string environment_name: integration.targetEnvironmentId as string
} }
); );
@@ -1173,24 +1169,24 @@ const syncSecretsGitHub = async ({
let encryptedSecrets: GitHubSecret[]; let encryptedSecrets: GitHubSecret[];
switch (integration.scope) { switch (integration.scope) {
case GithubScope.Org: case GithubScope.Org: {
encryptedSecrets = ( encryptedSecrets = (
await octokit.request("GET /orgs/{org}/actions/secrets", { await octokit.request("GET /orgs/{org}/actions/secrets", {
org: integration.owner as string org: integration.owner as string
}) })
).data.secrets; ).data.secrets;
break; break;
}
case GithubScope.Env: case GithubScope.Env: {
encryptedSecrets = ( encryptedSecrets = (
await octokit.request("GET /repositories/{repository_id}/environments/{environment_name}/secrets", { await octokit.request("GET /repositories/{repository_id}/environments/{environment_name}/secrets", {
repository_id: Number(integration.targetServiceId), repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string environment_name: integration.targetEnvironmentId as string
}) })
).data.secrets; ).data.secrets;
break; break;
}
default: default: {
encryptedSecrets = ( encryptedSecrets = (
await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", { await octokit.request("GET /repos/{owner}/{repo}/actions/secrets", {
owner: integration.owner as string, owner: integration.owner as string,
@@ -1198,111 +1194,139 @@ const syncSecretsGitHub = async ({
}) })
).data.secrets; ).data.secrets;
break; break;
}
} }
let encryptedSecretsMap: GitHubSecretRes = encryptedSecrets.reduce( for await (const encryptedSecret of encryptedSecrets) {
(obj, secret) => ({ if (
...obj, !(encryptedSecret.name in secrets) &&
[secret.name]: secret !(appendices?.prefix !== undefined && !encryptedSecret.name.startsWith(appendices?.prefix)) &&
}), !(appendices?.suffix !== undefined && !encryptedSecret.name.endsWith(appendices?.suffix))
{} ) {
); switch (integration.scope) {
case GithubScope.Org: {
// filter out secrets based on prefix or suffix await octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", {
encryptedSecretsMap = Object.keys(encryptedSecretsMap).reduce( org: integration.owner as string,
( secret_name: encryptedSecret.name
result: { });
[key: string]: GitHubSecret; break;
}, }
key case GithubScope.Env: {
) => { await octokit.request(
if ( "DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
(appendices?.prefix !== undefined ? key.startsWith(appendices?.prefix) : true) && {
(appendices?.suffix !== undefined ? key.endsWith(appendices?.suffix) : true) repository_id: Number(integration.appId),
) { environment_name: integration.targetEnvironmentId as string,
result[key] = encryptedSecretsMap[key]; secret_name: encryptedSecret.name
}
);
break;
}
default: {
await octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
owner: integration.owner as string,
repo: integration.app as string,
secret_name: encryptedSecret.name
});
break;
}
} }
return result; }
}, }
{}
);
await Promise.all( await sodium.ready.then(async () => {
Object.keys(encryptedSecretsMap).map(async (key) => { for await (const key of Object.keys(secrets)) {
if (key in secrets) return; // convert secret & base64 key to Uint8Array.
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
const binsec = sodium.from_string(secrets[key].value);
// 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);
switch (integration.scope) { switch (integration.scope) {
case GithubScope.Org: case GithubScope.Org:
return octokit.request("DELETE /orgs/{org}/actions/secrets/{secret_name}", { await octokit.request("PUT /orgs/{org}/actions/secrets/{secret_name}", {
org: integration.owner as string, org: integration.owner as string,
secret_name: key secret_name: key,
visibility: "all",
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
}); });
break;
case GithubScope.Env: case GithubScope.Env:
return octokit.request( await octokit.request(
"DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}", "PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
{ {
repository_id: Number(integration.targetServiceId), repository_id: Number(integration.appId),
environment_name: integration.targetEnvironmentId as string, environment_name: integration.targetEnvironmentId as string,
secret_name: key secret_name: key,
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
} }
); );
break;
default: default:
return octokit.request("DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}", { await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
owner: integration.owner as string, owner: integration.owner as string,
repo: integration.app as string, repo: integration.app as string,
secret_name: key secret_name: key,
encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id
}); });
break;
} }
}) }
); });
await Promise.all( // for await (const key of Object.keys(secrets)) {
Object.keys(secrets).map((key) => { // sodium.ready.then(async () => {
// let encryptedSecret; // // convert secret & base64 key to Uint8Array.
return sodium.ready.then(async () => { // const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
// convert secret & base64 key to Uint8Array. // const binsec = sodium.from_string(secrets[key].value);
const binkey = sodium.from_base64(repoPublicKey.key, sodium.base64_variants.ORIGINAL);
const binsec = sodium.from_string(secrets[key].value);
// encrypt secret using libsodium // // encrypt secret using libsodium
const encBytes = sodium.crypto_box_seal(binsec, binkey); // const encBytes = sodium.crypto_box_seal(binsec, binkey);
// convert encrypted Uint8Array to base64 // // convert encrypted Uint8Array to base64
const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL); // const encryptedSecret = sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
switch (integration.scope) { // switch (integration.scope) {
case GithubScope.Org: // case GithubScope.Org:
return octokit.request("PUT /orgs/{org}/actions/secrets/{secret_name}", { // await octokit.request("PUT /orgs/{org}/actions/secrets/{secret_name}", {
org: integration.owner as string, // org: integration.owner as string,
secret_name: key, // secret_name: key,
visibility: "all", // visibility: "all",
encrypted_value: encryptedSecret, // encrypted_value: encryptedSecret,
key_id: repoPublicKey.key_id // key_id: repoPublicKey.key_id
}); // });
case GithubScope.Env: // break;
return octokit.request( // case GithubScope.Env:
"PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}", // await octokit.request(
{ // "PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}",
repository_id: Number(integration.targetServiceId), // {
environment_name: integration.targetEnvironmentId as string, // repository_id: Number(integration.appId),
secret_name: key, // environment_name: integration.targetEnvironmentId as string,
encrypted_value: encryptedSecret, // secret_name: key,
key_id: repoPublicKey.key_id // encrypted_value: encryptedSecret,
} // key_id: repoPublicKey.key_id
); // }
default: // );
return octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", { // break;
owner: integration.owner as string, // default:
repo: integration.app as string, // await octokit.request("PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}", {
secret_name: key, // owner: integration.owner as string,
encrypted_value: encryptedSecret, // repo: integration.app as string,
key_id: repoPublicKey.key_id // secret_name: key,
}); // encrypted_value: encryptedSecret,
} // key_id: repoPublicKey.key_id
}); // });
}) // break;
); // }
// });
// }
}; };
/** /**
@@ -331,8 +331,6 @@ const fetchIntegrationAuthHerokuPipelines = async ({ integrationAuthId }: {
`/api/v1/integration-auth/${integrationAuthId}/heroku/pipelines` `/api/v1/integration-auth/${integrationAuthId}/heroku/pipelines`
); );
console.log(99999, pipelines)
return pipelines; return pipelines;
}; };
@@ -133,7 +133,7 @@ export default function GitHubCreateIntegrationPage() {
repoName, repoName,
repoOwner repoOwner
); );
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { useEffect(() => {
@@ -205,9 +205,8 @@ export default function GitHubCreateIntegrationPage() {
sourceEnvironment: data.selectedSourceEnvironment, sourceEnvironment: data.selectedSourceEnvironment,
scope: data.scope, scope: data.scope,
app: repoName, app: repoName,
appId: data.repoId,
owner: repoOwner, owner: repoOwner,
targetService: "Repository",
targetServiceId: data.repoId, // github repo id is needed for sync secret
targetEnvironmentId: data.envId, targetEnvironmentId: data.envId,
metadata: { metadata: {
secretSuffix: data.secretSuffix secretSuffix: data.secretSuffix
@@ -238,7 +237,7 @@ export default function GitHubCreateIntegrationPage() {
}; };
return integrationAuth && workspace && integrationAuthApps ? ( return integrationAuth && workspace && integrationAuthApps ? (
<div className="flex w-full flex-col items-center justify-center py-4"> <div className="flex w-full h-full flex-col items-center justify-center py-4">
<Head> <Head>
<title>Set Up GitHub Integration</title> <title>Set Up GitHub Integration</title>
<link rel="icon" href="/infisical.ico" /> <link rel="icon" href="/infisical.ico" />
@@ -337,9 +336,9 @@ export default function GitHubCreateIntegrationPage() {
onValueChange={onChange} onValueChange={onChange}
className="w-full border border-mineshaft-500" className="w-full border border-mineshaft-500"
> >
<SelectItem value="github-repo">Github Repositories</SelectItem> <SelectItem value="github-org">Organization</SelectItem>
<SelectItem value="github-org">Github Organization</SelectItem> <SelectItem value="github-repo">Repository</SelectItem>
<SelectItem value="github-env">Github Environment</SelectItem> <SelectItem value="github-env">Repository Environment</SelectItem>
</Select> </Select>
</FormControl> </FormControl>
)} )}
@@ -60,7 +60,7 @@ export const redirectForProviderAuth = (integrationOption: TCloudIntegration) =>
link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`; link = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${window.location.origin}/integrations/netlify/oauth2/callback`;
break; break;
case "github": case "github":
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,admin:org&redirect_uri=${window.location.origin}/integrations/github/oauth2/callback&state=${state}`;
break; break;
case "gitlab": case "gitlab":
link = `${window.location.origin}/integrations/gitlab/authorize`; link = `${window.location.origin}/integrations/gitlab/authorize`;