From 1d652bbb2f5cb37ef9ffe6121bd2a0462a08ec95 Mon Sep 17 00:00:00 2001 From: Sachin Tewari Date: Thu, 22 Oct 2020 13:01:32 +0530 Subject: [PATCH] Read git auth token from backend config (#2992) --- .../techdocs-backend/src/default-branch.ts | 47 +++---- plugins/techdocs-backend/src/git-auth.ts | 116 ++++++++++++++++++ plugins/techdocs-backend/src/helpers.ts | 10 +- 3 files changed, 140 insertions(+), 33 deletions(-) create mode 100644 plugins/techdocs-backend/src/git-auth.ts diff --git a/plugins/techdocs-backend/src/default-branch.ts b/plugins/techdocs-backend/src/default-branch.ts index 85ef1ead5c..e9164c5175 100644 --- a/plugins/techdocs-backend/src/default-branch.ts +++ b/plugins/techdocs-backend/src/default-branch.ts @@ -17,6 +17,13 @@ import fetch, { RequestInit } from 'node-fetch'; import parseGitUrl from 'git-url-parse'; import { Config } from '@backstage/config'; import { getRootLogger, loadBackendConfig } from '@backstage/backend-common'; +import { + getAzureHostToken, + getGitHost, + getGithubHostToken, + getGitlabHostToken, + getGitRepoType, +} from './git-auth'; interface IGitlabBranch { name: string; @@ -73,15 +80,12 @@ function getAzureApiUrl(url: string): URL { ); } -function getGithubRequestOptions(config: Config): RequestInit { +function getGithubRequestOptions(config: Config, host: string): RequestInit { const headers: HeadersInit = { Accept: 'application/vnd.github.v3.raw', }; - const token = - config.getOptionalString('catalog.processors.github.privateToken') ?? - config.getOptionalString('catalog.processors.githubApi.privateToken') ?? - process.env.GITHUB_TOKEN; + const token = getGithubHostToken(config, host); if (token) { headers.Authorization = `token ${token}`; @@ -92,16 +96,12 @@ function getGithubRequestOptions(config: Config): RequestInit { }; } -function getGitlabRequestOptions(config: Config): RequestInit { +function getGitlabRequestOptions(config: Config, host: string): RequestInit { const headers: HeadersInit = { 'PRIVATE-TOKEN': '', }; - const token = - config.getOptionalString('catalog.processors.gitlab.privateToken') ?? - config.getOptionalString('catalog.processors.gitlabApi.privateToken') ?? - process.env.GITLAB_TOKEN; - + const token = getGitlabHostToken(config, host); if (token) { headers['PRIVATE-TOKEN'] = token; } @@ -111,12 +111,10 @@ function getGitlabRequestOptions(config: Config): RequestInit { }; } -function getAzureRequestOptions(config: Config): RequestInit { +function getAzureRequestOptions(config: Config, host: string): RequestInit { const headers: HeadersInit = {}; - const token = - config.getOptionalString('catalog.processors.azureApi.privateToken') ?? - process.env.AZURE_TOKEN; + const token = getAzureHostToken(config, host); if (token !== '') { headers.Authorization = `Basic ${Buffer.from(`:${token}`, 'utf8').toString( @@ -136,7 +134,8 @@ async function getGithubDefaultBranch( config: Config, ): Promise { const path = getGithubApiUrl(config, repositoryUrl).toString(); - const options = getGithubRequestOptions(config); + const host = getGitHost(repositoryUrl); + const options = getGithubRequestOptions(config, host); try { const raw = await fetch(path, options); @@ -165,7 +164,8 @@ async function getGitlabDefaultBranch( ): Promise { const path = getGitlabApiUrl(repositoryUrl).toString(); - const options = getGitlabRequestOptions(config); + const gitlabHost = getGitHost(repositoryUrl); + const options = getGitlabRequestOptions(config, gitlabHost); try { const raw = await fetch(path, options); @@ -196,8 +196,8 @@ async function getAzureDefaultBranch( config: Config, ): Promise { const path = getAzureApiUrl(repositoryUrl).toString(); - - const options = getAzureRequestOptions(config); + const host = getGitHost(repositoryUrl); + const options = getAzureRequestOptions(config, host); try { const urlResponse = await fetch(path, options); @@ -232,14 +232,7 @@ export const getDefaultBranch = async ( ): Promise => { // TODO(Rugvip): Config should not be loaded here, pass it in instead const config = await loadBackendConfig({ logger: getRootLogger() }); - const typeMapping = [ - { url: /github*/g, type: 'github' }, - { url: /gitlab*/g, type: 'gitlab' }, - { url: /azure*/g, type: 'azure/api' }, - ]; - - const type = typeMapping.filter(item => item.url.test(repositoryUrl))[0] - ?.type; + const type = getGitRepoType(repositoryUrl); try { switch (type) { diff --git a/plugins/techdocs-backend/src/git-auth.ts b/plugins/techdocs-backend/src/git-auth.ts new file mode 100644 index 0000000000..d77596929f --- /dev/null +++ b/plugins/techdocs-backend/src/git-auth.ts @@ -0,0 +1,116 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import parseGitUrl from 'git-url-parse'; +import { Config } from '@backstage/config'; +import { getRootLogger, loadBackendConfig } from '@backstage/backend-common'; + +export function getGitHost(url: string): string { + const { resource } = parseGitUrl(url); + return resource; +} + +export function getGitRepoType(url: string): string { + const typeMapping = [ + { url: /github*/g, type: 'github' }, + { url: /gitlab*/g, type: 'gitlab' }, + { url: /azure*/g, type: 'azure/api' }, + ]; + + const type = typeMapping.filter(item => item.url.test(url))[0]?.type; + + return type; +} + +export function getGithubHostToken( + config: Config, + host: string, +): string | undefined { + const providerConfigs = + config.getOptionalConfigArray('integrations.github') ?? []; + + const hostConfig = providerConfigs.filter( + providerConfig => providerConfig.getOptionalString('host') === host, + ); + const token = + hostConfig[0]?.getOptionalString('token') ?? + config.getOptionalString('catalog.processors.github.privateToken') ?? + config.getOptionalString('catalog.processors.githubApi.privateToken') ?? + process.env.GITHUB_TOKEN; + + return token; +} + +export function getGitlabHostToken( + config: Config, + host: string, +): string | undefined { + const providerConfigs = + config.getOptionalConfigArray('integrations.gitlab') ?? []; + + const hostConfig = providerConfigs.filter( + providerConfig => providerConfig.getOptionalString('host') === host, + ); + const token = + hostConfig[0]?.getOptionalString('token') ?? + config.getOptionalString('catalog.processors.gitlab.privateToken') ?? + config.getOptionalString('catalog.processors.gitlabApi.privateToken') ?? + process.env.GITLAB_TOKEN; + + return token; +} + +export function getAzureHostToken( + config: Config, + host: string, +): string | undefined { + const providerConfigs = + config.getOptionalConfigArray('integrations.azure') ?? []; + + const hostConfig = providerConfigs.filter( + providerConfig => providerConfig.getOptionalString('host') === host, + ); + const token = + hostConfig[0]?.getOptionalString('token') ?? + config.getOptionalString('catalog.processors.azureApi.privateToken') ?? + process.env.AZURE_TOKEN; + + return token; +} + +export const getTokenForGitRepo = async ( + repositoryUrl: string, +): Promise => { + const config = await loadBackendConfig({ logger: getRootLogger() }); + + const host = getGitHost(repositoryUrl); + const type = getGitRepoType(repositoryUrl); + + try { + switch (type) { + case 'github': + return getGithubHostToken(config, host); + case 'gitlab': + return getGitlabHostToken(config, host); + case 'azure/api': + return getAzureHostToken(config, host); + + default: + throw new Error('Failed to get repository type'); + } + } catch (error) { + throw error; + } +}; diff --git a/plugins/techdocs-backend/src/helpers.ts b/plugins/techdocs-backend/src/helpers.ts index b2dcd1e8e8..4a21800b07 100644 --- a/plugins/techdocs-backend/src/helpers.ts +++ b/plugins/techdocs-backend/src/helpers.ts @@ -20,6 +20,7 @@ import parseGitUrl from 'git-url-parse'; import NodeGit, { Clone, Repository } from 'nodegit'; import fs from 'fs-extra'; import { getDefaultBranch } from './default-branch'; +import { getTokenForGitRepo } from './git-auth'; import { Entity } from '@backstage/catalog-model'; import { InputError } from '@backstage/backend-common'; import { RemoteProtocol } from './techdocs/stages/prepare/types'; @@ -127,11 +128,8 @@ export const checkoutGitRepository = async ( process.env.GITLAB_PRIVATE_TOKEN_USER || process.env.AZURE_PRIVATE_TOKEN_USER || ''; - const token = - process.env.GITHUB_TOKEN || - process.env.GITLAB_PRIVATE_TOKEN_USER || - process.env.AZURE_TOKEN || - ''; + + const token = await getTokenForGitRepo(repoUrl); if (fs.existsSync(repositoryTmpPath)) { try { @@ -153,7 +151,7 @@ export const checkoutGitRepository = async ( } } - if (user && token) { + if (token) { parsedGitLocation.token = `${user}:${token}`; }