Bind to LDAP using only the end-user identity

When auth.type = LDAP_BIND the actual username and password supplied
by the end user is used to connect to the LDAP server to query
for account information.  This permits Gerrit to be connected to
secured directory servers, without requiring a generic user account
for Gerrit itself.

To avoid breaking existing installations that rely upon the account
query to determine the DN for the authentication bind this new mode
uses the new LDAP_BIND auth type setting.

Bug: issue 423
Change-id: I8ec3adc36ae3f2363d344521d02755a2b385db0b
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-02-17 17:00:50 -08:00
parent c99630a25d
commit c892d34e34
12 changed files with 48 additions and 5 deletions

View File

@@ -63,6 +63,23 @@ it then verifies by performing a simple bind against the configured
<<ldap.server,ldap.server>>. In this configuration the web server
is not involved in the user authentication process.
+
The actual username used in the LDAP simple bind request is the
account's full DN, which is discovered by first querying the
directory using either an anonymous request, or the configured
<<ldap.username>> identity.
* `LDAP_BIND`
+
Gerrit prompts the user to enter a username and a password, which
it then verifies by performing a simple bind against the configured
<<ldap.server,ldap.server>>. In this configuration the web server
is not involved in the user authentication process.
+
Unlike LDAP above, the username used to perform the LDAP simple bind
request is the exact string supplied by in the dialog by the user.
The configured <<ldap.username>> identity is not used to obtain
account information.
+
* `DEVELOPMENT_BECOME_ANY_ACCOUNT`
+
*DO NOT USE*. Only for use in a development environment.

View File

@@ -304,6 +304,7 @@ public class Dispatcher {
new OpenIdSignInDialog(mode, to, msg).center();
break;
case LDAP:
case LDAP_BIND:
new UserPassSignInDialog(to, msg).center();
break;
default:

View File

@@ -189,6 +189,7 @@ public class Gerrit implements EntryPoint {
break;
case LDAP:
case LDAP_BIND:
new UserPassSignInDialog(token, null).center();
break;
}
@@ -442,6 +443,7 @@ public class Gerrit implements EntryPoint {
break;
case LDAP:
case LDAP_BIND:
if (cfg.getRegisterUrl() != null) {
menuRight.add(anchor(C.menuRegister(), cfg.getRegisterUrl()));
}

View File

@@ -222,6 +222,7 @@ public class AccountGroupScreen extends AccountScreen {
switch (Gerrit.getConfig().getAuthType()) {
case HTTP_LDAP:
case LDAP:
case LDAP_BIND:
break;
default:
return;

View File

@@ -76,6 +76,7 @@ class GerritConfigProvider implements Provider<GerritConfig> {
final GerritConfig config = new GerritConfig();
switch (authConfig.getAuthType()) {
case LDAP:
case LDAP_BIND:
config.setRegisterUrl(cfg.getString("auth", null, "registerurl"));
break;
}

View File

@@ -101,6 +101,7 @@ public class WebModule extends FactoryModule {
break;
case LDAP:
case LDAP_BIND:
install(new LdapAuthModule());
break;

View File

@@ -57,6 +57,7 @@ class InitAuth implements InitStep {
switch (auth_type) {
case LDAP:
case LDAP_BIND:
case HTTP_LDAP: {
String server =
ldap.string("LDAP server", "server", "ldap://localhost");

View File

@@ -20,8 +20,8 @@ import com.google.gwtorm.client.StringKey;
/** Association of an external account identifier to a local {@link Account}. */
public final class AccountExternalId {
/**
* Scheme used for {@link AuthType#LDAP}, {@link AuthType#HTTP}, and
* {@link AuthType#HTTP_LDAP} usernames.
* Scheme used for {@link AuthType#LDAP}, {@link AuthType#HTTP},
* {@link AuthType#HTTP_LDAP}, and {@link AuthType#LDAP_BIND} usernames.
* <p>
* The name {@code gerrit:} was a very poor choice.
*/

View File

@@ -47,6 +47,17 @@ public enum AuthType {
*/
LDAP,
/**
* Login collects username and password through a web form, and binds to LDAP.
* <p>
* Unlike {@link #HTTP_LDAP}, Gerrit presents a sign-in dialog to the user and
* makes the connection to the LDAP server on their behalf.
* <p>
* Unlike the more generic {@link #LDAP} mode, Gerrit can only query the
* directory via an actual authenticated user account.
*/
LDAP_BIND,
/** Development mode to enable becoming anyone you want. */
DEVELOPMENT_BECOME_ANY_ACCOUNT;
}

View File

@@ -307,7 +307,12 @@ class LdapRealm implements Realm {
throws AccountException {
final String username = who.getLocalUser();
try {
final DirContext ctx = open();
final DirContext ctx;
if (authConfig.getAuthType() == AuthType.LDAP_BIND) {
ctx = authenticate(username, who.getPassword());
} else {
ctx = open();
}
try {
final LdapQuery.Result m = findAccount(ctx, username);
@@ -541,13 +546,14 @@ class LdapRealm implements Realm {
return new InitialDirContext(env);
}
private void authenticate(String dn, String password) throws AccountException {
private DirContext authenticate(String dn, String password)
throws AccountException {
final Properties env = createContextProperties();
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, dn);
env.put(Context.SECURITY_CREDENTIALS, password != null ? password : "");
try {
new InitialDirContext(env).close();
return new InitialDirContext(env);
} catch (NamingException e) {
throw new AccountException("Incorrect username or password", e);
}

View File

@@ -123,6 +123,7 @@ public class AuthConfig {
case HTTP:
case HTTP_LDAP:
case LDAP:
case LDAP_BIND:
// Its safe to assume yes for an HTTP authentication type, as the
// only way in is through some external system that the admin trusts
//

View File

@@ -82,6 +82,7 @@ public class GerritGlobalModule extends FactoryModule {
switch (loginType) {
case HTTP_LDAP:
case LDAP:
case LDAP_BIND:
install(new LdapModule());
break;