Skip to content

Commit 1d73b87

Browse files
tcrespogpditommaso
andauthored
Add support for Bitbucket API tokens instead of access tokens (#6337) [ci fast]
Signed-off-by: Paolo Di Tommaso <[email protected]> Co-authored-by: Paolo Di Tommaso <[email protected]>
1 parent 66abf24 commit 1d73b87

File tree

3 files changed

+45
-22
lines changed

3 files changed

+45
-22
lines changed

docs/git.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ The following configuration properties are supported for each provider configura
3232
: User password required to access private repositories on the SCM server.
3333

3434
`providers.<provider>.token`
35-
: *Required only for private Gitlab servers*
35+
: *Required only for private Gitlab servers and supported for BitBucket*
3636
: Private API access token.
3737

3838
`providers.<provider>.platform`
@@ -51,19 +51,23 @@ The following configuration properties are supported for each provider configura
5151

5252
### BitBucket
5353

54-
Create a `bitbucket` entry in the [SCM configuration file](#git-configuration) specifying your user name and app password, as shown below:
54+
Create a `bitbucket` entry in the [SCM configuration file](#git-configuration) specifying your user name and either an API token or app password, as shown below:
5555

5656
```groovy
5757
providers {
5858
bitbucket {
5959
user = 'me'
60-
password = 'my-secret'
60+
token = 'my-api-token'
6161
}
6262
}
6363
```
6464

65+
:::{versionadded} 25.07.0-edge
66+
API tokens are supported for BitBucket authentication. When both `token` and `password` are provided, the API token takes priority over the app password.
67+
:::
68+
6569
:::{note}
66-
App passwords are substitute passwords for a user account which you can use for scripts and integrating tools in order to avoid putting your real password into configuration files. Learn more at [this link](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/).
70+
API tokens are substitute passwords for a user account which you can use for scripts and integrating tools in order to avoid putting your real password into configuration files. Learn more about [app passwords](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/) and [API tokens](https://support.atlassian.com/bitbucket-cloud/docs/using-api-tokens/).
6771
:::
6872

6973
### BitBucket Server

modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ package nextflow.scm
1919
import groovy.transform.Memoized
2020
import groovy.util.logging.Slf4j
2121
import nextflow.exception.AbortOperationException
22+
import org.eclipse.jgit.transport.CredentialsProvider
23+
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider
24+
2225
/**
2326
* Implements a repository provider for the BitBucket service
2427
*
@@ -41,19 +44,28 @@ final class BitbucketRepositoryProvider extends RepositoryProvider {
4144

4245
@Override
4346
protected String[] getAuth() {
44-
return config.token
45-
? new String[] { "Authorization", "Bearer ${config.token}" }
46-
: super.getAuth()
47+
if (!hasCredentials()) {
48+
return null
49+
}
50+
51+
String secret = getToken() ?: getPassword()
52+
String authString = "${getUser()}:${secret}".bytes.encodeBase64().toString()
53+
54+
return new String[] { "Authorization", "Basic " + authString }
4755
}
4856

4957
@Override
5058
boolean hasCredentials() {
51-
return getToken()
52-
? true
53-
: super.hasCredentials()
59+
return getUser() && (getPassword() || getToken())
60+
}
61+
62+
/** {@inheritDoc} */
63+
@Override
64+
CredentialsProvider getGitCredentials() {
65+
return new UsernamePasswordCredentialsProvider(getUser(), getToken() ?: getPassword())
5466
}
5567

56-
/** {@inheritDoc} */
68+
/** {@inheritDoc} */
5769
@Override
5870
String getName() { "BitBucket" }
5971

@@ -163,7 +175,12 @@ final class BitbucketRepositoryProvider extends RepositoryProvider {
163175
if( !result )
164176
throw new IllegalStateException("Missing clone URL for: $project")
165177

166-
return result.href
178+
/**
179+
* The clone URL when an API token is used is of the form: https://{bitbucket_username}:{api_token}@bitbucket.org/{workspace}/{repository}.git
180+
* @see <a href="https://support.atlassian.com/bitbucket-cloud/docs/using-api-tokens/">Using API tokens</a>
181+
*/
182+
String cloneUrl = result.href
183+
return getToken() ? cloneUrl.replace('@', ":${getToken()}@") : cloneUrl
167184
}
168185

169186
@Override

modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class BitbucketRepositoryProviderTest extends Specification {
3636
when:
3737
def url = new BitbucketRepositoryProvider('pditommaso/tutorial',config).getCloneUrl()
3838
then:
39-
url ==~ /https:\/\/\w[email protected]\/pditommaso\/tutorial.git/
39+
url ==~ /https:\/\/.[email protected]\/pditommaso\/tutorial.git/
4040
}
4141

4242
def testGetHomePage() {
@@ -182,11 +182,13 @@ class BitbucketRepositoryProviderTest extends Specification {
182182
provider.hasCredentials() == EXPECTED
183183

184184
where:
185-
EXPECTED | CONFIG
186-
false | new ProviderConfig('bitbucket')
187-
false | new ProviderConfig('bitbucket').setUser('foo')
188-
true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
189-
true | new ProviderConfig('bitbucket').setToken('xyz')
185+
EXPECTED | CONFIG
186+
false | new ProviderConfig('bitbucket')
187+
false | new ProviderConfig('bitbucket').setUser('foo')
188+
false | new ProviderConfig('bitbucket').setToken('xyz')
189+
true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
190+
true | new ProviderConfig('bitbucket').setUser('foo').setToken('xyz')
191+
true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar').setToken('xyz')
190192
}
191193

192194
@Unroll
@@ -198,9 +200,9 @@ class BitbucketRepositoryProviderTest extends Specification {
198200
provider.getAuth() == EXPECTED as String[]
199201

200202
where:
201-
EXPECTED | CONFIG
202-
null | new ProviderConfig('bitbucket')
203-
["Authorization", "Bearer xyz"] | new ProviderConfig('bitbucket').setToken('xyz')
204-
["Authorization", "Basic ${"foo:bar".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
203+
EXPECTED | CONFIG
204+
null | new ProviderConfig('bitbucket')
205+
["Authorization", "Basic ${"foo:bar".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
206+
["Authorization", "Basic ${"foo@nextflow.io:xyz".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo@nextflow.io').setToken('xyz')
205207
}
206208
}

0 commit comments

Comments
 (0)