Merge "SSO via client SSL certificates"
This commit is contained in:
@@ -56,6 +56,19 @@ from the user's account object in LDAP. The user's group membership
|
||||
is also pulled from LDAP, making any LDAP groups that a user is a
|
||||
member of available as groups in Gerrit.
|
||||
+
|
||||
* `CLIENT_SSL_CERT_LDAP`
|
||||
+
|
||||
This authentication type is actually kind of SSO. Gerrit will configure
|
||||
Jetty's SSL channel to request client's SSL certificate. For this
|
||||
authentication to work a Gerrit administrator has to import the root
|
||||
certificate of the trust chain used to issue the client's certificate
|
||||
into the <review-site>/etc/keystore.
|
||||
After the authentication is done Gerrit will obtain basic user
|
||||
registration (name and email) from LDAP, and some group memberships.
|
||||
Therefore, the "_LDAP" suffix in the name of this authentication type.
|
||||
This authentication type can only be used under hosted daemon mode, and
|
||||
the httpd.listenUrl must use https:// as the protocol.
|
||||
+
|
||||
* `LDAP`
|
||||
+
|
||||
Gerrit prompts the user to enter a username and a password, which
|
||||
@@ -1105,8 +1118,9 @@ By default, 5 minutes.
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
LDAP integration is only enabled if `auth.type` was set to
|
||||
`HTTP_LDAP` or `LDAP`. See above for a detailed description of
|
||||
the auth.type settings and their implications.
|
||||
`HTTP_LDAP`, `LDAP` or `CLIENT_SSL_CERT_LDAP`. See above for a
|
||||
detailed description of the auth.type settings and their
|
||||
implications.
|
||||
|
||||
An example LDAP configuration follows, and then discussion of
|
||||
the parameters introduced here. Suitable defaults for most
|
||||
|
||||
@@ -481,6 +481,7 @@ public class Gerrit implements EntryPoint {
|
||||
switch (cfg.getAuthType()) {
|
||||
case HTTP:
|
||||
case HTTP_LDAP:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
break;
|
||||
|
||||
case OPENID:
|
||||
|
||||
@@ -225,6 +225,7 @@ public class AccountGroupScreen extends AccountScreen {
|
||||
case HTTP_LDAP:
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.inject.Scopes.SINGLETON;
|
||||
import com.google.gerrit.common.data.GerritConfig;
|
||||
import com.google.gerrit.httpd.auth.become.BecomeAnyAccountLoginServlet;
|
||||
import com.google.gerrit.httpd.auth.container.HttpAuthModule;
|
||||
import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule;
|
||||
import com.google.gerrit.httpd.auth.ldap.LdapAuthModule;
|
||||
import com.google.gerrit.httpd.auth.openid.OpenIdModule;
|
||||
import com.google.gerrit.httpd.gitweb.GitWebModule;
|
||||
@@ -101,6 +102,10 @@ public class WebModule extends FactoryModule {
|
||||
install(new HttpAuthModule());
|
||||
break;
|
||||
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
install(new HttpsClientSslCertModule());
|
||||
break;
|
||||
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
install(new LdapAuthModule());
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2010 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.auth.container;
|
||||
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.server.account.AccountException;
|
||||
import com.google.gerrit.server.account.AccountManager;
|
||||
import com.google.gerrit.server.account.AuthRequest;
|
||||
import com.google.gerrit.server.account.AuthResult;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
@Singleton
|
||||
class HttpsClientSslCertAuthFilter implements Filter {
|
||||
|
||||
private static final Pattern REGEX_USERID = Pattern.compile("CN=([^,]*),.*");
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(HttpsClientSslCertAuthFilter.class);
|
||||
|
||||
private final Provider<WebSession> webSession;
|
||||
private final AccountManager accountManager;
|
||||
|
||||
@Inject
|
||||
HttpsClientSslCertAuthFilter(final Provider<WebSession> webSession,
|
||||
final AccountManager accountManager) {
|
||||
this.webSession = webSession;
|
||||
this.accountManager = accountManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse rsp,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
X509Certificate[] certs = (X509Certificate[]) req.getAttribute("javax.servlet.request.X509Certificate");
|
||||
if (certs == null || certs.length == 0) {
|
||||
throw new ServletException(
|
||||
"Couldn't get the attribute javax.servlet.request.X509Certificate from the request");
|
||||
}
|
||||
String name = certs[0].getSubjectDN().getName();
|
||||
Matcher m = REGEX_USERID.matcher(name);
|
||||
String userName;
|
||||
if (m.matches()) {
|
||||
userName = m.group(1);
|
||||
} else {
|
||||
throw new ServletException("Couldn't extract username from your certificate");
|
||||
}
|
||||
final AuthRequest areq = AuthRequest.forUser(userName);
|
||||
final AuthResult arsp;
|
||||
try {
|
||||
arsp = accountManager.authenticate(areq);
|
||||
} catch (AccountException e) {
|
||||
String err = "Unable to authenticate user \"" + userName + "\"";
|
||||
log.error(err, e);
|
||||
throw new ServletException(err, e);
|
||||
}
|
||||
webSession.get().login(arsp, true);
|
||||
chain.doFilter(req, rsp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2010 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.auth.container;
|
||||
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
|
||||
/** Servlets and support related to CLIENT_SSL_CERT_LDAP authentication. */
|
||||
public class HttpsClientSslCertModule extends ServletModule {
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
filter("/").through(HttpsClientSslCertAuthFilter.class);
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.gerrit.launcher.GerritLauncher;
|
||||
import com.google.gerrit.lifecycle.LifecycleListener;
|
||||
import com.google.gerrit.reviewdb.AuthType;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.config.SitePaths;
|
||||
import com.google.inject.Inject;
|
||||
@@ -139,6 +141,7 @@ public class JettyServer {
|
||||
final URI[] listenUrls = listenURLs(cfg);
|
||||
final boolean reuseAddress = cfg.getBoolean("httpd", "reuseaddress", true);
|
||||
final int acceptors = cfg.getInt("httpd", "acceptorThreads", 2);
|
||||
final AuthType authType = ConfigUtil.getEnum(cfg, "auth", null, "type", AuthType.OPENID);
|
||||
|
||||
reverseProxy = true;
|
||||
final Connector[] connectors = new Connector[listenUrls.length];
|
||||
@@ -147,11 +150,17 @@ public class JettyServer {
|
||||
final int defaultPort;
|
||||
final SelectChannelConnector c;
|
||||
|
||||
if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType) && ! "https".equals(u.getScheme())) {
|
||||
throw new IllegalArgumentException("Protocol '" + u.getScheme()
|
||||
+ "' " + " not supported in httpd.listenurl '" + u
|
||||
+ "' when auth.type = '" + AuthType.CLIENT_SSL_CERT_LDAP.name()
|
||||
+ "'; only 'https' is supported");
|
||||
}
|
||||
|
||||
if ("http".equals(u.getScheme())) {
|
||||
reverseProxy = false;
|
||||
defaultPort = 80;
|
||||
c = new SelectChannelConnector();
|
||||
|
||||
} else if ("https".equals(u.getScheme())) {
|
||||
final SslSelectChannelConnector ssl = new SslSelectChannelConnector();
|
||||
final File keystore = getFile(cfg, "sslkeystore", "etc/keystore");
|
||||
@@ -164,6 +173,10 @@ public class JettyServer {
|
||||
ssl.setKeyPassword(password);
|
||||
ssl.setTrustPassword(password);
|
||||
|
||||
if (AuthType.CLIENT_SSL_CERT_LDAP.equals(authType)) {
|
||||
ssl.setNeedClientAuth(true);
|
||||
}
|
||||
|
||||
reverseProxy = false;
|
||||
defaultPort = 443;
|
||||
c = ssl;
|
||||
|
||||
@@ -39,6 +39,21 @@ public enum AuthType {
|
||||
*/
|
||||
HTTP_LDAP,
|
||||
|
||||
/**
|
||||
* Login via client SSL certificate.
|
||||
* <p>
|
||||
* This authentication type is actually kind of SSO. Gerrit will configure
|
||||
* Jetty's SSL channel to request client's SSL certificate. For this
|
||||
* authentication to work a Gerrit administrator has to import the root
|
||||
* certificate of the trust chain used to issue the client's certificate
|
||||
* into the <review-site>/etc/keystore.
|
||||
* <p>
|
||||
* After the authentication is done Gerrit will obtain basic user
|
||||
* registration (name and email) from LDAP, and some group memberships.
|
||||
* Therefore, the "_LDAP" suffix in the name of this authentication type.
|
||||
*/
|
||||
CLIENT_SSL_CERT_LDAP,
|
||||
|
||||
/**
|
||||
* Login collects username and password through a web form, and binds to LDAP.
|
||||
* <p>
|
||||
|
||||
@@ -146,6 +146,7 @@ public class AuthConfig {
|
||||
case HTTP_LDAP:
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
// Its safe to assume yes for an HTTP authentication type, as the
|
||||
// only way in is through some external system that the admin trusts
|
||||
//
|
||||
|
||||
@@ -127,6 +127,7 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
case HTTP_LDAP:
|
||||
case LDAP:
|
||||
case LDAP_BIND:
|
||||
case CLIENT_SSL_CERT_LDAP:
|
||||
install(new LdapModule());
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user