Merge "Merge branch 'stable-2.11'"
This commit is contained in:
@@ -3406,10 +3406,14 @@ By default 0.
|
||||
+
|
||||
The maximum number of matches evaluated for change access when using full text search.
|
||||
+
|
||||
Making this number too high could have a negative impact on performance.
|
||||
+
|
||||
By default 100.
|
||||
|
||||
[[suggest.fullTextSearchRefresh]]suggest.fullTextSearchRefresh::
|
||||
+
|
||||
Refresh interval for the in-memory account search index.
|
||||
+
|
||||
By default 1 hour.
|
||||
|
||||
|
||||
[[theme]]
|
||||
=== Section theme
|
||||
|
||||
@@ -85,7 +85,7 @@ encryption algorithm is required.
|
||||
If you are encountering 'Page Not Found' errors when opening the change
|
||||
screen, your Apache proxy is very likely decoding the passed URL.
|
||||
Make sure to either use 'AllowEncodedSlashes On' together with
|
||||
'ProxyPass .. nodecode' or alternatively a 'mod_rewrite' configuration with
|
||||
'ProxyPass .. nocanon' or alternatively a 'mod_rewrite' configuration with
|
||||
'AllowEncodedSlashes NoDecode' set.
|
||||
|
||||
|
||||
|
||||
49
ReleaseNotes/ReleaseNotes-2.10.4.txt
Normal file
49
ReleaseNotes/ReleaseNotes-2.10.4.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
Release notes for Gerrit 2.10.4
|
||||
===============================
|
||||
|
||||
There are no schema changes from link:ReleaseNotes-2.10.3.1.html[2.10.3.1].
|
||||
|
||||
Download:
|
||||
link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war[
|
||||
https://gerrit-releases.storage.googleapis.com/gerrit-2.10.4.war]
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
* Support identity linking in hybrid OpenID and OAuth2 authentication.
|
||||
+
|
||||
Linking of user identities accross protocol boundaries and from one OAuth2
|
||||
identity to another OAuth2 identity is supported.
|
||||
|
||||
* Support identity linking in OAuth2 extension point.
|
||||
+
|
||||
Linking of user identities from one OAuth2 identity to another OAuth2
|
||||
identity is supported.
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
* link:https://code.google.com/p/gerrit/issues/detail?id=3300[Issue 3300]:
|
||||
Fix >10x performance degradation for Git push and replication operations.
|
||||
+
|
||||
A link:https://bugs.eclipse.org/bugs/show_bug.cgi?id=465509[regression in jgit]
|
||||
caused a performance degradation.
|
||||
|
||||
* link:https://code.google.com/p/gerrit/issues/detail?id=3312[Issue 3312]:
|
||||
Flush padding on patches downloaded as base64.
|
||||
+
|
||||
The padding was not flushed, which caused the downloaded patch to not be
|
||||
valid base64.
|
||||
|
||||
OAuth extension point
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Check for session validity during logout.
|
||||
+
|
||||
When user was trying to log out, after Gerrit restart, the session was
|
||||
invalidated and IllegalStateException was recorded in the error_log.
|
||||
|
||||
Updates
|
||||
-------
|
||||
|
||||
* Update jgit to 4.0.0.201505050340-m2.
|
||||
84
ReleaseNotes/ReleaseNotes-2.11.1.txt
Normal file
84
ReleaseNotes/ReleaseNotes-2.11.1.txt
Normal file
@@ -0,0 +1,84 @@
|
||||
Release notes for Gerrit 2.11.1
|
||||
===============================
|
||||
|
||||
Gerrit 2.11.1 is now available:
|
||||
|
||||
link:https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war[
|
||||
https://gerrit-releases.storage.googleapis.com/gerrit-2.11.1.war]
|
||||
|
||||
Gerrit 2.11.1 includes the bug fixes done with
|
||||
link:ReleaseNotes-2.10.4.html[Gerrit 2.10.4]. These bug fixes are *not* listed
|
||||
in these release notes.
|
||||
|
||||
There are no schema changes from link:ReleaseNotes-2.11.html[2.11].
|
||||
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=321[Issue 321]:
|
||||
Use in-memory Lucene index for a better reviewer suggestion.
|
||||
+
|
||||
Instead of a linear full text search through a list of accounts use an
|
||||
in-memory Lucene index. The index is periodically refreshed. The refresh period
|
||||
is configurable via the
|
||||
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-gerrit.html#suggest.fullTextSearchRefresh[
|
||||
suggest.fullTextSearchRefresh] parameter.
|
||||
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
* Fix PatchLineCommentsUtil.draftByChangeAuthor.
|
||||
+
|
||||
There is not a native index for this, and the ReviewDb case was not properly
|
||||
filtering a result by change.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3323[Issue 3323]:
|
||||
Fix internal server error when cloning from a slave while hiding some refs.
|
||||
|
||||
* Require 'View Plugins' capability to list plugins through SSH.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3191[Issue 3191]:
|
||||
Always show 'Not Current' as state when looking at old patch set.
|
||||
+
|
||||
For merged changes it was confusing for users to see the status as 'Merged' when
|
||||
they look at an old patch set.
|
||||
|
||||
* Fix project creation with plugin config if user is not project owner.
|
||||
+
|
||||
On project creation it is possible to specify plugin configuration values that
|
||||
should be stored in the `project.config` file. This failed if the calling user
|
||||
was not becoming owner of the created project, because only project owners can
|
||||
edit the `project.config` file.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3342[Issue 3342]:
|
||||
Log IOExceptions on failure to update project configuration.
|
||||
+
|
||||
Without logging these exceptions it's hard to guess why the update of the
|
||||
project configuration is failing.
|
||||
|
||||
* Don't show stack trace when failing to build BloomFilter during reindex.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3337[Issue 3337]:
|
||||
Reenable revert button when revert is cancelled.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3325[Issue 3325]:
|
||||
Add missing `--newrev` parameter to the
|
||||
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-hooks.html#_change_merged[
|
||||
change-merged hook documentation].
|
||||
|
||||
* Fix gc_log when running in a web container.
|
||||
+
|
||||
All logs supposed to be in gc_log file were ending up in main log instead when
|
||||
deploying Gerrit in a web container.
|
||||
|
||||
* link:http://code.google.com/p/gerrit/issues/detail?id=3346[Issue 3346]:
|
||||
Fix typo in the
|
||||
link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.11.1/config-reverseproxy.html[
|
||||
Apache 2 configuration documentation].
|
||||
|
||||
Updates
|
||||
-------
|
||||
|
||||
* Update CodeMirror to 5.0.
|
||||
@@ -486,7 +486,7 @@ before they are applied to the git repository.
|
||||
* Plugins can provide project-aware top menu extensions
|
||||
+
|
||||
Plugins can provide sub-menu items within the 'Projects' context. The
|
||||
'${projectName}' placeholder is replaced by the project name.
|
||||
'$\{projectName\}' placeholder is replaced by the project name.
|
||||
|
||||
* Auto register static/init.js as JavaScript plugin.
|
||||
+
|
||||
|
||||
@@ -4,11 +4,13 @@ Gerrit Code Review - Release Notes
|
||||
[[2_11]]
|
||||
Version 2.11.x
|
||||
--------------
|
||||
* link:ReleaseNotes-2.11.1.html[2.11.1]
|
||||
* link:ReleaseNotes-2.11.html[2.11]
|
||||
|
||||
[[2_10]]
|
||||
Version 2.10.x
|
||||
--------------
|
||||
* link:ReleaseNotes-2.10.4.html[2.10.4]
|
||||
* link:ReleaseNotes-2.10.3.1.html[2.10.3.1]
|
||||
* link:ReleaseNotes-2.10.3.html[2.10.3]
|
||||
* link:ReleaseNotes-2.10.2.html[2.10.2]
|
||||
|
||||
@@ -62,9 +62,9 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
group2 = group("users2");
|
||||
group3 = group("users3");
|
||||
|
||||
user1 = user("user1", group1);
|
||||
user2 = user("user2", group2);
|
||||
user3 = user("user3", group1, group2);
|
||||
user1 = user("user1", "First1 Last1", group1);
|
||||
user2 = user("user2", "First2 Last2", group2);
|
||||
user3 = user("user3", "First3 Last3", group1, group2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +116,7 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
String changeId = createChange().getChangeId();
|
||||
List<SuggestedReviewerInfo> reviewers;
|
||||
|
||||
reviewers = suggestReviewers(changeId, user2.fullName, 2);
|
||||
reviewers = suggestReviewers(changeId, user2.username, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
assertThat(Iterables.getOnlyElement(reviewers).account.name)
|
||||
.isEqualTo(user2.fullName);
|
||||
@@ -126,13 +126,13 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
assertThat(reviewers).isEmpty();
|
||||
|
||||
setApiUser(user2);
|
||||
reviewers = suggestReviewers(changeId, user2.fullName, 2);
|
||||
reviewers = suggestReviewers(changeId, user2.username, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
assertThat(Iterables.getOnlyElement(reviewers).account.name)
|
||||
.isEqualTo(user2.fullName);
|
||||
|
||||
setApiUser(user3);
|
||||
reviewers = suggestReviewers(changeId, user2.fullName, 2);
|
||||
reviewers = suggestReviewers(changeId, user2.username, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
assertThat(Iterables.getOnlyElement(reviewers).account.name)
|
||||
.isEqualTo(user2.fullName);
|
||||
@@ -145,13 +145,13 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
List<SuggestedReviewerInfo> reviewers;
|
||||
|
||||
setApiUser(user1);
|
||||
reviewers = suggestReviewers(changeId, user2.fullName, 2);
|
||||
reviewers = suggestReviewers(changeId, user2.username, 2);
|
||||
assertThat(reviewers).isEmpty();
|
||||
|
||||
setApiUser(user1); // Clear cached group info.
|
||||
allowGlobalCapabilities(group1.getGroupUUID(),
|
||||
GlobalCapability.VIEW_ALL_ACCOUNTS);
|
||||
reviewers = suggestReviewers(changeId, user2.fullName, 2);
|
||||
reviewers = suggestReviewers(changeId, user2.username, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
assertThat(Iterables.getOnlyElement(reviewers).account.name)
|
||||
.isEqualTo(user2.fullName);
|
||||
@@ -170,9 +170,49 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
@GerritConfig(name = "suggest.fullTextSearch", value = "true")
|
||||
public void suggestReviewersFullTextSearch() throws Exception {
|
||||
String changeId = createChange().getChangeId();
|
||||
List<SuggestedReviewerInfo> reviewers =
|
||||
suggestReviewers(changeId, "ser", 5);
|
||||
assertThat(reviewers).hasSize(4);
|
||||
List<SuggestedReviewerInfo> reviewers;
|
||||
|
||||
reviewers = suggestReviewers(changeId, "first", 4);
|
||||
assertThat(reviewers).hasSize(3);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "first1", 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "last", 4);
|
||||
assertThat(reviewers).hasSize(3);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "last1", 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "fi la", 4);
|
||||
assertThat(reviewers).hasSize(3);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "la fi", 4);
|
||||
assertThat(reviewers).hasSize(3);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "first1 la", 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "fi last1", 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "first1 last2", 1);
|
||||
assertThat(reviewers).hasSize(0);
|
||||
|
||||
reviewers = suggestReviewers(changeId, name("user"), 7);
|
||||
assertThat(reviewers).hasSize(6);
|
||||
|
||||
reviewers = suggestReviewers(changeId, user1.username, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, "example.com", 6);
|
||||
assertThat(reviewers).hasSize(5);
|
||||
|
||||
reviewers = suggestReviewers(changeId, user1.email, 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
|
||||
reviewers = suggestReviewers(changeId, user1.username + " example", 2);
|
||||
assertThat(reviewers).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -183,14 +223,14 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
public void suggestReviewersFullTextSearchLimitMaxMatches() throws Exception {
|
||||
String changeId = createChange().getChangeId();
|
||||
List<SuggestedReviewerInfo> reviewers =
|
||||
suggestReviewers(changeId, "ser", 3);
|
||||
suggestReviewers(changeId, name("user"), 2);
|
||||
assertThat(reviewers).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void suggestReviewersWithoutLimitOptionSpecified() throws Exception {
|
||||
String changeId = createChange().getChangeId();
|
||||
String query = user3.fullName;
|
||||
String query = user3.username;
|
||||
List<SuggestedReviewerInfo> suggestedReviewerInfos = gApi.changes()
|
||||
.id(changeId)
|
||||
.suggestReviewers(query)
|
||||
@@ -214,7 +254,8 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
return GroupDescriptions.toAccountGroup(d);
|
||||
}
|
||||
|
||||
private TestAccount user(String name, AccountGroup... groups) throws Exception {
|
||||
private TestAccount user(String name, String fullName, AccountGroup... groups)
|
||||
throws Exception {
|
||||
name = name(name);
|
||||
String[] groupNames = FluentIterable.from(Arrays.asList(groups))
|
||||
.transform(new Function<AccountGroup, String>() {
|
||||
@@ -223,6 +264,6 @@ public class SuggestReviewersIT extends AbstractDaemonTest {
|
||||
return in.getName();
|
||||
}
|
||||
}).toArray(String.class);
|
||||
return accounts.create(name, name + "@example.com", name, groupNames);
|
||||
return accounts.create(name, name + "@example.com", fullName, groupNames);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ public class MyIdentitiesScreen extends SettingsScreen {
|
||||
});
|
||||
add(deleteIdentity);
|
||||
|
||||
if (Gerrit.getConfig().getAuthType() == AuthType.OPENID) {
|
||||
if (Gerrit.getConfig().getAuthType() == AuthType.OPENID
|
||||
|| Gerrit.getConfig().getAuthType() == AuthType.OAUTH) {
|
||||
Button linkIdentity = new Button(Util.C.buttonLinkIdentity());
|
||||
linkIdentity.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
|
||||
@@ -50,7 +50,9 @@ class OAuthLogoutServlet extends HttpLogoutServlet {
|
||||
protected void doLogout(HttpServletRequest req, HttpServletResponse rsp)
|
||||
throws IOException {
|
||||
super.doLogout(req, rsp);
|
||||
oauthSession.get().logout();
|
||||
if (req.getSession(false) != null) {
|
||||
oauthSession.get().logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,11 +26,14 @@ import com.google.gerrit.extensions.restapi.Url;
|
||||
import com.google.gerrit.httpd.CanonicalWebUrl;
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
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.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.servlet.SessionScoped;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -52,18 +55,22 @@ class OAuthSession {
|
||||
private static final SecureRandom randomState = newRandomGenerator();
|
||||
private final String state;
|
||||
private final DynamicItem<WebSession> webSession;
|
||||
private final Provider<IdentifiedUser> identifiedUser;
|
||||
private final AccountManager accountManager;
|
||||
private final CanonicalWebUrl urlProvider;
|
||||
private OAuthServiceProvider serviceProvider;
|
||||
private OAuthToken token;
|
||||
private OAuthUserInfo user;
|
||||
private String redirectToken;
|
||||
private boolean linkMode;
|
||||
|
||||
@Inject
|
||||
OAuthSession(DynamicItem<WebSession> webSession,
|
||||
Provider<IdentifiedUser> identifiedUser,
|
||||
AccountManager accountManager,
|
||||
CanonicalWebUrl urlProvider) {
|
||||
this.state = generateRandomState();
|
||||
this.identifiedUser = identifiedUser;
|
||||
this.webSession = webSession;
|
||||
this.accountManager = accountManager;
|
||||
this.urlProvider = urlProvider;
|
||||
@@ -79,10 +86,6 @@ class OAuthSession {
|
||||
|
||||
boolean login(HttpServletRequest request, HttpServletResponse response,
|
||||
OAuthServiceProvider oauth) throws IOException {
|
||||
if (isLoggedIn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log.debug("Login " + this);
|
||||
|
||||
if (isOAuthFinal(request)) {
|
||||
@@ -122,46 +125,19 @@ class OAuthSession {
|
||||
|
||||
private void authenticateAndRedirect(HttpServletRequest req,
|
||||
HttpServletResponse rsp) throws IOException {
|
||||
com.google.gerrit.server.account.AuthRequest areq =
|
||||
new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
|
||||
AuthRequest areq = new AuthRequest(user.getExternalId());
|
||||
AuthResult arsp;
|
||||
try {
|
||||
String claimedIdentifier = user.getClaimedIdentity();
|
||||
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||
if (!Strings.isNullOrEmpty(claimedIdentifier)) {
|
||||
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||
if (claimedId != null && actualId != null) {
|
||||
if (claimedId.equals(actualId)) {
|
||||
// Both link to the same account, that's what we expected.
|
||||
log.debug("OAuth2: claimed identity equals current id");
|
||||
} else {
|
||||
// This is (for now) a fatal error. There are two records
|
||||
// for what might be the same user.
|
||||
//
|
||||
log.error("OAuth accounts disagree over user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier
|
||||
+ "\n" + " Delgate ID: " + actualId + " is "
|
||||
+ user.getExternalId());
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
} else if (claimedId != null && actualId == null) {
|
||||
// Claimed account already exists: link to it.
|
||||
//
|
||||
log.info("OAuth2: linking claimed identity to {}",
|
||||
claimedId.toString());
|
||||
try {
|
||||
accountManager.link(claimedId, areq);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot link: " + user.getExternalId()
|
||||
+ " to user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier);
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
if (!authenticateWithIdentityClaimedDuringHandshake(areq, rsp,
|
||||
claimedIdentifier)) {
|
||||
return;
|
||||
}
|
||||
} else if (linkMode) {
|
||||
if (!authenticateWithLinkedIdentity(areq, rsp)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
log.debug("OAuth2: claimed identity is empty");
|
||||
}
|
||||
areq.setUserName(user.getUserName());
|
||||
areq.setEmailAddress(user.getEmailAddress());
|
||||
@@ -181,6 +157,59 @@ class OAuthSession {
|
||||
rsp.sendRedirect(rdr.toString());
|
||||
}
|
||||
|
||||
private boolean authenticateWithIdentityClaimedDuringHandshake(
|
||||
AuthRequest req, HttpServletResponse rsp, String claimedIdentifier)
|
||||
throws AccountException, IOException {
|
||||
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||
if (claimedId != null && actualId != null) {
|
||||
if (claimedId.equals(actualId)) {
|
||||
// Both link to the same account, that's what we expected.
|
||||
log.debug("OAuth2: claimed identity equals current id");
|
||||
} else {
|
||||
// This is (for now) a fatal error. There are two records
|
||||
// for what might be the same user.
|
||||
//
|
||||
log.error("OAuth accounts disagree over user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier
|
||||
+ "\n" + " Delgate ID: " + actualId + " is "
|
||||
+ user.getExternalId());
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
} else if (claimedId != null && actualId == null) {
|
||||
// Claimed account already exists: link to it.
|
||||
//
|
||||
log.info("OAuth2: linking claimed identity to {}",
|
||||
claimedId.toString());
|
||||
try {
|
||||
accountManager.link(claimedId, req);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot link: " + user.getExternalId()
|
||||
+ " to user identity:\n"
|
||||
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier);
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean authenticateWithLinkedIdentity(AuthRequest areq,
|
||||
HttpServletResponse rsp) throws AccountException, IOException {
|
||||
try {
|
||||
accountManager.link(identifiedUser.get().getAccountId(), areq);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot link: " + user.getExternalId()
|
||||
+ " to user identity: " + identifiedUser.get().getAccountId());
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return false;
|
||||
} finally {
|
||||
linkMode = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void logout() {
|
||||
token = null;
|
||||
user = null;
|
||||
@@ -224,4 +253,12 @@ class OAuthSession {
|
||||
public OAuthServiceProvider getServiceProvider() {
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
public void setLinkMode(boolean linkMode) {
|
||||
this.linkMode = linkMode;
|
||||
}
|
||||
|
||||
public boolean isLinkMode() {
|
||||
return linkMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.httpd.HtmlDomUtil;
|
||||
import com.google.gerrit.httpd.LoginUrlToken;
|
||||
import com.google.gerrit.httpd.template.SiteHeaderFooter;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
@@ -48,7 +47,6 @@ import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
@Singleton
|
||||
/* OAuth web filter uses active OAuth session to perform OAuth requests */
|
||||
@@ -56,7 +54,6 @@ class OAuthWebFilter implements Filter {
|
||||
static final String GERRIT_LOGIN = "/login";
|
||||
|
||||
private final Provider<String> urlProvider;
|
||||
private final Provider<CurrentUser> currentUserProvider;
|
||||
private final Provider<OAuthSession> oauthSessionProvider;
|
||||
private final DynamicMap<OAuthServiceProvider> oauthServiceProviders;
|
||||
private final SiteHeaderFooter header;
|
||||
@@ -64,12 +61,10 @@ class OAuthWebFilter implements Filter {
|
||||
|
||||
@Inject
|
||||
OAuthWebFilter(@CanonicalWebUrl @Nullable Provider<String> urlProvider,
|
||||
Provider<CurrentUser> currentUserProvider,
|
||||
DynamicMap<OAuthServiceProvider> oauthServiceProviders,
|
||||
Provider<OAuthSession> oauthSessionProvider,
|
||||
SiteHeaderFooter header) {
|
||||
this.urlProvider = urlProvider;
|
||||
this.currentUserProvider = currentUserProvider;
|
||||
this.oauthServiceProviders = oauthServiceProviders;
|
||||
this.oauthSessionProvider = oauthSessionProvider;
|
||||
this.header = header;
|
||||
@@ -88,30 +83,20 @@ class OAuthWebFilter implements Filter {
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
|
||||
OAuthSession oauthSession = oauthSessionProvider.get();
|
||||
if (currentUserProvider.get().isIdentifiedUser()) {
|
||||
if (httpSession != null) {
|
||||
httpSession.invalidate();
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
} else {
|
||||
if (oauthSession.isLoggedIn()) {
|
||||
oauthSession.logout();
|
||||
}
|
||||
}
|
||||
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
OAuthSession oauthSession = oauthSessionProvider.get();
|
||||
if (request.getParameter("link") != null) {
|
||||
oauthSession.setLinkMode(true);
|
||||
oauthSession.setServiceProvider(null);
|
||||
}
|
||||
|
||||
String provider = httpRequest.getParameter("provider");
|
||||
OAuthServiceProvider service = ssoProvider == null
|
||||
? oauthSession.getServiceProvider()
|
||||
: ssoProvider;
|
||||
|
||||
if ((isGerritLogin(httpRequest)
|
||||
|| oauthSession.isOAuthFinal(httpRequest))
|
||||
&& !oauthSession.isLoggedIn()) {
|
||||
if (isGerritLogin(httpRequest) || oauthSession.isOAuthFinal(httpRequest)) {
|
||||
if (service == null && Strings.isNullOrEmpty(provider)) {
|
||||
selectProvider(httpRequest, httpResponse, null);
|
||||
return;
|
||||
|
||||
@@ -175,9 +175,9 @@ class LoginForm extends HttpServlet {
|
||||
oauthSession.logout();
|
||||
}
|
||||
if ((isGerritLogin(req)
|
||||
|| oauthSession.isOAuthFinal(req))
|
||||
&& !oauthSession.isLoggedIn()) {
|
||||
|| oauthSession.isOAuthFinal(req))) {
|
||||
oauthSession.setServiceProvider(oauthProvider);
|
||||
oauthSession.setLinkMode(link);
|
||||
oauthSession.login(req, res, oauthProvider);
|
||||
}
|
||||
}
|
||||
@@ -304,7 +304,7 @@ class LoginForm extends HttpServlet {
|
||||
oauthServiceProviders.byPlugin(pluginName);
|
||||
for (Map.Entry<String, Provider<OAuthServiceProvider>> e
|
||||
: m.entrySet()) {
|
||||
addProvider(providers, pluginName, e.getKey(),
|
||||
addProvider(providers, link, pluginName, e.getKey(),
|
||||
e.getValue().get().getName());
|
||||
}
|
||||
}
|
||||
@@ -327,13 +327,18 @@ class LoginForm extends HttpServlet {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addProvider(Element form, String pluginName,
|
||||
String id, String serviceName) {
|
||||
private static void addProvider(Element form, boolean link,
|
||||
String pluginName, String id, String serviceName) {
|
||||
Element div = form.getOwnerDocument().createElement("div");
|
||||
div.setAttribute("id", id);
|
||||
Element hyperlink = form.getOwnerDocument().createElement("a");
|
||||
hyperlink.setAttribute("href", String.format("?id=%s_%s",
|
||||
StringBuilder u = new StringBuilder(String.format("?id=%s_%s",
|
||||
pluginName, id));
|
||||
if (link) {
|
||||
u.append("&link");
|
||||
}
|
||||
hyperlink.setAttribute("href", u.toString());
|
||||
|
||||
hyperlink.setTextContent(serviceName +
|
||||
" (" + pluginName + " plugin)");
|
||||
div.appendChild(hyperlink);
|
||||
|
||||
@@ -27,11 +27,13 @@ import com.google.gerrit.httpd.CanonicalWebUrl;
|
||||
import com.google.gerrit.httpd.LoginUrlToken;
|
||||
import com.google.gerrit.httpd.WebSession;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.account.AccountException;
|
||||
import com.google.gerrit.server.account.AccountManager;
|
||||
import com.google.gerrit.server.account.AuthResult;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.servlet.SessionScoped;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -55,19 +57,23 @@ class OAuthSessionOverOpenID {
|
||||
private static final SecureRandom randomState = newRandomGenerator();
|
||||
private final String state;
|
||||
private final DynamicItem<WebSession> webSession;
|
||||
private final Provider<IdentifiedUser> identifiedUser;
|
||||
private final AccountManager accountManager;
|
||||
private final CanonicalWebUrl urlProvider;
|
||||
private OAuthServiceProvider serviceProvider;
|
||||
private OAuthToken token;
|
||||
private OAuthUserInfo user;
|
||||
private String redirectToken;
|
||||
private boolean linkMode;
|
||||
|
||||
@Inject
|
||||
OAuthSessionOverOpenID(DynamicItem<WebSession> webSession,
|
||||
Provider<IdentifiedUser> identifiedUser,
|
||||
AccountManager accountManager,
|
||||
CanonicalWebUrl urlProvider) {
|
||||
this.state = generateRandomState();
|
||||
this.webSession = webSession;
|
||||
this.identifiedUser = identifiedUser;
|
||||
this.accountManager = accountManager;
|
||||
this.urlProvider = urlProvider;
|
||||
}
|
||||
@@ -82,10 +88,6 @@ class OAuthSessionOverOpenID {
|
||||
|
||||
boolean login(HttpServletRequest request, HttpServletResponse response,
|
||||
OAuthServiceProvider oauth) throws IOException {
|
||||
if (isLoggedIn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
log.debug("Login " + this);
|
||||
|
||||
if (isOAuthFinal(request)) {
|
||||
@@ -96,7 +98,6 @@ class OAuthSessionOverOpenID {
|
||||
|
||||
log.debug("Login-Retrieve-User " + this);
|
||||
token = oauth.getAccessToken(new OAuthVerifier(request.getParameter("code")));
|
||||
|
||||
user = oauth.getUserInfo(token);
|
||||
|
||||
if (isLoggedIn()) {
|
||||
@@ -124,6 +125,7 @@ class OAuthSessionOverOpenID {
|
||||
try {
|
||||
String claimedIdentifier = user.getClaimedIdentity();
|
||||
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||
// Use case 1: claimed identity was provided during handshake phase
|
||||
if (!Strings.isNullOrEmpty(claimedIdentifier)) {
|
||||
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||
if (claimedId != null && actualId != null) {
|
||||
@@ -153,6 +155,18 @@ class OAuthSessionOverOpenID {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (linkMode) {
|
||||
// Use case 2: link mode activated from the UI
|
||||
try {
|
||||
accountManager.link(identifiedUser.get().getAccountId(), areq);
|
||||
} catch (OrmException e) {
|
||||
log.error("Cannot link: " + user.getExternalId()
|
||||
+ " to user identity: " + identifiedUser.get().getAccountId());
|
||||
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
} finally {
|
||||
linkMode = false;
|
||||
}
|
||||
}
|
||||
areq.setUserName(user.getUserName());
|
||||
areq.setEmailAddress(user.getEmailAddress());
|
||||
@@ -213,4 +227,12 @@ class OAuthSessionOverOpenID {
|
||||
public OAuthServiceProvider getServiceProvider() {
|
||||
return serviceProvider;
|
||||
}
|
||||
|
||||
public void setLinkMode(boolean linkMode) {
|
||||
this.linkMode = linkMode;
|
||||
}
|
||||
|
||||
public boolean isLinkMode() {
|
||||
return linkMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.google.gerrit.httpd.auth.openid;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.extensions.auth.oauth.OAuthServiceProvider;
|
||||
import com.google.gerrit.extensions.registration.DynamicMap;
|
||||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
@@ -34,7 +33,6 @@ import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
||||
/** OAuth web filter uses active OAuth session to perform OAuth requests */
|
||||
@@ -42,16 +40,13 @@ import javax.servlet.http.HttpSession;
|
||||
class OAuthWebFilterOverOpenID implements Filter {
|
||||
static final String GERRIT_LOGIN = "/login";
|
||||
|
||||
private final Provider<CurrentUser> currentUserProvider;
|
||||
private final Provider<OAuthSessionOverOpenID> oauthSessionProvider;
|
||||
private final DynamicMap<OAuthServiceProvider> oauthServiceProviders;
|
||||
private OAuthServiceProvider ssoProvider;
|
||||
|
||||
@Inject
|
||||
OAuthWebFilterOverOpenID(Provider<CurrentUser> currentUserProvider,
|
||||
DynamicMap<OAuthServiceProvider> oauthServiceProviders,
|
||||
OAuthWebFilterOverOpenID(DynamicMap<OAuthServiceProvider> oauthServiceProviders,
|
||||
Provider<OAuthSessionOverOpenID> oauthSessionProvider) {
|
||||
this.currentUserProvider = currentUserProvider;
|
||||
this.oauthServiceProviders = oauthServiceProviders;
|
||||
this.oauthSessionProvider = oauthSessionProvider;
|
||||
}
|
||||
@@ -69,15 +64,6 @@ class OAuthWebFilterOverOpenID implements Filter {
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
HttpSession httpSession = ((HttpServletRequest) request).getSession(false);
|
||||
if (currentUserProvider.get().isIdentifiedUser()) {
|
||||
if (httpSession != null) {
|
||||
httpSession.invalidate();
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
OAuthSessionOverOpenID oauthSession = oauthSessionProvider.get();
|
||||
@@ -85,9 +71,7 @@ class OAuthWebFilterOverOpenID implements Filter {
|
||||
? oauthSession.getServiceProvider()
|
||||
: ssoProvider;
|
||||
|
||||
if ((isGerritLogin(httpRequest)
|
||||
|| oauthSession.isOAuthFinal(httpRequest))
|
||||
&& !oauthSession.isLoggedIn()) {
|
||||
if (isGerritLogin(httpRequest) || oauthSession.isOAuthFinal(httpRequest)) {
|
||||
if (service == null) {
|
||||
throw new IllegalStateException("service is unknown");
|
||||
}
|
||||
|
||||
@@ -14,22 +14,48 @@
|
||||
|
||||
package com.google.gerrit.server.change;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.extensions.common.AccountInfo;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.server.AccountExternalIdAccess;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.account.AccountState;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.analysis.util.CharArraySet;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.IntField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.PrefixQuery;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -44,34 +70,115 @@ import java.util.concurrent.TimeUnit;
|
||||
public class ReviewerSuggestionCache {
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(ReviewerSuggestionCache.class);
|
||||
private final LoadingCache<Boolean, List<Account>> cache;
|
||||
|
||||
private static final String ID = "id";
|
||||
private static final String NAME = "name";
|
||||
private static final String EMAIL = "email";
|
||||
private static final String USERNAME = "username";
|
||||
private static final String[] ALL = {ID, NAME, EMAIL, USERNAME};
|
||||
|
||||
private final LoadingCache<Boolean, IndexSearcher> cache;
|
||||
private final Provider<ReviewDb> db;
|
||||
|
||||
@Inject
|
||||
ReviewerSuggestionCache(final Provider<ReviewDb> dbProvider) {
|
||||
ReviewerSuggestionCache(Provider<ReviewDb> db,
|
||||
@GerritServerConfig Config cfg) {
|
||||
this.db = db;
|
||||
long expiration = ConfigUtil.getTimeUnit(cfg,
|
||||
"suggest", null, "fullTextSearchRefresh",
|
||||
TimeUnit.HOURS.toMillis(1),
|
||||
TimeUnit.MILLISECONDS);
|
||||
this.cache =
|
||||
CacheBuilder.newBuilder().maximumSize(1)
|
||||
.expireAfterWrite(30, TimeUnit.SECONDS)
|
||||
.build(new CacheLoader<Boolean, List<Account>>() {
|
||||
.expireAfterWrite(expiration, TimeUnit.MILLISECONDS)
|
||||
.build(new CacheLoader<Boolean, IndexSearcher>() {
|
||||
@Override
|
||||
public List<Account> load(Boolean key) throws Exception {
|
||||
return ImmutableList.copyOf(Iterables.filter(
|
||||
dbProvider.get().accounts().all(),
|
||||
new Predicate<Account>() {
|
||||
@Override
|
||||
public boolean apply(Account in) {
|
||||
return in.isActive();
|
||||
}
|
||||
}));
|
||||
public IndexSearcher load(Boolean key) throws Exception {
|
||||
return index();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<Account> get() {
|
||||
List<AccountInfo> search(String query, int n) throws IOException {
|
||||
IndexSearcher searcher = get();
|
||||
if (searcher == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<String> segments = Splitter.on(' ').omitEmptyStrings().splitToList(
|
||||
query.toLowerCase());
|
||||
BooleanQuery q = new BooleanQuery();
|
||||
for (String field : ALL) {
|
||||
BooleanQuery and = new BooleanQuery();
|
||||
for (String s : segments) {
|
||||
and.add(new PrefixQuery(new Term(field, s)), Occur.MUST);
|
||||
}
|
||||
q.add(and, Occur.SHOULD);
|
||||
}
|
||||
|
||||
TopDocs results = searcher.search(q, n);
|
||||
ScoreDoc[] hits = results.scoreDocs;
|
||||
|
||||
List<AccountInfo> result = new LinkedList<>();
|
||||
|
||||
for (ScoreDoc h : hits) {
|
||||
Document doc = searcher.doc(h.doc);
|
||||
|
||||
AccountInfo info = new AccountInfo(
|
||||
doc.getField(ID).numericValue().intValue());
|
||||
info.name = doc.get(NAME);
|
||||
info.email = doc.get(EMAIL);
|
||||
info.username = doc.get(USERNAME);
|
||||
result.add(info);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IndexSearcher get() {
|
||||
try {
|
||||
return cache.get(true);
|
||||
} catch (ExecutionException e) {
|
||||
log.warn("Cannot fetch reviewers from cache", e);
|
||||
return Collections.emptyList();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IndexSearcher index() throws IOException, OrmException {
|
||||
RAMDirectory idx = new RAMDirectory();
|
||||
IndexWriterConfig config = new IndexWriterConfig(
|
||||
new StandardAnalyzer(CharArraySet.EMPTY_SET));
|
||||
config.setOpenMode(OpenMode.CREATE);
|
||||
|
||||
try (IndexWriter writer = new IndexWriter(idx, config)) {
|
||||
for (Account a : db.get().accounts().all()) {
|
||||
if (a.isActive()) {
|
||||
addAccount(writer, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new IndexSearcher(DirectoryReader.open(idx));
|
||||
}
|
||||
|
||||
private void addAccount(IndexWriter writer, Account a)
|
||||
throws IOException, OrmException {
|
||||
Document doc = new Document();
|
||||
doc.add(new IntField(ID, a.getId().get(), Store.YES));
|
||||
if (a.getFullName() != null) {
|
||||
doc.add(new TextField(NAME, a.getFullName(), Store.YES));
|
||||
}
|
||||
if (a.getPreferredEmail() != null) {
|
||||
doc.add(new StringField(EMAIL, a.getPreferredEmail().toLowerCase(),
|
||||
Store.YES));
|
||||
doc.add(new TextField(EMAIL, a.getPreferredEmail(), Store.YES));
|
||||
}
|
||||
AccountExternalIdAccess extIdAccess = db.get().accountExternalIds();
|
||||
String username = AccountState.getUserName(
|
||||
extIdAccess.byAccount(a.getId()).toList());
|
||||
if (username != null) {
|
||||
doc.add(new StringField(USERNAME, username, Store.YES));
|
||||
}
|
||||
writer.addDocument(doc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -279,41 +280,20 @@ public class SuggestReviewers implements RestReadView<ChangeResource> {
|
||||
}
|
||||
|
||||
private List<AccountInfo> suggestAccountFullTextSearch(
|
||||
VisibilityControl visibilityControl) throws OrmException {
|
||||
String str = query.toLowerCase();
|
||||
Map<Account.Id, AccountInfo> accountMap = new LinkedHashMap<>();
|
||||
List<Account> fullNameMatches = new ArrayList<>(fullTextMaxMatches);
|
||||
List<Account> emailMatches = new ArrayList<>(fullTextMaxMatches);
|
||||
VisibilityControl visibilityControl) throws IOException, OrmException {
|
||||
List<AccountInfo> results = reviewerSuggestionCache.search(
|
||||
query, fullTextMaxMatches);
|
||||
|
||||
for (Account a : reviewerSuggestionCache.get()) {
|
||||
if (a.getFullName() != null
|
||||
&& a.getFullName().toLowerCase().contains(str)) {
|
||||
fullNameMatches.add(a);
|
||||
} else if (a.getPreferredEmail() != null
|
||||
&& emailMatches.size() < fullTextMaxMatches
|
||||
&& a.getPreferredEmail().toLowerCase().contains(str)) {
|
||||
emailMatches.add(a);
|
||||
}
|
||||
if (fullNameMatches.size() >= fullTextMaxMatches) {
|
||||
break;
|
||||
Iterator<AccountInfo> it = results.iterator();
|
||||
while (it.hasNext()) {
|
||||
Account.Id accountId = new Account.Id(it.next()._accountId);
|
||||
if (!(visibilityControl.isVisibleTo(accountId)
|
||||
&& accountControl.canSee(accountId))) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
for (Account a : fullNameMatches) {
|
||||
addSuggestion(accountMap, a.getId(), visibilityControl);
|
||||
if (accountMap.size() >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (accountMap.size() < limit) {
|
||||
for (Account a : emailMatches) {
|
||||
addSuggestion(accountMap, a.getId(), visibilityControl);
|
||||
if (accountMap.size() >= limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accountLoader.fill();
|
||||
return Lists.newArrayList(accountMap.values());
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private boolean addSuggestion(Map<Account.Id, AccountInfo> map,
|
||||
|
||||
@@ -23,7 +23,7 @@ import com.google.gerrit.sshd.CommandMetaData;
|
||||
import com.google.gerrit.sshd.SshCommand;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
|
||||
@RequiresCapability(GlobalCapability.VIEW_PLUGINS)
|
||||
@CommandMetaData(name = "ls", description = "List the installed plugins",
|
||||
runsAt = MASTER_OR_SLAVE)
|
||||
final class PluginLsCommand extends SshCommand {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
include_defs('//lib/maven.defs')
|
||||
|
||||
REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL.
|
||||
VERS = '3.7.0.201502260915-r.63-gf1a15f7'
|
||||
REPO = MAVEN_CENTRAL # Leave here even if set to MAVEN_CENTRAL.
|
||||
VERS = '4.0.0.201505050340-m2'
|
||||
|
||||
maven_jar(
|
||||
name = 'jgit',
|
||||
id = 'org.eclipse.jgit:org.eclipse.jgit:' + VERS,
|
||||
bin_sha1 = 'f0690b06d4270cc823b03ca88fb996897edbc410',
|
||||
src_sha1 = 'c0b8232d4c5e8422198b35a90409c9826af68fa3',
|
||||
bin_sha1 = '1cc3120d39ed2b55584e631634e65c5d2e6c1cf7',
|
||||
src_sha1 = '425f578cc9d5ccb8f3b050a5ab1e2d7a0becb25d',
|
||||
license = 'jgit',
|
||||
repository = REPO,
|
||||
unsign = True,
|
||||
@@ -22,7 +22,7 @@ maven_jar(
|
||||
maven_jar(
|
||||
name = 'jgit-servlet',
|
||||
id = 'org.eclipse.jgit:org.eclipse.jgit.http.server:' + VERS,
|
||||
sha1 = '1284d550981a037ffe92441faf5d16cdfc09396d',
|
||||
sha1 = '2a9f55d1d92afef795542b995db6ab261007857f',
|
||||
license = 'jgit',
|
||||
repository = REPO,
|
||||
deps = [':jgit'],
|
||||
@@ -36,7 +36,7 @@ maven_jar(
|
||||
maven_jar(
|
||||
name = 'jgit-archive',
|
||||
id = 'org.eclipse.jgit:org.eclipse.jgit.archive:' + VERS,
|
||||
sha1 = '6d96c9c27cb85c6db165b160fb0e8038b66b764a',
|
||||
sha1 = 'ee3954753067818f8f734981a01c13ac33425f2c',
|
||||
license = 'jgit',
|
||||
repository = REPO,
|
||||
deps = [':jgit',
|
||||
@@ -53,7 +53,7 @@ maven_jar(
|
||||
maven_jar(
|
||||
name = 'junit',
|
||||
id = 'org.eclipse.jgit:org.eclipse.jgit.junit:' + VERS,
|
||||
sha1 = '48e25624fda973f2e9b7fa70a5204ee7a72e1bb3',
|
||||
sha1 = '6cc19f8f0a1791e26d4225625ecba6a31d9b830e',
|
||||
license = 'DO_NOT_DISTRIBUTE',
|
||||
repository = REPO,
|
||||
unsign = True,
|
||||
|
||||
Reference in New Issue
Block a user