Skip to content

Commit 6a215c8

Browse files
authored
Fix null credentialsId passed to GitSCM when authentication is username/password or the StandardUsernameCredential provided by the BitbucketAuthenticator is null. (#924)
1 parent 3ee8748 commit 6a215c8

File tree

4 files changed

+519
-647
lines changed

4 files changed

+519
-647
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@
121121
<artifactId>mockito-core</artifactId>
122122
<scope>test</scope>
123123
</dependency>
124+
<dependency>
125+
<groupId>org.assertj</groupId>
126+
<artifactId>assertj-core</artifactId>
127+
<version>3.26.3</version>
128+
<scope>test</scope>
129+
</dependency>
124130
<dependency>
125131
<groupId>org.jenkins-ci.plugins</groupId>
126132
<artifactId>git</artifactId>

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
4949
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
5050
import com.cloudbees.plugins.credentials.common.StandardCredentials;
51+
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
5152
import com.damnhandy.uri.template.UriTemplate;
5253
import com.fasterxml.jackson.databind.util.StdDateFormat;
5354
import edu.umd.cs.findbugs.annotations.CheckForNull;
@@ -66,6 +67,7 @@
6667
import hudson.model.Items;
6768
import hudson.model.TaskListener;
6869
import hudson.plugins.git.GitSCM;
70+
import hudson.plugins.git.extensions.GitSCMExtension;
6971
import hudson.scm.SCM;
7072
import hudson.security.AccessControlled;
7173
import hudson.util.FormFillFailure;
@@ -377,6 +379,7 @@ public String getEndpointJenkinsRootUrl() {
377379
return AbstractBitbucketEndpoint.getEndpointJenkinsRootUrl(serverUrl);
378380
}
379381

382+
@Override
380383
@NonNull
381384
public List<SCMSourceTrait> getTraits() {
382385
return Collections.unmodifiableList(traits);
@@ -397,11 +400,11 @@ public void setBitbucketServerUrl(String url) {
397400
if (endpoint != null) {
398401
// we have a match
399402
setServerUrl(endpoint.getServerUrl());
400-
return;
403+
} else {
404+
LOGGER.log(Level.WARNING, "Call to legacy setBitbucketServerUrl({0}) method is configuring a url missing "
405+
+ "from the global configuration.", url);
406+
setServerUrl(url);
401407
}
402-
LOGGER.log(Level.WARNING, "Call to legacy setBitbucketServerUrl({0}) method is configuring a url missing "
403-
+ "from the global configuration.", url);
404-
setServerUrl(url);
405408
}
406409

407410
@Deprecated
@@ -422,8 +425,8 @@ public String getBitbucketServerUrl() {
422425
@CheckForNull
423426
public String getCheckoutCredentialsId() {
424427
for (SCMSourceTrait t : traits) {
425-
if (t instanceof SSHCheckoutTrait) {
426-
return StringUtils.defaultString(((SSHCheckoutTrait) t).getCredentialsId(), DescriptorImpl.ANONYMOUS);
428+
if (t instanceof SSHCheckoutTrait sshTrait) {
429+
return StringUtils.defaultString(sshTrait.getCredentialsId(), DescriptorImpl.ANONYMOUS);
427430
}
428431
}
429432
return DescriptorImpl.SAME;
@@ -446,8 +449,8 @@ public void setCheckoutCredentialsId(String checkoutCredentialsId) {
446449
@NonNull
447450
public String getIncludes() {
448451
for (SCMSourceTrait trait : traits) {
449-
if (trait instanceof WildcardSCMHeadFilterTrait) {
450-
return ((WildcardSCMHeadFilterTrait) trait).getIncludes();
452+
if (trait instanceof WildcardSCMHeadFilterTrait wildcardTrait) {
453+
return wildcardTrait.getIncludes();
451454
}
452455
}
453456
return "*";
@@ -588,8 +591,7 @@ protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHe
588591
@Override
589592
protected Iterable<BitbucketPullRequest> create() {
590593
try {
591-
if (event instanceof HasPullRequests) {
592-
HasPullRequests hasPrEvent = (HasPullRequests) event;
594+
if (event instanceof HasPullRequests hasPrEvent) {
593595
return getBitbucketPullRequestsFromEvent(hasPrEvent, listener);
594596
}
595597

@@ -1000,12 +1002,34 @@ private BitbucketCommit findPRDestinationCommit(BitbucketPullRequest pr, TaskLis
10001002
public SCM build(SCMHead head, SCMRevision revision) {
10011003
initCloneLinks();
10021004

1003-
boolean sshAuth = traits.stream()
1004-
.anyMatch(SSHCheckoutTrait.class::isInstance);
1005+
String scmCredentialsId = credentialsId;
10051006

10061007
BitbucketAuthenticator authenticator = authenticator();
1007-
return new BitbucketGitSCMBuilder(this, head, revision, null)
1008-
.withExtension(new GitClientAuthenticatorExtension(authenticator == null || sshAuth ? null : authenticator.getCredentialsForSCM()))
1008+
GitSCMExtension scmExtension;
1009+
if (authenticator != null) {
1010+
// workaround to force git-plugin to use the configured username/password credentialsId as is
1011+
// remove this workaround as https://github.com/jenkinsci/bitbucket-branch-source-plugin/pull/867 will be merged
1012+
boolean sshAuth = traits.stream()
1013+
.anyMatch(SSHCheckoutTrait.class::isInstance);
1014+
if (sshAuth) {
1015+
// trait will do the magic
1016+
scmCredentialsId = null;
1017+
scmExtension = new GitClientAuthenticatorExtension(null);
1018+
} else {
1019+
StandardUsernameCredentials scmCredentials = authenticator.getCredentialsForSCM();
1020+
// extension overrides the configured credentialsId with a custom StandardUsernameCredentials provided by the Authenticator
1021+
scmExtension = new GitClientAuthenticatorExtension(scmCredentials);
1022+
if (scmCredentials != null) {
1023+
// will be overridden by git extension
1024+
scmCredentialsId = null;
1025+
}
1026+
}
1027+
} else {
1028+
scmExtension = new GitClientAuthenticatorExtension(null);
1029+
}
1030+
1031+
return new BitbucketGitSCMBuilder(this, head, revision, scmCredentialsId)
1032+
.withExtension(scmExtension)
10091033
.withCloneLinks(primaryCloneLinks, mirrorCloneLinks)
10101034
.withTraits(traits)
10111035
.build();
@@ -1115,20 +1139,18 @@ protected List<Action> retrieveActions(@NonNull SCMHead head,
11151139
template = UriTemplate.fromTemplate(getServerUrl() + CLOUD_REPO_TEMPLATE + "/{branchOrPR}/{prIdOrHead}")
11161140
.set("owner", repoOwner)
11171141
.set("repo", repository);
1118-
if (head instanceof PullRequestSCMHead) {
1119-
PullRequestSCMHead pr = (PullRequestSCMHead) head;
1120-
template.set("branchOrPR", "pull-requests").set("prIdOrHead", pr.getId());
1142+
if (head instanceof PullRequestSCMHead prHead) {
1143+
template.set("branchOrPR", "pull-requests").set("prIdOrHead", prHead.getId());
11211144
} else {
11221145
template.set("branchOrPR", "branch").set("prIdOrHead", head.getName());
11231146
}
11241147
} else {
1125-
if (head instanceof PullRequestSCMHead) {
1126-
PullRequestSCMHead pr = (PullRequestSCMHead) head;
1148+
if (head instanceof PullRequestSCMHead prHead) {
11271149
template = UriTemplate
11281150
.fromTemplate(getServerUrl() + SERVER_REPO_TEMPLATE + "/pull-requests/{id}/overview")
11291151
.set("owner", repoOwner)
11301152
.set("repo", repository)
1131-
.set("id", pr.getId());
1153+
.set("id", prHead.getId());
11321154
} else {
11331155
template = UriTemplate
11341156
.fromTemplate(getServerUrl() + SERVER_REPO_TEMPLATE + "/compare/commits{?sourceBranch}")
@@ -1137,10 +1159,9 @@ protected List<Action> retrieveActions(@NonNull SCMHead head,
11371159
.set("sourceBranch", Constants.R_HEADS + head.getName());
11381160
}
11391161
}
1140-
if (head instanceof PullRequestSCMHead) {
1141-
PullRequestSCMHead pr = (PullRequestSCMHead) head;
1142-
title = getPullRequestTitleCache().get(pr.getId());
1143-
ContributorMetadataAction contributor = getPullRequestContributorCache().get(pr.getId());
1162+
if (head instanceof PullRequestSCMHead prHead) {
1163+
title = getPullRequestTitleCache().get(prHead.getId());
1164+
ContributorMetadataAction contributor = getPullRequestContributorCache().get(prHead.getId());
11441165
if (contributor != null) {
11451166
result.add(contributor);
11461167
}
@@ -1149,8 +1170,8 @@ protected List<Action> retrieveActions(@NonNull SCMHead head,
11491170
result.add(new BitbucketLink("icon-bitbucket-branch", url));
11501171
result.add(new ObjectMetadataAction(title, null, url));
11511172
SCMSourceOwner owner = getOwner();
1152-
if (owner instanceof Actionable) {
1153-
for (BitbucketDefaultBranch p : ((Actionable) owner).getActions(BitbucketDefaultBranch.class)) {
1173+
if (owner instanceof Actionable actionable) {
1174+
for (BitbucketDefaultBranch p : actionable.getActions(BitbucketDefaultBranch.class)) {
11541175
if (StringUtils.equals(getRepoOwner(), p.getRepoOwner())
11551176
&& StringUtils.equals(repository, p.getRepository())
11561177
&& StringUtils.equals(p.getDefaultBranch(), head.getName())) {
@@ -1371,8 +1392,7 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner cont
13711392
public ListBoxModel doFillRepositoryItems(@AncestorInPath SCMSourceOwner context,
13721393
@QueryParameter String serverUrl,
13731394
@QueryParameter String credentialsId,
1374-
@QueryParameter String repoOwner)
1375-
throws IOException, InterruptedException {
1395+
@QueryParameter String repoOwner) throws IOException {
13761396
BitbucketSupplier<ListBoxModel> listBoxModelSupplier = bitbucket -> {
13771397
ListBoxModel result = new ListBoxModel();
13781398
BitbucketTeam team = bitbucket.getTeam();
@@ -1439,6 +1459,7 @@ public List<NamedArrayList<? extends SCMSourceTraitDescriptor>> getTraitsDescrip
14391459
return result;
14401460
}
14411461

1462+
@Override
14421463
public List<SCMSourceTrait> getTraitsDefaults() {
14431464
return Arrays.asList(
14441465
new BranchDiscoveryTrait(true, false),
@@ -1465,7 +1486,6 @@ public void record(@NonNull SCMHead scmHead, SCMRevision revision, boolean isMat
14651486
request.listener().getLogger().println(" Met criteria");
14661487
} else {
14671488
request.listener().getLogger().println(" Does not meet criteria");
1468-
return;
14691489
}
14701490

14711491
}
@@ -1484,8 +1504,8 @@ public BitbucketProbeFactory(BitbucketApi bitbucket, BitbucketSCMSourceRequest r
14841504
@NonNull
14851505
@Override
14861506
public Probe create(@NonNull final SCMHead head, @CheckForNull final I revisionInfo) throws IOException, InterruptedException {
1487-
final String hash = (revisionInfo instanceof BitbucketCommit) //
1488-
? ((BitbucketCommit) revisionInfo).getHash() //
1507+
final String hash = (revisionInfo instanceof BitbucketCommit bbRevision) //
1508+
? bbRevision.getHash() //
14891509
: (String) revisionInfo;
14901510

14911511
return new SCMSourceCriteria.Probe() {
@@ -1501,8 +1521,8 @@ public long lastModified() {
15011521
try {
15021522
BitbucketCommit commit = null;
15031523
if (hash != null) {
1504-
commit = (revisionInfo instanceof BitbucketCommit) //
1505-
? (BitbucketCommit) revisionInfo //
1524+
commit = (revisionInfo instanceof BitbucketCommit bbRevision) //
1525+
? bbRevision //
15061526
: bitbucket.resolveCommit(hash);
15071527
}
15081528

@@ -1559,8 +1579,7 @@ public SCMRevision create(@NonNull SCMHead head,
15591579
BitbucketCommit targetCommit = asCommit(targetInput);
15601580

15611581
SCMRevision revision;
1562-
if (head instanceof PullRequestSCMHead) {
1563-
PullRequestSCMHead prHead = (PullRequestSCMHead) head;
1582+
if (head instanceof PullRequestSCMHead prHead) {
15641583
SCMHead targetHead = prHead.getTarget();
15651584

15661585
return new PullRequestSCMRevision( //
@@ -1574,10 +1593,10 @@ public SCMRevision create(@NonNull SCMHead head,
15741593
}
15751594

15761595
private BitbucketCommit asCommit(I input) throws IOException, InterruptedException {
1577-
if (input instanceof String) {
1578-
return client.resolveCommit((String) input);
1579-
} else if (input instanceof BitbucketCommit) {
1580-
return (BitbucketCommit) input;
1596+
if (input instanceof String value) {
1597+
return client.resolveCommit(value);
1598+
} else if (input instanceof BitbucketCommit commit) {
1599+
return commit;
15811600
}
15821601
return null;
15831602
}
@@ -1626,14 +1645,14 @@ public WrappedException(Throwable cause) {
16261645

16271646
public void unwrap() throws IOException, InterruptedException {
16281647
Throwable cause = getCause();
1629-
if (cause instanceof IOException) {
1630-
throw (IOException) cause;
1648+
if (cause instanceof IOException ioEx) {
1649+
throw ioEx;
16311650
}
1632-
if (cause instanceof InterruptedException) {
1633-
throw (InterruptedException) cause;
1651+
if (cause instanceof InterruptedException interruptedEx) {
1652+
throw interruptedEx;
16341653
}
1635-
if (cause instanceof RuntimeException) {
1636-
throw (RuntimeException) cause;
1654+
if (cause instanceof RuntimeException rtEx) {
1655+
throw rtEx;
16371656
}
16381657
throw this;
16391658
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/credentials/BitbucketUsernamePasswordAuthenticator.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@
2626
package com.cloudbees.jenkins.plugins.bitbucket.api.credentials;
2727

2828
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
29-
import com.cloudbees.plugins.credentials.CredentialsScope;
30-
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
3129
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
32-
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
33-
import hudson.model.Descriptor.FormException;
3430
import java.util.logging.Level;
3531
import java.util.logging.Logger;
3632
import org.apache.http.HttpHost;
@@ -79,13 +75,4 @@ public void configureContext(HttpClientContext context, HttpHost host) {
7975
context.setAuthCache(authCache);
8076
}
8177

82-
@Override
83-
public StandardUsernameCredentials getCredentialsForSCM() {
84-
try {
85-
return new UsernamePasswordCredentialsImpl(
86-
CredentialsScope.GLOBAL, getId(), null, httpCredentials.getUserName(), httpCredentials.getPassword());
87-
} catch (FormException e) {
88-
throw new RuntimeException(e);
89-
}
90-
}
9178
}

0 commit comments

Comments
 (0)