diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt index 2f32d246e9..f2812dad3c 100644 --- a/Documentation/config-gerrit.txt +++ b/Documentation/config-gerrit.txt @@ -298,6 +298,25 @@ then Gerrit will do the authentication (using DIGEST authentication). + By default this is set to false. +[[auth.userNameToLowerCase]]auth.userNameToLowerCase:: ++ +If set the username that is received to authenticate a git operation +is converted to lower case for looking up the user account in Gerrit. ++ +By setting this parameter a case insensitive authentication for the +git operations can be achieved, if it is ensured that the usernames in +Gerrit (scheme `username`) are stored in lower case (e.g. if the +parameter link:#ldap.accountSshUserName[ldap.accountSshUserName] is +set to `${sAMAccountName.toLowerCase}`). It is important that for all +existing accounts this username is already in lower case. It is not +possible to convert the usernames of the existing accounts to lower +case because this would break the access to existing per-user +branches. ++ +This parameter only affects git over http and git over SSH traffic. ++ +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 index 2e20a3c555..11c94a72e5 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ContainerAuthFilter.java @@ -20,12 +20,16 @@ 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.gerrit.server.config.GerritServerConfig; import com.google.gwtjsonrpc.server.XsrfException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import org.eclipse.jgit.lib.Config; + import java.io.IOException; +import java.util.Locale; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -54,12 +58,14 @@ class ContainerAuthFilter implements Filter { private final Provider session; private final AccountCache accountCache; + private final Config config; @Inject - ContainerAuthFilter(Provider session, AccountCache accountCache) - throws XsrfException { + ContainerAuthFilter(Provider session, AccountCache accountCache, + @GerritServerConfig Config config) throws XsrfException { this.session = session; this.accountCache = accountCache; + this.config = config; } @Override @@ -84,11 +90,14 @@ class ContainerAuthFilter implements Filter { private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp) throws IOException { - final String username = req.getRemoteUser(); + String username = req.getRemoteUser(); if (username == null) { rsp.sendError(SC_FORBIDDEN); return false; } + if (config.getBoolean("auth", "userNameToLowerCase", false)) { + username = username.toLowerCase(Locale.US); + } final AccountState who = accountCache.getByUsername(username); if (who == null || !who.getAccount().isActive()) { rsp.sendError(SC_UNAUTHORIZED); diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java index 929d0340d1..4289521887 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/ProjectDigestFilter.java @@ -23,18 +23,22 @@ 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.CanonicalWebUrl; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gwtjsonrpc.server.SignedToken; import com.google.gwtjsonrpc.server.XsrfException; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; +import org.eclipse.jgit.lib.Config; + import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import javax.annotation.Nullable; @@ -67,16 +71,18 @@ class ProjectDigestFilter implements Filter { private final Provider urlProvider; private final Provider session; private final AccountCache accountCache; + private final Config config; private final SignedToken tokens; private ServletContext context; @Inject ProjectDigestFilter(@CanonicalWebUrl @Nullable Provider urlProvider, - Provider session, AccountCache accountCache) - throws XsrfException { + Provider session, AccountCache accountCache, + @GerritServerConfig Config config) throws XsrfException { this.urlProvider = urlProvider; this.session = session; this.accountCache = accountCache; + this.config = config; this.tokens = new SignedToken((int) SECONDS.convert(1, HOURS)); } @@ -111,7 +117,7 @@ class ProjectDigestFilter implements Filter { } final Map p = parseAuthorization(hdr); - final String username = p.get("username"); + final String user = p.get("username"); final String realm = p.get("realm"); final String nonce = p.get("nonce"); final String uri = p.get("uri"); @@ -121,7 +127,7 @@ class ProjectDigestFilter implements Filter { final String cnonce = p.get("cnonce"); final String method = req.getMethod(); - if (username == null // + if (user == null // || realm == null // || nonce == null // || uri == null // @@ -133,6 +139,11 @@ class ProjectDigestFilter implements Filter { return false; } + String username = user; + if (config.getBoolean("auth", "userNameToLowerCase", false)) { + username = username.toLowerCase(Locale.US); + } + final AccountState who = accountCache.getByUsername(username); if (who == null || ! who.getAccount().isActive()) { rsp.sendError(SC_UNAUTHORIZED); @@ -145,7 +156,7 @@ class ProjectDigestFilter implements Filter { return false; } - final String A1 = username + ":" + realm + ":" + passwd; + final String A1 = user + ":" + realm + ":" + passwd; final String A2 = method + ":" + uri; final String expect = KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2)); diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java index 977d20914f..805549e638 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/DatabasePubKeyAuth.java @@ -19,6 +19,7 @@ import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.PeerDaemonUser; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; import com.google.gerrit.sshd.SshScope.Context; import com.google.inject.Inject; @@ -33,6 +34,7 @@ import org.apache.sshd.common.SshException; import org.apache.sshd.common.util.Buffer; import org.apache.sshd.server.PublickeyAuthenticator; import org.apache.sshd.server.session.ServerSession; +import org.eclipse.jgit.lib.Config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +49,7 @@ import java.security.PublicKey; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Locale; import java.util.Set; /** @@ -61,17 +64,20 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator { private final SshLog sshLog; private final IdentifiedUser.GenericFactory userFactory; private final PeerDaemonUser.Factory peerFactory; + private final Config config; private final Set myHostKeys; private volatile PeerKeyCache peerKeyCache; @Inject DatabasePubKeyAuth(final SshKeyCacheImpl skc, final SshLog l, final IdentifiedUser.GenericFactory uf, final PeerDaemonUser.Factory pf, - final SitePaths site, final KeyPairProvider hostKeyProvider) { + final SitePaths site, final KeyPairProvider hostKeyProvider, + final @GerritServerConfig Config cfg) { sshKeyCache = skc; sshLog = l; userFactory = uf; peerFactory = pf; + config = cfg; myHostKeys = myHostKeys(hostKeyProvider); peerKeyCache = new PeerKeyCache(site.peer_keys); } @@ -91,7 +97,7 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator { } } - public boolean authenticate(final String username, + public boolean authenticate(String username, final PublicKey suppliedKey, final ServerSession session) { final SshSession sd = session.getAttribute(SshSession.KEY); @@ -107,6 +113,10 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator { } } + if (config.getBoolean("auth", "userNameToLowerCase", false)) { + username = username.toLowerCase(Locale.US); + } + final Iterable keyList = sshKeyCache.get(username); final SshKeyCacheEntry key = find(keyList, suppliedKey); if (key == null) {