Add allowedUrlPattern global config
Provide admins ability to allow only desired remote urls
that match configured patterns.
Change-Id: I7856e03829978b75bd7e2461bf498a27b5169480
diff --git a/src/main/java/com/googlesource/gerrit/plugins/webhooks/Configuration.java b/src/main/java/com/googlesource/gerrit/plugins/webhooks/Configuration.java
index eb54ec2..3c3f67b 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/webhooks/Configuration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/webhooks/Configuration.java
@@ -14,15 +14,23 @@
package com.googlesource.gerrit.plugins.webhooks;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.server.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
@Singleton
public class Configuration {
+ private static final FluentLogger log = FluentLogger.forEnclosingClass();
+
public static final String THREAD_POOL_SIZE = "threadPoolSize";
+ public static final String ALLOWED_URL_PATTERN = "allowedUrlPattern";
public static final String ALLOWED_EVENT = "allowedEvent";
public static final String MAX_ALLOWED_CONNECTION_TIMEOUT = "maxAllowedConnectionTimeout";
public static final String MAX_ALLOWED_SOCKET_TIMEOUT = "maxAllowedSocketTimeout";
@@ -42,6 +50,7 @@
private final int threadPoolSize;
private final boolean sslVerify;
private final String[] allowedEvents;
+ private final String[] allowedUrlPatterns;
private final int maxAllowedConnectionTimeout;
private final int maxAllowedSocketTimeout;
private final int maxAllowedTries;
@@ -57,6 +66,7 @@
threadPoolSize = cfg.getInt(THREAD_POOL_SIZE, DEFAULT_THREAD_POOL_SIZE);
sslVerify = cfg.getBoolean(RemoteConfig.SSL_VERIFY, DEFAULT_SSL_VERIFY);
allowedEvents = cfg.getStringList(ALLOWED_EVENT);
+ allowedUrlPatterns = cfg.getStringList(ALLOWED_URL_PATTERN);
maxAllowedConnectionTimeout = cfg.getInt(MAX_ALLOWED_CONNECTION_TIMEOUT, 0);
maxAllowedSocketTimeout = cfg.getInt(MAX_ALLOWED_SOCKET_TIMEOUT, 0);
maxAllowedTries = cfg.getInt(MAX_ALLOWED_TRIES, 0);
@@ -91,6 +101,19 @@
return allowedEvents;
}
+ public List<Pattern> getAllowedUrlPatterns() {
+ List<Pattern> patterns = new ArrayList<>();
+ for (String regex : allowedUrlPatterns) {
+ try {
+ patterns.add(Pattern.compile(regex));
+ } catch (PatternSyntaxException e) {
+ log.atWarning().withCause(e).log(
+ "Invalid webhook allowed URL pattern '%s' configured", regex);
+ }
+ }
+ return patterns;
+ }
+
public int getMaxAllowedConnectionTimeout() {
return maxAllowedConnectionTimeout;
}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/webhooks/EventHandler.java b/src/main/java/com/googlesource/gerrit/plugins/webhooks/EventHandler.java
index 986c84e..3cf1f9c 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/webhooks/EventHandler.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/webhooks/EventHandler.java
@@ -30,6 +30,7 @@
class EventHandler implements EventListener {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
+ private final Configuration global;
private final PluginConfigFactory configFactory;
private final String pluginName;
private final RemoteConfig.Factory remoteFactory;
@@ -37,10 +38,12 @@
@Inject
EventHandler(
+ Configuration global,
PluginConfigFactory configFactory,
@PluginName String pluginName,
RemoteConfig.Factory remoteFactory,
PostTask.Factory taskFactory) {
+ this.global = global;
this.configFactory = configFactory;
this.pluginName = pluginName;
this.remoteFactory = remoteFactory;
@@ -72,8 +75,18 @@
log.atWarning().log("remote.%s.url not defined, skipping this remote", name);
continue;
}
-
+ if (!isUrlAllowed(remote.getUrl())) {
+ log.atWarning().log(
+ "remote.%s.url does not match any allowed URL patterns, skipping this remote", name);
+ continue;
+ }
taskFactory.create(projectEvent, remote).schedule();
}
}
+
+ private boolean isUrlAllowed(String url) {
+ return global.getAllowedUrlPatterns().isEmpty()
+ || global.getAllowedUrlPatterns().stream()
+ .anyMatch(pattern -> pattern.matcher(url).matches());
+ }
}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 280b8e7..f4b3250 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -71,6 +71,11 @@
Multiple event types can be provided. If not specified, then all event
types are allowed.
+@PLUGIN@.allowedUrlPattern
+: A regex that remote URLs in the plugin config must match. If multiple
+ patterns are provided, the remote URL must match at least one of them.
+ If not specified, then all remote URLs are allowed.
+
@PLUGIN@.maxAllowedConnectionTimeout
: Maximum allowed value for the connection timeout. If a value greater than
this is configured in the @PLUGIN@.config, this value will be chosen
diff --git a/src/test/java/com/googlesource/gerrit/plugins/webhooks/EventHandlerTest.java b/src/test/java/com/googlesource/gerrit/plugins/webhooks/EventHandlerTest.java
index 8baeb5b..2aae9af 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/webhooks/EventHandlerTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/webhooks/EventHandlerTest.java
@@ -20,12 +20,14 @@
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.entities.Project;
import com.google.gerrit.server.config.PluginConfigFactory;
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.ProjectCreatedEvent;
import com.google.gerrit.server.project.NoSuchProjectException;
+import java.util.regex.Pattern;
import org.eclipse.jgit.lib.Config;
import org.junit.Before;
import org.junit.Test;
@@ -41,6 +43,8 @@
private static final String FOO = "foo";
private static final String FOO_URL = "foo-url";
+ @Mock private Configuration global;
+
@Mock private ProjectCreatedEvent projectCreated;
@Mock private PluginConfigFactory configFactory;
@@ -64,7 +68,7 @@
.thenReturn(config);
when(remoteFactory.create(eq(config), eq(FOO))).thenReturn(remote);
when(taskFactory.create(eq(projectCreated), eq(remote))).thenReturn(postTask);
- eventHandler = new EventHandler(configFactory, PLUGIN, remoteFactory, taskFactory);
+ eventHandler = new EventHandler(global, configFactory, PLUGIN, remoteFactory, taskFactory);
}
@Test
@@ -93,4 +97,27 @@
verifyNoInteractions(taskFactory);
verifyNoInteractions(postTask);
}
+
+ @Test
+ public void allowedUrlTaskScheduled() {
+ when(config.getSubsections(eq(REMOTE))).thenReturn(ImmutableSet.of(FOO));
+ when(global.getAllowedUrlPatterns()).thenReturn(ImmutableList.of(Pattern.compile(FOO_URL)));
+ when(remote.getUrl()).thenReturn(FOO_URL);
+
+ eventHandler.onEvent(projectCreated);
+ verify(taskFactory, times(1)).create(eq(projectCreated), eq(remote));
+ verify(postTask, times(1)).schedule();
+ }
+
+ @Test
+ public void allowedUrlTaskNotScheduled() {
+ when(config.getSubsections(eq(REMOTE))).thenReturn(ImmutableSet.of(FOO));
+ when(global.getAllowedUrlPatterns())
+ .thenReturn(ImmutableList.of(Pattern.compile("does-not-match")));
+ when(remote.getUrl()).thenReturn(FOO_URL);
+
+ eventHandler.onEvent(projectCreated);
+ verifyNoInteractions(taskFactory);
+ verifyNoInteractions(postTask);
+ }
}