Skip to content

Commit cadd390

Browse files
committed
[JENKINS-76027] Allow Bitbucket build status to be customised
Restrict BitbucketAuthenticatedClient to perform call only to the configured server to avoid security issue calling any endpoint using the configured credentials. Simplify extensions points interfaces removing those informations now shipped with the authenticated client.
1 parent 506ed6a commit cadd390

13 files changed

+157
-260
lines changed

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketAuthenticatedClient.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,62 @@
3838
*/
3939
public interface BitbucketAuthenticatedClient extends AutoCloseable {
4040

41+
/**
42+
* The owner of the repository where register the webhook.
43+
*/
44+
@NonNull
45+
String getRepositoryOwner();
46+
47+
/**
48+
* Name of the repository where register the webhook.
49+
*/
50+
@CheckForNull
51+
String getRepositoryName();
52+
53+
/**
54+
* Perform an HTTP POST to the configured endpoint.
55+
* <p>
56+
* Request will be sent as JSON
57+
*
58+
* @param path to call, it will prepend with the server URL
59+
* @param payload to send
60+
* @return the JSON string of the response
61+
* @throws IOException in case of connection failures
62+
*/
4163
String post(@NonNull String path, @CheckForNull String payload) throws IOException;
4264

65+
/**
66+
* Perform an HTTP PUT to the configured endpoint.
67+
* <p>
68+
* Request will be sent as JSON
69+
*
70+
* @param path to call, it will prepend with the server URL
71+
* @param payload to send
72+
* @return the JSON string of the response
73+
* @throws IOException in case of connection failures
74+
*/
4375
String put(@NonNull String path, @CheckForNull String payload) throws IOException;
4476

77+
/**
78+
* Perform an HTTP DELETE to the configured endpoint.
79+
* <p>
80+
* Request will be sent as JSON
81+
*
82+
* @param path to call, it will prepend with the server URL
83+
* @return the JSON string of the response
84+
* @throws IOException in case of connection failures
85+
*/
4586
String delete(@NonNull String path) throws IOException;
4687

88+
/**
89+
* Perform an HTTP GET to the configured endpoint.
90+
* <p>
91+
* Request will be sent as JSON
92+
*
93+
* @param path to call, it will prepend with the server URL
94+
* @return the JSON string of the response
95+
* @throws IOException in case of connection failures
96+
*/
4797
@NonNull
4898
String get(@NonNull String path) throws IOException;
4999

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/buildstatus/BitbucketBuildStatusNotifier.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,5 @@ public interface BitbucketBuildStatusNotifier extends ExtensionPoint {
4141
*/
4242
boolean isApplicable(@NonNull EndpointType type);
4343

44-
/**
45-
* The owner of the repository where register the webhook.
46-
*
47-
* @param repositoryOwner name
48-
*/
49-
void setRepositoryOwner(@NonNull String repositoryOwner);
50-
51-
/**
52-
* Name of the repository where register the webhook.
53-
*
54-
* @param repositoryName
55-
*/
56-
void setRepositoryName(@NonNull String repositoryName);
57-
58-
/**
59-
* The base URL of endpoint of the Bitbucket host.
60-
*
61-
* @param serverURL the base of the endpoint to call.
62-
*/
63-
void setServerURL(@NonNull String serverURL);
64-
6544
void sendBuildStatus(@NonNull BitbucketBuildStatus status, @NonNull BitbucketAuthenticatedClient client) throws IOException;
6645
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/webhook/BitbucketWebhookManager.java

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticatedClient;
2727
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketWebHook;
28+
import com.cloudbees.jenkins.plugins.bitbucket.api.endpoint.BitbucketEndpoint;
2829
import edu.umd.cs.findbugs.annotations.NonNull;
2930
import hudson.ExtensionPoint;
3031
import java.io.IOException;
@@ -48,27 +49,6 @@
4849
@Restricted(Beta.class)
4950
public interface BitbucketWebhookManager extends ExtensionPoint {
5051

51-
/**
52-
* The owner of the repository where register the webhook.
53-
*
54-
* @param repositoryOwner name
55-
*/
56-
void setRepositoryOwner(@NonNull String repositoryOwner);
57-
58-
/**
59-
* Name of the repository where register the webhook.
60-
*
61-
* @param repositoryName
62-
*/
63-
void setRepositoryName(@NonNull String repositoryName);
64-
65-
/**
66-
* The base URL of endpoint of the Bitbucket host.
67-
*
68-
* @param serverURL the base of the endpoint to call.
69-
*/
70-
void setServerURL(@NonNull String serverURL);
71-
7252
/**
7353
* The callback URL where send event payload.
7454
* <p>
@@ -78,8 +58,10 @@ public interface BitbucketWebhookManager extends ExtensionPoint {
7858
* endpoint to process own events.
7959
*
8060
* @param callbackURL used to send webhook payload.
61+
* @param endpoint this webhook is registered for, it could be used to
62+
* retrieve additional information to compose the callbackURL
8163
*/
82-
void setCallbackURL(@NonNull String callbackURL);
64+
void setCallbackURL(@NonNull String callbackURL, @NonNull BitbucketEndpoint endpoint);
8365

8466
/**
8567
* The configuration that returned this implementation class.

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/BitbucketCloudApiClient.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,6 @@ public List<BitbucketCloudWebhook> getWebHooks() throws IOException {
528528
@Override
529529
public void postBuildStatus(@NonNull BitbucketBuildStatus status) throws IOException {
530530
CloudBuildStatusNotifier notifier = new CloudBuildStatusNotifier();
531-
notifier.setRepositoryName(repositoryName);
532-
notifier.setRepositoryOwner(owner);
533531
notifier.sendBuildStatus(status, adapt(BitbucketAuthenticatedClient.class));
534532
}
535533

@@ -679,6 +677,12 @@ protected HttpHost getHost() {
679677
return API_HOST;
680678
}
681679

680+
@NonNull
681+
@Override
682+
protected String getBaseURL() {
683+
return "https://api.bitbucket.org";
684+
}
685+
682686
@NonNull
683687
@Override
684688
protected CloseableHttpClient getClient() {

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/WebhookAutoRegisterListener.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,11 @@ private BitbucketWebhookManager buildWebhookManager(BitbucketSCMSource source, B
187187
BitbucketWebhookManager manager = ExtensionList.lookupFirst(webhookConfig.getManager());
188188
// setup manager with base required information
189189
manager.apply(webhookConfig);
190-
manager.setServerURL(endpoint.getServerURL());
191-
manager.setRepositoryOwner(source.getRepoOwner());
192-
manager.setRepositoryName(source.getRepository());
193190

194191
String callbackRootURL = getCallbackRootURL(webhookConfig);
195192
// this is the base callback URL that webhook usually should call to be processed
196193
String callbackURL = callbackRootURL + BitbucketSCMSourcePushHookReceiver.FULL_PATH;
197-
manager.setCallbackURL(callbackURL);
194+
manager.setCallbackURL(callbackURL, endpoint);
198195

199196
// setup traits extra informations
200197
manager.withTrais(source.getTraits());

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/buildstatus/CloudBuildStatusNotifier.java

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,33 +37,16 @@
3737

3838
@Extension
3939
public class CloudBuildStatusNotifier implements BitbucketBuildStatusNotifier {
40-
private static final String COMMIT_BUILD_STATUS_URL = "https://api.bitbucket.org/2.0/repositories{/owner,repo}/commit/{hash}/statuses/build";
41-
42-
private String repositoryOwner;
43-
private String repositoryName;
44-
45-
@Override
46-
public void setRepositoryOwner(@NonNull String repositoryOwner) {
47-
this.repositoryOwner = repositoryOwner;
48-
}
49-
50-
@Override
51-
public void setRepositoryName(@NonNull String repositoryName) {
52-
this.repositoryName = repositoryName;
53-
}
54-
55-
@Override
56-
public void setServerURL(@NonNull String serverURL) {
57-
}
40+
private static final String COMMIT_BUILD_STATUS_URL = "/2.0/repositories{/owner,repo}/commit/{hash}/statuses/build";
5841

5942
@Override
6043
public void sendBuildStatus(@NonNull BitbucketBuildStatus status, @NonNull BitbucketAuthenticatedClient client) throws IOException {
6144
BitbucketBuildStatus newStatus = new BitbucketBuildStatus(status);
6245
newStatus.setName(abbreviate(newStatus.getName(), 255));
6346

6447
String url = UriTemplate.fromTemplate(COMMIT_BUILD_STATUS_URL)
65-
.set("owner", repositoryOwner)
66-
.set("repo", repositoryName)
48+
.set("owner", client.getRepositoryOwner())
49+
.set("repo", client.getRepositoryName())
6750
.set("hash", newStatus.getHash())
6851
.expand();
6952
client.post(url, JsonParser.toString(newStatus));

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/buildstatus/ServerBuildStatusNotifier.java

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,6 @@
4343
public class ServerBuildStatusNotifier implements BitbucketBuildStatusNotifier {
4444
private static final String API_COMMIT_STATUS_PATH = "/rest/api/1.0/projects/{owner}/repos/{repo}/commits/{hash}/builds";
4545

46-
private String repositoryOwner;
47-
private String repositoryName;
48-
private String serverURL;
49-
50-
@Override
51-
public void setRepositoryOwner(@NonNull String repositoryOwner) {
52-
this.repositoryOwner = repositoryOwner;
53-
}
54-
55-
@Override
56-
public void setRepositoryName(@NonNull String repositoryName) {
57-
this.repositoryName = repositoryName;
58-
}
59-
60-
@Override
61-
public void setServerURL(@NonNull String serverURL) {
62-
this.serverURL = serverURL;
63-
}
64-
6546
@Override
6647
public void sendBuildStatus(@NonNull BitbucketBuildStatus status, @NonNull BitbucketAuthenticatedClient client) throws IOException {
6748
BitbucketServerBuildStatus newStatus = new BitbucketServerBuildStatus(status);
@@ -72,15 +53,14 @@ public void sendBuildStatus(@NonNull BitbucketBuildStatus status, @NonNull Bitbu
7253
newStatus.setKey(substring(key, 0, 255 - 33) + '/' + DigestUtils.md5Hex(key));
7354
}
7455

75-
String url = UriTemplate.fromTemplate(this.serverURL + API_COMMIT_STATUS_PATH)
76-
.set("owner", repositoryOwner)
77-
.set("repo", repositoryName)
56+
String url = UriTemplate.fromTemplate(API_COMMIT_STATUS_PATH)
57+
.set("owner", client.getRepositoryOwner())
58+
.set("repo", client.getRepositoryOwner())
7859
.set("hash", newStatus.getHash())
7960
.expand();
8061
client.post(url, JsonParser.toString(newStatus));
8162
}
8263

83-
8464
@Override
8565
public boolean isApplicable(@NonNull EndpointType type) {
8666
return type == EndpointType.SERVER;

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/client/AbstractBitbucketApi.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ protected void setClientProxyParams(HttpClientBuilder builder) {
242242
@NonNull
243243
protected abstract HttpHost getHost();
244244

245+
@NonNull
246+
protected abstract String getBaseURL();
247+
245248
@NonNull
246249
protected abstract CloseableHttpClient getClient();
247250

@@ -395,8 +398,6 @@ private BitbucketWebhookManager buildManager() {
395398
BitbucketWebhookConfiguration configuration = endpoint.getWebhook();
396399
BitbucketWebhookManager manager = ExtensionList.lookupFirst(configuration.getManager());
397400
manager.apply(configuration);
398-
manager.setRepositoryOwner(getOwner());
399-
manager.setRepositoryName(getRepositoryName());
400401
return manager;
401402
}
402403

@@ -406,30 +407,51 @@ public <T> T adapt(Class<T> clazz) {
406407
if (clazz == BitbucketAuthenticatedClient.class) {
407408
return (T) new BitbucketAuthenticatedClient() {
408409

410+
private AbstractBitbucketApi delegate = AbstractBitbucketApi.this;
411+
409412
@Override
410413
public String post(@NonNull String path, @CheckForNull String payload) throws IOException {
411-
return postRequest(path, payload);
414+
return delegate.postRequest(completeURL(path), payload);
412415
}
413416

414417
@Override
415418
public String put(@NonNull String path, @CheckForNull String payload) throws IOException {
416-
return putRequest(path, payload);
419+
return delegate.putRequest(completeURL(path), payload);
417420
}
418421

419422
@Override
420423
public String delete(@NonNull String path) throws IOException {
421-
return deleteRequest(path);
424+
return delegate.deleteRequest(completeURL(path));
422425
}
423426

424427
@Override
425428
public String get(@NonNull String path) throws IOException {
426-
return getRequest(path);
429+
return delegate.getRequest(completeURL(path));
427430
}
428431

429432
@Override
430433
public void close() throws IOException {
431-
AbstractBitbucketApi.this.close();
434+
//delegate.close();
435+
}
436+
437+
@Override
438+
public String getRepositoryOwner() {
439+
return delegate.getOwner();
432440
}
441+
442+
@Override
443+
public String getRepositoryName() {
444+
return delegate.getRepositoryName();
445+
}
446+
447+
private String completeURL(@NonNull String path) {
448+
if (path.startsWith("/")) {
449+
return delegate.getBaseURL() + "/" + path;
450+
} else {
451+
return delegate.getBaseURL() + path;
452+
}
453+
}
454+
433455
};
434456
} else {
435457
return null;

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/notifier/BitbucketBuildStatusNotifications.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,11 @@ private static void sendNotification(@NonNull BitbucketSCMSource source,
235235
newBuildStatus.setParent(buildStatus.getParent());
236236
newBuildStatus.setUrl(buildStatus.getUrl());
237237

238-
String serverURL = source.getServerUrl();
239-
240238
BitbucketBuildStatusNotifier notifier = ExtensionList.lookup(BitbucketBuildStatusNotifier.class)
241239
.stream()
242240
.filter(n -> n.isApplicable(endpointType))
243241
.findFirst()
244242
.orElseThrow(() -> new BitbucketException("No notifier found that supportes endpoint of type " + endpointType));
245-
notifier.setRepositoryOwner(source.getRepoOwner());
246-
notifier.setRepositoryName(source.getRepository());
247-
notifier.setServerURL(serverURL);
248243
notifier.sendBuildStatus(newBuildStatus, client.adapt(BitbucketAuthenticatedClient.class));
249244
}
250245

0 commit comments

Comments
 (0)