Skip to content

Commit

Permalink
[JENKINS-75073] Missing serverUrl in fillCredentialsIdItems cause som…
Browse files Browse the repository at this point in the history
…e credentials missing in the initial configuration

If the number of server instances is only one, when a new Jenkins project is created the server list combobox is not available. This causes, in the UI component that loads credentials, to pass the stapler parameter serverUrl as an empty string. In this scenario, serverUrl must be taken from the first of configured endpoints so that it can properly filter the list of credentials.
  • Loading branch information
nfalco79 committed Jan 10, 2025
1 parent 43d58dd commit d142bf4
Show file tree
Hide file tree
Showing 16 changed files with 640 additions and 599 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,9 @@ public BitbucketGitSCMBuilder(@NonNull BitbucketSCMSource scmSource, @NonNull SC
this.scmSource = scmSource;

String serverURL = scmSource.getServerUrl();
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(serverURL);
if (endpoint == null) {
endpoint = new BitbucketServerEndpoint(null, serverURL, false, null);
}
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get()
.findEndpoint(serverURL)
.orElse(new BitbucketServerEndpoint(null, serverURL, false, null));

String repositoryURL = endpoint.getRepositoryUrl(scmSource.getRepoOwner(), scmSource.getRepository());
if (BitbucketApiUtils.isCloud(endpoint.getServerUrl())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.interceptor.RequirePOST;

import static com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils.getFromBitbucket;

Expand Down Expand Up @@ -290,7 +291,7 @@ public String getServerUrl() {
@DataBoundSetter
public void setServerUrl(String serverUrl) {
serverUrl = BitbucketEndpointConfiguration.normalizeServerUrl(serverUrl);
if (!StringUtils.equals(this.serverUrl, serverUrl)) {
if (serverUrl != null && !StringUtils.equals(this.serverUrl, serverUrl)) {

Check warning on line 294 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 294 is only partially covered, one branch is missing
this.serverUrl = serverUrl;
resetId();
}
Expand Down Expand Up @@ -384,14 +385,14 @@ public String getPattern() {
@DataBoundSetter
public void setBitbucketServerUrl(String url) {
url = BitbucketEndpointConfiguration.normalizeServerUrl(url);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(url);
if (endpoint != null) {
// we have a match
setServerUrl(url);
return;
url = StringUtils.defaultIfBlank(url, BitbucketCloudEndpoint.SERVER_URL);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get()
.findEndpoint(url)
.orElse(null);
if (endpoint == null) {
LOGGER.log(Level.WARNING, "Call to legacy setBitbucketServerUrl({0}) method is configuring a url missing "
+ "from the global configuration.", url);
}
LOGGER.log(Level.WARNING, "Call to legacy setBitbucketServerUrl({0}) method is configuring a url missing "
+ "from the global configuration.", url);
setServerUrl(url);
}

Expand All @@ -400,7 +401,10 @@ public void setBitbucketServerUrl(String url) {
@RestrictedSince("2.2.0")
@CheckForNull
public String getBitbucketServerUrl() {
if (BitbucketEndpointConfiguration.get().findEndpoint(serverUrl) instanceof BitbucketCloudEndpoint) {
if (BitbucketEndpointConfiguration.get()
.findEndpoint(serverUrl)
.filter(BitbucketCloudEndpoint.class::isInstance)
.isPresent()) {
return null;
}
return serverUrl;
Expand Down Expand Up @@ -629,7 +633,7 @@ public String getIconClassName() {
@Override
public SCMNavigator newInstance(String name) {
BitbucketSCMNavigator instance = new BitbucketSCMNavigator(StringUtils.defaultString(name));
instance.setTraits((List) getTraitsDefaults());
instance.setTraits(getTraitsDefaults());
return instance;
}

Expand All @@ -647,16 +651,21 @@ public ListBoxModel doFillServerUrlItems(@AncestorInPath SCMSourceOwner context)
return BitbucketEndpointConfiguration.get().getEndpointItems();
}

@RequirePOST
@SuppressWarnings("unused") // used By stapler
public FormValidation doCheckCredentialsId(@AncestorInPath SCMSourceOwner context, @QueryParameter String serverUrl, @QueryParameter String value) {
return BitbucketCredentials.checkCredentialsId(context, value, serverUrl);
public static FormValidation doCheckCredentialsId(@AncestorInPath SCMSourceOwner context,
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverURL,
@QueryParameter String value) {
return BitbucketCredentials.checkCredentialsId(context, value, serverURL);
}

@RequirePOST
@SuppressWarnings("unused") // used By stapler
public static FormValidation doCheckMirrorId(@QueryParameter String value, @QueryParameter String serverUrl) {
public static FormValidation doCheckMirrorId(@QueryParameter String value,

Check warning

Code scanning / Jenkins Security Scan

Stapler: Missing permission check Warning

Potential missing permission check in DescriptorImpl#doCheckMirrorId
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverURL) {
if (!value.isEmpty()) {
BitbucketServerWebhookImplementation webhookImplementation =
BitbucketServerEndpoint.findWebhookImplementation(serverUrl);
BitbucketServerEndpoint.findWebhookImplementation(serverURL);

Check warning on line 668 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 636-668 are not covered by tests
if (webhookImplementation == BitbucketServerWebhookImplementation.PLUGIN) {
return FormValidation.error("Mirror can only be used with native webhooks");
}
Expand All @@ -666,13 +675,13 @@ public static FormValidation doCheckMirrorId(@QueryParameter String value, @Quer

@SuppressWarnings("unused") // used By stapler
public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner context,
@QueryParameter String serverUrl) {
return BitbucketCredentials.fillCredentialsIdItems(context, serverUrl);
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverURL) {
return BitbucketCredentials.fillCredentialsIdItems(context, serverURL);
}

@SuppressWarnings("unused") // used By stapler
public ListBoxModel doFillMirrorIdItems(@AncestorInPath SCMSourceOwner context,
@QueryParameter String serverUrl,
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverUrl,
@QueryParameter String credentialsId,
@QueryParameter String repoOwner)
throws FormFillFailure {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,11 @@ public String getServerUrl() {

@DataBoundSetter
public void setServerUrl(@CheckForNull String serverUrl) {
this.serverUrl = BitbucketEndpointConfiguration.normalizeServerUrl(serverUrl);
if (serverUrl == null) {

Check warning on line 378 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 378 is only partially covered, one branch is missing
this.serverUrl = BitbucketEndpointConfiguration.get().getDefaultEndpoint().getServerUrl();

Check warning on line 379 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 379 is not covered by tests
} else {
this.serverUrl = BitbucketEndpointConfiguration.normalizeServerUrl(serverUrl);

Check warning on line 381 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / SpotBugs

NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE

NORMAL: Possible null pointer dereference in com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource.setServerUrl(String) due to return value of called method
Raw output
<p> The return value from a method is dereferenced without a null check, and the return value of that method is one that should generally be checked for null. This may lead to a <code>NullPointerException</code> when the code is executed. </p>
}
}

@NonNull
Expand All @@ -401,7 +405,10 @@ public void setTraits(@CheckForNull List<SCMSourceTrait> traits) {
@DataBoundSetter
public void setBitbucketServerUrl(String url) {
url = BitbucketEndpointConfiguration.normalizeServerUrl(url);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(url);
url = StringUtils.defaultIfBlank(url, BitbucketCloudEndpoint.SERVER_URL);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get()
.findEndpoint(url)
.orElse(null);
if (endpoint != null) {
// we have a match
setServerUrl(endpoint.getServerUrl());
Expand All @@ -418,7 +425,10 @@ public void setBitbucketServerUrl(String url) {
@CheckForNull
public String getBitbucketServerUrl() {
String serverUrl = getServerUrl();
if (BitbucketEndpointConfiguration.get().findEndpoint(serverUrl) instanceof BitbucketCloudEndpoint) {
if (BitbucketEndpointConfiguration.get()
.findEndpoint(serverUrl)
.filter(BitbucketCloudEndpoint.class::isInstance)
.isPresent()) {
return null;
}
return serverUrl;
Expand Down Expand Up @@ -1336,8 +1346,8 @@ public String getDisplayName() {
@SuppressWarnings("unused") // used By stapler
public FormValidation doCheckCredentialsId(@CheckForNull @AncestorInPath SCMSourceOwner context,
@QueryParameter String value,
@QueryParameter String serverUrl) {
return BitbucketCredentials.checkCredentialsId(context, value, serverUrl);
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverURL) {
return BitbucketCredentials.checkCredentialsId(context, value, serverURL);
}

@SuppressWarnings("unused") // used By stapler
Expand All @@ -1353,11 +1363,13 @@ public static FormValidation doCheckServerUrl(@AncestorInPath SCMSourceOwner con
return FormValidation.ok();
}

@RequirePOST
@SuppressWarnings("unused") // used By stapler
public static FormValidation doCheckMirrorId(@QueryParameter String value, @QueryParameter String serverUrl) {
public static FormValidation doCheckMirrorId(@QueryParameter String value,

Check warning

Code scanning / Jenkins Security Scan

Stapler: Missing permission check Warning

Potential missing permission check in DescriptorImpl#doCheckMirrorId
@QueryParameter(fixEmpty = true, value = "serverUrl") String serverURL) {
if (!value.isEmpty()) {
BitbucketServerWebhookImplementation webhookImplementation =
BitbucketServerEndpoint.findWebhookImplementation(serverUrl);
BitbucketServerEndpoint.findWebhookImplementation(serverURL);

Check warning on line 1372 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 1350-1372 are not covered by tests
if (webhookImplementation == BitbucketServerWebhookImplementation.PLUGIN) {
return FormValidation.error("Mirror can only be used with native webhooks");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import jenkins.scm.api.trait.SCMSourceBuilder;
Expand Down Expand Up @@ -74,7 +73,7 @@ public BitbucketSCMSourceBuilder(@CheckForNull String id, @NonNull String server
@NonNull String repoName, @CheckForNull String mirrorId) {
super(BitbucketSCMSource.class, repoName);
this.id = id;
this.serverUrl = BitbucketEndpointConfiguration.normalizeServerUrl(serverUrl);
this.serverUrl = serverUrl;
this.credentialsId = credentialsId;
this.mirrorId = mirrorId;
this.repoOwner = repoOwner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ protected boolean isMatch(@Nullable String serverUrl) {
@Override
protected BitbucketApi create(@Nullable String serverUrl, @Nullable BitbucketAuthenticator authenticator,
@NonNull String owner, @CheckForNull String projectKey, @CheckForNull String repository) {
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(BitbucketCloudEndpoint.SERVER_URL);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get()
.findEndpoint(BitbucketCloudEndpoint.SERVER_URL)
.orElse(null);
boolean enableCache = false;
int teamCacheDuration = 0;
int repositoriesCacheDuration = 0;
if (endpoint != null && endpoint instanceof BitbucketCloudEndpoint) {
enableCache = ((BitbucketCloudEndpoint) endpoint).isEnableCache();
teamCacheDuration = ((BitbucketCloudEndpoint) endpoint).getTeamCacheDuration();
repositoriesCacheDuration = ((BitbucketCloudEndpoint) endpoint).getRepositoriesCacheDuration();
if (endpoint instanceof BitbucketCloudEndpoint cloudEndpoint) {
enableCache = cloudEndpoint.isEnableCache();
teamCacheDuration = cloudEndpoint.getTeamCacheDuration();
repositoriesCacheDuration = cloudEndpoint.getRepositoriesCacheDuration();

Check warning on line 34 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/BitbucketCloudApiFactory.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 25-34 are not covered by tests
}
return new BitbucketCloudApiClient(
enableCache, teamCacheDuration, repositoriesCacheDuration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ public void setBitbucketJenkinsRootUrl(String bitbucketJenkinsRootUrl) {
*/
@NonNull
public String getEndpointJenkinsRootUrl() {
if (StringUtils.isBlank(bitbucketJenkinsRootUrl))
if (StringUtils.isBlank(bitbucketJenkinsRootUrl)) {
return ClassicDisplayURLProvider.get().getRoot();
else
} else {
return bitbucketJenkinsRootUrl;
}
}

/**
Expand All @@ -172,7 +173,9 @@ public static String getEndpointJenkinsRootUrl(String serverUrl) {
// Note: do not pre-initialize to the global value, so it can be
// reconfigured on the fly.

AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(serverUrl);
AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get()
.findEndpoint(serverUrl)
.orElse(null);
if (endpoint != null) {
return endpoint.getEndpointJenkinsRootUrl();
}
Expand Down
Loading

0 comments on commit d142bf4

Please sign in to comment.