Merge "Configurable front-end Servlet Filter as proxy surrogate."
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 8d134ff..ac2821b 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1648,6 +1648,38 @@
+
By default, 5 minutes.
+[[httpd.filterClass]]httpd.filterClass::
++
+Class that implements the javax.servlet.Filter interface
+for filtering any HTTP related traffic going through the Gerrit
+HTTP protocol.
+Class is loaded and configured in the Gerrit Jetty container
+and run in front of all Gerrit URL handlers, allowing the filter
+to inspect, modify, allow or reject each request.
+It needs to be provided as JAR library
+under $GERRIT_SITE/lib as it is resolved using the default Gerrit class
+loader and cannot be dynamically loaded by a plugin.
++
+Failing to load the Filter class would result in a Gerrit start-up
+failure, as this class is supposed to provide mandatory filtering
+in front of Gerrit HTTP protocol.
++
+Typical usage is in conjunction with the auth.type=HTTP as replacement
+of an Apache HTTP proxy layer as security enforcement on top of Gerrit
+by returning a trusted username as HTTP Header.
++
+Example of using a security library secure.jar under $GERRIT_SITE/lib
+that provides a org.anyorg.MySecureFilter Servlet Filter that enforces
+a trusted username in the `TRUSTED_USER` HTTP Header:
+
+----
+[auth]
+ type = HTTP
+ httpHeader = TRUSTED_USER
+
+[http]
+ filterClass = org.anyorg.MySecureFilter
+----
[[ldap]]Section ldap
~~~~~~~~~~~~~~~~~~~~
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index 37637d5..ddc12c8 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -76,6 +76,7 @@
import java.util.zip.ZipFile;
import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
@Singleton
public class JettyServer {
@@ -305,7 +306,7 @@
final List<ContextHandler> all = new ArrayList<ContextHandler>();
for (String path : paths) {
- all.add(makeContext(path, env));
+ all.add(makeContext(path, env, cfg));
}
if (all.size() == 1) {
@@ -325,7 +326,7 @@
}
private ContextHandler makeContext(final String contextPath,
- final JettyEnv env) throws MalformedURLException, IOException {
+ final JettyEnv env, final Config cfg) throws MalformedURLException, IOException {
final ServletContextHandler app = new ServletContextHandler();
// This enables the use of sessions in Jetty, feature available
@@ -344,6 +345,27 @@
//
app.setBaseResource(getBaseResource());
+ // HTTP front-end filter to be used as surrogate of Apache HTTP
+ // reverse-proxy filtering.
+ // It is meant to be used as simpler tiny deployment of custom-made
+ // security enforcement (Security tokens, IP-based security filtering, others)
+ String filterClassName = cfg.getString("httpd", null, "filterClass");
+ if (filterClassName != null) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<? extends Filter> filterClass =
+ (Class<? extends Filter>) Class.forName(filterClassName);
+ Filter filter = env.webInjector.getInstance(filterClass);
+ app.addFilter(new FilterHolder(filter), "/*",
+ EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));
+ } catch (Throwable e) {
+ String errorMessage =
+ "Unable to instantiate front-end HTTP Filter " + filterClassName;
+ log.error(errorMessage, e);
+ throw new IllegalArgumentException(errorMessage, e);
+ }
+ }
+
// Perform the same binding as our web.xml would do, but instead
// of using the listener to create the injector pass the one we
// already have built.