Merge "Move string utils join logic into Gerrit"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index d2a7fbc..28ee86a 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -298,6 +298,25 @@
+
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
~~~~~~~~~~~~~~~~~~~~~~
@@ -1081,6 +1100,12 @@
Valid replacements are `${project}` for the project name in Gerrit
and `${branch}` for the name of the branch.
+[[gitweb.linkname]]gitweb.linkname::
++
+Optional setting for modifying the link name presented to the user
+in the Gerrit web-UI.
++
+Default linkname for custom type is "gitweb".
[[hooks]]Section hooks
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1515,6 +1540,24 @@
Default is `(memberUid=${username})` for RFC 2307,
and unset (disabled) for Active Directory.
+[[ldap.localUsernameToLowerCase]]ldap.localUsernameToLowerCase::
++
+Converts the local username, that is used to login into the Gerrit
+WebUI, to lower case before doing the LDAP authentication. By setting
+this parameter to true, a case insensitive login to the Gerrit WebUI
+can be achieved.
++
+If set, it must be ensured that the local usernames for all existing
+accounts are converted to lower case, otherwise a user that has a
+local username that contains upper case characters cannot login
+anymore. The local usernames for the existing accounts can be
+converted to lower case by running the server program
+link:pgm-LocalUsernamesToLowerCase.html[LocalUsernamesToLowerCase].
+Please be aware that the conversion of the local usernames to lower
+case can't be undone. For newly created accounts the local username
+will be directly stored in lower case.
++
+By default, unset/false.
[[mimetype]]Section mimetype
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/pgm-LocalUsernamesToLowerCase.txt b/Documentation/pgm-LocalUsernamesToLowerCase.txt
new file mode 100644
index 0000000..9189fee
--- /dev/null
+++ b/Documentation/pgm-LocalUsernamesToLowerCase.txt
@@ -0,0 +1,68 @@
+LocalUsernamesToLowerCase
+=========================
+
+NAME
+----
+LocalUsernamesToLowerCase - Convert the local username of every
+account to lower case
+
+SYNOPSIS
+--------
+[verse]
+'java' -jar gerrit.war 'LocalUsernamesToLowerCase' -d <SITE_PATH>
+
+DESCRIPTION
+-----------
+Converts the local username for every account to lower case. The
+local username is the username that is used to login into the Gerrit
+WebUI.
+
+This task is only intended to be run if the configuration parameter
+link:config-gerrit.html#ldap.localUsernameToLowerCase[ldap.localUsernameToLowerCase]
+was set to true to achieve case insensitive LDAP login to the Gerrit
+WebUI.
+
+Please be aware that the conversion of the local usernames to lower
+case can't be undone.
+
+The program will produce errors if there are accounts that have the
+same local username, but with different case. In this case the local
+username for these accounts is not converted to lower case.
+
+This task can run in the background concurrently to the server if the
+database is MySQL or PostgreSQL. If the database is H2, this task
+must be run by itself.
+
+OPTIONS
+-------
+
+-d::
+\--site-path::
+ Location of the gerrit.config file, and all other per-site
+ configuration data, supporting libraries and log files.
+
+\--threads::
+ Number of threads to perform the scan work with. Defaults to
+ twice the number of CPUs available.
+
+CONTEXT
+-------
+This command can only be run on a server which has direct
+connectivity to the metadata database.
+
+EXAMPLES
+--------
+To convert the local username of every account to lower case:
+
+====
+ $ java -jar gerrit.war LocalUsernamesToLowerCase -d site_path
+====
+
+See Also
+--------
+
+* Configuration parameter link:config-gerrit.html#ldap.localUsernameToLowerCase[ldap.localUsernameToLowerCase]
+
+GERRIT
+------
+Part of link:index.html[Gerrit Code Review]
diff --git a/Documentation/pgm-index.txt b/Documentation/pgm-index.txt
index 4db4ab0..5cbe6ba0 100644
--- a/Documentation/pgm-index.txt
+++ b/Documentation/pgm-index.txt
@@ -36,6 +36,9 @@
link:pgm-ScanTrackingIds.html[ScanTrackingIds]::
Rescan all changes after configuring trackingids.
+link:pgm-LocalUsernamesToLowerCase.html[LocalUsernamesToLowerCase]::
+ Convert the local username of every account to lower case.
+
GERRIT
------
Part of link:index.html[Gerrit Code Review]
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java
index 7eb6955..2ea812b 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitWebType.java
@@ -27,18 +27,22 @@
if (name == null || name.isEmpty() || name.equalsIgnoreCase("gitweb")) {
type = new GitWebType();
+ type.setLinkName("gitweb");
type.setProject("?p=${project}.git;a=summary");
type.setRevision("?p=${project}.git;a=commit;h=${commit}");
type.setBranch("?p=${project}.git;a=shortlog;h=${branch}");
} else if (name.equalsIgnoreCase("cgit")) {
type = new GitWebType();
+ type.setLinkName("cgit");
type.setProject("${project}/summary");
type.setRevision("${project}/commit/?id=${commit}");
type.setBranch("${project}/log/?h=${branch}");
} else if (name.equalsIgnoreCase("custom")) {
type = new GitWebType();
+ // The custom name is not defined, let's keep the old style of using GitWeb
+ type.setLinkName("gitweb");
} else {
type = null;
@@ -47,6 +51,9 @@
return type;
}
+ /** name of the type. */
+ private String name;
+
/** String for revision view url. */
private String revision;
@@ -70,6 +77,15 @@
}
/**
+ * Get the String for link-name of the type.
+ *
+ * @return The String for link-name of the type
+ */
+ public String getLinkName() {
+ return name;
+ }
+
+ /**
* Get the String for project view.
*
* @return The String for project view
@@ -99,6 +115,17 @@
}
/**
+ * Set the pattern for link-name type.
+ *
+ * @param pattern The pattern for link-name type
+ */
+ public void setLinkName(final String name) {
+ if (name != null && !name.isEmpty()) {
+ this.name = name;
+ }
+ }
+
+ /**
* Set the pattern for project view.
*
* @param pattern The pattern for project view
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java
index 0460bf2..cf74e7c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GitwebLink.java
@@ -36,6 +36,10 @@
type = gitWebType;
}
+ public String getLinkName() {
+ return "(" + type.getLinkName() + ")";
+ }
+
public String toRevision(final Project.NameKey project, final PatchSet ps) {
ParameterizedString pattern = new ParameterizedString(type.getRevision());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
index dbbaf33..892aafc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -308,7 +308,7 @@
}
if (c != null) {
- table.setWidget(row, 4, new Anchor("(gitweb)", false, c.toBranch(k
+ table.setWidget(row, 4, new Anchor(c.getLinkName(), false, c.toBranch(k
.getNameKey())));
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 003a731..c65e0f8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -21,6 +21,7 @@
import com.google.gerrit.client.ui.AccountDashboardLink;
import com.google.gerrit.client.ui.ComplexDisclosurePanel;
import com.google.gerrit.client.ui.ListenableAccountDiffPreference;
+import com.google.gerrit.common.PageLinks;
import com.google.gerrit.common.data.ChangeDetail;
import com.google.gerrit.common.data.GitwebLink;
import com.google.gerrit.common.data.PatchSetDetail;
@@ -111,7 +112,7 @@
getHeader().add(revtxt);
if (gw != null) {
final Anchor revlink =
- new Anchor("(gitweb)", false, gw.toRevision(detail.getChange()
+ new Anchor(gw.getLinkName(), false, gw.toRevision(detail.getChange()
.getProject(), ps));
revlink.addStyleName(Gerrit.RESOURCES.css().patchSetLink());
getHeader().add(revlink);
@@ -436,7 +437,7 @@
@Override
public void onClick(final ClickEvent event) {
b.setEnabled(false);
- new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b),
+ new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b, true),
Util.C.revertChangeTitle(), Util.C.headingRevertMessage(),
Util.C.buttonRevertChangeSend(), Util.C.buttonRevertChangeCancel(),
Gerrit.RESOURCES.css().revertChangeDialog(), Gerrit.RESOURCES.css().revertMessage(),
@@ -456,7 +457,7 @@
@Override
public void onClick(final ClickEvent event) {
b.setEnabled(false);
- new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b),
+ new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b, false),
Util.C.abandonChangeTitle(), Util.C.headingAbandonMessage(),
Util.C.buttonAbandonChangeSend(), Util.C.buttonAbandonChangeCancel(),
Gerrit.RESOURCES.css().abandonChangeDialog(), Gerrit.RESOURCES.css().abandonMessage()) {
@@ -475,7 +476,7 @@
@Override
public void onClick(final ClickEvent event) {
b.setEnabled(false);
- new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b),
+ new CommentedChangeActionDialog(patchSet.getId(), createCommentedCallback(b, false),
Util.C.restoreChangeTitle(), Util.C.headingRestoreMessage(),
Util.C.buttonRestoreChangeSend(), Util.C.buttonRestoreChangeCancel(),
Gerrit.RESOURCES.css().abandonChangeDialog(), Gerrit.RESOURCES.css().abandonMessage()) {
@@ -633,10 +634,14 @@
}
}
- private AsyncCallback<ChangeDetail> createCommentedCallback(final Button b) {
+ private AsyncCallback<ChangeDetail> createCommentedCallback(final Button b, final boolean redirect) {
return new AsyncCallback<ChangeDetail>() {
public void onSuccess(ChangeDetail result) {
- changeScreen.update(result);
+ if (redirect) {
+ Gerrit.display(PageLinks.toChange(result.getChange().getId()));
+ } else {
+ changeScreen.update(result);
+ }
}
public void onFailure(Throwable caught) {
@@ -644,4 +649,4 @@
}
};
}
-}
\ No newline at end of file
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
index a29acf4..e35097e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/HistoryTable.java
@@ -98,7 +98,7 @@
fmt.setStyleName(3, col, Gerrit.RESOURCES.css().dataCell());
if (k.getCommentCount() > 0) {
- table.setText(3, col, Util.M.patchTableComments(k.getCommentCount()));
+ table.setText(3, col, Integer.toString(k.getCommentCount()));
}
col++;
}
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 dabd706..11c94a7 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
@@ -14,17 +14,22 @@
package com.google.gerrit.httpd;
+import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
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;
@@ -53,12 +58,14 @@
private final Provider<WebSession> session;
private final AccountCache accountCache;
+ private final Config config;
@Inject
- ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache)
- throws XsrfException {
+ ContainerAuthFilter(Provider<WebSession> session, AccountCache accountCache,
+ @GerritServerConfig Config config) throws XsrfException {
this.session = session;
this.accountCache = accountCache;
+ this.config = config;
}
@Override
@@ -83,9 +90,15 @@
private boolean verify(HttpServletRequest req, HttpServletResponseWrapper rsp)
throws IOException {
- final String username = req.getRemoteUser();
- final AccountState who =
- (username == null) ? null : accountCache.getByUsername(username);
+ 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);
return false;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
index b37a152..4016460 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitWebConfig.java
@@ -41,6 +41,7 @@
final String cfgCgi = cfg.getString("gitweb", null, "cgi");
type = GitWebType.fromName(cfg.getString("gitweb", null, "type"));
+ type.setLinkName(cfg.getString("gitweb", null, "linkname"));
type.setBranch(cfg.getString("gitweb", null, "branch"));
type.setProject(cfg.getString("gitweb", null, "project"));
type.setRevision(cfg.getString("gitweb", null, "revision"));
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 929d034..4289521 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 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 @@
private final Provider<String> urlProvider;
private final Provider<WebSession> session;
private final AccountCache accountCache;
+ private final Config config;
private final SignedToken tokens;
private ServletContext context;
@Inject
ProjectDigestFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
- Provider<WebSession> session, AccountCache accountCache)
- throws XsrfException {
+ Provider<WebSession> 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 @@
}
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 nonce = p.get("nonce");
final String uri = p.get("uri");
@@ -121,7 +127,7 @@
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 @@
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 @@
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-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
index 3dfa0e3..5e007fa 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/RevertChange.java
@@ -105,10 +105,10 @@
throw new NoSuchChangeException(changeId);
}
- ChangeUtil.revert(patchSetId, currentUser, message, db,
+ Change.Id revertedChangeId = ChangeUtil.revert(patchSetId, currentUser, message, db,
revertedSenderFactory, hooks, gitManager, patchSetInfoFactory,
replication, myIdent);
- return changeDetailFactory.create(changeId).call();
+ return changeDetailFactory.create(revertedChangeId).call();
}
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
new file mode 100644
index 0000000..082279c
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/LocalUsernamesToLowerCase.java
@@ -0,0 +1,145 @@
+// 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.pgm;
+
+import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER;
+
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.pgm.util.SiteProgram;
+import com.google.gerrit.reviewdb.AccountExternalId;
+import com.google.gerrit.reviewdb.ReviewDb;
+import com.google.gerrit.server.schema.SchemaVersionCheck;
+import com.google.gwtorm.client.OrmException;
+import com.google.gwtorm.client.SchemaFactory;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+
+import org.eclipse.jgit.lib.TextProgressMonitor;
+import org.kohsuke.args4j.Option;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/** Converts the local username for all accounts to lower case */
+public class LocalUsernamesToLowerCase extends SiteProgram {
+ @Option(name = "--threads", usage = "Number of concurrent threads to run")
+ private int threads = 2;
+
+ private final LifecycleManager manager = new LifecycleManager();
+ private final TextProgressMonitor monitor = new TextProgressMonitor();
+ private List<AccountExternalId> todo;
+
+ private Injector dbInjector;
+
+ @Inject
+ private SchemaFactory<ReviewDb> database;
+
+ @Override
+ public int run() throws Exception {
+ if (threads <= 0) {
+ threads = 1;
+ }
+
+ dbInjector = createDbInjector(MULTI_USER);
+ manager.add(dbInjector,
+ dbInjector.createChildInjector(SchemaVersionCheck.module()));
+ manager.start();
+ dbInjector.injectMembers(this);
+
+ final ReviewDb db = database.open();
+ try {
+ todo = db.accountExternalIds().all().toList();
+ synchronized (monitor) {
+ monitor.beginTask("Converting local username", todo.size());
+ }
+ } finally {
+ db.close();
+ }
+
+ final List<Worker> workers = new ArrayList<Worker>(threads);
+ for (int tid = 0; tid < threads; tid++) {
+ Worker t = new Worker();
+ t.start();
+ workers.add(t);
+ }
+ for (Worker t : workers) {
+ t.join();
+ }
+ synchronized (monitor) {
+ monitor.endTask();
+ }
+ manager.stop();
+ return 0;
+ }
+
+ private void convertLocalUserToLowerCase(final ReviewDb db,
+ final AccountExternalId extId) {
+ if (extId.isScheme(AccountExternalId.SCHEME_GERRIT)) {
+ final String localUser = extId.getSchemeRest();
+ final String localUserLowerCase = localUser.toLowerCase(Locale.US);
+ if (!localUser.equals(localUserLowerCase)) {
+ final AccountExternalId.Key extIdKeyLowerCase =
+ new AccountExternalId.Key(AccountExternalId.SCHEME_GERRIT,
+ localUserLowerCase);
+ final AccountExternalId extIdLowerCase =
+ new AccountExternalId(extId.getAccountId(), extIdKeyLowerCase);
+ try {
+ db.accountExternalIds().insert(Collections.singleton(extIdLowerCase));
+ db.accountExternalIds().delete(Collections.singleton(extId));
+ } catch (OrmException error) {
+ System.err.println("ERR " + error.getMessage());
+ }
+ }
+ }
+ }
+
+ private AccountExternalId next() {
+ synchronized (todo) {
+ if (todo.isEmpty()) {
+ return null;
+ }
+ return todo.remove(todo.size() - 1);
+ }
+ }
+
+ private class Worker extends Thread {
+ @Override
+ public void run() {
+ final ReviewDb db;
+ try {
+ db = database.open();
+ } catch (OrmException e) {
+ e.printStackTrace();
+ return;
+ }
+ try {
+ for (;;) {
+ final AccountExternalId extId = next();
+ if (extId == null) {
+ break;
+ }
+ convertLocalUserToLowerCase(db, extId);
+ synchronized (monitor) {
+ monitor.update(1);
+ }
+ }
+ } finally {
+ db.close();
+ }
+ }
+ }
+}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java
index 0719035..4c78139 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountExternalIdAccess.java
@@ -42,4 +42,7 @@
@Query("WHERE emailAddress >= ? AND emailAddress <= ? ORDER BY emailAddress LIMIT ?")
ResultSet<AccountExternalId> suggestByEmailAddress(String emailA,
String emailB, int limit) throws OrmException;
+
+ @Query
+ ResultSet<AccountExternalId> all() throws OrmException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 36ac938..89f5ab2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -256,7 +256,7 @@
hooks.doChangeAbandonedHook(updatedChange, user.getAccount(), message);
}
- public static void revert(final PatchSet.Id patchSetId,
+ public static Change.Id revert(final PatchSet.Id patchSetId,
final IdentifiedUser user, final String message, final ReviewDb db,
final RevertedSender.Factory revertedSenderFactory,
final ChangeHookRunner hooks, GitRepositoryManager gitManager,
@@ -352,6 +352,8 @@
cm.send();
hooks.doPatchsetCreatedHook(change, ps);
+
+ return change.getId();
} finally {
revWalk.release();
git.close();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java
index 2a54029..034b176 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AuthRequest.java
@@ -52,7 +52,7 @@
return r;
}
- private final String externalId;
+ private String externalId;
private String password;
private String displayName;
private String emailAddress;
@@ -78,6 +78,14 @@
return null;
}
+ public void setLocalUser(final String localUser) {
+ if (isScheme(SCHEME_GERRIT)) {
+ final AccountExternalId.Key key =
+ new AccountExternalId.Key(SCHEME_GERRIT, localUser);
+ externalId = key.get();
+ }
+ }
+
public String getPassword() {
return password;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembersFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembersFactory.java
index aae3a909..58743df 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembersFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembersFactory.java
@@ -70,16 +70,22 @@
@Override
public Set<Account> call() throws NoSuchGroupException,
NoSuchProjectException, OrmException {
- if (AccountGroup.PROJECT_OWNERS.equals(groupUUID)) {
- return getProjectOwners();
- }
-
- return getAllGroupMembers(groupCache.get(groupUUID),
- new HashSet<AccountGroup.Id>());
+ return listAccounts(groupUUID, new HashSet<AccountGroup.UUID>());
}
- private Set<Account> getProjectOwners() throws NoSuchProjectException,
- NoSuchGroupException, OrmException {
+ private Set<Account> listAccounts(final AccountGroup.UUID groupUUID,
+ final Set<AccountGroup.UUID> seen) throws NoSuchGroupException,
+ OrmException, NoSuchProjectException {
+ if (AccountGroup.PROJECT_OWNERS.equals(groupUUID)) {
+ return getProjectOwners(seen);
+ } else {
+ return getGroupMembers(groupCache.get(groupUUID), seen);
+ }
+ }
+
+ private Set<Account> getProjectOwners(final Set<AccountGroup.UUID> seen)
+ throws NoSuchProjectException, NoSuchGroupException, OrmException {
+ seen.add(AccountGroup.PROJECT_OWNERS);
if (project == null) {
return Collections.emptySet();
}
@@ -90,16 +96,17 @@
final HashSet<Account> projectOwners = new HashSet<Account>();
for (final AccountGroup.UUID ownerGroup : ownerGroups) {
- projectOwners.addAll(getAllGroupMembers(groupCache.get(ownerGroup),
- new HashSet<AccountGroup.Id>()));
+ if (!seen.contains(ownerGroup)) {
+ projectOwners.addAll(listAccounts(ownerGroup, seen));
+ }
}
return projectOwners;
}
- private Set<Account> getAllGroupMembers(final AccountGroup group,
- final Set<AccountGroup.Id> seen) throws NoSuchGroupException,
- OrmException {
- seen.add(group.getId());
+ private Set<Account> getGroupMembers(final AccountGroup group,
+ final Set<AccountGroup.UUID> seen) throws NoSuchGroupException,
+ OrmException, NoSuchProjectException {
+ seen.add(group.getGroupUUID());
final GroupDetail groupDetail =
groupDetailFactory.create(group.getId()).call();
@@ -110,10 +117,11 @@
}
}
if (groupDetail.includes != null) {
- for (AccountGroupInclude groupInclude : groupDetail.includes) {
- if (!seen.contains(groupInclude.getIncludeId())) {
- members.addAll(getAllGroupMembers(
- groupCache.get(groupInclude.getIncludeId()), seen));
+ for (final AccountGroupInclude groupInclude : groupDetail.includes) {
+ final AccountGroup includedGroup =
+ groupCache.get(groupInclude.getIncludeId());
+ if (!seen.contains(includedGroup.getGroupUUID())) {
+ members.addAll(listAccounts(includedGroup.getGroupUUID(), seen));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
index e804ce6..72d7cb0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -49,6 +49,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -67,6 +68,7 @@
private final EmailExpander emailExpander;
private final Cache<String, Account.Id> usernameCache;
private final Set<Account.FieldName> readOnlyAccountFields;
+ private final Config config;
private final Cache<String, Set<AccountGroup.UUID>> membershipCache;
@@ -83,6 +85,7 @@
this.emailExpander = emailExpander;
this.usernameCache = usernameCache;
this.membershipCache = membershipCache;
+ this.config = config;
this.readOnlyAccountFields = new HashSet<Account.FieldName>();
@@ -181,6 +184,10 @@
public AuthRequest authenticate(final AuthRequest who)
throws AccountException {
+ if (config.getBoolean("ldap", "localUsernameToLowerCase", false)) {
+ who.setLocalUser(who.getLocalUser().toLowerCase(Locale.US));
+ }
+
final String username = who.getLocalUser();
try {
final DirContext ctx;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 7886ec8..cf79c6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -527,7 +527,7 @@
// Let the core receive process handle it
} else {
- reject(cmd);
+ reject(cmd, "can not create new references");
}
}
@@ -541,7 +541,7 @@
validateNewCommits(ctl, cmd);
// Let the core receive process handle it
} else {
- reject(cmd);
+ reject(cmd, "can not update the reference as a fast forward");
}
}
@@ -569,7 +569,7 @@
if (ctl.canDelete()) {
// Let the core receive process handle it
} else {
- reject(cmd);
+ reject(cmd, "can not delete references");
}
}
@@ -665,7 +665,7 @@
destBranchName.substring(0, split));
destBranchCtl = projectControl.controlForRef(destBranch);
if (!destBranchCtl.canUpload()) {
- reject(cmd);
+ reject(cmd, "can not upload a change to this reference");
return;
}
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 977d209..805549e 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.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.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.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Locale;
import java.util.Set;
/**
@@ -61,17 +64,20 @@
private final SshLog sshLog;
private final IdentifiedUser.GenericFactory userFactory;
private final PeerDaemonUser.Factory peerFactory;
+ private final Config config;
private final Set<PublicKey> 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 @@
}
}
- 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 @@
}
}
+ if (config.getBoolean("auth", "userNameToLowerCase", false)) {
+ username = username.toLowerCase(Locale.US);
+ }
+
final Iterable<SshKeyCacheEntry> keyList = sshKeyCache.get(username);
final SshKeyCacheEntry key = find(keyList, suppliedKey);
if (key == null) {