+ * There cannot be multiple processors processing the same incoming webhook for
+ * a specific event installed on the system, meaning the processor must fit to
+ * the incoming request as much as possible or the hook will be rejected.
+ */
+@Restricted(Beta.class)
+public interface BitbucketWebhookProcessor extends ExtensionPoint {
+ static final String SCAN_ON_EMPTY_CHANGES_PROPERTY_NAME = "bitbucket.hooks.processor.scanOnEmptyChanges";
+
+ /**
+ * Called by first for this processor that must respond if is able to handle
+ * this specific request
+ *
+ * @param headers request
+ * @param parameters request
+ * @return {@code true} if this processor is able to handle this hook
+ * request, {@code false} otherwise.
+ */
+ boolean canHandle(@NonNull Map HookEventType(@NonNull String key, Class clazz) {
+ HookEventType(@NonNull String key) {
this.key = key;
- this.clazz = clazz;
}
- @CheckForNull
+ @NonNull
public static HookEventType fromString(String key) {
for (HookEventType value : HookEventType.values()) {
if (value.getKey().equals(key)) {
return value;
}
}
- return null;
- }
-
- public HookProcessor getProcessor() {
- try {
- return (HookProcessor) clazz.getDeclaredConstructor().newInstance();
- } catch (ReflectiveOperationException e) {
- throw new AssertionError("Can not instantiate hook payload processor: " + e.getMessage());
- }
+ throw new IllegalArgumentException("No enum constant " + HookEventType.class.getCanonicalName() + " have key " + key);
}
public String getKey() {
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/HookProcessor.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/HookProcessor.java
deleted file mode 100644
index 0bcc1682e..000000000
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/HookProcessor.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * The MIT License
- *
- * Copyright (c) 2016, CloudBees, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package com.cloudbees.jenkins.plugins.bitbucket.hooks;
-
-import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource;
-import hudson.security.ACL;
-import hudson.security.ACLContext;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import jenkins.scm.api.SCMHeadEvent;
-import jenkins.scm.api.SCMSource;
-import jenkins.scm.api.SCMSourceOwner;
-import jenkins.scm.api.SCMSourceOwners;
-import jenkins.util.SystemProperties;
-import org.apache.commons.lang3.StringUtils;
-
-/**
- * Abstract hook processor.
- *
- * Add new hook processors by extending this class and implement {@link #process(HookEventType, String, BitbucketType, String)},
- * extract details from the hook payload and then fire an {@link jenkins.scm.api.SCMEvent} to dispatch it to the SCM API.
- */
-public abstract class HookProcessor {
-
- protected static final String SCAN_ON_EMPTY_CHANGES_PROPERTY_NAME = "bitbucket.hooks.processor.scanOnEmptyChanges";
- protected static final boolean SCAN_ON_EMPTY_CHANGES = SystemProperties.getBoolean(SCAN_ON_EMPTY_CHANGES_PROPERTY_NAME, true);
-
- private static final Logger LOGGER = Logger.getLogger(HookProcessor.class.getName());
-
- /**
- * See Event Payloads for more
- * information about the payload parameter format.
- * @param type the type of hook.
- * @param payload the hook payload
- * @param instanceType the Bitbucket type that called the hook
- * @param origin the origin of the event.
- */
- public abstract void process(HookEventType type, String payload, BitbucketType instanceType, String origin);
-
- /**
- * See Event Payloads for more
- * information about the payload parameter format.
- * @param type the type of hook.
- * @param payload the hook payload
- * @param instanceType the Bitbucket type that called the hook
- * @param origin the origin of the event.
- * @param serverURL special value for native Bitbucket Server hooks which don't expose the server URL in the payload.
- */
- public void process(HookEventType type, String payload, BitbucketType instanceType, String origin, String serverURL) {
- process(type, payload, instanceType, origin);
- }
-
- /**
- * To be called by implementations once the owner and the repository have been extracted from the payload.
- *
- * @param owner the repository owner as configured in the SCMSource
- * @param repository the repository name as configured in the SCMSource
- * @param mirrorId the mirror id if applicable, may be null
- */
- protected void scmSourceReIndex(final String owner, final String repository, final String mirrorId) {
- try (ACLContext context = ACL.as2(ACL.SYSTEM2)) {
- boolean reindexed = false;
- for (SCMSourceOwner scmOwner : SCMSourceOwners.all()) {
- List extends SCMHeadEvent {
+
+ AbstractSCMHeadEvent(Type type, P payload, String origin) {
+ super(type, payload, origin);
+ }
+
+ @Override
+ public boolean isMatch(@NonNull SCMNavigator navigator) {
+ if (!(navigator instanceof BitbucketSCMNavigator)) {
+ return false;
+ }
+ BitbucketSCMNavigator bbNav = (BitbucketSCMNavigator) navigator;
+ if (!isProjectKeyMatch(bbNav.getProjectKey())) {
+ return false;
+ }
+
+ if (!isServerURLMatch(bbNav.getServerUrl())) {
+ return false;
+ }
+ return StringUtils.equalsIgnoreCase(bbNav.getRepoOwner(), getRepository().getOwnerName());
+ }
+
+ protected abstract BitbucketRepository getRepository();
+
+ private boolean isProjectKeyMatch(String projectKey) {
+ if (StringUtils.isBlank(projectKey)) {
+ return true;
+ }
+ BitbucketRepository repository = getRepository();
+ if (repository.getProject() != null) {
+ return projectKey.equals(repository.getProject().getKey());
+ }
+ return true;
+ }
+
+ protected boolean isServerURLMatch(String serverURL) {
+ if (serverURL == null || BitbucketApiUtils.isCloud(serverURL)) {
+ // this is a Bitbucket cloud navigator
+ if (getPayload() instanceof BitbucketServerPullRequestEvent || getPayload() instanceof BitbucketServerPushEvent) {
+ return false;
+ }
+ } else {
+ // this is a Bitbucket server navigator
+ if (getPayload() instanceof BitbucketCloudPullRequestEvent || getPayload() instanceof BitbucketCloudPushEvent) {
+ return false;
+ }
+ Map extends SCMHeadEvent {
- protected AbstractSCMHeadEvent(Type type, P payload, String origin) {
+ AbstractSCMHeadEvent(Type type, P payload, String origin) {
super(type, payload, origin);
}
@@ -77,12 +80,12 @@ private boolean isProjectKeyMatch(String projectKey) {
protected boolean isServerURLMatch(String serverURL) {
if (serverURL == null || BitbucketApiUtils.isCloud(serverURL)) {
// this is a Bitbucket cloud navigator
- if (getPayload() instanceof BitbucketServerPullRequestEvent) {
+ if (getPayload() instanceof BitbucketServerPullRequestEvent || getPayload() instanceof BitbucketServerPushEvent) {
return false;
}
} else {
// this is a Bitbucket server navigator
- if (getPayload() instanceof BitbucketCloudPullRequestEvent) {
+ if (getPayload() instanceof BitbucketCloudPullRequestEvent || getPayload() instanceof BitbucketCloudPushEvent) {
return false;
}
Map
+ * If configured less than this it will be changed to the minimal.
+ */
+ public static BitbucketServerVersion getMinSupportedVersion() {
+ return BitbucketServerVersion.VERSION_8;
+ }
}
diff --git a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java
index ff0e93971..7d05f8fb2 100644
--- a/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java
+++ b/src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java
@@ -46,18 +46,14 @@
import com.cloudbees.jenkins.plugins.bitbucket.impl.endpoint.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
-import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerVersion;
-import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranches;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBuildStatus;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerCommit;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequestCanMerge;
-import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketPluginWebhook;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerProject;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
-import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerWebhook;
import com.damnhandy.uri.template.UriTemplate;
import com.damnhandy.uri.template.impl.Operator;
import com.fasterxml.jackson.core.JacksonException;
@@ -76,7 +72,6 @@
import java.lang.reflect.ParameterizedType;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -97,7 +92,6 @@
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.message.BasicNameValuePair;
-import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang3.StringUtils.abbreviate;
import static org.apache.commons.lang3.StringUtils.substring;
@@ -159,17 +153,10 @@ public class BitbucketServerAPIClient extends AbstractBitbucketApi implements Bi
*/
private final boolean userCentric;
private final String baseURL;
- private final BitbucketServerWebhookImplementation webhookImplementation;
private final CloseableHttpClient client;
public BitbucketServerAPIClient(@NonNull String baseURL, @NonNull String owner, @CheckForNull String repositoryName,
@CheckForNull BitbucketAuthenticator authenticator, boolean userCentric) {
- this(baseURL, owner, repositoryName, authenticator, userCentric, BitbucketServerEndpoint.findWebhookImplementation(baseURL));
- }
-
- public BitbucketServerAPIClient(@NonNull String baseURL, @NonNull String owner, @CheckForNull String repositoryName,
- @CheckForNull BitbucketAuthenticator authenticator, boolean userCentric,
- @NonNull BitbucketServerWebhookImplementation webhookImplementation) {
super(authenticator);
this.userCentric = userCentric;
this.owner = Util.fixEmptyAndTrim(owner);
@@ -178,7 +165,6 @@ public BitbucketServerAPIClient(@NonNull String baseURL, @NonNull String owner,
}
this.repositoryName = repositoryName;
this.baseURL = Util.removeTrailingSlash(baseURL);
- this.webhookImplementation = requireNonNull(webhookImplementation);
this.client = setupClientBuilder().build();
}
@@ -272,12 +258,10 @@ private List
+ For security reasons most credentials are only available when HTTPS is used.
+
-
-