Permit plugins to implement AllRequestFilter

If a plugin extends AllRequestFilter and binds it as a member of the
DynamicSet, the plugin can filter any HTTP request that passes through
the server. This allows a monitoring plugin, or an access control
plugin, to glue in very early in the serving pipeline and see all
requests.

The plugin point is after the RequestContextFilter configures the Guice
environment used by Gerrit, but before any user authentication can
happen. An access control plugin might be able to reject connections
based on remote IP address, or client side SSL certificate, but cannot
rely on Gerrit user information.

If multiple plugins register implementations, the order they are
invoked is currently undefined. The likely order is derived from
the sorting of the names in the plugins/ directory, but we don't
promise any ordering.

Change-Id: I8709373379ec09e79502f67446b8a7b805e8cec7
This commit is contained in:
Shawn O. Pearce
2012-06-26 16:46:17 -07:00
parent a00d091f39
commit 6430f1275d
3 changed files with 89 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
// Copyright (C) 2012 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.servlet.ServletModule;
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/** Filters all HTTP requests passing through the server. */
public abstract class AllRequestFilter implements Filter {
public static ServletModule module() {
return new ServletModule() {
@Override
protected void configureServlets() {
DynamicSet.setOf(binder(), AllRequestFilter.class);
filter("/*").through(FilterProxy.class);
}
};
}
@Singleton
static class FilterProxy implements Filter {
private final DynamicSet<AllRequestFilter> filters;
@Inject
FilterProxy(DynamicSet<AllRequestFilter> filters) {
this.filters = filters;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res,
final FilterChain last) throws IOException, ServletException {
final Iterator<AllRequestFilter> itr = filters.iterator();
new FilterChain() {
@Override
public void doFilter(ServletRequest req, ServletResponse res)
throws IOException, ServletException {
if (itr.hasNext()) {
itr.next().doFilter(req, res, this);
} else {
last.doFilter(req, res);
}
}
}.doFilter(req, res);
}
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void destroy() {
}
}
@Override
public void init(FilterConfig config) throws ServletException {
}
@Override
public void destroy() {
}
}

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.pgm;
import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
import com.google.gerrit.common.ChangeHookRunner;
import com.google.gerrit.httpd.AllRequestFilter;
import com.google.gerrit.httpd.CacheBasedWebSession;
import com.google.gerrit.httpd.GitOverHttpModule;
import com.google.gerrit.httpd.HttpCanonicalWebUrlProvider;
@@ -354,6 +355,7 @@ public class Daemon extends SiteProgram {
private Injector createWebInjector() {
final List<Module> modules = new ArrayList<Module>();
modules.add(RequestContextFilter.module());
modules.add(AllRequestFilter.module());
modules.add(CacheBasedWebSession.module());
modules.add(HttpContactStoreConnection.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class));

View File

@@ -224,6 +224,7 @@ public class WebAppInitializer extends GuiceServletContextListener {
private Injector createWebInjector() {
final List<Module> modules = new ArrayList<Module>();
modules.add(RequestContextFilter.module());
modules.add(AllRequestFilter.module());
modules.add(sysInjector.getInstance(GitOverHttpModule.class));
modules.add(sshInjector.getInstance(WebModule.class));
modules.add(sshInjector.getInstance(WebSshGlueModule.class));