Skip to content

Commit

Permalink
feat(git): move git autentification via environment variables to git …
Browse files Browse the repository at this point in the history
…auth (#22821)

Co-authored-by: Michael Kriese <[email protected]>
  • Loading branch information
Shegox and viceice authored Jun 20, 2023
1 parent dc43ea5 commit 6c6984b
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 93 deletions.
94 changes: 2 additions & 92 deletions lib/modules/manager/gomod/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import semver from 'semver';
import { quote } from 'shlex';
import upath from 'upath';
import { GlobalConfig } from '../../../config/global';
import type { PlatformId } from '../../../constants';
import { TEMPORARY_ERROR } from '../../../constants/error-messages';
import { logger } from '../../../logger';
import type { HostRule } from '../../../types';
import { exec } from '../../../util/exec';
import type { ExecOptions } from '../../../util/exec/types';
import {
Expand All @@ -16,10 +14,8 @@ import {
writeLocalFile,
} from '../../../util/fs';
import { getRepoStatus } from '../../../util/git';
import { getGitAuthenticatedEnvironmentVariables } from '../../../util/git/auth';
import { find, getAll } from '../../../util/host-rules';
import { getGitEnvironmentVariables } from '../../../util/git/auth';
import { regEx } from '../../../util/regex';
import { createURLFromHostOrURL, validateUrl } from '../../../util/url';
import { isValid } from '../../versioning/semver';
import type {
PackageDependency,
Expand All @@ -28,94 +24,8 @@ import type {
UpdateArtifactsResult,
} from '../types';

const githubApiUrls = new Set([
'github.com',
'api.github.com',
'https://api.github.com',
'https://api.github.com/',
]);

const { major, valid } = semver;

function getGitEnvironmentVariables(): NodeJS.ProcessEnv {
let environmentVariables: NodeJS.ProcessEnv = {};

// hard-coded logic to use authentication for github.com based on the githubToken for api.github.com
const githubToken = find({
hostType: 'github',
url: 'https://api.github.com/',
});

if (githubToken?.token) {
environmentVariables = getGitAuthenticatedEnvironmentVariables(
'https://github.com/',
githubToken
);
}

// get extra host rules for other git-based Go Module hosts
// filter rules without `matchHost` and `token` and github api github rules
const hostRules = getAll()
.filter((r) => r.matchHost && r.token)
.filter((r) => !githubToken || !githubApiUrls.has(r.matchHost!));

const goGitAllowedHostType = new Set<string>([
// All known git platforms
'azure',
'bitbucket',
'bitbucket-server',
'gitea',
'github',
'gitlab',
] satisfies PlatformId[]);

// for each hostRule without hostType we add additional authentication variables to the environmentVariables
for (const hostRule of hostRules) {
if (hostRule.hostType === 'go' || !hostRule.hostType) {
environmentVariables = addAuthFromHostRule(
hostRule,
environmentVariables
);
}
}

// for each hostRule with hostType we add additional authentication variables to the environmentVariables
for (const hostRule of hostRules) {
if (hostRule.hostType && goGitAllowedHostType.has(hostRule.hostType)) {
environmentVariables = addAuthFromHostRule(
hostRule,
environmentVariables
);
}
}
return environmentVariables;
}

function addAuthFromHostRule(
hostRule: HostRule,
env: NodeJS.ProcessEnv
): NodeJS.ProcessEnv {
let environmentVariables = env;
const httpUrl = createURLFromHostOrURL(hostRule.matchHost!)?.toString();
if (validateUrl(httpUrl)) {
logger.debug(
// TODO: types (#7154)
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Adding Git authentication for Go Module retrieval for ${httpUrl} using token auth.`
);
environmentVariables = getGitAuthenticatedEnvironmentVariables(
httpUrl!,
hostRule,
environmentVariables
);
} else {
logger.warn(
`Could not parse registryUrl ${hostRule.matchHost!} or not using http(s). Ignoring`
);
}
return environmentVariables;
}

function getUpdateImportPathCmds(
updatedDeps: PackageDependency[],
{ constraints }: UpdateArtifactsConfig
Expand Down Expand Up @@ -285,7 +195,7 @@ export async function updateArtifacts({
GOINSECURE: process.env.GOINSECURE,
GOFLAGS: useModcacherw(goConstraints) ? '-modcacherw' : null,
CGO_ENABLED: GlobalConfig.get('binarySource') === 'docker' ? '0' : null,
...getGitEnvironmentVariables(),
...getGitEnvironmentVariables(['go']),
},
docker: {},
toolConstraints: [
Expand Down
134 changes: 133 additions & 1 deletion lib/util/git/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { getGitAuthenticatedEnvironmentVariables } from './auth';
import { add, clear } from '../host-rules';
import {
getGitAuthenticatedEnvironmentVariables,
getGitEnvironmentVariables,
} from './auth';

describe('util/git/auth', () => {
afterEach(() => {
Expand Down Expand Up @@ -307,4 +311,132 @@ describe('util/git/auth', () => {
});
});
});

describe('getGitEnvironmentVariables()', () => {
beforeEach(() => {
clear();
});

it('returns empty object if no environment variables exist', () => {
expect(getGitEnvironmentVariables()).toStrictEqual({});
});

it('returns environment variables with token if hostRule for api.github.com exists', () => {
add({
hostType: 'github',
matchHost: 'api.github.com',
token: 'token123',
});
expect(getGitEnvironmentVariables()).toStrictEqual({
GIT_CONFIG_COUNT: '3',
GIT_CONFIG_KEY_0: 'url.https://ssh:[email protected]/.insteadOf',
GIT_CONFIG_KEY_1: 'url.https://git:[email protected]/.insteadOf',
GIT_CONFIG_KEY_2: 'url.https://[email protected]/.insteadOf',
GIT_CONFIG_VALUE_0: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_1: '[email protected]:',
GIT_CONFIG_VALUE_2: 'https://github.com/',
});
});

it('returns environment variables with token if hostRule for multiple hostsRules', () => {
add({
hostType: 'github',
matchHost: 'api.github.com',
token: 'token123',
});
add({
hostType: 'gitlab',
matchHost: 'https://gitlab.example.com',
token: 'token234',
});
add({
hostType: 'github',
matchHost: 'https://github.example.com',
token: 'token345',
});
expect(getGitEnvironmentVariables()).toStrictEqual({
GIT_CONFIG_COUNT: '9',
GIT_CONFIG_KEY_0: 'url.https://ssh:[email protected]/.insteadOf',
GIT_CONFIG_KEY_1: 'url.https://git:[email protected]/.insteadOf',
GIT_CONFIG_KEY_2: 'url.https://[email protected]/.insteadOf',
GIT_CONFIG_KEY_3:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_KEY_4:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_KEY_5:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_KEY_6:
'url.https://ssh:[email protected]/.insteadOf',
GIT_CONFIG_KEY_7:
'url.https://git:[email protected]/.insteadOf',
GIT_CONFIG_KEY_8: 'url.https://[email protected]/.insteadOf',
GIT_CONFIG_VALUE_0: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_1: '[email protected]:',
GIT_CONFIG_VALUE_2: 'https://github.com/',
GIT_CONFIG_VALUE_3: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_4: '[email protected]:',
GIT_CONFIG_VALUE_5: 'https://gitlab.example.com/',
GIT_CONFIG_VALUE_6: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_7: '[email protected]:',
GIT_CONFIG_VALUE_8: 'https://github.example.com/',
});
});

it('returns environment variables with token if hostRule is for Gitlab', () => {
add({
hostType: 'gitlab',
matchHost: 'https://gitlab.example.com',
token: 'token123',
});
expect(getGitEnvironmentVariables()).toStrictEqual({
GIT_CONFIG_COUNT: '3',
GIT_CONFIG_KEY_0:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_KEY_1:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_KEY_2:
'url.https://gitlab-ci-token:[email protected]/.insteadOf',
GIT_CONFIG_VALUE_0: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_1: '[email protected]:',
GIT_CONFIG_VALUE_2: 'https://gitlab.example.com/',
});
});

it('returns no environment variables when hostType is not supported', () => {
add({
hostType: 'custom',
matchHost: 'https://custom.example.com',
token: 'token123',
});
expect(getGitEnvironmentVariables()).toStrictEqual({});
});

it('returns environment variables when hostType is explicitly set', () => {
add({
hostType: 'custom',
matchHost: 'https://custom.example.com',
token: 'token123',
});
expect(getGitEnvironmentVariables(['custom'])).toStrictEqual({
GIT_CONFIG_COUNT: '3',
GIT_CONFIG_KEY_0:
'url.https://ssh:[email protected]/.insteadOf',
GIT_CONFIG_KEY_1:
'url.https://git:[email protected]/.insteadOf',
GIT_CONFIG_KEY_2: 'url.https://[email protected]/.insteadOf',
GIT_CONFIG_VALUE_0: 'ssh://[email protected]/',
GIT_CONFIG_VALUE_1: '[email protected]:',
GIT_CONFIG_VALUE_2: 'https://custom.example.com/',
});
});

it('returns empty environment variables when matchHost contains invalid protocol', () => {
add({
hostType: 'github',
matchHost: 'invalid://*.github.example.com',
token: 'token123',
});
expect(getGitEnvironmentVariables(['custom'])).toStrictEqual({});
});
});
});
88 changes: 88 additions & 0 deletions lib/util/git/auth.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import type { PlatformId } from '../../constants/platforms';
import { logger } from '../../logger';
import type { HostRule } from '../../types';
import { detectPlatform } from '../common';
import { find, getAll } from '../host-rules';
import { regEx } from '../regex';
import { createURLFromHostOrURL, validateUrl } from '../url';
import type { AuthenticationRule } from './types';
import { parseGitUrl } from './url';

const githubApiUrls = new Set([
'github.com',
'api.github.com',
'https://api.github.com',
'https://api.github.com/',
]);

const standardGitAllowedHostTypes = [
// All known git platforms
'azure',
'bitbucket',
'bitbucket-server',
'gitea',
'github',
'gitlab',
] satisfies PlatformId[];

/**
* Add authorization to a Git Url and returns a new environment variables object
* @returns a new NodeJS.ProcessEnv object without modifying any input parameters
Expand Down Expand Up @@ -124,3 +144,71 @@ export function getAuthenticationRules(

return authenticationRules;
}

export function getGitEnvironmentVariables(
additionalHostTypes: string[] = []
): NodeJS.ProcessEnv {
let environmentVariables: NodeJS.ProcessEnv = {};

// hard-coded logic to use authentication for github.com based on the githubToken for api.github.com
const githubToken = find({
hostType: 'github',
url: 'https://api.github.com/',
});

if (githubToken?.token) {
environmentVariables = getGitAuthenticatedEnvironmentVariables(
'https://github.com/',
githubToken
);
}

// construct the Set of allowed hostTypes consisting of the standard Git provides
// plus additionalHostTypes, which are provided as parameter
const gitAllowedHostTypes = new Set<string>([
...standardGitAllowedHostTypes,
...additionalHostTypes,
]);

// filter rules without `matchHost` and `token` and github api github rules
const hostRules = getAll()
.filter((r) => r.matchHost && r.token)
.filter((r) => !githubToken || !githubApiUrls.has(r.matchHost!));

// for each hostRule without hostType we add additional authentication variables to the environmentVariables
// for each hostRule with hostType we add additional authentication variables to the environmentVariables
for (const hostRule of hostRules) {
if (!hostRule.hostType || gitAllowedHostTypes.has(hostRule.hostType)) {
environmentVariables = addAuthFromHostRule(
hostRule,
environmentVariables
);
}
}
return environmentVariables;
}

function addAuthFromHostRule(
hostRule: HostRule,
env: NodeJS.ProcessEnv
): NodeJS.ProcessEnv {
let environmentVariables = env;
const httpUrl = createURLFromHostOrURL(hostRule.matchHost!)?.toString();
if (validateUrl(httpUrl)) {
logger.trace(
// TODO: types (#7154)
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`Adding Git authentication for ${httpUrl} using token auth.`
);
environmentVariables = getGitAuthenticatedEnvironmentVariables(
httpUrl!,
hostRule,
environmentVariables
);
} else {
logger.debug(
`Could not parse registryUrl ${hostRule.matchHost!} or not using http(s). Ignoring`
);
}
return environmentVariables;
}

0 comments on commit 6c6984b

Please sign in to comment.