Merge "Use page-error in gr-repo-detail-list and gr-plugin-list"
diff --git a/Documentation/cmd-index-start.txt b/Documentation/cmd-index-start.txt
index 769360d..5a002f3 100644
--- a/Documentation/cmd-index-start.txt
+++ b/Documentation/cmd-index-start.txt
@@ -34,6 +34,8 @@
Currently supported values:
* changes
* accounts
+ * groups
+ * projects
--force::
Force an online re-index.
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index de73930..cc485d5 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2441,6 +2441,21 @@
+
By default, true.
+[[httpd.gracefulStopTimeout]]httpd.gracefulStopTimeout::
++
+Set a graceful stop time. If set, the daemon ensures that all incoming
+calls are preserved for a maximum period of time, before starting
+the graceful shutdown process. Sites behind a workload balancer such as
+HAProxy would need this to be set for avoiding serving errors during
+rolling restarts.
++
+Values should use common unit suffixes to express their setting:
++
+* s, sec, second, seconds
+* m, min, minute, minutes
++
+By default, 0 seconds (immediate shutdown).
+
[[httpd.inheritChannel]]httpd.inheritChannel::
+
If true, permits the daemon to inherit its server socket channel
@@ -2760,7 +2775,7 @@
simultaneous writes that may cause one of the writes to not be reflected in the
index. The check to avoid this does consume some resources.
+
-Defaults to true.
+Defaults to false.
[[index.scheduledIndexer]]
==== Subsection index.scheduledIndexer
@@ -4032,7 +4047,7 @@
Only used when `sendemail.from` is set to `USER`.
List of allowed domains. If user's email matches one of the domains, emails will
be sent as USER, otherwise as MIXED mode. Wildcards may be specified by
-including `*` to match any number of characters, for example `*.example.com`
+including `\*` to match any number of characters, for example `*.example.com`
matches any subdomain of `example.com`.
+
By default, `*`.
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 84e4062..6272b54 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -217,9 +217,9 @@
* `AnyWithBlock`
+
-The lowest possible negative value, if present, blocks a submit, Any
-other value enables a submit. To permit blocking submits, ensure
-that a negative value is defined.
+The label is not mandatory but the lowest possible negative value,
+if present, blocks a submit. To permit blocking submits, ensure that a
+negative value is defined.
* `MaxNoBlock`
+
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt
index 2d0812e..de5171c 100644
--- a/Documentation/intro-project-owner.txt
+++ b/Documentation/intro-project-owner.txt
@@ -424,10 +424,13 @@
As project owner you can administrate the branches of your project in
the Gerrit Web UI under `Projects` > `List` > <your project> >
-`Branches`. In the Web UI both link:project-configuration.html#branch-creation[
-branch creation] and link:project-configuration.html#branch-deletion[branch
-deletion] are allowed for project owners without requiring any
-additional access rights.
+`Branches`. In the Web UI link:project-configuration.html#branch-creation[
+branch creation] is allowed if you have
+link:access-control.html#category_create[Create Reference] access right and
+link:project-configuration.html#branch-deletion[branch deletion] is allowed if
+you have the link:access-control.html#category_delete[Delete Reference] or the
+link:access-control.html#category_push[Push] access right with the `force`
+option.
By setting `HEAD` on the project you can define its
link:project-configuration.html#default-branch[default branch]. For convenience
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 3805c80..6ce7f1f 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -19,7 +19,7 @@
[[tools]]
== Tools
-Gerrit speaks the git protocol. This means in order to work with Gerrit
+Gerrit uses the git protocol. This means in order to work with Gerrit
you do *not* need to install any Gerrit client, but having a regular
git client, such as the link:http://git-scm.com/[git command line] or
link:http://eclipse.org/egit/[EGit] in Eclipse, is sufficient.
diff --git a/Documentation/json.txt b/Documentation/json.txt
index 2b34185..7360bd4 100644
--- a/Documentation/json.txt
+++ b/Documentation/json.txt
@@ -157,7 +157,8 @@
oldRev:: The old value of the ref, prior to the update.
-newRev:: The new value the ref was updated to.
+newRev:: The new value the ref was updated to. Zero value (`0000000000000000000000000000000000000000`)
+indicates that the ref was deleted.
refName:: Full ref name within project.
diff --git a/Documentation/project-configuration.txt b/Documentation/project-configuration.txt
index 5c79c1b..f76b5e4 100644
--- a/Documentation/project-configuration.txt
+++ b/Documentation/project-configuration.txt
@@ -257,9 +257,7 @@
To be able to create new branches the user must have the
link:access-control.html#category_create[Create Reference] access
-right. In addition, project owners and Gerrit administrators can create
-new branches from the Web UI or via REST even without having the
-`Create Reference` access right.
+right.
When using the Web UI, the REST endpoint or the SSH command it is only
possible to create branches on commits that already exist in the
@@ -295,9 +293,7 @@
To be able to delete branches, the user must have the
link:access-control.html#category_delete[Delete Reference] or the
link:access-control.html#category_push[Push] access right with the
-`force` option. In addition, project owners and Gerrit administrators
-can delete branches from the Web UI or via REST even without having the
-`Force Push` access right.
+`force` option.
[[default-branch]]
=== Default Branch
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index 49d5ee5..00fd81f 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -1549,8 +1549,11 @@
|Field Name ||Description
|`id` ||The URL encoded UUID of the group.
|`name` |
-not set if returned in a map where the group name is used as map key|
-The name of the group.
+optional, not set if returned in a map where the group name is used as map key|
+The name of the group. +
+For external groups the group name is missing if there is no group
+backend that can resolve the group UUID. E.g. this can happen when a
+plugin that provided a group backend was uninstalled.
|`url` |optional|
URL to information about the group. Typically a URL to a web page that
permits users to apply to join the group, or manage their membership.
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 0f030a6..2ad8cbd 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -4,9 +4,7 @@
Most basic searches can be viewed by clicking on a link along the top
menu bar. The link will prefill the search box with a common search
-query, execute it, and present the results. If exactly one change
-matches the search, the change will be presented instead of a list.
-
+query, execute it, and present the results.
[options="header"]
|=================================================
@@ -14,7 +12,7 @@
|All > Open | status:open '(or is:open)'
|All > Merged | status:merged
|All > Abandoned | status:abandoned
-|My > Watched Changes | status:open is:watched
+|My > Watched Changes | is:watched is:open
|My > Starred Changes | is:starred
|My > Draft Comments | has:draft
|Open changes in Foo | status:open project:Foo
@@ -36,6 +34,9 @@
|Approval requirement | Code-Review>=+2, Verified=1
|=============================================================
+For change searches (i.e. those using a numerical id, Change-Id, or commit
+SHA1), if the search results in a single change that change will be
+presented instead of a list.
[[search-operators]]
== Search Operators
@@ -601,7 +602,7 @@
link:#submittable[submittable:ok].)
`is:open (label:Verified-1 OR label:Code-Review-2)`::
-`is:open (label:Verified=reject OR label:Code-Review:reject)`::
+`is:open (label:Verified=reject OR label:Code-Review=reject)`::
+
Changes that are blocked from submission due to a blocking score.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java
index b056afa..5e38a14 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupAuditLogScreen.java
@@ -113,17 +113,19 @@
GroupInfo member = auditEvent.memberAsGroup();
if (AccountGroup.isInternalGroup(member.getGroupUUID())) {
table.setWidget(
- row, 3, new Hyperlink(member.name(), Dispatcher.toGroup(member.getGroupUUID())));
+ row,
+ 3,
+ new Hyperlink(formatGroup(member), Dispatcher.toGroup(member.getGroupUUID())));
fmt.getElement(row, 3).setTitle(null);
} else if (member.url() != null) {
Anchor a = new Anchor();
- a.setText(member.name());
+ a.setText(formatGroup(member));
a.setHref(member.url());
a.setTitle("UUID " + member.getGroupUUID().get());
table.setWidget(row, 3, a);
fmt.getElement(row, 3).setTitle(null);
} else {
- table.setText(row, 3, member.name());
+ table.setText(row, 3, formatGroup(member));
fmt.getElement(row, 3).setTitle("UUID " + member.getGroupUUID().get());
}
break;
@@ -148,4 +150,10 @@
b.append(")");
return b.toString();
}
+
+ private static String formatGroup(GroupInfo group) {
+ return group.name() != null && !group.name().isEmpty()
+ ? group.name()
+ : group.getGroupUUID().get();
+ }
}
diff --git a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 54ba802..6233dbb 100644
--- a/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -360,8 +360,8 @@
accountIndexer.index(accountId);
}
- private void reindexAllGroups() throws OrmException, IOException, ConfigInvalidException {
- Iterable<GroupReference> allGroups = groups.getAllGroupReferences(db)::iterator;
+ private void reindexAllGroups() throws IOException, ConfigInvalidException {
+ Iterable<GroupReference> allGroups = groups.getAllGroupReferences()::iterator;
for (GroupReference group : allGroups) {
groupIndexer.index(group.getUUID());
}
diff --git a/java/com/google/gerrit/acceptance/AccountCreator.java b/java/com/google/gerrit/acceptance/AccountCreator.java
index dc5e59d..c6e03a8 100644
--- a/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -23,7 +23,6 @@
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.AccountsUpdate;
@@ -35,7 +34,6 @@
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -57,7 +55,6 @@
public class AccountCreator {
private final Map<String, TestAccount> accounts;
- private final SchemaFactory<ReviewDb> reviewDbProvider;
private final Sequences sequences;
private final Provider<AccountsUpdate> accountsUpdateProvider;
private final VersionedAuthorizedKeys.Accessor authorizedKeys;
@@ -68,7 +65,6 @@
@Inject
AccountCreator(
- SchemaFactory<ReviewDb> schema,
Sequences sequences,
@ServerInitiated Provider<AccountsUpdate> accountsUpdateProvider,
VersionedAuthorizedKeys.Accessor authorizedKeys,
@@ -77,7 +73,6 @@
SshKeyCache sshKeyCache,
@SshEnabled boolean sshEnabled) {
accounts = new HashMap<>();
- reviewDbProvider = schema;
this.sequences = sequences;
this.accountsUpdateProvider = accountsUpdateProvider;
this.authorizedKeys = authorizedKeys;
@@ -98,51 +93,49 @@
if (account != null) {
return account;
}
- try (ReviewDb db = reviewDbProvider.open()) {
- Account.Id id = new Account.Id(sequences.nextAccountId());
+ Account.Id id = new Account.Id(sequences.nextAccountId());
- List<ExternalId> extIds = new ArrayList<>(2);
- String httpPass = null;
- if (username != null) {
- httpPass = "http-pass";
- extIds.add(ExternalId.createUsername(username, id, httpPass));
- }
-
- if (email != null) {
- extIds.add(ExternalId.createEmail(id, email));
- }
-
- accountsUpdateProvider
- .get()
- .insert(
- "Create Test Account",
- id,
- u -> u.setFullName(fullName).setPreferredEmail(email).addExternalIds(extIds));
-
- if (groupNames != null) {
- for (String n : groupNames) {
- AccountGroup.NameKey k = new AccountGroup.NameKey(n);
- Optional<InternalGroup> group = groupCache.get(k);
- if (!group.isPresent()) {
- throw new NoSuchGroupException(n);
- }
- addGroupMember(db, group.get().getGroupUUID(), id);
- }
- }
-
- KeyPair sshKey = null;
- if (sshEnabled && username != null) {
- sshKey = genSshKey();
- authorizedKeys.addKey(id, publicKey(sshKey, email));
- sshKeyCache.evict(username);
- }
-
- account = new TestAccount(id, username, email, fullName, sshKey, httpPass);
- if (username != null) {
- accounts.put(username, account);
- }
- return account;
+ List<ExternalId> extIds = new ArrayList<>(2);
+ String httpPass = null;
+ if (username != null) {
+ httpPass = "http-pass";
+ extIds.add(ExternalId.createUsername(username, id, httpPass));
}
+
+ if (email != null) {
+ extIds.add(ExternalId.createEmail(id, email));
+ }
+
+ accountsUpdateProvider
+ .get()
+ .insert(
+ "Create Test Account",
+ id,
+ u -> u.setFullName(fullName).setPreferredEmail(email).addExternalIds(extIds));
+
+ if (groupNames != null) {
+ for (String n : groupNames) {
+ AccountGroup.NameKey k = new AccountGroup.NameKey(n);
+ Optional<InternalGroup> group = groupCache.get(k);
+ if (!group.isPresent()) {
+ throw new NoSuchGroupException(n);
+ }
+ addGroupMember(group.get().getGroupUUID(), id);
+ }
+ }
+
+ KeyPair sshKey = null;
+ if (sshEnabled && username != null) {
+ sshKey = genSshKey();
+ authorizedKeys.addKey(id, publicKey(sshKey, email));
+ sshKeyCache.evict(username);
+ }
+
+ account = new TestAccount(id, username, email, fullName, sshKey, httpPass);
+ if (username != null) {
+ accounts.put(username, account);
+ }
+ return account;
}
public TestAccount create(@Nullable String username, String group) throws Exception {
@@ -193,12 +186,12 @@
return out.toString(US_ASCII.name()).trim();
}
- private void addGroupMember(ReviewDb db, AccountGroup.UUID groupUuid, Account.Id accountId)
+ private void addGroupMember(AccountGroup.UUID groupUuid, Account.Id accountId)
throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
.build();
- groupsUpdateProvider.get().updateGroup(db, groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
}
}
diff --git a/java/com/google/gerrit/acceptance/GerritServer.java b/java/com/google/gerrit/acceptance/GerritServer.java
index 37ef20a..c056454 100644
--- a/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/java/com/google/gerrit/acceptance/GerritServer.java
@@ -36,7 +36,6 @@
import com.google.gerrit.server.util.SocketUtil;
import com.google.gerrit.server.util.SystemLog;
import com.google.gerrit.testing.FakeEmailSender;
-import com.google.gerrit.testing.GroupNoteDbMode;
import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryRepositoryManager;
import com.google.gerrit.testing.NoteDbChecker;
@@ -419,7 +418,6 @@
cfg.setBoolean("index", null, "reindexAfterRefUpdate", false);
NoteDbMode.newNotesMigrationFromEnv().setConfigValues(cfg);
- GroupNoteDbMode.get().getGroupsMigration().setConfigValuesIfNotSetYet(cfg);
}
private static Injector createTestInjector(Daemon daemon) throws Exception {
diff --git a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
index 58dfa94..83a3874 100644
--- a/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
+++ b/java/com/google/gerrit/acceptance/InMemoryTestingDatabaseModule.java
@@ -29,7 +29,6 @@
import com.google.gerrit.server.config.TrackingFootersProvider;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeBundleReader;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.GwtormChangeBundleReader;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.schema.DataSourceType;
@@ -93,7 +92,6 @@
bind(DataSourceType.class).to(InMemoryH2Type.class);
install(new NotesMigration.Module());
- install(new GroupsMigration.Module());
TypeLiteral<SchemaFactory<ReviewDb>> schemaFactory =
new TypeLiteral<SchemaFactory<ReviewDb>>() {};
bind(schemaFactory).to(NotesMigrationSchemaFactory.class);
diff --git a/java/com/google/gerrit/acceptance/InProcessProtocol.java b/java/com/google/gerrit/acceptance/InProcessProtocol.java
index 24d4b6b..7e2796a 100644
--- a/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -208,7 +208,6 @@
}
private static class Upload implements UploadPackFactory<Context> {
- private final Provider<CurrentUser> userProvider;
private final TransferConfig transferConfig;
private final DynamicSet<UploadPackInitializer> uploadPackInitializers;
private final DynamicSet<PreUploadHook> preUploadHooks;
@@ -219,7 +218,6 @@
@Inject
Upload(
- Provider<CurrentUser> userProvider,
TransferConfig transferConfig,
DynamicSet<UploadPackInitializer> uploadPackInitializers,
DynamicSet<PreUploadHook> preUploadHooks,
@@ -227,7 +225,6 @@
ThreadLocalRequestContext threadContext,
ProjectCache projectCache,
PermissionBackend permissionBackend) {
- this.userProvider = userProvider;
this.transferConfig = transferConfig;
this.uploadPackInitializers = uploadPackInitializers;
this.preUploadHooks = preUploadHooks;
@@ -246,7 +243,7 @@
threadContext.setContext(req);
current.set(req);
- PermissionBackend.ForProject perm = permissionBackend.user(userProvider).project(req.project);
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(req.project);
try {
perm.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
@@ -318,7 +315,7 @@
current.set(req);
try {
permissionBackend
- .user(userProvider)
+ .currentUser()
.project(req.project)
.check(ProjectPermission.RUN_RECEIVE_PACK);
} catch (AuthException e) {
diff --git a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
index 09ffe9d..b754351 100644
--- a/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
+++ b/java/com/google/gerrit/acceptance/StandaloneSiteTest.java
@@ -35,9 +35,13 @@
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
+import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+import org.eclipse.jgit.util.SystemReader;
import org.junit.Rule;
import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
@@ -109,8 +113,12 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- beforeTest(description);
- base.evaluate();
+ try {
+ beforeTest(description);
+ base.evaluate();
+ } finally {
+ afterTest();
+ }
}
};
}
@@ -122,13 +130,65 @@
protected Account.Id adminId;
private GerritServer.Description serverDesc;
+ private SystemReader oldSystemReader;
private void beforeTest(Description description) throws Exception {
+ // SystemReader must be overridden before creating any repos, since they read the user/system
+ // configs at initialization time, and are then stored in the RepositoryCache forever.
+ oldSystemReader = setFakeSystemReader(tempSiteDir.getRoot());
+
serverDesc = GerritServer.Description.forTestMethod(description, configName);
sitePaths = new SitePaths(tempSiteDir.getRoot().toPath());
GerritServer.init(serverDesc, baseConfig, sitePaths.site_path);
}
+ private static SystemReader setFakeSystemReader(File tempDir) {
+ SystemReader oldSystemReader = SystemReader.getInstance();
+ SystemReader.setInstance(
+ new SystemReader() {
+ @Override
+ public String getHostname() {
+ return oldSystemReader.getHostname();
+ }
+
+ @Override
+ public String getenv(String variable) {
+ return oldSystemReader.getenv(variable);
+ }
+
+ @Override
+ public String getProperty(String key) {
+ return oldSystemReader.getProperty(key);
+ }
+
+ @Override
+ public FileBasedConfig openUserConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "user.config"), FS.detect());
+ }
+
+ @Override
+ public FileBasedConfig openSystemConfig(Config parent, FS fs) {
+ return new FileBasedConfig(parent, new File(tempDir, "system.config"), FS.detect());
+ }
+
+ @Override
+ public long getCurrentTime() {
+ return oldSystemReader.getCurrentTime();
+ }
+
+ @Override
+ public int getTimezone(long when) {
+ return oldSystemReader.getTimezone(when);
+ }
+ });
+ return oldSystemReader;
+ }
+
+ private void afterTest() throws Exception {
+ SystemReader.setInstance(oldSystemReader);
+ oldSystemReader = null;
+ }
+
protected ServerContext startServer() throws Exception {
return startServer(null);
}
@@ -153,7 +213,10 @@
}
protected static void runGerrit(String... args) throws Exception {
- assertThat(GerritLauncher.mainImpl(args))
+ // Use invokeProgram with the current classloader, rather than mainImpl, which would create a
+ // new classloader. This is necessary so that static state, particularly the SystemReader, is
+ // shared with the test method.
+ assertThat(GerritLauncher.invokeProgram(StandaloneSiteTest.class.getClassLoader(), args))
.named("gerrit.war " + Arrays.stream(args).collect(joining(" ")))
.isEqualTo(0);
}
diff --git a/java/com/google/gerrit/common/data/LabelFunction.java b/java/com/google/gerrit/common/data/LabelFunction.java
index 0ce2c29..7d13c70 100644
--- a/java/com/google/gerrit/common/data/LabelFunction.java
+++ b/java/com/google/gerrit/common/data/LabelFunction.java
@@ -15,6 +15,7 @@
package com.google.gerrit.common.data;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -27,15 +28,15 @@
* rules, in which case the choice of function in the project config is ignored.
*
* <p>Function semantics are documented in {@code config-labels.txt}, and actual behavior is
- * implemented in Prolog in {@code gerrit_common.pl}.
+ * implemented both in Prolog in {@code gerrit_common.pl} and in the {@link #check} method.
*/
public enum LabelFunction {
- MAX_WITH_BLOCK("MaxWithBlock", true),
- ANY_WITH_BLOCK("AnyWithBlock", true),
- MAX_NO_BLOCK("MaxNoBlock", false),
- NO_BLOCK("NoBlock", false),
- NO_OP("NoOp", false),
- PATCH_SET_LOCK("PatchSetLock", false);
+ ANY_WITH_BLOCK("AnyWithBlock", true, false, false),
+ MAX_WITH_BLOCK("MaxWithBlock", true, true, true),
+ MAX_NO_BLOCK("MaxNoBlock", false, true, true),
+ NO_BLOCK("NoBlock"),
+ NO_OP("NoOp"),
+ PATCH_SET_LOCK("PatchSetLock");
public static final Map<String, LabelFunction> ALL;
@@ -53,10 +54,18 @@
private final String name;
private final boolean isBlock;
+ private final boolean isRequired;
+ private final boolean requiresMaxValue;
- private LabelFunction(String name, boolean isBlock) {
+ LabelFunction(String name) {
+ this(name, false, false, false);
+ }
+
+ LabelFunction(String name, boolean isBlock, boolean isRequired, boolean requiresMaxValue) {
this.name = name;
this.isBlock = isBlock;
+ this.isRequired = isRequired;
+ this.requiresMaxValue = requiresMaxValue;
}
/** The function name as defined in documentation and {@code project.config}. */
@@ -68,4 +77,47 @@
public boolean isBlock() {
return isBlock;
}
+
+ /** Whether the label is a mandatory label, meaning absence of votes will prevent submission. */
+ public boolean isRequired() {
+ return isRequired;
+ }
+
+ /** Whether the label requires a vote with the maximum value to allow submission. */
+ public boolean isMaxValueRequired() {
+ return requiresMaxValue;
+ }
+
+ public SubmitRecord.Label check(LabelType labelType, Iterable<PatchSetApproval> approvals) {
+ SubmitRecord.Label submitRecordLabel = new SubmitRecord.Label();
+ submitRecordLabel.label = labelType.getName();
+
+ submitRecordLabel.status = SubmitRecord.Label.Status.MAY;
+ if (isRequired) {
+ submitRecordLabel.status = SubmitRecord.Label.Status.NEED;
+ }
+
+ for (PatchSetApproval a : approvals) {
+ if (a.getValue() == 0) {
+ continue;
+ }
+
+ if (isBlock && labelType.isMaxNegative(a)) {
+ submitRecordLabel.appliedBy = a.getAccountId();
+ submitRecordLabel.status = SubmitRecord.Label.Status.REJECT;
+ return submitRecordLabel;
+ }
+
+ if (labelType.isMaxPositive(a) || !requiresMaxValue) {
+ submitRecordLabel.appliedBy = a.getAccountId();
+
+ submitRecordLabel.status = SubmitRecord.Label.Status.MAY;
+ if (isRequired) {
+ submitRecordLabel.status = SubmitRecord.Label.Status.OK;
+ }
+ }
+ }
+
+ return submitRecordLabel;
+ }
}
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 5bdd9ca..739726e 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -184,7 +184,7 @@
try {
Project.NameKey nameKey = new Project.NameKey(projectName);
ProjectState state = projectCache.checkedGet(nameKey);
- if (state == null) {
+ if (state == null || !state.statePermitsRead()) {
throw new RepositoryNotFoundException(nameKey.get());
}
req.setAttribute(ATT_STATE, state);
@@ -241,16 +241,12 @@
static class UploadFilter implements Filter {
private final UploadValidators.Factory uploadValidatorsFactory;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> userProvider;
@Inject
UploadFilter(
- UploadValidators.Factory uploadValidatorsFactory,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> userProvider) {
+ UploadValidators.Factory uploadValidatorsFactory, PermissionBackend permissionBackend) {
this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
- this.userProvider = userProvider;
}
@Override
@@ -261,7 +257,7 @@
ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER);
PermissionBackend.ForProject perm =
- permissionBackend.user(userProvider).project(state.getNameKey());
+ permissionBackend.currentUser().project(state.getNameKey());
try {
perm.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
@@ -356,7 +352,7 @@
Capable s;
try {
permissionBackend
- .user(userProvider)
+ .currentUser()
.project(state.getNameKey())
.check(ProjectPermission.RUN_RECEIVE_PACK);
s = arc.canUpload();
diff --git a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
index b39f027..ba2a063 100644
--- a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
+++ b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java
@@ -112,11 +112,11 @@
GitRepositoryManager repoManager,
ProjectCache projectCache,
PermissionBackend permissionBackend,
- Provider<AnonymousUser> anonymousUserProvider,
Provider<CurrentUser> userProvider,
SitePaths site,
@GerritServerConfig Config cfg,
SshInfo sshInfo,
+ Provider<AnonymousUser> anonymousUserProvider,
GitwebConfig gitwebConfig,
GitwebCgiConfig gitwebCgiConfig)
throws IOException {
@@ -423,7 +423,10 @@
}
projectState.checkStatePermitsRead();
- permissionBackend.user(userProvider).project(nameKey).check(ProjectPermission.READ);
+ permissionBackend
+ .user(anonymousUserProvider.get())
+ .project(nameKey)
+ .check(ProjectPermission.READ);
} catch (AuthException e) {
sendErrorOrRedirect(req, rsp, HttpServletResponse.SC_NOT_FOUND);
return;
@@ -584,7 +587,7 @@
if (projectState.statePermitsRead()
&& permissionBackend
- .user(anonymousUserProvider)
+ .user(anonymousUserProvider.get())
.project(nameKey)
.testOrFalse(ProjectPermission.READ)) {
env.set("GERRIT_ANONYMOUS_READ", "1");
diff --git a/java/com/google/gerrit/httpd/init/WebAppInitializer.java b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
index bf1dd35..bac3fd0 100644
--- a/java/com/google/gerrit/httpd/init/WebAppInitializer.java
+++ b/java/com/google/gerrit/httpd/init/WebAppInitializer.java
@@ -71,7 +71,6 @@
import com.google.gerrit.server.mail.receive.MailReceiver;
import com.google.gerrit.server.mail.send.SmtpEmailSender;
import com.google.gerrit.server.mime.MimeUtil2Module;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.permissions.DefaultPermissionBackendModule;
@@ -309,7 +308,6 @@
}
modules.add(new DatabaseModule());
modules.add(new NotesMigration.Module());
- modules.add(new GroupsMigration.Module());
modules.add(new DropWizardMetricMaker.ApiModule());
return Guice.createInjector(PRODUCTION, modules);
}
diff --git a/java/com/google/gerrit/httpd/raw/CatServlet.java b/java/com/google/gerrit/httpd/raw/CatServlet.java
index 013c8e9..4b5c227 100644
--- a/java/com/google/gerrit/httpd/raw/CatServlet.java
+++ b/java/com/google/gerrit/httpd/raw/CatServlet.java
@@ -21,7 +21,6 @@
import com.google.gerrit.reviewdb.client.Patch;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
@@ -54,7 +53,6 @@
@Singleton
public class CatServlet extends HttpServlet {
private final Provider<ReviewDb> requestDb;
- private final Provider<CurrentUser> userProvider;
private final ChangeEditUtil changeEditUtil;
private final PatchSetUtil psUtil;
private final ChangeNotes.Factory changeNotesFactory;
@@ -64,14 +62,12 @@
@Inject
CatServlet(
Provider<ReviewDb> sf,
- Provider<CurrentUser> usrprv,
ChangeEditUtil ceu,
PatchSetUtil psu,
ChangeNotes.Factory cnf,
PermissionBackend pb,
ProjectCache pc) {
requestDb = sf;
- userProvider = usrprv;
changeEditUtil = ceu;
psUtil = psu;
changeNotesFactory = cnf;
@@ -132,7 +128,7 @@
try {
ChangeNotes notes = changeNotesFactory.createChecked(changeId);
permissionBackend
- .user(userProvider)
+ .currentUser()
.change(notes)
.database(requestDb)
.check(ChangePermission.READ);
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 131dbf9..515624f 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -295,7 +295,7 @@
RestCollection<RestResource, RestResource> rc = members.get();
globals
.permissionBackend
- .user(globals.currentUser)
+ .user(globals.currentUser.get())
.checkAny(GlobalPermission.fromAnnotation(rc.getClass()));
viewData = new ViewData(null, null);
@@ -839,10 +839,10 @@
}
Object obj = createInstance(type);
- Field[] fields = obj.getClass().getDeclaredFields();
- if (fields.length == 0 && Strings.isNullOrEmpty(value)) {
+ if (Strings.isNullOrEmpty(value)) {
return obj;
}
+ Field[] fields = obj.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getAnnotation(DefaultInput.class) != null && f.getType() == String.class) {
f.setAccessible(true);
@@ -1189,7 +1189,7 @@
throws AuthException, PermissionBackendException {
globals
.permissionBackend
- .user(globals.currentUser)
+ .user(globals.currentUser.get())
.checkAny(GlobalPermission.fromAnnotation(d.pluginName, d.view.getClass()));
}
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
index 48e06e9..f117b24 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -35,7 +35,6 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupControl;
@@ -51,7 +50,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.ArrayList;
@@ -70,7 +68,6 @@
private final GroupBackend groupBackend;
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final GroupControl.Factory groupControlFactory;
private final MetaDataUpdate.Server metaDataUpdateFactory;
private final AllProjectsName allProjectsName;
@@ -83,7 +80,6 @@
GroupBackend groupBackend,
ProjectCache projectCache,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
GroupControl.Factory groupControlFactory,
MetaDataUpdate.Server metaDataUpdateFactory,
AllProjectsName allProjectsName,
@@ -92,7 +88,6 @@
this.groupBackend = groupBackend;
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
- this.user = user;
this.groupControlFactory = groupControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.allProjectsName = allProjectsName;
@@ -131,7 +126,7 @@
List<AccessSection> local = new ArrayList<>();
Set<String> ownerOf = new HashSet<>();
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(projectName);
boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
boolean canWriteProjectConfig = true;
try {
@@ -217,7 +212,7 @@
detail.setInheritsFrom(config.getProject().getParent(allProjectsName));
if (projectName.equals(allProjectsName)
- && permissionBackend.user(user).testOrFalse(ADMINISTRATE_SERVER)) {
+ && permissionBackend.currentUser().testOrFalse(ADMINISTRATE_SERVER)) {
ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
}
@@ -264,8 +259,14 @@
throws NoSuchProjectException, IOException, PermissionBackendException,
ResourceConflictException {
ProjectState state = projectCache.checkedGet(projectName);
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ ProjectPermission permissionToCheck =
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
try {
- permissionBackend.user(user).project(projectName).check(ProjectPermission.ACCESS);
+ permissionBackend.currentUser().project(projectName).check(permissionToCheck);
} catch (AuthException e) {
throw new NoSuchProjectException(projectName);
}
@@ -285,7 +286,7 @@
private boolean isAdmin() throws PermissionBackendException {
try {
- permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
return true;
} catch (AuthException e) {
return false;
diff --git a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index e44d680..6c26020 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -24,7 +24,6 @@
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.common.errors.InvalidNameException;
import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.common.errors.UpdateParentFailedException;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
@@ -109,13 +108,9 @@
@Override
public final T call()
throws NoSuchProjectException, IOException, ConfigInvalidException, InvalidNameException,
- NoSuchGroupException, OrmException, UpdateParentFailedException,
- PermissionDeniedException, PermissionBackendException, ResourceConflictException {
- try {
- contributorAgreements.check(projectName, user);
- } catch (AuthException e) {
- throw new PermissionDeniedException(e.getMessage());
- }
+ NoSuchGroupException, OrmException, UpdateParentFailedException, AuthException,
+ PermissionBackendException, ResourceConflictException {
+ contributorAgreements.check(projectName, user);
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
ProjectConfig config = ProjectConfig.read(md, base);
@@ -195,7 +190,7 @@
protected abstract T updateProjectConfig(
ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
throws IOException, NoSuchProjectException, ConfigInvalidException, OrmException,
- PermissionDeniedException, PermissionBackendException, ResourceConflictException;
+ AuthException, PermissionBackendException, ResourceConflictException;
private void replace(ProjectConfig config, Set<String> toDelete, AccessSection section)
throws NoSuchGroupException {
diff --git a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 43dddf9..23b80ca 100644
--- a/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -20,7 +20,6 @@
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.PermissionRule;
-import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -133,16 +132,16 @@
@Override
protected Change.Id updateProjectConfig(
ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
- throws IOException, OrmException, PermissionDeniedException, PermissionBackendException,
+ throws IOException, OrmException, AuthException, PermissionBackendException,
ConfigInvalidException, ResourceConflictException {
PermissionBackend.ForProject perm = permissionBackend.user(user).project(config.getName());
if (!check(perm, ProjectPermission.READ_CONFIG)) {
- throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
+ throw new AuthException(RefNames.REFS_CONFIG + " not visible");
}
if (!check(perm, ProjectPermission.WRITE_CONFIG)
&& !check(perm.ref(RefNames.REFS_CONFIG), RefPermission.CREATE_CHANGE)) {
- throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
+ throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG);
}
projectCache.checkedGet(config.getName()).checkStatePermitsWrite();
diff --git a/java/com/google/gerrit/launcher/GerritLauncher.java b/java/com/google/gerrit/launcher/GerritLauncher.java
index e8892be..b7d232d 100644
--- a/java/com/google/gerrit/launcher/GerritLauncher.java
+++ b/java/com/google/gerrit/launcher/GerritLauncher.java
@@ -64,6 +64,17 @@
System.exit(mainImpl(argv));
}
+ /**
+ * Invokes a proram.
+ *
+ * <p>Creates a new classloader to load and run the program class. To reuse a classloader across
+ * calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
+ *
+ * @param argv arguments, as would be passed to {@code gerrit.war}. The first argument is the
+ * program name.
+ * @return program return code.
+ * @throws Exception if any error occurs.
+ */
public static int mainImpl(String[] argv) throws Exception {
if (argv.length == 0) {
File me;
@@ -164,7 +175,16 @@
}
}
- private static int invokeProgram(ClassLoader loader, String[] origArgv) throws Exception {
+ /**
+ * Invokes a proram in the provided {@code ClassLoader}.
+ *
+ * @param loader classloader to load program class from.
+ * @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the
+ * program name.
+ * @return program return code.
+ * @throws Exception if any error occurs.
+ */
+ public static int invokeProgram(ClassLoader loader, String[] origArgv) throws Exception {
String name = origArgv[0];
final String[] argv = new String[origArgv.length - 1];
System.arraycopy(origArgv, 1, argv, 0, argv.length);
diff --git a/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java b/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
index 6abf17c..55c932c 100644
--- a/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
+++ b/java/com/google/gerrit/metrics/dropwizard/MetricsCollection.java
@@ -21,7 +21,6 @@
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -35,7 +34,6 @@
private final DynamicMap<RestView<MetricResource>> views;
private final Provider<ListMetrics> list;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final DropWizardMetricMaker metrics;
@Inject
@@ -43,12 +41,10 @@
DynamicMap<RestView<MetricResource>> views,
Provider<ListMetrics> list,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
DropWizardMetricMaker metrics) {
this.views = views;
this.list = list;
this.permissionBackend = permissionBackend;
- this.user = user;
this.metrics = metrics;
}
@@ -65,7 +61,7 @@
@Override
public MetricResource parse(ConfigResource parent, IdString id)
throws ResourceNotFoundException, AuthException, PermissionBackendException {
- permissionBackend.user(user).check(GlobalPermission.VIEW_CACHES);
+ permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
Metric metric = metrics.getMetric(id.get());
if (metric == null) {
diff --git a/java/com/google/gerrit/pgm/MigrateToNoteDb.java b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
index 22fdb2c..10761c7 100644
--- a/java/com/google/gerrit/pgm/MigrateToNoteDb.java
+++ b/java/com/google/gerrit/pgm/MigrateToNoteDb.java
@@ -29,13 +29,16 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.index.DummyIndexModule;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
+import com.google.gerrit.server.notedb.rebuild.GcAllUsers;
import com.google.gerrit.server.notedb.rebuild.NoteDbMigrator;
import com.google.gerrit.server.schema.DataSourceType;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.kohsuke.args4j.Option;
@@ -99,6 +102,7 @@
private LifecycleManager dbManager;
private LifecycleManager sysManager;
+ @Inject private GcAllUsers gcAllUsers;
@Inject private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
@Override
@@ -137,6 +141,9 @@
migrator.migrate();
}
}
+ try (PrintWriter w = new PrintWriter(System.out, true)) {
+ gcAllUsers.run(w);
+ }
} finally {
stop();
}
@@ -190,6 +197,7 @@
install(dbInjector.getInstance(BatchProgramModule.class));
install(new DummyIndexModule());
factory(ChangeResource.Factory.class);
+ factory(GarbageCollection.Factory.class);
}
});
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
index 3895d16..b6eac05 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyServer.java
@@ -39,6 +39,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import org.eclipse.jetty.http.HttpScheme;
@@ -56,6 +57,7 @@
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
@@ -149,6 +151,15 @@
httpd.addBean(mbean);
}
+ long gracefulStopTimeout =
+ cfg.getTimeUnit("httpd", null, "gracefulStopTimeout", 0L, TimeUnit.MILLISECONDS);
+ if (gracefulStopTimeout > 0) {
+ StatisticsHandler statsHandler = new StatisticsHandler();
+ statsHandler.setHandler(app);
+ app = statsHandler;
+ httpd.setStopTimeout(gracefulStopTimeout);
+ }
+
httpd.setHandler(app);
httpd.setStopAtShutdown(false);
}
diff --git a/java/com/google/gerrit/pgm/init/GroupsOnInit.java b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
index 4b752e6..1d3e516 100644
--- a/java/com/google/gerrit/pgm/init/GroupsOnInit.java
+++ b/java/com/google/gerrit/pgm/init/GroupsOnInit.java
@@ -16,11 +16,8 @@
import static com.google.common.base.Preconditions.checkArgument;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.errors.NoSuchGroupException;
@@ -28,8 +25,6 @@
import com.google.gerrit.pgm.init.api.InitFlags;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdentProvider;
@@ -41,17 +36,13 @@
import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
-import com.google.gerrit.server.notedb.GroupsMigration;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.sql.Timestamp;
-import java.util.List;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
@@ -74,14 +65,12 @@
private final InitFlags flags;
private final SitePaths site;
private final String allUsers;
- private final GroupsMigration groupsMigration;
@Inject
public GroupsOnInit(InitFlags flags, SitePaths site, AllUsersNameOnInitProvider allUsers) {
this.flags = flags;
this.site = site;
this.allUsers = allUsers.get();
- this.groupsMigration = new GroupsMigration(flags.cfg);
}
/**
@@ -97,15 +86,6 @@
*/
public InternalGroup getExistingGroup(ReviewDb db, GroupReference groupReference)
throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- return getExistingGroupFromNoteDb(groupReference);
- }
-
- return getExistingGroupFromReviewDb(db, groupReference);
- }
-
- private InternalGroup getExistingGroupFromNoteDb(GroupReference groupReference)
- throws IOException, ConfigInvalidException, NoSuchGroupException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
@@ -119,23 +99,6 @@
throw new NoSuchGroupException(groupReference.getUUID());
}
- private static InternalGroup getExistingGroupFromReviewDb(
- ReviewDb db, GroupReference groupReference) throws OrmException, NoSuchGroupException {
- String groupName = groupReference.getName();
- AccountGroupName accountGroupName =
- db.accountGroupNames().get(new AccountGroup.NameKey(groupName));
- if (accountGroupName == null) {
- throw new NoSuchGroupException(groupName);
- }
-
- AccountGroup.Id groupId = accountGroupName.getId();
- AccountGroup group = db.accountGroups().get(groupId);
- if (group == null) {
- throw new NoSuchGroupException(groupName);
- }
- return Groups.asInternalGroup(db, group);
- }
-
/**
* Returns {@code GroupReference}s for all internal groups.
*
@@ -147,18 +110,13 @@
*/
public Stream<GroupReference> getAllGroupReferences(ReviewDb db)
throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- File allUsersRepoPath = getPathToAllUsersRepository();
- if (allUsersRepoPath != null) {
- try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
- return GroupNameNotes.loadAllGroups(allUsersRepo).stream();
- }
+ File allUsersRepoPath = getPathToAllUsersRepository();
+ if (allUsersRepoPath != null) {
+ try (Repository allUsersRepo = new FileRepository(allUsersRepoPath)) {
+ return GroupNameNotes.loadAllGroups(allUsersRepo).stream();
}
- return Stream.empty();
}
-
- return Streams.stream(db.accountGroups().all())
- .map(group -> new GroupReference(group.getGroupUUID(), group.getName()));
+ return Stream.empty();
}
/**
@@ -176,49 +134,6 @@
*/
public void addGroupMember(ReviewDb db, AccountGroup.UUID groupUuid, Account account)
throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
- addGroupMemberInReviewDb(db, groupUuid, account.getId());
- if (!groupsMigration.writeToNoteDb()) {
- return;
- }
- addGroupMemberInNoteDb(groupUuid, account);
- }
-
- private static void addGroupMemberInReviewDb(
- ReviewDb db, AccountGroup.UUID groupUuid, Account.Id accountId)
- throws OrmException, NoSuchGroupException {
- AccountGroup group = getExistingGroup(db, groupUuid);
- AccountGroup.Id groupId = group.getId();
-
- if (isMember(db, groupId, accountId)) {
- return;
- }
-
- db.accountGroupMembers()
- .insert(
- ImmutableList.of(
- new AccountGroupMember(new AccountGroupMember.Key(accountId, groupId))));
- }
-
- private static AccountGroup getExistingGroup(ReviewDb db, AccountGroup.UUID groupUuid)
- throws OrmException, NoSuchGroupException {
- List<AccountGroup> accountGroups = db.accountGroups().byUUID(groupUuid).toList();
- if (accountGroups.size() == 1) {
- return Iterables.getOnlyElement(accountGroups);
- } else if (accountGroups.isEmpty()) {
- throw new NoSuchGroupException(groupUuid);
- } else {
- throw new OrmDuplicateKeyException("Duplicate group UUID " + groupUuid);
- }
- }
-
- private static boolean isMember(ReviewDb db, AccountGroup.Id groupId, Account.Id accountId)
- throws OrmException {
- AccountGroupMember.Key key = new AccountGroupMember.Key(accountId, groupId);
- return db.accountGroupMembers().get(key) != null;
- }
-
- private void addGroupMemberInNoteDb(AccountGroup.UUID groupUuid, Account account)
- throws IOException, ConfigInvalidException, NoSuchGroupException {
File allUsersRepoPath = getPathToAllUsersRepository();
if (allUsersRepoPath != null) {
try (Repository repository = new FileRepository(allUsersRepoPath)) {
diff --git a/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 357674d..ffec375 100644
--- a/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -72,6 +72,7 @@
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gerrit.server.restapi.group.GroupModule;
+import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.inject.Inject;
@@ -172,6 +173,7 @@
// Submit rule evaluator
factory(SubmitRuleEvaluator.Factory.class);
install(new PrologModule());
+ install(new DefaultSubmitRule.Module());
bind(ChangeJson.Factory.class).toProvider(Providers.<ChangeJson.Factory>of(null));
bind(EventUtil.class).toProvider(Providers.<EventUtil>of(null));
diff --git a/java/com/google/gerrit/pgm/util/SiteProgram.java b/java/com/google/gerrit/pgm/util/SiteProgram.java
index afabcf6..b59e085 100644
--- a/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -28,7 +28,6 @@
import com.google.gerrit.server.config.GerritServerConfigModule;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.schema.DataSourceModule;
import com.google.gerrit.server.schema.DataSourceProvider;
@@ -184,7 +183,6 @@
modules.add(new SchemaModule());
modules.add(cfgInjector.getInstance(GitRepositoryManagerModule.class));
modules.add(new NotesMigration.Module());
- modules.add(new GroupsMigration.Module());
try {
return Guice.createInjector(PRODUCTION, modules);
diff --git a/java/com/google/gerrit/reviewdb/server/DisallowReadFromGroupsReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/DisallowReadFromGroupsReviewDbWrapper.java
deleted file mode 100644
index 1bfbd37..0000000
--- a/java/com/google/gerrit/reviewdb/server/DisallowReadFromGroupsReviewDbWrapper.java
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright (C) 2017 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.reviewdb.server;
-
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-
-public class DisallowReadFromGroupsReviewDbWrapper extends ReviewDbWrapper {
- private static final String MSG = "This table has been migrated to NoteDb";
-
- private final Groups groups;
- private final GroupNames groupNames;
- private final GroupMembers groupMembers;
- private final GroupMemberAudits groupMemberAudits;
- private final ByIds byIds;
- private final ByIdAudits byIdAudits;
-
- public DisallowReadFromGroupsReviewDbWrapper(ReviewDb db) {
- super(db);
- groups = new Groups(delegate.accountGroups());
- groupNames = new GroupNames(delegate.accountGroupNames());
- groupMembers = new GroupMembers(delegate.accountGroupMembers());
- groupMemberAudits = new GroupMemberAudits(delegate.accountGroupMembersAudit());
- byIds = new ByIds(delegate.accountGroupById());
- byIdAudits = new ByIdAudits(delegate.accountGroupByIdAud());
- }
-
- @Override
- public AccountGroupAccess accountGroups() {
- return groups;
- }
-
- @Override
- public AccountGroupNameAccess accountGroupNames() {
- return groupNames;
- }
-
- @Override
- public AccountGroupMemberAccess accountGroupMembers() {
- return groupMembers;
- }
-
- @Override
- public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
- return groupMemberAudits;
- }
-
- @Override
- public AccountGroupByIdAccess accountGroupById() {
- return byIds;
- }
-
- @Override
- public AccountGroupByIdAudAccess accountGroupByIdAud() {
- return byIdAudits;
- }
-
- private static class Groups extends AccountGroupAccessWrapper {
- protected Groups(AccountGroupAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroup> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroup, OrmException> getAsync(
- AccountGroup.Id key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroup> get(Iterable<AccountGroup.Id> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroup get(AccountGroup.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroup> all() {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class GroupNames extends AccountGroupNameAccessWrapper {
- protected GroupNames(AccountGroupNameAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroupName> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupName, OrmException> getAsync(
- AccountGroup.NameKey key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupName> get(Iterable<AccountGroup.NameKey> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroupName get(AccountGroup.NameKey name) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupName> all() {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class GroupMembers extends AccountGroupMemberAccessWrapper {
- protected GroupMembers(AccountGroupMemberAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroupMember> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupMember, OrmException>
- getAsync(AccountGroupMember.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMember> get(Iterable<AccountGroupMember.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroupMember get(AccountGroupMember.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMember> byAccount(Account.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class GroupMemberAudits extends AccountGroupMemberAuditAccessWrapper {
- protected GroupMemberAudits(AccountGroupMemberAuditAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupMemberAudit, OrmException>
- getAsync(AccountGroupMemberAudit.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> get(Iterable<AccountGroupMemberAudit.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroupMemberAudit get(AccountGroupMemberAudit.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroupAccount(
- AccountGroup.Id groupId, Account.Id accountId) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroup(AccountGroup.Id groupId) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class ByIds extends AccountGroupByIdAccessWrapper {
- protected ByIds(AccountGroupByIdAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroupById> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupById, OrmException> getAsync(
- AccountGroupById.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupById> get(Iterable<AccountGroupById.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroupById get(AccountGroupById.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupById> byIncludeUUID(AccountGroup.UUID uuid) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupById> byGroup(AccountGroup.Id id) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupById> all() {
- throw new UnsupportedOperationException(MSG);
- }
- }
-
- private static class ByIdAudits extends AccountGroupByIdAudAccessWrapper {
- protected ByIdAudits(AccountGroupByIdAudAccess delegate) {
- super(delegate);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> iterateAllEntities() {
- throw new UnsupportedOperationException(MSG);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public com.google.common.util.concurrent.CheckedFuture<AccountGroupByIdAud, OrmException>
- getAsync(AccountGroupByIdAud.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> get(Iterable<AccountGroupByIdAud.Key> keys) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public AccountGroupByIdAud get(AccountGroupByIdAud.Key key) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroupInclude(
- AccountGroup.Id groupId, AccountGroup.UUID incGroupUUID) {
- throw new UnsupportedOperationException(MSG);
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroup(AccountGroup.Id groupId) {
- throw new UnsupportedOperationException(MSG);
- }
- }
-}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDb.java b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
index 22a9cf3..4e648b9 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDb.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDb.java
@@ -53,17 +53,13 @@
// Deleted @Relation(id = 8)
- @Relation(id = 10)
- AccountGroupAccess accountGroups();
+ // Deleted @Relation(id = 10)
- @Relation(id = 11)
- AccountGroupNameAccess accountGroupNames();
+ // Deleted @Relation(id = 11)
- @Relation(id = 12)
- AccountGroupMemberAccess accountGroupMembers();
+ // Deleted @Relation(id = 12)
- @Relation(id = 13)
- AccountGroupMemberAuditAccess accountGroupMembersAudit();
+ // Deleted @Relation(id = 13)
// Deleted @Relation(id = 17)
@@ -92,11 +88,9 @@
// Deleted @Relation(id = 28)
- @Relation(id = 29)
- AccountGroupByIdAccess accountGroupById();
+ // Deleted @Relation(id = 29)
- @Relation(id = 30)
- AccountGroupByIdAudAccess accountGroupByIdAud();
+ // Deleted @Relation(id = 30)
int FIRST_ACCOUNT_ID = 1000000;
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java b/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
index ef057eb..aed9778 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDbUtil.java
@@ -53,9 +53,6 @@
if (db instanceof DisallowReadFromChangesReviewDbWrapper) {
return unwrapDb(((DisallowReadFromChangesReviewDbWrapper) db).unsafeGetDelegate());
}
- if (db instanceof DisallowReadFromGroupsReviewDbWrapper) {
- return unwrapDb(((DisallowReadFromGroupsReviewDbWrapper) db).unsafeGetDelegate());
- }
return db;
}
diff --git a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
index 788c4d4..f8e93ae 100644
--- a/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
+++ b/java/com/google/gerrit/reviewdb/server/ReviewDbWrapper.java
@@ -115,26 +115,6 @@
}
@Override
- public AccountGroupAccess accountGroups() {
- return delegate.accountGroups();
- }
-
- @Override
- public AccountGroupNameAccess accountGroupNames() {
- return delegate.accountGroupNames();
- }
-
- @Override
- public AccountGroupMemberAccess accountGroupMembers() {
- return delegate.accountGroupMembers();
- }
-
- @Override
- public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
- return delegate.accountGroupMembersAudit();
- }
-
- @Override
public ChangeAccess changes() {
return delegate.changes();
}
@@ -160,16 +140,6 @@
}
@Override
- public AccountGroupByIdAccess accountGroupById() {
- return delegate.accountGroupById();
- }
-
- @Override
- public AccountGroupByIdAudAccess accountGroupByIdAud() {
- return delegate.accountGroupByIdAud();
- }
-
- @Override
@SuppressWarnings("deprecation")
public int nextAccountId() throws OrmException {
return delegate.nextAccountId();
diff --git a/java/com/google/gerrit/server/account/AccountManager.java b/java/com/google/gerrit/server/account/AccountManager.java
index 79c57484..8e00130 100644
--- a/java/com/google/gerrit/server/account/AccountManager.java
+++ b/java/com/google/gerrit/server/account/AccountManager.java
@@ -28,7 +28,6 @@
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.ServerInitiated;
@@ -43,7 +42,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.ssh.SshKeyCache;
import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -65,7 +63,6 @@
public class AccountManager {
private static final Logger log = LoggerFactory.getLogger(AccountManager.class);
- private final SchemaFactory<ReviewDb> schema;
private final Sequences sequences;
private final Accounts accounts;
private final Provider<AccountsUpdate> accountsUpdateProvider;
@@ -82,7 +79,6 @@
@Inject
AccountManager(
- SchemaFactory<ReviewDb> schema,
Sequences sequences,
@GerritServerConfig Config cfg,
Accounts accounts,
@@ -95,7 +91,6 @@
ExternalIds externalIds,
GroupsUpdate.Factory groupsUpdateFactory,
SetInactiveFlag setInactiveFlag) {
- this.schema = schema;
this.sequences = sequences;
this.accounts = accounts;
this.accountsUpdateProvider = accountsUpdateProvider;
@@ -140,51 +135,49 @@
throw e;
}
try {
- try (ReviewDb db = schema.open()) {
- ExternalId id = externalIds.get(who.getExternalIdKey());
- if (id == null) {
- if (who.getUserName().isPresent()) {
- ExternalId.Key key = ExternalId.Key.create(SCHEME_USERNAME, who.getUserName().get());
- ExternalId existingId = externalIds.get(key);
- if (existingId != null) {
- // An inconsistency is detected in the database, having a record for scheme "username:"
- // but no record for scheme "gerrit:". Try to recover by linking
- // "gerrit:" identity to the existing account.
- log.warn(
- "User {} already has an account; link new identity to the existing account.",
- who.getUserName());
- return link(existingId.accountId(), who);
- }
+ ExternalId id = externalIds.get(who.getExternalIdKey());
+ if (id == null) {
+ if (who.getUserName().isPresent()) {
+ ExternalId.Key key = ExternalId.Key.create(SCHEME_USERNAME, who.getUserName().get());
+ ExternalId existingId = externalIds.get(key);
+ if (existingId != null) {
+ // An inconsistency is detected in the database, having a record for scheme "username:"
+ // but no record for scheme "gerrit:". Try to recover by linking
+ // "gerrit:" identity to the existing account.
+ log.warn(
+ "User {} already has an account; link new identity to the existing account.",
+ who.getUserName());
+ return link(existingId.accountId(), who);
}
- // New account, automatically create and return.
- log.info("External ID not found. Attempting to create new account.");
- return create(db, who);
}
-
- Optional<AccountState> accountState = byIdCache.get(id.accountId());
- if (!accountState.isPresent()) {
- log.error(
- String.format(
- "Authentication with external ID %s failed. Account %s doesn't exist.",
- id.key().get(), id.accountId().get()));
- throw new AccountException("Authentication error, account not found");
- }
-
- // Account exists
- Optional<Account> act = updateAccountActiveStatus(who, accountState.get().getAccount());
- if (!act.isPresent()) {
- // The account was deleted since we checked for it last time. This should never happen
- // since we don't support deletion of accounts.
- throw new AccountException("Authentication error, account not found");
- }
- if (!act.get().isActive()) {
- throw new AccountException("Authentication error, account inactive");
- }
-
- // return the identity to the caller.
- update(who, id);
- return new AuthResult(id.accountId(), who.getExternalIdKey(), false);
+ // New account, automatically create and return.
+ log.info("External ID not found. Attempting to create new account.");
+ return create(who);
}
+
+ Optional<AccountState> accountState = byIdCache.get(id.accountId());
+ if (!accountState.isPresent()) {
+ log.error(
+ String.format(
+ "Authentication with external ID %s failed. Account %s doesn't exist.",
+ id.key().get(), id.accountId().get()));
+ throw new AccountException("Authentication error, account not found");
+ }
+
+ // Account exists
+ Optional<Account> act = updateAccountActiveStatus(who, accountState.get().getAccount());
+ if (!act.isPresent()) {
+ // The account was deleted since we checked for it last time. This should never happen
+ // since we don't support deletion of accounts.
+ throw new AccountException("Authentication error, account not found");
+ }
+ if (!act.get().isActive()) {
+ throw new AccountException("Authentication error, account inactive");
+ }
+
+ // return the identity to the caller.
+ update(who, id);
+ return new AuthResult(id.accountId(), who.getExternalIdKey(), false);
} catch (OrmException | ConfigInvalidException e) {
throw new AccountException("Authentication error", e);
}
@@ -289,7 +282,7 @@
}
}
- private AuthResult create(ReviewDb db, AuthRequest who)
+ private AuthResult create(AuthRequest who)
throws OrmException, AccountException, IOException, ConfigInvalidException {
Account.Id newId = new Account.Id(sequences.nextAccountId());
@@ -349,7 +342,7 @@
.getPermission(GlobalCapability.ADMINISTRATE_SERVER);
AccountGroup.UUID adminGroupUuid = admin.getRules().get(0).getGroup().getUUID();
- addGroupMember(db, adminGroupUuid, user);
+ addGroupMember(adminGroupUuid, user);
}
realm.onCreateAccount(who, accountState.getAccount());
@@ -369,7 +362,7 @@
return ExternalId.create(SCHEME_USERNAME, username, accountId);
}
- private void addGroupMember(ReviewDb db, AccountGroup.UUID groupUuid, IdentifiedUser user)
+ private void addGroupMember(AccountGroup.UUID groupUuid, IdentifiedUser user)
throws OrmException, IOException, ConfigInvalidException, AccountException {
// The user initiated this request by logging in. -> Attribute all modifications to that user.
GroupsUpdate groupsUpdate = groupsUpdateFactory.create(user);
@@ -379,7 +372,7 @@
memberIds -> Sets.union(memberIds, ImmutableSet.of(user.getAccountId())))
.build();
try {
- groupsUpdate.updateGroup(db, groupUuid, groupUpdate);
+ groupsUpdate.updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new AccountException(String.format("Group %s not found", groupUuid));
}
diff --git a/java/com/google/gerrit/server/account/GroupCacheImpl.java b/java/com/google/gerrit/server/account/GroupCacheImpl.java
index 58eaadc..a20aab7 100644
--- a/java/com/google/gerrit/server/account/GroupCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -17,12 +17,10 @@
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.query.group.InternalGroupQuery;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
@@ -166,20 +164,16 @@
}
static class ByUUIDLoader extends CacheLoader<String, Optional<InternalGroup>> {
- private final SchemaFactory<ReviewDb> schema;
private final Groups groups;
@Inject
- ByUUIDLoader(SchemaFactory<ReviewDb> sf, Groups groups) {
- schema = sf;
+ ByUUIDLoader(Groups groups) {
this.groups = groups;
}
@Override
public Optional<InternalGroup> load(String uuid) throws Exception {
- try (ReviewDb db = schema.open()) {
- return groups.getGroup(db, new AccountGroup.UUID(uuid));
- }
+ return groups.getGroup(new AccountGroup.UUID(uuid));
}
}
}
diff --git a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
index d8472a6..ba81c6a 100644
--- a/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
+++ b/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -23,13 +23,11 @@
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.cache.CacheModule;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.query.group.InternalGroupQuery;
import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Provider;
@@ -180,20 +178,16 @@
}
static class AllExternalLoader extends CacheLoader<String, ImmutableList<AccountGroup.UUID>> {
- private final SchemaFactory<ReviewDb> schema;
private final Groups groups;
@Inject
- AllExternalLoader(SchemaFactory<ReviewDb> sf, Groups groups) {
- schema = sf;
+ AllExternalLoader(Groups groups) {
this.groups = groups;
}
@Override
public ImmutableList<AccountGroup.UUID> load(String key) throws Exception {
- try (ReviewDb db = schema.open()) {
- return groups.getExternalGroups(db).collect(toImmutableList());
- }
+ return groups.getExternalGroups().collect(toImmutableList());
}
}
}
diff --git a/java/com/google/gerrit/server/account/InternalGroupBackend.java b/java/com/google/gerrit/server/account/InternalGroupBackend.java
index 4547807b..ea6eb87 100644
--- a/java/com/google/gerrit/server/account/InternalGroupBackend.java
+++ b/java/com/google/gerrit/server/account/InternalGroupBackend.java
@@ -20,15 +20,12 @@
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsNoteDbConsistencyChecker;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -43,7 +40,6 @@
private final GroupControl.Factory groupControlFactory;
private final GroupCache groupCache;
private final Groups groups;
- private final SchemaFactory<ReviewDb> schema;
private final IncludingGroupMembership.Factory groupMembershipFactory;
@Inject
@@ -51,12 +47,10 @@
GroupControl.Factory groupControlFactory,
GroupCache groupCache,
Groups groups,
- SchemaFactory<ReviewDb> schema,
IncludingGroupMembership.Factory groupMembershipFactory) {
this.groupControlFactory = groupControlFactory;
this.groupCache = groupCache;
this.groups = groups;
- this.schema = schema;
this.groupMembershipFactory = groupMembershipFactory;
}
@@ -77,13 +71,13 @@
@Override
public Collection<GroupReference> suggest(String name, ProjectState project) {
- try (ReviewDb db = schema.open()) {
+ try {
return groups
- .getAllGroupReferences(db)
+ .getAllGroupReferences()
.filter(group -> startsWithIgnoreCase(group, name))
.filter(this::isVisible)
.collect(toList());
- } catch (OrmException | IOException | ConfigInvalidException e) {
+ } catch (IOException | ConfigInvalidException e) {
return ImmutableList.of();
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index ffd413a..442bc2a 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -278,8 +278,6 @@
*/
public static ExternalId parse(String noteId, byte[] raw, ObjectId blobId)
throws ConfigInvalidException {
- checkNotNull(blobId);
-
Config externalIdConfig = new Config();
try {
externalIdConfig.fromText(new String(raw, UTF_8));
@@ -287,6 +285,13 @@
throw invalidConfig(noteId, e.getMessage());
}
+ return parse(noteId, externalIdConfig, blobId);
+ }
+
+ public static ExternalId parse(String noteId, Config externalIdConfig, ObjectId blobId)
+ throws ConfigInvalidException {
+ checkNotNull(blobId);
+
Set<String> externalIdKeys = externalIdConfig.getSubsections(EXTERNAL_ID_SECTION);
if (externalIdKeys.size() != 1) {
throw invalidConfig(
@@ -439,11 +444,17 @@
// c.setString(...) ensures that account IDs are human readable.
c.setString(
EXTERNAL_ID_SECTION, externalIdKey, ACCOUNT_ID_KEY, Integer.toString(accountId().get()));
+
if (email() != null) {
c.setString(EXTERNAL_ID_SECTION, externalIdKey, EMAIL_KEY, email());
+ } else {
+ c.unset(EXTERNAL_ID_SECTION, externalIdKey, EMAIL_KEY);
}
+
if (password() != null) {
c.setString(EXTERNAL_ID_SECTION, externalIdKey, PASSWORD_KEY, password());
+ } else {
+ c.unset(EXTERNAL_ID_SECTION, externalIdKey, PASSWORD_KEY);
}
}
}
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
index 972dfbd..ecb201f 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdNotes.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
@@ -371,9 +372,9 @@
Set<ExternalId> newExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId extId : extIds) {
- ExternalId insertedExtId = upsert(rw, inserter, noteMap, extId);
+ ExternalId insertedExtId = upsert(rw, inserter, noteMap, f, extId);
newExtIds.add(insertedExtId);
}
});
@@ -399,9 +400,9 @@
Set<ExternalId> removedExtIds = get(ExternalId.Key.from(extIds));
Set<ExternalId> updatedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId extId : extIds) {
- ExternalId updatedExtId = upsert(rw, inserter, noteMap, extId);
+ ExternalId updatedExtId = upsert(rw, inserter, noteMap, f, extId);
updatedExtIds.add(updatedExtId);
}
});
@@ -429,9 +430,9 @@
checkLoaded();
Set<ExternalId> removedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId extId : extIds) {
- remove(rw, noteMap, extId);
+ remove(rw, noteMap, f, extId);
removedExtIds.add(extId);
}
});
@@ -458,9 +459,9 @@
checkLoaded();
Set<ExternalId> removedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId.Key extIdKey : extIdKeys) {
- ExternalId removedExtId = remove(rw, noteMap, extIdKey, accountId);
+ ExternalId removedExtId = remove(rw, noteMap, f, extIdKey, accountId);
removedExtIds.add(removedExtId);
}
});
@@ -476,9 +477,9 @@
checkLoaded();
Set<ExternalId> removedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId.Key extIdKey : extIdKeys) {
- ExternalId extId = remove(rw, noteMap, extIdKey, null);
+ ExternalId extId = remove(rw, noteMap, f, extIdKey, null);
removedExtIds.add(extId);
}
});
@@ -506,16 +507,16 @@
Set<ExternalId> removedExtIds = new HashSet<>();
Set<ExternalId> updatedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId.Key extIdKey : toDelete) {
- ExternalId removedExtId = remove(rw, noteMap, extIdKey, accountId);
+ ExternalId removedExtId = remove(rw, noteMap, f, extIdKey, accountId);
if (removedExtId != null) {
removedExtIds.add(removedExtId);
}
}
for (ExternalId extId : toAdd) {
- ExternalId insertedExtId = upsert(rw, inserter, noteMap, extId);
+ ExternalId insertedExtId = upsert(rw, inserter, noteMap, f, extId);
updatedExtIds.add(insertedExtId);
}
});
@@ -540,14 +541,14 @@
Set<ExternalId> removedExtIds = new HashSet<>();
Set<ExternalId> updatedExtIds = new HashSet<>();
noteMapUpdates.add(
- (rw, n) -> {
+ (rw, n, f) -> {
for (ExternalId.Key extIdKey : toDelete) {
- ExternalId removedExtId = remove(rw, noteMap, extIdKey, null);
+ ExternalId removedExtId = remove(rw, noteMap, f, extIdKey, null);
removedExtIds.add(removedExtId);
}
for (ExternalId extId : toAdd) {
- ExternalId insertedExtId = upsert(rw, inserter, noteMap, extId);
+ ExternalId insertedExtId = upsert(rw, inserter, noteMap, f, extId);
updatedExtIds.add(insertedExtId);
}
});
@@ -660,14 +661,22 @@
}
try (RevWalk rw = new RevWalk(repo)) {
+ Set<String> footers = new HashSet<>();
for (NoteMapUpdate noteMapUpdate : noteMapUpdates) {
try {
- noteMapUpdate.execute(rw, noteMap);
+ noteMapUpdate.execute(rw, noteMap, footers);
} catch (DuplicateExternalIdKeyException e) {
throw new IOException(e);
}
}
noteMapUpdates.clear();
+ if (!footers.isEmpty()) {
+ commit.setMessage(
+ footers
+ .stream()
+ .sorted()
+ .collect(joining("\n", commit.getMessage().trim() + "\n\n", "")));
+ }
RevTree oldTree = revision != null ? rw.parseTree(revision) : null;
ObjectId newTreeId = noteMap.writeTree(inserter);
@@ -718,14 +727,17 @@
* <p>If the external ID already exists it is overwritten.
*/
private static ExternalId upsert(
- RevWalk rw, ObjectInserter ins, NoteMap noteMap, ExternalId extId)
+ RevWalk rw, ObjectInserter ins, NoteMap noteMap, Set<String> footers, ExternalId extId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extId.key().sha1();
Config c = new Config();
if (noteMap.contains(extId.key().sha1())) {
- byte[] raw = readNoteData(rw, noteMap.get(noteId));
+ ObjectId noteDataId = noteMap.get(noteId);
+ byte[] raw = readNoteData(rw, noteDataId);
try {
c = new BlobBasedConfig(null, raw);
+ ExternalId oldExtId = ExternalId.parse(noteId.name(), c, noteDataId);
+ addFooters(footers, oldExtId);
} catch (ConfigInvalidException e) {
throw new ConfigInvalidException(
String.format("Invalid external id config for note %s: %s", noteId, e.getMessage()));
@@ -735,7 +747,9 @@
byte[] raw = c.toText().getBytes(UTF_8);
ObjectId noteData = ins.insert(OBJ_BLOB, raw);
noteMap.set(noteId, noteData);
- return ExternalId.create(extId, noteData);
+ ExternalId newExtId = ExternalId.create(extId, noteData);
+ addFooters(footers, newExtId);
+ return newExtId;
}
/**
@@ -744,7 +758,8 @@
* @throws IllegalStateException is thrown if there is an existing external ID that has the same
* key, but otherwise doesn't match the specified external ID.
*/
- private static ExternalId remove(RevWalk rw, NoteMap noteMap, ExternalId extId)
+ private static ExternalId remove(
+ RevWalk rw, NoteMap noteMap, Set<String> footers, ExternalId extId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extId.key().sha1();
if (!noteMap.contains(noteId)) {
@@ -760,6 +775,7 @@
extId.toString(),
actualExtId.toString());
noteMap.remove(noteId);
+ addFooters(footers, actualExtId);
return actualExtId;
}
@@ -772,7 +788,11 @@
* exists
*/
private static ExternalId remove(
- RevWalk rw, NoteMap noteMap, ExternalId.Key extIdKey, Account.Id expectedAccountId)
+ RevWalk rw,
+ NoteMap noteMap,
+ Set<String> footers,
+ ExternalId.Key extIdKey,
+ Account.Id expectedAccountId)
throws IOException, ConfigInvalidException {
ObjectId noteId = extIdKey.sha1();
if (!noteMap.contains(noteId)) {
@@ -792,9 +812,17 @@
extId.accountId().get());
}
noteMap.remove(noteId);
+ addFooters(footers, extId);
return extId;
}
+ private static void addFooters(Set<String> footers, ExternalId extId) {
+ footers.add("Account: " + extId.accountId().get());
+ if (extId.email() != null) {
+ footers.add("Email: " + extId.email());
+ }
+ }
+
private void checkExternalIdsDontExist(Collection<ExternalId> extIds)
throws DuplicateExternalIdKeyException, IOException {
checkExternalIdKeysDontExist(ExternalId.Key.from(extIds));
@@ -823,7 +851,7 @@
@FunctionalInterface
private interface NoteMapUpdate {
- void execute(RevWalk rw, NoteMap noteMap)
+ void execute(RevWalk rw, NoteMap noteMap, Set<String> footers)
throws IOException, ConfigInvalidException, DuplicateExternalIdKeyException;
}
diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index f5f1a34..44b6610 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -100,7 +100,7 @@
}
try {
CreateAccount impl = createAccount.create(in.username);
- permissionBackend.user(self).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
+ permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
AccountInfo info = impl.apply(TopLevelResource.INSTANCE, in).value();
return id(info._accountId);
} catch (Exception e) {
diff --git a/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index 0b3bc64..247be44 100644
--- a/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -26,7 +26,6 @@
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ProjectResource;
@@ -49,7 +48,6 @@
private final ProjectsCollection projects;
private final Provider<ListGroups> listGroups;
private final Provider<QueryGroups> queryGroups;
- private final Provider<CurrentUser> user;
private final PermissionBackend permissionBackend;
private final CreateGroup.Factory createGroup;
private final GroupApiImpl.Factory api;
@@ -61,7 +59,6 @@
ProjectsCollection projects,
Provider<ListGroups> listGroups,
Provider<QueryGroups> queryGroups,
- Provider<CurrentUser> user,
PermissionBackend permissionBackend,
CreateGroup.Factory createGroup,
GroupApiImpl.Factory api) {
@@ -70,7 +67,6 @@
this.projects = projects;
this.listGroups = listGroups;
this.queryGroups = queryGroups;
- this.user = user;
this.permissionBackend = permissionBackend;
this.createGroup = createGroup;
this.api = api;
@@ -95,7 +91,7 @@
}
try {
CreateGroup impl = createGroup.create(in.name);
- permissionBackend.user(user).checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
+ permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(impl.getClass()));
GroupInfo info = impl.apply(TopLevelResource.INSTANCE, in);
return id(info.id);
} catch (Exception e) {
diff --git a/java/com/google/gerrit/server/args4j/ProjectHandler.java b/java/com/google/gerrit/server/args4j/ProjectHandler.java
index 8959d97..1d40b53 100644
--- a/java/com/google/gerrit/server/args4j/ProjectHandler.java
+++ b/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -17,7 +17,6 @@
import com.google.gerrit.common.ProjectUtil;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
@@ -25,7 +24,6 @@
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import org.kohsuke.args4j.CmdLineException;
@@ -42,20 +40,17 @@
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
@Inject
public ProjectHandler(
ProjectCache projectCache,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
@Assisted final CmdLineParser parser,
@Assisted final OptionDef option,
@Assisted final Setter<ProjectState> setter) {
super(parser, option, setter);
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
- this.user = user;
}
@Override
@@ -84,7 +79,13 @@
if (state == null) {
throw new CmdLineException(owner, String.format("project %s not found", nameWithoutSuffix));
}
- permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ ProjectPermission permissionToCheck =
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
+ permissionBackend.currentUser().project(nameKey).check(permissionToCheck);
} catch (AuthException e) {
throw new CmdLineException(owner, new NoSuchProjectException(nameKey).getMessage());
} catch (PermissionBackendException | IOException e) {
diff --git a/java/com/google/gerrit/server/config/GerritGlobalModule.java b/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 9536d55..21e4cb1 100644
--- a/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -155,7 +155,6 @@
import com.google.gerrit.server.mail.send.SetAssigneeSender;
import com.google.gerrit.server.mime.FileTypeRegistry;
import com.google.gerrit.server.mime.MimeUtilFileTypeRegistry;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NoteDbModule;
import com.google.gerrit.server.patch.PatchListCacheImpl;
import com.google.gerrit.server.patch.PatchScriptFactory;
@@ -175,6 +174,7 @@
import com.google.gerrit.server.query.change.ConflictsCacheImpl;
import com.google.gerrit.server.restapi.config.ConfigRestModule;
import com.google.gerrit.server.restapi.group.GroupModule;
+import com.google.gerrit.server.rules.DefaultSubmitRule;
import com.google.gerrit.server.rules.PrologModule;
import com.google.gerrit.server.rules.RulesCache;
import com.google.gerrit.server.rules.SubmitRule;
@@ -204,14 +204,11 @@
public class GerritGlobalModule extends FactoryModule {
private final Config cfg;
private final AuthModule authModule;
- private final GroupsMigration groupsMigration;
@Inject
- GerritGlobalModule(
- @GerritServerConfig Config cfg, AuthModule authModule, GroupsMigration groupsMigration) {
+ GerritGlobalModule(@GerritServerConfig Config cfg, AuthModule authModule) {
this.cfg = cfg;
this.authModule = authModule;
- this.groupsMigration = groupsMigration;
}
@Override
@@ -246,6 +243,7 @@
install(new GroupModule());
install(new NoteDbModule(cfg));
install(new PrologModule());
+ install(new DefaultSubmitRule.Module());
install(new ReceiveCommitsModule());
install(new SshAddressesModule());
install(ThreadLocalRequestContext.module());
@@ -307,7 +305,6 @@
install(new com.google.gerrit.server.restapi.access.Module());
install(new ConfigRestModule());
install(new com.google.gerrit.server.restapi.change.Module());
- install(new com.google.gerrit.server.group.Module(groupsMigration));
install(new com.google.gerrit.server.restapi.account.Module());
install(new com.google.gerrit.server.restapi.project.Module());
install(new com.google.gerrit.server.restapi.group.Module());
diff --git a/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index 64f5ae7..9880dae 100644
--- a/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -400,7 +400,7 @@
}
try {
permissionBackend
- .user(currentUser)
+ .currentUser()
.database(reviewDb)
.change(notes)
.check(ChangePermission.ADD_PATCH_SET);
diff --git a/java/com/google/gerrit/server/events/EventBroker.java b/java/com/google/gerrit/server/events/EventBroker.java
index 8f12cb3..62e8d12 100644
--- a/java/com/google/gerrit/server/events/EventBroker.java
+++ b/java/com/google/gerrit/server/events/EventBroker.java
@@ -150,6 +150,11 @@
protected boolean isVisibleTo(Project.NameKey project, CurrentUser user) {
try {
+ ProjectState state = projectCache.get(project);
+ if (state == null || !state.statePermitsRead()) {
+ return false;
+ }
+
permissionBackend.user(user).project(project).check(ProjectPermission.ACCESS);
return true;
} catch (AuthException | PermissionBackendException e) {
diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java
index 6714055..043c2e4 100644
--- a/java/com/google/gerrit/server/extensions/webui/UiActions.java
+++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -31,13 +31,11 @@
import com.google.gerrit.extensions.webui.PrivateInternals_UiActionDescription;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.extensions.webui.UiAction.Description;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendCondition;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.HashMap;
import java.util.Iterator;
@@ -57,12 +55,10 @@
}
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> userProvider;
@Inject
- UiActions(PermissionBackend permissionBackend, Provider<CurrentUser> userProvider) {
+ UiActions(PermissionBackend permissionBackend) {
this.permissionBackend = permissionBackend;
- this.userProvider = userProvider;
}
public <R extends RestResource> Iterable<UiAction.Description> from(
@@ -146,7 +142,7 @@
return null;
}
if (!globalRequired.isEmpty()) {
- PermissionBackend.WithUser withUser = permissionBackend.user(userProvider);
+ PermissionBackend.WithUser withUser = permissionBackend.currentUser();
Iterator<GlobalOrPluginPermission> i = globalRequired.iterator();
BooleanCondition p = withUser.testCond(i.next());
while (i.hasNext()) {
diff --git a/java/com/google/gerrit/server/git/receive/NoteDbPushOption.java b/java/com/google/gerrit/server/git/receive/NoteDbPushOption.java
new file mode 100644
index 0000000..8142d0a
--- /dev/null
+++ b/java/com/google/gerrit/server/git/receive/NoteDbPushOption.java
@@ -0,0 +1,47 @@
+// Copyright (C) 2018 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.server.git.receive;
+
+import static com.google.common.collect.ImmutableMap.toImmutableMap;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Optional;
+
+/** Possible values for {@code -o notedb=X} push option. */
+public enum NoteDbPushOption {
+ DISALLOW,
+ ALLOW;
+
+ public static final String OPTION_NAME = "notedb";
+
+ private static final ImmutableMap<String, NoteDbPushOption> ALL =
+ Arrays.stream(values()).collect(toImmutableMap(NoteDbPushOption::value, o -> o));
+
+ /**
+ * Parses an option value from a lowercase string representation.
+ *
+ * @param value input value.
+ * @return parsed value, or empty if no value matched.
+ */
+ public static Optional<NoteDbPushOption> parse(String value) {
+ return Optional.ofNullable(ALL.get(value));
+ }
+
+ public String value() {
+ return name().toLowerCase(Locale.US);
+ }
+}
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 0e54e59..8eaa9de 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -387,6 +387,7 @@
private boolean newChangeForAllNotInTarget;
private String setFullNameTo;
private boolean setChangeAsPrivate;
+ private Optional<NoteDbPushOption> noteDbPushOption;
// Handles for outputting back over the wire to the end user.
private Task newProgress;
@@ -561,6 +562,7 @@
commandProgress = progress.beginSubTask("refs", UNKNOWN);
try {
+ parsePushOptions();
parseCommands(commands);
} catch (PermissionBackendException | NoSuchProjectException | IOException err) {
for (ReceiveCommand cmd : actualCommands) {
@@ -811,8 +813,7 @@
return sb.append(":\n").append(error.get()).toString();
}
- private void parseCommands(Collection<ReceiveCommand> commands)
- throws PermissionBackendException, NoSuchProjectException, IOException {
+ private void parsePushOptions() {
List<String> optionList = rp.getPushOptions();
if (optionList != null) {
for (String option : optionList) {
@@ -825,6 +826,22 @@
}
}
+ List<String> noteDbValues = pushOptions.get("notedb");
+ if (!noteDbValues.isEmpty()) {
+ // These semantics for duplicates/errors are somewhat arbitrary and may not match e.g. the
+ // CommandLineParser behavior used by MagicBranchInput.
+ String value = noteDbValues.get(noteDbValues.size() - 1);
+ noteDbPushOption = NoteDbPushOption.parse(value);
+ if (!noteDbPushOption.isPresent()) {
+ addError("Invalid value in -o " + NoteDbPushOption.OPTION_NAME + "=" + value);
+ }
+ } else {
+ noteDbPushOption = Optional.of(NoteDbPushOption.DISALLOW);
+ }
+ }
+
+ private void parseCommands(Collection<ReceiveCommand> commands)
+ throws PermissionBackendException, NoSuchProjectException, IOException {
logDebug("Parsing {} commands", commands.size());
for (ReceiveCommand cmd : commands) {
if (cmd.getResult() != NOT_ATTEMPTED) {
@@ -875,6 +892,38 @@
continue;
}
+ if (RefNames.isNoteDbMetaRef(cmd.getRefName())) {
+ // Reject pushes to NoteDb refs without a special option and permission. Note that this
+ // prohibition doesn't depend on NoteDb being enabled in any way, since all sites will
+ // migrate to NoteDb eventually, and we don't want garbage data waiting there when the
+ // migration finishes.
+ logDebug(
+ "{} NoteDb ref {} with {}={}",
+ cmd.getType(),
+ cmd.getRefName(),
+ NoteDbPushOption.OPTION_NAME,
+ noteDbPushOption);
+ if (!Optional.of(NoteDbPushOption.ALLOW).equals(noteDbPushOption)) {
+ // Only reject this command, not the whole push. This supports the use case of "git clone
+ // --mirror" followed by "git push --mirror", when the user doesn't really intend to clone
+ // or mirror the NoteDb data; there is no single refspec that describes all refs *except*
+ // NoteDb refs.
+ reject(
+ cmd,
+ "NoteDb update requires -o "
+ + NoteDbPushOption.OPTION_NAME
+ + "="
+ + NoteDbPushOption.ALLOW.value());
+ continue;
+ }
+ try {
+ permissionBackend.user(user).check(GlobalPermission.ACCESS_DATABASE);
+ } catch (AuthException e) {
+ reject(cmd, "NoteDb update requires access database permission");
+ continue;
+ }
+ }
+
switch (cmd.getType()) {
case CREATE:
parseCreate(cmd);
diff --git a/java/com/google/gerrit/server/group/DbGroupAuditListener.java b/java/com/google/gerrit/server/group/DbGroupAuditListener.java
deleted file mode 100644
index 8b53348..0000000
--- a/java/com/google/gerrit/server/group/DbGroupAuditListener.java
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright (C) 2014 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.server.group;
-
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.reviewdb.server.ReviewDbUtil.unwrapDb;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountState;
-import com.google.gerrit.server.account.GroupCache;
-import com.google.gerrit.server.account.UniversalGroupBackend;
-import com.google.gerrit.server.audit.group.GroupAuditEvent;
-import com.google.gerrit.server.audit.group.GroupAuditListener;
-import com.google.gerrit.server.audit.group.GroupMemberAuditEvent;
-import com.google.gerrit.server.audit.group.GroupSubgroupAuditEvent;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.inject.Inject;
-import java.sql.Timestamp;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import org.slf4j.Logger;
-
-class DbGroupAuditListener implements GroupAuditListener {
- private static final Logger log = org.slf4j.LoggerFactory.getLogger(DbGroupAuditListener.class);
-
- private final SchemaFactory<ReviewDb> schema;
- private final AccountCache accountCache;
- private final GroupCache groupCache;
- private final UniversalGroupBackend groupBackend;
-
- @Inject
- DbGroupAuditListener(
- SchemaFactory<ReviewDb> schema,
- AccountCache accountCache,
- GroupCache groupCache,
- UniversalGroupBackend groupBackend) {
- this.schema = schema;
- this.accountCache = accountCache;
- this.groupCache = groupCache;
- this.groupBackend = groupBackend;
- }
-
- @Override
- public void onAddMembers(GroupMemberAuditEvent event) {
- Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
- if (!updatedGroup.isPresent()) {
- logFailToLoadUpdatedGroup(event);
- return;
- }
-
- InternalGroup group = updatedGroup.get();
- try (ReviewDb db = unwrapDb(schema.open())) {
- db.accountGroupMembersAudit().insert(toAccountGroupMemberAudits(event, group.getId()));
- } catch (OrmException e) {
- logOrmException(
- "Cannot log add accounts to group event performed by user", event, group.getName(), e);
- }
- }
-
- @Override
- public void onDeleteMembers(GroupMemberAuditEvent event) {
- Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
- if (!updatedGroup.isPresent()) {
- logFailToLoadUpdatedGroup(event);
- return;
- }
-
- InternalGroup group = updatedGroup.get();
- List<AccountGroupMemberAudit> auditInserts = new ArrayList<>();
- List<AccountGroupMemberAudit> auditUpdates = new ArrayList<>();
- try (ReviewDb db = unwrapDb(schema.open())) {
- for (Account.Id accountId : event.getModifiedMembers()) {
- AccountGroupMemberAudit audit = null;
- ResultSet<AccountGroupMemberAudit> audits =
- db.accountGroupMembersAudit().byGroupAccount(group.getId(), accountId);
- for (AccountGroupMemberAudit a : audits) {
- if (a.isActive()) {
- audit = a;
- break;
- }
- }
-
- if (audit != null) {
- audit.removed(event.getActor(), event.getTimestamp());
- auditUpdates.add(audit);
- continue;
- }
- AccountGroupMember.Key key = new AccountGroupMember.Key(accountId, group.getId());
- audit =
- new AccountGroupMemberAudit(
- new AccountGroupMember(key), event.getActor(), event.getTimestamp());
- audit.removedLegacy();
- auditInserts.add(audit);
- }
- db.accountGroupMembersAudit().update(auditUpdates);
- db.accountGroupMembersAudit().insert(auditInserts);
- } catch (OrmException e) {
- logOrmException(
- "Cannot log delete accounts from group event performed by user",
- event,
- group.getName(),
- e);
- }
- }
-
- @Override
- public void onAddSubgroups(GroupSubgroupAuditEvent event) {
- Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
- if (!updatedGroup.isPresent()) {
- logFailToLoadUpdatedGroup(event);
- return;
- }
-
- InternalGroup group = updatedGroup.get();
- try (ReviewDb db = unwrapDb(schema.open())) {
- db.accountGroupByIdAud().insert(toAccountGroupByIdAudits(event, group.getId()));
- } catch (OrmException e) {
- logOrmException(
- "Cannot log add groups to group event performed by user", event, group.getName(), e);
- }
- }
-
- @Override
- public void onDeleteSubgroups(GroupSubgroupAuditEvent event) {
- Optional<InternalGroup> updatedGroup = groupCache.get(event.getUpdatedGroup());
- if (!updatedGroup.isPresent()) {
- logFailToLoadUpdatedGroup(event);
- return;
- }
-
- InternalGroup group = updatedGroup.get();
- List<AccountGroupByIdAud> auditUpdates = new ArrayList<>();
- try (ReviewDb db = unwrapDb(schema.open())) {
- for (AccountGroup.UUID uuid : event.getModifiedSubgroups()) {
- AccountGroupByIdAud audit = null;
- ResultSet<AccountGroupByIdAud> audits =
- db.accountGroupByIdAud().byGroupInclude(updatedGroup.get().getId(), uuid);
- for (AccountGroupByIdAud a : audits) {
- if (a.isActive()) {
- audit = a;
- break;
- }
- }
-
- if (audit != null) {
- audit.removed(event.getActor(), event.getTimestamp());
- auditUpdates.add(audit);
- }
- }
- db.accountGroupByIdAud().update(auditUpdates);
- } catch (OrmException e) {
- logOrmException(
- "Cannot log delete groups from group event performed by user", event, group.getName(), e);
- }
- }
-
- private void logFailToLoadUpdatedGroup(GroupAuditEvent event) {
- ImmutableList<String> descriptions = createEventDescriptions(event, "(fail to load group)");
- String message =
- createErrorMessage("Fail to load the updated group", event.getActor(), descriptions);
- log.error(message);
- }
-
- private void logOrmException(
- String header, GroupAuditEvent event, String updatedGroupName, OrmException e) {
- ImmutableList<String> descriptions = createEventDescriptions(event, updatedGroupName);
- String message = createErrorMessage(header, event.getActor(), descriptions);
- log.error(message, e);
- }
-
- private ImmutableList<String> createEventDescriptions(
- GroupAuditEvent event, String updatedGroupName) {
- ImmutableList.Builder<String> builder = ImmutableList.builder();
- if (event instanceof GroupMemberAuditEvent) {
- GroupMemberAuditEvent memberAuditEvent = (GroupMemberAuditEvent) event;
- for (Account.Id accountId : memberAuditEvent.getModifiedMembers()) {
- String userName = getUserName(accountId).orElse("");
- builder.add(
- MessageFormat.format(
- "account {0}/{1}, group {2}/{3}",
- accountId, userName, event.getUpdatedGroup(), updatedGroupName));
- }
- } else if (event instanceof GroupSubgroupAuditEvent) {
- GroupSubgroupAuditEvent subgroupAuditEvent = (GroupSubgroupAuditEvent) event;
- for (AccountGroup.UUID groupUuid : subgroupAuditEvent.getModifiedSubgroups()) {
- String groupName = groupBackend.get(groupUuid).getName();
- builder.add(
- MessageFormat.format(
- "group {0}/{1}, group {2}/{3}",
- groupUuid, groupName, subgroupAuditEvent.getUpdatedGroup(), updatedGroupName));
- }
- }
-
- return builder.build();
- }
-
- private String createErrorMessage(
- String header, Account.Id me, ImmutableList<String> descriptions) {
- StringBuilder message = new StringBuilder(header);
- message.append(" ");
- message.append(me);
- message.append("/");
- message.append(getUserName(me).orElse(null));
- message.append(": ");
- message.append(Joiner.on("; ").join(descriptions));
- return message.toString();
- }
-
- private Optional<String> getUserName(Account.Id accountId) {
- return accountCache.get(accountId).map(AccountState::getUserName).orElse(Optional.empty());
- }
-
- private static ImmutableSet<AccountGroupMemberAudit> toAccountGroupMemberAudits(
- GroupMemberAuditEvent event, AccountGroup.Id updatedGroupId) {
- Timestamp timestamp = event.getTimestamp();
- Account.Id actor = event.getActor();
- return event
- .getModifiedMembers()
- .stream()
- .map(
- member ->
- new AccountGroupMemberAudit(
- new AccountGroupMemberAudit.Key(member, updatedGroupId, timestamp), actor))
- .collect(toImmutableSet());
- }
-
- private static ImmutableSet<AccountGroupByIdAud> toAccountGroupByIdAudits(
- GroupSubgroupAuditEvent event, AccountGroup.Id updatedGroupId) {
- Timestamp timestamp = event.getTimestamp();
- Account.Id actor = event.getActor();
- return event
- .getModifiedSubgroups()
- .stream()
- .map(
- subgroup ->
- new AccountGroupByIdAud(
- new AccountGroupByIdAud.Key(updatedGroupId, subgroup, timestamp), actor))
- .collect(toImmutableSet());
- }
-}
diff --git a/java/com/google/gerrit/server/group/Module.java b/java/com/google/gerrit/server/group/Module.java
deleted file mode 100644
index a1d8378b..0000000
--- a/java/com/google/gerrit/server/group/Module.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.google.gerrit.server.group;
-
-import com.google.gerrit.extensions.config.FactoryModule;
-import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.server.audit.group.GroupAuditListener;
-import com.google.gerrit.server.notedb.GroupsMigration;
-
-public class Module extends FactoryModule {
- private final GroupsMigration groupsMigration;
-
- public Module(GroupsMigration groupsMigration) {
- this.groupsMigration = groupsMigration;
- }
-
- @Override
- protected void configure() {
- if (!groupsMigration.disableGroupReviewDb()) {
- // DbGroupAuditListener is used solely for the ReviewDb audit log. It does not respect
- // ReviewDb wrappers that disable reads. Hence, we don't want to bind it if ReviewDb is
- // disabled.
- DynamicSet.bind(binder(), GroupAuditListener.class).to(DbGroupAuditListener.class);
- }
- }
-}
diff --git a/java/com/google/gerrit/server/group/SystemGroupBackend.java b/java/com/google/gerrit/server/group/SystemGroupBackend.java
index 91cc11c..ebbb7a1 100644
--- a/java/com/google/gerrit/server/group/SystemGroupBackend.java
+++ b/java/com/google/gerrit/server/group/SystemGroupBackend.java
@@ -24,7 +24,6 @@
import com.google.gerrit.common.data.GroupDescription;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.StartupCheck;
import com.google.gerrit.server.StartupException;
@@ -34,8 +33,6 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -191,13 +188,11 @@
public static class NameCheck implements StartupCheck {
private final Config cfg;
private final Groups groups;
- private final SchemaFactory<ReviewDb> schema;
@Inject
- NameCheck(@GerritServerConfig Config cfg, Groups groups, SchemaFactory<ReviewDb> schema) {
+ NameCheck(@GerritServerConfig Config cfg, Groups groups) {
this.cfg = cfg;
this.groups = groups;
- this.schema = schema;
}
@Override
@@ -216,14 +211,14 @@
}
Optional<GroupReference> conflictingGroup;
- try (ReviewDb db = schema.open()) {
+ try {
conflictingGroup =
groups
- .getAllGroupReferences(db)
+ .getAllGroupReferences()
.filter(group -> hasConfiguredName(byLowerCaseConfiguredName, group))
.findAny();
- } catch (OrmException | IOException | ConfigInvalidException ignored) {
+ } catch (IOException | ConfigInvalidException ignored) {
return;
}
diff --git a/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java b/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
index 63ae8ca..62cc20d 100644
--- a/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
+++ b/java/com/google/gerrit/server/group/db/GroupConfigCommitMessage.java
@@ -16,6 +16,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import com.google.gerrit.server.group.InternalGroup;
import java.util.Optional;
import java.util.Set;
@@ -55,9 +56,12 @@
StringJoiner footerJoiner = new StringJoiner("\n", "\n\n", "");
footerJoiner.setEmptyValue("");
- getFooterForRename().ifPresent(footerJoiner::add);
- getFootersForMemberModifications().forEach(footerJoiner::add);
- getFootersForSubgroupModifications().forEach(footerJoiner::add);
+ Streams.concat(
+ Streams.stream(getFooterForRename()),
+ getFootersForMemberModifications(),
+ getFootersForSubgroupModifications())
+ .sorted()
+ .forEach(footerJoiner::add);
String footer = footerJoiner.toString();
return summaryLine + footer;
diff --git a/java/com/google/gerrit/server/group/db/Groups.java b/java/com/google/gerrit/server/group/db/Groups.java
index bbdc8d2..46fa998 100644
--- a/java/com/google/gerrit/server/group/db/Groups.java
+++ b/java/com/google/gerrit/server/group/db/Groups.java
@@ -14,28 +14,15 @@
package com.google.gerrit.server.group.db;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Streams;
import com.google.gerrit.common.data.GroupReference;
-import com.google.gerrit.common.errors.NoSuchGroupException;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.notedb.GroupsMigration;
-import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -48,10 +35,10 @@
/**
* A database accessor for read calls related to groups.
*
- * <p>All calls which read group related details from the database (either ReviewDb or NoteDb) are
- * gathered here. Other classes should always use this class instead of accessing the database
- * directly. There are a few exceptions though: schema classes, wrapper classes, and classes
- * executed during init. The latter ones should use {@code GroupsOnInit} instead.
+ * <p>All calls which read group related details from the database are gathered here. Other classes
+ * should always use this class instead of accessing the database directly. There are a few
+ * exceptions though: schema classes, wrapper classes, and classes executed during init. The latter
+ * ones should use {@code GroupsOnInit} instead.
*
* <p>Most callers should not need to read groups directly from the database; they should use the
* {@link com.google.gerrit.server.account.GroupCache GroupCache} instead.
@@ -60,18 +47,13 @@
*/
@Singleton
public class Groups {
- private final GroupsMigration groupsMigration;
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final AuditLogReader auditLogReader;
@Inject
public Groups(
- GroupsMigration groupsMigration,
- GitRepositoryManager repoManager,
- AllUsersName allUsersName,
- AuditLogReader auditLogReader) {
- this.groupsMigration = groupsMigration;
+ GitRepositoryManager repoManager, AllUsersName allUsersName, AuditLogReader auditLogReader) {
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.auditLogReader = auditLogReader;
@@ -80,27 +62,16 @@
/**
* Returns the {@code InternalGroup} for the specified UUID if it exists.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @param groupUuid the UUID of the group
* @return the found {@code InternalGroup} if it exists, or else an empty {@code Optional}
- * @throws OrmDuplicateKeyException if multiple groups are found for the specified UUID
- * @throws OrmException if the group couldn't be retrieved from ReviewDb
* @throws IOException if the group couldn't be retrieved from NoteDb
* @throws ConfigInvalidException if the group couldn't be retrieved from NoteDb
*/
- public Optional<InternalGroup> getGroup(ReviewDb db, AccountGroup.UUID groupUuid)
- throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- return getGroupFromNoteDb(allUsersRepo, groupUuid);
- }
+ public Optional<InternalGroup> getGroup(AccountGroup.UUID groupUuid)
+ throws IOException, ConfigInvalidException {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ return getGroupFromNoteDb(allUsersRepo, groupUuid);
}
-
- Optional<AccountGroup> accountGroup = getGroupFromReviewDb(db, groupUuid);
- if (!accountGroup.isPresent()) {
- return Optional.empty();
- }
- return Optional.of(asInternalGroup(db, accountGroup.get()));
}
private static Optional<InternalGroup> getGroupFromNoteDb(
@@ -116,130 +87,31 @@
return loadedGroup;
}
- public static InternalGroup asInternalGroup(ReviewDb db, AccountGroup accountGroup)
- throws OrmException {
- ImmutableSet<Account.Id> members =
- getMembersFromReviewDb(db, accountGroup.getId()).collect(toImmutableSet());
- ImmutableSet<AccountGroup.UUID> subgroups =
- getSubgroupsFromReviewDb(db, accountGroup.getId()).collect(toImmutableSet());
- return InternalGroup.create(accountGroup, members, subgroups);
- }
-
- /**
- * Returns the {@code AccountGroup} for the specified UUID.
- *
- * @param db the {@code ReviewDb} instance to use for lookups
- * @param groupUuid the UUID of the group
- * @return the {@code AccountGroup} which has the specified UUID
- * @throws OrmDuplicateKeyException if multiple groups are found for the specified UUID
- * @throws OrmException if the group couldn't be retrieved from ReviewDb
- * @throws NoSuchGroupException if a group with such a UUID doesn't exist
- */
- static AccountGroup getExistingGroupFromReviewDb(ReviewDb db, AccountGroup.UUID groupUuid)
- throws OrmException, NoSuchGroupException {
- Optional<AccountGroup> group = getGroupFromReviewDb(db, groupUuid);
- return group.orElseThrow(() -> new NoSuchGroupException(groupUuid));
- }
-
- /**
- * Returns the {@code AccountGroup} for the specified UUID if it exists.
- *
- * @param db the {@code ReviewDb} instance to use for lookups
- * @param groupUuid the UUID of the group
- * @return the found {@code AccountGroup} if it exists, or else an empty {@code Optional}
- * @throws OrmDuplicateKeyException if multiple groups are found for the specified UUID
- * @throws OrmException if the group couldn't be retrieved from ReviewDb
- */
- private static Optional<AccountGroup> getGroupFromReviewDb(
- ReviewDb db, AccountGroup.UUID groupUuid) throws OrmException {
- List<AccountGroup> accountGroups = db.accountGroups().byUUID(groupUuid).toList();
- if (accountGroups.size() == 1) {
- return Optional.of(Iterables.getOnlyElement(accountGroups));
- } else if (accountGroups.isEmpty()) {
- return Optional.empty();
- } else {
- throw new OrmDuplicateKeyException("Duplicate group UUID " + groupUuid);
- }
- }
-
/**
* Returns {@code GroupReference}s for all internal groups.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @return a stream of the {@code GroupReference}s of all internal groups
- * @throws OrmException if an error occurs while reading from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
*/
- public Stream<GroupReference> getAllGroupReferences(ReviewDb db)
- throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- return GroupNameNotes.loadAllGroups(allUsersRepo).stream();
- }
+ public Stream<GroupReference> getAllGroupReferences() throws IOException, ConfigInvalidException {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ return GroupNameNotes.loadAllGroups(allUsersRepo).stream();
}
-
- return Streams.stream(db.accountGroups().all())
- .map(group -> new GroupReference(group.getGroupUUID(), group.getName()));
- }
-
- /**
- * Returns the members (accounts) of a group.
- *
- * <p><strong>Note</strong>: This method doesn't check whether the accounts exist!
- *
- * @param db the {@code ReviewDb} instance to use for lookups
- * @param groupId the ID of the group
- * @return a stream of the IDs of the members
- * @throws OrmException if an error occurs while reading from ReviewDb
- */
- static Stream<Account.Id> getMembersFromReviewDb(ReviewDb db, AccountGroup.Id groupId)
- throws OrmException {
- ResultSet<AccountGroupMember> accountGroupMembers = db.accountGroupMembers().byGroup(groupId);
- return Streams.stream(accountGroupMembers).map(AccountGroupMember::getAccountId);
- }
-
- /**
- * Returns the subgroups of a group.
- *
- * <p>This parent group must be an internal group whereas the subgroups can either be internal or
- * external groups.
- *
- * <p><strong>Note</strong>: This method doesn't check whether the subgroups exist!
- *
- * @param db the {@code ReviewDb} instance to use for lookups
- * @param groupId the ID of the group
- * @return a stream of the UUIDs of the subgroups
- * @throws OrmException if an error occurs while reading from ReviewDb
- */
- static Stream<AccountGroup.UUID> getSubgroupsFromReviewDb(ReviewDb db, AccountGroup.Id groupId)
- throws OrmException {
- ResultSet<AccountGroupById> accountGroupByIds = db.accountGroupById().byGroup(groupId);
- return Streams.stream(accountGroupByIds).map(AccountGroupById::getIncludeUUID).distinct();
}
/**
* Returns all known external groups. External groups are 'known' when they are specified as a
* subgroup of an internal group.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @return a stream of the UUIDs of the known external groups
- * @throws OrmException if an error occurs while reading from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the data in NoteDb is in an incorrect format
*/
- public Stream<AccountGroup.UUID> getExternalGroups(ReviewDb db)
- throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- return getExternalGroupsFromNoteDb(allUsersRepo);
- }
+ public Stream<AccountGroup.UUID> getExternalGroups() throws IOException, ConfigInvalidException {
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ return getExternalGroupsFromNoteDb(allUsersRepo);
}
-
- return Streams.stream(db.accountGroupById().all())
- .map(AccountGroupById::getIncludeUUID)
- .distinct()
- .filter(groupUuid -> !AccountGroup.isInternalGroup(groupUuid));
}
private static Stream<AccountGroup.UUID> getExternalGroupsFromNoteDb(Repository allUsersRepo)
@@ -259,50 +131,28 @@
/**
* Returns the membership audit records for a given group.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @param repo All-Users repository.
* @param groupUuid the UUID of the group
* @return the audit records, in arbitrary order; empty if the group does not exist
- * @throws OrmException if an error occurs while reading from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the group couldn't be retrieved from NoteDb
*/
- public List<AccountGroupMemberAudit> getMembersAudit(
- ReviewDb db, Repository repo, AccountGroup.UUID groupUuid)
- throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- return auditLogReader.getMembersAudit(repo, groupUuid);
- }
- Optional<AccountGroup> group = getGroupFromReviewDb(db, groupUuid);
- if (!group.isPresent()) {
- return ImmutableList.of();
- }
-
- return db.accountGroupMembersAudit().byGroup(group.get().getId()).toList();
+ public List<AccountGroupMemberAudit> getMembersAudit(Repository repo, AccountGroup.UUID groupUuid)
+ throws IOException, ConfigInvalidException {
+ return auditLogReader.getMembersAudit(repo, groupUuid);
}
/**
* Returns the subgroup audit records for a given group.
*
- * @param db the {@code ReviewDb} instance to use for lookups
* @param repo All-Users repository.
* @param groupUuid the UUID of the group
* @return the audit records, in arbitrary order; empty if the group does not exist
- * @throws OrmException if an error occurs while reading from ReviewDb
* @throws IOException if an error occurs while reading from NoteDb
* @throws ConfigInvalidException if the group couldn't be retrieved from NoteDb
*/
- public List<AccountGroupByIdAud> getSubgroupsAudit(
- ReviewDb db, Repository repo, AccountGroup.UUID groupUuid)
- throws OrmException, IOException, ConfigInvalidException {
- if (groupsMigration.readFromNoteDb()) {
- return auditLogReader.getSubgroupsAudit(repo, groupUuid);
- }
- Optional<AccountGroup> group = getGroupFromReviewDb(db, groupUuid);
- if (!group.isPresent()) {
- return ImmutableList.of();
- }
-
- return db.accountGroupByIdAud().byGroup(group.get().getId()).toList();
+ public List<AccountGroupByIdAud> getSubgroupsAudit(Repository repo, AccountGroup.UUID groupUuid)
+ throws IOException, ConfigInvalidException {
+ return auditLogReader.getSubgroupsAudit(repo, groupUuid);
}
}
diff --git a/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java b/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
index 2335d32..9b86221 100644
--- a/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
+++ b/java/com/google/gerrit/server/group/db/GroupsConsistencyChecker.java
@@ -26,7 +26,6 @@
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.InternalGroup;
-import com.google.gerrit.server.notedb.GroupsMigration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
@@ -52,7 +51,6 @@
private final Accounts accounts;
private final GitRepositoryManager repoManager;
private final GroupsNoteDbConsistencyChecker globalChecker;
- private final GroupsMigration groupsMigration;
@Inject
GroupsConsistencyChecker(
@@ -60,22 +58,16 @@
GroupBackend groupBackend,
Accounts accounts,
GitRepositoryManager repositoryManager,
- GroupsNoteDbConsistencyChecker globalChecker,
- GroupsMigration groupsMigration) {
+ GroupsNoteDbConsistencyChecker globalChecker) {
this.allUsersName = allUsersName;
this.groupBackend = groupBackend;
this.accounts = accounts;
this.repoManager = repositoryManager;
this.globalChecker = globalChecker;
- this.groupsMigration = groupsMigration;
}
/** Checks that all internal group references exist, and that no groups have cycles. */
public List<ConsistencyProblemInfo> check() throws IOException {
- if (!groupsMigration.writeToNoteDb()) {
- return new ArrayList<>();
- }
-
try (Repository repo = repoManager.openRepository(allUsersName)) {
GroupsNoteDbConsistencyChecker.Result result = globalChecker.check(repo);
if (!result.problems.isEmpty()) {
diff --git a/java/com/google/gerrit/server/group/db/GroupsUpdate.java b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
index f10409e..1fad1fb 100644
--- a/java/com/google/gerrit/server/group/db/GroupsUpdate.java
+++ b/java/com/google/gerrit/server/group/db/GroupsUpdate.java
@@ -14,14 +14,10 @@
package com.google.gerrit.server.group.db;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.gerrit.server.group.db.Groups.getExistingGroupFromReviewDb;
-
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.gerrit.common.Nullable;
@@ -29,12 +25,7 @@
import com.google.gerrit.common.errors.NoSuchGroupException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbUtil;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
@@ -51,11 +42,9 @@
import com.google.gerrit.server.git.RenameGroupOp;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.index.group.GroupIndexer;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -75,10 +64,10 @@
/**
* A database accessor for write calls related to groups.
*
- * <p>All calls which write group related details to the database (either ReviewDb or NoteDb) are
- * gathered here. Other classes should always use this class instead of accessing the database
- * directly. There are a few exceptions though: schema classes, wrapper classes, and classes
- * executed during init. The latter ones should use {@code GroupsOnInit} instead.
+ * <p>All calls which write group related details to the database are gathered here. Other classes
+ * should always use this class instead of accessing the database directly. There are a few
+ * exceptions though: schema classes, wrapper classes, and classes executed during init. The latter
+ * ones should use {@code GroupsOnInit} instead.
*
* <p>If not explicitly stated, all methods of this class refer to <em>internal</em> groups.
*/
@@ -109,7 +98,6 @@
private final AuditLogFormatter auditLogFormatter;
private final PersonIdent authorIdent;
private final MetaDataUpdateFactory metaDataUpdateFactory;
- private final GroupsMigration groupsMigration;
private final GitReferenceUpdated gitRefUpdated;
private final RetryHelper retryHelper;
@@ -127,7 +115,6 @@
@GerritServerId String serverId,
@GerritPersonIdent PersonIdent serverIdent,
MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
- GroupsMigration groupsMigration,
GitReferenceUpdated gitRefUpdated,
RetryHelper retryHelper,
@Assisted @Nullable IdentifiedUser currentUser) {
@@ -138,7 +125,6 @@
this.indexer = indexer;
this.auditService = auditService;
this.renameGroupOpFactory = renameGroupOpFactory;
- this.groupsMigration = groupsMigration;
this.gitRefUpdated = gitRefUpdated;
this.retryHelper = retryHelper;
this.currentUser = currentUser;
@@ -184,37 +170,18 @@
/**
* Creates the specified group for the specified members (accounts).
*
- * @param db the {@code ReviewDb} instance to update
* @param groupCreation an {@code InternalGroupCreation} which specifies all mandatory properties
* of the group
* @param groupUpdate an {@code InternalGroupUpdate} which specifies optional properties of the
* group. If this {@code InternalGroupUpdate} updates a property which was already specified
* by the {@code InternalGroupCreation}, the value of this {@code InternalGroupUpdate} wins.
- * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
* @throws OrmDuplicateKeyException if a group with the chosen name already exists
* @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
* @return the created {@code InternalGroup}
*/
public InternalGroup createGroup(
- ReviewDb db, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmException, IOException, ConfigInvalidException {
- if (!groupsMigration.disableGroupReviewDb()) {
- if (!groupUpdate.getUpdatedOn().isPresent()) {
- // Set updatedOn to a specific value so that the same timestamp is used for ReviewDb and
- // NoteDb.
- groupUpdate = groupUpdate.toBuilder().setUpdatedOn(TimeUtil.nowTs()).build();
- }
-
- InternalGroup createdGroupInReviewDb =
- createGroupInReviewDb(ReviewDbUtil.unwrapDb(db), groupCreation, groupUpdate);
-
- if (!groupsMigration.writeToNoteDb()) {
- updateCachesOnGroupCreation(createdGroupInReviewDb);
- dispatchAuditEventsOnGroupCreation(createdGroupInReviewDb);
- return createdGroupInReviewDb;
- }
- }
-
+ InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
+ throws OrmDuplicateKeyException, IOException, ConfigInvalidException {
InternalGroup createdGroup = createGroupInNoteDbWithRetry(groupCreation, groupUpdate);
updateCachesOnGroupCreation(createdGroup);
dispatchAuditEventsOnGroupCreation(createdGroup);
@@ -224,67 +191,32 @@
/**
* Updates the specified group.
*
- * @param db the {@code ReviewDb} instance to update
* @param groupUuid the UUID of the group to update
* @param groupUpdate an {@code InternalGroupUpdate} which indicates the desired updates on the
* group
- * @throws OrmException if an error occurs while reading/writing from/to ReviewDb
- * @throws com.google.gwtorm.server.OrmDuplicateKeyException if the new name of the group is used
- * by another group
+ * @throws OrmDuplicateKeyException if the new name of the group is used by another group
* @throws IOException if indexing fails, or an error occurs while reading/writing from/to NoteDb
* @throws NoSuchGroupException if the specified group doesn't exist
*/
- public void updateGroup(ReviewDb db, AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws OrmException, IOException, NoSuchGroupException, ConfigInvalidException {
+ public void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
+ throws OrmDuplicateKeyException, IOException, NoSuchGroupException, ConfigInvalidException {
Optional<Timestamp> updatedOn = groupUpdate.getUpdatedOn();
if (!updatedOn.isPresent()) {
- // Set updatedOn to a specific value so that the same timestamp is used for ReviewDb and
- // NoteDb. This timestamp is also used by audit events.
updatedOn = Optional.of(TimeUtil.nowTs());
groupUpdate = groupUpdate.toBuilder().setUpdatedOn(updatedOn.get()).build();
}
- UpdateResult result = updateGroupInDb(db, groupUuid, groupUpdate);
+ UpdateResult result = updateGroupInDb(groupUuid, groupUpdate);
updateCachesOnGroupUpdate(result);
dispatchAuditEventsOnGroupUpdate(result, updatedOn.get());
}
@VisibleForTesting
- public UpdateResult updateGroupInDb(
- ReviewDb db, AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
- UpdateResult reviewDbUpdateResult = null;
- if (!groupsMigration.disableGroupReviewDb()) {
- AccountGroup group = getExistingGroupFromReviewDb(ReviewDbUtil.unwrapDb(db), groupUuid);
- reviewDbUpdateResult = updateGroupInReviewDb(ReviewDbUtil.unwrapDb(db), group, groupUpdate);
-
- if (!groupsMigration.writeToNoteDb()) {
- return reviewDbUpdateResult;
- }
- }
-
+ public UpdateResult updateGroupInDb(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
+ throws OrmDuplicateKeyException, NoSuchGroupException, IOException, ConfigInvalidException {
Optional<UpdateResult> noteDbUpdateResult =
updateGroupInNoteDbWithRetry(groupUuid, groupUpdate);
- return noteDbUpdateResult.orElse(reviewDbUpdateResult);
- }
-
- private InternalGroup createGroupInReviewDb(
- ReviewDb db, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmException {
-
- AccountGroupName gn = new AccountGroupName(groupCreation.getNameKey(), groupCreation.getId());
- // first insert the group name to validate that the group name hasn't
- // already been used to create another group
- db.accountGroupNames().insert(ImmutableList.of(gn));
-
- Timestamp createdOn = groupUpdate.getUpdatedOn().orElseGet(TimeUtil::nowTs);
- AccountGroup group = createAccountGroup(groupCreation, createdOn);
- UpdateResult updateResult = updateGroupInReviewDb(db, group, groupUpdate);
- return InternalGroup.create(
- group,
- updateResult.getAddedMembers(),
- updateResult.getAddedSubgroups(),
- updateResult.getRefState());
+ return noteDbUpdateResult.orElse(null);
}
public static AccountGroup createAccountGroup(
@@ -308,157 +240,9 @@
groupUpdate.getVisibleToAll().ifPresent(group::setVisibleToAll);
}
- private UpdateResult updateGroupInReviewDb(
- ReviewDb db, AccountGroup group, InternalGroupUpdate groupUpdate) throws OrmException {
- AccountGroup.NameKey originalName = group.getNameKey();
- applyUpdate(group, groupUpdate);
- AccountGroup.NameKey updatedName = group.getNameKey();
-
- // The name must be inserted first so that we stop early for already used names.
- updateNameInReviewDb(db, group.getId(), originalName, updatedName);
- db.accountGroups().upsert(ImmutableList.of(group));
-
- ImmutableSet<Account.Id> originalMembers =
- Groups.getMembersFromReviewDb(db, group.getId()).collect(toImmutableSet());
- ImmutableSet<Account.Id> updatedMembers =
- ImmutableSet.copyOf(groupUpdate.getMemberModification().apply(originalMembers));
- ImmutableSet<AccountGroup.UUID> originalSubgroups =
- Groups.getSubgroupsFromReviewDb(db, group.getId()).collect(toImmutableSet());
- ImmutableSet<AccountGroup.UUID> updatedSubgroups =
- ImmutableSet.copyOf(groupUpdate.getSubgroupModification().apply(originalSubgroups));
-
- Set<Account.Id> addedMembers =
- addGroupMembersInReviewDb(db, group.getId(), originalMembers, updatedMembers);
- Set<Account.Id> deletedMembers =
- deleteGroupMembersInReviewDb(db, group.getId(), originalMembers, updatedMembers);
- Set<AccountGroup.UUID> addedSubgroups =
- addSubgroupsInReviewDb(db, group.getId(), originalSubgroups, updatedSubgroups);
- Set<AccountGroup.UUID> deletedSubgroups =
- deleteSubgroupsInReviewDb(db, group.getId(), originalSubgroups, updatedSubgroups);
-
- UpdateResult.Builder resultBuilder =
- UpdateResult.builder()
- .setGroupUuid(group.getGroupUUID())
- .setGroupId(group.getId())
- .setGroupName(group.getNameKey())
- .setAddedMembers(addedMembers)
- .setDeletedMembers(deletedMembers)
- .setAddedSubgroups(addedSubgroups)
- .setDeletedSubgroups(deletedSubgroups);
- if (!Objects.equals(originalName, updatedName)) {
- resultBuilder.setPreviousGroupName(originalName);
- }
- return resultBuilder.build();
- }
-
- private static void updateNameInReviewDb(
- ReviewDb db,
- AccountGroup.Id groupId,
- AccountGroup.NameKey originalName,
- AccountGroup.NameKey updatedName)
- throws OrmException {
- try {
- AccountGroupName id = new AccountGroupName(updatedName, groupId);
- db.accountGroupNames().insert(ImmutableList.of(id));
- } catch (OrmException e) {
- AccountGroupName other = db.accountGroupNames().get(updatedName);
- if (other != null) {
- // If we are using this identity, don't report the exception.
- if (other.getId().equals(groupId)) {
- return;
- }
- }
- throw e;
- }
- db.accountGroupNames().deleteKeys(ImmutableList.of(originalName));
- }
-
- private static Set<Account.Id> addGroupMembersInReviewDb(
- ReviewDb db,
- AccountGroup.Id groupId,
- ImmutableSet<Account.Id> originalMembers,
- ImmutableSet<Account.Id> updatedMembers)
- throws OrmException {
- Set<Account.Id> accountIds = Sets.difference(updatedMembers, originalMembers);
- if (accountIds.isEmpty()) {
- return accountIds;
- }
-
- ImmutableSet<AccountGroupMember> newMembers = toAccountGroupMembers(groupId, accountIds);
- db.accountGroupMembers().insert(newMembers);
- return accountIds;
- }
-
- private static Set<Account.Id> deleteGroupMembersInReviewDb(
- ReviewDb db,
- AccountGroup.Id groupId,
- ImmutableSet<Account.Id> originalMembers,
- ImmutableSet<Account.Id> updatedMembers)
- throws OrmException {
- Set<Account.Id> accountIds = Sets.difference(originalMembers, updatedMembers);
- if (accountIds.isEmpty()) {
- return accountIds;
- }
-
- ImmutableSet<AccountGroupMember> membersToRemove = toAccountGroupMembers(groupId, accountIds);
- db.accountGroupMembers().delete(membersToRemove);
- return accountIds;
- }
-
- private static ImmutableSet<AccountGroupMember> toAccountGroupMembers(
- AccountGroup.Id groupId, Set<Account.Id> accountIds) {
- return accountIds
- .stream()
- .map(accountId -> new AccountGroupMember.Key(accountId, groupId))
- .map(AccountGroupMember::new)
- .collect(toImmutableSet());
- }
-
- private static Set<AccountGroup.UUID> addSubgroupsInReviewDb(
- ReviewDb db,
- AccountGroup.Id parentGroupId,
- ImmutableSet<AccountGroup.UUID> originalSubgroups,
- ImmutableSet<AccountGroup.UUID> updatedSubgroups)
- throws OrmException {
- Set<AccountGroup.UUID> subgroupUuids = Sets.difference(updatedSubgroups, originalSubgroups);
- if (subgroupUuids.isEmpty()) {
- return subgroupUuids;
- }
-
- ImmutableSet<AccountGroupById> newSubgroups = toAccountGroupByIds(parentGroupId, subgroupUuids);
- db.accountGroupById().insert(newSubgroups);
- return subgroupUuids;
- }
-
- private static Set<AccountGroup.UUID> deleteSubgroupsInReviewDb(
- ReviewDb db,
- AccountGroup.Id parentGroupId,
- ImmutableSet<AccountGroup.UUID> originalSubgroups,
- ImmutableSet<AccountGroup.UUID> updatedSubgroups)
- throws OrmException {
- Set<AccountGroup.UUID> subgroupUuids = Sets.difference(originalSubgroups, updatedSubgroups);
- if (subgroupUuids.isEmpty()) {
- return subgroupUuids;
- }
-
- ImmutableSet<AccountGroupById> subgroupsToRemove =
- toAccountGroupByIds(parentGroupId, subgroupUuids);
- db.accountGroupById().delete(subgroupsToRemove);
- return subgroupUuids;
- }
-
- private static ImmutableSet<AccountGroupById> toAccountGroupByIds(
- AccountGroup.Id parentGroupId, Set<AccountGroup.UUID> subgroupUuids) {
- return subgroupUuids
- .stream()
- .map(subgroupUuid -> new AccountGroupById.Key(parentGroupId, subgroupUuid))
- .map(AccountGroupById::new)
- .collect(toImmutableSet());
- }
-
private InternalGroup createGroupInNoteDbWithRetry(
InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmException {
+ throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
try {
return retryHelper.execute(
RetryHelper.ActionType.GROUP_UPDATE,
@@ -514,14 +298,11 @@
private Optional<UpdateResult> updateGroupInNoteDb(
AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
- throws IOException, ConfigInvalidException, OrmDuplicateKeyException, NoSuchGroupException {
+ throws IOException, ConfigInvalidException, OrmDuplicateKeyException {
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
GroupConfig groupConfig = GroupConfig.loadForGroup(allUsersRepo, groupUuid);
groupConfig.setGroupUpdate(groupUpdate, auditLogFormatter);
if (!groupConfig.getLoadedGroup().isPresent()) {
- if (groupsMigration.readFromNoteDb()) {
- throw new NoSuchGroupException(groupUuid);
- }
return Optional.empty();
}
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 262c732..6dbad6a 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -517,7 +517,6 @@
/** Number of unresolved comments of the change. */
public static final FieldDef<ChangeData, Integer> UNRESOLVED_COMMENT_COUNT =
intRange(ChangeQueryBuilder.FIELD_UNRESOLVED_COMMENT_COUNT)
- .stored()
.build(ChangeData::unresolvedCommentCount);
/** Whether the change is mergeable. */
@@ -536,13 +535,11 @@
/** The number of inserted lines in this change. */
public static final FieldDef<ChangeData, Integer> ADDED =
intRange(ChangeQueryBuilder.FIELD_ADDED)
- .stored()
.build(cd -> cd.changedLines().isPresent() ? cd.changedLines().get().insertions : null);
/** The number of deleted lines in this change. */
public static final FieldDef<ChangeData, Integer> DELETED =
intRange(ChangeQueryBuilder.FIELD_DELETED)
- .stored()
.build(cd -> cd.changedLines().isPresent() ? cd.changedLines().get().deletions : null);
/** The total number of modified lines in this change. */
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndexer.java b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
index cf51197..1ea115a 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndexer.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndexer.java
@@ -152,7 +152,7 @@
}
private static boolean autoReindexIfStale(Config cfg) {
- return cfg.getBoolean("index", null, "autoReindexIfStale", true);
+ return cfg.getBoolean("index", null, "autoReindexIfStale", false);
}
/**
diff --git a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
index 47dad94..c90bece 100644
--- a/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
+++ b/java/com/google/gerrit/server/index/group/AllGroupsIndexer.java
@@ -24,14 +24,11 @@
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.index.SiteIndexer;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsNoteDbConsistencyChecker;
import com.google.gerrit.server.index.IndexExecutor;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -52,18 +49,15 @@
public class AllGroupsIndexer extends SiteIndexer<AccountGroup.UUID, InternalGroup, GroupIndex> {
private static final Logger log = LoggerFactory.getLogger(AllGroupsIndexer.class);
- private final SchemaFactory<ReviewDb> schemaFactory;
private final ListeningExecutorService executor;
private final GroupCache groupCache;
private final Groups groups;
@Inject
AllGroupsIndexer(
- SchemaFactory<ReviewDb> schemaFactory,
@IndexExecutor(BATCH) ListeningExecutorService executor,
GroupCache groupCache,
Groups groups) {
- this.schemaFactory = schemaFactory;
this.executor = executor;
this.groupCache = groupCache;
this.groups = groups;
@@ -77,7 +71,7 @@
List<AccountGroup.UUID> uuids;
try {
uuids = collectGroups(progress);
- } catch (OrmException | IOException | ConfigInvalidException e) {
+ } catch (IOException | ConfigInvalidException e) {
log.error("Error collecting groups", e);
return new SiteIndexer.Result(sw, false, 0, 0);
}
@@ -133,13 +127,10 @@
}
private List<AccountGroup.UUID> collectGroups(ProgressMonitor progress)
- throws OrmException, IOException, ConfigInvalidException {
+ throws IOException, ConfigInvalidException {
progress.beginTask("Collecting groups", ProgressMonitor.UNKNOWN);
- try (ReviewDb db = schemaFactory.open()) {
- return groups
- .getAllGroupReferences(db)
- .map(GroupReference::getUUID)
- .collect(toImmutableList());
+ try {
+ return groups.getAllGroupReferences().map(GroupReference::getUUID).collect(toImmutableList());
} finally {
progress.endTask();
}
diff --git a/java/com/google/gerrit/server/index/group/StalenessChecker.java b/java/com/google/gerrit/server/index/group/StalenessChecker.java
index 94e1be7..7900287 100644
--- a/java/com/google/gerrit/server/index/group/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/group/StalenessChecker.java
@@ -21,7 +21,6 @@
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -47,27 +46,20 @@
private final GitRepositoryManager repoManager;
private final IndexConfig indexConfig;
private final AllUsersName allUsers;
- private final GroupsMigration groupsMigration;
@Inject
StalenessChecker(
GroupIndexCollection indexes,
GitRepositoryManager repoManager,
IndexConfig indexConfig,
- AllUsersName allUsers,
- GroupsMigration groupsMigration) {
+ AllUsersName allUsers) {
this.indexes = indexes;
this.repoManager = repoManager;
this.indexConfig = indexConfig;
this.allUsers = allUsers;
- this.groupsMigration = groupsMigration;
}
public boolean isStale(AccountGroup.UUID uuid) throws IOException {
- if (!groupsMigration.readFromNoteDb()) {
- return false; // This class only treats staleness for groups in NoteDb.
- }
-
GroupIndex i = indexes.getSearchIndex();
if (i == null) {
return false; // No index; caller couldn't do anything if it is stale.
diff --git a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
index 10d14a8..a12e226 100644
--- a/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
+++ b/java/com/google/gerrit/server/mail/send/CreateChangeSender.java
@@ -19,7 +19,6 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.ProjectWatches.NotifyType;
import com.google.gerrit.server.mail.send.ProjectWatch.Watchers;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -39,19 +38,16 @@
CreateChangeSender create(Project.NameKey project, Change.Id id);
}
- private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final PermissionBackend permissionBackend;
@Inject
public CreateChangeSender(
EmailArguments ea,
- IdentifiedUser.GenericFactory identifiedUserFactory,
PermissionBackend permissionBackend,
@Assisted Project.NameKey project,
@Assisted Change.Id id)
throws OrmException {
super(ea, newChangeData(ea, project, id));
- this.identifiedUserFactory = identifiedUserFactory;
this.permissionBackend = permissionBackend;
}
@@ -83,7 +79,7 @@
private boolean isOwnerOfProjectOrBranch(Account.Id userId) {
return permissionBackend
- .user(identifiedUserFactory.create(userId))
+ .absentUser(userId)
.ref(change.getDest())
.testOrFalse(RefPermission.WRITE_CONFIG);
}
diff --git a/java/com/google/gerrit/server/notedb/GroupsMigration.java b/java/com/google/gerrit/server/notedb/GroupsMigration.java
deleted file mode 100644
index 293f3c6..0000000
--- a/java/com/google/gerrit/server/notedb/GroupsMigration.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2017 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.server.notedb;
-
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.READ;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.WRITE;
-
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.inject.AbstractModule;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.Set;
-import org.eclipse.jgit.lib.Config;
-
-@Singleton
-public class GroupsMigration {
- public static class Module extends AbstractModule {
- @Override
- public void configure() {
- bind(GroupsMigration.class);
- }
- }
-
- private final boolean writeToNoteDb;
- private final boolean readFromNoteDb;
- private final boolean disableGroupReviewDb;
-
- @Inject
- public GroupsMigration(@GerritServerConfig Config cfg) {
- // TODO(aliceks): Remove these flags when all other necessary TODOs for writing groups to
- // NoteDb have been addressed.
- // Don't flip these flags in a production setting! We only added them to spread the
- // implementation of groups in NoteDb among several changes which are gradually merged.
- this(
- cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, false),
- cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, false),
- cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false));
- }
-
- public GroupsMigration(
- boolean writeToNoteDb, boolean readFromNoteDb, boolean disableGroupReviewDb) {
- this.writeToNoteDb = writeToNoteDb;
- this.readFromNoteDb = readFromNoteDb;
- this.disableGroupReviewDb = disableGroupReviewDb;
- }
-
- public boolean writeToNoteDb() {
- return writeToNoteDb;
- }
-
- public boolean readFromNoteDb() {
- return readFromNoteDb;
- }
-
- public boolean disableGroupReviewDb() {
- return disableGroupReviewDb;
- }
-
- public void setConfigValuesIfNotSetYet(Config cfg) {
- Set<String> subsections = cfg.getSubsections(SECTION_NOTE_DB);
- if (!subsections.contains(GROUPS.key())) {
- cfg.setBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, writeToNoteDb());
- cfg.setBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, readFromNoteDb());
- cfg.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, disableGroupReviewDb());
- }
- }
-}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java b/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
new file mode 100644
index 0000000..0653192
--- /dev/null
+++ b/java/com/google/gerrit/server/notedb/rebuild/GcAllUsers.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2018 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.server.notedb.rebuild;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_GC_SECTION;
+import static org.eclipse.jgit.lib.ConfigConstants.CONFIG_KEY_AUTO;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.GarbageCollectionResult;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.git.GarbageCollection;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class GcAllUsers {
+ private static final Logger log = LoggerFactory.getLogger(GcAllUsers.class);
+
+ private final AllUsersName allUsers;
+ private final GarbageCollection.Factory gcFactory;
+ private final GitRepositoryManager repoManager;
+
+ @Inject
+ GcAllUsers(
+ AllUsersName allUsers,
+ GarbageCollection.Factory gcFactory,
+ GitRepositoryManager repoManager) {
+ this.allUsers = allUsers;
+ this.gcFactory = gcFactory;
+ this.repoManager = repoManager;
+ }
+
+ public void runWithLogger() {
+ // Print log messages using logger, and skip progress.
+ run(s -> log.info(s), null);
+ }
+
+ public void run(PrintWriter writer) {
+ // Print both log messages and progress to given writer.
+ run(checkNotNull(writer)::println, writer);
+ }
+
+ private void run(Consumer<String> logOneLine, @Nullable PrintWriter progressWriter) {
+ if (!(repoManager instanceof LocalDiskRepositoryManager)) {
+ logOneLine.accept("Skipping GC of " + allUsers + "; not a local disk repo");
+ return;
+ }
+ if (!enableAutoGc(logOneLine)) {
+ logOneLine.accept(
+ "Skipping GC of "
+ + allUsers
+ + " due to disabling "
+ + CONFIG_GC_SECTION
+ + "."
+ + CONFIG_KEY_AUTO);
+ logOneLine.accept(
+ "If loading accounts is slow after the NoteDb migration, run `git gc` on "
+ + allUsers
+ + " manually");
+ return;
+ }
+
+ if (progressWriter == null) {
+ // Mimic log line from GarbageCollection.
+ logOneLine.accept("collecting garbage for \"" + allUsers + "\":\n");
+ }
+ GarbageCollectionResult result =
+ gcFactory.create().run(ImmutableList.of(allUsers), progressWriter);
+ if (!result.hasErrors()) {
+ return;
+ }
+ for (GarbageCollectionResult.Error e : result.getErrors()) {
+ switch (e.getType()) {
+ case GC_ALREADY_SCHEDULED:
+ logOneLine.accept("GC already scheduled for " + e.getProjectName());
+ break;
+ case GC_FAILED:
+ logOneLine.accept("GC failed for " + e.getProjectName());
+ break;
+ case REPOSITORY_NOT_FOUND:
+ logOneLine.accept(e.getProjectName() + " repo not found");
+ break;
+ default:
+ logOneLine.accept("GC failed for " + e.getProjectName() + ": " + e.getType());
+ break;
+ }
+ }
+ }
+
+ private boolean enableAutoGc(Consumer<String> logOneLine) {
+ try (Repository repo = repoManager.openRepository(allUsers)) {
+ return repo.getConfig().getInt(CONFIG_GC_SECTION, CONFIG_KEY_AUTO, -1) != 0;
+ } catch (IOException e) {
+ logOneLine.accept(
+ "Error reading config for " + allUsers + ":\n" + Throwables.getStackTraceAsString(e));
+ return false;
+ }
+ }
+}
diff --git a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
index 535736d..65755ed 100644
--- a/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
+++ b/java/com/google/gerrit/server/notedb/rebuild/OnlineNoteDbMigrator.java
@@ -50,19 +50,22 @@
}
}
- private Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
+ private final GcAllUsers gcAllUsers;
private final OnlineUpgrader indexUpgrader;
+ private final Provider<NoteDbMigrator.Builder> migratorBuilderProvider;
private final boolean upgradeIndex;
private final boolean trial;
@Inject
OnlineNoteDbMigrator(
@GerritServerConfig Config cfg,
- Provider<NoteDbMigrator.Builder> migratorBuilderProvider,
+ GcAllUsers gcAllUsers,
OnlineUpgrader indexUpgrader,
+ Provider<NoteDbMigrator.Builder> migratorBuilderProvider,
@Named(TRIAL) boolean trial) {
- this.migratorBuilderProvider = migratorBuilderProvider;
+ this.gcAllUsers = gcAllUsers;
this.indexUpgrader = indexUpgrader;
+ this.migratorBuilderProvider = migratorBuilderProvider;
this.upgradeIndex = VersionManager.getOnlineUpgrade(cfg);
this.trial = trial || NoteDbMigrator.getTrialMode(cfg);
}
@@ -88,6 +91,7 @@
} catch (Exception e) {
log.error("Error in online NoteDb migration", e);
}
+ gcAllUsers.runWithLogger();
log.info("Online NoteDb migration completed in {}s", sw.elapsed(TimeUnit.SECONDS));
if (upgradeIndex) {
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 31a7e84..b0e5310 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -205,11 +205,7 @@
PatchSet psEntityB = psb.get() == 0 ? new PatchSet(psb) : psUtil.get(db, notes, psb);
if (psEntityA != null || psEntityB != null) {
try {
- permissionBackend
- .user(userProvider)
- .change(notes)
- .database(db)
- .check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).database(db).check(ChangePermission.READ);
} catch (AuthException e) {
throw new NoSuchChangeException(changeId);
}
diff --git a/java/com/google/gerrit/server/permissions/ChangeControl.java b/java/com/google/gerrit/server/permissions/ChangeControl.java
index 47c8543..e49686a 100644
--- a/java/com/google/gerrit/server/permissions/ChangeControl.java
+++ b/java/com/google/gerrit/server/permissions/ChangeControl.java
@@ -138,19 +138,6 @@
&& !isPatchSetLocked(db);
}
- /** Can this user delete this change? */
- private boolean canDelete(Change.Status status) {
- switch (status) {
- case NEW:
- case ABANDONED:
- return (isOwner() && refControl.canPerform(Permission.DELETE_OWN_CHANGES))
- || getProjectControl().isAdmin();
- case MERGED:
- default:
- return false;
- }
- }
-
/** Can this user rebase this change? */
private boolean canRebase(ReviewDb db) throws OrmException {
return (isOwner() || refControl.canSubmit(isOwner()) || refControl.canRebase())
@@ -369,7 +356,8 @@
case ABANDON:
return canAbandon(db());
case DELETE:
- return canDelete(getChange().getStatus());
+ return (isOwner() && refControl.canPerform(Permission.DELETE_OWN_CHANGES))
+ || getProjectControl().isAdmin();
case ADD_PATCH_SET:
return canAddPatchSet(db());
case EDIT_ASSIGNEE:
diff --git a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
index 800d877..2c8c951 100644
--- a/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/DefaultPermissionBackend.java
@@ -23,15 +23,18 @@
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.api.access.PluginPermission;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PeerDaemonUser;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
@@ -44,14 +47,21 @@
public class DefaultPermissionBackend extends PermissionBackend {
private static final CurrentUser.PropertyKey<Boolean> IS_ADMIN = CurrentUser.PropertyKey.create();
+ private final Provider<CurrentUser> currentUser;
private final ProjectCache projectCache;
private final ProjectControl.Factory projectControlFactory;
+ private final IdentifiedUser.GenericFactory identifiedUserFactory;
@Inject
DefaultPermissionBackend(
- ProjectCache projectCache, ProjectControl.Factory projectControlFactory) {
+ Provider<CurrentUser> currentUser,
+ ProjectCache projectCache,
+ ProjectControl.Factory projectControlFactory,
+ IdentifiedUser.GenericFactory identifiedUserFactory) {
+ this.currentUser = currentUser;
this.projectCache = projectCache;
this.projectControlFactory = projectControlFactory;
+ this.identifiedUserFactory = identifiedUserFactory;
}
private CapabilityCollection capabilities() {
@@ -59,10 +69,21 @@
}
@Override
+ public WithUser currentUser() {
+ return new WithUserImpl(currentUser.get());
+ }
+
+ @Override
public WithUser user(CurrentUser user) {
return new WithUserImpl(checkNotNull(user, "user"));
}
+ @Override
+ public WithUser absentUser(Account.Id user) {
+ IdentifiedUser identifiedUser = identifiedUserFactory.create(checkNotNull(user, "user"));
+ return new WithUserImpl(identifiedUser);
+ }
+
class WithUserImpl extends WithUser {
private final CurrentUser user;
private Boolean admin;
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index 84a3d87..5905800 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -105,8 +105,7 @@
permissionBackend.user(user).database(db).project(projectState.getNameKey());
}
- Map<String, Ref> filter(Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
- throws PermissionBackendException {
+ Map<String, Ref> filter(Map<String, Ref> refs, Repository repo, RefFilterOptions opts) {
if (projectState.isAllUsers()) {
refs = addUsersSelfSymref(refs);
}
@@ -283,7 +282,10 @@
.ref(visibleChanges.get(id).get())
.check(RefPermission.READ_PRIVATE_CHANGES);
return true;
- } catch (PermissionBackendException | AuthException e) {
+ } catch (AuthException e) {
+ return false;
+ } catch (PermissionBackendException e) {
+ log.error("Failed to check permission for " + id + " in " + projectState.getName(), e);
return false;
}
}
@@ -336,7 +338,7 @@
return r.notes();
}
} catch (PermissionBackendException e) {
- log.warn("Failed to check permission for " + r.id() + " in " + projectState.getName(), e);
+ log.error("Failed to check permission for " + r.id() + " in " + projectState.getName(), e);
}
return null;
}
diff --git a/java/com/google/gerrit/server/permissions/PermissionBackend.java b/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 56c300d..ed10184 100644
--- a/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.api.access.GlobalOrPluginPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -90,13 +91,26 @@
public abstract class PermissionBackend {
private static final Logger logger = LoggerFactory.getLogger(PermissionBackend.class);
- /** @return lightweight factory scoped to answer for the specified user. */
+ /** Returns an instance scoped to the current user. */
+ public abstract WithUser currentUser();
+
+ /**
+ * Returns an instance scoped to the specified user. Should be used in cases where the user could
+ * either be the issuer of the current request or an impersonated user. PermissionBackends that do
+ * not support impersonation can fail with an {@code IllegalStateException}.
+ *
+ * <p>If an instance scoped to the current user is desired, use {@code currentUser()} instead.
+ */
public abstract WithUser user(CurrentUser user);
- /** @return lightweight factory scoped to answer for the specified user. */
- public <U extends CurrentUser> WithUser user(Provider<U> user) {
- return user(checkNotNull(user, "Provider<CurrentUser>").get());
- }
+ /**
+ * Returns an instance scoped to the provided user. Should be used in cases where the caller wants
+ * to check the permissions of a user who is not the issuer of the current request and not the
+ * target of impersonation.
+ *
+ * <p>Usage should be very limited as this can expose a group-oracle.
+ */
+ public abstract WithUser absentUser(Account.Id user);
/**
* Bulk evaluate a set of {@link PermissionBackendCondition} for view handling.
@@ -135,18 +149,18 @@
/** PermissionBackend scoped to a specific user. */
public abstract static class WithUser extends AcceptsReviewDb<WithUser> {
- /** @return user this instance is scoped to. */
+ /** Returns the user this instance is scoped to. */
public abstract CurrentUser user();
- /** @return instance scoped for the specified project. */
+ /** Returns an instance scoped for the specified project. */
public abstract ForProject project(Project.NameKey project);
- /** @return instance scoped for the {@code ref}, and its parent project. */
+ /** Returns an instance scoped for the {@code ref}, and its parent project. */
public ForRef ref(Branch.NameKey ref) {
return project(ref.getParentKey()).ref(ref.get()).database(db);
}
- /** @return instance scoped for the change, and its destination ref and project. */
+ /** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeData cd) {
try {
return ref(cd.change().getDest()).change(cd);
@@ -155,15 +169,15 @@
}
}
- /** @return instance scoped for the change, and its destination ref and project. */
+ /** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeNotes notes) {
return ref(notes.getChange().getDest()).change(notes);
}
/**
- * @return instance scoped for the change loaded from index, and its destination ref and
- * project. This method should only be used when database access is harmful and potentially
- * stale data from the index is acceptable.
+ * Returns an instance scoped for the change loaded from index, and its destination ref and
+ * project. This method should only be used when database access is harmful and potentially
+ * stale data from the index is acceptable.
*/
public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
return ref(notes.getChange().getDest()).indexedChange(cd, notes);
@@ -250,19 +264,19 @@
/** PermissionBackend scoped to a user and project. */
public abstract static class ForProject extends AcceptsReviewDb<ForProject> {
- /** @return user this instance is scoped to. */
+ /** Returns the user this instance is scoped to. */
public abstract CurrentUser user();
- /** @return fully qualified resource path that this instance is scoped to. */
+ /** Returns the fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
- /** @return new instance rescoped to same project, but different {@code user}. */
+ /** Returns a new instance rescoped to same project, but different {@code user}. */
public abstract ForProject user(CurrentUser user);
- /** @return instance scoped for {@code ref} in this project. */
+ /** Returns an instance scoped for {@code ref} in this project. */
public abstract ForRef ref(String ref);
- /** @return instance scoped for the change, and its destination ref and project. */
+ /** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeData cd) {
try {
return ref(cd.change().getDest().get()).change(cd);
@@ -271,15 +285,15 @@
}
}
- /** @return instance scoped for the change, and its destination ref and project. */
+ /** Returns an instance scoped for the change, and its destination ref and project. */
public ForChange change(ChangeNotes notes) {
return ref(notes.getChange().getDest().get()).change(notes);
}
/**
- * @return instance scoped for the change loaded from index, and its destination ref and
- * project. This method should only be used when database access is harmful and potentially
- * stale data from the index is acceptable.
+ * Returns an instance scoped for the change loaded from index, and its destination ref and
+ * project. This method should only be used when database access is harmful and potentially
+ * stale data from the index is acceptable.
*/
public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
return ref(notes.getChange().getDest().get()).indexedChange(cd, notes);
@@ -311,8 +325,8 @@
}
/**
- * @return a partition of the provided refs that are visible to the user that this instance is
- * scoped to.
+ * Returns a partition of the provided refs that are visible to the user that this instance is
+ * scoped to.
*/
public abstract Map<String, Ref> filter(
Map<String, Ref> refs, Repository repo, RefFilterOptions opts)
@@ -352,19 +366,19 @@
/** PermissionBackend scoped to a user, project and reference. */
public abstract static class ForRef extends AcceptsReviewDb<ForRef> {
- /** @return user this instance is scoped to. */
+ /** Returns the user this instance is scoped to. */
public abstract CurrentUser user();
- /** @return fully qualified resource path that this instance is scoped to. */
+ /** Returns a fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
- /** @return new instance rescoped to same reference, but different {@code user}. */
+ /** Returns a new instance rescoped to same reference, but different {@code user}. */
public abstract ForRef user(CurrentUser user);
- /** @return instance scoped to change. */
+ /** Returns an instance scoped to change. */
public abstract ForChange change(ChangeData cd);
- /** @return instance scoped to change. */
+ /** Returns an instance scoped to change. */
public abstract ForChange change(ChangeNotes notes);
/**
@@ -410,13 +424,13 @@
/** PermissionBackend scoped to a user, project, reference and change. */
public abstract static class ForChange extends AcceptsReviewDb<ForChange> {
- /** @return user this instance is scoped to. */
+ /** Returns the user this instance is scoped to. */
public abstract CurrentUser user();
- /** @return fully qualified resource path that this instance is scoped to. */
+ /** Returns the fully qualified resource path that this instance is scoped to. */
public abstract String resourcePath();
- /** @return new instance rescoped to same change, but different {@code user}. */
+ /** Returns a new instance rescoped to same change, but different {@code user}. */
public abstract ForChange user(CurrentUser user);
/** Verify scoped user can {@code perm}, throwing if denied. */
diff --git a/java/com/google/gerrit/server/permissions/ProjectControl.java b/java/com/google/gerrit/server/permissions/ProjectControl.java
index 30ed180..dbd60ea 100644
--- a/java/com/google/gerrit/server/permissions/ProjectControl.java
+++ b/java/com/google/gerrit/server/permissions/ProjectControl.java
@@ -202,11 +202,6 @@
return false;
}
- /** Returns whether the project is hidden. */
- private boolean isHidden() {
- return getProject().getState().equals(com.google.gerrit.extensions.client.ProjectState.HIDDEN);
- }
-
private boolean canAddRefs() {
return (canPerformOnAnyRef(Permission.CREATE) || isAdmin());
}
@@ -400,8 +395,7 @@
private boolean can(ProjectPermission perm) throws PermissionBackendException {
switch (perm) {
case ACCESS:
- return (!isHidden() && (user.isInternalUser() || canPerformOnAnyRef(Permission.READ)))
- || isOwner();
+ return user.isInternalUser() || isOwner() || canPerformOnAnyRef(Permission.READ);
case READ:
return allRefsAreVisible(Collections.emptySet());
diff --git a/java/com/google/gerrit/server/plugins/DisablePlugin.java b/java/com/google/gerrit/server/plugins/DisablePlugin.java
index 266350f..62eb993 100644
--- a/java/com/google/gerrit/server/plugins/DisablePlugin.java
+++ b/java/com/google/gerrit/server/plugins/DisablePlugin.java
@@ -19,33 +19,28 @@
import com.google.gerrit.extensions.common.PluginInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@Singleton
public class DisablePlugin implements RestModifyView<PluginResource, Input> {
private final PluginLoader loader;
- private final Provider<IdentifiedUser> user;
private final PermissionBackend permissionBackend;
@Inject
- DisablePlugin(
- PluginLoader loader, Provider<IdentifiedUser> user, PermissionBackend permissionBackend) {
+ DisablePlugin(PluginLoader loader, PermissionBackend permissionBackend) {
this.loader = loader;
- this.user = user;
this.permissionBackend = permissionBackend;
}
@Override
public PluginInfo apply(PluginResource resource, Input input) throws RestApiException {
try {
- permissionBackend.user(user).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
} catch (PermissionBackendException e) {
throw new RestApiException("Could not check permission", e);
}
diff --git a/java/com/google/gerrit/server/project/ChildProjects.java b/java/com/google/gerrit/server/project/ChildProjects.java
index 0b174f6..868d0af 100644
--- a/java/com/google/gerrit/server/project/ChildProjects.java
+++ b/java/com/google/gerrit/server/project/ChildProjects.java
@@ -20,13 +20,11 @@
import com.google.common.collect.Multimap;
import com.google.gerrit.extensions.common.ProjectInfo;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.HashMap;
@@ -38,7 +36,6 @@
public class ChildProjects {
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final AllProjectsName allProjects;
private final ProjectJson json;
@@ -46,32 +43,30 @@
ChildProjects(
ProjectCache projectCache,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
AllProjectsName allProjectsName,
ProjectJson json) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
- this.user = user;
this.allProjects = allProjectsName;
this.json = json;
}
/** Gets all child projects recursively. */
public List<ProjectInfo> list(Project.NameKey parent) throws PermissionBackendException {
- Map<Project.NameKey, Project> projects = readAllProjects();
+ Map<Project.NameKey, Project> projects = readAllReadableProjects();
Multimap<Project.NameKey, Project.NameKey> children = parentToChildren(projects);
- PermissionBackend.WithUser perm = permissionBackend.user(user);
+ PermissionBackend.WithUser perm = permissionBackend.currentUser();
List<ProjectInfo> results = new ArrayList<>();
depthFirstFormat(results, perm, projects, children, parent);
return results;
}
- private Map<Project.NameKey, Project> readAllProjects() {
+ private Map<Project.NameKey, Project> readAllReadableProjects() {
Map<Project.NameKey, Project> projects = new HashMap<>();
for (Project.NameKey name : projectCache.all()) {
ProjectState c = projectCache.get(name);
- if (c != null) {
+ if (c != null && c.statePermitsRead()) {
projects.put(c.getNameKey(), c.getProject());
}
}
diff --git a/java/com/google/gerrit/server/project/CreateRefControl.java b/java/com/google/gerrit/server/project/CreateRefControl.java
index 7d4797a..90a7455 100644
--- a/java/com/google/gerrit/server/project/CreateRefControl.java
+++ b/java/com/google/gerrit/server/project/CreateRefControl.java
@@ -17,6 +17,7 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -75,10 +76,10 @@
}
ps.checkStatePermitsWrite();
- PermissionBackend.ForRef perm = permissionBackend.user(user).ref(branch);
+ PermissionBackend.ForRef perm = permissionBackend.user(user.get()).ref(branch);
if (object instanceof RevCommit) {
perm.check(RefPermission.CREATE);
- checkCreateCommit(repo, (RevCommit) object, ps, perm);
+ checkCreateCommit(repo, (RevCommit) object, ps.getNameKey(), perm);
} else if (object instanceof RevTag) {
RevTag tag = (RevTag) object;
try (RevWalk rw = new RevWalk(repo)) {
@@ -98,14 +99,14 @@
RevObject target = tag.getObject();
if (target instanceof RevCommit) {
- checkCreateCommit(repo, (RevCommit) target, ps, perm);
+ checkCreateCommit(repo, (RevCommit) target, ps.getNameKey(), perm);
} else {
checkCreateRef(user, repo, branch, target);
}
// If the tag has a PGP signature, allow a lower level of permission
// than if it doesn't have a PGP signature.
- PermissionBackend.ForRef forRef = permissionBackend.user(user).ref(branch);
+ PermissionBackend.ForRef forRef = permissionBackend.user(user.get()).ref(branch);
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
forRef.check(RefPermission.CREATE_SIGNED_TAG);
} else {
@@ -119,7 +120,7 @@
* new commit to the repository.
*/
private void checkCreateCommit(
- Repository repo, RevCommit commit, ProjectState projectState, PermissionBackend.ForRef forRef)
+ Repository repo, RevCommit commit, Project.NameKey project, PermissionBackend.ForRef forRef)
throws AuthException, PermissionBackendException {
try {
// If the user has update (push) permission, they can create the ref regardless
@@ -129,7 +130,7 @@
} catch (AuthException denied) {
// Fall through to check reachability.
}
- if (reachable.fromHeadsOrTags(projectState, repo, commit)) {
+ if (reachable.fromHeadsOrTags(project, repo, commit)) {
// If the user has no push permissions, check whether the object is
// merged into a branch or tag readable by this user. If so, they are
// not effectively "pushing" more objects, so they can create the ref
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 2ef7891..e3140b5 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -67,6 +67,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Ref;
@@ -197,6 +198,24 @@
return capabilities;
}
+ /**
+ * Returns true if the Prolog engine is expected to run for this project, that is if this project
+ * or a parent possesses a rules.pl file.
+ */
+ public boolean hasPrologRules() {
+ // We check if this project has a rules.pl file
+ if (getConfig().getRulesId() != null) {
+ return true;
+ }
+
+ // If not, we check the parents.
+ return parents()
+ .stream()
+ .map(ProjectState::getConfig)
+ .map(ProjectConfig::getRulesId)
+ .anyMatch(Objects::nonNull);
+ }
+
/** @return Construct a new PrologEnvironment for the calling thread. */
public PrologEnvironment newPrologEnvironment() throws CompileException {
PrologMachineCopy pmc = rulesMachine;
diff --git a/java/com/google/gerrit/server/project/Reachable.java b/java/com/google/gerrit/server/project/Reachable.java
index 67d8f70..0196d92 100644
--- a/java/com/google/gerrit/server/project/Reachable.java
+++ b/java/com/google/gerrit/server/project/Reachable.java
@@ -16,13 +16,12 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.change.IncludedInResolver;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collection;
@@ -45,37 +44,34 @@
private static final Logger log = LoggerFactory.getLogger(Reachable.class);
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
@Inject
- Reachable(PermissionBackend permissionBackend, Provider<CurrentUser> user) {
+ Reachable(PermissionBackend permissionBackend) {
this.permissionBackend = permissionBackend;
- this.user = user;
}
/** @return true if a commit is reachable from a given set of refs. */
public boolean fromRefs(
- ProjectState state, Repository repo, RevCommit commit, Map<String, Ref> refs) {
+ Project.NameKey project, Repository repo, RevCommit commit, Map<String, Ref> refs) {
try (RevWalk rw = new RevWalk(repo)) {
- // TODO(hiesel) Convert interface to Project.NameKey
Map<String, Ref> filtered =
permissionBackend
- .user(user)
- .project(state.getNameKey())
+ .currentUser()
+ .project(project)
.filter(refs, repo, RefFilterOptions.builder().setFilterTagsSeparately(true).build());
return IncludedInResolver.includedInAny(repo, rw, commit, filtered.values());
} catch (IOException | PermissionBackendException e) {
log.error(
String.format(
"Cannot verify permissions to commit object %s in repository %s",
- commit.name(), state.getNameKey()),
+ commit.name(), project),
e);
return false;
}
}
/** @return true if a commit is reachable from a repo's branches and tags. */
- boolean fromHeadsOrTags(ProjectState state, Repository repo, RevCommit commit) {
+ boolean fromHeadsOrTags(Project.NameKey project, Repository repo, RevCommit commit) {
try {
RefDatabase refdb = repo.getRefDatabase();
Collection<Ref> heads = refdb.getRefs(Constants.R_HEADS).values();
@@ -84,12 +80,12 @@
for (Ref r : Iterables.concat(heads, tags)) {
refs.put(r.getName(), r);
}
- return fromRefs(state, repo, commit, refs);
+ return fromRefs(project, repo, commit, refs);
} catch (IOException e) {
log.error(
String.format(
"Cannot verify permissions to commit object %s in repository %s",
- commit.name(), state.getProject().getNameKey()),
+ commit.name(), project),
e);
return false;
}
diff --git a/java/com/google/gerrit/server/project/RefUtil.java b/java/com/google/gerrit/server/project/RefUtil.java
index 62e48be..e42a7df 100644
--- a/java/com/google/gerrit/server/project/RefUtil.java
+++ b/java/com/google/gerrit/server/project/RefUtil.java
@@ -39,6 +39,8 @@
public class RefUtil {
private static final Logger log = LoggerFactory.getLogger(RefUtil.class);
+ private RefUtil() {}
+
public static ObjectId parseBaseRevision(
Repository repo, Project.NameKey projectName, String baseRevision)
throws InvalidRevisionException {
diff --git a/java/com/google/gerrit/server/project/SuggestParentCandidates.java b/java/com/google/gerrit/server/project/SuggestParentCandidates.java
index 7c7f8a5..99833af 100644
--- a/java/com/google/gerrit/server/project/SuggestParentCandidates.java
+++ b/java/com/google/gerrit/server/project/SuggestParentCandidates.java
@@ -17,13 +17,11 @@
import static java.util.stream.Collectors.toList;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.HashSet;
import java.util.List;
@@ -33,35 +31,30 @@
public class SuggestParentCandidates {
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final AllProjectsName allProjects;
@Inject
SuggestParentCandidates(
- ProjectCache projectCache,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
- AllProjectsName allProjects) {
+ ProjectCache projectCache, PermissionBackend permissionBackend, AllProjectsName allProjects) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
- this.user = user;
this.allProjects = allProjects;
}
public List<Project.NameKey> getNameKeys() throws PermissionBackendException {
return permissionBackend
- .user(user)
- .filter(ProjectPermission.ACCESS, parents())
+ .currentUser()
+ .filter(ProjectPermission.ACCESS, readableParents())
.stream()
.sorted()
.collect(toList());
}
- private Set<Project.NameKey> parents() {
+ private Set<Project.NameKey> readableParents() {
Set<Project.NameKey> parents = new HashSet<>();
for (Project.NameKey p : projectCache.all()) {
ProjectState ps = projectCache.get(p);
- if (ps != null) {
+ if (ps != null && ps.statePermitsRead()) {
Project.NameKey parent = ps.getProject().getParent();
if (parent != null) {
parents.add(parent);
diff --git a/java/com/google/gerrit/server/query/account/AccountPredicates.java b/java/com/google/gerrit/server/query/account/AccountPredicates.java
index acb963c..57a0dcc 100644
--- a/java/com/google/gerrit/server/query/account/AccountPredicates.java
+++ b/java/com/google/gerrit/server/query/account/AccountPredicates.java
@@ -126,8 +126,7 @@
public static Predicate<AccountState> cansee(
AccountQueryBuilder.Arguments args, ChangeNotes changeNotes) {
- return new CanSeeChangePredicate(
- args.db, args.permissionBackend, args.userFactory, changeNotes);
+ return new CanSeeChangePredicate(args.db, args.permissionBackend, changeNotes);
}
static class AccountPredicate extends IndexPredicate<AccountState>
diff --git a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
index 8b6e1e4..f627ec8 100644
--- a/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/account/AccountQueryBuilder.java
@@ -63,7 +63,6 @@
public static class Arguments {
final Provider<ReviewDb> db;
final ChangeFinder changeFinder;
- final IdentifiedUser.GenericFactory userFactory;
final PermissionBackend permissionBackend;
private final Provider<CurrentUser> self;
@@ -75,13 +74,11 @@
AccountIndexCollection indexes,
Provider<ReviewDb> db,
ChangeFinder changeFinder,
- IdentifiedUser.GenericFactory userFactory,
PermissionBackend permissionBackend) {
this.self = self;
this.indexes = indexes;
this.db = db;
this.changeFinder = changeFinder;
- this.userFactory = userFactory;
this.permissionBackend = permissionBackend;
}
diff --git a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
index c436a45..b5e7b90 100644
--- a/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
+++ b/java/com/google/gerrit/server/query/account/CanSeeChangePredicate.java
@@ -17,7 +17,6 @@
import com.google.gerrit.index.query.PostFilterPredicate;
import com.google.gerrit.index.query.Predicate;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.ChangePermission;
@@ -31,17 +30,12 @@
public class CanSeeChangePredicate extends PostFilterPredicate<AccountState> {
private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
- private final IdentifiedUser.GenericFactory userFactory;
private final ChangeNotes changeNotes;
CanSeeChangePredicate(
- Provider<ReviewDb> db,
- PermissionBackend permissionBackend,
- IdentifiedUser.GenericFactory userFactory,
- ChangeNotes changeNotes) {
+ Provider<ReviewDb> db, PermissionBackend permissionBackend, ChangeNotes changeNotes) {
this.db = db;
this.permissionBackend = permissionBackend;
- this.userFactory = userFactory;
this.changeNotes = changeNotes;
}
@@ -49,7 +43,7 @@
public boolean match(AccountState accountState) throws OrmException {
try {
return permissionBackend
- .user(userFactory.create(accountState.getAccount().getId()))
+ .absentUser(accountState.getAccount().getId())
.database(db)
.change(changeNotes)
.test(ChangePermission.READ);
@@ -65,7 +59,7 @@
@Override
public Predicate<AccountState> copy(Collection<? extends Predicate<AccountState>> children) {
- return new CanSeeChangePredicate(db, permissionBackend, userFactory, changeNotes);
+ return new CanSeeChangePredicate(db, permissionBackend, changeNotes);
}
@Override
diff --git a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index f1be580..02386ae 100644
--- a/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -17,11 +17,9 @@
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
-import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
@@ -37,8 +35,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Set;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Query wrapper for the account index.
@@ -47,8 +43,6 @@
* holding on to a single instance.
*/
public class InternalAccountQuery extends InternalQuery<AccountState> {
- private static final Logger log = LoggerFactory.getLogger(InternalAccountQuery.class);
-
@Inject
InternalAccountQuery(
AccountQueryProcessor queryProcessor,
@@ -94,28 +88,6 @@
return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
}
- public AccountState oneByExternalId(String externalId) throws OrmException {
- return oneByExternalId(ExternalId.Key.parse(externalId));
- }
-
- public AccountState oneByExternalId(String scheme, String id) throws OrmException {
- return oneByExternalId(ExternalId.Key.create(scheme, id));
- }
-
- public AccountState oneByExternalId(ExternalId.Key externalId) throws OrmException {
- List<AccountState> accountStates = byExternalId(externalId);
- if (accountStates.size() == 1) {
- return accountStates.get(0);
- } else if (accountStates.size() > 0) {
- StringBuilder msg = new StringBuilder();
- msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: ");
- Joiner.on(", ")
- .appendTo(msg, Lists.transform(accountStates, AccountState.ACCOUNT_ID_FUNCTION));
- log.warn(msg.toString());
- }
- return null;
- }
-
public List<AccountState> byFullName(String fullName) throws OrmException {
return query(AccountPredicates.fullName(fullName));
}
diff --git a/java/com/google/gerrit/server/restapi/account/AddSshKey.java b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
index 4485f70..7539cf3 100644
--- a/java/com/google/gerrit/server/restapi/account/AddSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/AddSshKey.java
@@ -75,7 +75,7 @@
throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
return apply(rsrc.getUser(), input);
}
diff --git a/java/com/google/gerrit/server/restapi/account/Capabilities.java b/java/com/google/gerrit/server/restapi/account/Capabilities.java
index e337662..2dd54a5 100644
--- a/java/com/google/gerrit/server/restapi/account/Capabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/Capabilities.java
@@ -62,7 +62,7 @@
throws ResourceNotFoundException, AuthException, PermissionBackendException {
IdentifiedUser target = parent.getUser();
if (self.get() != target) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
GlobalOrPluginPermission perm = parse(id);
diff --git a/java/com/google/gerrit/server/restapi/account/CreateAccount.java b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
index 31bf93f..404b3d3 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateAccount.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateAccount.java
@@ -36,7 +36,6 @@
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.Sequences;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountExternalIdCreator;
@@ -67,7 +66,6 @@
CreateAccount create(String username);
}
- private final ReviewDb db;
private final Sequences seq;
private final GroupsCollection groupsCollection;
private final VersionedAuthorizedKeys.Accessor authorizedKeys;
@@ -81,7 +79,6 @@
@Inject
CreateAccount(
- ReviewDb db,
Sequences seq,
GroupsCollection groupsCollection,
VersionedAuthorizedKeys.Accessor authorizedKeys,
@@ -92,7 +89,6 @@
@UserInitiated Provider<GroupsUpdate> groupsUpdate,
OutgoingEmailValidator validator,
@Assisted String username) {
- this.db = db;
this.seq = seq;
this.groupsCollection = groupsCollection;
this.authorizedKeys = authorizedKeys;
@@ -202,6 +198,6 @@
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, ImmutableSet.of(accountId)))
.build();
- groupsUpdate.get().updateGroup(db, groupUuid, groupUpdate);
+ groupsUpdate.get().updateGroup(groupUuid, groupUpdate);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/CreateEmail.java b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
index 8ec024e..2248e35 100644
--- a/java/com/google/gerrit/server/restapi/account/CreateEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/CreateEmail.java
@@ -98,7 +98,7 @@
}
if (self.get() != rsrc.getUser() || input.noConfirmation) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
if (!realm.allowsEdit(AccountFieldName.REGISTER_NEW_EMAIL)) {
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
index d36dfe9..ad1b103 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteEmail.java
@@ -72,7 +72,7 @@
MethodNotAllowedException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
return apply(rsrc.getUser(), rsrc.getEmail());
}
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
index 61ba8cc..472756e 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteExternalIds.java
@@ -68,7 +68,7 @@
throws RestApiException, IOException, OrmException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != resource.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ACCESS_DATABASE);
+ permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
}
if (extIds == null || extIds.size() == 0) {
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
index 2621a4a..f278df2 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteSshKey.java
@@ -58,7 +58,7 @@
throws AuthException, OrmException, RepositoryNotFoundException, IOException,
ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
authorizedKeys.deleteKey(rsrc.getUser().getAccountId(), rsrc.getSshKey().getKey().get());
diff --git a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
index 6242e1e..ce10b38 100644
--- a/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/DeleteWatchedProjects.java
@@ -62,7 +62,7 @@
throws AuthException, UnprocessableEntityException, OrmException, IOException,
ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
if (input == null) {
return Response.none();
diff --git a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
index d75a01a..c7a6bae 100644
--- a/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
+++ b/java/com/google/gerrit/server/restapi/account/EmailsCollection.java
@@ -65,7 +65,7 @@
public AccountResource.Email parse(AccountResource rsrc, IdString id)
throws ResourceNotFoundException, PermissionBackendException, AuthException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
if ("preferred".equals(id.get())) {
diff --git a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
index 5260bef0..c623e3e 100644
--- a/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
+++ b/java/com/google/gerrit/server/restapi/account/GetCapabilities.java
@@ -76,7 +76,7 @@
@Override
public Object apply(AccountResource rsrc) throws AuthException, PermissionBackendException {
- PermissionBackend.WithUser perm = permissionBackend.user(self);
+ PermissionBackend.WithUser perm = permissionBackend.currentUser();
if (self.get() != rsrc.getUser()) {
perm.check(GlobalPermission.ADMINISTRATE_SERVER);
perm = permissionBackend.user(rsrc.getUser());
diff --git a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
index af87025..a8d14f6 100644
--- a/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetDiffPreferences.java
@@ -51,7 +51,7 @@
public DiffPreferencesInfo apply(AccountResource rsrc)
throws RestApiException, ConfigInvalidException, IOException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
Account.Id id = rsrc.getUser().getAccountId();
diff --git a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
index 1ea5cec..f24991d 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEditPreferences.java
@@ -51,7 +51,7 @@
public EditPreferencesInfo apply(AccountResource rsrc)
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
Account.Id id = rsrc.getUser().getAccountId();
diff --git a/java/com/google/gerrit/server/restapi/account/GetEmails.java b/java/com/google/gerrit/server/restapi/account/GetEmails.java
index 640cc64..63d042c 100644
--- a/java/com/google/gerrit/server/restapi/account/GetEmails.java
+++ b/java/com/google/gerrit/server/restapi/account/GetEmails.java
@@ -45,7 +45,7 @@
public List<EmailInfo> apply(AccountResource rsrc)
throws AuthException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
List<EmailInfo> emails = new ArrayList<>();
diff --git a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
index 284c7f9..2f72ad7 100644
--- a/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
+++ b/java/com/google/gerrit/server/restapi/account/GetExternalIds.java
@@ -62,7 +62,7 @@
public List<AccountExternalIdInfo> apply(AccountResource resource)
throws RestApiException, IOException, OrmException, PermissionBackendException {
if (self.get() != resource.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ACCESS_DATABASE);
+ permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
}
Collection<ExternalId> ids = externalIds.byAccount(resource.getUser().getAccountId());
diff --git a/java/com/google/gerrit/server/restapi/account/GetPreferences.java b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
index efc2de1..e32d434 100644
--- a/java/com/google/gerrit/server/restapi/account/GetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/GetPreferences.java
@@ -49,7 +49,7 @@
public GeneralPreferencesInfo apply(AccountResource rsrc)
throws RestApiException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
Account.Id id = rsrc.getUser().getAccountId();
diff --git a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
index 15156dc..cd8dc09 100644
--- a/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/GetSshKeys.java
@@ -58,7 +58,7 @@
throws AuthException, OrmException, RepositoryNotFoundException, IOException,
ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
return apply(rsrc.getUser());
}
diff --git a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
index 389dc0e..3a6595c 100644
--- a/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/GetWatchedProjects.java
@@ -62,7 +62,7 @@
throws OrmException, AuthException, IOException, ConfigInvalidException,
PermissionBackendException, ResourceNotFoundException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
Account.Id accountId = rsrc.getUser().getAccountId();
diff --git a/java/com/google/gerrit/server/restapi/account/Index.java b/java/com/google/gerrit/server/restapi/account/Index.java
index fc6b7b3..0d8171d 100644
--- a/java/com/google/gerrit/server/restapi/account/Index.java
+++ b/java/com/google/gerrit/server/restapi/account/Index.java
@@ -50,7 +50,7 @@
public Response<?> apply(AccountResource rsrc, Input input)
throws IOException, AuthException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
accountIndexer.get().index(rsrc.getUser().getAccountId());
diff --git a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
index bdc6dcb..6486d18 100644
--- a/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
+++ b/java/com/google/gerrit/server/restapi/account/PostWatchedProjects.java
@@ -69,7 +69,7 @@
throws OrmException, RestApiException, IOException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
Map<ProjectWatchKey, Set<NotifyType>> projectWatches = asMap(input);
diff --git a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
index 468f285..7f7c2ae 100644
--- a/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
+++ b/java/com/google/gerrit/server/restapi/account/PutHttpPassword.java
@@ -76,7 +76,7 @@
throws AuthException, ResourceNotFoundException, ResourceConflictException, OrmException,
IOException, ConfigInvalidException, PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
if (input == null) {
@@ -91,7 +91,7 @@
newPassword = null;
} else {
// Only administrators can explicitly set the password.
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
newPassword = input.httpPassword;
}
return apply(rsrc.getUser(), newPassword);
diff --git a/java/com/google/gerrit/server/restapi/account/PutName.java b/java/com/google/gerrit/server/restapi/account/PutName.java
index 031249d..a982331 100644
--- a/java/com/google/gerrit/server/restapi/account/PutName.java
+++ b/java/com/google/gerrit/server/restapi/account/PutName.java
@@ -63,7 +63,7 @@
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
IOException, PermissionBackendException, ConfigInvalidException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
return apply(rsrc.getUser(), input);
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutPreferred.java b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
index aafdfc1..a30e074 100644
--- a/java/com/google/gerrit/server/restapi/account/PutPreferred.java
+++ b/java/com/google/gerrit/server/restapi/account/PutPreferred.java
@@ -57,7 +57,7 @@
throws AuthException, ResourceNotFoundException, OrmException, IOException,
PermissionBackendException, ConfigInvalidException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
return apply(rsrc.getUser(), rsrc.getEmail());
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutStatus.java b/java/com/google/gerrit/server/restapi/account/PutStatus.java
index c9ae2a0..3c173a0 100644
--- a/java/com/google/gerrit/server/restapi/account/PutStatus.java
+++ b/java/com/google/gerrit/server/restapi/account/PutStatus.java
@@ -57,7 +57,7 @@
throws AuthException, ResourceNotFoundException, OrmException, IOException,
PermissionBackendException, ConfigInvalidException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
return apply(rsrc.getUser(), input);
}
diff --git a/java/com/google/gerrit/server/restapi/account/PutUsername.java b/java/com/google/gerrit/server/restapi/account/PutUsername.java
index 9893cf0..4024c10 100644
--- a/java/com/google/gerrit/server/restapi/account/PutUsername.java
+++ b/java/com/google/gerrit/server/restapi/account/PutUsername.java
@@ -75,7 +75,7 @@
ResourceConflictException, OrmException, IOException, ConfigInvalidException,
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
}
if (!realm.allowsEdit(AccountFieldName.USER_NAME)) {
diff --git a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
index fa4550d..516b485 100644
--- a/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
+++ b/java/com/google/gerrit/server/restapi/account/QueryAccounts.java
@@ -28,7 +28,6 @@
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.query.QueryResult;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountDirectory.FillOptions;
import com.google.gerrit.server.account.AccountInfoComparator;
import com.google.gerrit.server.account.AccountLoader;
@@ -42,7 +41,6 @@
import com.google.gerrit.server.query.account.AccountQueryProcessor;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashMap;
@@ -55,7 +53,6 @@
public class QueryAccounts implements RestReadView<TopLevelResource> {
private static final int MAX_SUGGEST_RESULTS = 100;
- private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
private final AccountLoader.Factory accountLoaderFactory;
private final AccountQueryBuilder queryBuilder;
@@ -125,13 +122,11 @@
@Inject
QueryAccounts(
- Provider<CurrentUser> self,
PermissionBackend permissionBackend,
AccountLoader.Factory accountLoaderFactory,
AccountQueryBuilder queryBuilder,
AccountQueryProcessor queryProcessor,
@GerritServerConfig Config cfg) {
- this.self = self;
this.permissionBackend = permissionBackend;
this.accountLoaderFactory = accountLoaderFactory;
this.queryBuilder = queryBuilder;
@@ -170,7 +165,7 @@
}
boolean modifyAccountCapabilityChecked = false;
if (options.contains(ListAccountsOption.ALL_EMAILS)) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
modifyAccountCapabilityChecked = true;
fillOptions.add(FillOptions.EMAIL);
fillOptions.add(FillOptions.SECONDARY_EMAILS);
@@ -180,7 +175,7 @@
fillOptions.add(FillOptions.EMAIL);
if (modifyAccountCapabilityChecked
- || permissionBackend.user(self).test(GlobalPermission.MODIFY_ACCOUNT)) {
+ || permissionBackend.currentUser().test(GlobalPermission.MODIFY_ACCOUNT)) {
fillOptions.add(FillOptions.SECONDARY_EMAILS);
}
}
diff --git a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
index 1e300c7..6aa88de 100644
--- a/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetDiffPreferences.java
@@ -58,7 +58,7 @@
throws RestApiException, ConfigInvalidException, RepositoryNotFoundException, IOException,
PermissionBackendException, OrmException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
if (input == null) {
diff --git a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
index e06eaf3..dad6e0f 100644
--- a/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetEditPreferences.java
@@ -59,7 +59,7 @@
throws RestApiException, RepositoryNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException, OrmException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
if (input == null) {
diff --git a/java/com/google/gerrit/server/restapi/account/SetPreferences.java b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
index dcc2695..11ecfdb 100644
--- a/java/com/google/gerrit/server/restapi/account/SetPreferences.java
+++ b/java/com/google/gerrit/server/restapi/account/SetPreferences.java
@@ -64,7 +64,7 @@
throws RestApiException, IOException, ConfigInvalidException, PermissionBackendException,
OrmException {
if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
}
checkDownloadScheme(input.downloadScheme);
diff --git a/java/com/google/gerrit/server/restapi/account/SshKeys.java b/java/com/google/gerrit/server/restapi/account/SshKeys.java
index 52861ce..fa447c2 100644
--- a/java/com/google/gerrit/server/restapi/account/SshKeys.java
+++ b/java/com/google/gerrit/server/restapi/account/SshKeys.java
@@ -68,7 +68,7 @@
PermissionBackendException {
if (self.get() != rsrc.getUser()) {
try {
- permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
+ permissionBackend.currentUser().check(GlobalPermission.MODIFY_ACCOUNT);
} catch (AuthException e) {
// If lacking MODIFY_ACCOUNT claim the resource does not exist.
throw new ResourceNotFoundException();
diff --git a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
index e44896e..bf8bb1f 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangesCollection.java
@@ -137,7 +137,7 @@
private boolean canRead(ChangeNotes notes) throws PermissionBackendException, IOException {
try {
- permissionBackend.user(user).change(notes).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(notes).database(db).check(ChangePermission.READ);
} catch (AuthException e) {
return false;
}
diff --git a/java/com/google/gerrit/server/restapi/change/Check.java b/java/com/google/gerrit/server/restapi/change/Check.java
index fbf19be..f3e0077 100644
--- a/java/com/google/gerrit/server/restapi/change/Check.java
+++ b/java/com/google/gerrit/server/restapi/change/Check.java
@@ -22,7 +22,6 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -32,7 +31,6 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import javax.inject.Singleton;
@@ -40,13 +38,11 @@
public class Check
implements RestReadView<ChangeResource>, RestModifyView<ChangeResource, FixInput> {
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final ChangeJson.Factory jsonFactory;
@Inject
- Check(PermissionBackend permissionBackend, Provider<CurrentUser> user, ChangeJson.Factory json) {
+ Check(PermissionBackend permissionBackend, ChangeJson.Factory json) {
this.permissionBackend = permissionBackend;
- this.user = user;
this.jsonFactory = json;
}
@@ -59,7 +55,7 @@
public Response<ChangeInfo> apply(ChangeResource rsrc, FixInput input)
throws RestApiException, OrmException, PermissionBackendException, NoSuchProjectException,
IOException {
- PermissionBackend.WithUser perm = permissionBackend.user(user);
+ PermissionBackend.WithUser perm = permissionBackend.currentUser();
if (!rsrc.isUserOwner()) {
try {
perm.project(rsrc.getProject()).check(ProjectPermission.READ_CONFIG);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPick.java b/java/com/google/gerrit/server/restapi/change/CherryPick.java
index 2de5ba46..26a85e1 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPick.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPick.java
@@ -25,7 +25,6 @@
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeJson;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.git.IntegrationException;
@@ -44,7 +43,6 @@
import com.google.gerrit.server.update.UpdateException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -57,7 +55,6 @@
implements UiAction<RevisionResource> {
private static final Logger log = LoggerFactory.getLogger(CherryPick.class);
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final CherryPickChange cherryPickChange;
private final ChangeJson.Factory json;
private final ContributorAgreementsChecker contributorAgreements;
@@ -66,7 +63,6 @@
@Inject
CherryPick(
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
RetryHelper retryHelper,
CherryPickChange cherryPickChange,
ChangeJson.Factory json,
@@ -74,7 +70,6 @@
ProjectCache projectCache) {
super(retryHelper);
this.permissionBackend = permissionBackend;
- this.user = user;
this.cherryPickChange = cherryPickChange;
this.json = json;
this.contributorAgreements = contributorAgreements;
@@ -97,7 +92,7 @@
contributorAgreements.check(rsrc.getProject(), rsrc.getUser());
permissionBackend
- .user(user)
+ .currentUser()
.project(rsrc.getChange().getProject())
.ref(refName)
.check(RefPermission.CREATE_CHANGE);
@@ -134,7 +129,7 @@
and(
rsrc.isCurrent() && projectStatePermitsWrite,
permissionBackend
- .user(user)
+ .currentUser()
.project(rsrc.getProject())
.testCond(ProjectPermission.CREATE_CHANGE)));
}
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
index 7c10086..3221eaf 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickCommit.java
@@ -90,7 +90,7 @@
String refName = RefNames.fullName(destination);
contributorAgreements.check(projectName, user.get());
permissionBackend
- .user(user)
+ .currentUser()
.project(projectName)
.ref(refName)
.check(RefPermission.CREATE_CHANGE);
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 499cbb6..d8aa880 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -192,7 +192,11 @@
Project.NameKey project = rsrc.getNameKey();
String refName = RefNames.fullName(input.branch);
- permissionBackend.user(user).project(project).ref(refName).check(RefPermission.CREATE_CHANGE);
+ permissionBackend
+ .currentUser()
+ .project(project)
+ .ref(refName)
+ .check(RefPermission.CREATE_CHANGE);
rsrc.getProjectState().checkStatePermitsWrite();
try (Repository git = gitManager.openRepository(project);
@@ -208,7 +212,7 @@
}
ChangeNotes change = Iterables.getOnlyElement(notes);
try {
- permissionBackend.user(user).change(change).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(change).database(db).check(ChangePermission.READ);
} catch (AuthException e) {
throw new UnprocessableEntityException("Read not permitted for " + input.baseChange);
}
diff --git a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
index dcaba77..70a2251 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateMergePatchSet.java
@@ -210,7 +210,7 @@
}
ChangeNotes change = Iterables.getOnlyElement(notes);
try {
- permissionBackend.user(user).change(change).database(db).check(ChangePermission.READ);
+ permissionBackend.currentUser().change(change).database(db).check(ChangePermission.READ);
} catch (AuthException e) {
throw new UnprocessableEntityException("Read not permitted for " + baseChange);
}
diff --git a/java/com/google/gerrit/server/restapi/change/DeleteChange.java b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
index e33b4a4..4bd10ed 100644
--- a/java/com/google/gerrit/server/restapi/change/DeleteChange.java
+++ b/java/com/google/gerrit/server/restapi/change/DeleteChange.java
@@ -56,7 +56,7 @@
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws RestApiException, UpdateException, PermissionBackendException {
- if (rsrc.getChange().getStatus() == Change.Status.MERGED) {
+ if (!isChangeDeletable(rsrc.getChange().getStatus())) {
throw new MethodNotAllowedException("delete not permitted");
}
rsrc.permissions().database(db).check(ChangePermission.DELETE);
@@ -78,20 +78,15 @@
return new UiAction.Description()
.setLabel("Delete")
.setTitle("Delete change " + rsrc.getId())
- .setVisible(and(couldDeleteWhenIn(status), perm.testCond(ChangePermission.DELETE)));
+ .setVisible(and(isChangeDeletable(status), perm.testCond(ChangePermission.DELETE)));
}
- private boolean couldDeleteWhenIn(Change.Status status) {
- switch (status) {
- case NEW:
- case ABANDONED:
- // New or abandoned changes can be deleted with the right permissions.
- return true;
-
- case MERGED:
- // Merged changes should never be deleted.
- return false;
+ private static boolean isChangeDeletable(Change.Status status) {
+ if (status == Change.Status.MERGED) {
+ // Merged changes should never be deleted.
+ return false;
}
- return false;
+ // New or abandoned changes can be deleted with the right permissions.
+ return true;
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/Index.java b/java/com/google/gerrit/server/restapi/change/Index.java
index 55f53a6..a5dd868 100644
--- a/java/com/google/gerrit/server/restapi/change/Index.java
+++ b/java/com/google/gerrit/server/restapi/change/Index.java
@@ -18,7 +18,6 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.index.change.ChangeIndexer;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -38,7 +37,6 @@
private final Provider<ReviewDb> db;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final ChangeIndexer indexer;
@Inject
@@ -46,12 +44,10 @@
Provider<ReviewDb> db,
RetryHelper retryHelper,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
ChangeIndexer indexer) {
super(retryHelper);
this.db = db;
this.permissionBackend = permissionBackend;
- this.user = user;
this.indexer = indexer;
}
@@ -59,7 +55,7 @@
protected Response<?> applyImpl(
BatchUpdate.Factory updateFactory, ChangeResource rsrc, Input input)
throws IOException, AuthException, OrmException, PermissionBackendException {
- permissionBackend.user(user).check(GlobalPermission.MAINTAIN_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
indexer.index(db.get(), rsrc.getChange());
return Response.none();
}
diff --git a/java/com/google/gerrit/server/restapi/change/PostReviewers.java b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
index f9d7083..2049929 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReviewers.java
@@ -360,7 +360,7 @@
ListMultimap<RecipientType, Account.Id> accountsToNotify)
throws PermissionBackendException {
if (!permissionBackend
- .user(anonymousProvider)
+ .user(anonymousProvider.get())
.change(rsrc.getNotes())
.database(dbProvider)
.test(ChangePermission.READ)) {
diff --git a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
index 303401c..fbdfb54 100644
--- a/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
+++ b/java/com/google/gerrit/server/restapi/change/RelatedChangesSorter.java
@@ -29,7 +29,6 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -64,20 +63,17 @@
private final PermissionBackend permissionBackend;
private final Provider<ReviewDb> dbProvider;
private final ProjectCache projectCache;
- private final Provider<CurrentUser> currentUserProvider;
@Inject
RelatedChangesSorter(
GitRepositoryManager repoManager,
PermissionBackend permissionBackend,
Provider<ReviewDb> dbProvider,
- ProjectCache projectCache,
- Provider<CurrentUser> currentUserProvider) {
+ ProjectCache projectCache) {
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
this.dbProvider = dbProvider;
this.projectCache = projectCache;
- this.currentUserProvider = currentUserProvider;
}
public List<PatchSetData> sort(List<ChangeData> in, PatchSet startPs)
@@ -239,8 +235,7 @@
}
private boolean isVisible(PatchSetData psd) throws PermissionBackendException, IOException {
- PermissionBackend.WithUser perm =
- permissionBackend.user(currentUserProvider).database(dbProvider);
+ PermissionBackend.WithUser perm = permissionBackend.currentUser().database(dbProvider);
try {
perm.change(psd.data()).check(ChangePermission.READ);
} catch (AuthException e) {
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
index 5f6b088..78687cd 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewerRecommender.java
@@ -20,6 +20,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.index.query.Predicate;
@@ -100,7 +101,7 @@
}
public List<Account.Id> suggestReviewers(
- ChangeNotes changeNotes,
+ @Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
List<Account.Id> candidateList)
diff --git a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
index 38ed756..95557b5 100644
--- a/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
+++ b/java/com/google/gerrit/server/restapi/change/ReviewersUtil.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.extensions.common.GroupBaseInfo;
import com.google.gerrit.extensions.common.SuggestedReviewerInfo;
@@ -161,17 +162,25 @@
}
public List<SuggestedReviewerInfo> suggestReviewers(
- ChangeNotes changeNotes,
+ @Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
VisibilityControl visibilityControl,
boolean excludeGroups)
throws IOException, OrmException, ConfigInvalidException, PermissionBackendException {
CurrentUser currentUser = self.get();
- log.debug(
- "Suggesting reviewers for change {} to user {}.",
- changeNotes.getChangeId().get(),
- currentUser.getLoggableName());
+ if (changeNotes != null) {
+ log.debug(
+ "Suggesting reviewers for change {} to user {}.",
+ changeNotes.getChangeId().get(),
+ currentUser.getLoggableName());
+ } else {
+ log.debug(
+ "Suggesting default reviewers for project {} to user {}.",
+ projectState.getName(),
+ currentUser.getLoggableName());
+ }
+
String query = suggestReviewers.getQuery();
log.debug("Query: {}", query);
int limit = suggestReviewers.getLimit();
@@ -272,7 +281,7 @@
}
private List<Account.Id> recommendAccounts(
- ChangeNotes changeNotes,
+ @Nullable ChangeNotes changeNotes,
SuggestReviewers suggestReviewers,
ProjectState projectState,
List<Account.Id> candidateList)
@@ -286,7 +295,7 @@
private List<SuggestedReviewerInfo> loadAccounts(List<Account.Id> accountIds)
throws OrmException, PermissionBackendException {
Set<FillOptions> fillOptions =
- permissionBackend.user(self).test(GlobalPermission.MODIFY_ACCOUNT)
+ permissionBackend.currentUser().test(GlobalPermission.MODIFY_ACCOUNT)
? EnumSet.of(FillOptions.SECONDARY_EMAILS)
: EnumSet.noneOf(FillOptions.class);
fillOptions.addAll(AccountLoader.DETAILED_OPTIONS);
diff --git a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
index f487c28..5298857 100644
--- a/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
+++ b/java/com/google/gerrit/server/restapi/change/SetReadyForReview.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -24,7 +27,6 @@
import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gerrit.server.change.WorkInProgressOp.Input;
@@ -44,7 +46,6 @@
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
private final Provider<ReviewDb> db;
- private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
@Inject
@@ -52,12 +53,10 @@
RetryHelper retryHelper,
WorkInProgressOp.Factory opFactory,
Provider<ReviewDb> db,
- Provider<CurrentUser> self,
PermissionBackend permissionBackend) {
super(retryHelper);
this.opFactory = opFactory;
this.db = db;
- this.self = self;
this.permissionBackend = permissionBackend;
}
@@ -67,7 +66,7 @@
throws RestApiException, UpdateException, PermissionBackendException {
Change change = rsrc.getChange();
if (!rsrc.isUserOwner()
- && !permissionBackend.user(self).test(GlobalPermission.ADMINISTRATE_SERVER)) {
+ && !permissionBackend.currentUser().test(GlobalPermission.ADMINISTRATE_SERVER)) {
throw new AuthException("not allowed to set ready for review");
}
@@ -93,8 +92,12 @@
.setLabel("Start Review")
.setTitle("Set Ready For Review")
.setVisible(
- rsrc.isUserOwner()
- && rsrc.getChange().getStatus() == Status.NEW
- && rsrc.getChange().isWorkInProgress());
+ and(
+ rsrc.getChange().getStatus() == Status.NEW && rsrc.getChange().isWorkInProgress(),
+ or(
+ rsrc.isUserOwner(),
+ permissionBackend
+ .currentUser()
+ .testCond(GlobalPermission.ADMINISTRATE_SERVER))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
index 7fcf6a0..93568d5 100644
--- a/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
+++ b/java/com/google/gerrit/server/restapi/change/SetWorkInProgress.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.restapi.change;
+import static com.google.gerrit.extensions.conditions.BooleanCondition.and;
+import static com.google.gerrit.extensions.conditions.BooleanCondition.or;
+
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -24,7 +27,6 @@
import com.google.gerrit.reviewdb.client.Change.Status;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeUtil;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.WorkInProgressOp;
import com.google.gerrit.server.change.WorkInProgressOp.Input;
@@ -44,7 +46,6 @@
implements UiAction<ChangeResource> {
private final WorkInProgressOp.Factory opFactory;
private final Provider<ReviewDb> db;
- private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
@Inject
@@ -52,12 +53,10 @@
WorkInProgressOp.Factory opFactory,
RetryHelper retryHelper,
Provider<ReviewDb> db,
- Provider<CurrentUser> self,
PermissionBackend permissionBackend) {
super(retryHelper);
this.opFactory = opFactory;
this.db = db;
- this.self = self;
this.permissionBackend = permissionBackend;
}
@@ -68,7 +67,7 @@
Change change = rsrc.getChange();
if (!rsrc.isUserOwner()
- && !permissionBackend.user(self).test(GlobalPermission.ADMINISTRATE_SERVER)) {
+ && !permissionBackend.currentUser().test(GlobalPermission.ADMINISTRATE_SERVER)) {
throw new AuthException("not allowed to set work in progress");
}
@@ -94,8 +93,12 @@
.setLabel("WIP")
.setTitle("Set Work In Progress")
.setVisible(
- rsrc.isUserOwner()
- && rsrc.getChange().getStatus() == Status.NEW
- && !rsrc.getChange().isWorkInProgress());
+ and(
+ rsrc.getChange().getStatus() == Status.NEW && !rsrc.getChange().isWorkInProgress(),
+ or(
+ rsrc.isUserOwner(),
+ permissionBackend
+ .currentUser()
+ .testCond(GlobalPermission.ADMINISTRATE_SERVER))));
}
}
diff --git a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
index 4033d10..2dac5ef 100644
--- a/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
+++ b/java/com/google/gerrit/server/restapi/change/SuggestChangeReviewers.java
@@ -88,7 +88,7 @@
private VisibilityControl getVisibility(ChangeResource rsrc) {
// Use the destination reference, not the change, as private changes deny anyone who is not
// already a reviewer.
- PermissionBackend.ForRef perm = permissionBackend.user(self).ref(rsrc.getChange().getDest());
+ PermissionBackend.ForRef perm = permissionBackend.currentUser().ref(rsrc.getChange().getDest());
return new VisibilityControl() {
@Override
public boolean isVisibleTo(Account.Id account) throws OrmException {
diff --git a/java/com/google/gerrit/server/restapi/config/CachesCollection.java b/java/com/google/gerrit/server/restapi/config/CachesCollection.java
index cfdc648..e3d9e3c 100644
--- a/java/com/google/gerrit/server/restapi/config/CachesCollection.java
+++ b/java/com/google/gerrit/server/restapi/config/CachesCollection.java
@@ -27,7 +27,6 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.CacheResource;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.permissions.GlobalPermission;
@@ -45,7 +44,6 @@
private final DynamicMap<RestView<CacheResource>> views;
private final Provider<ListCaches> list;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> self;
private final DynamicMap<Cache<?, ?>> cacheMap;
private final PostCaches postCaches;
@@ -54,13 +52,11 @@
DynamicMap<RestView<CacheResource>> views,
Provider<ListCaches> list,
PermissionBackend permissionBackend,
- Provider<CurrentUser> self,
DynamicMap<Cache<?, ?>> cacheMap,
PostCaches postCaches) {
this.views = views;
this.list = list;
this.permissionBackend = permissionBackend;
- this.self = self;
this.cacheMap = cacheMap;
this.postCaches = postCaches;
}
@@ -73,7 +69,7 @@
@Override
public CacheResource parse(ConfigResource parent, IdString id)
throws AuthException, ResourceNotFoundException, PermissionBackendException {
- permissionBackend.user(self).check(GlobalPermission.VIEW_CACHES);
+ permissionBackend.currentUser().check(GlobalPermission.VIEW_CACHES);
String cacheName = id.get();
String pluginName = "gerrit";
diff --git a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
index 95b20c2..a16736b 100644
--- a/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
+++ b/java/com/google/gerrit/server/restapi/config/CheckConsistency.java
@@ -22,7 +22,6 @@
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountsConsistencyChecker;
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
import com.google.gerrit.server.config.ConfigResource;
@@ -32,7 +31,6 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -40,7 +38,6 @@
@Singleton
public class CheckConsistency implements RestModifyView<ConfigResource, ConsistencyCheckInput> {
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final AccountsConsistencyChecker accountsConsistencyChecker;
private final ExternalIdsConsistencyChecker externalIdsConsistencyChecker;
private final GroupsConsistencyChecker groupsConsistencyChecker;
@@ -48,12 +45,10 @@
@Inject
CheckConsistency(
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
AccountsConsistencyChecker accountsConsistencyChecker,
ExternalIdsConsistencyChecker externalIdsConsistencyChecker,
GroupsConsistencyChecker groupsChecker) {
this.permissionBackend = permissionBackend;
- this.user = user;
this.accountsConsistencyChecker = accountsConsistencyChecker;
this.externalIdsConsistencyChecker = externalIdsConsistencyChecker;
this.groupsConsistencyChecker = groupsChecker;
@@ -63,7 +58,7 @@
public ConsistencyCheckInfo apply(ConfigResource resource, ConsistencyCheckInput input)
throws RestApiException, IOException, OrmException, PermissionBackendException,
ConfigInvalidException {
- permissionBackend.user(user).check(GlobalPermission.ACCESS_DATABASE);
+ permissionBackend.currentUser().check(GlobalPermission.ACCESS_DATABASE);
if (input == null
|| (input.checkAccounts == null
diff --git a/java/com/google/gerrit/server/restapi/config/FlushCache.java b/java/com/google/gerrit/server/restapi/config/FlushCache.java
index 55e9dc3..9ea9e33 100644
--- a/java/com/google/gerrit/server/restapi/config/FlushCache.java
+++ b/java/com/google/gerrit/server/restapi/config/FlushCache.java
@@ -22,13 +22,11 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.CacheResource;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
@RequiresAnyCapability({FLUSH_CACHES, MAINTAIN_SERVER})
@@ -38,19 +36,17 @@
public static final String WEB_SESSIONS = "web_sessions";
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> self;
@Inject
- public FlushCache(PermissionBackend permissionBackend, Provider<CurrentUser> self) {
+ public FlushCache(PermissionBackend permissionBackend) {
this.permissionBackend = permissionBackend;
- this.self = self;
}
@Override
public Response<String> apply(CacheResource rsrc, Input input)
throws AuthException, PermissionBackendException {
if (WEB_SESSIONS.equals(rsrc.getName())) {
- permissionBackend.user(self).check(GlobalPermission.MAINTAIN_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.MAINTAIN_SERVER);
}
rsrc.getCache().invalidateAll();
diff --git a/java/com/google/gerrit/server/restapi/config/ListTasks.java b/java/com/google/gerrit/server/restapi/config/ListTasks.java
index 71ee5ad..fb2819c 100644
--- a/java/com/google/gerrit/server/restapi/config/ListTasks.java
+++ b/java/com/google/gerrit/server/restapi/config/ListTasks.java
@@ -28,6 +28,8 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.IdGenerator;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -46,13 +48,18 @@
private final PermissionBackend permissionBackend;
private final WorkQueue workQueue;
private final Provider<CurrentUser> self;
+ private final ProjectCache projectCache;
@Inject
public ListTasks(
- PermissionBackend permissionBackend, WorkQueue workQueue, Provider<CurrentUser> self) {
+ PermissionBackend permissionBackend,
+ WorkQueue workQueue,
+ Provider<CurrentUser> self,
+ ProjectCache projectCache) {
this.permissionBackend = permissionBackend;
this.workQueue = workQueue;
this.self = self;
+ this.projectCache = projectCache;
}
@Override
@@ -77,14 +84,17 @@
if (task.projectName != null) {
Boolean visible = visibilityCache.get(task.projectName);
if (visible == null) {
- try {
- permissionBackend
- .user(user)
- .project(new Project.NameKey(task.projectName))
- .check(ProjectPermission.ACCESS);
- visible = true;
- } catch (AuthException e) {
+ Project.NameKey nameKey = new Project.NameKey(task.projectName);
+ ProjectState state = projectCache.get(nameKey);
+ if (state == null || !state.statePermitsRead()) {
visible = false;
+ } else {
+ try {
+ permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
+ visible = true;
+ } catch (AuthException e) {
+ visible = false;
+ }
}
visibilityCache.put(task.projectName, visible);
}
diff --git a/java/com/google/gerrit/server/restapi/config/TasksCollection.java b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
index f5b6e56..dda54a0 100644
--- a/java/com/google/gerrit/server/restapi/config/TasksCollection.java
+++ b/java/com/google/gerrit/server/restapi/config/TasksCollection.java
@@ -18,8 +18,10 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.TaskResource;
@@ -30,6 +32,8 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -41,6 +45,7 @@
private final WorkQueue workQueue;
private final Provider<CurrentUser> self;
private final PermissionBackend permissionBackend;
+ private final ProjectCache projectCache;
@Inject
TasksCollection(
@@ -48,12 +53,14 @@
ListTasks list,
WorkQueue workQueue,
Provider<CurrentUser> self,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache) {
this.views = views;
this.list = list;
this.workQueue = workQueue;
this.self = self;
this.permissionBackend = permissionBackend;
+ this.projectCache = projectCache;
}
@Override
@@ -63,7 +70,8 @@
@Override
public TaskResource parse(ConfigResource parent, IdString id)
- throws ResourceNotFoundException, AuthException, PermissionBackendException {
+ throws ResourceNotFoundException, AuthException, PermissionBackendException,
+ ResourceConflictException {
CurrentUser user = self.get();
if (!user.isIdentifiedUser()) {
throw new AuthException("Authentication required");
@@ -78,11 +86,16 @@
Task<?> task = workQueue.getTask(taskId);
if (task instanceof ProjectTask) {
+ Project.NameKey nameKey = ((ProjectTask<?>) task).getProjectNameKey();
+ ProjectState state = projectCache.get(nameKey);
+ if (state == null) {
+ throw new ResourceNotFoundException(String.format("project %s not found", nameKey));
+ }
+
+ state.checkStatePermitsRead();
+
try {
- permissionBackend
- .user(user)
- .project(((ProjectTask<?>) task).getProjectNameKey())
- .check(ProjectPermission.ACCESS);
+ permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
return new TaskResource(task);
} catch (AuthException e) {
// Fall through and try view queue permission.
diff --git a/java/com/google/gerrit/server/restapi/group/AddMembers.java b/java/com/google/gerrit/server/restapi/group/AddMembers.java
index f65b29f..9ddafe3 100644
--- a/java/com/google/gerrit/server/restapi/group/AddMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/AddMembers.java
@@ -29,7 +29,6 @@
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException;
@@ -92,7 +91,6 @@
private final AccountResolver accountResolver;
private final AccountCache accountCache;
private final AccountLoader.Factory infoFactory;
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
@@ -103,7 +101,6 @@
AccountResolver accountResolver,
AccountCache accountCache,
AccountLoader.Factory infoFactory,
- Provider<ReviewDb> db,
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.accountManager = accountManager;
this.authType = authConfig.getAuthType();
@@ -111,7 +108,6 @@
this.accountResolver = accountResolver;
this.accountCache = accountCache;
this.infoFactory = infoFactory;
- this.db = db;
this.groupsUpdateProvider = groupsUpdateProvider;
}
@@ -186,7 +182,7 @@
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.union(memberIds, newMemberIds))
.build();
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
}
private Optional<Account> createAccountByLdap(String user) throws IOException {
diff --git a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
index e29bb7c..e11f389 100644
--- a/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/AddSubgroups.java
@@ -28,7 +28,6 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
@@ -75,18 +74,15 @@
}
private final GroupsCollection groupsCollection;
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
private final GroupJson json;
@Inject
public AddSubgroups(
GroupsCollection groupsCollection,
- Provider<ReviewDb> db,
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
GroupJson json) {
this.groupsCollection = groupsCollection;
- this.db = db;
this.groupsUpdateProvider = groupsUpdateProvider;
this.json = json;
}
@@ -128,7 +124,7 @@
InternalGroupUpdate.builder()
.setSubgroupModification(subgroupUuids -> Sets.union(subgroupUuids, newSubgroupUuids))
.build();
- groupsUpdateProvider.get().updateGroup(db.get(), parentGroupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(parentGroupUuid, groupUpdate);
}
static class PutSubgroup implements RestModifyView<GroupResource, Input> {
diff --git a/java/com/google/gerrit/server/restapi/group/CreateGroup.java b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
index a0c88f2..79f9688 100644
--- a/java/com/google/gerrit/server/restapi/group/CreateGroup.java
+++ b/java/com/google/gerrit/server/restapi/group/CreateGroup.java
@@ -34,7 +34,6 @@
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.Sequences;
@@ -75,7 +74,6 @@
private final Provider<IdentifiedUser> self;
private final PersonIdent serverIdent;
- private final ReviewDb db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
private final GroupCache groupCache;
private final GroupsCollection groups;
@@ -91,7 +89,6 @@
CreateGroup(
Provider<IdentifiedUser> self,
@GerritPersonIdent PersonIdent serverIdent,
- ReviewDb db,
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
GroupCache groupCache,
GroupsCollection groups,
@@ -104,7 +101,6 @@
Sequences sequences) {
this.self = self;
this.serverIdent = serverIdent;
- this.db = db;
this.groupsUpdateProvider = groupsUpdateProvider;
this.groupCache = groupCache;
this.groups = groups;
@@ -222,7 +218,7 @@
groupUpdateBuilder.setMemberModification(
members -> ImmutableSet.copyOf(createGroupArgs.initialMembers));
try {
- return groupsUpdateProvider.get().createGroup(db, groupCreation, groupUpdateBuilder.build());
+ return groupsUpdateProvider.get().createGroup(groupCreation, groupUpdateBuilder.build());
} catch (OrmDuplicateKeyException e) {
throw new ResourceConflictException(
"group '" + createGroupArgs.getGroupName() + "' already exists");
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
index d92c521..3685469 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteMembers.java
@@ -25,7 +25,6 @@
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
@@ -46,16 +45,12 @@
@Singleton
public class DeleteMembers implements RestModifyView<GroupResource, Input> {
private final AccountsCollection accounts;
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
DeleteMembers(
- AccountsCollection accounts,
- Provider<ReviewDb> db,
- @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
+ AccountsCollection accounts, @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.accounts = accounts;
- this.db = db;
this.groupsUpdateProvider = groupsUpdateProvider;
}
@@ -93,7 +88,7 @@
InternalGroupUpdate.builder()
.setMemberModification(memberIds -> Sets.difference(memberIds, accountIds))
.build();
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
}
@Singleton
diff --git a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
index 6a5ac5d..0eba8c7 100644
--- a/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
+++ b/java/com/google/gerrit/server/restapi/group/DeleteSubgroups.java
@@ -25,7 +25,6 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.group.GroupResource;
@@ -45,16 +44,13 @@
@Singleton
public class DeleteSubgroups implements RestModifyView<GroupResource, Input> {
private final GroupsCollection groupsCollection;
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
DeleteSubgroups(
GroupsCollection groupsCollection,
- Provider<ReviewDb> db,
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.groupsCollection = groupsCollection;
- this.db = db;
this.groupsUpdateProvider = groupsUpdateProvider;
}
@@ -96,7 +92,7 @@
.setSubgroupModification(
subgroupUuids -> Sets.difference(subgroupUuids, removedSubgroupUuids))
.build();
- groupsUpdateProvider.get().updateGroup(db.get(), parentGroupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(parentGroupUuid, groupUpdate);
}
@Singleton
diff --git a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
index 51fffbb..eb66a37 100644
--- a/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
+++ b/java/com/google/gerrit/server/restapi/group/GetAuditLog.java
@@ -26,7 +26,6 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupCache;
@@ -38,7 +37,6 @@
import com.google.gerrit.server.group.db.Groups;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.ArrayList;
@@ -50,7 +48,6 @@
@Singleton
public class GetAuditLog implements RestReadView<GroupResource> {
- private final Provider<ReviewDb> db;
private final AccountLoader.Factory accountLoaderFactory;
private final AllUsersName allUsers;
private final GroupCache groupCache;
@@ -61,7 +58,6 @@
@Inject
public GetAuditLog(
- Provider<ReviewDb> db,
AccountLoader.Factory accountLoaderFactory,
AllUsersName allUsers,
GroupCache groupCache,
@@ -69,7 +65,6 @@
GroupBackend groupBackend,
Groups groups,
GitRepositoryManager repoManager) {
- this.db = db;
this.accountLoaderFactory = accountLoaderFactory;
this.allUsers = allUsers;
this.groupCache = groupCache;
@@ -95,7 +90,7 @@
try (Repository allUsersRepo = repoManager.openRepository(allUsers)) {
for (AccountGroupMemberAudit auditEvent :
- groups.getMembersAudit(db.get(), allUsersRepo, group.getGroupUUID())) {
+ groups.getMembersAudit(allUsersRepo, group.getGroupUUID())) {
AccountInfo member = accountLoader.get(auditEvent.getMemberId());
auditEvents.add(
@@ -110,7 +105,7 @@
}
for (AccountGroupByIdAud auditEvent :
- groups.getSubgroupsAudit(db.get(), allUsersRepo, group.getGroupUUID())) {
+ groups.getSubgroupsAudit(allUsersRepo, group.getGroupUUID())) {
AccountGroup.UUID includedGroupUUID = auditEvent.getIncludeUUID();
Optional<InternalGroup> includedGroup = groupCache.get(includedGroupUUID);
GroupInfo member;
diff --git a/java/com/google/gerrit/server/restapi/group/ListGroups.java b/java/com/google/gerrit/server/restapi/group/ListGroups.java
index 91aee6d..0dbd7b6 100644
--- a/java/com/google/gerrit/server/restapi/group/ListGroups.java
+++ b/java/com/google/gerrit/server/restapi/group/ListGroups.java
@@ -33,7 +33,6 @@
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountResource;
@@ -83,7 +82,6 @@
private final GroupBackend groupBackend;
private final Groups groups;
private final GroupsCollection groupsCollection;
- private final Provider<ReviewDb> db;
private EnumSet<ListGroupsOption> options = EnumSet.noneOf(ListGroupsOption.class);
private boolean visibleToAll;
@@ -232,8 +230,7 @@
final GroupsCollection groupsCollection,
GroupJson json,
GroupBackend groupBackend,
- Groups groups,
- Provider<ReviewDb> db) {
+ Groups groups) {
this.groupCache = groupCache;
this.groupControlFactory = groupControlFactory;
this.genericGroupControlFactory = genericGroupControlFactory;
@@ -244,7 +241,6 @@
this.groupBackend = groupBackend;
this.groups = groups;
this.groupsCollection = groupsCollection;
- this.db = db;
}
public void setOptions(EnumSet<ListGroupsOption> options) {
@@ -316,8 +312,7 @@
return groupInfos;
}
- private Stream<GroupReference> getAllExistingGroups()
- throws OrmException, IOException, ConfigInvalidException {
+ private Stream<GroupReference> getAllExistingGroups() throws IOException, ConfigInvalidException {
if (!projects.isEmpty()) {
return projects
.stream()
@@ -325,7 +320,7 @@
.flatMap(Collection::stream)
.distinct();
}
- return groups.getAllGroupReferences(db.get());
+ return groups.getAllGroupReferences();
}
private List<GroupInfo> suggestGroups() throws OrmException, BadRequestException {
@@ -388,7 +383,7 @@
Pattern pattern = getRegexPattern();
Stream<? extends GroupDescription.Internal> foundGroups =
groups
- .getAllGroupReferences(db.get())
+ .getAllGroupReferences()
.filter(group -> isRelevant(pattern, group))
.map(this::loadGroup)
.flatMap(Streams::stream)
diff --git a/java/com/google/gerrit/server/restapi/group/PutDescription.java b/java/com/google/gerrit/server/restapi/group/PutDescription.java
index 6a68c7a..d407f69 100644
--- a/java/com/google/gerrit/server/restapi/group/PutDescription.java
+++ b/java/com/google/gerrit/server/restapi/group/PutDescription.java
@@ -23,7 +23,6 @@
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -38,13 +37,10 @@
@Singleton
public class PutDescription implements RestModifyView<GroupResource, DescriptionInput> {
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
- PutDescription(
- Provider<ReviewDb> db, @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
- this.db = db;
+ PutDescription(@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.groupsUpdateProvider = groupsUpdateProvider;
}
@@ -69,7 +65,7 @@
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription(newDescription).build();
try {
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
}
diff --git a/java/com/google/gerrit/server/restapi/group/PutName.java b/java/com/google/gerrit/server/restapi/group/PutName.java
index f1dd4f1..1f1968a 100644
--- a/java/com/google/gerrit/server/restapi/group/PutName.java
+++ b/java/com/google/gerrit/server/restapi/group/PutName.java
@@ -24,13 +24,11 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gwtorm.server.OrmDuplicateKeyException;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -39,19 +37,17 @@
@Singleton
public class PutName implements RestModifyView<GroupResource, NameInput> {
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
- PutName(Provider<ReviewDb> db, @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
- this.db = db;
+ PutName(@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.groupsUpdateProvider = groupsUpdateProvider;
}
@Override
public String apply(GroupResource rsrc, NameInput input)
throws NotInternalGroupException, AuthException, BadRequestException,
- ResourceConflictException, ResourceNotFoundException, OrmException, IOException,
+ ResourceConflictException, ResourceNotFoundException, IOException,
ConfigInvalidException {
GroupDescription.Internal internalGroup =
rsrc.asInternalGroup().orElseThrow(NotInternalGroupException::new);
@@ -74,13 +70,13 @@
}
private void renameGroup(GroupDescription.Internal group, String newName)
- throws ResourceConflictException, ResourceNotFoundException, OrmException, IOException,
+ throws ResourceConflictException, ResourceNotFoundException, IOException,
ConfigInvalidException {
AccountGroup.UUID groupUuid = group.getGroupUUID();
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setName(new AccountGroup.NameKey(newName)).build();
try {
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
} catch (OrmDuplicateKeyException e) {
diff --git a/java/com/google/gerrit/server/restapi/group/PutOptions.java b/java/com/google/gerrit/server/restapi/group/PutOptions.java
index ab2ae1a..29b87d2 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOptions.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOptions.java
@@ -22,7 +22,6 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -36,12 +35,10 @@
@Singleton
public class PutOptions implements RestModifyView<GroupResource, GroupOptionsInfo> {
- private final Provider<ReviewDb> db;
private final Provider<GroupsUpdate> groupsUpdateProvider;
@Inject
- PutOptions(Provider<ReviewDb> db, @UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
- this.db = db;
+ PutOptions(@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider) {
this.groupsUpdateProvider = groupsUpdateProvider;
}
@@ -67,7 +64,7 @@
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setVisibleToAll(input.visibleToAll).build();
try {
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
}
diff --git a/java/com/google/gerrit/server/restapi/group/PutOwner.java b/java/com/google/gerrit/server/restapi/group/PutOwner.java
index 4d16e1e..5e7563e 100644
--- a/java/com/google/gerrit/server/restapi/group/PutOwner.java
+++ b/java/com/google/gerrit/server/restapi/group/PutOwner.java
@@ -25,7 +25,6 @@
import com.google.gerrit.extensions.restapi.RestModifyView;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.UserInitiated;
import com.google.gerrit.server.group.GroupResource;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -41,18 +40,15 @@
public class PutOwner implements RestModifyView<GroupResource, OwnerInput> {
private final GroupsCollection groupsCollection;
private final Provider<GroupsUpdate> groupsUpdateProvider;
- private final Provider<ReviewDb> db;
private final GroupJson json;
@Inject
PutOwner(
GroupsCollection groupsCollection,
@UserInitiated Provider<GroupsUpdate> groupsUpdateProvider,
- Provider<ReviewDb> db,
GroupJson json) {
this.groupsCollection = groupsCollection;
this.groupsUpdateProvider = groupsUpdateProvider;
- this.db = db;
this.json = json;
}
@@ -77,7 +73,7 @@
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setOwnerGroupUUID(owner.getGroupUUID()).build();
try {
- groupsUpdateProvider.get().updateGroup(db.get(), groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
} catch (NoSuchGroupException e) {
throw new ResourceNotFoundException(String.format("Group %s not found", groupUuid));
}
diff --git a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
index da5f058..f8ff7b9 100644
--- a/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/BranchesCollection.java
@@ -24,7 +24,6 @@
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -45,7 +44,6 @@
private final DynamicMap<RestView<BranchResource>> views;
private final Provider<ListBranches> list;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final GitRepositoryManager repoManager;
private final CreateBranch.Factory createBranchFactory;
@@ -54,13 +52,11 @@
DynamicMap<RestView<BranchResource>> views,
Provider<ListBranches> list,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
GitRepositoryManager repoManager,
CreateBranch.Factory createBranchFactory) {
this.views = views;
this.list = list;
this.permissionBackend = permissionBackend;
- this.user = user;
this.repoManager = repoManager;
this.createBranchFactory = createBranchFactory;
}
@@ -86,7 +82,7 @@
// rights on the symbolic reference itself. This check prevents seeing a hidden
// branch simply because the symbolic reference name was visible.
permissionBackend
- .user(user)
+ .currentUser()
.project(project)
.ref(ref.isSymbolic() ? ref.getTarget().getName() : ref.getName())
.check(RefPermission.READ);
diff --git a/java/com/google/gerrit/server/restapi/project/CheckAccess.java b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
index f98a96a..2c0653a 100644
--- a/java/com/google/gerrit/server/restapi/project/CheckAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/CheckAccess.java
@@ -88,7 +88,7 @@
IdentifiedUser user = userFactory.create(match.getId());
try {
permissionBackend.user(user).project(rsrc.getNameKey()).check(ProjectPermission.ACCESS);
- } catch (AuthException | PermissionBackendException e) {
+ } catch (AuthException e) {
info.message =
String.format(
"user %s (%s) cannot see project %s",
@@ -119,7 +119,7 @@
.user(user)
.ref(new Branch.NameKey(rsrc.getNameKey(), input.ref))
.check(refPerm);
- } catch (AuthException | PermissionBackendException e) {
+ } catch (AuthException e) {
info.status = HttpServletResponse.SC_FORBIDDEN;
info.message =
String.format(
diff --git a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
index 9b9d008..87b5343 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitsCollection.java
@@ -122,6 +122,6 @@
}
}
- return reachable.fromRefs(state, repo, commit, repo.getAllRefs());
+ return reachable.fromRefs(project, repo, commit, repo.getAllRefs());
}
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
index c154e0e..91dd923 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateAccessChange.java
@@ -18,7 +18,6 @@
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.errors.InvalidNameException;
-import com.google.gerrit.common.errors.PermissionDeniedException;
import com.google.gerrit.extensions.api.access.ProjectAccessInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.AuthException;
@@ -94,19 +93,18 @@
@Override
public Response<ChangeInfo> apply(ProjectResource rsrc, ProjectAccessInput input)
- throws PermissionBackendException, PermissionDeniedException, IOException,
- ConfigInvalidException, OrmException, InvalidNameException, UpdateException,
- RestApiException {
+ throws PermissionBackendException, AuthException, IOException, ConfigInvalidException,
+ OrmException, InvalidNameException, UpdateException, RestApiException {
PermissionBackend.ForProject forProject =
permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey());
if (!check(forProject, ProjectPermission.READ_CONFIG)) {
- throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
+ throw new AuthException(RefNames.REFS_CONFIG + " not visible");
}
if (!check(forProject, ProjectPermission.WRITE_CONFIG)) {
try {
forProject.ref(RefNames.REFS_CONFIG).check(RefPermission.CREATE_CHANGE);
} catch (AuthException denied) {
- throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
+ throw new AuthException("cannot create change for " + RefNames.REFS_CONFIG);
}
}
projectCache.checkedGet(rsrc.getNameKey()).checkStatePermitsWrite();
diff --git a/java/com/google/gerrit/server/restapi/project/CreateBranch.java b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
index b87e2f9..9f3c473 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateBranch.java
@@ -179,7 +179,7 @@
info.ref = ref;
info.revision = revid.getName();
info.canDelete =
- permissionBackend.user(identifiedUser).ref(name).testOrFalse(RefPermission.DELETE)
+ permissionBackend.currentUser().ref(name).testOrFalse(RefPermission.DELETE)
&& rsrc.getProjectState().statePermitsWrite()
? true
: null;
diff --git a/java/com/google/gerrit/server/restapi/project/CreateTag.java b/java/com/google/gerrit/server/restapi/project/CreateTag.java
index 2dc2c4a..2c3735f 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateTag.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateTag.java
@@ -26,7 +26,6 @@
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -39,7 +38,6 @@
import com.google.gerrit.server.project.RefUtil;
import com.google.gerrit.server.project.RefUtil.InvalidRevisionException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.TimeZone;
@@ -63,7 +61,6 @@
}
private final PermissionBackend permissionBackend;
- private final Provider<IdentifiedUser> identifiedUser;
private final GitRepositoryManager repoManager;
private final TagCache tagCache;
private final GitReferenceUpdated referenceUpdated;
@@ -73,14 +70,12 @@
@Inject
CreateTag(
PermissionBackend permissionBackend,
- Provider<IdentifiedUser> identifiedUser,
GitRepositoryManager repoManager,
TagCache tagCache,
GitReferenceUpdated referenceUpdated,
WebLinks webLinks,
@Assisted String ref) {
this.permissionBackend = permissionBackend;
- this.identifiedUser = identifiedUser;
this.repoManager = repoManager;
this.tagCache = tagCache;
this.referenceUpdated = referenceUpdated;
@@ -103,7 +98,7 @@
ref = RefUtil.normalizeTagRef(ref);
PermissionBackend.ForRef perm =
- permissionBackend.user(identifiedUser).project(resource.getNameKey()).ref(ref);
+ permissionBackend.currentUser().project(resource.getNameKey()).ref(ref);
try (Repository repo = repoManager.openRepository(resource.getNameKey())) {
ObjectId revid = RefUtil.parseBaseRevision(repo, resource.getNameKey(), input.revision);
@@ -134,7 +129,10 @@
if (isAnnotated) {
tag.setMessage(input.message)
.setTagger(
- identifiedUser.get().newCommitterIdent(TimeUtil.nowTs(), TimeZone.getDefault()));
+ resource
+ .getUser()
+ .asIdentifiedUser()
+ .newCommitterIdent(TimeUtil.nowTs(), TimeZone.getDefault()));
}
Ref result = tag.call();
@@ -145,7 +143,7 @@
ref,
ObjectId.zeroId(),
result.getObjectId(),
- identifiedUser.get().state());
+ resource.getUser().asIdentifiedUser().state());
try (RevWalk w = new RevWalk(repo)) {
return ListTags.createTagInfo(perm, result, w, resource.getProjectState(), links);
}
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
index 3114f8a..89213a0 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteBranch.java
@@ -21,7 +21,6 @@
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
@@ -38,25 +37,22 @@
private final Provider<InternalChangeQuery> queryProvider;
private final DeleteRef.Factory deleteRefFactory;
- private final Provider<CurrentUser> user;
private final PermissionBackend permissionBackend;
@Inject
DeleteBranch(
Provider<InternalChangeQuery> queryProvider,
DeleteRef.Factory deleteRefFactory,
- Provider<CurrentUser> user,
PermissionBackend permissionBackend) {
this.queryProvider = queryProvider;
this.deleteRefFactory = deleteRefFactory;
- this.user = user;
this.permissionBackend = permissionBackend;
}
@Override
public Response<?> apply(BranchResource rsrc, Input input)
throws RestApiException, OrmException, IOException, PermissionBackendException {
- permissionBackend.user(user).ref(rsrc.getBranchKey()).check(RefPermission.DELETE);
+ permissionBackend.currentUser().ref(rsrc.getBranchKey()).check(RefPermission.DELETE);
rsrc.getProjectState().checkStatePermitsWrite();
if (!queryProvider.get().setLimit(1).byBranchOpen(rsrc.getBranchKey()).isEmpty()) {
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteRef.java b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
index 56d68f9..c51fc56 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteRef.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteRef.java
@@ -222,7 +222,7 @@
try {
permissionBackend
- .user(identifiedUser)
+ .currentUser()
.project(project.getNameKey())
.ref(refName)
.check(RefPermission.DELETE);
diff --git a/java/com/google/gerrit/server/restapi/project/DeleteTag.java b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
index f432129..a3886bc 100644
--- a/java/com/google/gerrit/server/restapi/project/DeleteTag.java
+++ b/java/com/google/gerrit/server/restapi/project/DeleteTag.java
@@ -18,7 +18,6 @@
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestModifyView;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.RefPermission;
@@ -26,7 +25,6 @@
import com.google.gerrit.server.project.TagResource;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -34,16 +32,11 @@
public class DeleteTag implements RestModifyView<TagResource, Input> {
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final DeleteRef.Factory deleteRefFactory;
@Inject
- DeleteTag(
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
- DeleteRef.Factory deleteRefFactory) {
+ DeleteTag(PermissionBackend permissionBackend, DeleteRef.Factory deleteRefFactory) {
this.permissionBackend = permissionBackend;
- this.user = user;
this.deleteRefFactory = deleteRefFactory;
}
@@ -52,7 +45,7 @@
throws OrmException, RestApiException, IOException, PermissionBackendException {
String tag = RefUtil.normalizeTagRef(resource.getTagInfo().ref);
permissionBackend
- .user(user)
+ .currentUser()
.project(resource.getNameKey())
.ref(tag)
.check(RefPermission.DELETE);
diff --git a/java/com/google/gerrit/server/restapi/project/GetAccess.java b/java/com/google/gerrit/server/restapi/project/GetAccess.java
index 21d6013..38eb20a7 100644
--- a/java/com/google/gerrit/server/restapi/project/GetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/GetAccess.java
@@ -137,7 +137,7 @@
Project.NameKey projectName = rsrc.getNameKey();
ProjectAccessInfo info = new ProjectAccessInfo();
ProjectState projectState = projectCache.checkedGet(projectName);
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(projectName);
ProjectConfig config;
try (MetaDataUpdate md = metaDataUpdateFactory.create(projectName)) {
@@ -160,12 +160,12 @@
config.commit(md);
projectCache.evict(config.getProject());
projectState = projectCache.checkedGet(projectName);
- perm = permissionBackend.user(user).project(projectName);
+ perm = permissionBackend.currentUser().project(projectName);
} else if (config.getRevision() != null
&& !config.getRevision().equals(projectState.getConfig().getRevision())) {
projectCache.evict(config.getProject());
projectState = projectCache.checkedGet(projectName);
- perm = permissionBackend.user(user).project(projectName);
+ perm = permissionBackend.currentUser().project(projectName);
}
} catch (ConfigInvalidException e) {
throw new ResourceConflictException(e.getMessage());
@@ -239,7 +239,7 @@
}
if (info.ownerOf.isEmpty()
- && permissionBackend.user(user).test(GlobalPermission.ADMINISTRATE_SERVER)) {
+ && permissionBackend.currentUser().test(GlobalPermission.ADMINISTRATE_SERVER)) {
// Special case: If the section list is empty, this project has no current
// access control information. Fall back to site administrators.
info.ownerOf.add(AccessSection.ALL);
@@ -255,7 +255,7 @@
}
if (projectName.equals(allProjectsName)
- && permissionBackend.user(user).testOrFalse(ADMINISTRATE_SERVER)) {
+ && permissionBackend.currentUser().testOrFalse(ADMINISTRATE_SERVER)) {
info.ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
}
diff --git a/java/com/google/gerrit/server/restapi/project/ListBranches.java b/java/com/google/gerrit/server/restapi/project/ListBranches.java
index 05b6def..b6fa6d0 100644
--- a/java/com/google/gerrit/server/restapi/project/ListBranches.java
+++ b/java/com/google/gerrit/server/restapi/project/ListBranches.java
@@ -40,7 +40,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefFilter;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -58,7 +57,6 @@
public class ListBranches implements RestReadView<ProjectResource> {
private final GitRepositoryManager repoManager;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final DynamicMap<RestView<BranchResource>> branchViews;
private final UiActions uiActions;
private final WebLinks webLinks;
@@ -112,13 +110,11 @@
public ListBranches(
GitRepositoryManager repoManager,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
DynamicMap<RestView<BranchResource>> branchViews,
UiActions uiActions,
WebLinks webLinks) {
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
- this.user = user;
this.branchViews = branchViews;
this.uiActions = uiActions;
this.webLinks = webLinks;
@@ -183,7 +179,7 @@
}
}
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(rsrc.getNameKey());
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(rsrc.getNameKey());
List<BranchInfo> branches = new ArrayList<>(refs.size());
for (Ref ref : refs) {
if (ref.isSymbolic()) {
diff --git a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
index 5727c6b..3067c89 100644
--- a/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListChildProjects.java
@@ -20,7 +20,6 @@
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -31,7 +30,6 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -44,7 +42,6 @@
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final AllProjectsName allProjects;
private final ProjectJson json;
private final ChildProjects childProjects;
@@ -53,13 +50,11 @@
ListChildProjects(
ProjectCache projectCache,
PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
AllProjectsName allProjectsName,
ProjectJson json,
ChildProjects childProjects) {
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
- this.user = user;
this.allProjects = allProjectsName;
this.json = json;
this.childProjects = childProjects;
@@ -85,12 +80,14 @@
Map<Project.NameKey, Project> children = new HashMap<>();
for (Project.NameKey name : projectCache.all()) {
ProjectState c = projectCache.get(name);
- if (c != null && parent.equals(c.getProject().getParent(allProjects))) {
+ if (c != null
+ && parent.equals(c.getProject().getParent(allProjects))
+ && c.statePermitsRead()) {
children.put(c.getNameKey(), c.getProject());
}
}
return permissionBackend
- .user(user)
+ .currentUser()
.filter(ProjectPermission.ACCESS, children.keySet())
.stream()
.sorted()
diff --git a/java/com/google/gerrit/server/restapi/project/ListDashboards.java b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
index 2c8ea2e..882e922 100644
--- a/java/com/google/gerrit/server/restapi/project/ListDashboards.java
+++ b/java/com/google/gerrit/server/restapi/project/ListDashboards.java
@@ -20,7 +20,6 @@
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -29,7 +28,6 @@
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
@@ -53,19 +51,14 @@
private final GitRepositoryManager gitManager;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
@Option(name = "--inherited", usage = "include inherited dashboards")
private boolean inherited;
@Inject
- ListDashboards(
- GitRepositoryManager gitManager,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user) {
+ ListDashboards(GitRepositoryManager gitManager, PermissionBackend permissionBackend) {
this.gitManager = gitManager;
this.permissionBackend = permissionBackend;
- this.user = user;
}
@Override
@@ -95,16 +88,19 @@
private Collection<ProjectState> tree(ProjectResource rsrc) throws PermissionBackendException {
Map<Project.NameKey, ProjectState> tree = new LinkedHashMap<>();
for (ProjectState ps : rsrc.getProjectState().tree()) {
- tree.put(ps.getNameKey(), ps);
+ if (ps.statePermitsRead()) {
+ tree.put(ps.getNameKey(), ps);
+ }
}
+
tree.keySet()
- .retainAll(permissionBackend.user(user).filter(ProjectPermission.ACCESS, tree.keySet()));
+ .retainAll(permissionBackend.currentUser().filter(ProjectPermission.ACCESS, tree.keySet()));
return tree.values();
}
private List<DashboardInfo> scan(ProjectState state, String project, boolean setDefault)
throws ResourceNotFoundException, IOException, PermissionBackendException {
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(state.getNameKey());
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(state.getNameKey());
try (Repository git = gitManager.openRepository(state.getNameKey());
RevWalk rw = new RevWalk(git)) {
List<DashboardInfo> all = new ArrayList<>();
diff --git a/java/com/google/gerrit/server/restapi/project/ListProjects.java b/java/com/google/gerrit/server/restapi/project/ListProjects.java
index a1572c6..9a8232e 100644
--- a/java/com/google/gerrit/server/restapi/project/ListProjects.java
+++ b/java/com/google/gerrit/server/restapi/project/ListProjects.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.restapi.project;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.extensions.client.ProjectState.HIDDEN;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
@@ -521,11 +522,28 @@
if (type == FilterType.PARENT_CANDIDATES) {
matches = parentsOf(matches);
}
- // TODO(dborowitz): Streamified PermissionBackend#filter.
- return perm.filter(ProjectPermission.ACCESS, matches.collect(toList()))
- .stream()
- .sorted()
- .collect(toList());
+
+ List<Project.NameKey> results = new ArrayList<>();
+ List<Project.NameKey> projectNameKeys = matches.sorted().collect(toList());
+ for (Project.NameKey nameKey : projectNameKeys) {
+ ProjectState state = projectCache.get(nameKey);
+ checkNotNull(state, "Failed to load project %s", nameKey);
+
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ ProjectPermission permissionToCheck =
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
+ try {
+ perm.project(nameKey).check(permissionToCheck);
+ results.add(nameKey);
+ } catch (AuthException e) {
+ // Not added to results.
+ }
+ }
+
+ return results;
}
private Stream<Project.NameKey> parentsOf(Stream<Project.NameKey> matches) {
@@ -551,13 +569,19 @@
}
private boolean isParentAccessible(
- Map<Project.NameKey, Boolean> checked, PermissionBackend.WithUser perm, ProjectState p)
+ Map<Project.NameKey, Boolean> checked, PermissionBackend.WithUser perm, ProjectState state)
throws PermissionBackendException {
- Project.NameKey name = p.getNameKey();
+ Project.NameKey name = state.getNameKey();
Boolean b = checked.get(name);
if (b == null) {
try {
- perm.project(name).check(ProjectPermission.ACCESS);
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ ProjectPermission permissionToCheck =
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
+ perm.project(name).check(permissionToCheck);
b = true;
} catch (AuthException denied) {
b = false;
diff --git a/java/com/google/gerrit/server/restapi/project/ListTags.java b/java/com/google/gerrit/server/restapi/project/ListTags.java
index b0148c1..31ec7e1 100644
--- a/java/com/google/gerrit/server/restapi/project/ListTags.java
+++ b/java/com/google/gerrit/server/restapi/project/ListTags.java
@@ -24,7 +24,6 @@
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CommonConverters;
-import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.WebLinks;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -35,7 +34,6 @@
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.RefFilter;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
@@ -58,7 +56,6 @@
public class ListTags implements RestReadView<ProjectResource> {
private final GitRepositoryManager repoManager;
private final PermissionBackend permissionBackend;
- private final Provider<CurrentUser> user;
private final WebLinks links;
@Option(
@@ -108,13 +105,9 @@
@Inject
public ListTags(
- GitRepositoryManager repoManager,
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
- WebLinks webLinks) {
+ GitRepositoryManager repoManager, PermissionBackend permissionBackend, WebLinks webLinks) {
this.repoManager = repoManager;
this.permissionBackend = permissionBackend;
- this.user = user;
this.links = webLinks;
}
@@ -133,7 +126,8 @@
List<TagInfo> tags = new ArrayList<>();
- PermissionBackend.ForProject perm = permissionBackend.user(user).project(resource.getNameKey());
+ PermissionBackend.ForProject perm =
+ permissionBackend.currentUser().project(resource.getNameKey());
try (Repository repo = getRepository(resource.getNameKey());
RevWalk rw = new RevWalk(repo)) {
Map<String, Ref> all =
@@ -236,7 +230,7 @@
Project.NameKey project, Repository repo, Map<String, Ref> tags)
throws PermissionBackendException {
return permissionBackend
- .user(user)
+ .currentUser()
.project(project)
.filter(
tags,
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index c14ebab3..3af8424 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -150,8 +150,14 @@
}
if (checkAccess) {
+ // Hidden projects(permitsRead = false) should only be accessible by the project owners.
+ // READ_CONFIG is checked here because it's only allowed to project owners(ACCESS may also
+ // be allowed for other users). Allowing project owners to access here will help them to view
+ // and update the config of hidden projects easily.
+ ProjectPermission permissionToCheck =
+ state.statePermitsRead() ? ProjectPermission.ACCESS : ProjectPermission.READ_CONFIG;
try {
- permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
+ permissionBackend.currentUser().project(nameKey).check(permissionToCheck);
} catch (AuthException e) {
return null; // Pretend like not found on access denied.
}
@@ -161,7 +167,7 @@
// ACTIVE). Individual views should still check for checkStatePermitsRead() and this should
// just serve as a safety net in case the individual check is forgotten.
try {
- permissionBackend.user(user).project(nameKey).check(ProjectPermission.WRITE_CONFIG);
+ permissionBackend.currentUser().project(nameKey).check(ProjectPermission.WRITE_CONFIG);
} catch (AuthException e) {
state.checkStatePermitsRead();
}
diff --git a/java/com/google/gerrit/server/restapi/project/PutConfig.java b/java/com/google/gerrit/server/restapi/project/PutConfig.java
index 69c4c05..2cf407a 100644
--- a/java/com/google/gerrit/server/restapi/project/PutConfig.java
+++ b/java/com/google/gerrit/server/restapi/project/PutConfig.java
@@ -111,7 +111,10 @@
@Override
public ConfigInfo apply(ProjectResource rsrc, ConfigInput input)
throws RestApiException, PermissionBackendException {
- permissionBackend.user(user).project(rsrc.getNameKey()).check(ProjectPermission.WRITE_CONFIG);
+ permissionBackend
+ .currentUser()
+ .project(rsrc.getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
return apply(rsrc.getProjectState(), input);
}
diff --git a/java/com/google/gerrit/server/restapi/project/SetAccess.java b/java/com/google/gerrit/server/restapi/project/SetAccess.java
index 5a34522..80a07b5 100644
--- a/java/com/google/gerrit/server/restapi/project/SetAccess.java
+++ b/java/com/google/gerrit/server/restapi/project/SetAccess.java
@@ -97,12 +97,12 @@
boolean isGlobalCapabilities = AccessSection.GLOBAL_CAPABILITIES.equals(section.getName());
if (isGlobalCapabilities) {
if (!checkedAdmin) {
- permissionBackend.user(identifiedUser).check(GlobalPermission.ADMINISTRATE_SERVER);
+ permissionBackend.currentUser().check(GlobalPermission.ADMINISTRATE_SERVER);
checkedAdmin = true;
}
} else {
permissionBackend
- .user(identifiedUser)
+ .currentUser()
.project(rsrc.getNameKey())
.ref(section.getName())
.check(RefPermission.WRITE_CONFIG);
diff --git a/java/com/google/gerrit/server/rules/DefaultSubmitRule.java b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
new file mode 100644
index 0000000..a9482fe
--- /dev/null
+++ b/java/com/google/gerrit/server/rules/DefaultSubmitRule.java
@@ -0,0 +1,132 @@
+// Copyright (C) 2018 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.server.rules;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+
+import com.google.gerrit.common.data.LabelFunction;
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.SubmitRecord;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.extensions.config.FactoryModule;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.project.SubmitRuleOptions;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Java implementation of Gerrit's default pre-submit rules behavior: check if the labels have the
+ * correct values, according to the {@link LabelFunction} they are attached to.
+ *
+ * <p>As this behavior is also implemented by the Prolog rules system, we skip it if at least one
+ * project in the hierarchy has a {@code rules.pl} file.
+ */
+@Singleton
+public final class DefaultSubmitRule implements SubmitRule {
+ private static final Logger log = LoggerFactory.getLogger(DefaultSubmitRule.class);
+
+ public static class Module extends FactoryModule {
+ @Override
+ public void configure() {
+ bind(SubmitRule.class)
+ .annotatedWith(Exports.named("DefaultRules"))
+ .to(DefaultSubmitRule.class);
+ }
+ }
+
+ private final ProjectCache projectCache;
+
+ @Inject
+ DefaultSubmitRule(ProjectCache projectCache) {
+ this.projectCache = projectCache;
+ }
+
+ @Override
+ public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions options) {
+ ProjectState projectState = projectCache.get(cd.project());
+
+ // In case at least one project has a rules.pl file, we let Prolog handle it.
+ // The Prolog rules engine will also handle the labels for us.
+ if (projectState == null || projectState.hasPrologRules()) {
+ return Collections.emptyList();
+ }
+
+ SubmitRecord submitRecord = new SubmitRecord();
+ submitRecord.status = SubmitRecord.Status.OK;
+
+ List<LabelType> labelTypes;
+ List<PatchSetApproval> approvals;
+ try {
+ labelTypes = cd.getLabelTypes().getLabelTypes();
+ approvals = cd.currentApprovals();
+ } catch (OrmException e) {
+ log.error("Unable to fetch labels and approvals for change {}", cd.getId(), e);
+
+ submitRecord.errorMessage = "Unable to fetch labels and approvals for the change";
+ submitRecord.status = SubmitRecord.Status.RULE_ERROR;
+ return Collections.singletonList(submitRecord);
+ }
+
+ submitRecord.labels = new ArrayList<>(labelTypes.size());
+
+ for (LabelType t : labelTypes) {
+ LabelFunction labelFunction = t.getFunction();
+ if (labelFunction == null) {
+ log.error(
+ "Unable to find the LabelFunction for label {}, change {}", t.getName(), cd.getId());
+
+ submitRecord.errorMessage = "Unable to find the LabelFunction for label " + t.getName();
+ submitRecord.status = SubmitRecord.Status.RULE_ERROR;
+ return Collections.singletonList(submitRecord);
+ }
+
+ Collection<PatchSetApproval> approvalsForLabel = getApprovalsForLabel(approvals, t);
+ SubmitRecord.Label label = labelFunction.check(t, approvalsForLabel);
+ submitRecord.labels.add(label);
+
+ switch (label.status) {
+ case OK:
+ case MAY:
+ break;
+
+ case NEED:
+ case REJECT:
+ case IMPOSSIBLE:
+ submitRecord.status = SubmitRecord.Status.NOT_READY;
+ break;
+ }
+ }
+
+ return Collections.singletonList(submitRecord);
+ }
+
+ private static List<PatchSetApproval> getApprovalsForLabel(
+ List<PatchSetApproval> approvals, LabelType t) {
+ return approvals
+ .stream()
+ .filter(input -> input.getLabel().equals(t.getLabelId().get()))
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/com/google/gerrit/server/rules/PrologRule.java b/java/com/google/gerrit/server/rules/PrologRule.java
index 44f18c5f..deddc36 100644
--- a/java/com/google/gerrit/server/rules/PrologRule.java
+++ b/java/com/google/gerrit/server/rules/PrologRule.java
@@ -16,25 +16,34 @@
import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gerrit.server.rules.PrologRuleEvaluator.Factory;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.Collection;
+import java.util.Collections;
@Singleton
public class PrologRule implements SubmitRule {
-
- private final Factory factory;
+ private final PrologRuleEvaluator.Factory factory;
+ private final ProjectCache projectCache;
@Inject
- private PrologRule(PrologRuleEvaluator.Factory factory) {
+ private PrologRule(PrologRuleEvaluator.Factory factory, ProjectCache projectCache) {
this.factory = factory;
+ this.projectCache = projectCache;
}
@Override
public Collection<SubmitRecord> evaluate(ChangeData cd, SubmitRuleOptions opts) {
+ ProjectState projectState = projectCache.get(cd.project());
+ // We only want to run the prolog engine if we have at least one rules.pl file to use.
+ if (projectState == null || !projectState.hasPrologRules()) {
+ return Collections.emptyList();
+ }
+
return getEvaluator(cd, opts).evaluate();
}
diff --git a/java/com/google/gerrit/server/schema/GroupBundle.java b/java/com/google/gerrit/server/schema/GroupBundle.java
index 302ea55..3c1c409 100644
--- a/java/com/google/gerrit/server/schema/GroupBundle.java
+++ b/java/com/google/gerrit/server/schema/GroupBundle.java
@@ -195,7 +195,7 @@
Timestamp createdOn = rs.getTimestamp(3);
String description = rs.getString(4);
AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
- boolean visibleToAll = rs.getBoolean(6);
+ boolean visibleToAll = "Y".equals(rs.getString(6));
AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
group.setDescription(description);
diff --git a/java/com/google/gerrit/server/schema/NoGroupsReviewDbWrapper.java b/java/com/google/gerrit/server/schema/NoGroupsReviewDbWrapper.java
deleted file mode 100644
index 33c4d77..0000000
--- a/java/com/google/gerrit/server/schema/NoGroupsReviewDbWrapper.java
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright (C) 2017 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.server.schema;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.AccountGroupMember;
-import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
-import com.google.gerrit.reviewdb.server.AccountGroupAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupMemberAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupMemberAuditAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupNameAccess;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
-import com.google.gwtorm.server.ListResultSet;
-import com.google.gwtorm.server.OrmException;
-import com.google.gwtorm.server.ResultSet;
-
-/**
- * Wrapper for ReviewDb that never calls the underlying groups tables.
- *
- * <p>See {@link NotesMigrationSchemaFactory} for discussion.
- */
-public class NoGroupsReviewDbWrapper extends ReviewDbWrapper {
- private static <T> ResultSet<T> empty() {
- return new ListResultSet<>(ImmutableList.of());
- }
-
- private final AccountGroupAccess groups;
- private final AccountGroupNameAccess groupNames;
- private final AccountGroupMemberAccess members;
- private final AccountGroupMemberAuditAccess memberAudits;
- private final AccountGroupByIdAccess byIds;
- private final AccountGroupByIdAudAccess byIdAudits;
-
- protected NoGroupsReviewDbWrapper(ReviewDb db) {
- super(db);
- this.groups = new Groups(this, delegate);
- this.groupNames = new GroupNames(this, delegate);
- this.members = new Members(this, delegate);
- this.memberAudits = new MemberAudits(this, delegate);
- this.byIds = new ByIds(this, delegate);
- this.byIdAudits = new ByIdAudits(this, delegate);
- }
-
- @Override
- public AccountGroupAccess accountGroups() {
- return groups;
- }
-
- @Override
- public AccountGroupNameAccess accountGroupNames() {
- return groupNames;
- }
-
- @Override
- public AccountGroupMemberAccess accountGroupMembers() {
- return members;
- }
-
- @Override
- public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
- return memberAudits;
- }
-
- @Override
- public AccountGroupByIdAudAccess accountGroupByIdAud() {
- return byIdAudits;
- }
-
- @Override
- public AccountGroupByIdAccess accountGroupById() {
- return byIds;
- }
-
- private static class Groups extends AbstractDisabledAccess<AccountGroup, AccountGroup.Id>
- implements AccountGroupAccess {
- private Groups(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroups());
- }
-
- @Override
- public ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroup> all() throws OrmException {
- return empty();
- }
- }
-
- private static class GroupNames
- extends AbstractDisabledAccess<AccountGroupName, AccountGroup.NameKey>
- implements AccountGroupNameAccess {
- private GroupNames(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroupNames());
- }
-
- @Override
- public ResultSet<AccountGroupName> all() throws OrmException {
- return empty();
- }
- }
-
- private static class Members
- extends AbstractDisabledAccess<AccountGroupMember, AccountGroupMember.Key>
- implements AccountGroupMemberAccess {
- private Members(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroupMembers());
- }
-
- @Override
- public ResultSet<AccountGroupMember> byAccount(Account.Id id) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroupMember> byGroup(AccountGroup.Id id) throws OrmException {
- return empty();
- }
- }
-
- private static class MemberAudits
- extends AbstractDisabledAccess<AccountGroupMemberAudit, AccountGroupMemberAudit.Key>
- implements AccountGroupMemberAuditAccess {
- private MemberAudits(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroupMembersAudit());
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroupAccount(
- AccountGroup.Id groupId, com.google.gerrit.reviewdb.client.Account.Id accountId)
- throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroupMemberAudit> byGroup(AccountGroup.Id groupId) throws OrmException {
- return empty();
- }
- }
-
- private static class ByIds extends AbstractDisabledAccess<AccountGroupById, AccountGroupById.Key>
- implements AccountGroupByIdAccess {
- private ByIds(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroupById());
- }
-
- @Override
- public ResultSet<AccountGroupById> byIncludeUUID(AccountGroup.UUID uuid) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroupById> byGroup(AccountGroup.Id id) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroupById> all() throws OrmException {
- return empty();
- }
- }
-
- private static class ByIdAudits
- extends AbstractDisabledAccess<AccountGroupByIdAud, AccountGroupByIdAud.Key>
- implements AccountGroupByIdAudAccess {
- private ByIdAudits(ReviewDbWrapper wrapper, ReviewDb db) {
- super(wrapper, db.accountGroupByIdAud());
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroupInclude(
- AccountGroup.Id groupId, AccountGroup.UUID incGroupUUID) throws OrmException {
- return empty();
- }
-
- @Override
- public ResultSet<AccountGroupByIdAud> byGroup(AccountGroup.Id groupId) throws OrmException {
- return empty();
- }
- }
-}
diff --git a/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java b/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
index 9bc8b61..0d95610 100644
--- a/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
+++ b/java/com/google/gerrit/server/schema/NotesMigrationSchemaFactory.java
@@ -15,9 +15,7 @@
package com.google.gerrit.server.schema;
import com.google.gerrit.reviewdb.server.DisallowReadFromChangesReviewDbWrapper;
-import com.google.gerrit.reviewdb.server.DisallowReadFromGroupsReviewDbWrapper;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
@@ -28,16 +26,12 @@
public class NotesMigrationSchemaFactory implements SchemaFactory<ReviewDb> {
private final SchemaFactory<ReviewDb> delegate;
private final NotesMigration migration;
- private final GroupsMigration groupsMigration;
@Inject
NotesMigrationSchemaFactory(
- @ReviewDbFactory SchemaFactory<ReviewDb> delegate,
- NotesMigration migration,
- GroupsMigration groupsMigration) {
+ @ReviewDbFactory SchemaFactory<ReviewDb> delegate, NotesMigration migration) {
this.delegate = delegate;
this.migration = migration;
- this.groupsMigration = groupsMigration;
}
@Override
@@ -73,23 +67,12 @@
db = new NoChangesReviewDbWrapper(db);
}
- if (groupsMigration.readFromNoteDb() && groupsMigration.disableGroupReviewDb()) {
- // Disable writes to group tables in ReviewDb (ReviewDb access for groups are No-Ops).
- db = new NoGroupsReviewDbWrapper(db);
- }
-
// Second create the wrappers which can be removed by ReviewDbUtil#unwrapDb(ReviewDb).
if (migration.readChanges()) {
// If reading changes from NoteDb is configured, changes should not be read from ReviewDb.
// Make sure that any attempt to read a change from ReviewDb anyway fails with an exception.
db = new DisallowReadFromChangesReviewDbWrapper(db);
}
-
- if (groupsMigration.readFromNoteDb()) {
- // If reading groups from NoteDb is configured, groups should not be read from ReviewDb.
- // Make sure that any attempt to read a group from ReviewDb anyway fails with an exception.
- db = new DisallowReadFromGroupsReviewDbWrapper(db);
- }
return db;
}
}
diff --git a/java/com/google/gerrit/server/schema/SchemaCreator.java b/java/com/google/gerrit/server/schema/SchemaCreator.java
index ba68b2c..88b3e28 100644
--- a/java/com/google/gerrit/server/schema/SchemaCreator.java
+++ b/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -14,13 +14,11 @@
package com.google.gerrit.server.schema;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.metrics.MetricMaker;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupName;
import com.google.gerrit.reviewdb.client.CurrentSchemaVersion;
import com.google.gerrit.reviewdb.client.SystemConfig;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -40,12 +38,10 @@
import com.google.gerrit.server.group.db.AuditLogFormatter;
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
-import com.google.gerrit.server.group.db.GroupsUpdate;
import com.google.gerrit.server.group.db.InternalGroupCreation;
import com.google.gerrit.server.group.db.InternalGroupUpdate;
import com.google.gerrit.server.index.group.GroupIndex;
import com.google.gerrit.server.index.group.GroupIndexCollection;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gwtorm.jdbc.JdbcExecutor;
@@ -73,7 +69,6 @@
private final PersonIdent serverUser;
private final DataSourceType dataSourceType;
private final GroupIndexCollection indexCollection;
- private final GroupsMigration groupsMigration;
private final String serverId;
private final Config config;
@@ -91,7 +86,6 @@
@GerritPersonIdent PersonIdent au,
DataSourceType dst,
GroupIndexCollection ic,
- GroupsMigration gm,
@GerritServerId String serverId,
@GerritServerConfig Config config,
MetricMaker metricMaker,
@@ -106,7 +100,6 @@
au,
dst,
ic,
- gm,
serverId,
config,
metricMaker,
@@ -123,7 +116,6 @@
@GerritPersonIdent PersonIdent au,
DataSourceType dst,
GroupIndexCollection ic,
- GroupsMigration gm,
String serverId,
Config config,
MetricMaker metricMaker,
@@ -137,7 +129,6 @@
serverUser = au;
dataSourceType = dst;
indexCollection = ic;
- groupsMigration = gm;
this.serverId = serverId;
this.config = config;
@@ -176,25 +167,24 @@
allUsersName,
metricMaker);
try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
- createAdminsGroup(db, seqs, allUsersRepo, admins);
- createBatchUsersGroup(db, seqs, allUsersRepo, batchUsers, admins.getUUID());
+ createAdminsGroup(seqs, allUsersRepo, admins);
+ createBatchUsersGroup(seqs, allUsersRepo, batchUsers, admins.getUUID());
}
dataSourceType.getIndexScript().run(db);
}
private void createAdminsGroup(
- ReviewDb db, Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
+ Sequences seqs, Repository allUsersRepo, GroupReference groupReference)
throws OrmException, IOException, ConfigInvalidException {
InternalGroupCreation groupCreation = getGroupCreation(seqs, groupReference);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription("Gerrit Site Administrators").build();
- createGroup(db, allUsersRepo, groupCreation, groupUpdate);
+ createGroup(allUsersRepo, groupCreation, groupUpdate);
}
private void createBatchUsersGroup(
- ReviewDb db,
Sequences seqs,
Repository allUsersRepo,
GroupReference groupReference,
@@ -207,35 +197,16 @@
.setOwnerGroupUUID(adminsGroupUuid)
.build();
- createGroup(db, allUsersRepo, groupCreation, groupUpdate);
+ createGroup(allUsersRepo, groupCreation, groupUpdate);
}
private void createGroup(
- ReviewDb db,
- Repository allUsersRepo,
- InternalGroupCreation groupCreation,
- InternalGroupUpdate groupUpdate)
+ Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws OrmException, ConfigInvalidException, IOException {
- InternalGroup groupInReviewDb = createGroupInReviewDb(db, groupCreation, groupUpdate);
-
- if (!groupsMigration.writeToNoteDb()) {
- index(groupInReviewDb);
- return;
- }
-
InternalGroup createdGroup = createGroupInNoteDb(allUsersRepo, groupCreation, groupUpdate);
index(createdGroup);
}
- private static InternalGroup createGroupInReviewDb(
- ReviewDb db, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
- throws OrmException {
- AccountGroup group = GroupsUpdate.createAccountGroup(groupCreation, groupUpdate);
- db.accountGroupNames().insert(ImmutableList.of(new AccountGroupName(group)));
- db.accountGroups().insert(ImmutableList.of(group));
- return InternalGroup.create(group, ImmutableSet.of(), ImmutableSet.of());
- }
-
private InternalGroup createGroupInNoteDb(
Repository allUsersRepo, InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws ConfigInvalidException, IOException, OrmDuplicateKeyException {
diff --git a/java/com/google/gerrit/server/schema/SchemaVersion.java b/java/com/google/gerrit/server/schema/SchemaVersion.java
index 4b8c13f..e8a59d0 100644
--- a/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -35,7 +35,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_166> C = Schema_166.class;
+ public static final Class<Schema_168> C = Schema_168.class;
public static int getBinaryVersion() {
return guessVersion(C);
diff --git a/java/com/google/gerrit/server/schema/Schema_167.java b/java/com/google/gerrit/server/schema/Schema_167.java
new file mode 100644
index 0000000..5e93b2c
--- /dev/null
+++ b/java/com/google/gerrit/server/schema/Schema_167.java
@@ -0,0 +1,287 @@
+// Copyright (C) 2018 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.server.schema;
+
+import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
+import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
+import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.account.AccountConfig;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.group.db.AuditLogFormatter;
+import com.google.gerrit.server.group.db.GroupNameNotes;
+import com.google.gerrit.server.update.RefUpdateUtil;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.BatchRefUpdate;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Migrate groups from ReviewDb to NoteDb. */
+public class Schema_167 extends SchemaVersion {
+ private static final Logger log = LoggerFactory.getLogger(Schema_167.class);
+
+ private final GitRepositoryManager repoManager;
+ private final AllUsersName allUsersName;
+ private final Config gerritConfig;
+ private final SitePaths sitePaths;
+ private final PersonIdent serverIdent;
+ private final SystemGroupBackend systemGroupBackend;
+
+ @Inject
+ protected Schema_167(
+ Provider<Schema_166> prior,
+ GitRepositoryManager repoManager,
+ AllUsersName allUsersName,
+ @GerritServerConfig Config gerritConfig,
+ SitePaths sitePaths,
+ @GerritPersonIdent PersonIdent serverIdent,
+ SystemGroupBackend systemGroupBackend) {
+ super(prior);
+ this.repoManager = repoManager;
+ this.allUsersName = allUsersName;
+ this.gerritConfig = gerritConfig;
+ this.sitePaths = sitePaths;
+ this.serverIdent = serverIdent;
+ this.systemGroupBackend = systemGroupBackend;
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
+ if (gerritConfig.getBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false)) {
+ // Groups in ReviewDb have already been disabled, nothing to do.
+ return;
+ }
+
+ try (Repository allUsersRepo = repoManager.openRepository(allUsersName)) {
+ List<GroupReference> allGroupReferences = readGroupReferencesFromReviewDb(db);
+
+ BatchRefUpdate batchRefUpdate = allUsersRepo.getRefDatabase().newBatchUpdate();
+ writeAllGroupNamesToNoteDb(allUsersRepo, allGroupReferences, batchRefUpdate);
+
+ GroupRebuilder groupRebuilder = createGroupRebuilder(db, allUsersRepo);
+ for (GroupReference groupReference : allGroupReferences) {
+ migrateOneGroupToNoteDb(
+ db, allUsersRepo, groupRebuilder, groupReference.getUUID(), batchRefUpdate);
+ }
+
+ RefUpdateUtil.executeChecked(batchRefUpdate, allUsersRepo);
+ } catch (IOException | ConfigInvalidException e) {
+ throw new OrmException(
+ String.format("Failed to migrate groups to NoteDb for %s", allUsersName.get()), e);
+ }
+ }
+
+ private List<GroupReference> readGroupReferencesFromReviewDb(ReviewDb db) throws SQLException {
+ try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT group_uuid, name FROM account_groups")) {
+ List<GroupReference> allGroupReferences = new ArrayList<>();
+ while (rs.next()) {
+ AccountGroup.UUID groupUuid = new AccountGroup.UUID(rs.getString(1));
+ String groupName = rs.getString(2);
+ allGroupReferences.add(new GroupReference(groupUuid, groupName));
+ }
+ return allGroupReferences;
+ }
+ }
+
+ private void writeAllGroupNamesToNoteDb(
+ Repository allUsersRepo,
+ List<GroupReference> allGroupReferences,
+ BatchRefUpdate batchRefUpdate)
+ throws IOException {
+ try (ObjectInserter inserter = allUsersRepo.newObjectInserter()) {
+ GroupNameNotes.updateAllGroups(
+ allUsersRepo, inserter, batchRefUpdate, allGroupReferences, serverIdent);
+ inserter.flush();
+ }
+ }
+
+ private GroupRebuilder createGroupRebuilder(ReviewDb db, Repository allUsersRepo)
+ throws IOException, ConfigInvalidException {
+ AuditLogFormatter auditLogFormatter =
+ createAuditLogFormatter(db, allUsersRepo, gerritConfig, sitePaths);
+ return new GroupRebuilder(serverIdent, allUsersName, auditLogFormatter);
+ }
+
+ private AuditLogFormatter createAuditLogFormatter(
+ ReviewDb db, Repository allUsersRepo, Config gerritConfig, SitePaths sitePaths)
+ throws IOException, ConfigInvalidException {
+ String serverId = new GerritServerIdProvider(gerritConfig, sitePaths).get();
+ SimpleInMemoryAccountCache accountCache = new SimpleInMemoryAccountCache(allUsersRepo);
+ SimpleInMemoryGroupCache groupCache = new SimpleInMemoryGroupCache(db);
+ return AuditLogFormatter.create(
+ accountCache::get,
+ uuid -> {
+ if (systemGroupBackend.handles(uuid)) {
+ return Optional.ofNullable(systemGroupBackend.get(uuid));
+ }
+ return groupCache.get(uuid);
+ },
+ serverId);
+ }
+
+ private static void migrateOneGroupToNoteDb(
+ ReviewDb db,
+ Repository allUsersRepo,
+ GroupRebuilder rebuilder,
+ AccountGroup.UUID uuid,
+ BatchRefUpdate batchRefUpdate)
+ throws ConfigInvalidException, IOException, OrmException {
+ GroupBundle reviewDbBundle = GroupBundle.Factory.fromReviewDb(db, uuid);
+ RefUpdateUtil.deleteChecked(allUsersRepo, RefNames.refsGroups(uuid));
+ rebuilder.rebuild(allUsersRepo, reviewDbBundle, batchRefUpdate);
+ }
+
+ // The regular account cache isn't available during init. -> Use a simple replacement which tries
+ // to load every account only once from disk.
+ private static class SimpleInMemoryAccountCache {
+ private final Repository allUsersRepo;
+ private Map<Account.Id, Optional<Account>> accounts = new HashMap<>();
+
+ public SimpleInMemoryAccountCache(Repository allUsersRepo) {
+ this.allUsersRepo = allUsersRepo;
+ }
+
+ public Optional<Account> get(Account.Id accountId) {
+ accounts.computeIfAbsent(accountId, this::load);
+ return accounts.get(accountId);
+ }
+
+ private Optional<Account> load(Account.Id accountId) {
+ try {
+ AccountConfig accountConfig = new AccountConfig(accountId, allUsersRepo).load();
+ return accountConfig.getLoadedAccount();
+ } catch (IOException | ConfigInvalidException ignored) {
+ log.warn(
+ "Failed to load account {}."
+ + " Cannot get account name for group audit log commit messages.",
+ accountId.get(),
+ ignored);
+ return Optional.empty();
+ }
+ }
+ }
+
+ // The regular GroupBackends (especially external GroupBackends) and our internal group cache
+ // aren't available during init. -> Use a simple replacement which tries to look up only internal
+ // groups and which loads every internal group only once from disc. (There's no way we can look up
+ // external groups during init. As we need those groups only for cosmetic aspects in
+ // AuditLogFormatter, it's safe to exclude them.)
+ private static class SimpleInMemoryGroupCache {
+ private final ReviewDb db;
+ private Map<AccountGroup.UUID, Optional<GroupDescription.Basic>> groups = new HashMap<>();
+
+ public SimpleInMemoryGroupCache(ReviewDb db) {
+ this.db = db;
+ }
+
+ public Optional<GroupDescription.Basic> get(AccountGroup.UUID groupUuid) {
+ groups.computeIfAbsent(groupUuid, this::load);
+ return groups.get(groupUuid);
+ }
+
+ private Optional<GroupDescription.Basic> load(AccountGroup.UUID groupUuid) {
+ if (!AccountGroup.isInternalGroup(groupUuid)) {
+ return Optional.empty();
+ }
+
+ List<GroupDescription.Basic> groupDescriptions = getGroupDescriptions(groupUuid);
+ if (groupDescriptions.size() == 1) {
+ return Optional.of(Iterables.getOnlyElement(groupDescriptions));
+ }
+ return Optional.empty();
+ }
+
+ private List<GroupDescription.Basic> getGroupDescriptions(AccountGroup.UUID groupUuid) {
+ try (Statement stmt = ReviewDbWrapper.unwrapJbdcSchema(db).getConnection().createStatement();
+ ResultSet rs =
+ stmt.executeQuery(
+ "SELECT name FROM account_groups where group_uuid = '" + groupUuid + "'")) {
+ List<GroupDescription.Basic> groupDescriptions = new ArrayList<>();
+ while (rs.next()) {
+ String groupName = rs.getString(1);
+ groupDescriptions.add(toGroupDescription(groupUuid, groupName));
+ }
+ return groupDescriptions;
+ } catch (SQLException ignored) {
+ log.warn(
+ "Failed to load group {}."
+ + " Cannot get group name for group audit log commit messages.",
+ groupUuid.get(),
+ ignored);
+ return ImmutableList.of();
+ }
+ }
+
+ private static GroupDescription.Basic toGroupDescription(
+ AccountGroup.UUID groupUuid, String groupName) {
+ return new GroupDescription.Basic() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return groupUuid;
+ }
+
+ @Override
+ public String getName() {
+ return groupName;
+ }
+
+ @Nullable
+ @Override
+ public String getEmailAddress() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getUrl() {
+ return null;
+ }
+ };
+ }
+ }
+}
diff --git a/java/com/google/gerrit/common/errors/PermissionDeniedException.java b/java/com/google/gerrit/server/schema/Schema_168.java
similarity index 63%
rename from java/com/google/gerrit/common/errors/PermissionDeniedException.java
rename to java/com/google/gerrit/server/schema/Schema_168.java
index 0faf498..3ea8468 100644
--- a/java/com/google/gerrit/common/errors/PermissionDeniedException.java
+++ b/java/com/google/gerrit/server/schema/Schema_168.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2011 The Android Open Source Project
+// Copyright (C) 2018 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.
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.common.errors;
+package com.google.gerrit.server.schema;
-/** Indicates the user cannot perform this task. */
-public class PermissionDeniedException extends Exception {
- private static final long serialVersionUID = 1L;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
- public PermissionDeniedException(String msg) {
- super(msg);
+/** Drop group tables. */
+public class Schema_168 extends SchemaVersion {
+ @Inject
+ Schema_168(Provider<Schema_167> prior) {
+ super(prior);
}
}
diff --git a/java/com/google/gerrit/sshd/commands/KillCommand.java b/java/com/google/gerrit/sshd/commands/KillCommand.java
index a7e751a..ef12f5f 100644
--- a/java/com/google/gerrit/sshd/commands/KillCommand.java
+++ b/java/com/google/gerrit/sshd/commands/KillCommand.java
@@ -20,6 +20,7 @@
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.TaskResource;
@@ -51,7 +52,10 @@
try {
TaskResource taskRsrc = tasksCollection.parse(cfgRsrc, IdString.fromDecoded(id));
deleteTask.apply(taskRsrc, null);
- } catch (AuthException | ResourceNotFoundException | PermissionBackendException e) {
+ } catch (AuthException
+ | ResourceNotFoundException
+ | ResourceConflictException
+ | PermissionBackendException e) {
stderr.print("kill: " + id + ": No such task\n");
}
}
diff --git a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
index 9e334e6..cd9fbda 100644
--- a/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
+++ b/java/com/google/gerrit/sshd/commands/RenameGroupCommand.java
@@ -23,7 +23,6 @@
import com.google.gerrit.server.restapi.group.PutName;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
-import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import java.io.IOException;
import org.eclipse.jgit.errors.ConfigInvalidException;
@@ -53,7 +52,7 @@
NameInput input = new NameInput();
input.name = newGroupName;
putName.apply(rsrc, input);
- } catch (RestApiException | OrmException | IOException | ConfigInvalidException e) {
+ } catch (RestApiException | IOException | ConfigInvalidException e) {
throw die(e);
}
}
diff --git a/java/com/google/gerrit/testing/DisabledReviewDb.java b/java/com/google/gerrit/testing/DisabledReviewDb.java
index 998b893..d902e11 100644
--- a/java/com/google/gerrit/testing/DisabledReviewDb.java
+++ b/java/com/google/gerrit/testing/DisabledReviewDb.java
@@ -14,12 +14,6 @@
package com.google.gerrit.testing;
-import com.google.gerrit.reviewdb.server.AccountGroupAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupByIdAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupByIdAudAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupMemberAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupMemberAuditAccess;
-import com.google.gerrit.reviewdb.server.AccountGroupNameAccess;
import com.google.gerrit.reviewdb.server.ChangeAccess;
import com.google.gerrit.reviewdb.server.ChangeMessageAccess;
import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
@@ -82,26 +76,6 @@
}
@Override
- public AccountGroupAccess accountGroups() {
- throw new Disabled();
- }
-
- @Override
- public AccountGroupNameAccess accountGroupNames() {
- throw new Disabled();
- }
-
- @Override
- public AccountGroupMemberAccess accountGroupMembers() {
- throw new Disabled();
- }
-
- @Override
- public AccountGroupMemberAuditAccess accountGroupMembersAudit() {
- throw new Disabled();
- }
-
- @Override
public ChangeAccess changes() {
throw new Disabled();
}
@@ -127,16 +101,6 @@
}
@Override
- public AccountGroupByIdAccess accountGroupById() {
- throw new Disabled();
- }
-
- @Override
- public AccountGroupByIdAudAccess accountGroupByIdAud() {
- throw new Disabled();
- }
-
- @Override
public int nextAccountId() {
throw new Disabled();
}
diff --git a/java/com/google/gerrit/testing/GroupNoteDbMode.java b/java/com/google/gerrit/testing/GroupNoteDbMode.java
deleted file mode 100644
index 86e92b8..0000000
--- a/java/com/google/gerrit/testing/GroupNoteDbMode.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2017 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.testing;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import com.google.common.base.Enums;
-import com.google.common.base.Strings;
-import com.google.gerrit.server.notedb.GroupsMigration;
-
-public enum GroupNoteDbMode {
- /** NoteDb is disabled, groups are only in ReviewDb */
- OFF(new GroupsMigration(false, false, false)),
-
- /** Writing new groups to NoteDb is enabled. */
- WRITE(new GroupsMigration(true, false, false)),
-
- /**
- * Reading/writing groups from/to NoteDb is enabled. Trying to read groups from ReviewDb throws an
- * exception.
- */
- READ_WRITE(new GroupsMigration(true, true, false)),
-
- /**
- * All group tables in ReviewDb are entirely disabled. Trying to read groups from ReviewDb throws
- * an exception. Reading groups through an unwrapped ReviewDb instance writing groups to ReviewDb
- * is a No-Op.
- */
- ON(new GroupsMigration(true, true, true));
-
- private static final String ENV_VAR = "GERRIT_NOTEDB_GROUPS";
- private static final String SYS_PROP = "gerrit.notedb.groups";
-
- public static GroupNoteDbMode get() {
- String value = System.getenv(ENV_VAR);
- if (Strings.isNullOrEmpty(value)) {
- value = System.getProperty(SYS_PROP);
- }
- if (Strings.isNullOrEmpty(value)) {
- return OFF;
- }
- value = value.toUpperCase().replace("-", "_");
- GroupNoteDbMode mode = Enums.getIfPresent(GroupNoteDbMode.class, value).orNull();
- if (!Strings.isNullOrEmpty(System.getenv(ENV_VAR))) {
- checkArgument(
- mode != null, "Invalid value for env variable %s: %s", ENV_VAR, System.getenv(ENV_VAR));
- } else {
- checkArgument(
- mode != null,
- "Invalid value for system property %s: %s",
- SYS_PROP,
- System.getProperty(SYS_PROP));
- }
- return mode;
- }
-
- private final GroupsMigration groupsMigration;
-
- private GroupNoteDbMode(GroupsMigration groupsMigration) {
- this.groupsMigration = groupsMigration;
- }
-
- public GroupsMigration getGroupsMigration() {
- return groupsMigration;
- }
-}
diff --git a/java/com/google/gerrit/testing/InMemoryModule.java b/java/com/google/gerrit/testing/InMemoryModule.java
index ea4b174..af7c72c 100644
--- a/java/com/google/gerrit/testing/InMemoryModule.java
+++ b/java/com/google/gerrit/testing/InMemoryModule.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.Scopes.SINGLETON;
+import com.google.common.base.Strings;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.gerrit.extensions.client.AuthType;
@@ -46,6 +47,7 @@
import com.google.gerrit.server.config.GerritOptions;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.GerritServerId;
+import com.google.gerrit.server.config.GerritServerIdProvider;
import com.google.gerrit.server.config.SitePath;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.config.TrackingFootersProvider;
@@ -183,7 +185,7 @@
bind(String.class)
.annotatedWith(AnonymousCowardName.class)
.toProvider(AnonymousCowardNameProvider.class);
- bind(String.class).annotatedWith(GerritServerId.class).toInstance("gerrit");
+
bind(AllProjectsName.class).toProvider(AllProjectsNameProvider.class);
bind(AllUsersName.class).toProvider(AllUsersNameProvider.class);
bind(GitRepositoryManager.class).to(InMemoryRepositoryManager.class);
@@ -269,6 +271,19 @@
@Provides
@Singleton
+ @GerritServerId
+ public String createServerId() {
+ String serverId =
+ cfg.getString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY);
+ if (!Strings.isNullOrEmpty(serverId)) {
+ return serverId;
+ }
+
+ return "gerrit";
+ }
+
+ @Provides
+ @Singleton
InMemoryDatabase getInMemoryDatabase(SchemaCreator schemaCreator) throws OrmException {
return new InMemoryDatabase(schemaCreator);
}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
index 389efb4..7af057f 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupIndexerIT.java
@@ -15,11 +15,6 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth.assertWithMessage;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.READ;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.WRITE;
import static com.google.gerrit.truth.ListSubject.assertThat;
import static com.google.gerrit.truth.OptionalSubject.assertThat;
@@ -29,7 +24,6 @@
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.group.InternalGroup;
@@ -48,27 +42,15 @@
import java.util.List;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
import org.junit.Rule;
import org.junit.Test;
public class GroupIndexerIT {
- private static Config createPureNoteDbConfig() {
- Config config = new Config();
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, true);
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, true);
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, true);
- return config;
- }
-
- @Rule
- public InMemoryTestEnvironment testEnvironment =
- new InMemoryTestEnvironment(GroupIndexerIT::createPureNoteDbConfig);
+ @Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
@Inject private GroupIndexer groupIndexer;
@Inject private GerritApi gApi;
@Inject private GroupCache groupCache;
- @Inject private ReviewDb db;
@Inject @ServerInitiated private GroupsUpdate groupsUpdate;
@Inject private Provider<InternalGroupQuery> groupQueryProvider;
@@ -176,7 +158,7 @@
private void updateGroupWithoutCacheOrIndex(
AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
throws OrmException, NoSuchGroupException, IOException, ConfigInvalidException {
- groupsUpdate.updateGroupInDb(db, groupUuid, groupUpdate);
+ groupsUpdate.updateGroupInDb(groupUuid, groupUpdate);
}
private static OptionalSubject<InternalGroupSubject, InternalGroup> assertThatGroup(
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
index 4e9c37b..87a566e 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsConsistencyIT.java
@@ -15,9 +15,7 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
@@ -32,11 +30,7 @@
import com.google.gerrit.server.group.db.GroupConfig;
import com.google.gerrit.server.group.db.GroupNameNotes;
import com.google.gerrit.server.group.db.testing.GroupTestUtil;
-import com.google.gerrit.server.notedb.GroupsMigration;
-import com.google.gerrit.server.notedb.NotesMigration;
-import com.google.gerrit.testing.ConfigSuite;
import java.util.List;
-import javax.inject.Inject;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.RefRename;
import org.eclipse.jgit.lib.RefUpdate;
@@ -53,17 +47,6 @@
@Sandboxed
@NoHttpd
public class GroupsConsistencyIT extends AbstractDaemonTest {
-
- @ConfigSuite.Config
- public static Config noteDbConfig() {
- Config config = new Config();
- config.setBoolean(NotesMigration.SECTION_NOTE_DB, GROUPS.key(), NotesMigration.WRITE, true);
- config.setBoolean(NotesMigration.SECTION_NOTE_DB, GROUPS.key(), NotesMigration.READ, true);
- return config;
- }
-
- @Inject private GroupsMigration groupsMigration;
-
private GroupInfo gAdmin;
private GroupInfo g1;
private GroupInfo g2;
@@ -72,7 +55,6 @@
@Before
public void basicSetup() throws Exception {
- assume().that(groupsInNoteDb()).isTrue();
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
String name1 = createGroup("g1");
@@ -87,10 +69,6 @@
this.gAdmin = gApi.groups().id("Administrators").detail();
}
- private boolean groupsInNoteDb() {
- return groupsMigration.writeToNoteDb();
- }
-
@Test
public void allGood() throws Exception {
assertThat(check()).isEmpty();
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 0e5da12..c176acd 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -16,18 +16,12 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.GitUtil.deleteRef;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.acceptance.api.group.GroupAssert.assertGroupInfo;
import static com.google.gerrit.acceptance.rest.account.AccountAssert.assertAccountInfos;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.READ;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.WRITE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.stream.Collectors.toList;
@@ -85,7 +79,6 @@
import com.google.gerrit.server.index.group.GroupIndexer;
import com.google.gerrit.server.index.group.StalenessChecker;
import com.google.gerrit.server.util.MagicBranch;
-import com.google.gerrit.testing.ConfigSuite;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.io.IOException;
@@ -119,21 +112,6 @@
@NoHttpd
public class GroupsIT extends AbstractDaemonTest {
- @ConfigSuite.Config
- public static Config noteDbConfig() {
- Config config = new Config();
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, true);
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, true);
- return config;
- }
-
- @ConfigSuite.Config
- public static Config disableReviewDb() {
- Config config = noteDbConfig();
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, true);
- return config;
- }
-
@Inject private Groups groups;
@Inject @ServerInitiated private GroupsUpdate groupsUpdate;
@Inject private GroupIncludeCache groupIncludeCache;
@@ -732,7 +710,7 @@
@Test
public void listAllGroups() throws Exception {
List<String> expectedGroups =
- groups.getAllGroupReferences(db).map(GroupReference::getName).sorted().collect(toList());
+ groups.getAllGroupReferences().map(GroupReference::getName).sorted().collect(toList());
assertThat(expectedGroups.size()).isAtLeast(2);
assertThat(gApi.groups().list().getAsMap().keySet())
.containsExactlyElementsIn(expectedGroups)
@@ -908,8 +886,6 @@
@Sandboxed
@IgnoreGroupInconsistencies
public void getAuditLogAfterDeletingASubgroup() throws Exception {
- assume().that(readGroupsFromNoteDb()).isTrue();
-
GroupInfo parentGroup = gApi.groups().create(name("parent-group")).get();
// Creates a subgroup and adds it to "parent-group" as a subgroup.
@@ -973,7 +949,6 @@
@Test
public void pushToGroupBranchIsRejectedForAllUsersRepo() throws Exception {
- assume().that(groupsInNoteDb()).isTrue(); // branch only exists when groups are in NoteDb
assertPushToGroupBranch(
allUsers, RefNames.refsGroups(adminGroupUuid()), "group update not allowed");
}
@@ -989,7 +964,6 @@
@Test
public void pushToGroupNamesBranchIsRejectedForAllUsersRepo() throws Exception {
- assume().that(groupsInNoteDb()).isTrue(); // branch only exists when groups are in NoteDb
// refs/meta/group-names isn't usually available for fetch, so grant ACCESS_DATABASE
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
assertPushToGroupBranch(allUsers, RefNames.REFS_GROUPNAMES, "group update not allowed");
@@ -1114,8 +1088,6 @@
@Test
@IgnoreGroupInconsistencies
public void cannotCreateGroupNamesBranch() throws Exception {
- assume().that(groupsInNoteDb()).isTrue();
-
// Use ProjectResetter to restore the group names ref
try (ProjectResetter resetter =
projectResetter
@@ -1155,7 +1127,6 @@
@Test
public void cannotDeleteGroupBranch() throws Exception {
- assume().that(groupsInNoteDb()).isTrue();
testCannotDeleteGroupBranch(RefNames.REFS_GROUPS + "*", RefNames.refsGroups(adminGroupUuid()));
}
@@ -1168,8 +1139,6 @@
@Test
public void cannotDeleteGroupNamesBranch() throws Exception {
- assume().that(groupsInNoteDb()).isTrue();
-
// refs/meta/group-names is only visible with ACCESS_DATABASE
allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
@@ -1199,8 +1168,6 @@
@Test
@IgnoreGroupInconsistencies
public void stalenessChecker() throws Exception {
- assume().that(readGroupsFromNoteDb()).isTrue();
-
// Newly created group is not stale
GroupInfo groupInfo = gApi.groups().create(name("foo")).get();
AccountGroup.UUID groupUuid = new AccountGroup.UUID(groupInfo.id);
@@ -1246,8 +1213,6 @@
@Test
@Sandboxed
public void groupsOfUserCanBeListedInSlaveMode() throws Exception {
- assume().that(readGroupsFromNoteDb()).isTrue();
-
GroupInput groupInput = new GroupInput();
groupInput.name = name("contributors");
groupInput.members = ImmutableList.of(user.username);
@@ -1267,11 +1232,8 @@
@GerritConfig(name = "index.autoReindexIfStale", value = "false")
@IgnoreGroupInconsistencies
public void reindexGroupsInSlaveMode() throws Exception {
- assume().that(readGroupsFromNoteDb()).isTrue();
- assume().that(cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false)).isTrue();
-
List<AccountGroup.UUID> expectedGroups =
- groups.getAllGroupReferences(db).map(GroupReference::getUUID).collect(toList());
+ groups.getAllGroupReferences().map(GroupReference::getUUID).collect(toList());
assertThat(expectedGroups.size()).isAtLeast(2);
// Restart the server as slave, on startup of the slave all groups are indexed.
@@ -1303,7 +1265,7 @@
// Update a group without updating the cache or index,
// then run the reindexer -> only the updated group is reindexed.
groupsUpdate.updateGroupInDb(
- db, groupUuid, InternalGroupUpdate.builder().setDescription("bar").build());
+ groupUuid, InternalGroupUpdate.builder().setDescription("bar").build());
slaveGroupIndexer.run();
groupIndexedCounter.assertReindexOf(groupUuid);
@@ -1328,10 +1290,8 @@
@GerritConfig(name = "index.autoReindexIfStale", value = "false")
@IgnoreGroupInconsistencies
public void disabledReindexGroupsOnStartupSlaveMode() throws Exception {
- assume().that(readGroupsFromNoteDb()).isTrue();
-
List<AccountGroup.UUID> expectedGroups =
- groups.getAllGroupReferences(db).map(GroupReference::getUUID).collect(toList());
+ groups.getAllGroupReferences().map(GroupReference::getUUID).collect(toList());
assertThat(expectedGroups.size()).isAtLeast(2);
restartAsSlave();
@@ -1360,8 +1320,6 @@
private void pushToGroupBranchForReviewAndSubmit(
Project.NameKey project, String groupRef, String expectedError) throws Exception {
- assume().that(groupsInNoteDb()).isTrue(); // branch only exists when groups are in NoteDb
-
grantLabel(
"Code-Review", -2, 2, project, RefNames.REFS_GROUPS + "*", false, REGISTERED_USERS, false);
grant(project, RefNames.REFS_GROUPS + "*", Permission.SUBMIT, false, REGISTERED_USERS);
@@ -1484,14 +1442,6 @@
}
}
- private boolean groupsInNoteDb() {
- return cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, false);
- }
-
- private boolean readGroupsFromNoteDb() {
- return groupsInNoteDb() && cfg.getBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, false);
- }
-
@Target({METHOD})
@Retention(RUNTIME)
private @interface IgnoreGroupInconsistencies {}
diff --git a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
index e7ddcae..44be241 100644
--- a/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/group/GroupsUpdateIT.java
@@ -15,17 +15,11 @@
package com.google.gerrit.acceptance.api.group;
import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
-import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.READ;
-import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
-import static com.google.gerrit.server.notedb.NotesMigration.WRITE;
import com.google.common.collect.ImmutableSet;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ServerInitiated;
import com.google.gerrit.server.group.db.Groups;
import com.google.gerrit.server.group.db.GroupsUpdate;
@@ -39,27 +33,14 @@
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
-import org.eclipse.jgit.lib.Config;
import org.junit.Rule;
import org.junit.Test;
public class GroupsUpdateIT {
-
- private static Config createPureNoteDbConfig() {
- Config config = new Config();
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), WRITE, true);
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), READ, true);
- config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, true);
- return config;
- }
-
- @Rule
- public InMemoryTestEnvironment testEnvironment =
- new InMemoryTestEnvironment(GroupsUpdateIT::createPureNoteDbConfig);
+ @Rule public InMemoryTestEnvironment testEnvironment = new InMemoryTestEnvironment();
@Inject @ServerInitiated private Provider<GroupsUpdate> groupsUpdateProvider;
@Inject private Groups groups;
- @Inject private ReviewDb reviewDb;
@Test
public void groupCreationIsRetriedWhenFailedDueToConcurrentNameModification() throws Exception {
@@ -100,17 +81,16 @@
private void createGroup(InternalGroupCreation groupCreation, InternalGroupUpdate groupUpdate)
throws OrmException, IOException, ConfigInvalidException {
- groupsUpdateProvider.get().createGroup(reviewDb, groupCreation, groupUpdate);
+ groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
}
private void updateGroup(AccountGroup.UUID groupUuid, InternalGroupUpdate groupUpdate)
throws Exception {
- groupsUpdateProvider.get().updateGroup(reviewDb, groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroup(groupUuid, groupUpdate);
}
- private Stream<String> getAllGroupNames()
- throws OrmException, IOException, ConfigInvalidException {
- return groups.getAllGroupReferences(reviewDb).map(GroupReference::getName);
+ private Stream<String> getAllGroupNames() throws IOException, ConfigInvalidException {
+ return groups.getAllGroupReferences().map(GroupReference::getName);
}
private static InternalGroupCreation getGroupCreation(String groupName, String groupUuid) {
@@ -145,7 +125,7 @@
InternalGroupCreation groupCreation = getGroupCreation(groupName, groupName + "-UUID");
InternalGroupUpdate groupUpdate = InternalGroupUpdate.builder().build();
try {
- groupsUpdateProvider.get().createGroup(reviewDb, groupCreation, groupUpdate);
+ groupsUpdateProvider.get().createGroup(groupCreation, groupUpdate);
} catch (OrmException | IOException | ConfigInvalidException e) {
throw new IllegalStateException(e);
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 867ace4..0988dab 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -20,6 +20,7 @@
import static com.google.gerrit.acceptance.GitUtil.assertPushOk;
import static com.google.gerrit.acceptance.GitUtil.assertPushRejected;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
+import static com.google.gerrit.acceptance.GitUtil.pushOne;
import static com.google.gerrit.acceptance.PushOneCommit.FILE_NAME;
import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
import static com.google.gerrit.extensions.client.ListChangesOption.ALL_REVISIONS;
@@ -30,6 +31,7 @@
import static com.google.gerrit.extensions.common.testing.EditInfoSubject.assertThat;
import static com.google.gerrit.server.git.receive.ReceiveConstants.PUSH_OPTION_SKIP_VALIDATION;
import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static com.google.gerrit.server.project.testing.Util.category;
import static com.google.gerrit.server.project.testing.Util.value;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -48,6 +50,7 @@
import com.google.gerrit.acceptance.Sandboxed;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.TestProjectInput;
+import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.DraftInput;
@@ -78,6 +81,7 @@
import com.google.gerrit.reviewdb.client.RevId;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.receive.NoteDbPushOption;
import com.google.gerrit.server.git.receive.ReceiveConstants;
import com.google.gerrit.server.git.validators.CommitValidators.ChangeIdValidator;
import com.google.gerrit.server.group.SystemGroupBackend;
@@ -2018,6 +2022,53 @@
.endsWith("Pushing to refs/publish/* is deprecated, use refs/for/* instead.\n");
}
+ @Test
+ public void pushNoteDbRef() throws Exception {
+ String ref = "refs/changes/34/1234/meta";
+ RevCommit c = testRepo.commit().message("Junk NoteDb commit").create();
+ PushResult pr = pushOne(testRepo, c.name(), ref, false, false, null);
+ assertThat(pr.getMessages()).doesNotContain(NoteDbPushOption.OPTION_NAME);
+ assertPushRejected(pr, ref, "NoteDb update requires -o notedb=allow");
+
+ pr = pushOne(testRepo, c.name(), ref, false, false, ImmutableList.of("notedb=foobar"));
+ assertThat(pr.getMessages()).contains("Invalid value in -o notedb=foobar");
+ assertPushRejected(pr, ref, "NoteDb update requires -o notedb=allow");
+
+ List<String> opts = ImmutableList.of("notedb=allow");
+ pr = pushOne(testRepo, c.name(), ref, false, false, opts);
+ assertPushRejected(pr, ref, "NoteDb update requires access database permission");
+
+ allowGlobalCapabilities(REGISTERED_USERS, GlobalCapability.ACCESS_DATABASE);
+ pr = pushOne(testRepo, c.name(), ref, false, false, opts);
+ assertPushRejected(pr, ref, "prohibited by Gerrit: create not permitted for " + ref);
+
+ grant(project, "refs/changes/*", Permission.CREATE);
+ grant(project, "refs/changes/*", Permission.PUSH);
+ grantSkipValidation(project, "refs/changes/*", REGISTERED_USERS);
+ pr = pushOne(testRepo, c.name(), ref, false, false, opts);
+ assertPushOk(pr, ref);
+ }
+
+ @Test
+ public void pushNoteDbRefWithoutOptionOnlyFailsThatCommand() throws Exception {
+ String ref = "refs/changes/34/1234/meta";
+ RevCommit noteDbCommit = testRepo.commit().message("Junk NoteDb commit").create();
+ RevCommit changeCommit =
+ testRepo.branch("HEAD").commit().message("A change").insertChangeId().create();
+ PushResult pr =
+ Iterables.getOnlyElement(
+ testRepo
+ .git()
+ .push()
+ .setRefSpecs(
+ new RefSpec(noteDbCommit.name() + ":" + ref),
+ new RefSpec(changeCommit.name() + ":refs/for/master"))
+ .call());
+
+ assertPushRejected(pr, ref, "NoteDb update requires -o notedb=allow");
+ assertPushOk(pr, "refs/for/master");
+ }
+
private DraftInput newDraft(String path, int line, String message) {
DraftInput d = new DraftInput();
d.path = path;
diff --git a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
index 35fcc94..1bb23fb 100644
--- a/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
+++ b/javatests/com/google/gerrit/acceptance/pgm/StandaloneNoteDbMigrationIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.pgm;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
@@ -28,7 +29,9 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalDiskRepositoryManager;
import com.google.gerrit.server.index.GerritIndexStatus;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
@@ -41,6 +44,11 @@
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -72,6 +80,16 @@
gerritConfig = new FileBasedConfig(sitePaths.gerrit_config.toFile(), FS.detect());
// Unlike in the running server, for tests, we don't stack notedb.config on gerrit.config.
noteDbConfig = new FileBasedConfig(sitePaths.notedb_config.toFile(), FS.detect());
+
+ // Set gc.pruneExpire=now so GC prunes all unreachable objects from All-Users, which allows us
+ // to reliably test that it behaves as expected.
+ Path cfgPath = sitePaths.site_path.resolve("git").resolve("All-Users.git").resolve("config");
+ assertWithMessage("Expected All-Users config at %s", cfgPath)
+ .that(Files.isRegularFile(cfgPath))
+ .isTrue();
+ FileBasedConfig cfg = new FileBasedConfig(cfgPath.toFile(), FS.detect());
+ cfg.setString("gc", null, "pruneExpire", "now");
+ cfg.save();
}
@Test
@@ -114,11 +132,17 @@
migrate();
assertNotesMigrationState(NotesMigrationState.NOTE_DB);
+ File allUsersDir;
try (ServerContext ctx = startServer()) {
GitRepositoryManager repoManager = ctx.getInjector().getInstance(GitRepositoryManager.class);
try (Repository repo = repoManager.openRepository(project)) {
assertThat(repo.exactRef(RefNames.changeMetaRef(changeId))).isNotNull();
}
+ assertThat(repoManager).isInstanceOf(LocalDiskRepositoryManager.class);
+ try (Repository repo =
+ repoManager.openRepository(ctx.getInjector().getInstance(AllUsersName.class))) {
+ allUsersDir = repo.getDirectory();
+ }
try (ReviewDb db = openUnderlyingReviewDb(ctx)) {
Change c = db.changes().get(changeId);
@@ -137,6 +161,15 @@
}
assertNoAutoMigrateConfig(gerritConfig);
assertAutoMigrateConfig(noteDbConfig, false);
+
+ try (FileRepository repo = new FileRepository(allUsersDir)) {
+ try (Stream<Path> paths = Files.walk(repo.getObjectsDirectory().toPath())) {
+ assertThat(paths.filter(p -> !p.toString().contains("pack") && Files.isRegularFile(p)))
+ .named("loose object files in All-Users")
+ .isEmpty();
+ }
+ assertThat(repo.getObjectDatabase().getPacks()).named("packfiles in All-Users").hasSize(1);
+ }
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
index 7d60b8d..95b64e0 100644
--- a/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/account/ExternalIdIT.java
@@ -15,6 +15,7 @@
package com.google.gerrit.acceptance.rest.account;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
@@ -31,6 +32,7 @@
import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
@@ -74,6 +76,7 @@
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.revwalk.FooterLine;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.PushResult;
@@ -779,6 +782,130 @@
assertThat(externalIds.byAccount(admin.id)).containsExactlyElementsIn(expectedExternalIds);
}
+ @Test
+ public void unsetEmail() throws Exception {
+ ExternalId extId = ExternalId.createWithEmail("x", "1", user.id, "x@example.com");
+ insertExtId(extId);
+
+ ExternalId extIdWithoutEmail = ExternalId.create("x", "1", user.id);
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.upsert(extIdWithoutEmail);
+ extIdNotes.commit(md);
+
+ assertThat(extIdNotes.get(extId.key())).hasValue(extIdWithoutEmail);
+ }
+ }
+
+ @Test
+ public void unsetHttpPassword() throws Exception {
+ ExternalId extId =
+ ExternalId.createWithPassword(ExternalId.Key.create("y", "1"), user.id, null, "secret");
+ insertExtId(extId);
+
+ ExternalId extIdWithoutPassword = ExternalId.create("y", "1", user.id);
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.upsert(extIdWithoutPassword);
+ extIdNotes.commit(md);
+
+ assertThat(extIdNotes.get(extId.key())).hasValue(extIdWithoutPassword);
+ }
+ }
+
+ @Test
+ public void footers() throws Exception {
+ // Insert external ID for different accounts
+ TestAccount user1 = accountCreator.create("user1");
+ TestAccount user2 = accountCreator.create("user2");
+ ExternalId extId1 = ExternalId.create("foo", "1", user1.id);
+ ExternalId extId2 = ExternalId.create("foo", "2", user1.id);
+ ExternalId extId3 = ExternalId.create("foo", "3", user2.id);
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.insert(ImmutableSet.of(extId1, extId2, extId3));
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly("Account: " + user1.getId(), "Account: " + user2.getId())
+ .inOrder();
+ }
+
+ // Insert external ID with different emails
+ ExternalId extId4 = ExternalId.createWithEmail("foo", "4", user1.id, "foo4@example.com");
+ ExternalId extId5 = ExternalId.createWithEmail("foo", "5", user2.id, "foo5@example.com");
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.insert(ImmutableSet.of(extId4, extId5));
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly(
+ "Account: " + user1.getId(),
+ "Account: " + user2.getId(),
+ "Email: foo4@example.com",
+ "Email: foo5@example.com")
+ .inOrder();
+ }
+
+ // Update external ID - Add Email
+ ExternalId extId1a = ExternalId.createWithEmail("foo", "1", user1.id, "foo1@example.com");
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.upsert(extId1a);
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+ .inOrder();
+ }
+
+ // Update external ID - Remove Email
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.upsert(extId1);
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly("Account: " + user1.getId(), "Email: foo1@example.com")
+ .inOrder();
+ }
+
+ // Delete external IDs
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.delete(ImmutableSet.of(extId1, extId5));
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly(
+ "Account: " + user1.getId(), "Account: " + user2.getId(), "Email: foo5@example.com")
+ .inOrder();
+ }
+
+ // Delete external ID by key without email
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.delete(extId2.accountId(), extId2.key());
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c)).containsExactly("Account: " + user1.getId()).inOrder();
+ }
+
+ // Delete external ID by key with email
+ try (Repository allUsersRepo = repoManager.openRepository(allUsers);
+ MetaDataUpdate md = metaDataUpdateFactory.create(allUsers)) {
+ ExternalIdNotes extIdNotes = externalIdNotesFactory.load(allUsersRepo);
+ extIdNotes.delete(extId4.accountId(), extId4.key());
+ RevCommit c = extIdNotes.commit(md);
+ assertThat(getFooters(c))
+ .containsExactly("Account: " + user1.getId(), "Email: foo4@example.com")
+ .inOrder();
+ }
+ }
+
private void insertExtId(ExternalId extId) throws Exception {
accountsUpdateProvider
.get()
@@ -823,6 +950,10 @@
}
}
+ private List<String> getFooters(RevCommit c) {
+ return c.getFooterLines().stream().map(FooterLine::toString).collect(toList());
+ }
+
private List<AccountExternalIdInfo> toExternalIdInfos(Collection<ExternalId> extIds) {
return extIds.stream().map(this::toExternalIdInfo).collect(toList());
}
diff --git a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
index e7d7fc7..0c1be53 100644
--- a/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/project/CustomLabelIT.java
@@ -232,6 +232,23 @@
}
@Test
+ public void customLabelMaxWithBlock_MaxVoteNegativeVoteBlock() throws Exception {
+ label.setFunction(MAX_WITH_BLOCK);
+ saveLabelConfig();
+ PushOneCommit.Result r = createChange();
+ revision(r).review(new ReviewInput().label(label.getName(), 1));
+ revision(r).review(new ReviewInput().label(label.getName(), -1));
+ ChangeInfo c = getWithLabels(r);
+ LabelInfo q = c.labels.get(label.getName());
+ assertThat(q.all).hasSize(1);
+ assertThat(q.approved).isNull();
+ assertThat(q.recommended).isNull();
+ assertThat(q.disliked).isNull();
+ assertThat(q.rejected).isNotNull();
+ assertThat(q.blocking).isTrue();
+ }
+
+ @Test
public void customLabel_DisallowPostSubmit() throws Exception {
label.setFunction(NO_OP);
label.setAllowPostSubmit(false);
diff --git a/javatests/com/google/gerrit/common/data/LabelFunctionTest.java b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
new file mode 100644
index 0000000..985f514
--- /dev/null
+++ b/javatests/com/google/gerrit/common/data/LabelFunctionTest.java
@@ -0,0 +1,148 @@
+// Copyright (C) 2018 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.common.data;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.LabelId;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.PatchSet.Id;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import java.sql.Date;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class LabelFunctionTest {
+ private static final String LABEL_NAME = "Verified";
+ private static final LabelId LABEL_ID = new LabelId(LABEL_NAME);
+ private static final Change.Id CHANGE_ID = new Change.Id(100);
+ private static final PatchSet.Id PS_ID = new PatchSet.Id(CHANGE_ID, 1);
+ private static final LabelType VERIFIED_LABEL = makeLabel();
+ private static final PatchSetApproval APPROVAL_2 = makeApproval(2);
+ private static final PatchSetApproval APPROVAL_1 = makeApproval(1);
+ private static final PatchSetApproval APPROVAL_0 = makeApproval(0);
+ private static final PatchSetApproval APPROVAL_M1 = makeApproval(-1);
+ private static final PatchSetApproval APPROVAL_M2 = makeApproval(-2);
+
+ @Test
+ public void checkLabelNameIsCorrect() {
+ for (LabelFunction function : LabelFunction.values()) {
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, ImmutableList.of());
+ assertThat(myLabel.label).isEqualTo("Verified");
+ }
+ }
+
+ @Test
+ public void checkFunctionDoesNothing() {
+ checkNothingHappens(LabelFunction.NO_BLOCK);
+ checkNothingHappens(LabelFunction.NO_OP);
+ checkNothingHappens(LabelFunction.PATCH_SET_LOCK);
+ checkNothingHappens(LabelFunction.ANY_WITH_BLOCK);
+
+ checkLabelIsRequired(LabelFunction.MAX_WITH_BLOCK);
+ checkLabelIsRequired(LabelFunction.MAX_NO_BLOCK);
+ }
+
+ @Test
+ public void checkBlockWorks() {
+ checkBlockWorks(LabelFunction.ANY_WITH_BLOCK);
+ checkBlockWorks(LabelFunction.MAX_WITH_BLOCK);
+ }
+
+ @Test
+ public void checkMaxWorks() {
+ checkMaxIsEnforced(LabelFunction.MAX_NO_BLOCK);
+ checkMaxIsEnforced(LabelFunction.MAX_WITH_BLOCK);
+
+ checkMaxValidatesTheLabel(LabelFunction.MAX_NO_BLOCK);
+ checkMaxValidatesTheLabel(LabelFunction.MAX_WITH_BLOCK);
+ }
+
+ @Test
+ public void checkMaxNoBlockIgnoresMin() {
+ List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_M2, APPROVAL_2, APPROVAL_M2);
+
+ SubmitRecord.Label myLabel = LabelFunction.MAX_NO_BLOCK.check(VERIFIED_LABEL, approvals);
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.OK);
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.getAccountId());
+ }
+
+ private static LabelType makeLabel() {
+ List<LabelValue> values = new ArrayList<>();
+ // The label text is irrelevant here, only the numerical value is used
+ values.add(new LabelValue((short) -2, "Great job, please fix compilation."));
+ values.add(new LabelValue((short) -1, "Really good, please make some minor changes."));
+ values.add(new LabelValue((short) 0, "No vote."));
+ values.add(new LabelValue((short) 1, "Closest thing perfection."));
+ values.add(new LabelValue((short) 2, "Perfect!"));
+ return new LabelType(LABEL_NAME, values);
+ }
+
+ private static PatchSetApproval makeApproval(int value) {
+ Account.Id accountId = new Account.Id(10000 + value);
+ PatchSetApproval.Key key = makeKey(PS_ID, accountId, LABEL_ID);
+ return new PatchSetApproval(key, (short) value, Date.from(Instant.now()));
+ }
+
+ private static PatchSetApproval.Key makeKey(Id psId, Account.Id accountId, LabelId labelId) {
+ return new PatchSetApproval.Key(psId, accountId, labelId);
+ }
+
+ private static void checkBlockWorks(LabelFunction function) {
+ List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_M2, APPROVAL_2);
+
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.REJECT);
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_M2.getAccountId());
+ }
+
+ private static void checkNothingHappens(LabelFunction function) {
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, ImmutableList.of());
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.MAY);
+ assertThat(myLabel.appliedBy).isNull();
+ }
+
+ private static void checkLabelIsRequired(LabelFunction function) {
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, ImmutableList.of());
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.NEED);
+ assertThat(myLabel.appliedBy).isNull();
+ }
+
+ private static void checkMaxIsEnforced(LabelFunction function) {
+ List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_0);
+
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.NEED);
+ }
+
+ private static void checkMaxValidatesTheLabel(LabelFunction function) {
+ List<PatchSetApproval> approvals = ImmutableList.of(APPROVAL_1, APPROVAL_2, APPROVAL_M1);
+
+ SubmitRecord.Label myLabel = function.check(VERIFIED_LABEL, approvals);
+
+ assertThat(myLabel.status).isEqualTo(SubmitRecord.Label.Status.OK);
+ assertThat(myLabel.appliedBy).isEqualTo(APPROVAL_2.getAccountId());
+ }
+}
diff --git a/javatests/com/google/gerrit/server/BUILD b/javatests/com/google/gerrit/server/BUILD
index 75f3b3e..24d2822 100644
--- a/javatests/com/google/gerrit/server/BUILD
+++ b/javatests/com/google/gerrit/server/BUILD
@@ -42,16 +42,19 @@
"//java/com/google/gerrit/metrics",
"//java/com/google/gerrit/reviewdb:server",
"//java/com/google/gerrit/server",
+ "//java/com/google/gerrit/server/group/testing",
"//java/com/google/gerrit/server/project/testing:project-test-util",
"//java/com/google/gerrit/server/restapi",
"//java/com/google/gerrit/server/schema",
"//java/com/google/gerrit/testing:gerrit-test-util",
+ "//java/com/google/gerrit/truth",
"//java/org/eclipse/jgit:server",
"//lib:grappa",
"//lib:gson",
"//lib:guava-retrying",
"//lib:gwtorm",
"//lib:truth-java8-extension",
+ "//lib/auto:auto-value",
"//lib/commons:codec",
"//lib/guice",
"//lib/jgit/org.eclipse.jgit:jgit",
diff --git a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
index ac9775a..a3fbb5c 100644
--- a/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
+++ b/javatests/com/google/gerrit/server/group/db/AbstractGroupTest.java
@@ -27,11 +27,14 @@
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.group.InternalGroup;
import com.google.gerrit.testing.GerritBaseTests;
import com.google.gerrit.testing.InMemoryRepositoryManager;
+import java.io.IOException;
import java.sql.Timestamp;
import java.util.Optional;
import java.util.TimeZone;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -116,9 +119,9 @@
getAccountName(id), getAccountEmail(id), ident.getWhen(), ident.getTimeZone());
}
- protected static AuditLogFormatter getAuditLogFormatter() {
+ protected AuditLogFormatter getAuditLogFormatter() {
return AuditLogFormatter.create(
- AbstractGroupTest::getAccount, AbstractGroupTest::getGroup, SERVER_ID);
+ AbstractGroupTest::getAccount, uuid -> getGroup(uuid), SERVER_ID);
}
private static Optional<Account> getAccount(Account.Id id) {
@@ -127,7 +130,7 @@
return Optional.of(account);
}
- private static Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
+ private Optional<GroupDescription.Basic> getGroup(AccountGroup.UUID uuid) {
GroupDescription.Basic group =
new GroupDescription.Basic() {
@Override
@@ -137,7 +140,14 @@
@Override
public String getName() {
- return "Group " + uuid;
+ try {
+ return GroupConfig.loadForGroup(allUsersRepo, uuid)
+ .getLoadedGroup()
+ .map(InternalGroup::getName)
+ .orElse("Group " + uuid);
+ } catch (IOException | ConfigInvalidException e) {
+ return "Group " + uuid;
+ }
}
@Nullable
diff --git a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
index 4effa94..dbbfe3a 100644
--- a/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
+++ b/javatests/com/google/gerrit/server/group/db/GroupConfigTest.java
@@ -1350,7 +1350,7 @@
RevCommit revCommit = getLatestCommitForGroup(groupUuid);
assertThat(revCommit.getFullMessage())
- .isEqualTo("Create group\n\nAdd: John <13@server-id>\nAdd: Jane <7@server-id>");
+ .isEqualTo("Create group\n\nAdd: Jane <7@server-id>\nAdd: John <13@server-id>");
}
@Test
@@ -1397,7 +1397,7 @@
RevCommit revCommit = getLatestCommitForGroup(groupUuid);
assertThat(revCommit.getFullMessage())
- .isEqualTo("Update group\n\nAdd: John <13@GerritServer1>\nAdd: Jane <7@GerritServer1>");
+ .isEqualTo("Update group\n\nAdd: Jane <7@GerritServer1>\nAdd: John <13@GerritServer1>");
}
@Test
@@ -1531,11 +1531,11 @@
.isEqualTo(
"Update group\n"
+ "\n"
- + "Rename from Old name to New name\n"
- + "Remove: Jane <7@serverId>\n"
+ + "Add-group: Bots <129403>\n"
+ "Add: John <13@serverId>\n"
+ "Remove-group: Verifiers <8903493>\n"
- + "Add-group: Bots <129403>");
+ + "Remove: Jane <7@serverId>\n"
+ + "Rename from Old name to New name");
}
private static Timestamp toTimestamp(LocalDateTime localDateTime) {
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index ddda473..c3b5af9 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -51,6 +51,7 @@
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
+import com.google.gerrit.extensions.client.ProjectWatchInfo;
import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
@@ -228,8 +229,7 @@
"Add Email",
userId,
u -> u.addExternalId(ExternalId.createEmail(userId, email)).setPreferredEmail(email));
- user = userFactory.create(userId);
- requestContext.setContext(newRequestContext(userId));
+ resetUser();
}
protected RequestContext newRequestContext(Account.Id requestUserId) {
@@ -247,6 +247,11 @@
};
}
+ protected void resetUser() {
+ user = userFactory.create(userId);
+ requestContext.setContext(newRequestContext(userId));
+ }
+
@After
public void tearDownInjector() {
if (lifecycle != null) {
@@ -385,6 +390,20 @@
}
@Test
+ public void byStatusAbandoned() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ ChangeInserter ins1 = newChangeWithStatus(repo, Change.Status.MERGED);
+ insert(repo, ins1);
+ ChangeInserter ins2 = newChangeWithStatus(repo, Change.Status.ABANDONED);
+ Change change1 = insert(repo, ins2);
+ insert(repo, newChangeWithStatus(repo, Change.Status.NEW));
+
+ assertQuery("status:abandoned", change1);
+ assertQuery("status:ABANDONED", change1);
+ assertQuery("is:abandoned", change1);
+ }
+
+ @Test
public void byStatusPrefix() throws Exception {
TestRepository<Repo> repo = createProject("repo");
ChangeInserter ins1 = newChangeWithStatus(repo, Change.Status.NEW);
@@ -1502,6 +1521,8 @@
Change change1 = insert(repo, newChange(repo));
Change change2 = insert(repo, newChange(repo));
+ assertQuery("has:draft");
+
DraftInput in = new DraftInput();
in.line = 1;
in.message = "nit: trailing whitespace";
@@ -1517,6 +1538,7 @@
int user2 =
accountManager.authenticate(AuthRequest.forUser("anotheruser")).getAccountId().get();
+ assertQuery("has:draft", change2, change1);
assertQuery("draftby:" + userId.get(), change2, change1);
assertQuery("draftby:" + user2);
}
@@ -2156,6 +2178,35 @@
}
@Test
+ public void watched() throws Exception {
+ TestRepository<Repo> repo = createProject("repo");
+ ChangeInserter ins1 = newChangeWithStatus(repo, Change.Status.NEW);
+ Change change1 = insert(repo, ins1);
+
+ TestRepository<Repo> repo2 = createProject("repo2");
+
+ ChangeInserter ins2 = newChangeWithStatus(repo2, Change.Status.NEW);
+ insert(repo2, ins2);
+
+ assertQuery("is:watched");
+ assertQuery("watchedby:self");
+
+ List<ProjectWatchInfo> projectsToWatch = new ArrayList<>();
+ ProjectWatchInfo pwi = new ProjectWatchInfo();
+ pwi.project = "repo";
+ pwi.filter = null;
+ pwi.notifyAbandonedChanges = true;
+ pwi.notifyNewChanges = true;
+ pwi.notifyAllComments = true;
+ projectsToWatch.add(pwi);
+ gApi.accounts().self().setWatchedProjects(projectsToWatch);
+ resetUser();
+
+ assertQuery("is:watched", change1);
+ assertQuery("watchedby:self", change1);
+ }
+
+ @Test
public void selfAndMe() throws Exception {
TestRepository<Repo> repo = createProject("repo");
Change change1 = insert(repo, newChange(repo));
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
index 2bff3f9..f1b65d4 100644
--- a/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/AbstractQueryGroupsTest.java
@@ -359,7 +359,7 @@
AccountGroup.UUID groupUuid = new AccountGroup.UUID(group1.id);
InternalGroupUpdate groupUpdate =
InternalGroupUpdate.builder().setDescription(newDescription).build();
- groupsUpdateProvider.get().updateGroupInDb(db, groupUuid, groupUpdate);
+ groupsUpdateProvider.get().updateGroupInDb(groupUuid, groupUpdate);
assertQuery("description:" + group1.description, group1);
assertQuery("description:" + newDescription);
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderIT.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderIT.java
deleted file mode 100644
index 709be8e..0000000
--- a/javatests/com/google/gerrit/server/schema/GroupRebuilderIT.java
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright (C) 2017 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.server.schema;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.TruthJUnit.assume;
-import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
-
-import com.google.common.collect.ImmutableList;
-import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.extensions.api.GerritApi;
-import com.google.gerrit.extensions.api.accounts.AccountInput;
-import com.google.gerrit.extensions.common.AccountInfo;
-import com.google.gerrit.extensions.common.CommitInfo;
-import com.google.gerrit.extensions.common.GroupInfo;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.reviewdb.client.AccountGroup;
-import com.google.gerrit.reviewdb.client.AccountGroupById;
-import com.google.gerrit.reviewdb.client.AccountGroupByIdAud;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.ServerInitiated;
-import com.google.gerrit.server.account.AccountCache;
-import com.google.gerrit.server.account.AccountsUpdate;
-import com.google.gerrit.server.account.GroupBackend;
-import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.GerritServerId;
-import com.google.gerrit.server.config.GerritServerIdProvider;
-import com.google.gerrit.server.git.CommitUtil;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.group.db.AuditLogFormatter;
-import com.google.gerrit.server.notedb.GroupsMigration;
-import com.google.gerrit.testing.GerritBaseTests;
-import com.google.gerrit.testing.InMemoryTestEnvironment;
-import com.google.gerrit.testing.TestTimeUtil;
-import com.google.gerrit.testing.TestTimeUtil.TempClockStep;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.eclipse.jgit.revwalk.RevWalk;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-public class GroupRebuilderIT extends GerritBaseTests {
-
- private static Config createConfigWithServerId() {
- Config config = new Config();
- config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
- return config;
- }
-
- @Rule
- public InMemoryTestEnvironment testEnv =
- new InMemoryTestEnvironment(GroupRebuilderIT::createConfigWithServerId);
-
- @Inject private GroupsMigration migration;
- @Inject private GerritApi gApi;
- @Inject private ReviewDb db;
- @Inject private GitRepositoryManager repoManager;
- @Inject private AllUsersName allUsersName;
- @Inject private IdentifiedUser currentUser;
- @Inject private @GerritServerId String serverId;
- @Inject private AccountCache accountCache;
- @Inject private @ServerInitiated AccountsUpdate accountsUpdate;
- @Inject private GroupBackend groupBackend;
- @Inject private GroupBundle.Factory bundleFactory;
- @Inject private @GerritPersonIdent Provider<PersonIdent> serverIdent;
-
- private GroupRebuilder rebuilder;
-
- @Before
- public void setup() throws Exception {
- // This test is explicitly testing the migration from ReviewDb to NoteDb, and handles reading
- // from NoteDb manually. It should work regardless of the value of noteDb.groups.write, however.
- assume().that(migration.readFromNoteDb()).isFalse();
-
- accountsUpdate.update(
- "Set Name for CurrentUser", currentUser.getAccountId(), u -> u.setFullName("current"));
-
- AuditLogFormatter auditLogFormatter =
- AuditLogFormatter.createBackedBy(accountCache, groupBackend, serverId);
- rebuilder = new GroupRebuilder(serverIdent.get(), allUsersName, auditLogFormatter);
- }
-
- @Before
- public void setTimeForTesting() {
- TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
- }
-
- @After
- public void resetTime() {
- TestTimeUtil.useSystemTime();
- }
-
- @Test
- public void basicGroupProperties() throws Exception {
- GroupInfo createdGroup = gApi.groups().create("group").get();
- GroupBundle reviewDbBundle =
- GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(createdGroup.id));
-
- deleteGroupRefs(reviewDbBundle);
- assertMigratedCleanly(rebuild(reviewDbBundle), reviewDbBundle);
- }
-
- @Test
- public void logFormat() throws Exception {
- AccountInfo user1 = createAccount("user1");
- AccountInfo user2 = createAccount("user2");
- GroupInfo group1 = gApi.groups().create("group1").get();
- GroupInfo group2 = gApi.groups().create("group2").get();
-
- try (TempClockStep step = TestTimeUtil.freezeClock()) {
- gApi.groups()
- .id(group1.id)
- .addMembers(Integer.toString(user1._accountId), Integer.toString(user2._accountId));
- }
- TimeUtil.nowTs();
-
- try (TempClockStep step = TestTimeUtil.freezeClock()) {
- gApi.groups().id(group1.id).addGroups(group2.id, SystemGroupBackend.REGISTERED_USERS.get());
- }
-
- GroupBundle reviewDbBundle =
- GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(group1.id));
- deleteGroupRefs(reviewDbBundle);
-
- GroupBundle noteDbBundle = rebuild(reviewDbBundle);
- assertMigratedCleanly(noteDbBundle, reviewDbBundle);
-
- ImmutableList<CommitInfo> log = log(group1);
- assertThat(log).hasSize(4);
-
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(0)).author().name().isEqualTo(serverIdent.get().getName());
- assertThat(log.get(0)).author().email().isEqualTo(serverIdent.get().getEmailAddress());
- assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
- assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.get().getTimeZoneOffset());
- assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
-
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
-
- assertThat(log.get(2))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + ("Add: user1 <" + user1._accountId + "@" + serverId + ">\n")
- + ("Add: user2 <" + user2._accountId + "@" + serverId + ">"));
- assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
-
- assertThat(log.get(3))
- .message()
- .isEqualTo(
- "Update group\n"
- + "\n"
- + ("Add-group: " + group2.name + " <" + group2.id + ">\n")
- + ("Add-group: Registered Users <global:Registered-Users>"));
- assertThat(log.get(3)).author().name().isEqualTo(currentUser.getName());
- assertThat(log.get(3)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
- assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
- }
-
- @Test
- public void unknownGroupUuid() throws Exception {
- GroupInfo group = gApi.groups().create("group").get();
-
- AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("mybackend:foo");
-
- AccountGroupById byId =
- new AccountGroupById(
- new AccountGroupById.Key(new AccountGroup.Id(group.groupId), subgroupUuid));
- assertThat(groupBackend.handles(byId.getIncludeUUID())).isFalse();
- db.accountGroupById().insert(Collections.singleton(byId));
-
- AccountGroupByIdAud audit =
- new AccountGroupByIdAud(byId, currentUser.getAccountId(), TimeUtil.nowTs());
- db.accountGroupByIdAud().insert(Collections.singleton(audit));
-
- GroupBundle reviewDbBundle =
- GroupBundle.Factory.fromReviewDb(db, new AccountGroup.UUID(group.id));
- deleteGroupRefs(reviewDbBundle);
-
- GroupBundle noteDbBundle = rebuild(reviewDbBundle);
- assertMigratedCleanly(noteDbBundle, reviewDbBundle);
-
- ImmutableList<CommitInfo> log = log(group);
- assertThat(log).hasSize(3);
-
- assertThat(log.get(0)).message().isEqualTo("Create group");
- assertThat(log.get(1))
- .message()
- .isEqualTo(
- "Update group\n\nAdd: "
- + currentUser.getName()
- + " <"
- + currentUser.getAccountId()
- + "@"
- + serverId
- + ">");
- assertThat(log.get(2))
- .message()
- .isEqualTo("Update group\n\nAdd-group: mybackend:foo <mybackend:foo>");
- }
-
- private void deleteGroupRefs(GroupBundle bundle) throws Exception {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- String refName = RefNames.refsGroups(bundle.uuid());
- RefUpdate ru = repo.updateRef(refName);
- ru.setForceUpdate(true);
- Ref oldRef = repo.exactRef(refName);
- if (oldRef == null) {
- return;
- }
- ru.setExpectedOldObjectId(oldRef.getObjectId());
- ru.setNewObjectId(ObjectId.zeroId());
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
- }
- }
-
- private GroupBundle rebuild(GroupBundle reviewDbBundle) throws Exception {
- try (Repository repo = repoManager.openRepository(allUsersName)) {
- rebuilder.rebuild(repo, reviewDbBundle, null);
- return bundleFactory.fromNoteDb(repo, reviewDbBundle.uuid());
- }
- }
-
- private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
- assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
- }
-
- private AccountInfo createAccount(String name) throws RestApiException {
- AccountInput accountInput = new AccountInput();
- accountInput.username = name;
- accountInput.name = name;
- return gApi.accounts().create(accountInput).get();
- }
-
- private ImmutableList<CommitInfo> log(GroupInfo g) throws Exception {
- ImmutableList.Builder<CommitInfo> result = ImmutableList.builder();
- List<Date> commitDates = new ArrayList<>();
- try (Repository repo = repoManager.openRepository(allUsersName);
- RevWalk rw = new RevWalk(repo)) {
- Ref ref = repo.exactRef(RefNames.refsGroups(new AccountGroup.UUID(g.id)));
- if (ref != null) {
- rw.sort(RevSort.REVERSE);
- rw.setRetainBody(true);
- rw.markStart(rw.parseCommit(ref.getObjectId()));
- for (RevCommit c : rw) {
- result.add(CommitUtil.toCommitInfo(c));
- commitDates.add(c.getCommitterIdent().getWhen());
- }
- }
- }
- assertThat(commitDates).named("commit timestamps for %s", result).isOrdered();
- return result.build();
- }
-}
diff --git a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
index 6c670c0..a6178ac 100644
--- a/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
+++ b/javatests/com/google/gerrit/server/schema/GroupRebuilderTest.java
@@ -181,10 +181,10 @@
log.get(1),
"Update group\n"
+ "\n"
- + "Add: Account 1 <1@server-id>\n"
- + "Add: Account 2 <2@server-id>\n"
+ "Add-group: Group x <x>\n"
- + "Add-group: Group y <y>");
+ + "Add-group: Group y <y>\n"
+ + "Add: Account 1 <1@server-id>\n"
+ + "Add: Account 2 <2@server-id>");
}
@Test
diff --git a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
index 047b933..ed94c97 100644
--- a/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
+++ b/javatests/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -33,7 +33,6 @@
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.group.SystemGroupBackend;
-import com.google.gerrit.server.notedb.GroupsMigration;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.testing.InMemoryDatabase;
import com.google.gerrit.testing.InMemoryH2Type;
@@ -119,7 +118,6 @@
bind(SystemGroupBackend.class);
install(new NotesMigration.Module());
- install(new GroupsMigration.Module());
bind(MetricMaker.class).to(DisabledMetricMaker.class);
}
})
diff --git a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
index b817e71..42af2ca 100644
--- a/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
+++ b/javatests/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
@@ -18,25 +18,30 @@
import static com.google.common.truth.TruthJUnit.assume;
import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.extensions.api.groups.GroupInput;
-import com.google.gerrit.extensions.common.GroupInfo;
-import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.Id;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.restapi.group.CreateGroup;
+import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.account.GroupUUID;
import com.google.gerrit.testing.InMemoryTestEnvironment;
import com.google.gerrit.testing.TestUpdateUI;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.Statement;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
+import org.eclipse.jgit.lib.PersonIdent;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -46,20 +51,58 @@
@Rule public InMemoryTestEnvironment testEnv = new InMemoryTestEnvironment();
- @Inject private CreateGroup.Factory createGroupFactory;
@Inject private Schema_151 schema151;
@Inject private ReviewDb db;
+ @Inject private IdentifiedUser currentUser;
+ @Inject private @GerritPersonIdent Provider<PersonIdent> serverIdent;
+ @Inject private Sequences seq;
private Connection connection;
private PreparedStatement createdOnRetrieval;
private PreparedStatement createdOnUpdate;
private PreparedStatement auditEntryDeletion;
+ private JdbcSchema jdbcSchema;
+
+ @Before
+ public void unwrapDb() {
+ jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
+ }
@Before
public void setUp() throws Exception {
assume().that(db instanceof JdbcSchema).isTrue();
connection = ((JdbcSchema) db).getConnection();
+
+ try (Statement stmt = connection.createStatement()) {
+ stmt.execute(
+ "CREATE TABLE account_groups ("
+ + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " name varchar(255) DEFAULT '' NOT NULL,"
+ + " created_on TIMESTAMP,"
+ + " description CLOB,"
+ + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_members ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " account_id INTEGER DEFAULT 0 NOT NULL"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_members_audit ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " account_id INTEGER DEFAULT 0 NOT NULL,"
+ + " added_by INTEGER DEFAULT 0 NOT NULL,"
+ + " added_on TIMESTAMP,"
+ + " removed_by INTEGER,"
+ + " removed_on TIMESTAMP"
+ + ")");
+ }
+
createdOnRetrieval =
connection.prepareStatement("SELECT created_on FROM account_groups WHERE group_id = ?");
createdOnUpdate =
@@ -87,7 +130,7 @@
@Test
public void createdOnIsPopulatedForGroupsCreatedAfterAudit() throws Exception {
Timestamp testStartTime = TimeUtil.nowTs();
- AccountGroup.Id groupId = createGroup("Group for schema migration");
+ AccountGroup.Id groupId = createGroupInReviewDb("Group for schema migration");
setCreatedOnToVeryOldTimestamp(groupId);
schema151.migrateData(db, new TestUpdateUI());
@@ -98,7 +141,7 @@
@Test
public void createdOnIsPopulatedForGroupsCreatedBeforeAudit() throws Exception {
- AccountGroup.Id groupId = createGroup("Ancient group for schema migration");
+ AccountGroup.Id groupId = createGroupInReviewDb("Ancient group for schema migration");
setCreatedOnToVeryOldTimestamp(groupId);
removeAuditEntriesFor(groupId);
@@ -108,12 +151,16 @@
assertThat(createdOn).isEqualTo(AccountGroup.auditCreationInstantTs());
}
- private AccountGroup.Id createGroup(String name) throws Exception {
- GroupInput groupInput = new GroupInput();
- groupInput.name = name;
- GroupInfo groupInfo =
- createGroupFactory.create(name).apply(TopLevelResource.INSTANCE, groupInput);
- return new Id(groupInfo.groupId);
+ private AccountGroup.Id createGroupInReviewDb(String name) throws Exception {
+ AccountGroup group =
+ new AccountGroup(
+ new AccountGroup.NameKey(name),
+ new AccountGroup.Id(seq.nextGroupId()),
+ GroupUUID.make(name, serverIdent.get()),
+ TimeUtil.nowTs());
+ storeInReviewDb(group);
+ addMembersInReviewDb(group.getId(), currentUser.getAccountId());
+ return group.getId();
}
private Timestamp getCreatedOn(Id groupId) throws Exception {
@@ -138,4 +185,69 @@
auditEntryDeletion.setInt(1, groupId.get());
auditEntryDeletion.executeUpdate();
}
+
+ private void storeInReviewDb(AccountGroup... groups) throws Exception {
+ try (PreparedStatement stmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_groups"
+ + " (group_uuid,"
+ + " group_id,"
+ + " name,"
+ + " description,"
+ + " created_on,"
+ + " owner_group_uuid,"
+ + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
+ for (AccountGroup group : groups) {
+ stmt.setString(1, group.getGroupUUID().get());
+ stmt.setInt(2, group.getId().get());
+ stmt.setString(3, group.getName());
+ stmt.setString(4, group.getDescription());
+ stmt.setTimestamp(5, group.getCreatedOn());
+ stmt.setString(6, group.getOwnerGroupUUID().get());
+ stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ }
+ }
+
+ private void addMembersInReviewDb(AccountGroup.Id groupId, Account.Id... memberIds)
+ throws Exception {
+ try (PreparedStatement addMemberStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_members"
+ + " (group_id,"
+ + " account_id) VALUES ("
+ + groupId.get()
+ + ", ?)");
+ PreparedStatement addMemberAuditStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_members_audit"
+ + " (group_id,"
+ + " account_id,"
+ + " added_by,"
+ + " added_on) VALUES ("
+ + groupId.get()
+ + ", ?, "
+ + currentUser.getAccountId().get()
+ + ", ?)")) {
+ Timestamp addedOn = TimeUtil.nowTs();
+ for (Account.Id memberId : memberIds) {
+ addMemberStmt.setInt(1, memberId.get());
+ addMemberStmt.addBatch();
+
+ addMemberAuditStmt.setInt(1, memberId.get());
+ addMemberAuditStmt.setTimestamp(2, addedOn);
+ addMemberAuditStmt.addBatch();
+ }
+ addMemberStmt.executeBatch();
+ addMemberAuditStmt.executeBatch();
+ }
+ }
}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java
new file mode 100644
index 0000000..57689b3
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInNoteDbTest.java
@@ -0,0 +1,227 @@
+// Copyright (C) 2018 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.server.schema;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
+import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
+import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.ServerInitiated;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.db.GroupNameNotes;
+import com.google.gerrit.server.group.db.GroupsUpdate;
+import com.google.gerrit.server.group.db.InternalGroupCreation;
+import com.google.gerrit.server.group.db.InternalGroupUpdate;
+import com.google.gerrit.testing.InMemoryTestEnvironment;
+import com.google.gerrit.testing.TestUpdateUI;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class Schema_166_to_167_WithGroupsInNoteDbTest {
+ private static Config createConfig() {
+ Config config = new Config();
+ config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
+
+ // Disable groups in ReviewDb. This means the primary storage for groups is NoteDb.
+ config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, true);
+
+ return config;
+ }
+
+ @Rule
+ public InMemoryTestEnvironment testEnv =
+ new InMemoryTestEnvironment(Schema_166_to_167_WithGroupsInNoteDbTest::createConfig);
+
+ @Inject private Schema_167 schema167;
+ @Inject private ReviewDb db;
+ @Inject private GitRepositoryManager gitRepoManager;
+ @Inject private AllUsersName allUsersName;
+ @Inject private @ServerInitiated GroupsUpdate groupsUpdate;
+ @Inject private Sequences seq;
+
+ private JdbcSchema jdbcSchema;
+
+ @Before
+ public void initDb() throws Exception {
+ jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
+
+ try (Statement stmt = jdbcSchema.getConnection().createStatement()) {
+ stmt.execute(
+ "CREATE TABLE account_groups ("
+ + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " name varchar(255) DEFAULT '' NOT NULL,"
+ + " created_on TIMESTAMP,"
+ + " description CLOB,"
+ + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
+ + ")");
+ }
+ }
+
+ @Test
+ public void migrationIsSkipped() throws Exception {
+ // Create a group in NoteDb (doesn't create the group in ReviewDb since
+ // disableReviewDb == true)
+ InternalGroup internalGroup =
+ groupsUpdate.createGroup(
+ InternalGroupCreation.builder()
+ .setNameKey(new AccountGroup.NameKey("users"))
+ .setGroupUUID(new AccountGroup.UUID("users"))
+ .setId(new AccountGroup.Id(seq.nextGroupId()))
+ .build(),
+ InternalGroupUpdate.builder().setDescription("description").build());
+
+ // Insert the group into ReviewDb
+ AccountGroup group1 =
+ newGroup()
+ .setName(internalGroup.getName())
+ .setGroupUuid(internalGroup.getGroupUUID())
+ .setId(internalGroup.getId())
+ .setCreatedOn(internalGroup.getCreatedOn())
+ .setDescription(internalGroup.getDescription())
+ .setGroupUuid(internalGroup.getGroupUUID())
+ .setVisibleToAll(internalGroup.isVisibleToAll())
+ .build();
+ storeInReviewDb(group1);
+
+ // Update the group description in ReviewDb so that the group state differs between ReviewDb and
+ // NoteDb
+ group1.setDescription("outdated");
+ updateInReviewDb(group1);
+
+ // Create a group that only exists in ReviewDb
+ AccountGroup group2 = newGroup().setName("reviewDbOnlyGroup").build();
+ storeInReviewDb(group2);
+
+ // Remember the SHA1 of the group ref in NoteDb
+ ObjectId groupSha1 = getGroupSha1(group1.getGroupUUID());
+
+ executeSchemaMigration(schema167);
+
+ // Verify the groups in NoteDb: "users" should still exist, "reviewDbOnlyGroup" should not have
+ // been created
+ ImmutableList<GroupReference> groupReferences = getAllGroupsFromNoteDb();
+ ImmutableList<String> groupNames =
+ groupReferences.stream().map(GroupReference::getName).collect(toImmutableList());
+ assertThat(groupNames).contains("users");
+ assertThat(groupNames).doesNotContain("reviewDbOnlyGroup");
+
+ // Verify that the group refs in NoteDb were not touched.
+ assertThat(getGroupSha1(group1.getGroupUUID())).isEqualTo(groupSha1);
+ assertThat(getGroupSha1(group2.getGroupUUID())).isNull();
+ }
+
+ private static TestGroup.Builder newGroup() {
+ return TestGroup.builder();
+ }
+
+ private void storeInReviewDb(AccountGroup... groups) throws Exception {
+ try (PreparedStatement stmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_groups"
+ + " (group_uuid,"
+ + " group_id,"
+ + " name,"
+ + " description,"
+ + " created_on,"
+ + " owner_group_uuid,"
+ + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
+ for (AccountGroup group : groups) {
+ stmt.setString(1, group.getGroupUUID().get());
+ stmt.setInt(2, group.getId().get());
+ stmt.setString(3, group.getName());
+ stmt.setString(4, group.getDescription());
+ stmt.setTimestamp(5, group.getCreatedOn());
+ stmt.setString(6, group.getOwnerGroupUUID().get());
+ stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ }
+ }
+
+ private void updateInReviewDb(AccountGroup... groups) throws Exception {
+ try (PreparedStatement stmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "UPDATE account_groups SET"
+ + " group_uuid = ?,"
+ + " name = ?,"
+ + " description = ?,"
+ + " created_on = ?,"
+ + " owner_group_uuid = ?,"
+ + " visible_to_all = ?"
+ + " WHERE group_id = ?")) {
+ for (AccountGroup group : groups) {
+ stmt.setString(1, group.getGroupUUID().get());
+ stmt.setString(2, group.getName());
+ stmt.setString(3, group.getDescription());
+ stmt.setTimestamp(4, group.getCreatedOn());
+ stmt.setString(5, group.getOwnerGroupUUID().get());
+ stmt.setString(6, group.isVisibleToAll() ? "Y" : "N");
+ stmt.setInt(7, group.getId().get());
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ }
+ }
+
+ private void executeSchemaMigration(SchemaVersion schema) throws Exception {
+ schema.migrateData(db, new TestUpdateUI());
+ }
+
+ private ImmutableList<GroupReference> getAllGroupsFromNoteDb()
+ throws IOException, ConfigInvalidException {
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
+ return GroupNameNotes.loadAllGroups(allUsersRepo);
+ }
+ }
+
+ @Nullable
+ private ObjectId getGroupSha1(AccountGroup.UUID groupUuid) throws IOException {
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
+ Ref ref = allUsersRepo.exactRef(RefNames.refsGroups(groupUuid));
+ return ref != null ? ref.getObjectId() : null;
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
new file mode 100644
index 0000000..9cd57e0
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/Schema_166_to_167_WithGroupsInReviewDbTest.java
@@ -0,0 +1,1204 @@
+// Copyright (C) 2018 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.server.schema;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.extensions.common.testing.CommitInfoSubject.assertThat;
+import static com.google.gerrit.server.notedb.NoteDbTable.GROUPS;
+import static com.google.gerrit.server.notedb.NotesMigration.DISABLE_REVIEW_DB;
+import static com.google.gerrit.server.notedb.NotesMigration.SECTION_NOTE_DB;
+import static com.google.gerrit.truth.OptionalSubject.assertThat;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.common.data.GroupDescription;
+import com.google.gerrit.common.data.GroupDescription.Basic;
+import com.google.gerrit.common.data.GroupReference;
+import com.google.gerrit.extensions.api.GerritApi;
+import com.google.gerrit.extensions.api.accounts.AccountInput;
+import com.google.gerrit.extensions.api.config.ConsistencyCheckInfo;
+import com.google.gerrit.extensions.api.groups.GroupInput;
+import com.google.gerrit.extensions.common.AccountInfo;
+import com.google.gerrit.extensions.common.CommitInfo;
+import com.google.gerrit.extensions.common.GroupAuditEventInfo;
+import com.google.gerrit.extensions.common.GroupAuditEventInfo.GroupMemberAuditEventInfo;
+import com.google.gerrit.extensions.common.GroupAuditEventInfo.Type;
+import com.google.gerrit.extensions.common.GroupAuditEventInfo.UserMemberAuditEventInfo;
+import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.extensions.common.GroupOptionsInfo;
+import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import com.google.gerrit.reviewdb.client.RefNames;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbWrapper;
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.Sequences;
+import com.google.gerrit.server.account.GroupBackend;
+import com.google.gerrit.server.account.GroupMembership;
+import com.google.gerrit.server.account.GroupUUID;
+import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.GerritServerId;
+import com.google.gerrit.server.config.GerritServerIdProvider;
+import com.google.gerrit.server.git.CommitUtil;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.group.InternalGroup;
+import com.google.gerrit.server.group.SystemGroupBackend;
+import com.google.gerrit.server.group.db.GroupConfig;
+import com.google.gerrit.server.group.db.GroupNameNotes;
+import com.google.gerrit.server.group.db.GroupsConsistencyChecker;
+import com.google.gerrit.server.group.testing.InternalGroupSubject;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.testing.InMemoryTestEnvironment;
+import com.google.gerrit.testing.TestTimeUtil;
+import com.google.gerrit.testing.TestTimeUtil.TempClockStep;
+import com.google.gerrit.testing.TestUpdateUI;
+import com.google.gerrit.truth.OptionalSubject;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.time.LocalDate;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class Schema_166_to_167_WithGroupsInReviewDbTest {
+ private static Config createConfig() {
+ Config config = new Config();
+ config.setString(GerritServerIdProvider.SECTION, null, GerritServerIdProvider.KEY, "1234567");
+
+ // Enable groups in ReviewDb. This means the primary storage for groups is ReviewDb.
+ config.setBoolean(SECTION_NOTE_DB, GROUPS.key(), DISABLE_REVIEW_DB, false);
+
+ return config;
+ }
+
+ @Rule
+ public InMemoryTestEnvironment testEnv =
+ new InMemoryTestEnvironment(Schema_166_to_167_WithGroupsInReviewDbTest::createConfig);
+
+ @Inject private GerritApi gApi;
+ @Inject private Schema_167 schema167;
+ @Inject private ReviewDb db;
+ @Inject private GitRepositoryManager gitRepoManager;
+ @Inject private AllUsersName allUsersName;
+ @Inject private GroupsConsistencyChecker consistencyChecker;
+ @Inject private IdentifiedUser currentUser;
+ @Inject private @GerritServerId String serverId;
+ @Inject private @GerritPersonIdent PersonIdent serverIdent;
+ @Inject private GroupBundle.Factory groupBundleFactory;
+ @Inject private GroupBackend groupBackend;
+ @Inject private DynamicSet<GroupBackend> backends;
+ @Inject private Sequences seq;
+
+ private JdbcSchema jdbcSchema;
+
+ @Before
+ public void initDb() throws Exception {
+ jdbcSchema = ReviewDbWrapper.unwrapJbdcSchema(db);
+
+ try (Statement stmt = jdbcSchema.getConnection().createStatement()) {
+ stmt.execute(
+ "CREATE TABLE account_groups ("
+ + " group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " name varchar(255) DEFAULT '' NOT NULL,"
+ + " created_on TIMESTAMP,"
+ + " description CLOB,"
+ + " owner_group_uuid varchar(255) DEFAULT '' NOT NULL,"
+ + " visible_to_all CHAR(1) DEFAULT 'N' NOT NULL"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_members ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " account_id INTEGER DEFAULT 0 NOT NULL"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_members_audit ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " account_id INTEGER DEFAULT 0 NOT NULL,"
+ + " added_by INTEGER DEFAULT 0 NOT NULL,"
+ + " added_on TIMESTAMP,"
+ + " removed_by INTEGER,"
+ + " removed_on TIMESTAMP"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_by_id ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " include_uuid VARCHAR(255) DEFAULT '' NOT NULL"
+ + ")");
+
+ stmt.execute(
+ "CREATE TABLE account_group_by_id_aud ("
+ + " group_id INTEGER DEFAULT 0 NOT NULL,"
+ + " include_uuid VARCHAR(255) DEFAULT '' NOT NULL,"
+ + " added_by INTEGER DEFAULT 0 NOT NULL,"
+ + " added_on TIMESTAMP,"
+ + " removed_by INTEGER,"
+ + " removed_on TIMESTAMP"
+ + ")");
+ }
+ }
+
+ @Before
+ public void setTimeForTesting() {
+ TestTimeUtil.resetWithClockStep(1, TimeUnit.SECONDS);
+ }
+
+ @After
+ public void resetTime() {
+ TestTimeUtil.useSystemTime();
+ }
+
+ @Test
+ public void reviewDbOnlyGroupsAreMigratedToNoteDb() throws Exception {
+ // Create groups only in ReviewDb
+ AccountGroup group1 = newGroup().setName("verifiers").build();
+ AccountGroup group2 = newGroup().setName("contributors").build();
+ storeInReviewDb(group1, group2);
+
+ executeSchemaMigration(schema167, group1, group2);
+
+ ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
+ ImmutableList<String> groupNames =
+ groups.stream().map(GroupReference::getName).collect(toImmutableList());
+ assertThat(groupNames).containsAllOf("verifiers", "contributors");
+ }
+
+ @Test
+ public void alreadyExistingGroupsAreMigratedToNoteDb() throws Exception {
+ // Create group in NoteDb and ReviewDb
+ GroupInput groupInput = new GroupInput();
+ groupInput.name = "verifiers";
+ groupInput.description = "old";
+ GroupInfo group1 = gApi.groups().create(groupInput).get();
+ storeInReviewDb(group1);
+
+ // Update group only in ReviewDb
+ AccountGroup group1InReviewDb = getFromReviewDb(new AccountGroup.Id(group1.groupId));
+ group1InReviewDb.setDescription("new");
+ updateInReviewDb(group1InReviewDb);
+
+ // Create a second group in NoteDb and ReviewDb
+ GroupInfo group2 = gApi.groups().create("contributors").get();
+ storeInReviewDb(group2);
+
+ executeSchemaMigration(schema167, group1, group2);
+
+ // Verify that both groups are present in NoteDb
+ ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
+ ImmutableList<String> groupNames =
+ groups.stream().map(GroupReference::getName).collect(toImmutableList());
+ assertThat(groupNames).containsAllOf("verifiers", "contributors");
+
+ // Verify that group1 has the description from ReviewDb
+ Optional<InternalGroup> group1InNoteDb = getGroupFromNoteDb(new AccountGroup.UUID(group1.id));
+ assertThatGroup(group1InNoteDb).value().description().isEqualTo("new");
+ }
+
+ @Test
+ public void adminGroupIsMigratedToNoteDb() throws Exception {
+ // Administrators group is automatically created for all Gerrit servers (NoteDb only).
+ GroupInfo adminGroup = gApi.groups().id("Administrators").get();
+ storeInReviewDb(adminGroup);
+
+ executeSchemaMigration(schema167, adminGroup);
+
+ ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
+ ImmutableList<String> groupNames =
+ groups.stream().map(GroupReference::getName).collect(toImmutableList());
+ assertThat(groupNames).contains("Administrators");
+ }
+
+ @Test
+ public void nonInteractiveUsersGroupIsMigratedToNoteDb() throws Exception {
+ // 'Non-Interactive Users' group is automatically created for all Gerrit servers (NoteDb only).
+ GroupInfo nonInteractiveUsersGroup = gApi.groups().id("Non-Interactive Users").get();
+ storeInReviewDb(nonInteractiveUsersGroup);
+
+ executeSchemaMigration(schema167, nonInteractiveUsersGroup);
+
+ ImmutableList<GroupReference> groups = getAllGroupsFromNoteDb();
+ ImmutableList<String> groupNames =
+ groups.stream().map(GroupReference::getName).collect(toImmutableList());
+ assertThat(groupNames).contains("Non-Interactive Users");
+ }
+
+ @Test
+ public void groupsAreConsistentAfterMigrationToNoteDb() throws Exception {
+ // Administrators group are automatically created for all Gerrit servers (NoteDb only).
+ GroupInfo adminGroup = gApi.groups().id("Administrators").get();
+ GroupInfo nonInteractiveUsersGroup = gApi.groups().id("Non-Interactive Users").get();
+ storeInReviewDb(adminGroup, nonInteractiveUsersGroup);
+
+ AccountGroup group1 = newGroup().setName("verifiers").build();
+ AccountGroup group2 = newGroup().setName("contributors").build();
+ storeInReviewDb(group1, group2);
+
+ executeSchemaMigration(schema167, group1, group2);
+
+ List<ConsistencyCheckInfo.ConsistencyProblemInfo> consistencyProblems =
+ consistencyChecker.check();
+ assertThat(consistencyProblems).isEmpty();
+ }
+
+ @Test
+ public void nameIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().setName("verifiers").build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().name().isEqualTo("verifiers");
+ }
+
+ @Test
+ public void emptyNameIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().setName("").build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().name().isEqualTo("");
+ }
+
+ @Test
+ public void uuidIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup.UUID groupUuid = new AccountGroup.UUID("ABCDEF");
+ AccountGroup group = newGroup().setGroupUuid(groupUuid).build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(groupUuid);
+ assertThatGroup(groupInNoteDb).value().groupUuid().isEqualTo(groupUuid);
+ }
+
+ @Test
+ public void idIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup.Id id = new AccountGroup.Id(12345);
+ AccountGroup group = newGroup().setId(id).build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().id().isEqualTo(id);
+ }
+
+ @Test
+ public void createdOnIsKeptDuringMigrationToNoteDb() throws Exception {
+ Timestamp createdOn =
+ Timestamp.from(
+ LocalDate.of(2018, Month.FEBRUARY, 20)
+ .atTime(18, 2, 56)
+ .atZone(ZoneOffset.UTC)
+ .toInstant());
+ AccountGroup group = newGroup().setCreatedOn(createdOn).build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().createdOn().isEqualTo(createdOn);
+ }
+
+ @Test
+ public void ownerUuidIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID("UVWXYZ");
+ AccountGroup group = newGroup().setOwnerGroupUuid(ownerGroupUuid).build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().ownerGroupUuid().isEqualTo(ownerGroupUuid);
+ }
+
+ @Test
+ public void descriptionIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().setDescription("A test group").build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().description().isEqualTo("A test group");
+ }
+
+ @Test
+ public void absentDescriptionIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().description().isNull();
+ }
+
+ @Test
+ public void visibleToAllIsKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().setVisibleToAll(true).build();
+ storeInReviewDb(group);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().visibleToAll().isTrue();
+ }
+
+ @Test
+ public void membersAreKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().build();
+ storeInReviewDb(group);
+ Account.Id member1 = new Account.Id(23456);
+ Account.Id member2 = new Account.Id(93483);
+ addMembersInReviewDb(group.getId(), member1, member2);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().members().containsExactly(member1, member2);
+ }
+
+ @Test
+ public void subgroupsAreKeptDuringMigrationToNoteDb() throws Exception {
+ AccountGroup group = newGroup().build();
+ storeInReviewDb(group);
+ AccountGroup.UUID subgroup1 = new AccountGroup.UUID("FGHIKL");
+ AccountGroup.UUID subgroup2 = new AccountGroup.UUID("MNOPQR");
+ addSubgroupsInReviewDb(group.getId(), subgroup1, subgroup2);
+
+ executeSchemaMigration(schema167, group);
+
+ Optional<InternalGroup> groupInNoteDb = getGroupFromNoteDb(group.getGroupUUID());
+ assertThatGroup(groupInNoteDb).value().subgroups().containsExactly(subgroup1, subgroup2);
+ }
+
+ @Test
+ public void logFormatWithAccountsAndGerritGroups() throws Exception {
+ AccountInfo user1 = createAccount("user1");
+ AccountInfo user2 = createAccount("user2");
+
+ AccountGroup group1 = createInReviewDb("group1");
+ AccountGroup group2 = createInReviewDb("group2");
+ AccountGroup group3 = createInReviewDb("group3");
+
+ // Add some accounts
+ try (TempClockStep step = TestTimeUtil.freezeClock()) {
+ addMembersInReviewDb(
+ group1.getId(), new Account.Id(user1._accountId), new Account.Id(user2._accountId));
+ }
+ TimeUtil.nowTs();
+
+ // Add some Gerrit groups
+ try (TempClockStep step = TestTimeUtil.freezeClock()) {
+ addSubgroupsInReviewDb(group1.getId(), group2.getGroupUUID(), group3.getGroupUUID());
+ }
+
+ executeSchemaMigration(schema167, group1, group2, group3);
+
+ GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group1.getGroupUUID());
+
+ ImmutableList<CommitInfo> log = log(group1);
+ assertThat(log).hasSize(4);
+
+ // Verify commit that created the group
+ assertThat(log.get(0)).message().isEqualTo("Create group");
+ assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
+ assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
+ assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
+ assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
+ assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
+
+ // Verify commit that the group creator as member
+ assertThat(log.get(1))
+ .message()
+ .isEqualTo(
+ "Update group\n\nAdd: "
+ + currentUser.getName()
+ + " <"
+ + currentUser.getAccountId()
+ + "@"
+ + serverId
+ + ">");
+ assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
+
+ // Verify commit that added members
+ assertThat(log.get(2))
+ .message()
+ .isEqualTo(
+ "Update group\n"
+ + "\n"
+ + ("Add: user1 <" + user1._accountId + "@" + serverId + ">\n")
+ + ("Add: user2 <" + user2._accountId + "@" + serverId + ">"));
+ assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
+
+ // Verify commit that added Gerrit groups
+ assertThat(log.get(3))
+ .message()
+ .isEqualTo(
+ "Update group\n"
+ + "\n"
+ + ("Add-group: " + group2.getName() + " <" + group2.getGroupUUID().get() + ">\n")
+ + ("Add-group: " + group3.getName() + " <" + group3.getGroupUUID().get() + ">"));
+ assertThat(log.get(3)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(3)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(3)).committer().hasSameDateAs(log.get(3).author);
+
+ // Verify that audit log is correctly read by Gerrit
+ List<? extends GroupAuditEventInfo> auditEvents =
+ gApi.groups().id(group1.getGroupUUID().get()).auditLog();
+ assertThat(auditEvents).hasSize(5);
+ AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
+ assertMemberAuditEvent(
+ auditEvents.get(4), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
+ assertMemberAuditEvents(
+ auditEvents.get(3),
+ auditEvents.get(2),
+ Type.ADD_USER,
+ currentUser.getAccountId(),
+ user1,
+ user2);
+ assertSubgroupAuditEvents(
+ auditEvents.get(1),
+ auditEvents.get(0),
+ Type.ADD_GROUP,
+ currentUser.getAccountId(),
+ toGroupInfo(group2),
+ toGroupInfo(group3));
+ }
+
+ @Test
+ public void logFormatWithSystemGroups() throws Exception {
+ AccountGroup group = createInReviewDb("group");
+
+ try (TempClockStep step = TestTimeUtil.freezeClock()) {
+ addSubgroupsInReviewDb(
+ group.getId(), SystemGroupBackend.ANONYMOUS_USERS, SystemGroupBackend.REGISTERED_USERS);
+ }
+
+ executeSchemaMigration(schema167, group);
+
+ GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
+
+ ImmutableList<CommitInfo> log = log(group);
+ assertThat(log).hasSize(3);
+
+ // Verify commit that created the group
+ assertThat(log.get(0)).message().isEqualTo("Create group");
+ assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
+ assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
+ assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
+ assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
+ assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
+
+ // Verify commit that the group creator as member
+ assertThat(log.get(1))
+ .message()
+ .isEqualTo(
+ "Update group\n\nAdd: "
+ + currentUser.getName()
+ + " <"
+ + currentUser.getAccountId()
+ + "@"
+ + serverId
+ + ">");
+ assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
+
+ // Verify commit that added system groups
+ assertThat(log.get(2))
+ .message()
+ .isEqualTo(
+ "Update group\n"
+ + "\n"
+ + "Add-group: Anonymous Users <global:Anonymous-Users>\n"
+ + "Add-group: Registered Users <global:Registered-Users>");
+ assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
+
+ // Verify that audit log is correctly read by Gerrit
+ List<? extends GroupAuditEventInfo> auditEvents =
+ gApi.groups().id(group.getGroupUUID().get()).auditLog();
+ assertThat(auditEvents).hasSize(3);
+ AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
+ assertMemberAuditEvent(
+ auditEvents.get(2), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
+ assertSubgroupAuditEvents(
+ auditEvents.get(1),
+ auditEvents.get(0),
+ Type.ADD_GROUP,
+ currentUser.getAccountId(),
+ groupInfoForExternalGroup(SystemGroupBackend.ANONYMOUS_USERS),
+ groupInfoForExternalGroup(SystemGroupBackend.REGISTERED_USERS));
+ }
+
+ @Test
+ public void logFormatWithExternalGroup() throws Exception {
+ AccountGroup group = createInReviewDb("group");
+
+ backends.add(new TestGroupBackend());
+ AccountGroup.UUID subgroupUuid = TestGroupBackend.createUuuid("foo");
+
+ assertThat(groupBackend.handles(subgroupUuid)).isTrue();
+ addSubgroupsInReviewDb(group.getId(), subgroupUuid);
+
+ executeSchemaMigration(schema167, group);
+
+ GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
+
+ ImmutableList<CommitInfo> log = log(group);
+ assertThat(log).hasSize(3);
+
+ // Verify commit that created the group
+ assertThat(log.get(0)).message().isEqualTo("Create group");
+ assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
+ assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
+ assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
+ assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
+ assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
+
+ // Verify commit that the group creator as member
+ assertThat(log.get(1))
+ .message()
+ .isEqualTo(
+ "Update group\n\nAdd: "
+ + currentUser.getName()
+ + " <"
+ + currentUser.getAccountId()
+ + "@"
+ + serverId
+ + ">");
+ assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
+
+ // Verify commit that added system groups
+ // Note: The schema migration can only resolve names of Gerrit groups, not of external groups
+ // and system groups, hence the UUID shows up in commit messages where we would otherwise
+ // expect the group name.
+ assertThat(log.get(2))
+ .message()
+ .isEqualTo(
+ "Update group\n"
+ + "\n"
+ + "Add-group: "
+ + TestGroupBackend.PREFIX
+ + "foo <"
+ + TestGroupBackend.PREFIX
+ + "foo>");
+ assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
+
+ // Verify that audit log is correctly read by Gerrit
+ List<? extends GroupAuditEventInfo> auditEvents =
+ gApi.groups().id(group.getGroupUUID().get()).auditLog();
+ assertThat(auditEvents).hasSize(2);
+ AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
+ assertMemberAuditEvent(
+ auditEvents.get(1), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
+ assertSubgroupAuditEvent(
+ auditEvents.get(0),
+ Type.ADD_GROUP,
+ currentUser.getAccountId(),
+ groupInfoForExternalGroup(subgroupUuid));
+ }
+
+ @Test
+ public void logFormatWithNonExistingExternalGroup() throws Exception {
+ AccountGroup group = createInReviewDb("group");
+
+ AccountGroup.UUID subgroupUuid = new AccountGroup.UUID("notExisting:foo");
+
+ assertThat(groupBackend.handles(subgroupUuid)).isFalse();
+ addSubgroupsInReviewDb(group.getId(), subgroupUuid);
+
+ executeSchemaMigration(schema167, group);
+
+ GroupBundle noteDbBundle = readGroupBundleFromNoteDb(group.getGroupUUID());
+
+ ImmutableList<CommitInfo> log = log(group);
+ assertThat(log).hasSize(3);
+
+ // Verify commit that created the group
+ assertThat(log.get(0)).message().isEqualTo("Create group");
+ assertThat(log.get(0)).author().name().isEqualTo(serverIdent.getName());
+ assertThat(log.get(0)).author().email().isEqualTo(serverIdent.getEmailAddress());
+ assertThat(log.get(0)).author().date().isEqualTo(noteDbBundle.group().getCreatedOn());
+ assertThat(log.get(0)).author().tz().isEqualTo(serverIdent.getTimeZoneOffset());
+ assertThat(log.get(0)).committer().isEqualTo(log.get(0).author);
+
+ // Verify commit that the group creator as member
+ assertThat(log.get(1))
+ .message()
+ .isEqualTo(
+ "Update group\n\nAdd: "
+ + currentUser.getName()
+ + " <"
+ + currentUser.getAccountId()
+ + "@"
+ + serverId
+ + ">");
+ assertThat(log.get(1)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(1)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(1)).committer().hasSameDateAs(log.get(1).author);
+
+ // Verify commit that added system groups
+ // Note: The schema migration can only resolve names of Gerrit groups, not of external groups
+ // and system groups, hence the UUID shows up in commit messages where we would otherwise
+ // expect the group name.
+ assertThat(log.get(2))
+ .message()
+ .isEqualTo("Update group\n" + "\n" + "Add-group: notExisting:foo <notExisting:foo>");
+ assertThat(log.get(2)).author().name().isEqualTo(currentUser.getName());
+ assertThat(log.get(2)).author().email().isEqualTo(currentUser.getAccountId() + "@" + serverId);
+ assertThat(log.get(2)).committer().hasSameDateAs(log.get(2).author);
+
+ // Verify that audit log is correctly read by Gerrit
+ List<? extends GroupAuditEventInfo> auditEvents =
+ gApi.groups().id(group.getGroupUUID().get()).auditLog();
+ assertThat(auditEvents).hasSize(2);
+ AccountInfo currentUserInfo = gApi.accounts().id(currentUser.getAccountId().get()).get();
+ assertMemberAuditEvent(
+ auditEvents.get(1), Type.ADD_USER, currentUser.getAccountId(), currentUserInfo);
+ assertSubgroupAuditEvent(
+ auditEvents.get(0),
+ Type.ADD_GROUP,
+ currentUser.getAccountId(),
+ groupInfoForExternalGroup(subgroupUuid));
+ }
+
+ private static TestGroup.Builder newGroup() {
+ return TestGroup.builder();
+ }
+
+ private AccountGroup createInReviewDb(String groupName) throws Exception {
+ AccountGroup group =
+ new AccountGroup(
+ new AccountGroup.NameKey(groupName),
+ new AccountGroup.Id(seq.nextGroupId()),
+ GroupUUID.make(groupName, serverIdent),
+ TimeUtil.nowTs());
+ storeInReviewDb(group);
+ addMembersInReviewDb(group.getId(), currentUser.getAccountId());
+ return group;
+ }
+
+ private void storeInReviewDb(GroupInfo... groups) throws Exception {
+ storeInReviewDb(
+ Arrays.stream(groups)
+ .map(Schema_166_to_167_WithGroupsInReviewDbTest::toAccountGroup)
+ .toArray(AccountGroup[]::new));
+ }
+
+ private void storeInReviewDb(AccountGroup... groups) throws Exception {
+ try (PreparedStatement stmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_groups"
+ + " (group_uuid,"
+ + " group_id,"
+ + " name,"
+ + " description,"
+ + " created_on,"
+ + " owner_group_uuid,"
+ + " visible_to_all) VALUES (?, ?, ?, ?, ?, ?, ?)")) {
+ for (AccountGroup group : groups) {
+ stmt.setString(1, group.getGroupUUID().get());
+ stmt.setInt(2, group.getId().get());
+ stmt.setString(3, group.getName());
+ stmt.setString(4, group.getDescription());
+ stmt.setTimestamp(5, group.getCreatedOn());
+ stmt.setString(6, group.getOwnerGroupUUID().get());
+ stmt.setString(7, group.isVisibleToAll() ? "Y" : "N");
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ }
+ }
+
+ private void updateInReviewDb(AccountGroup... groups) throws Exception {
+ try (PreparedStatement stmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "UPDATE account_groups SET"
+ + " group_uuid = ?,"
+ + " name = ?,"
+ + " description = ?,"
+ + " created_on = ?,"
+ + " owner_group_uuid = ?,"
+ + " visible_to_all = ?"
+ + " WHERE group_id = ?")) {
+ for (AccountGroup group : groups) {
+ stmt.setString(1, group.getGroupUUID().get());
+ stmt.setString(2, group.getName());
+ stmt.setString(3, group.getDescription());
+ stmt.setTimestamp(4, group.getCreatedOn());
+ stmt.setString(5, group.getOwnerGroupUUID().get());
+ stmt.setString(6, group.isVisibleToAll() ? "Y" : "N");
+ stmt.setInt(7, group.getId().get());
+ stmt.addBatch();
+ }
+ stmt.executeBatch();
+ }
+ }
+
+ private AccountGroup getFromReviewDb(AccountGroup.Id groupId) throws Exception {
+ try (Statement stmt = jdbcSchema.getConnection().createStatement();
+ ResultSet rs =
+ stmt.executeQuery(
+ "SELECT group_uuid,"
+ + " name,"
+ + " description,"
+ + " created_on,"
+ + " owner_group_uuid,"
+ + " visible_to_all"
+ + " FROM account_groups"
+ + " WHERE group_id = "
+ + groupId.get())) {
+ if (!rs.next()) {
+ throw new OrmException(String.format("Group %s not found", groupId.get()));
+ }
+
+ AccountGroup.UUID groupUuid = new AccountGroup.UUID(rs.getString(1));
+ AccountGroup.NameKey groupName = new AccountGroup.NameKey(rs.getString(2));
+ String description = rs.getString(3);
+ Timestamp createdOn = rs.getTimestamp(4);
+ AccountGroup.UUID ownerGroupUuid = new AccountGroup.UUID(rs.getString(5));
+ boolean visibleToAll = "Y".equals(rs.getString(6));
+
+ AccountGroup group = new AccountGroup(groupName, groupId, groupUuid, createdOn);
+ group.setDescription(description);
+ group.setOwnerGroupUUID(ownerGroupUuid);
+ group.setVisibleToAll(visibleToAll);
+
+ if (rs.next()) {
+ throw new OrmException(String.format("Group ID %s is ambiguous", groupId.get()));
+ }
+
+ return group;
+ }
+ }
+
+ private void addMembersInReviewDb(AccountGroup.Id groupId, Account.Id... memberIds)
+ throws Exception {
+ try (PreparedStatement addMemberStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_members"
+ + " (group_id,"
+ + " account_id) VALUES ("
+ + groupId.get()
+ + ", ?)");
+ PreparedStatement addMemberAuditStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_members_audit"
+ + " (group_id,"
+ + " account_id,"
+ + " added_by,"
+ + " added_on) VALUES ("
+ + groupId.get()
+ + ", ?, "
+ + currentUser.getAccountId().get()
+ + ", ?)")) {
+ Timestamp addedOn = TimeUtil.nowTs();
+ for (Account.Id memberId : memberIds) {
+ addMemberStmt.setInt(1, memberId.get());
+ addMemberStmt.addBatch();
+
+ addMemberAuditStmt.setInt(1, memberId.get());
+ addMemberAuditStmt.setTimestamp(2, addedOn);
+ addMemberAuditStmt.addBatch();
+ }
+ addMemberStmt.executeBatch();
+ addMemberAuditStmt.executeBatch();
+ }
+ }
+
+ private void addSubgroupsInReviewDb(AccountGroup.Id groupId, AccountGroup.UUID... subgroupUuids)
+ throws Exception {
+ try (PreparedStatement addSubGroupStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_by_id"
+ + " (group_id,"
+ + " include_uuid) VALUES ("
+ + groupId.get()
+ + ", ?)");
+ PreparedStatement addSubGroupAuditStmt =
+ jdbcSchema
+ .getConnection()
+ .prepareStatement(
+ "INSERT INTO account_group_by_id_aud"
+ + " (group_id,"
+ + " include_uuid,"
+ + " added_by,"
+ + " added_on) VALUES ("
+ + groupId.get()
+ + ", ?, "
+ + currentUser.getAccountId().get()
+ + ", ?)")) {
+ Timestamp addedOn = TimeUtil.nowTs();
+ for (AccountGroup.UUID subgroupUuid : subgroupUuids) {
+ addSubGroupStmt.setString(1, subgroupUuid.get());
+ addSubGroupStmt.addBatch();
+
+ addSubGroupAuditStmt.setString(1, subgroupUuid.get());
+ addSubGroupAuditStmt.setTimestamp(2, addedOn);
+ addSubGroupAuditStmt.addBatch();
+ }
+ addSubGroupStmt.executeBatch();
+ addSubGroupAuditStmt.executeBatch();
+ }
+ }
+
+ private AccountInfo createAccount(String name) throws RestApiException {
+ AccountInput accountInput = new AccountInput();
+ accountInput.username = name;
+ accountInput.name = name;
+ return gApi.accounts().create(accountInput).get();
+ }
+
+ private GroupBundle readGroupBundleFromNoteDb(AccountGroup.UUID groupUuid) throws Exception {
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
+ return groupBundleFactory.fromNoteDb(allUsersRepo, groupUuid);
+ }
+ }
+
+ private void executeSchemaMigration(SchemaVersion schema, AccountGroup... groupsToVerify)
+ throws Exception {
+ executeSchemaMigration(
+ schema,
+ Arrays.stream(groupsToVerify)
+ .map(AccountGroup::getGroupUUID)
+ .toArray(AccountGroup.UUID[]::new));
+ }
+
+ private void executeSchemaMigration(SchemaVersion schema, GroupInfo... groupsToVerify)
+ throws Exception {
+ executeSchemaMigration(
+ schema,
+ Arrays.stream(groupsToVerify)
+ .map(i -> new AccountGroup.UUID(i.id))
+ .toArray(AccountGroup.UUID[]::new));
+ }
+
+ private void executeSchemaMigration(SchemaVersion schema, AccountGroup.UUID... groupsToVerify)
+ throws Exception {
+ List<GroupBundle> reviewDbBundles = new ArrayList<>();
+ for (AccountGroup.UUID groupUuid : groupsToVerify) {
+ reviewDbBundles.add(GroupBundle.Factory.fromReviewDb(db, groupUuid));
+ }
+
+ schema.migrateData(db, new TestUpdateUI());
+
+ for (GroupBundle reviewDbBundle : reviewDbBundles) {
+ assertMigratedCleanly(readGroupBundleFromNoteDb(reviewDbBundle.uuid()), reviewDbBundle);
+ }
+ }
+
+ private void assertMigratedCleanly(GroupBundle noteDbBundle, GroupBundle expectedReviewDbBundle) {
+ assertThat(GroupBundle.compareWithAudits(expectedReviewDbBundle, noteDbBundle)).isEmpty();
+ }
+
+ private ImmutableList<CommitInfo> log(AccountGroup group) throws Exception {
+ ImmutableList.Builder<CommitInfo> result = ImmutableList.builder();
+ List<Date> commitDates = new ArrayList<>();
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName);
+ RevWalk rw = new RevWalk(allUsersRepo)) {
+ Ref ref = allUsersRepo.exactRef(RefNames.refsGroups(group.getGroupUUID()));
+ if (ref != null) {
+ rw.sort(RevSort.REVERSE);
+ rw.setRetainBody(true);
+ rw.markStart(rw.parseCommit(ref.getObjectId()));
+ for (RevCommit c : rw) {
+ result.add(CommitUtil.toCommitInfo(c));
+ commitDates.add(c.getCommitterIdent().getWhen());
+ }
+ }
+ }
+ assertThat(commitDates).named("commit timestamps for %s", result).isOrdered();
+ return result.build();
+ }
+
+ private ImmutableList<GroupReference> getAllGroupsFromNoteDb()
+ throws IOException, ConfigInvalidException {
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
+ return GroupNameNotes.loadAllGroups(allUsersRepo);
+ }
+ }
+
+ private Optional<InternalGroup> getGroupFromNoteDb(AccountGroup.UUID groupUuid) throws Exception {
+ try (Repository allUsersRepo = gitRepoManager.openRepository(allUsersName)) {
+ return GroupConfig.loadForGroup(allUsersRepo, groupUuid).getLoadedGroup();
+ }
+ }
+
+ private static OptionalSubject<InternalGroupSubject, InternalGroup> assertThatGroup(
+ Optional<InternalGroup> group) {
+ return assertThat(group, InternalGroupSubject::assertThat).named("group");
+ }
+
+ private void assertMemberAuditEvent(
+ GroupAuditEventInfo info,
+ Type expectedType,
+ Account.Id expectedUser,
+ AccountInfo expectedMember) {
+ assertThat(info.user._accountId).isEqualTo(expectedUser.get());
+ assertThat(info.type).isEqualTo(expectedType);
+ assertThat(info).isInstanceOf(UserMemberAuditEventInfo.class);
+ assertAccount(((UserMemberAuditEventInfo) info).member, expectedMember);
+ }
+
+ private void assertMemberAuditEvents(
+ GroupAuditEventInfo info1,
+ GroupAuditEventInfo info2,
+ Type expectedType,
+ Account.Id expectedUser,
+ AccountInfo expectedMember1,
+ AccountInfo expectedMember2) {
+ assertThat(info1).isInstanceOf(UserMemberAuditEventInfo.class);
+ assertThat(info2).isInstanceOf(UserMemberAuditEventInfo.class);
+
+ UserMemberAuditEventInfo event1 = (UserMemberAuditEventInfo) info1;
+ UserMemberAuditEventInfo event2 = (UserMemberAuditEventInfo) info2;
+
+ assertThat(event1.member._accountId)
+ .isAnyOf(expectedMember1._accountId, expectedMember2._accountId);
+ assertThat(event2.member._accountId)
+ .isAnyOf(expectedMember1._accountId, expectedMember2._accountId);
+ assertThat(event1.member._accountId).isNotEqualTo(event2.member._accountId);
+
+ if (event1.member._accountId == expectedMember1._accountId) {
+ assertMemberAuditEvent(info1, expectedType, expectedUser, expectedMember1);
+ assertMemberAuditEvent(info2, expectedType, expectedUser, expectedMember2);
+ } else {
+ assertMemberAuditEvent(info1, expectedType, expectedUser, expectedMember2);
+ assertMemberAuditEvent(info2, expectedType, expectedUser, expectedMember1);
+ }
+ }
+
+ private void assertSubgroupAuditEvent(
+ GroupAuditEventInfo info,
+ Type expectedType,
+ Account.Id expectedUser,
+ GroupInfo expectedSubGroup) {
+ assertThat(info.user._accountId).isEqualTo(expectedUser.get());
+ assertThat(info.type).isEqualTo(expectedType);
+ assertThat(info).isInstanceOf(GroupMemberAuditEventInfo.class);
+ assertGroup(((GroupMemberAuditEventInfo) info).member, expectedSubGroup);
+ }
+
+ private void assertSubgroupAuditEvents(
+ GroupAuditEventInfo info1,
+ GroupAuditEventInfo info2,
+ Type expectedType,
+ Account.Id expectedUser,
+ GroupInfo expectedSubGroup1,
+ GroupInfo expectedSubGroup2) {
+ assertThat(info1).isInstanceOf(GroupMemberAuditEventInfo.class);
+ assertThat(info2).isInstanceOf(GroupMemberAuditEventInfo.class);
+
+ GroupMemberAuditEventInfo event1 = (GroupMemberAuditEventInfo) info1;
+ GroupMemberAuditEventInfo event2 = (GroupMemberAuditEventInfo) info2;
+
+ assertThat(event1.member.id).isAnyOf(expectedSubGroup1.id, expectedSubGroup2.id);
+ assertThat(event2.member.id).isAnyOf(expectedSubGroup1.id, expectedSubGroup2.id);
+ assertThat(event1.member.id).isNotEqualTo(event2.member.id);
+
+ if (event1.member.id.equals(expectedSubGroup1.id)) {
+ assertSubgroupAuditEvent(info1, expectedType, expectedUser, expectedSubGroup1);
+ assertSubgroupAuditEvent(info2, expectedType, expectedUser, expectedSubGroup2);
+ } else {
+ assertSubgroupAuditEvent(info1, expectedType, expectedUser, expectedSubGroup2);
+ assertSubgroupAuditEvent(info2, expectedType, expectedUser, expectedSubGroup1);
+ }
+ }
+
+ private void assertAccount(AccountInfo actual, AccountInfo expected) {
+ assertThat(actual._accountId).isEqualTo(expected._accountId);
+ assertThat(actual.name).isEqualTo(expected.name);
+ assertThat(actual.email).isEqualTo(expected.email);
+ assertThat(actual.username).isEqualTo(expected.username);
+ }
+
+ private void assertGroup(GroupInfo actual, GroupInfo expected) {
+ assertThat(actual.id).isEqualTo(expected.id);
+ assertThat(actual.name).isEqualTo(expected.name);
+ assertThat(actual.groupId).isEqualTo(expected.groupId);
+ }
+
+ private GroupInfo groupInfoForExternalGroup(AccountGroup.UUID groupUuid) {
+ GroupInfo groupInfo = new GroupInfo();
+ groupInfo.id = IdString.fromDecoded(groupUuid.get()).encoded();
+
+ if (groupBackend.handles(groupUuid)) {
+ groupInfo.name = groupBackend.get(groupUuid).getName();
+ }
+
+ return groupInfo;
+ }
+
+ private static AccountGroup toAccountGroup(GroupInfo info) {
+ AccountGroup group =
+ new AccountGroup(
+ new AccountGroup.NameKey(info.name),
+ new AccountGroup.Id(info.groupId),
+ new AccountGroup.UUID(info.id),
+ info.createdOn);
+ group.setDescription(info.description);
+ if (info.ownerId != null) {
+ group.setOwnerGroupUUID(new AccountGroup.UUID(info.ownerId));
+ }
+ group.setVisibleToAll(
+ info.options != null && info.options.visibleToAll != null && info.options.visibleToAll);
+ return group;
+ }
+
+ private static GroupInfo toGroupInfo(AccountGroup group) {
+ GroupInfo groupInfo = new GroupInfo();
+ groupInfo.id = group.getGroupUUID().get();
+ groupInfo.groupId = group.getId().get();
+ groupInfo.name = group.getName();
+ groupInfo.createdOn = group.getCreatedOn();
+ groupInfo.description = group.getDescription();
+ groupInfo.owner = group.getOwnerGroupUUID().get();
+ groupInfo.options = new GroupOptionsInfo();
+ groupInfo.options.visibleToAll = group.isVisibleToAll() ? true : null;
+ return groupInfo;
+ }
+
+ private static class TestGroupBackend implements GroupBackend {
+ static final String PREFIX = "testbackend:";
+
+ static AccountGroup.UUID createUuuid(String name) {
+ return new AccountGroup.UUID(PREFIX + name);
+ }
+
+ @Override
+ public Collection<GroupReference> suggest(String name, ProjectState project) {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public GroupMembership membershipsOf(IdentifiedUser user) {
+ return new GroupMembership() {
+ @Override
+ public Set<AccountGroup.UUID> intersection(Iterable<AccountGroup.UUID> groupIds) {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Set<AccountGroup.UUID> getKnownGroups() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public boolean containsAnyOf(Iterable<AccountGroup.UUID> groupIds) {
+ return false;
+ }
+
+ @Override
+ public boolean contains(AccountGroup.UUID groupId) {
+ return false;
+ }
+ };
+ }
+
+ @Override
+ public boolean isVisibleToAll(AccountGroup.UUID uuid) {
+ return false;
+ }
+
+ @Override
+ public boolean handles(AccountGroup.UUID uuid) {
+ return uuid.get().startsWith(PREFIX);
+ }
+
+ @Override
+ public Basic get(AccountGroup.UUID uuid) {
+ return new GroupDescription.Basic() {
+ @Override
+ public AccountGroup.UUID getGroupUUID() {
+ return uuid;
+ }
+
+ @Override
+ public String getName() {
+ return uuid.get().substring(PREFIX.length());
+ }
+
+ @Override
+ public String getEmailAddress() {
+ return null;
+ }
+
+ @Override
+ public String getUrl() {
+ return null;
+ }
+ };
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/server/schema/TestGroup.java b/javatests/com/google/gerrit/server/schema/TestGroup.java
new file mode 100644
index 0000000..49cf028
--- /dev/null
+++ b/javatests/com/google/gerrit/server/schema/TestGroup.java
@@ -0,0 +1,81 @@
+// Copyright (C) 2018 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.server.schema;
+
+import com.google.auto.value.AutoValue;
+import com.google.gerrit.common.TimeUtil;
+import com.google.gerrit.reviewdb.client.AccountGroup;
+import java.sql.Timestamp;
+import java.util.Optional;
+import org.junit.Ignore;
+
+@AutoValue
+@Ignore
+public abstract class TestGroup {
+ abstract Optional<AccountGroup.NameKey> getNameKey();
+
+ abstract Optional<AccountGroup.UUID> getGroupUuid();
+
+ abstract Optional<AccountGroup.Id> getId();
+
+ abstract Optional<Timestamp> getCreatedOn();
+
+ abstract Optional<AccountGroup.UUID> getOwnerGroupUuid();
+
+ abstract Optional<String> getDescription();
+
+ abstract boolean isVisibleToAll();
+
+ public static Builder builder() {
+ return new AutoValue_TestGroup.Builder().setVisibleToAll(false);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder setNameKey(AccountGroup.NameKey nameKey);
+
+ public Builder setName(String name) {
+ return setNameKey(new AccountGroup.NameKey(name));
+ }
+
+ public abstract Builder setGroupUuid(AccountGroup.UUID uuid);
+
+ public abstract Builder setId(AccountGroup.Id id);
+
+ public abstract Builder setCreatedOn(Timestamp createdOn);
+
+ public abstract Builder setOwnerGroupUuid(AccountGroup.UUID ownerGroupUuid);
+
+ public abstract Builder setDescription(String description);
+
+ public abstract Builder setVisibleToAll(boolean visibleToAll);
+
+ public abstract TestGroup autoBuild();
+
+ public AccountGroup build() {
+ TestGroup testGroup = autoBuild();
+ AccountGroup.NameKey name = testGroup.getNameKey().orElse(new AccountGroup.NameKey("users"));
+ AccountGroup.Id id = testGroup.getId().orElse(new AccountGroup.Id(Math.abs(name.hashCode())));
+ AccountGroup.UUID uuid =
+ testGroup.getGroupUuid().orElse(new AccountGroup.UUID(name + "-UUID"));
+ Timestamp createdOn = testGroup.getCreatedOn().orElseGet(TimeUtil::nowTs);
+ AccountGroup accountGroup = new AccountGroup(name, id, uuid, createdOn);
+ testGroup.getOwnerGroupUuid().ifPresent(accountGroup::setOwnerGroupUUID);
+ testGroup.getDescription().ifPresent(accountGroup::setDescription);
+ accountGroup.setVisibleToAll(testGroup.isVisibleToAll());
+ return accountGroup;
+ }
+ }
+}
diff --git a/lib/LICENSE-ba-linkify b/lib/LICENSE-ba-linkify
new file mode 100644
index 0000000..93672f9
--- /dev/null
+++ b/lib/LICENSE-ba-linkify
@@ -0,0 +1,22 @@
+Copyright (c) 2009 "Cowboy" Ben Alman
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/ba-linkify/BUILD b/lib/ba-linkify/BUILD
new file mode 100644
index 0000000..9a8b442
--- /dev/null
+++ b/lib/ba-linkify/BUILD
@@ -0,0 +1 @@
+exports_files(["ba-linkify.js"])
diff --git a/lib/ba-linkify/README.md b/lib/ba-linkify/README.md
new file mode 100644
index 0000000..8c3e9a4
--- /dev/null
+++ b/lib/ba-linkify/README.md
@@ -0,0 +1,6 @@
+This is the latest version of ba-linkify.js from:
+https://github.com/cowboy/javascript-linkify/blob/178ffc271f89cef403faf73cabd74dda0a79af62/ba-linkify.js
+
+The file was modified manually to include a @license JSDoc tag. The file hasn't
+been updated since 2009, but on the off chance you need to update it, please
+make sure you include a @license.
diff --git a/lib/ba-linkify/ba-linkify.js b/lib/ba-linkify/ba-linkify.js
new file mode 100644
index 0000000..32fbea3
--- /dev/null
+++ b/lib/ba-linkify/ba-linkify.js
@@ -0,0 +1,239 @@
+/**
+ * @license
+ * Copyright (c) 2009 "Cowboy" Ben Alman
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*!
+ * JavaScript Linkify - v0.3 - 6/27/2009
+ * http://benalman.com/projects/javascript-linkify/
+ *
+ * Copyright (c) 2009 "Cowboy" Ben Alman
+ * Dual licensed under the MIT and GPL licenses.
+ * http://benalman.com/about/license/
+ *
+ * Some regexps adapted from http://userscripts.org/scripts/review/7122
+ */
+
+// Script: JavaScript Linkify: Process links in text!
+//
+// *Version: 0.3, Last updated: 6/27/2009*
+//
+// Project Home - http://benalman.com/projects/javascript-linkify/
+// GitHub - http://github.com/cowboy/javascript-linkify/
+// Source - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.js
+// (Minified) - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.min.js (2.8kb)
+//
+// About: License
+//
+// Copyright (c) 2009 "Cowboy" Ben Alman,
+// Dual licensed under the MIT and GPL licenses.
+// http://benalman.com/about/license/
+//
+// About: Examples
+//
+// This working example, complete with fully commented code, illustrates one way
+// in which this code can be used.
+//
+// Linkify - http://benalman.com/code/projects/javascript-linkify/examples/linkify/
+//
+// About: Support and Testing
+//
+// Information about what browsers this code has been tested in.
+//
+// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.7, Safari 3-4, Chrome, Opera 9.6-10.
+//
+// About: Release History
+//
+// 0.3 - (6/27/2009) Initial release
+
+// Function: linkify
+//
+// Turn text into linkified html.
+//
+// Usage:
+//
+// > var html = linkify( text [, options ] );
+//
+// Arguments:
+//
+// text - (String) Non-HTML text containing links to be parsed.
+// options - (Object) An optional object containing linkify parse options.
+//
+// Options:
+//
+// callback (Function) - If specified, this will be called once for each link-
+// or non-link-chunk with two arguments, text and href. If the chunk is
+// non-link, href will be omitted. If unspecified, the default linkification
+// callback is used.
+// punct_regexp (RegExp) - A RegExp that will be used to trim trailing
+// punctuation from links, instead of the default. If set to null, trailing
+// punctuation will not be trimmed.
+//
+// Returns:
+//
+// (String) An HTML string containing links.
+
+window.linkify = (function(){
+ var
+ SCHEME = "[a-z\\d.-]+://",
+ IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
+ HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
+ TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
+ HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
+ PATH = "(?:[;/][^#?<>\\s]*)?",
+ QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
+ URI1 = "\\b" + SCHEME + "[^<>\\s]+",
+ URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
+
+ MAILTO = "mailto:",
+ EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
+
+ URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
+ SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
+
+ quotes = {
+ "'": "`",
+ '>': '<',
+ ')': '(',
+ ']': '[',
+ '}': '{',
+ '»': '«',
+ '›': '‹'
+ },
+
+ default_options = {
+ callback: function( text, href ) {
+ return href ? '<a href="' + href + '" title="' + href + '">' + text + '</a>' : text;
+ },
+ punct_regexp: /(?:[!?.,:;'"]|(?:&|&)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
+ };
+
+ return function( txt, options ) {
+ options = options || {};
+
+ // Temp variables.
+ var arr,
+ i,
+ link,
+ href,
+
+ // Output HTML.
+ html = '',
+
+ // Store text / link parts, in order, for re-combination.
+ parts = [],
+
+ // Used for keeping track of indices in the text.
+ idx_prev,
+ idx_last,
+ idx,
+ link_last,
+
+ // Used for trimming trailing punctuation and quotes from links.
+ matches_begin,
+ matches_end,
+ quote_begin,
+ quote_end;
+
+ // Initialize options.
+ for ( i in default_options ) {
+ if ( options[ i ] === undefined ) {
+ options[ i ] = default_options[ i ];
+ }
+ }
+
+ // Find links.
+ while ( arr = URI_RE.exec( txt ) ) {
+
+ link = arr[0];
+ idx_last = URI_RE.lastIndex;
+ idx = idx_last - link.length;
+
+ // Not a link if preceded by certain characters.
+ if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
+ continue;
+ }
+
+ // Trim trailing punctuation.
+ do {
+ // If no changes are made, we don't want to loop forever!
+ link_last = link;
+
+ quote_end = link.substr( -1 )
+ quote_begin = quotes[ quote_end ];
+
+ // Ending quote character?
+ if ( quote_begin ) {
+ matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
+ matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
+
+ // If quotes are unbalanced, remove trailing quote character.
+ if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
+ link = link.substr( 0, link.length - 1 );
+ idx_last--;
+ }
+ }
+
+ // Ending non-quote punctuation character?
+ if ( options.punct_regexp ) {
+ link = link.replace( options.punct_regexp, function(a){
+ idx_last -= a.length;
+ return '';
+ });
+ }
+ } while ( link.length && link !== link_last );
+
+ href = link;
+
+ // Add appropriate protocol to naked links.
+ if ( !SCHEME_RE.test( href ) ) {
+ href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
+ : !href.indexOf( 'irc.' ) ? 'irc://'
+ : !href.indexOf( 'ftp.' ) ? 'ftp://'
+ : 'http://' )
+ + href;
+ }
+
+ // Push preceding non-link text onto the array.
+ if ( idx_prev != idx ) {
+ parts.push([ txt.slice( idx_prev, idx ) ]);
+ idx_prev = idx_last;
+ }
+
+ // Push massaged link onto the array
+ parts.push([ link, href ]);
+ };
+
+ // Push remaining non-link text onto the array.
+ parts.push([ txt.substr( idx_prev ) ]);
+
+ // Process the array items.
+ for ( i = 0; i < parts.length; i++ ) {
+ html += options.callback.apply( window, parts[i] );
+ }
+
+ // In case of catastrophic failure, return the original text;
+ return html || txt;
+ };
+
+})();
diff --git a/lib/js/BUILD b/lib/js/BUILD
index 0bb7b0c..8a7986e 100644
--- a/lib/js/BUILD
+++ b/lib/js/BUILD
@@ -24,18 +24,18 @@
define_bower_components()
-js_component(
- name = "highlightjs",
- srcs = ["//lib/highlightjs:highlight.min.js"],
- license = "//lib:LICENSE-highlightjs",
-)
-
filegroup(
name = "highlightjs_files",
srcs = ["//lib/highlightjs:highlight.min.js"],
data = ["//lib:LICENSE-highlightjs"],
)
+js_component(
+ name = "ba-linkify",
+ srcs = ["//lib/ba-linkify:ba-linkify.js"],
+ license = "//lib:LICENSE-ba-linkify",
+)
+
bower_component(
name = "codemirror-minified",
license = "//lib:LICENSE-codemirror-minified",
diff --git a/plugins/replication b/plugins/replication
index d8f5bce..a62d1c6 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit d8f5bcec21492cc21e2499c332298a94ff8defc6
+Subproject commit a62d1c601e6c7fb669c847a0e1843e6f60cd1cb2
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 7487ad5..b338cbf 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -8,10 +8,9 @@
bower_component_bundle(
name = "polygerrit_components.bower_components",
deps = [
+ "//lib/js:ba-linkify",
"//lib/js:es6-promise",
"//lib/js:fetch",
- # TODO(hanwen): this is inserted separately in the UI zip. Do we need this here?
- "//lib/js:highlightjs",
"//lib/js:iron-a11y-keys-behavior",
"//lib/js:iron-autogrow-textarea",
"//lib/js:iron-dropdown",
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
index c15df90..47f47ec 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.html
@@ -32,44 +32,60 @@
<dom-module id="gr-repo-access">
<template>
<style include="shared-styles">
- gr-button {
+ gr-button,
+ #inheritsFrom,
+ #editInheritFromInput,
+ .editing #inheritFromName,
+ .weblinks,
+ #loadingText,
+ .loading {
display: none;
}
- .admin gr-button.visible {
- display: inline-block;
- margin: 1em 0;
+ #inheritsFrom.show {
+ display: flex;
+ min-height: 2em;
+ align-items: center;
}
.weblink {
margin-right: .2em;
}
- .weblinks {
- display: none;
- }
- .weblinks.show {
+ .weblinks.show,
+ #loadingText.loading,
+ .referenceContainer {
display: block;
}
- .loading {
- display: none;
+ .rightsText {
+ margin-right: .3rem;
}
- #loading.loading {
- display: block;
+
+ .editing gr-button,
+ .admin #editBtn {
+ display: inline-block;
+ margin: 1em 0;
}
- #loading:not(.loading) {
- display: none;
+ .editing #editInheritFromInput {
+ display: inline-block;
}
</style>
<style include="gr-menu-page-styles"></style>
- <main class$="[[_computeAdminClass(_isAdmin, _canUpload)]]">
- <div id="loading" class$="[[_computeLoadingClass(_loading)]]">
+ <main class$="[[_computeMainClass(_isAdmin, _canUpload, _editing)]]">
+ <div id="loadingText" class$="[[_computeLoadingClass(_loading)]]">
Loading...
</div>
<div id="loadedContent" class$="[[_computeLoadingClass(_loading)]]">
- <template is="dom-if" if="[[_inheritsFrom]]">
- <h3 id="inheritsFrom">Rights Inherit From
- <a href$="[[_computeParentHref(_inheritsFrom.name)]]" rel="noopener">
- [[_inheritsFrom.name]]</a>
- </h3>
- </template>
+ <h3 id="inheritsFrom" class$="[[_computeShowInherit(_inheritsFrom)]]">
+ <span class="rightsText">Rights Inherit From</span>
+ <a
+ href$="[[_computeParentHref(_inheritsFrom.name)]]"
+ rel="noopener"
+ id="inheritFromName">
+ [[_inheritsFrom.name]]</a>
+ <gr-autocomplete
+ id="editInheritFromInput"
+ text="{{_inheritFromFilter}}"
+ query="[[_query]]"
+ on-commit="_handleUpdateInheritFrom"></gr-autocomplete>
+ </h3>
<div class$="weblinks [[_computeWebLinkClass(_weblinks)]]">
History:
<template is="dom-repeat" items="[[_weblinks]]" as="link">
@@ -79,11 +95,9 @@
</template>
</div>
<gr-button id="editBtn"
- class="visible"
on-tap="_handleEdit">[[_editOrCancel(_editing)]]</gr-button>
<gr-button id="saveBtn"
primary
- class$="[[_computeShowSaveClass(_editing)]]"
on-tap="_handleSaveForReview"
disabled$="[[!_modified]]">
Save for review</gr-button>
@@ -98,9 +112,10 @@
editing="[[_editing]]"
groups="[[_groups]]"></gr-access-section>
</template>
- <gr-button id="addReferenceBtn"
- class$="[[_computeShowSaveClass(_editing)]]"
- on-tap="_handleCreateSection">Add Reference</gr-button>
+ <div class="referenceContainer">
+ <gr-button id="addReferenceBtn"
+ on-tap="_handleCreateSection">Add Reference</gr-button>
+ </div>
</div>
</main>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index a8d21e2..1982b9a 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -21,6 +21,8 @@
const NOTHING_TO_SAVE = 'No changes to save.';
+ const MAX_AUTOCOMPLETE_RESULTS = 20;
+
/**
* Fired when save is a no-op
*
@@ -85,6 +87,13 @@
type: Boolean,
value: false,
},
+ _inheritFromFilter: String,
+ _query: {
+ type: Function,
+ value() {
+ return this._getInheritFromSuggestions.bind(this);
+ },
+ },
_ownerOf: Array,
_capabilities: Object,
_groups: Object,
@@ -142,7 +151,18 @@
.then(res => {
if (!res) { return Promise.resolve(); }
- this._inheritsFrom = res.inherits_from;
+ // Keep a copy of the original inherit from values separate from
+ // the ones data bound to gr-autocomplete, so the original value
+ // can be restored if the user cancels.
+ this._inheritsFrom = res.inherits_from ? Object.assign({},
+ res.inherits_from) : null;
+ this._originalInheritsFrom = res.inherits_from ? Object.assign({},
+ res.inherits_from) : null;
+ // Initialize the filter value so when the user clicks edit, the
+ // current value appears. If there is no parent repo, it is
+ // initialized as an empty string.
+ this._inheritFromFilter = res.inherits_from ?
+ this._inheritsFrom.name : '';
this._local = res.local;
this._groups = res.groups;
this._weblinks = res.config_web_links || [];
@@ -176,6 +196,33 @@
});
},
+ _handleUpdateInheritFrom(e) {
+ const projectId = decodeURIComponent(e.detail.value);
+ if (!this._inheritsFrom) {
+ this._inheritsFrom = {};
+ }
+ this._inheritsFrom.id = projectId;
+ this._inheritsFrom.name = this._inheritFromFilter;
+ this._handleAccessModified();
+ },
+
+ _getInheritFromSuggestions() {
+ return this.$.restAPI.getRepos(
+ this._inheritFromFilter,
+ MAX_AUTOCOMPLETE_RESULTS)
+ .then(response => {
+ const projects = [];
+ for (const key in response) {
+ if (!response.hasOwnProperty(key)) { continue; }
+ projects.push({
+ name: key,
+ value: response[key].id,
+ });
+ }
+ return projects;
+ });
+ },
+
_computeLoadingClass(loading) {
return loading ? 'loading' : '';
},
@@ -192,11 +239,22 @@
return weblinks.length ? 'show' : '';
},
+ _computeShowInherit(inheritsFrom) {
+ return inheritsFrom ? 'show' : '';
+ },
+
_handleEditingChanged(editing, editingOld) {
// Ignore when editing gets set initially.
if (!editingOld || editing) { return; }
// Remove any unsaved but added refs.
- this._sections = this._sections.filter(p => !p.value.added);
+ if (this._sections) {
+ this._sections = this._sections.filter(p => !p.value.added);
+ }
+ // Restore inheritFrom.
+ if (this._inheritsFrom) {
+ this._inheritsFrom = Object.assign({}, this._originalInheritsFrom);
+ this._inheritFromFilter = this._inheritsFrom.name;
+ }
for (const key of Object.keys(this._local)) {
if (this._local[key].added) {
delete this._local[key];
@@ -297,7 +355,19 @@
remove: {},
};
+ const inheritFromChanged =
+ // Inherit from changed
+ (this._originalInheritsFrom &&
+ this._originalInheritsFrom.id !== this._inheritsFrom.id) ||
+ // Inherit froma dded (did not have one initially);
+ (!this._originalInheritsFrom && this._inheritsFrom
+ && this._inheritsFrom.id);
+
this._recursivelyUpdateAddRemoveObj(this._local, addRemoveObj);
+
+ if (inheritFromChanged) {
+ addRemoveObj.parent = this._inheritsFrom.id;
+ }
return addRemoveObj;
},
@@ -318,20 +388,25 @@
_handleSaveForReview() {
const addRemoveObj = this._computeAddAndRemove();
-
// If there are no changes, don't actually save.
if (!Object.keys(addRemoveObj.add).length &&
- !Object.keys(addRemoveObj.remove).length) {
+ !Object.keys(addRemoveObj.remove).length &&
+ !addRemoveObj.parent) {
this.dispatchEvent(new CustomEvent('show-alert',
{detail: {message: NOTHING_TO_SAVE}, bubbles: true}));
return;
}
- return this.$.restAPI.setProjectAccessRightsForReview(this.repo, {
+ const obj = {
add: addRemoveObj.add,
remove: addRemoveObj.remove,
- }).then(change => {
- Gerrit.Nav.navigateToChange(change);
- });
+ };
+ if (addRemoveObj.parent) {
+ obj.parent = addRemoveObj.parent;
+ }
+ return this.$.restAPI.setProjectAccessRightsForReview(this.repo, obj)
+ .then(change => {
+ Gerrit.Nav.navigateToChange(change);
+ });
},
_computeShowSaveClass(editing) {
@@ -339,8 +414,19 @@
return 'visible';
},
- _computeAdminClass(isAdmin, canUpload) {
- return isAdmin || canUpload ? 'admin' : '';
+ _computeEditingClass(editing) {
+ return editing ? 'editing': '';
+ },
+
+ _computeMainClass(isAdmin, canUpload, editing) {
+ const classList = [];
+ if (isAdmin || canUpload) {
+ classList.push('admin');
+ }
+ if (editing) {
+ classList.push('editing');
+ }
+ return classList.join(' ');
},
_computeParentHref(repoName) {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
index 988fc7c..66fece3 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
@@ -194,23 +194,48 @@
'/admin/repos/test-repo,access');
});
- test('_computeAdminClass', () => {
+ test('_computeMainClass', () => {
let isAdmin = true;
- assert.equal(element._computeAdminClass(isAdmin), 'admin');
+ const editing = true;
+ const canUpload = false;
+ assert.equal(element._computeMainClass(isAdmin, canUpload), 'admin');
+ assert.equal(element._computeMainClass(isAdmin, canUpload, editing),
+ 'admin editing');
isAdmin = false;
- assert.equal(element._computeAdminClass(isAdmin), '');
+ assert.equal(element._computeMainClass(isAdmin, canUpload), '');
+ assert.equal(element._computeMainClass(isAdmin, canUpload, editing),
+ 'editing');
});
test('inherit section', () => {
+ element._local = {};
sandbox.stub(element, '_computeParentHref');
- assert.isNotOk(Polymer.dom(element.root).querySelector('#inheritsFrom'));
+ // Nothing should appear when no inherit from and not in edit mode.
+ assert.equal(getComputedStyle(element.$.inheritsFrom).display, 'none');
+ // The autocomplete should be hidden, and the link should be displayed.
assert.isFalse(element._computeParentHref.called);
+ // When it edit mode, the autocomplete should appear.
+ element._editing = true;
+ // When editing, the autocomplete should still not be shown.
+ assert.equal(getComputedStyle(element.$.inheritsFrom).display, 'none');
+ element._editing = false;
element._inheritsFrom = {
name: 'another-repo',
};
+ // When there is a parent project, the link should be displayed.
flushAsynchronousOperations();
- assert.isOk(Polymer.dom(element.root).querySelector('#inheritsFrom'));
+ assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
+ assert.notEqual(getComputedStyle(element.$.inheritFromName).display,
+ 'none');
+ assert.equal(getComputedStyle(element.$.editInheritFromInput).display,
+ 'none');
assert.isTrue(element._computeParentHref.called);
+ element._editing = true;
+ // When editing, the autocomplete should be shown.
+ assert.notEqual(getComputedStyle(element.$.inheritsFrom).display, 'none');
+ assert.equal(getComputedStyle(element.$.inheritFromName).display, 'none');
+ assert.notEqual(getComputedStyle(element.$.editInheritFromInput).display,
+ 'none');
});
test('_computeLoadingClass', () => {
@@ -240,14 +265,25 @@
assert.equal(getComputedStyle(element.$.saveBtn).display, 'none');
assert.notEqual(getComputedStyle(element.$.editBtn).display, 'none');
assert.equal(element.$.editBtn.innerText, 'EDIT');
+ assert.equal(getComputedStyle(element.$.editInheritFromInput).display,
+ 'none');
+ element._inheritsFrom = {
+ id: 'test-project',
+ };
+ flushAsynchronousOperations();
+ assert.equal(getComputedStyle(element.$$('#editInheritFromInput'))
+ .display, 'none');
MockInteractions.tap(element.$.editBtn);
+ flushAsynchronousOperations();
// Edit button changes to Cancel button, and Save button is visible but
// disabled.
assert.equal(element.$.editBtn.innerText, 'CANCEL');
assert.notEqual(getComputedStyle(element.$.saveBtn).display, 'none');
assert.isTrue(element.$.saveBtn.disabled);
+ assert.notEqual(getComputedStyle(element.$$('#editInheritFromInput'))
+ .display, 'none');
// Save button should be enabled after access is modified
element.fire('access-modified');
@@ -286,7 +322,18 @@
assert.isTrue(element._handleAccessModified.called);
});
- test('_handleAccessModified called with event fired', () => {
+ test('_handleAccessModified called when parent changes', () => {
+ element._inheritsFrom = {
+ id: 'test-project',
+ };
+ flushAsynchronousOperations();
+ element.$$('#editInheritFromInput').fire('commit');
+ sandbox.spy(element, '_handleAccessModified');
+ element.fire('access-modified');
+ assert.isTrue(element._handleAccessModified.called);
+ });
+
+ test('_handleSaveForReview', () => {
const saveStub =
sandbox.stub(element.$.restAPI, 'setProjectAccessRightsForReview');
sandbox.stub(element, '_computeAddAndRemove').returns({
@@ -335,6 +382,18 @@
assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
});
+ test('_handleSaveForReview parent change', () => {
+ element._inheritsFrom = {
+ id: 'test-project',
+ };
+ element._originalInheritsFrom = {
+ id: 'test-project-original',
+ };
+ assert.deepEqual(element._computeAddAndRemove(), {
+ parent: 'test-project', add: {}, remove: {},
+ });
+ });
+
test('_handleSaveForReview rules', () => {
// Delete a rule.
element._local['refs/*'].permissions.owner.rules[123].deleted = true;
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
index 025ff79..c94a716 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
+++ b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry.html
@@ -37,9 +37,9 @@
threshold="[[suggestFrom]]"
query="[[query]]"
allow-non-suggested-values="[[allowAnyInput]]"
+ no-debounce
on-commit="_handleInputCommit"
clear-on-commit
- no-debounce
warn-uncommitted
text="{{_inputText}}">
</gr-autocomplete>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
index d097d01..d54bbc3 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.html
@@ -57,6 +57,10 @@
align-items: center;
color: #777;
}
+ #confirmSubmitDialog .changeSubject {
+ margin: 1em;
+ text-align: center;
+ }
@media screen and (max-width: 50em) {
#mainContent,
section,
@@ -207,6 +211,25 @@
Do you really want to delete the edit?
</div>
</gr-confirm-dialog>
+ <gr-confirm-dialog
+ id="confirmSubmitDialog"
+ class="confirmDialog"
+ confirm-label="Submit"
+ confirm-on-enter
+ on-cancel="_handleConfirmDialogCancel"
+ on-confirm="_handleSubmitConfirm">
+ <div class="header" slot="header">
+ Submit Change
+ </div>
+ <div class="main" slot="main">
+ <p>
+ Are you sure you want to to <strong>submit</strong> this change?
+ </p>
+ <p class="changeSubject">
+ <strong>[[change.subject]]</strong>
+ </p>
+ </div>
+ </gr-confirm-dialog>
</gr-overlay>
<gr-js-api-interface id="jsAPI"></gr-js-api-interface>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
index 5c27757..6d8108f 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions.js
@@ -203,6 +203,7 @@
* branch: string,
* id: string,
* project: string,
+ * subject: string,
* }}
*/
change: Object,
@@ -909,10 +910,9 @@
this._handleDownloadTap();
break;
case RevisionActions.SUBMIT:
- if (!this._canSubmitChange()) {
- return;
- }
- // eslint-disable-next-line no-fallthrough
+ if (!this._canSubmitChange()) { return; }
+ this._showActionDialog(this.$.confirmSubmitDialog);
+ break;
default:
this._fireAction(this._prependSlash(key),
this.revisionActions[key], true);
@@ -1035,6 +1035,12 @@
this._fireAction('/edit', this.actions.deleteEdit, false);
},
+ _handleSubmitConfirm() {
+ if (!this._canSubmitChange()) { return; }
+ this._hideAllDialogs();
+ this._fireAction('/submit', this.revisionActions.submit, true);
+ },
+
_getActionOverflowIndex(type, key) {
return this._overflowActions.findIndex(action => {
return action.type === type && action.key === key;
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index ca6337a..f09acd4 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -254,6 +254,7 @@
.returns(Promise.resolve('test'));
sandbox.stub(element, 'fetchChangeUpdates',
() => { return Promise.resolve({isLatest: true}); });
+ const showDialogStub = sandbox.stub(element, '_showActionDialog');
element.change = {
revisions: {
rev1: {_number: 1},
@@ -267,13 +268,29 @@
assert.ok(submitButton);
MockInteractions.tap(submitButton);
- // Upon success it should fire the reload-change event.
- element.addEventListener('reload-change', () => {
- done();
- });
+ assert.isTrue(showDialogStub.calledOnce);
+ assert.equal(showDialogStub.lastCall.args[0],
+ element.$.confirmSubmitDialog);
+ done();
});
});
+ test('_handleSubmitConfirm', () => {
+ const fireStub = sandbox.stub(element, '_fireAction');
+ sandbox.stub(element, '_canSubmitChange').returns(true);
+ element._handleSubmitConfirm();
+ assert.isTrue(fireStub.calledOnce);
+ assert.deepEqual(fireStub.lastCall.args,
+ ['/submit', element.revisionActions.submit, true]);
+ });
+
+ test('_handleSubmitConfirm when not able to submit', () => {
+ const fireStub = sandbox.stub(element, '_fireAction');
+ sandbox.stub(element, '_canSubmitChange').returns(false);
+ element._handleSubmitConfirm();
+ assert.isFalse(fireStub.called);
+ });
+
test('submit change with plugin hook', done => {
sandbox.stub(element, '_canSubmitChange',
() => { return false; });
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 42404aa..ff4562b 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -611,6 +611,7 @@
on-send="_handleReplySent"
on-cancel="_handleReplyCancel"
on-autogrow="_handleReplyAutogrow"
+ on-send-disabled-changed="_resetReplyOverlayFocusStops"
hidden$="[[!_loggedIn]]">
</gr-reply-dialog>
</gr-overlay>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 9be7db4..5c6859d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -999,7 +999,7 @@
*/
_openReplyDialog(opt_section) {
this.$.replyOverlay.open().then(() => {
- this.$.replyOverlay.setFocusStops(this.$.replyDialog.getFocusStops());
+ this._resetReplyOverlayFocusStops();
this.$.replyDialog.open(opt_section);
Polymer.dom.flush();
this.$.replyOverlay.center();
@@ -1554,5 +1554,9 @@
_handleStopEditTap() {
Gerrit.Nav.navigateToChange(this._change, this._patchRange.patchNum);
},
+
+ _resetReplyOverlayFocusStops() {
+ this.$.replyOverlay.setFocusStops(this.$.replyDialog.getFocusStops());
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
index 8e179cc..5d69499 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog.html
@@ -105,6 +105,7 @@
<gr-autocomplete
id="parentInput"
query="[[_query]]"
+ no-debounce
text="{{_inputText}}"
on-tap="_handleEnterChangeNumberTap"
on-commit="_handleBaseSelected"
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
index 24815db..b92fe1d 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
@@ -166,7 +166,8 @@
test('input text change triggers function', () => {
sandbox.spy(element, '_getRecentChanges');
- element._inputText = '1';
+ element.$.parentInput.noDebounce = true;
+ element.$.parentInput.text = '1';
assert.isTrue(element._getRecentChanges.calledOnce);
element._inputText = '12';
assert.isTrue(element._getRecentChanges.calledTwice);
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
index 0cfae94..af3acc4 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
@@ -115,7 +115,7 @@
sandbox.restore();
});
- test('send blocked when invalid email is supplied to ccs', () => {
+ test('_submit blocked when invalid email is supplied to ccs', () => {
const sendStub = sandbox.stub(element, 'send').returns(Promise.resolve());
// Stub the below function to avoid side effects from the send promise
// resolving.
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index 088875f..8225dac 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -64,6 +64,8 @@
display: flex;
justify-content: space-between;
position: sticky;
+ /* @see Issue 8602 */
+ z-index: 1;
}
.actions .right gr-button {
margin-left: 1em;
@@ -291,9 +293,10 @@
class="action cancel"
on-tap="_cancelTapHandler">Cancel</gr-button>
<gr-button
+ id="sendButton"
link
primary
- disabled="[[_computeSendButtonDisabled(_sendButtonLabel, diffDrafts, draft, _reviewersMutated, _labelsChanged, _includeComments)]]"
+ disabled="[[_sendDisabled]]"
class="action send"
has-tooltip
title$="[[_computeSendButtonTooltip(canBeStarted)]]"
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 3875e25..e8c720a 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -52,6 +52,8 @@
// googlesource.com.
const START_REVIEW_MESSAGE = 'This change is ready for review.';
+ const EMPTY_REPLY_MESSAGE = 'Cannot send an empty reply.';
+
Polymer({
is: 'gr-reply-dialog',
@@ -87,6 +89,12 @@
* @event comment-refresh
*/
+ /**
+ * Fires when the state of the send button (enabled/disabled) changes.
+ *
+ * @event send-disabled-changed
+ */
+
properties: {
/**
* @type {{ _number: number, removable_reviewers: Array }}
@@ -206,6 +214,12 @@
type: String,
value: '',
},
+ _sendDisabled: {
+ type: Boolean,
+ computed: '_computeSendButtonDisabled(_sendButtonLabel, diffDrafts, ' +
+ 'draft, _reviewersMutated, _labelsChanged, _includeComments)',
+ observer: '_sendDisabledChanged',
+ },
},
FocusTarget,
@@ -273,9 +287,10 @@
},
getFocusStops() {
+ const end = this._sendDisabled ? this.$.cancelButton : this.$.sendButton;
return {
start: this.$.reviewers.focusStart,
- end: this.$.sendButton,
+ end,
};
},
@@ -726,6 +741,13 @@
// the text field of the CC entry.
return;
}
+ if (this._sendDisabled) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ bubbles: true,
+ detail: {message: EMPTY_REPLY_MESSAGE},
+ }));
+ return;
+ }
return this.send(this._includeComments, this.canBeStarted)
.then(keepReviewers => {
this._purgeReviewersPendingRemove(false, keepReviewers);
@@ -856,5 +878,9 @@
setPluginMessage(message) {
this._pluginMessage = message;
},
+
+ _sendDisabledChanged(sendDisabled) {
+ this.dispatchEvent(new CustomEvent('send-disabled-changed'));
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index b9eebdc..41d7b49 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -1100,6 +1100,33 @@
assert.isFalse(fn('Send', {}, '', false, true, false));
});
+ test('_submit blocked when no mutations exist', () => {
+ const sendStub = sandbox.stub(element, 'send').returns(Promise.resolve());
+ // Stub the below function to avoid side effects from the send promise
+ // resolving.
+ sandbox.stub(element, '_purgeReviewersPendingRemove');
+ element.diffDrafts = {};
+ flushAsynchronousOperations();
+
+ MockInteractions.tap(element.$$('gr-button.send'));
+ assert.isFalse(sendStub.called);
+
+ element.diffDrafts = {test: true};
+ flushAsynchronousOperations();
+
+ MockInteractions.tap(element.$$('gr-button.send'));
+ assert.isTrue(sendStub.called);
+ });
+
+ test('getFocusStops', () => {
+ // Setting diffDrafts to an empty object causes _sendDisabled to be
+ // computed to false.
+ element.diffDrafts = {};
+ assert.equal(element.getFocusStops().end, element.$.cancelButton);
+ element.diffDrafts = {test: true};
+ assert.equal(element.getFocusStops().end, element.$.sendButton);
+ });
+
test('setPluginMessage', () => {
element.setPluginMessage('foo');
assert.equal(element.$.pluginMessage.textContent, 'foo');
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
index 169fd85..fdd730d 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
@@ -48,7 +48,6 @@
id="searchInput"
text="{{_inputVal}}"
query="[[query]]"
- debounce-wait="200"
on-commit="_handleInputCommit"
allow-non-suggested-values
multi
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
index 017f2a3..997e1ba 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.js
@@ -288,6 +288,7 @@
const button = this._createElement('gr-button', 'showContext');
button.setAttribute('link', true);
+ button.setAttribute('no-uppercase', true);
let text;
const groups = []; // The groups that replace this one if tapped.
@@ -306,7 +307,7 @@
[0, contextLines.length - context]);
}
- button.textContent = text;
+ Polymer.dom(button).textContent = text;
button.addEventListener('tap', e => {
e.detail = {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 78efae7..129bff1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -95,7 +95,7 @@
let buttons = td.querySelectorAll('gr-button.showContext');
assert.equal(buttons.length, 1);
- assert.equal(buttons[0].textContent, 'Show 10 common lines');
+ assert.equal(Polymer.dom(buttons[0]).textContent, 'Show 10 common lines');
// Add another line.
line.contextGroup.lines.push('lorem upsum');
@@ -105,9 +105,9 @@
buttons = td.querySelectorAll('gr-button.showContext');
assert.equal(buttons.length, 3);
- assert.equal(buttons[0].textContent, '+10↑');
- assert.equal(buttons[1].textContent, 'Show 11 common lines');
- assert.equal(buttons[2].textContent, '+10↓');
+ assert.equal(Polymer.dom(buttons[0]).textContent, '+10↑');
+ assert.equal(Polymer.dom(buttons[1]).textContent, 'Show 11 common lines');
+ assert.equal(Polymer.dom(buttons[2]).textContent, '+10↓');
});
test('newlines 1', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
index 99cfb03..fc23837 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -160,12 +160,15 @@
.contextControl {
background-color: #fff7d4;
border: 1px solid #f6e6a5;
- color: rgba(0,0,0.54);
}
.contextControl gr-button {
display: inline-block;
- font-family: var(--monospace-font-family);
text-decoration: none;
+ --gr-button-color: rgba(0,0,0,.54);
+ --gr-button: {
+ font-family: var(--monospace-font-family);
+ padding: .2em;
+ }
}
.contextControl td:not(.lineNum) {
text-align: center;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
index 9841564..f280c33 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -321,7 +321,7 @@
},
_handleTap(e) {
- const el = Polymer.dom(e).rootTarget;
+ const el = Polymer.dom(e).localTarget;
if (el.classList.contains('showContext')) {
this.$.diffBuilder.showContext(e.detail.groups, e.detail.section);
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
index 9d85d37..c67a2af 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls_test.html
@@ -61,12 +61,14 @@
suite('edit button CUJ', () => {
let navStubs;
+ let openAutoCcmplete;
setup(() => {
navStubs = [
sandbox.stub(Gerrit.Nav, 'getEditUrlForDiff'),
sandbox.stub(Gerrit.Nav, 'navigateToRelativeUrl'),
];
+ openAutoCcmplete = element.$.openDialog.querySelector('gr-autocomplete');
});
test('_isValidPath', () => {
@@ -84,8 +86,8 @@
assert.isTrue(element._hideAllDialogs.called);
assert.isTrue(element.$.openDialog.disabled);
assert.isFalse(queryStub.called);
- element.$.openDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ openAutoCcmplete.noDebounce = true;
+ openAutoCcmplete.text = 'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.openDialog.disabled);
MockInteractions.tap(element.$.openDialog.$$('gr-button[primary]'));
@@ -100,8 +102,8 @@
MockInteractions.tap(element.$$('#open'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.openDialog.disabled);
- element.$.openDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ openAutoCcmplete.noDebounce = true;
+ openAutoCcmplete.text = 'src/test.cpp';
assert.isFalse(element.$.openDialog.disabled);
MockInteractions.tap(element.$.openDialog.$$('gr-button'));
for (const stub of navStubs) { assert.isFalse(stub.called); }
@@ -114,10 +116,13 @@
suite('delete button CUJ', () => {
let navStub;
let deleteStub;
+ let deleteAutocomplete;
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
deleteStub = sandbox.stub(element.$.restAPI, 'deleteFileInChangeEdit');
+ deleteAutocomplete =
+ element.$.deleteDialog.querySelector('gr-autocomplete');
});
test('delete', () => {
@@ -126,8 +131,8 @@
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.deleteDialog.disabled);
assert.isFalse(queryStub.called);
- element.$.deleteDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ deleteAutocomplete.noDebounce = true;
+ deleteAutocomplete.text = 'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.deleteDialog.disabled);
MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
@@ -149,8 +154,8 @@
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.deleteDialog.disabled);
assert.isFalse(queryStub.called);
- element.$.deleteDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ deleteAutocomplete.noDebounce = true;
+ deleteAutocomplete.text = 'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isFalse(element.$.deleteDialog.disabled);
MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
@@ -183,10 +188,13 @@
suite('rename button CUJ', () => {
let navStub;
let renameStub;
+ let renameAutocomplete;
setup(() => {
navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
renameStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit');
+ renameAutocomplete =
+ element.$.renameDialog.querySelector('gr-autocomplete');
});
test('rename', () => {
@@ -195,8 +203,8 @@
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.renameDialog.disabled);
assert.isFalse(queryStub.called);
- element.$.renameDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ renameAutocomplete.noDebounce = true;
+ renameAutocomplete.text = 'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
@@ -223,8 +231,8 @@
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.renameDialog.disabled);
assert.isFalse(queryStub.called);
- element.$.renameDialog.querySelector('gr-autocomplete').text =
- 'src/test.cpp';
+ renameAutocomplete.noDebounce = true;
+ renameAutocomplete.text = 'src/test.cpp';
assert.isTrue(queryStub.called);
assert.isTrue(element.$.renameDialog.disabled);
diff --git a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
index 31715c1..963d2e3 100644
--- a/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-cla-view/gr-cla-view.html
@@ -56,7 +56,8 @@
margin: 0 2em;
padding: .5em;
}
- .alreadySubmittedText.hide {
+ .alreadySubmittedText.hide,
+ .hideAgreementsTextBox {
display: none;
}
main {
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
index 0b8c331..558140f 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor.html
@@ -96,7 +96,6 @@
<th>
<gr-autocomplete
id="newProject"
- debounce-wait="200"
query="[[_query]]"
threshold="1"
placeholder="Project"></gr-autocomplete>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
index 287ccdd..1eaad4e 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.js
@@ -18,6 +18,7 @@
'use strict';
const TOKENIZE_REGEX = /(?:[^\s"]+|"[^"]*")+/g;
+ const DEBOUNCE_WAIT_MS = 200;
Polymer({
is: 'gr-autocomplete',
@@ -88,7 +89,6 @@
text: {
type: String,
value: '',
- observer: '_updateSuggestions',
notify: true,
},
@@ -133,12 +133,11 @@
},
/**
- * The number of milliseconds to use as the debounce wait time. If null,
- * no debouncing is used.
+ * When true, querying for suggestions is not debounced w/r/t keypresses
*/
- debounceWait: {
- type: Number,
- value: null,
+ noDebounce: {
+ type: Boolean,
+ value: false,
},
/** @type {?} */
@@ -168,6 +167,7 @@
observers: [
'_maybeOpenDropdown(_suggestions, _focused)',
+ '_updateSuggestions(text, threshold, noDebounce)',
],
attached() {
@@ -176,6 +176,7 @@
detached() {
this.unlisten(document.body, 'tap', '_handleBodyTap');
+ this.cancelDebouncer('update-suggestions');
},
get focusStart() {
@@ -219,7 +220,7 @@
_onInputFocus() {
this._focused = true;
- this._updateSuggestions();
+ this._updateSuggestions(this.text, this.threshold, this.noDebounce);
this.$.input.classList.remove('warnUncommitted');
// Needed so that --paper-input-container-input updated style is applied.
this.updateStyles();
@@ -232,14 +233,13 @@
this.updateStyles();
},
- _updateSuggestions() {
+ _updateSuggestions(text, threshold, noDebounce) {
if (this._disableSuggestions) { return; }
- if (this.text === undefined || this.text.length < this.threshold) {
+ if (text === undefined || text.length < threshold) {
this._suggestions = [];
this.value = '';
return;
}
- const text = this.text;
const update = () => {
this.query(text).then(suggestions => {
@@ -258,10 +258,10 @@
});
};
- if (this.debounceWait) {
- this.debounce('update-suggestions', update, this.debounceWait);
- } else {
+ if (noDebounce) {
update();
+ } else {
+ this.debounce('update-suggestions', update, DEBOUNCE_WAIT_MS);
}
},
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index dfb0a0d..872f79f 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -28,7 +28,7 @@
<test-fixture id="basic">
<template>
- <gr-autocomplete></gr-autocomplete>
+ <gr-autocomplete no-debounce></gr-autocomplete>
</template>
</test-fixture>
@@ -236,7 +236,7 @@
assert.isTrue(queryStub.called);
});
- test('debounceWait debounces the query', () => {
+ test('noDebounce=false debounces the query', () => {
const queryStub = sandbox.spy(() => {
return Promise.resolve([]);
});
@@ -244,11 +244,11 @@
const debounceStub = sandbox.stub(element, 'debounce',
(name, cb) => { callback = cb; });
element.query = queryStub;
- element.debounceWait = 100;
+ element.noDebounce = false;
element.text = 'a';
assert.isFalse(queryStub.called);
assert.isTrue(debounceStub.called);
- assert.equal(debounceStub.lastCall.args[2], 100);
+ assert.equal(debounceStub.lastCall.args[2], 200);
assert.isFunction(callback);
callback();
assert.isTrue(queryStub.called);
@@ -261,9 +261,7 @@
});
test('undefined or empty text results in no suggestions', () => {
- sandbox.spy(element, '_updateSuggestions');
- element.text = undefined;
- assert(element._updateSuggestions.calledOnce);
+ element._updateSuggestions(undefined, 0, null);
assert.equal(element._suggestions.length, 0);
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index 8867b9d..0119a5e 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -338,6 +338,13 @@
sandbox.stub(Gerrit, '_pluginInstalled');
Gerrit.install(p => { plugin = p; }, '0.1',
'http://test.com/plugins/testplugin/static/test.js');
+
+ // testplugin has already been installed once (in setup).
+ assert.isFalse(Gerrit._pluginInstalled.called);
+
+ // testplugin2 plugin has not yet been installed.
+ Gerrit.install(p => { plugin = p; }, '0.1',
+ 'http://test.com/plugins/testplugin2/static/test.js');
assert.isTrue(Gerrit._pluginInstalled.calledOnce);
});
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
index efb88401..372f899 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js
@@ -430,14 +430,23 @@
const src = opt_src || (document.currentScript &&
(document.currentScript.src || document.currentScript.baseURI));
const name = getPluginNameFromUrl(new URL(src));
- const plugin = plugins[name] || new Plugin(src);
+ const existingPlugin = plugins[name];
+ const plugin = existingPlugin || new Plugin(src);
try {
callback(plugin);
plugins[name] = plugin;
} catch (e) {
console.warn(`${name} install failed: ${e.name}: ${e.message}`);
}
- Gerrit._pluginInstalled();
+ // Don't double count plugins that may have an html and js install.
+ // TODO(beckysiegel) remove name check once name issue is resolved.
+ // If there isn't a name, it's due to an issue with the polyfill for
+ // html imports in Safari/Firefox. In this case, other plugin related
+ // features may still be broken, but still make sure to call.
+ // _pluginInstalled.
+ if (!name || !existingPlugin) {
+ Gerrit._pluginInstalled();
+ }
};
Gerrit.getLoggedIn = function() {
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/ba-linkify.js b/polygerrit-ui/app/elements/shared/gr-linked-text/ba-linkify.js
deleted file mode 100644
index 26dacd6..0000000
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/ba-linkify.js
+++ /dev/null
@@ -1,191 +0,0 @@
-/*!
- * JavaScript Linkify - v0.3 - 6/27/2009
- * http://benalman.com/projects/javascript-linkify/
- *
- * Copyright (c) 2009 "Cowboy" Ben Alman
- * Dual licensed under the MIT and GPL licenses.
- * http://benalman.com/about/license/
- *
- * Some regexps adapted from http://userscripts.org/scripts/review/7122
- */
-
-// Script: JavaScript Linkify: Process links in text!
-//
-// *Version: 0.3, Last updated: 6/27/2009*
-//
-// Project Home - http://benalman.com/projects/javascript-linkify/
-// GitHub - http://github.com/cowboy/javascript-linkify/
-// Source - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.js
-// (Minified) - http://github.com/cowboy/javascript-linkify/raw/master/ba-linkify.min.js (2.8kb)
-//
-// About: License
-//
-// Copyright (c) 2009 "Cowboy" Ben Alman,
-// Dual licensed under the MIT and GPL licenses.
-// http://benalman.com/about/license/
-//
-// Permission is hereby granted, free of charge, to any person
-// obtaining a copy of this software and associated documentation
-// files (the "Software"), to deal in the Software without
-// restriction, including without limitation the rights to use,
-// copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the
-// Software is furnished to do so, subject to the following
-// conditions:
-
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-// OTHER DEALINGS IN THE SOFTWARE.
-
-window.linkify = (function(){
- var
- SCHEME = "[a-z\\d.-]+://",
- IPV4 = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
- HOSTNAME = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
- TLD = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
- HOST_OR_IP = "(?:" + HOSTNAME + TLD + "|" + IPV4 + ")",
- PATH = "(?:[;/][^#?<>\\s]*)?",
- QUERY_FRAG = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",
- URI1 = "\\b" + SCHEME + "[^<>\\s]+",
- URI2 = "\\b" + HOST_OR_IP + PATH + QUERY_FRAG + "(?!\\w)",
-
- MAILTO = "mailto:",
- EMAIL = "(?:" + MAILTO + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + HOST_OR_IP + QUERY_FRAG + "(?!\\w)",
-
- URI_RE = new RegExp( "(?:" + URI1 + "|" + URI2 + "|" + EMAIL + ")", "ig" ),
- SCHEME_RE = new RegExp( "^" + SCHEME, "i" ),
-
- quotes = {
- "'": "`",
- '>': '<',
- ')': '(',
- ']': '[',
- '}': '{',
- '»': '«',
- '›': '‹'
- },
-
- default_options = {
- callback: function( text, href ) {
- return href ? '<a href="' + href + '" title="' + href + '">' + text + '</a>' : text;
- },
- punct_regexp: /(?:[!?.,:;'"]|(?:&|&)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
- };
-
- return function( txt, options ) {
- options = options || {};
-
- // Temp variables.
- var arr,
- i,
- link,
- href,
-
- // Output HTML.
- html = '',
-
- // Store text / link parts, in order, for re-combination.
- parts = [],
-
- // Used for keeping track of indices in the text.
- idx_prev,
- idx_last,
- idx,
- link_last,
-
- // Used for trimming trailing punctuation and quotes from links.
- matches_begin,
- matches_end,
- quote_begin,
- quote_end;
-
- // Initialize options.
- for ( i in default_options ) {
- if ( options[ i ] === undefined ) {
- options[ i ] = default_options[ i ];
- }
- }
-
- // Find links.
- while ( arr = URI_RE.exec( txt ) ) {
-
- link = arr[0];
- idx_last = URI_RE.lastIndex;
- idx = idx_last - link.length;
-
- // Not a link if preceded by certain characters.
- if ( /[\/:]/.test( txt.charAt( idx - 1 ) ) ) {
- continue;
- }
-
- // Trim trailing punctuation.
- do {
- // If no changes are made, we don't want to loop forever!
- link_last = link;
-
- quote_end = link.substr( -1 )
- quote_begin = quotes[ quote_end ];
-
- // Ending quote character?
- if ( quote_begin ) {
- matches_begin = link.match( new RegExp( '\\' + quote_begin + '(?!$)', 'g' ) );
- matches_end = link.match( new RegExp( '\\' + quote_end, 'g' ) );
-
- // If quotes are unbalanced, remove trailing quote character.
- if ( ( matches_begin ? matches_begin.length : 0 ) < ( matches_end ? matches_end.length : 0 ) ) {
- link = link.substr( 0, link.length - 1 );
- idx_last--;
- }
- }
-
- // Ending non-quote punctuation character?
- if ( options.punct_regexp ) {
- link = link.replace( options.punct_regexp, function(a){
- idx_last -= a.length;
- return '';
- });
- }
- } while ( link.length && link !== link_last );
-
- href = link;
-
- // Add appropriate protocol to naked links.
- if ( !SCHEME_RE.test( href ) ) {
- href = ( href.indexOf( '@' ) !== -1 ? ( !href.indexOf( MAILTO ) ? '' : MAILTO )
- : !href.indexOf( 'irc.' ) ? 'irc://'
- : !href.indexOf( 'ftp.' ) ? 'ftp://'
- : 'http://' )
- + href;
- }
-
- // Push preceding non-link text onto the array.
- if ( idx_prev != idx ) {
- parts.push([ txt.slice( idx_prev, idx ) ]);
- idx_prev = idx_last;
- }
-
- // Push massaged link onto the array
- parts.push([ link, href ]);
- };
-
- // Push remaining non-link text onto the array.
- parts.push([ txt.substr( idx_prev ) ]);
-
- // Process the array items.
- for ( i = 0; i < parts.length; i++ ) {
- html += options.callback.apply( window, parts[i] );
- }
-
- // In case of catastrophic failure, return the original text;
- return html || txt;
- };
-
-})();
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
index 13443ee..ec589fe 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text.html
@@ -18,7 +18,7 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../styles/shared-styles.html">
-<script src="ba-linkify.js"></script>
+<script src="../../../bower_components/ba-linkify/ba-linkify.js"></script>
<script src="link-text-parser.js"></script>
<dom-module id="gr-linked-text">
<template>
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index 80b01ce..8917fd7 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -101,6 +101,9 @@
--paper-toggle-button-checked-bar-color: var(--color-link);
--paper-toggle-button-checked-button-color: var(--color-link);
}
+ strong {
+ font-family: var(--font-family-bold);
+ }
</style>
</template>
</dom-module>
diff --git a/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index 8f87503..c58edb7 100644
--- a/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -5,20 +5,6 @@
--
-- *********************************************************************
--- AccountGroupMemberAccess
--- @PrimaryKey covers: byAccount
-CREATE INDEX account_group_members_byGroup
-ON account_group_members (group_id);
-
-
--- *********************************************************************
--- AccountGroupByIdAccess
--- @PrimaryKey covers: byGroup
-CREATE INDEX account_group_id_byInclude
-ON account_group_by_id (include_uuid);
-
-
--- *********************************************************************
-- ApprovalCategoryAccess
-- too small to bother indexing
diff --git a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
index 57b1a4a..7f0f1bd 100644
--- a/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ b/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
@@ -6,21 +6,6 @@
--
-- *********************************************************************
--- AccountGroupMemberAccess
--- @PrimaryKey covers: byAccount
-CREATE INDEX account_group_members_byGroup
-ON account_group_members (group_id)
-#
-
--- *********************************************************************
--- AccountGroupIncludeByUuidAccess
--- @PrimaryKey covers: byGroup
-CREATE INDEX acc_gr_incl_by_uuid_byInclude
-ON account_group_by_id (include_uuid)
-#
-
-
--- *********************************************************************
-- ApprovalCategoryAccess
-- too small to bother indexing
diff --git a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index e1d88ef..f2f24e1 100644
--- a/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -52,20 +52,6 @@
--
-- *********************************************************************
--- AccountGroupMemberAccess
--- @PrimaryKey covers: byAccount
-CREATE INDEX account_group_members_byGroup
-ON account_group_members (group_id);
-
-
--- *********************************************************************
--- AccountGroupByIdAccess
--- @PrimaryKey covers: byGroup
-CREATE INDEX account_group_id_byInclude
-ON account_group_by_id (include_uuid);
-
-
--- *********************************************************************
-- ApprovalCategoryAccess
-- too small to bother indexing