Merge "Allow container-based authentication for git over http"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 2d7b787e..66c8863 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -287,6 +287,17 @@
 +
 By default, unset/false.
 
+[[auth.trustContainerAuth]]auth.trustContainerAuth::
++
+If true then it is the responsibility of the container hosting
+Gerrit to authenticate users. In this case Gerrit will blindly trust
+the container.
++
+This parameter only affects git over http traffic. If set to false
+then Gerrit will do the authentication (using DIGEST authentication).
++
+By default this is set to false.
+
 [[cache]]Section cache
 ~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
new file mode 100644
index 0000000..dabd706
--- /dev/null
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java
@@ -0,0 +1,96 @@
+// Copyright (C) 2011 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 static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
+
+import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountState;
+import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtjsonrpc.server.XsrfException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+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;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+/**
+ * Trust the authentication which is done by the container.
+ * <p>
+ * Check whether the container has already authenticated the user. If yes, then
+ * lookup the account and set the account ID in our current session.
+ * <p>
+ * This filter should only be configured to run, when authentication is
+ * configured to trust container authentication. This filter is intended only to
+ * protect the {@link ProjectServlet} and its handled URLs, which provide remote
+ * repository access over HTTP.
+ */
+@Singleton
+class ContainerAuthFilter implements Filter {
+  public static final String REALM_NAME = "Gerrit Code Review";
+
+  private final Provider<WebSession> session;
+  private final AccountCache accountCache;
+
+  @Inject
+  ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache)
+      throws XsrfException {
+    this.session = session;
+    this.accountCache = accountCache;
+  }
+
+  @Override
+  public void init(FilterConfig config) {
+  }
+
+  @Override
+  public void destroy() {
+  }
+
+  @Override
+  public void doFilter(ServletRequest request, ServletResponse response,
+      FilterChain chain) throws IOException, ServletException {
+    HttpServletRequest req = (HttpServletRequest) request;
+    HttpServletResponseWrapper rsp =
+        new HttpServletResponseWrapper((HttpServletResponse) response);
+
+    if (verify(req, rsp)) {
+      chain.doFilter(req, response);
+    }
+  }
+
+  private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp)
+      throws IOException {
+    final String username = req.getRemoteUser();
+    final AccountState who =
+        (username == null) ? null : accountCache.getByUsername(username);
+    if (who == null || !who.getAccount().isActive()) {
+      rsp.sendError(SC_UNAUTHORIZED);
+      return false;
+    }
+    session.get().setUserAccountId(who.getAccount().getId());
+    return true;
+  }
+}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index d859024..8d6d684 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -24,6 +24,7 @@
 import com.google.gerrit.httpd.raw.StaticServlet;
 import com.google.gerrit.httpd.raw.ToolServlet;
 import com.google.gerrit.reviewdb.Change;
+import com.google.gerrit.server.config.AuthConfig;
 import com.google.gwtexpui.server.CacheControlFilter;
 import com.google.inject.Key;
 import com.google.inject.Provider;
@@ -37,6 +38,12 @@
 import javax.servlet.http.HttpServletResponse;
 
 class UrlModule extends ServletModule {
+  private final AuthConfig authConfig;
+
+  public UrlModule(AuthConfig authConfig) {
+    this.authConfig = authConfig;
+  }
+
   @Override
   protected void configureServlets() {
     filter("/*").through(Key.get(CacheControlFilter.class));
@@ -54,7 +61,11 @@
     serve("/tools/*").with(ToolServlet.class);
 
     filter("/p/*").through(ProjectAccessPathFilter.class);
-    filter("/p/*").through(ProjectDigestFilter.class);
+    if (authConfig.isTrustContainerAuth()) {
+      filter("/p/*").through(ContainerAuthFilter.class);
+    } else {
+      filter("/p/*").through(ProjectDigestFilter.class);
+    }
     serve("/p/*").with(ProjectServlet.class);
 
     serve("/Main.class").with(notFound());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index cc2e144..96ca017 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -55,7 +55,7 @@
 public class WebModule extends FactoryModule {
   private final Provider<SshInfo> sshInfoProvider;
   private final Provider<SshKeyCache> sshKeyCacheProvider;
-  private final AuthType authType;
+  private final AuthConfig authConfig;
   private final boolean wantSSL;
   private final GitWebConfig gitWebConfig;
 
@@ -67,7 +67,7 @@
       final Injector creatingInjector) {
     this.sshInfoProvider = sshInfoProvider;
     this.sshKeyCacheProvider = sshKeyCacheProvider;
-    this.authType = authConfig.getAuthType();
+    this.authConfig = authConfig;
     this.wantSSL = canonicalUrl != null && canonicalUrl.startsWith("https:");
 
     this.gitWebConfig =
@@ -92,7 +92,7 @@
       install(new RequireSslFilter.Module());
     }
 
-    switch (authType) {
+    switch (authConfig.getAuthType()) {
       case OPENID:
         install(new OpenIdModule());
         break;
@@ -121,10 +121,10 @@
         break;
 
       default:
-        throw new ProvisionException("Unsupported loginType: " + authType);
+        throw new ProvisionException("Unsupported loginType: " + authConfig.getAuthType());
     }
 
-    install(new UrlModule());
+    install(new UrlModule(authConfig));
     install(new UiRpcModule());
     install(new GerritRequestModule());
     install(new ProjectServlet.Module());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
index f61a7c6..f1a9328 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -35,6 +35,7 @@
 public class AuthConfig {
   private final AuthType authType;
   private final String httpHeader;
+  private final boolean trustContainerAuth;
   private final String logoutUrl;
   private final List<OpenIdProviderPattern> trustedOpenIDs;
   private final List<OpenIdProviderPattern> allowedOpenIDs;
@@ -54,6 +55,7 @@
     allowedOpenIDs = toPatterns(cfg, "allowedOpenID");
     cookiePath = cfg.getString("auth", null, "cookiepath");
     cookieSecure = cfg.getBoolean("auth", "cookiesecure", false);
+    trustContainerAuth = cfg.getBoolean("auth", "trustContainerAuth", false);
 
     String key = cfg.getString("auth", null, "registerEmailPrivateKey");
     if (key != null && !key.isEmpty()) {
@@ -125,6 +127,11 @@
     return allowedOpenIDs;
   }
 
+  /** Whether git-over-http should trust authentication done by container. */
+  public boolean isTrustContainerAuth() {
+    return trustContainerAuth;
+  }
+
   public boolean isIdentityTrustable(final Collection<AccountExternalId> ids) {
     switch (getAuthType()) {
       case DEVELOPMENT_BECOME_ANY_ACCOUNT: