Enable case insensitive authentication for git operations
A new configuration parameter is introduced that converts the username
that is received to authenticate a git operation 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 'ldap.accountSshUserName' is set to
'${sAMAccountName.toLowerCase}').
Change-Id: Icee61ab71b18b71885c20a67cf0152ee45b228f0
Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
@@ -298,6 +298,25 @@ then Gerrit will do the authentication (using DIGEST authentication).
|
|||||||
+
|
+
|
||||||
By default this is set to false.
|
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
|
[[cache]]Section cache
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -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.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.config.AuthConfig;
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gwtjsonrpc.server.XsrfException;
|
import com.google.gwtjsonrpc.server.XsrfException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import javax.servlet.Filter;
|
import javax.servlet.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
@@ -54,12 +58,14 @@ class ContainerAuthFilter implements Filter {
|
|||||||
|
|
||||||
private final Provider<WebSession> session;
|
private final Provider<WebSession> session;
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
|
private final Config config;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache)
|
ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache,
|
||||||
throws XsrfException {
|
@GerritServerConfig Config config) throws XsrfException {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,11 +90,14 @@ class ContainerAuthFilter implements Filter {
|
|||||||
|
|
||||||
private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp)
|
private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
final String username = req.getRemoteUser();
|
String username = req.getRemoteUser();
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
rsp.sendError(SC_FORBIDDEN);
|
rsp.sendError(SC_FORBIDDEN);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (config.getBoolean("auth", "userNameToLowerCase", false)) {
|
||||||
|
username = username.toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
final AccountState who = accountCache.getByUsername(username);
|
final AccountState who = accountCache.getByUsername(username);
|
||||||
if (who == null || !who.getAccount().isActive()) {
|
if (who == null || !who.getAccount().isActive()) {
|
||||||
rsp.sendError(SC_UNAUTHORIZED);
|
rsp.sendError(SC_UNAUTHORIZED);
|
||||||
|
|||||||
@@ -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.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
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.SignedToken;
|
||||||
import com.google.gwtjsonrpc.server.XsrfException;
|
import com.google.gwtjsonrpc.server.XsrfException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -67,16 +71,18 @@ class ProjectDigestFilter implements Filter {
|
|||||||
private final Provider<String> urlProvider;
|
private final Provider<String> urlProvider;
|
||||||
private final Provider<WebSession> session;
|
private final Provider<WebSession> session;
|
||||||
private final AccountCache accountCache;
|
private final AccountCache accountCache;
|
||||||
|
private final Config config;
|
||||||
private final SignedToken tokens;
|
private final SignedToken tokens;
|
||||||
private ServletContext context;
|
private ServletContext context;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ProjectDigestFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
|
ProjectDigestFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
|
||||||
Provider<WebSession> session, AccountCache accountCache)
|
Provider<WebSession> session, AccountCache accountCache,
|
||||||
throws XsrfException {
|
@GerritServerConfig Config config) throws XsrfException {
|
||||||
this.urlProvider = urlProvider;
|
this.urlProvider = urlProvider;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.accountCache = accountCache;
|
this.accountCache = accountCache;
|
||||||
|
this.config = config;
|
||||||
this.tokens = new SignedToken((int) SECONDS.convert(1, HOURS));
|
this.tokens = new SignedToken((int) SECONDS.convert(1, HOURS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +117,7 @@ class ProjectDigestFilter implements Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, String> p = parseAuthorization(hdr);
|
final Map<String, String> p = parseAuthorization(hdr);
|
||||||
final String username = p.get("username");
|
final String user = p.get("username");
|
||||||
final String realm = p.get("realm");
|
final String realm = p.get("realm");
|
||||||
final String nonce = p.get("nonce");
|
final String nonce = p.get("nonce");
|
||||||
final String uri = p.get("uri");
|
final String uri = p.get("uri");
|
||||||
@@ -121,7 +127,7 @@ class ProjectDigestFilter implements Filter {
|
|||||||
final String cnonce = p.get("cnonce");
|
final String cnonce = p.get("cnonce");
|
||||||
final String method = req.getMethod();
|
final String method = req.getMethod();
|
||||||
|
|
||||||
if (username == null //
|
if (user == null //
|
||||||
|| realm == null //
|
|| realm == null //
|
||||||
|| nonce == null //
|
|| nonce == null //
|
||||||
|| uri == null //
|
|| uri == null //
|
||||||
@@ -133,6 +139,11 @@ class ProjectDigestFilter implements Filter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String username = user;
|
||||||
|
if (config.getBoolean("auth", "userNameToLowerCase", false)) {
|
||||||
|
username = username.toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
final AccountState who = accountCache.getByUsername(username);
|
final AccountState who = accountCache.getByUsername(username);
|
||||||
if (who == null || ! who.getAccount().isActive()) {
|
if (who == null || ! who.getAccount().isActive()) {
|
||||||
rsp.sendError(SC_UNAUTHORIZED);
|
rsp.sendError(SC_UNAUTHORIZED);
|
||||||
@@ -145,7 +156,7 @@ class ProjectDigestFilter implements Filter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String A1 = username + ":" + realm + ":" + passwd;
|
final String A1 = user + ":" + realm + ":" + passwd;
|
||||||
final String A2 = method + ":" + uri;
|
final String A2 = method + ":" + uri;
|
||||||
final String expect =
|
final String expect =
|
||||||
KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2));
|
KD(H(A1), nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + H(A2));
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import com.google.gerrit.server.AccessPath;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.PeerDaemonUser;
|
import com.google.gerrit.server.PeerDaemonUser;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
import com.google.gerrit.sshd.SshScope.Context;
|
import com.google.gerrit.sshd.SshScope.Context;
|
||||||
import com.google.inject.Inject;
|
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.common.util.Buffer;
|
||||||
import org.apache.sshd.server.PublickeyAuthenticator;
|
import org.apache.sshd.server.PublickeyAuthenticator;
|
||||||
import org.apache.sshd.server.session.ServerSession;
|
import org.apache.sshd.server.session.ServerSession;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ import java.security.PublicKey;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -61,17 +64,20 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
|||||||
private final SshLog sshLog;
|
private final SshLog sshLog;
|
||||||
private final IdentifiedUser.GenericFactory userFactory;
|
private final IdentifiedUser.GenericFactory userFactory;
|
||||||
private final PeerDaemonUser.Factory peerFactory;
|
private final PeerDaemonUser.Factory peerFactory;
|
||||||
|
private final Config config;
|
||||||
private final Set<PublicKey> myHostKeys;
|
private final Set<PublicKey> myHostKeys;
|
||||||
private volatile PeerKeyCache peerKeyCache;
|
private volatile PeerKeyCache peerKeyCache;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DatabasePubKeyAuth(final SshKeyCacheImpl skc, final SshLog l,
|
DatabasePubKeyAuth(final SshKeyCacheImpl skc, final SshLog l,
|
||||||
final IdentifiedUser.GenericFactory uf, final PeerDaemonUser.Factory pf,
|
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;
|
sshKeyCache = skc;
|
||||||
sshLog = l;
|
sshLog = l;
|
||||||
userFactory = uf;
|
userFactory = uf;
|
||||||
peerFactory = pf;
|
peerFactory = pf;
|
||||||
|
config = cfg;
|
||||||
myHostKeys = myHostKeys(hostKeyProvider);
|
myHostKeys = myHostKeys(hostKeyProvider);
|
||||||
peerKeyCache = new PeerKeyCache(site.peer_keys);
|
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 PublicKey suppliedKey, final ServerSession session) {
|
||||||
final SshSession sd = session.getAttribute(SshSession.KEY);
|
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<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
|
final Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
|
||||||
final SshKeyCacheEntry key = find(keyList, suppliedKey);
|
final SshKeyCacheEntry key = find(keyList, suppliedKey);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user