From efbf2b1f90d185707baef0047d9ac5558057993c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Thu, 21 Mar 2024 22:00:01 +0000 Subject: [PATCH 1/9] add http access token support for bitbucket server --- .../__snapshots__/index.spec.ts.snap | 24 ++++++++++++++++++ .../__snapshots__/utils.spec.ts.snap | 9 +++++++ .../platform/bitbucket-server/index.spec.ts | 23 ++++++++++++++++- .../platform/bitbucket-server/index.ts | 7 ++++-- .../platform/bitbucket-server/utils.spec.ts | 25 +++++++++++++++++++ .../platform/bitbucket-server/utils.ts | 20 ++++++++++++--- 6 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap diff --git a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap index 76d488bcf8bbf9..a91f385975edb8 100644 --- a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap +++ b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap @@ -154,6 +154,18 @@ exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatf } `; +exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatform() should not throw if token 1`] = ` +{ + "endpoint": "endpoint/", +} +`; + +exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatform() should not throw if username/password 1`] = ` +{ + "endpoint": "endpoint/", +} +`; + exports[`modules/platform/bitbucket-server/index endpoint with no path initRepo() generates URL if API does not contain clone links 1`] = ` { "defaultBranch": "master", @@ -358,6 +370,18 @@ exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform } `; +exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform() should not throw if token 1`] = ` +{ + "endpoint": "endpoint/", +} +`; + +exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform() should not throw if username/password 1`] = ` +{ + "endpoint": "endpoint/", +} +`; + exports[`modules/platform/bitbucket-server/index endpoint with path initRepo() generates URL if API does not contain clone links 1`] = ` { "defaultBranch": "master", diff --git a/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap b/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap new file mode 100644 index 00000000000000..c62fd504d02d87 --- /dev/null +++ b/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`modules/platform/bitbucket-server/utils getExtraCloneOpts should configure bearer token 1`] = ` +{ + "-c": "http.extraheader=Authorization: Bearer abc", +} +`; + +exports[`modules/platform/bitbucket-server/utils getExtraCloneOpts should not configure bearer token 1`] = `{}`; diff --git a/lib/modules/platform/bitbucket-server/index.spec.ts b/lib/modules/platform/bitbucket-server/index.spec.ts index 1130a3bb678740..03e04af47571b8 100644 --- a/lib/modules/platform/bitbucket-server/index.spec.ts +++ b/lib/modules/platform/bitbucket-server/index.spec.ts @@ -247,13 +247,34 @@ describe('modules/platform/bitbucket-server/index', () => { await expect(bitbucket.initPlatform({})).rejects.toThrow(); }); - it('should throw if no username/password', async () => { + it('should throw if no username/password/token', async () => { expect.assertions(1); await expect( bitbucket.initPlatform({ endpoint: 'endpoint' }), ).rejects.toThrow(); }); + it('should not throw if username/password', async () => { + expect.assertions(1); + expect( + await bitbucket.initPlatform({ + endpoint: 'endpoint', + username: 'abc', + password: '123', + }), + ).toMatchSnapshot(); + }); + + it('should not throw if token', async () => { + expect.assertions(1); + expect( + await bitbucket.initPlatform({ + endpoint: 'endpoint', + token: 'abc', + }), + ).toMatchSnapshot(); + }); + it('should throw if version could not be fetched', async () => { httpMock .scope('https://stash.renovatebot.com') diff --git a/lib/modules/platform/bitbucket-server/index.ts b/lib/modules/platform/bitbucket-server/index.ts index 32600a1926c59e..aab4764c3e1a37 100644 --- a/lib/modules/platform/bitbucket-server/index.ts +++ b/lib/modules/platform/bitbucket-server/index.ts @@ -49,6 +49,7 @@ import type { BbsRestUserRef, } from './types'; import * as utils from './utils'; +import { getExtraCloneOpts } from './utils'; /* * Version: 5.3 (EOL Date: 15 Aug 2019) @@ -84,15 +85,16 @@ function updatePrVersion(pr: number, version: number): number { export async function initPlatform({ endpoint, + token, username, password, }: PlatformParams): Promise { if (!endpoint) { throw new Error('Init: You must configure a Bitbucket Server endpoint'); } - if (!(username && password)) { + if (!(username && password) && !token) { throw new Error( - 'Init: You must configure a Bitbucket Server username/password', + 'Init: You must either configure a Bitbucket Server username/password or a HTTP access token', ); } // TODO: Add a connection check that endpoint/username/password combination are valid (#9595) @@ -231,6 +233,7 @@ export async function initRepo({ await git.initRepo({ ...config, url, + extraCloneOpts: getExtraCloneOpts(opts), cloneSubmodules, fullClone: semver.lte(defaults.version, '8.0.0'), }); diff --git a/lib/modules/platform/bitbucket-server/utils.spec.ts b/lib/modules/platform/bitbucket-server/utils.spec.ts index 24c6c54cbb7228..5155acbce7213d 100644 --- a/lib/modules/platform/bitbucket-server/utils.spec.ts +++ b/lib/modules/platform/bitbucket-server/utils.spec.ts @@ -8,6 +8,7 @@ import type { } from './types'; import { BITBUCKET_INVALID_REVIEWERS_EXCEPTION, + getExtraCloneOpts, getInvalidReviewers, getRepoGitUrl, } from './utils'; @@ -268,7 +269,31 @@ describe('modules/platform/bitbucket-server/utils', () => { ), ); }); + + it('works gitUrl:endpoint no basic auth', () => { + expect( + getRepoGitUrl( + 'SOME/repo', + url.toString(), + 'endpoint', + infoMock(url, 'SOME', 'repo'), + {}, + ), + ).toBe(httpLink(url.toString(), 'SOME', 'repo')); + }); }); }); }); + + describe('getExtraCloneOpts', () => { + it('should not configure bearer token', () => { + const res = getExtraCloneOpts({}); + expect(res).toMatchSnapshot(); + }); + + it('should configure bearer token', () => { + const res = getExtraCloneOpts({ token: 'abc' }); + expect(res).toMatchSnapshot(); + }); + }); }); diff --git a/lib/modules/platform/bitbucket-server/utils.ts b/lib/modules/platform/bitbucket-server/utils.ts index 882451953cbc90..d0c33d69d768f7 100644 --- a/lib/modules/platform/bitbucket-server/utils.ts +++ b/lib/modules/platform/bitbucket-server/utils.ts @@ -4,10 +4,11 @@ import is from '@sindresorhus/is'; import { CONFIG_GIT_URL_UNAVAILABLE } from '../../../constants/error-messages'; import { logger } from '../../../logger'; import type { HostRule } from '../../../types'; -import type { GitProtocol } from '../../../types/git'; +import type { GitOptions, GitProtocol } from '../../../types/git'; import * as git from '../../../util/git'; import { BitbucketServerHttp } from '../../../util/http/bitbucket-server'; import type { HttpOptions, HttpResponse } from '../../../util/http/types'; +import { addSecretForSanitizing } from '../../../util/sanitize'; import { parseUrl } from '../../../util/url'; import { getPrBodyStruct } from '../pr-body'; import type { GitUrlOption } from '../types'; @@ -174,9 +175,10 @@ function injectAuth(url: string, opts: HostRule): string { logger.debug(`Invalid url: ${url}`); throw new Error(CONFIG_GIT_URL_UNAVAILABLE); } - // TODO: null checks (#22198) - repoUrl.username = opts.username!; - repoUrl.password = opts.password!; + if (!opts.token && opts.username && opts.password) { + repoUrl.username = opts.username; + repoUrl.password = opts.password; + } return repoUrl.toString(); } @@ -208,3 +210,13 @@ export function getRepoGitUrl( // SSH urls can be used directly return cloneUrl.href; } + +export function getExtraCloneOpts(opts: HostRule): GitOptions { + if (opts.token) { + addSecretForSanitizing(opts.token, 'global'); + return { + '-c': `http.extraheader=Authorization: Bearer ${opts.token}`, + }; + } + return {}; +} From 483ebe0a3a4e0b8a2fc3f90c2d38ee53f46802dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 19:27:02 +0000 Subject: [PATCH 2/9] http access tokens must not have an user --- lib/modules/platform/bitbucket-server/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/platform/bitbucket-server/index.ts b/lib/modules/platform/bitbucket-server/index.ts index aab4764c3e1a37..adda7028e7cc69 100644 --- a/lib/modules/platform/bitbucket-server/index.ts +++ b/lib/modules/platform/bitbucket-server/index.ts @@ -331,7 +331,7 @@ export async function getPrList(refreshCache?: boolean): Promise { const searchParams: Record = { state: 'ALL', }; - if (!config.ignorePrAuthor) { + if (!config.ignorePrAuthor && config.username !== undefined) { searchParams['role.1'] = 'AUTHOR'; searchParams['username.1'] = config.username; } From 725fbc396aa47c5baaba1ff865d98806a27abb3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 19:34:00 +0000 Subject: [PATCH 3/9] setting password and token is invalid --- lib/modules/platform/bitbucket-server/index.spec.ts | 12 ++++++++++++ lib/modules/platform/bitbucket-server/index.ts | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/lib/modules/platform/bitbucket-server/index.spec.ts b/lib/modules/platform/bitbucket-server/index.spec.ts index 03e04af47571b8..1dc837a461f0f8 100644 --- a/lib/modules/platform/bitbucket-server/index.spec.ts +++ b/lib/modules/platform/bitbucket-server/index.spec.ts @@ -254,6 +254,18 @@ describe('modules/platform/bitbucket-server/index', () => { ).rejects.toThrow(); }); + it('should throw if password and token is set', async () => { + expect.assertions(1); + await expect( + bitbucket.initPlatform({ + endpoint: 'endpoint', + username: 'abc', + password: '123', + token: 'abc', + }), + ).rejects.toThrow(); + }); + it('should not throw if username/password', async () => { expect.assertions(1); expect( diff --git a/lib/modules/platform/bitbucket-server/index.ts b/lib/modules/platform/bitbucket-server/index.ts index adda7028e7cc69..1d0fb5f6078952 100644 --- a/lib/modules/platform/bitbucket-server/index.ts +++ b/lib/modules/platform/bitbucket-server/index.ts @@ -96,6 +96,10 @@ export async function initPlatform({ throw new Error( 'Init: You must either configure a Bitbucket Server username/password or a HTTP access token', ); + } else if (password && token) { + throw new Error( + 'Init: You must either configure a Bitbucket Server password or a HTTP access token', + ); } // TODO: Add a connection check that endpoint/username/password combination are valid (#9595) defaults.endpoint = ensureTrailingSlash(endpoint); From b2a4fba1d5713fe8506c78a106973aa2f89fa698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 19:45:33 +0000 Subject: [PATCH 4/9] don't use password for token based auth --- lib/modules/platform/bitbucket-server/readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/modules/platform/bitbucket-server/readme.md b/lib/modules/platform/bitbucket-server/readme.md index 1eae83c077993a..ed8b5c6b7d79cf 100644 --- a/lib/modules/platform/bitbucket-server/readme.md +++ b/lib/modules/platform/bitbucket-server/readme.md @@ -2,12 +2,12 @@ ## Authentication -First, create a [HTTP access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) for the bot account. +First, create a [HTTP access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) for the bot account or a project/repository token. Let Renovate use your HTTP access token by doing _one_ of the following: -- Set your HTTP access token as a `password` in your `config.js` file -- Set your HTTP access token as an environment variable `RENOVATE_PASSWORD` -- Set your HTTP access token when you run Renovate in the CLI with `--password=` +- Set your HTTP access token as a `token` in your `config.js` file +- Set your HTTP access token as an environment variable `RENOVATE_TOKEN` +- Set your HTTP access token when you run Renovate in the CLI with `--token=` Remember to set `platform=bitbucket-server` somewhere in your Renovate config file. From 41f778ea3c43ad2200d2c67b965534290929ee57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 20:53:26 +0000 Subject: [PATCH 5/9] token already sanitized --- lib/modules/platform/bitbucket-server/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/modules/platform/bitbucket-server/utils.ts b/lib/modules/platform/bitbucket-server/utils.ts index d0c33d69d768f7..d59aca50d510d7 100644 --- a/lib/modules/platform/bitbucket-server/utils.ts +++ b/lib/modules/platform/bitbucket-server/utils.ts @@ -213,7 +213,6 @@ export function getRepoGitUrl( export function getExtraCloneOpts(opts: HostRule): GitOptions { if (opts.token) { - addSecretForSanitizing(opts.token, 'global'); return { '-c': `http.extraheader=Authorization: Bearer ${opts.token}`, }; From 1e4379e001c99e6e82970892065a8b843e7d586b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 21:02:13 +0000 Subject: [PATCH 6/9] rify when token or password is valid --- lib/modules/platform/bitbucket-server/readme.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/modules/platform/bitbucket-server/readme.md b/lib/modules/platform/bitbucket-server/readme.md index ed8b5c6b7d79cf..038ca9b5ad4fbb 100644 --- a/lib/modules/platform/bitbucket-server/readme.md +++ b/lib/modules/platform/bitbucket-server/readme.md @@ -2,12 +2,14 @@ ## Authentication -First, create a [HTTP access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) for the bot account or a project/repository token. +First, create a [HTTP access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) for the bot account. Let Renovate use your HTTP access token by doing _one_ of the following: -- Set your HTTP access token as a `token` in your `config.js` file -- Set your HTTP access token as an environment variable `RENOVATE_TOKEN` -- Set your HTTP access token when you run Renovate in the CLI with `--token=` +- Set your HTTP access token as a `token` or `password` in your `config.js` file +- Set your HTTP access token as an environment variable `RENOVATE_TOKEN` or `RENOVATE_PASSWORD` +- Set your HTTP access token when you run Renovate in the CLI with `--token=` or `--password=` + +If you use project or repository based HTTP access tokens, it can only be used as `token`. Remember to set `platform=bitbucket-server` somewhere in your Renovate config file. From fb163522fa6f49e1feaea7cd426683042cf86ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Fri, 22 Mar 2024 21:27:12 +0000 Subject: [PATCH 7/9] don't use snapshots --- .../__snapshots__/index.spec.ts.snap | 24 ------------------- .../__snapshots__/utils.spec.ts.snap | 9 ------- .../platform/bitbucket-server/index.spec.ts | 12 +++++----- .../platform/bitbucket-server/utils.spec.ts | 6 +++-- 4 files changed, 10 insertions(+), 41 deletions(-) delete mode 100644 lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap diff --git a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap index a91f385975edb8..76d488bcf8bbf9 100644 --- a/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap +++ b/lib/modules/platform/bitbucket-server/__snapshots__/index.spec.ts.snap @@ -154,18 +154,6 @@ exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatf } `; -exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatform() should not throw if token 1`] = ` -{ - "endpoint": "endpoint/", -} -`; - -exports[`modules/platform/bitbucket-server/index endpoint with no path initPlatform() should not throw if username/password 1`] = ` -{ - "endpoint": "endpoint/", -} -`; - exports[`modules/platform/bitbucket-server/index endpoint with no path initRepo() generates URL if API does not contain clone links 1`] = ` { "defaultBranch": "master", @@ -370,18 +358,6 @@ exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform } `; -exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform() should not throw if token 1`] = ` -{ - "endpoint": "endpoint/", -} -`; - -exports[`modules/platform/bitbucket-server/index endpoint with path initPlatform() should not throw if username/password 1`] = ` -{ - "endpoint": "endpoint/", -} -`; - exports[`modules/platform/bitbucket-server/index endpoint with path initRepo() generates URL if API does not contain clone links 1`] = ` { "defaultBranch": "master", diff --git a/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap b/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap deleted file mode 100644 index c62fd504d02d87..00000000000000 --- a/lib/modules/platform/bitbucket-server/__snapshots__/utils.spec.ts.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`modules/platform/bitbucket-server/utils getExtraCloneOpts should configure bearer token 1`] = ` -{ - "-c": "http.extraheader=Authorization: Bearer abc", -} -`; - -exports[`modules/platform/bitbucket-server/utils getExtraCloneOpts should not configure bearer token 1`] = `{}`; diff --git a/lib/modules/platform/bitbucket-server/index.spec.ts b/lib/modules/platform/bitbucket-server/index.spec.ts index 1dc837a461f0f8..b980ab9f61c2ab 100644 --- a/lib/modules/platform/bitbucket-server/index.spec.ts +++ b/lib/modules/platform/bitbucket-server/index.spec.ts @@ -268,23 +268,23 @@ describe('modules/platform/bitbucket-server/index', () => { it('should not throw if username/password', async () => { expect.assertions(1); - expect( - await bitbucket.initPlatform({ + await expect( + bitbucket.initPlatform({ endpoint: 'endpoint', username: 'abc', password: '123', }), - ).toMatchSnapshot(); + ).resolves.not.toThrow(); }); it('should not throw if token', async () => { expect.assertions(1); - expect( - await bitbucket.initPlatform({ + await expect( + bitbucket.initPlatform({ endpoint: 'endpoint', token: 'abc', }), - ).toMatchSnapshot(); + ).resolves.not.toThrow(); }); it('should throw if version could not be fetched', async () => { diff --git a/lib/modules/platform/bitbucket-server/utils.spec.ts b/lib/modules/platform/bitbucket-server/utils.spec.ts index 5155acbce7213d..9625459ad310e3 100644 --- a/lib/modules/platform/bitbucket-server/utils.spec.ts +++ b/lib/modules/platform/bitbucket-server/utils.spec.ts @@ -288,12 +288,14 @@ describe('modules/platform/bitbucket-server/utils', () => { describe('getExtraCloneOpts', () => { it('should not configure bearer token', () => { const res = getExtraCloneOpts({}); - expect(res).toMatchSnapshot(); + expect(res).toEqual({}); }); it('should configure bearer token', () => { const res = getExtraCloneOpts({ token: 'abc' }); - expect(res).toMatchSnapshot(); + expect(res).toEqual({ + '-c': 'http.extraheader=Authorization: Bearer abc', + }); }); }); }); From f411975ce404b41158dc63f8db9552b8008ede3d Mon Sep 17 00:00:00 2001 From: Gagarmel Date: Thu, 11 Apr 2024 20:34:08 +0200 Subject: [PATCH 8/9] remove password part from config example Co-authored-by: Michael Kriese --- lib/modules/platform/bitbucket-server/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/modules/platform/bitbucket-server/readme.md b/lib/modules/platform/bitbucket-server/readme.md index 038ca9b5ad4fbb..722c0f0defe4a5 100644 --- a/lib/modules/platform/bitbucket-server/readme.md +++ b/lib/modules/platform/bitbucket-server/readme.md @@ -5,9 +5,9 @@ First, create a [HTTP access token](https://confluence.atlassian.com/bitbucketserver/personal-access-tokens-939515499.html) for the bot account. Let Renovate use your HTTP access token by doing _one_ of the following: -- Set your HTTP access token as a `token` or `password` in your `config.js` file -- Set your HTTP access token as an environment variable `RENOVATE_TOKEN` or `RENOVATE_PASSWORD` -- Set your HTTP access token when you run Renovate in the CLI with `--token=` or `--password=` +- Set your HTTP access token as a `token` in your `config.js` file +- Set your HTTP access token as an environment variable `RENOVATE_TOKEN` +- Set your HTTP access token when you run Renovate in the CLI with `--token=` If you use project or repository based HTTP access tokens, it can only be used as `token`. From f9906dc43cc0aa2692e93811483cd5c039c62687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janosch=20Sch=C3=A4fer?= Date: Thu, 11 Apr 2024 19:50:46 +0000 Subject: [PATCH 9/9] remove unused import --- lib/modules/platform/bitbucket-server/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/modules/platform/bitbucket-server/utils.ts b/lib/modules/platform/bitbucket-server/utils.ts index d59aca50d510d7..92099dafab02b6 100644 --- a/lib/modules/platform/bitbucket-server/utils.ts +++ b/lib/modules/platform/bitbucket-server/utils.ts @@ -8,7 +8,6 @@ import type { GitOptions, GitProtocol } from '../../../types/git'; import * as git from '../../../util/git'; import { BitbucketServerHttp } from '../../../util/http/bitbucket-server'; import type { HttpOptions, HttpResponse } from '../../../util/http/types'; -import { addSecretForSanitizing } from '../../../util/sanitize'; import { parseUrl } from '../../../util/url'; import { getPrBodyStruct } from '../pr-body'; import type { GitUrlOption } from '../types';