Use a filter to enforce all HTTP requests through SSL

Rather than checking only the host page, enforce SSL on every
request, including our JavaScript, icons, and RPCs.

Change-Id: I902e958fd53afa8e16fbc9c599852ee9faaec474
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2009-12-12 20:11:48 -08:00
parent fae4227ccb
commit a2a69c8e58
3 changed files with 112 additions and 36 deletions

View File

@@ -0,0 +1,97 @@
// Copyright (C) 2009 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.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.Nullable;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.servlet.ServletModule;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** Requires the connection to use SSL, redirects if not. */
@Singleton
class RequireSslFilter implements Filter {
static class Module extends ServletModule {
@Override
protected void configureServlets() {
filter("/*").through(RequireSslFilter.class);
}
}
private final Provider<String> urlProvider;
@Inject
RequireSslFilter(@CanonicalWebUrl @Nullable final Provider<String> urlProvider) {
this.urlProvider = urlProvider;
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void destroy() {
}
@Override
public void doFilter(final ServletRequest request,
final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
final HttpServletResponse rsp = (HttpServletResponse) response;
if (isSecure(req)) {
chain.doFilter(request, response);
} else {
// If we wanted SSL, but the user didn't come to us over it,
// force SSL by issuing a protocol redirect. Try to keep the
// name "localhost" in case this is an SSH port tunnel.
//
final String url;
if (isLocalHost(req)) {
final StringBuffer b = req.getRequestURL();
b.replace(0, b.indexOf(":"), "https");
url = b.toString();
} else {
url = urlProvider.get();
}
rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
rsp.setHeader("Location", url);
}
}
private static boolean isSecure(final HttpServletRequest req) {
return "https".equals(req.getScheme()) || req.isSecure();
}
private static boolean isLocalHost(final HttpServletRequest req) {
return "localhost".equals(req.getServerName())
|| "127.0.0.1".equals(req.getServerName());
}
}

View File

@@ -29,8 +29,10 @@ import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.account.AccountManager;
import com.google.gerrit.server.config.AuthConfig;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.FactoryModule;
import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.config.Nullable;
import com.google.gerrit.server.contact.ContactStore;
import com.google.gerrit.server.contact.ContactStoreProvider;
import com.google.gerrit.server.ssh.SshInfo;
@@ -49,15 +51,19 @@ public class WebModule extends FactoryModule {
private final Provider<SshInfo> sshInfoProvider;
private final Provider<SshKeyCache> sshKeyCacheProvider;
private final AuthType authType;
private final boolean wantSSL;
private final GitWebConfig gitWebConfig;
@Inject
WebModule(final Provider<SshInfo> sshInfoProvider,
final Provider<SshKeyCache> sshKeyCacheProvider,
final AuthConfig authConfig, final Injector creatingInjector) {
final AuthConfig authConfig,
@CanonicalWebUrl @Nullable final String canonicalUrl,
final Injector creatingInjector) {
this.sshInfoProvider = sshInfoProvider;
this.sshKeyCacheProvider = sshKeyCacheProvider;
this.authType = authConfig.getAuthType();
this.wantSSL = canonicalUrl != null && canonicalUrl.startsWith("https:");
this.gitWebConfig =
creatingInjector.createChildInjector(new AbstractModule() {
@@ -77,6 +83,10 @@ public class WebModule extends FactoryModule {
}
});
if (wantSSL) {
install(new RequireSslFilter.Module());
}
switch (authType) {
case OPENID:
install(new OpenIdModule());

View File

@@ -19,8 +19,7 @@ import com.google.gerrit.common.data.HostPageData;
import com.google.gerrit.httpd.HtmlDomUtil;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.Nullable;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePath;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.gwtjsonrpc.server.JsonServlet;
@@ -28,6 +27,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.w3c.dom.Document;
@@ -52,20 +52,15 @@ import javax.servlet.http.HttpServletResponse;
public class HostPageServlet extends HttpServlet {
private final Provider<CurrentUser> currentUser;
private final GerritConfig config;
private final Provider<String> urlProvider;
private final boolean wantSSL;
private final Document hostDoc;
@Inject
HostPageServlet(final Provider<CurrentUser> cu,
@SitePath final File sitePath, final GerritConfig gc,
@CanonicalWebUrl @Nullable final Provider<String> up,
@CanonicalWebUrl @Nullable final String configuredUrl,
final ServletContext servletContext) throws IOException {
@GerritServerConfig final Config cfg, final ServletContext servletContext)
throws IOException {
currentUser = cu;
urlProvider = up;
config = gc;
wantSSL = configuredUrl != null && configuredUrl.startsWith("https:");
final String pageName = "HostPage.html";
hostDoc = HtmlDomUtil.parseFile(getClass(), pageName);
@@ -187,23 +182,6 @@ public class HostPageServlet extends HttpServlet {
@Override
protected void doGet(final HttpServletRequest req,
final HttpServletResponse rsp) throws IOException {
// If we wanted SSL, but the user didn't come to us over an SSL channel,
// force it to be SSL by issuing a protocol redirect. Try to keep the
// name "localhost" in case this is an SSH port tunnel.
//
if (wantSSL && !isSecure(req)) {
final StringBuffer reqUrl = req.getRequestURL();
if (isLocalHost(req)) {
reqUrl.replace(0, reqUrl.indexOf(":"), "https");
} else {
reqUrl.setLength(0);
reqUrl.append(urlProvider.get());
}
rsp.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
rsp.setHeader("Location", reqUrl.toString());
return;
}
final HostPageData pageData = new HostPageData();
pageData.config = config;
@@ -237,13 +215,4 @@ public class HostPageServlet extends HttpServlet {
out.close();
}
}
private static boolean isSecure(final HttpServletRequest req) {
return "https".equals(req.getScheme()) || req.isSecure();
}
private static boolean isLocalHost(final HttpServletRequest req) {
return "localhost".equals(req.getServerName())
|| "127.0.0.1".equals(req.getServerName());
}
}