diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 2d7b787e80..66c8863566 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -287,6 +287,17 @@ Account users register with the Gerrit server. + 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 0000000000..dabd7061fd --- /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. + *

+ * Check whether the container has already authenticated the user. If yes, then + * lookup the account and set the account ID in our current session. + *

+ * 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 session; + private final AccountCache accountCache; + + @Inject + ContainerAuthFilter(Provider 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 d859024dc8..8d6d68453b 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.SshInfoServlet; 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.HttpServletRequest; 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 @@ class UrlModule extends ServletModule { 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 cc2e144742..96ca017fa4 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 @@ import javax.annotation.Nullable; public class WebModule extends FactoryModule { private final Provider sshInfoProvider; private final Provider sshKeyCacheProvider; - private final AuthType authType; + private final AuthConfig authConfig; private final boolean wantSSL; private final GitWebConfig gitWebConfig; @@ -67,7 +67,7 @@ public class WebModule extends FactoryModule { 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 @@ public class WebModule extends FactoryModule { install(new RequireSslFilter.Module()); } - switch (authType) { + switch (authConfig.getAuthType()) { case OPENID: install(new OpenIdModule()); break; @@ -121,10 +121,10 @@ public class WebModule extends FactoryModule { 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 f61a7c6b97..f1a932856b 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 @@ import java.util.concurrent.TimeUnit; public class AuthConfig { private final AuthType authType; private final String httpHeader; + private final boolean trustContainerAuth; private final String logoutUrl; private final List trustedOpenIDs; private final List allowedOpenIDs; @@ -54,6 +55,7 @@ public class AuthConfig { 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 @@ public class AuthConfig { return allowedOpenIDs; } + /** Whether git-over-http should trust authentication done by container. */ + public boolean isTrustContainerAuth() { + return trustContainerAuth; + } + public boolean isIdentityTrustable(final Collection ids) { switch (getAuthType()) { case DEVELOPMENT_BECOME_ANY_ACCOUNT: