Merge "Add gr-icons to polygerrit"
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index 3f6acf6..121fcad 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -124,12 +124,6 @@
or invalid value) and votes that are not permitted for the user are
silently ignored.
---strict-labels::
- Require ability to vote on all specified labels before reviewing change.
- If the vote is invalid (invalid label or invalid name), the vote is not
- permitted for the user, or the vote is on an outdated or closed patch set,
- return an error instead of silently discarding the vote.
-
--tag::
-t::
Apply a 'TAG' to the change message, votes, and inline comments. The 'TAG'
diff --git a/Documentation/database-setup.txt b/Documentation/database-setup.txt
index fc43b97..d35772e 100644
--- a/Documentation/database-setup.txt
+++ b/Documentation/database-setup.txt
@@ -55,7 +55,7 @@
[[createdb_mysql]]
=== MySQL
-Requirements: MySQL version 5.5 or later.
+Requirements: MySQL version 5.1 or later.
This option is also more complicated than the H2 option. Just as with
PostgreSQL it's also recommended for larger installations.
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 983e5e2..f7028e0 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -483,6 +483,15 @@
submitted by Rebase Always and Cherry Pick submit strategies as well as
change being queried with COMMIT_FOOTERS option.
+[[merge-super-set-computation]]
+== Merge Super Set Computation
+
+The algorithm to compute the merge super set to detect changes that
+should be submitted together can be customized by implementing
+`com.google.gerrit.server.git.MergeSuperSetComputation`.
+MergeSuperSetComputation is a DynamicItem, so Gerrit may only have one
+implementation.
+
[[receive-pack]]
== Receive Pack Initializers
diff --git a/Documentation/dev-release.txt b/Documentation/dev-release.txt
index ddf7e7f..53ded48 100644
--- a/Documentation/dev-release.txt
+++ b/Documentation/dev-release.txt
@@ -79,19 +79,19 @@
Before doing the release build, the `GERRIT_VERSION` in the `version.bzl`
file must be updated, e.g. change it from `$version-SNAPSHOT` to `$version`.
-In addition the version must be updated in a number of pom.xml files.
+In addition the version must be updated in a number of `*_pom.xml` files.
To do this run the `./tools/version.py` script and provide the new
version as parameter, e.g.:
----
- ./tools/version.py 2.5
+ version=2.15
+ ./tools/version.py $version
----
Commit the changes and create a signed release tag on the new commit:
----
- version=2.15
git tag -s -m "v$version" "v$version"
----
@@ -137,7 +137,7 @@
configuration] for deploying to Maven Central
* Make sure that the version is updated in the `version.bzl` file and in
-the `pom.xml` files as described in the link:#update-versions[Update
+the `*_pom.xml` files as described in the link:#update-versions[Update
Versions and Create Release Tag] section.
* Push the WAR to Maven Central:
@@ -192,7 +192,7 @@
+
Use this URL for further testing of the artifacts in this repository,
e.g. to try building a plugin against the plugin API in this repository
-update the version in the `pom.xml` and configure the repository:
+update the version in the `*_pom.xml` and configure the repository:
+
----
<repositories>
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 50afe40..d9e356d 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6693,13 +6693,6 @@
|`robot_comments` |optional|
The robot comments that should be added as a map that maps a file path
to a list of link:#robot-comment-input[RobotCommentInput] entities.
-|`strict_labels` |`true` if not set|
-Whether all labels are required to be within the user's permitted ranges
-based on access controls. +
-If `true`, attempting to use a label not granted to the user will fail
-the entire modify operation early. +
-If `false`, the operation will execute anyway, but the proposed labels
-will be modified to be the "best" value allowed by the access controls.
|`drafts` |optional|
Draft handling that defines how draft comments are handled that are
already in the database but that were not also described in this
diff --git a/Documentation/rest-api-groups.txt b/Documentation/rest-api-groups.txt
index 7eac992..45c5e34 100644
--- a/Documentation/rest-api-groups.txt
+++ b/Documentation/rest-api-groups.txt
@@ -124,6 +124,40 @@
* `MEMBERS`: include list of direct group members.
--
+==== Find groups that are owned by another group
+
+By setting `ownedBy` and specifying the link:#group-id[\{group-id\}] of another
+group, it is possible to find all the groups for which the owning group is the
+given group.
+
+.Request
+----
+ GET /groups/?ownedBy=7ca042f4d5847936fcb90ca91057673157fd06fc HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "MyProject-Committers": {
+ "id": "9999c971bb4ab872aab759d8c49833ee6b9ff320",
+ "url": "#/admin/groups/uuid-9999c971bb4ab872aab759d8c49833ee6b9ff320",
+ "options": {
+ "visible_to_all": true
+ },
+ "description":"contains all committers for MyProject",
+ "group_id": 551,
+ "owner": "MyProject-Owners",
+ "owner_id": "7ca042f4d5847936fcb90ca91057673157fd06fc",
+ "created_on": "2013-02-01 09:59:32.126000000"
+ }
+ }
+----
+
==== Check if a group is owned by the calling user
By setting the option `owned` and specifying a group to inspect with
the option `group`/`g`, it is possible to find out if this group is
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
index e2e29c9..b6eaa22 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/InProcessProtocol.java
@@ -39,9 +39,7 @@
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.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.RequestScopePropagator;
@@ -286,7 +284,7 @@
private static class Receive implements ReceivePackFactory<Context> {
private final Provider<CurrentUser> userProvider;
- private final ProjectControl.GenericFactory projectControlFactory;
+ private final ProjectCache projectCache;
private final AsyncReceiveCommits.Factory factory;
private final TransferConfig config;
private final DynamicSet<ReceivePackInitializer> receivePackInitializers;
@@ -297,7 +295,7 @@
@Inject
Receive(
Provider<CurrentUser> userProvider,
- ProjectControl.GenericFactory projectControlFactory,
+ ProjectCache projectCache,
AsyncReceiveCommits.Factory factory,
TransferConfig config,
DynamicSet<ReceivePackInitializer> receivePackInitializers,
@@ -305,7 +303,7 @@
ThreadLocalRequestContext threadContext,
PermissionBackend permissionBackend) {
this.userProvider = userProvider;
- this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
this.factory = factory;
this.config = config;
this.receivePackInitializers = receivePackInitializers;
@@ -333,8 +331,14 @@
throw new RuntimeException(e);
}
try {
- ProjectControl ctl = projectControlFactory.controlFor(req.project, userProvider.get());
- AsyncReceiveCommits arc = factory.create(ctl, db, null, ImmutableSetMultimap.of());
+ IdentifiedUser identifiedUser = userProvider.get().asIdentifiedUser();
+ ProjectState projectState = projectCache.checkedGet(req.project);
+ if (projectState == null) {
+ throw new RuntimeException(String.format("project %s not found", req.project));
+ }
+
+ AsyncReceiveCommits arc =
+ factory.create(projectState, identifiedUser, db, null, ImmutableSetMultimap.of());
ReceivePack rp = arc.getReceivePack();
Capable r = arc.canUpload();
@@ -342,17 +346,17 @@
throw new ServiceNotAuthorizedException();
}
- rp.setRefLogIdent(ctl.getUser().asIdentifiedUser().newRefLogIdent());
+ rp.setRefLogIdent(identifiedUser.newRefLogIdent());
rp.setTimeout(config.getTimeout());
rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit());
for (ReceivePackInitializer initializer : receivePackInitializers) {
- initializer.init(ctl.getProject().getNameKey(), rp);
+ initializer.init(projectState.getNameKey(), rp);
}
rp.setPostReceiveHook(PostReceiveHookChain.newChain(Lists.newArrayList(postReceiveHooks)));
return rp;
- } catch (NoSuchProjectException | IOException e) {
+ } catch (IOException e) {
throw new RuntimeException(e);
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index baa0a68..eef9d87 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -1582,7 +1582,6 @@
// Exact request format made by GWT UI at ddc6b7160fe416fed9e7e3180489d44c82fd64f8.
ReviewInput in = new ReviewInput();
in.labels = ImmutableMap.of("Code-Review", (short) 0);
- in.strictLabels = true;
in.drafts = DraftHandling.PUBLISH_ALL_REVISIONS;
in.message = "comment";
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).review(in);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
index 305a2b0..2118f29 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/group/GroupsIT.java
@@ -491,6 +491,33 @@
}
@Test
+ public void getGroupsByOwner() throws Exception {
+ String parent = createGroup("test-parent");
+ List<String> children =
+ Arrays.asList(createGroup("test-child1", parent), createGroup("test-child2", parent));
+
+ // By UUID
+ List<GroupInfo> owned =
+ gApi.groups().list().withOwnedBy(getFromCache(parent).getGroupUUID().get()).get();
+ assertThat(owned.stream().map(g -> g.name).collect(toList()))
+ .containsExactlyElementsIn(children);
+
+ // By name
+ owned = gApi.groups().list().withOwnedBy(parent).get();
+ assertThat(owned.stream().map(g -> g.name).collect(toList()))
+ .containsExactlyElementsIn(children);
+
+ // By group that does not own any others
+ owned = gApi.groups().list().withOwnedBy(owned.get(0).id).get();
+ assertThat(owned).isEmpty();
+
+ // By non-existing group
+ exception.expect(UnprocessableEntityException.class);
+ exception.expectMessage("Group Not Found: does-not-exist");
+ gApi.groups().list().withOwnedBy("does-not-exist").get();
+ }
+
+ @Test
public void onlyVisibleGroupsReturned() throws Exception {
String newGroupName = name("newGroup");
GroupInput in = new GroupInput();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 94dcf31..afa3147 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -204,7 +204,7 @@
ConfigInput input = createTestConfigInput();
setApiUser(user);
exception.expect(AuthException.class);
- exception.expectMessage("restricted to project owner");
+ exception.expectMessage("write config not permitted");
gApi.projects().name(project.get()).config(input);
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
index 36843a5..9b88e0d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/ImpersonationIT.java
@@ -146,7 +146,6 @@
ReviewInput in = new ReviewInput();
in.onBehalfOf = user.id.toString();
- in.strictLabels = true;
in.label("Not-A-Label", 5);
exception.expect(BadRequestException.class);
@@ -155,23 +154,6 @@
}
@Test
- public void voteOnBehalfOfInvalidLabelIgnoredWithoutStrictLabels() throws Exception {
- allowCodeReviewOnBehalfOf();
- PushOneCommit.Result r = createChange();
- RevisionApi revision = gApi.changes().id(r.getChangeId()).current();
-
- ReviewInput in = new ReviewInput();
- in.onBehalfOf = user.id.toString();
- in.strictLabels = false;
- in.label("Code-Review", 1);
- in.label("Not-A-Label", 5);
-
- revision.review(in);
-
- assertThat(gApi.changes().id(r.getChangeId()).get().labels).doesNotContainKey("Not-A-Label");
- }
-
- @Test
public void voteOnBehalfOfLabelNotPermitted() throws Exception {
ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
LabelType verified = Util.verified();
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailSenderIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
index 43f046a..4f51e1f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/server/mail/MailSenderIT.java
@@ -18,6 +18,7 @@
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.server.mail.send.EmailHeader;
+import java.net.URI;
import java.util.Map;
import org.junit.Test;
@@ -31,9 +32,7 @@
// Check that the custom address was added as Reply-To
assertThat(sender.getMessages()).hasSize(1);
Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
- assertThat(headers.get("Reply-To")).isInstanceOf(EmailHeader.String.class);
- assertThat(((EmailHeader.String) headers.get("Reply-To")).getString())
- .isEqualTo("custom@gerritcodereview.com");
+ assertThat(headerString(headers, "Reply-To")).isEqualTo("custom@gerritcodereview.com");
}
@Test
@@ -42,7 +41,30 @@
// Check that the user's email was added as Reply-To
assertThat(sender.getMessages()).hasSize(1);
Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
- assertThat(headers.get("Reply-To")).isInstanceOf(EmailHeader.String.class);
- assertThat(((EmailHeader.String) headers.get("Reply-To")).getString()).contains(user.email);
+ assertThat(headerString(headers, "Reply-To")).contains(user.email);
+ }
+
+ @Test
+ public void outgoingMailHasListHeaders() throws Exception {
+ String changeId = createChangeWithReview(user);
+ // Check that the mail has the expected headers
+ assertThat(sender.getMessages()).hasSize(1);
+ Map<String, EmailHeader> headers = sender.getMessages().iterator().next().headers();
+ String hostname = URI.create(canonicalWebUrl.get()).getHost();
+ String listId = String.format("<gerrit-%s.%s>", project.get(), hostname);
+ String unsubscribeLink = String.format("<%ssettings>", canonicalWebUrl.get());
+ String threadId =
+ String.format(
+ "<gerrit.%s.%s@%s>",
+ gApi.changes().id(changeId).get().created.getTime(), changeId, hostname);
+ assertThat(headerString(headers, "List-Id")).isEqualTo(listId);
+ assertThat(headerString(headers, "List-Unsubscribe")).isEqualTo(unsubscribeLink);
+ assertThat(headerString(headers, "In-Reply-To")).isEqualTo(threadId);
+ }
+
+ private String headerString(Map<String, EmailHeader> headers, String name) {
+ EmailHeader header = headers.get(name);
+ assertThat(header).isInstanceOf(EmailHeader.String.class);
+ return ((EmailHeader.String) header).getString();
}
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
index 567d9ba..0243ba3 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/groups/Groups.java
@@ -80,6 +80,7 @@
private String substring;
private String suggest;
private String regex;
+ private String ownedBy;
public List<GroupInfo> get() throws RestApiException {
Map<String, GroupInfo> map = getAsMap();
@@ -160,6 +161,11 @@
return this;
}
+ public ListRequest withOwnedBy(String ownedBy) {
+ this.ownedBy = ownedBy;
+ return this;
+ }
+
public EnumSet<ListGroupsOption> getOptions() {
return options;
}
@@ -203,6 +209,10 @@
public String getSuggest() {
return suggest;
}
+
+ public String getOwnedBy() {
+ return ownedBy;
+ }
}
/**
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
index 113651b..f851d5e 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ReviewInput.java
@@ -60,7 +60,6 @@
private native void init() /*-{
this.labels = {};
- this.strict_labels = true;
}-*/;
public final native void prePost() /*-{
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 329beab..efb8fc5 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -34,8 +34,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.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -80,7 +80,7 @@
public class GitOverHttpServlet extends GitServlet {
private static final long serialVersionUID = 1L;
- private static final String ATT_CONTROL = ProjectControl.class.getName();
+ private static final String ATT_STATE = ProjectState.class.getName();
private static final String ATT_ARC = AsyncReceiveCommits.class.getName();
private static final String ID_CACHE = "adv_bases";
@@ -145,18 +145,18 @@
private final GitRepositoryManager manager;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> userProvider;
- private final ProjectControl.GenericFactory projectControlFactory;
+ private final ProjectCache projectCache;
@Inject
Resolver(
GitRepositoryManager manager,
PermissionBackend permissionBackend,
Provider<CurrentUser> userProvider,
- ProjectControl.GenericFactory projectControlFactory) {
+ ProjectCache projectCache) {
this.manager = manager;
this.permissionBackend = permissionBackend;
this.userProvider = userProvider;
- this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
}
@Override
@@ -182,13 +182,11 @@
try {
Project.NameKey nameKey = new Project.NameKey(projectName);
- ProjectControl pc;
- try {
- pc = projectControlFactory.controlFor(nameKey, user);
- } catch (NoSuchProjectException err) {
- throw new RepositoryNotFoundException(projectName);
+ ProjectState state = projectCache.checkedGet(nameKey);
+ if (state == null) {
+ throw new RepositoryNotFoundException(nameKey.get());
}
- req.setAttribute(ATT_CONTROL, pc);
+ req.setAttribute(ATT_STATE, state);
try {
permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
@@ -231,9 +229,9 @@
up.setTimeout(config.getTimeout());
up.setPreUploadHook(PreUploadHookChain.newChain(Lists.newArrayList(preUploadHooks)));
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
- ProjectControl pc = (ProjectControl) req.getAttribute(ATT_CONTROL);
+ ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
for (UploadPackInitializer initializer : uploadPackInitializers) {
- initializer.init(pc.getProject().getNameKey(), up);
+ initializer.init(state.getNameKey(), up);
}
return up;
}
@@ -243,15 +241,18 @@
private final VisibleRefFilter.Factory refFilterFactory;
private final UploadValidators.Factory uploadValidatorsFactory;
private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> userProvider;
@Inject
UploadFilter(
VisibleRefFilter.Factory refFilterFactory,
UploadValidators.Factory uploadValidatorsFactory,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ Provider<CurrentUser> userProvider) {
this.refFilterFactory = refFilterFactory;
this.uploadValidatorsFactory = uploadValidatorsFactory;
this.permissionBackend = permissionBackend;
+ this.userProvider = userProvider;
}
@Override
@@ -259,13 +260,13 @@
throws IOException, ServletException {
// The Resolver above already checked READ access for us.
Repository repo = ServletUtils.getRepository(request);
- ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
+ ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
UploadPack up = (UploadPack) request.getAttribute(ServletUtils.ATTRIBUTE_HANDLER);
try {
permissionBackend
- .user(pc.getUser())
- .project(pc.getProject().getNameKey())
+ .user(userProvider)
+ .project(state.getNameKey())
.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
GitSmartHttpTools.sendError(
@@ -280,10 +281,10 @@
// We use getRemoteHost() here instead of getRemoteAddr() because REMOTE_ADDR
// may have been overridden by a proxy server -- we'll try to avoid this.
UploadValidators uploadValidators =
- uploadValidatorsFactory.create(pc.getProject(), repo, request.getRemoteHost());
+ uploadValidatorsFactory.create(state.getProject(), repo, request.getRemoteHost());
up.setPreUploadHook(
PreUploadHookChain.newChain(Lists.newArrayList(up.getPreUploadHook(), uploadValidators)));
- up.setAdvertiseRefsHook(refFilterFactory.create(pc.getProjectState(), repo));
+ up.setAdvertiseRefsHook(refFilterFactory.create(state, repo));
next.doFilter(request, response);
}
@@ -297,23 +298,27 @@
static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> {
private final AsyncReceiveCommits.Factory factory;
+ private final Provider<CurrentUser> userProvider;
@Inject
- ReceiveFactory(AsyncReceiveCommits.Factory factory) {
+ ReceiveFactory(AsyncReceiveCommits.Factory factory, Provider<CurrentUser> userProvider) {
this.factory = factory;
+ this.userProvider = userProvider;
}
@Override
public ReceivePack create(HttpServletRequest req, Repository db)
throws ServiceNotAuthorizedException {
- final ProjectControl pc = (ProjectControl) req.getAttribute(ATT_CONTROL);
+ final ProjectState state = (ProjectState) req.getAttribute(ATT_STATE);
- if (!(pc.getUser().isIdentifiedUser())) {
+ if (!(userProvider.get().isIdentifiedUser())) {
// Anonymous users are not permitted to push.
throw new ServiceNotAuthorizedException();
}
- AsyncReceiveCommits arc = factory.create(pc, db, null, ImmutableSetMultimap.of());
+ AsyncReceiveCommits arc =
+ factory.create(
+ state, userProvider.get().asIdentifiedUser(), db, null, ImmutableSetMultimap.of());
ReceivePack rp = arc.getReceivePack();
req.setAttribute(ATT_ARC, arc);
return rp;
@@ -331,13 +336,16 @@
static class ReceiveFilter implements Filter {
private final Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache;
private final PermissionBackend permissionBackend;
+ private final Provider<CurrentUser> userProvider;
@Inject
ReceiveFilter(
@Named(ID_CACHE) Cache<AdvertisedObjectsCacheKey, Set<ObjectId>> cache,
- PermissionBackend permissionBackend) {
+ PermissionBackend permissionBackend,
+ Provider<CurrentUser> userProvider) {
this.cache = cache;
this.permissionBackend = permissionBackend;
+ this.userProvider = userProvider;
}
@Override
@@ -348,13 +356,12 @@
AsyncReceiveCommits arc = (AsyncReceiveCommits) request.getAttribute(ATT_ARC);
ReceivePack rp = arc.getReceivePack();
rp.getAdvertiseRefsHook().advertiseRefs(rp);
- ProjectControl pc = (ProjectControl) request.getAttribute(ATT_CONTROL);
- Project.NameKey projectName = pc.getProject().getNameKey();
+ ProjectState state = (ProjectState) request.getAttribute(ATT_STATE);
try {
permissionBackend
- .user(pc.getUser())
- .project(pc.getProject().getNameKey())
+ .user(userProvider)
+ .project(state.getNameKey())
.check(ProjectPermission.RUN_RECEIVE_PACK);
} catch (AuthException e) {
GitSmartHttpTools.sendError(
@@ -382,13 +389,13 @@
return;
}
- if (!(pc.getUser().isIdentifiedUser())) {
+ if (!(userProvider.get().isIdentifiedUser())) {
chain.doFilter(request, response);
return;
}
AdvertisedObjectsCacheKey cacheKey =
- AdvertisedObjectsCacheKey.create(pc.getUser().getAccountId(), projectName);
+ AdvertisedObjectsCacheKey.create(userProvider.get().getAccountId(), state.getNameKey());
if (isGet) {
cache.invalidate(cacheKey);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
index 1d116b7..6210385 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/UrlModule.java
@@ -134,7 +134,7 @@
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
- toGerrit(req.getRequestURI(), req, rsp);
+ toGerrit(req.getRequestURI().substring(req.getContextPath().length()), req, rsp);
}
});
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
index 129ccf8..62232db 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -30,7 +30,6 @@
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.SetParent;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -58,7 +57,6 @@
@Inject
ChangeProjectAccess(
ProjectAccessFactory.Factory projectAccessFactory,
- ProjectControl.Factory projectControlFactory,
ProjectCache projectCache,
GroupBackend groupBackend,
MetaDataUpdate.User metaDataUpdateFactory,
@@ -74,19 +72,18 @@
@Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
@Nullable @Assisted String message) {
super(
- projectControlFactory,
groupBackend,
metaDataUpdateFactory,
allProjects,
setParent,
user.get(),
- permissionBackend,
projectName,
base,
sectionList,
parentProjectName,
message,
contributorAgreements,
+ permissionBackend,
true);
this.projectAccessFactory = projectAccessFactory;
this.projectCache = projectCache;
@@ -95,10 +92,7 @@
@Override
protected ProjectAccess updateProjectConfig(
- ProjectControl projectControl,
- ProjectConfig config,
- MetaDataUpdate md,
- boolean parentProjectUpdate)
+ ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
throws IOException, NoSuchProjectException, ConfigInvalidException,
PermissionBackendException {
RevCommit commit = config.commit(md);
@@ -108,7 +102,7 @@
RefNames.REFS_CONFIG,
base,
commit.getId(),
- projectControl.getUser().asIdentifiedUser().getAccount());
+ user.asIdentifiedUser().getAccount());
projectCache.evict(config.getProject());
return projectAccessFactory.create(projectName).call();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
index 8c873a6..98f6b3f 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -48,7 +48,7 @@
import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.assistedinject.Assisted;
@@ -70,7 +70,6 @@
private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
- private final ProjectControl.GenericFactory projectControlFactory;
private final GroupControl.Factory groupControlFactory;
private final MetaDataUpdate.Server metaDataUpdateFactory;
private final AllProjectsName allProjectsName;
@@ -84,7 +83,6 @@
ProjectCache projectCache,
PermissionBackend permissionBackend,
Provider<CurrentUser> user,
- ProjectControl.GenericFactory projectControlFactory,
GroupControl.Factory groupControlFactory,
MetaDataUpdate.Server metaDataUpdateFactory,
AllProjectsName allProjectsName,
@@ -94,7 +92,6 @@
this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.user = user;
- this.projectControlFactory = projectControlFactory;
this.groupControlFactory = groupControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.allProjectsName = allProjectsName;
@@ -107,7 +104,7 @@
public ProjectAccess call()
throws NoSuchProjectException, IOException, ConfigInvalidException,
PermissionBackendException {
- ProjectControl pc = checkProjectControl();
+ ProjectState projectState = checkProjectState();
// Load the current configuration from the repository, ensuring its the most
// recent version available. If it differs from what was in the project
@@ -120,11 +117,11 @@
md.setMessage("Update group names\n");
config.commit(md);
projectCache.evict(config.getProject());
- pc = checkProjectControl();
+ projectState = checkProjectState();
} else if (config.getRevision() != null
- && !config.getRevision().equals(pc.getProjectState().getConfig().getRevision())) {
+ && !config.getRevision().equals(projectState.getConfig().getRevision())) {
projectCache.evict(config.getProject());
- pc = checkProjectControl();
+ projectState = checkProjectState();
}
}
@@ -133,11 +130,17 @@
Map<AccountGroup.UUID, Boolean> visibleGroups = new HashMap<>();
PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
+ boolean canWriteProjectConfig = true;
+ try {
+ perm.check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ canWriteProjectConfig = false;
+ }
for (AccessSection section : config.getAccessSections()) {
String name = section.getName();
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (pc.isOwner()) {
+ if (canWriteProjectConfig) {
local.add(section);
ownerOf.add(name);
@@ -218,11 +221,11 @@
detail.setLocal(local);
detail.setOwnerOf(ownerOf);
detail.setCanUpload(
- pc.isOwner()
+ canWriteProjectConfig
|| (checkReadConfig && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)));
- detail.setConfigVisible(pc.isOwner() || checkReadConfig);
+ detail.setConfigVisible(canWriteProjectConfig || checkReadConfig);
detail.setGroupInfo(buildGroupInfo(local));
- detail.setLabelTypes(pc.getProjectState().getLabelTypes());
+ detail.setLabelTypes(projectState.getLabelTypes());
detail.setFileHistoryLinks(getConfigFileLogLinks(projectName.get()));
return detail;
}
@@ -252,15 +255,15 @@
return Maps.filterEntries(infos, in -> in.getValue() != null);
}
- private ProjectControl checkProjectControl()
+ private ProjectState checkProjectState()
throws NoSuchProjectException, IOException, PermissionBackendException {
- ProjectControl pc = projectControlFactory.controlFor(projectName, user.get());
+ ProjectState state = projectCache.checkedGet(projectName);
try {
permissionBackend.user(user).project(projectName).check(ProjectPermission.ACCESS);
} catch (AuthException e) {
throw new NoSuchProjectException(projectName);
}
- return pc;
+ return state;
}
private static boolean check(PermissionBackend.ForProject ctx, String ref, RefPermission perm)
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index e92af7c..5bde72b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -14,6 +14,7 @@
package com.google.gerrit.httpd.rpc.project;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.common.ProjectAccessUtil.mergeSections;
import com.google.common.base.MoreObjects;
@@ -38,10 +39,10 @@
import com.google.gerrit.server.git.ProjectConfig;
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.permissions.RefPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.RefPattern;
import com.google.gerrit.server.project.SetParent;
import com.google.gwtorm.server.OrmException;
@@ -56,44 +57,43 @@
public abstract class ProjectAccessHandler<T> extends Handler<T> {
- private final ProjectControl.Factory projectControlFactory;
protected final GroupBackend groupBackend;
+ protected final Project.NameKey projectName;
+ protected final ObjectId base;
+ protected final CurrentUser user;
+
private final MetaDataUpdate.User metaDataUpdateFactory;
private final AllProjectsName allProjects;
private final Provider<SetParent> setParent;
private final ContributorAgreementsChecker contributorAgreements;
- private final CurrentUser user;
private final PermissionBackend permissionBackend;
-
- protected final Project.NameKey projectName;
- protected final ObjectId base;
- private List<AccessSection> sectionList;
private final Project.NameKey parentProjectName;
+
protected String message;
+
+ private List<AccessSection> sectionList;
private boolean checkIfOwner;
+ private Boolean canWriteConfig;
protected ProjectAccessHandler(
- ProjectControl.Factory projectControlFactory,
GroupBackend groupBackend,
MetaDataUpdate.User metaDataUpdateFactory,
AllProjectsName allProjects,
Provider<SetParent> setParent,
CurrentUser user,
- PermissionBackend permissionBackend,
Project.NameKey projectName,
ObjectId base,
List<AccessSection> sectionList,
Project.NameKey parentProjectName,
String message,
ContributorAgreementsChecker contributorAgreements,
+ PermissionBackend permissionBackend,
boolean checkIfOwner) {
- this.projectControlFactory = projectControlFactory;
this.groupBackend = groupBackend;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.allProjects = allProjects;
this.setParent = setParent;
this.user = user;
- this.permissionBackend = permissionBackend;
this.projectName = projectName;
this.base = base;
@@ -101,6 +101,7 @@
this.parentProjectName = parentProjectName;
this.message = message;
this.contributorAgreements = contributorAgreements;
+ this.permissionBackend = permissionBackend;
this.checkIfOwner = checkIfOwner;
}
@@ -109,10 +110,8 @@
throws NoSuchProjectException, IOException, ConfigInvalidException, InvalidNameException,
NoSuchGroupException, OrmException, UpdateParentFailedException,
PermissionDeniedException, PermissionBackendException {
- final ProjectControl projectControl = projectControlFactory.controlFor(projectName);
-
try {
- contributorAgreements.check(projectName, projectControl.getUser());
+ contributorAgreements.check(projectName, user);
} catch (AuthException e) {
throw new PermissionDeniedException(e.getMessage());
}
@@ -126,7 +125,7 @@
String name = section.getName();
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (checkIfOwner && !projectControl.isOwner()) {
+ if (checkIfOwner && !canWriteConfig()) {
continue;
}
replace(config, toDelete, section);
@@ -144,7 +143,7 @@
for (String name : toDelete) {
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (!checkIfOwner || projectControl.isOwner()) {
+ if (!checkIfOwner || canWriteConfig()) {
config.remove(config.getAccessSection(name));
}
@@ -161,8 +160,8 @@
setParent
.get()
.validateParentUpdate(
- projectControl.getProject().getNameKey(),
- projectControl.getUser().asIdentifiedUser(),
+ projectName,
+ user.asIdentifiedUser(),
MoreObjects.firstNonNull(parentProjectName, allProjects).get(),
checkIfOwner);
} catch (AuthException e) {
@@ -186,17 +185,14 @@
md.setMessage("Modify access rules\n");
}
- return updateProjectConfig(projectControl, config, md, parentProjectUpdate);
+ return updateProjectConfig(config, md, parentProjectUpdate);
} catch (RepositoryNotFoundException notFound) {
throw new NoSuchProjectException(projectName);
}
}
protected abstract T updateProjectConfig(
- ProjectControl projectControl,
- ProjectConfig config,
- MetaDataUpdate md,
- boolean parentProjectUpdate)
+ ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
throws IOException, NoSuchProjectException, ConfigInvalidException, OrmException,
PermissionDeniedException, PermissionBackendException;
@@ -229,4 +225,20 @@
ref.setUUID(group.getUUID());
}
}
+
+ /** Provide a local cache for {@code ProjectPermission.WRITE_CONFIG} capability. */
+ private boolean canWriteConfig() throws PermissionBackendException {
+ checkNotNull(user);
+
+ if (canWriteConfig != null) {
+ return canWriteConfig;
+ }
+ try {
+ permissionBackend.user(user).project(projectName).check(ProjectPermission.WRITE_CONFIG);
+ canWriteConfig = true;
+ } catch (AuthException e) {
+ canWriteConfig = false;
+ }
+ return canWriteConfig;
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index a8a120a..e7e0021 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -43,10 +43,10 @@
import com.google.gerrit.server.group.SystemGroupBackend;
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.permissions.RefPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.SetParent;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
@@ -83,7 +83,6 @@
@Inject
ReviewProjectAccess(
- final ProjectControl.Factory projectControlFactory,
PermissionBackend permissionBackend,
GroupBackend groupBackend,
MetaDataUpdate.User metaDataUpdateFactory,
@@ -104,19 +103,18 @@
@Nullable @Assisted("parentProjectName") Project.NameKey parentProjectName,
@Nullable @Assisted String message) {
super(
- projectControlFactory,
groupBackend,
metaDataUpdateFactory,
allProjects,
setParent,
user.get(),
- permissionBackend,
projectName,
base,
sectionList,
parentProjectName,
message,
contributorAgreements,
+ permissionBackend,
false);
this.db = db;
this.permissionBackend = permissionBackend;
@@ -133,27 +131,16 @@
@SuppressWarnings("deprecation")
@Override
protected Change.Id updateProjectConfig(
- ProjectControl projectControl,
- ProjectConfig config,
- MetaDataUpdate md,
- boolean parentProjectUpdate)
+ ProjectConfig config, MetaDataUpdate md, boolean parentProjectUpdate)
throws IOException, OrmException, PermissionDeniedException, PermissionBackendException {
- PermissionBackend.ForRef metaRef =
- permissionBackend
- .user(projectControl.getUser())
- .project(projectControl.getProject().getNameKey())
- .ref(RefNames.REFS_CONFIG);
- try {
- metaRef.check(RefPermission.READ);
- } catch (AuthException denied) {
+ PermissionBackend.ForProject perm = permissionBackend.user(user).project(config.getName());
+ if (!check(perm, ProjectPermission.READ_CONFIG)) {
throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
}
- if (!projectControl.isOwner()) {
- try {
- metaRef.check(RefPermission.CREATE_CHANGE);
- } catch (AuthException denied) {
- throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
- }
+
+ 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);
}
md.setInsertChangeId(true);
@@ -169,8 +156,7 @@
ObjectReader objReader = objInserter.newReader();
RevWalk rw = new RevWalk(objReader);
BatchUpdate bu =
- updateFactory.create(
- db, config.getProject().getNameKey(), projectControl.getUser(), TimeUtil.nowTs())) {
+ updateFactory.create(db, config.getProject().getNameKey(), user, TimeUtil.nowTs())) {
bu.setRepository(md.getRepository(), rw, objInserter);
bu.insertChange(
changeInserterFactory
@@ -227,4 +213,24 @@
}
}
}
+
+ private boolean check(PermissionBackend.ForRef perm, RefPermission p)
+ throws PermissionBackendException {
+ try {
+ perm.check(p);
+ return true;
+ } catch (AuthException denied) {
+ return false;
+ }
+ }
+
+ private boolean check(PermissionBackend.ForProject perm, ProjectPermission p)
+ throws PermissionBackendException {
+ try {
+ perm.check(p);
+ return true;
+ } catch (AuthException denied) {
+ return false;
+ }
+ }
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 6b5c157..1a5600f 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -66,6 +66,7 @@
import com.google.gerrit.server.config.RestCacheAdminModule;
import com.google.gerrit.server.events.StreamEventsApiListener;
import com.google.gerrit.server.git.GarbageCollectionModule;
+import com.google.gerrit.server.git.LocalMergeSuperSetComputation;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
@@ -472,6 +473,7 @@
if (testSysModule != null) {
modules.add(testSysModule);
}
+ modules.add(new LocalMergeSuperSetComputation.Module());
return cfgInjector.createChildInjector(modules);
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
index 7e6bf63..e3a1d66 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/JDBCInitializer.java
@@ -40,7 +40,7 @@
} else if (url.startsWith("jdbc:mariadb:")) {
database.set("driver", "org.mariadb.jdbc.Driver");
} else if (url.startsWith("jdbc:mysql:")) {
- database.set("driver", "com.mysql.cj.jdbc.Driver");
+ database.set("driver", "com.mysql.jdbc.Driver");
} else if (url.startsWith("jdbc:postgresql:")) {
database.set("driver", "org.postgresql.Driver");
}
diff --git a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
index 7429cea..a82edb32 100644
--- a/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
+++ b/gerrit-pgm/src/main/resources/com/google/gerrit/pgm/init/libraries.config
@@ -13,9 +13,9 @@
# limitations under the License.
[library "mysqlDriver"]
- name = MySQL Connector/J 6.0.6
- url = https://repo1.maven.org/maven2/mysql/mysql-connector-java/6.0.6/mysql-connector-java-6.0.6.jar
- sha1 = 1d19b184dbc596008cc71c83596f051c3ec4097f
+ name = MySQL Connector/J 5.1.43
+ url = https://repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.43/mysql-connector-java-5.1.43.jar
+ sha1 = dee9103eec0d877f3a21c82d4d9e9f4fbd2d6e0a
remove = mysql-connector-java-.*[.]jar
[library "mariadbDriver"]
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
index 63f7202..d7f6e30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/CmdLineParserModule.java
@@ -25,10 +25,10 @@
import com.google.gerrit.server.args4j.ChangeIdHandler;
import com.google.gerrit.server.args4j.ObjectIdHandler;
import com.google.gerrit.server.args4j.PatchSetIdHandler;
-import com.google.gerrit.server.args4j.ProjectControlHandler;
+import com.google.gerrit.server.args4j.ProjectHandler;
import com.google.gerrit.server.args4j.SocketAddressHandler;
import com.google.gerrit.server.args4j.TimestampHandler;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.util.cli.CmdLineParser;
import com.google.gerrit.util.cli.OptionHandlerUtil;
import com.google.gerrit.util.cli.OptionHandlers;
@@ -51,7 +51,7 @@
registerOptionHandler(Change.Id.class, ChangeIdHandler.class);
registerOptionHandler(ObjectId.class, ObjectIdHandler.class);
registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class);
- registerOptionHandler(ProjectControl.class, ProjectControlHandler.class);
+ registerOptionHandler(ProjectState.class, ProjectHandler.class);
registerOptionHandler(SocketAddress.class, SocketAddressHandler.class);
registerOptionHandler(Timestamp.class, TimestampHandler.class);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
index ee25d54..2971037 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReviewersUtil.java
@@ -111,8 +111,7 @@
private final AccountQueryBuilder accountQueryBuilder;
private final Provider<AccountQueryProcessor> queryProvider;
private final GroupBackend groupBackend;
- private final GroupMembers.Factory groupMembersFactory;
- private final Provider<CurrentUser> currentUser;
+ private final GroupMembers groupMembers;
private final ReviewerRecommender reviewerRecommender;
private final Metrics metrics;
@@ -122,8 +121,7 @@
AccountQueryBuilder accountQueryBuilder,
Provider<AccountQueryProcessor> queryProvider,
GroupBackend groupBackend,
- GroupMembers.Factory groupMembersFactory,
- Provider<CurrentUser> currentUser,
+ GroupMembers groupMembers,
ReviewerRecommender reviewerRecommender,
Metrics metrics) {
Set<FillOptions> fillOptions = EnumSet.of(FillOptions.SECONDARY_EMAILS);
@@ -131,9 +129,8 @@
this.accountLoader = accountLoaderFactory.create(fillOptions);
this.accountQueryBuilder = accountQueryBuilder;
this.queryProvider = queryProvider;
- this.currentUser = currentUser;
this.groupBackend = groupBackend;
- this.groupMembersFactory = groupMembersFactory;
+ this.groupMembers = groupMembers;
this.reviewerRecommender = reviewerRecommender;
this.metrics = metrics;
}
@@ -303,10 +300,7 @@
}
try {
- Set<Account> members =
- groupMembersFactory
- .create(currentUser.get())
- .listAccounts(group.getUUID(), project.getNameKey());
+ Set<Account> members = groupMembers.listAccounts(group.getUUID(), project.getNameKey());
if (members.isEmpty()) {
return result;
@@ -330,9 +324,7 @@
return result;
}
}
- } catch (NoSuchGroupException e) {
- return result;
- } catch (NoSuchProjectException e) {
+ } catch (NoSuchGroupException | NoSuchProjectException e) {
return result;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
index 12bd8ff..7971d30 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/StarredChangesUtil.java
@@ -420,7 +420,7 @@
public static Set<Integer> getStarredPatchSets(Set<String> labels, String label) {
return labels
.stream()
- .filter(l -> l.startsWith(label))
+ .filter(l -> l.startsWith(label + "/"))
.filter(l -> Ints.tryParse(l.substring(label.length() + 1)) != null)
.map(l -> Integer.valueOf(l.substring(label.length() + 1)))
.collect(toSet());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
index 1666eb1..ffb405c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteWatchedProjects.java
@@ -34,6 +34,7 @@
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.List;
+import java.util.Objects;
import org.eclipse.jgit.errors.ConfigInvalidException;
@Singleton
@@ -72,6 +73,7 @@
accountId,
input
.stream()
+ .filter(Objects::nonNull)
.map(w -> ProjectWatchKey.create(new Project.NameKey(w.project), w.filter))
.collect(toList()));
accountCache.evict(accountId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
index 4dc960d..f1f688c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupMembers.java
@@ -21,15 +21,14 @@
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.group.InternalGroup;
import com.google.gerrit.server.group.InternalGroupDescription;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
@@ -37,28 +36,22 @@
import java.util.Set;
public class GroupMembers {
- public interface Factory {
- GroupMembers create(CurrentUser currentUser);
- }
private final GroupCache groupCache;
private final GroupControl.Factory groupControlFactory;
private final AccountCache accountCache;
- private final ProjectControl.GenericFactory projectControl;
- private final CurrentUser currentUser;
+ private final ProjectCache projectCache;
@Inject
GroupMembers(
GroupCache groupCache,
GroupControl.Factory groupControlFactory,
AccountCache accountCache,
- ProjectControl.GenericFactory projectControl,
- @Assisted CurrentUser currentUser) {
+ ProjectCache projectCache) {
this.groupCache = groupCache;
this.groupControlFactory = groupControlFactory;
this.accountCache = accountCache;
- this.projectControl = projectControl;
- this.currentUser = currentUser;
+ this.projectCache = projectCache;
}
public Set<Account> listAccounts(AccountGroup.UUID groupUUID, Project.NameKey project)
@@ -88,11 +81,13 @@
return Collections.emptySet();
}
- final Iterable<AccountGroup.UUID> ownerGroups =
- projectControl.controlFor(project, currentUser).getProjectState().getAllOwners();
+ ProjectState projectState = projectCache.checkedGet(project);
+ if (projectState == null) {
+ throw new NoSuchProjectException(project);
+ }
final HashSet<Account> projectOwners = new HashSet<>();
- for (AccountGroup.UUID ownerGroup : ownerGroups) {
+ for (AccountGroup.UUID ownerGroup : projectState.getAllOwners()) {
if (!seen.contains(ownerGroup)) {
projectOwners.addAll(listAccounts(ownerGroup, project, seen));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
index e1e72ba..f439f7d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/groups/GroupsImpl.java
@@ -34,6 +34,7 @@
import com.google.gerrit.server.group.QueryGroups;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectsCollection;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -119,7 +120,8 @@
for (String project : req.getProjects()) {
try {
- list.addProject(projects.parse(tlr, IdString.fromDecoded(project)).getControl());
+ ProjectResource rsrc = projects.parse(tlr, IdString.fromDecoded(project));
+ list.addProject(rsrc.getProjectState());
} catch (Exception e) {
throw asRestApiException("Error looking up project " + project, e);
}
@@ -131,6 +133,10 @@
list.setVisibleToAll(req.getVisibleToAll());
+ if (req.getOwnedBy() != null) {
+ list.setOwnedBy(req.getOwnedBy());
+ }
+
if (req.getUser() != null) {
try {
list.setUser(accounts.parse(req.getUser()).getAccountId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
index 0d4afd6..12c4244 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/DashboardApiImpl.java
@@ -79,7 +79,8 @@
SetDashboardInput input = new SetDashboardInput();
input.id = id;
try {
- set.apply(DashboardResource.projectDefault(project.getControl()), input);
+ set.apply(
+ DashboardResource.projectDefault(project.getProjectState(), project.getUser()), input);
} catch (Exception e) {
String msg = String.format("Cannot %s default dashboard", id != null ? "set" : "remove");
throw asRestApiException(msg, e);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
index 9fd4d48..92bf46e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/projects/ProjectApiImpl.java
@@ -378,7 +378,11 @@
@Override
public ConfigInfo config(ConfigInput in) throws RestApiException {
- return putConfig.apply(checkExists(), in);
+ try {
+ return putConfig.apply(checkExists(), in);
+ } catch (Exception e) {
+ throw asRestApiException("Cannot list tags", e);
+ }
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectHandler.java
similarity index 83%
rename from gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectHandler.java
index 1823527..8959d97 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectControlHandler.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/args4j/ProjectHandler.java
@@ -22,7 +22,8 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+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;
@@ -36,23 +37,23 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ProjectControlHandler extends OptionHandler<ProjectControl> {
- private static final Logger log = LoggerFactory.getLogger(ProjectControlHandler.class);
+public class ProjectHandler extends OptionHandler<ProjectState> {
+ private static final Logger log = LoggerFactory.getLogger(ProjectHandler.class);
- private final ProjectControl.GenericFactory projectControlFactory;
+ private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
@Inject
- public ProjectControlHandler(
- ProjectControl.GenericFactory projectControlFactory,
+ public ProjectHandler(
+ ProjectCache projectCache,
PermissionBackend permissionBackend,
Provider<CurrentUser> user,
@Assisted final CmdLineParser parser,
@Assisted final OptionDef option,
- @Assisted final Setter<ProjectControl> setter) {
+ @Assisted final Setter<ProjectState> setter) {
super(parser, option, setter);
- this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.user = user;
}
@@ -77,20 +78,21 @@
String nameWithoutSuffix = ProjectUtil.stripGitSuffix(projectName);
Project.NameKey nameKey = new Project.NameKey(nameWithoutSuffix);
- ProjectControl control;
+ ProjectState state;
try {
- control = projectControlFactory.controlFor(nameKey, user.get());
+ state = projectCache.checkedGet(nameKey);
+ if (state == null) {
+ throw new CmdLineException(owner, String.format("project %s not found", nameWithoutSuffix));
+ }
permissionBackend.user(user).project(nameKey).check(ProjectPermission.ACCESS);
} catch (AuthException e) {
throw new CmdLineException(owner, new NoSuchProjectException(nameKey).getMessage());
- } catch (NoSuchProjectException e) {
- throw new CmdLineException(owner, e.getMessage());
} catch (PermissionBackendException | IOException e) {
log.warn("Cannot load project " + nameWithoutSuffix, e);
throw new CmdLineException(owner, new NoSuchProjectException(nameKey).getMessage());
}
- setter.addValue(control);
+ setter.addValue(state);
return 1;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Check.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Check.java
index 157928b..bf0fc83 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Check.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Check.java
@@ -22,11 +22,10 @@
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.permissions.GlobalPermission;
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.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -37,18 +36,12 @@
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final ChangeJson.Factory jsonFactory;
- private final ProjectControl.GenericFactory projectControlFactory;
@Inject
- Check(
- PermissionBackend permissionBackend,
- Provider<CurrentUser> user,
- ChangeJson.Factory json,
- ProjectControl.GenericFactory projectControlFactory) {
+ Check(PermissionBackend permissionBackend, Provider<CurrentUser> user, ChangeJson.Factory json) {
this.permissionBackend = permissionBackend;
this.user = user;
this.jsonFactory = json;
- this.projectControlFactory = projectControlFactory;
}
@Override
@@ -60,9 +53,9 @@
public Response<ChangeInfo> apply(ChangeResource rsrc, FixInput input)
throws RestApiException, OrmException, PermissionBackendException, NoSuchProjectException,
IOException {
- if (!rsrc.isUserOwner()
- && !projectControlFactory.controlFor(rsrc.getProject(), rsrc.getUser()).isOwner()) {
- permissionBackend.user(user).check(GlobalPermission.MAINTAIN_SERVER);
+ PermissionBackend.WithUser perm = permissionBackend.user(user);
+ if (!rsrc.isUserOwner()) {
+ perm.project(rsrc.getProject()).check(ProjectPermission.READ_CONFIG);
}
return Response.withMustRevalidate(newChangeJson().fix(input).format(rsrc));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index 4f03f37..8b84f2b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -49,7 +49,7 @@
import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -92,7 +92,7 @@
private final PatchSetInserter.Factory patchSetInserterFactory;
private final MergeUtil.Factory mergeUtilFactory;
private final ChangeNotes.Factory changeNotesFactory;
- private final ProjectControl.GenericFactory projectControlFactory;
+ private final ProjectCache projectCache;
private final ApprovalsUtil approvalsUtil;
private final ChangeMessagesUtil changeMessagesUtil;
private final NotifyUtil notifyUtil;
@@ -109,7 +109,7 @@
PatchSetInserter.Factory patchSetInserterFactory,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory changeNotesFactory,
- ProjectControl.GenericFactory projectControlFactory,
+ ProjectCache projectCache,
ApprovalsUtil approvalsUtil,
ChangeMessagesUtil changeMessagesUtil,
NotifyUtil notifyUtil) {
@@ -123,7 +123,7 @@
this.patchSetInserterFactory = patchSetInserterFactory;
this.mergeUtilFactory = mergeUtilFactory;
this.changeNotesFactory = changeNotesFactory;
- this.projectControlFactory = projectControlFactory;
+ this.projectCache = projectCache;
this.approvalsUtil = approvalsUtil;
this.changeMessagesUtil = changeMessagesUtil;
this.notifyUtil = notifyUtil;
@@ -197,10 +197,11 @@
String commitMessage = ChangeIdUtil.insertId(input.message, computedChangeId).trim() + '\n';
CodeReviewCommit cherryPickCommit;
- ProjectControl projectControl =
- projectControlFactory.controlFor(dest.getParentKey(), identifiedUser);
+ ProjectState projectState = projectCache.checkedGet(dest.getParentKey());
+ if (projectState == null) {
+ throw new NoSuchProjectException(dest.getParentKey());
+ }
try {
- ProjectState projectState = projectControl.getProjectState();
cherryPickCommit =
mergeUtilFactory
.create(projectState)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 58634a5..0022656 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -233,7 +233,7 @@
input.drafts = DraftHandling.DELETE;
}
if (input.labels != null) {
- checkLabels(revision, labelTypes, input.strictLabels, input.labels);
+ checkLabels(revision, labelTypes, input.labels);
}
if (input.comments != null) {
cleanUpComments(input.comments);
@@ -443,12 +443,9 @@
while (itr.hasNext()) {
Map.Entry<String, Short> ent = itr.next();
LabelType type = labelTypes.byLabel(ent.getKey());
- if (type == null && in.strictLabels) {
+ if (type == null) {
throw new BadRequestException(
String.format("label \"%s\" is not a configured label", ent.getKey()));
- } else if (type == null) {
- itr.remove();
- continue;
}
if (!caller.isInternalUser()) {
@@ -479,8 +476,7 @@
changeResourceFactory.create(rev.getNotes(), reviewer), rev.getPatchSet());
}
- private void checkLabels(
- RevisionResource rsrc, LabelTypes labelTypes, boolean strict, Map<String, Short> labels)
+ private void checkLabels(RevisionResource rsrc, LabelTypes labelTypes, Map<String, Short> labels)
throws BadRequestException, AuthException, PermissionBackendException {
PermissionBackend.ForChange perm = rsrc.permissions();
Iterator<Map.Entry<String, Short>> itr = labels.entrySet().iterator();
@@ -488,12 +484,8 @@
Map.Entry<String, Short> ent = itr.next();
LabelType lt = labelTypes.byLabel(ent.getKey());
if (lt == null) {
- if (strict) {
- throw new BadRequestException(
- String.format("label \"%s\" is not a configured label", ent.getKey()));
- }
- itr.remove();
- continue;
+ throw new BadRequestException(
+ String.format("label \"%s\" is not a configured label", ent.getKey()));
}
if (ent.getValue() == null || ent.getValue() == 0) {
@@ -503,23 +495,16 @@
}
if (lt.getValue(ent.getValue()) == null) {
- if (strict) {
- throw new BadRequestException(
- String.format("label \"%s\": %d is not a valid value", ent.getKey(), ent.getValue()));
- }
- itr.remove();
- continue;
+ throw new BadRequestException(
+ String.format("label \"%s\": %d is not a valid value", ent.getKey(), ent.getValue()));
}
short val = ent.getValue();
try {
perm.check(new LabelPermission.WithValue(lt, val));
} catch (AuthException e) {
- if (strict) {
- throw new AuthException(
- String.format("Applying label \"%s\": %d is restricted", lt.getName(), val));
- }
- ent.setValue(perm.squashThenCheck(lt, val));
+ throw new AuthException(
+ String.format("Applying label \"%s\": %d is restricted", lt.getName(), val));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
index f642aa4..3664293 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReviewers.java
@@ -91,7 +91,7 @@
private final PermissionBackend permissionBackend;
private final GroupsCollection groupsCollection;
- private final GroupMembers.Factory groupMembersFactory;
+ private final GroupMembers groupMembers;
private final AccountLoader.Factory accountLoaderFactory;
private final Provider<ReviewDb> dbProvider;
private final ChangeData.Factory changeDataFactory;
@@ -111,7 +111,7 @@
ReviewerResource.Factory reviewerFactory,
PermissionBackend permissionBackend,
GroupsCollection groupsCollection,
- GroupMembers.Factory groupMembersFactory,
+ GroupMembers groupMembers,
AccountLoader.Factory accountLoaderFactory,
Provider<ReviewDb> db,
ChangeData.Factory changeDataFactory,
@@ -130,7 +130,7 @@
this.reviewerFactory = reviewerFactory;
this.permissionBackend = permissionBackend;
this.groupsCollection = groupsCollection;
- this.groupMembersFactory = groupMembersFactory;
+ this.groupMembers = groupMembers;
this.accountLoaderFactory = accountLoaderFactory;
this.dbProvider = db;
this.changeDataFactory = changeDataFactory;
@@ -287,10 +287,7 @@
Set<Account.Id> reviewers = new HashSet<>();
Set<Account> members;
try {
- members =
- groupMembersFactory
- .create(rsrc.getUser())
- .listAccounts(group.getGroupUUID(), rsrc.getProject());
+ members = groupMembers.listAccounts(group.getGroupUUID(), rsrc.getProject());
} catch (NoSuchGroupException e) {
return fail(
reviewer,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 0e4e8b4..a387192 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -91,7 +91,6 @@
import com.google.gerrit.server.account.GroupCacheImpl;
import com.google.gerrit.server.account.GroupControl;
import com.google.gerrit.server.account.GroupIncludeCacheImpl;
-import com.google.gerrit.server.account.GroupMembers;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
import com.google.gerrit.server.account.externalids.ExternalIdModule;
import com.google.gerrit.server.api.accounts.AccountExternalIdCreator;
@@ -114,6 +113,7 @@
import com.google.gerrit.server.git.EmailMerge;
import com.google.gerrit.server.git.GitModule;
import com.google.gerrit.server.git.GitModules;
+import com.google.gerrit.server.git.MergeSuperSetComputation;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.git.MergedByPushOp;
import com.google.gerrit.server.git.NotesBranchUtil;
@@ -250,7 +250,6 @@
factory(ChangeData.AssistedFactory.class);
factory(ChangeJson.AssistedFactory.class);
factory(CreateChangeSender.Factory.class);
- factory(GroupMembers.Factory.class);
factory(EmailMerge.Factory.class);
factory(MergedSender.Factory.class);
factory(MergeUtil.Factory.class);
@@ -377,6 +376,7 @@
DynamicItem.itemOf(binder(), AccountPatchReviewStore.class);
DynamicSet.setOf(binder(), AssigneeValidationListener.class);
DynamicSet.setOf(binder(), ActionVisitor.class);
+ DynamicItem.itemOf(binder(), MergeSuperSetComputation.class);
DynamicMap.mapOf(binder(), MailFilter.class);
bind(MailFilter.class).annotatedWith(Exports.named("ListMailFilter")).to(ListMailFilter.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
index 322d158..4991715 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
@@ -17,12 +17,15 @@
import static com.google.gerrit.reviewdb.client.RefNames.REFS_REJECT_COMMITS;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.gerrit.common.errors.PermissionDeniedException;
+import com.google.gerrit.extensions.restapi.AuthException;
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.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.project.ProjectControl;
+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;
@@ -70,6 +73,7 @@
private final Provider<IdentifiedUser> currentUser;
private final GitRepositoryManager repoManager;
private final TimeZone tz;
+ private final PermissionBackend permissionBackend;
private NotesBranchUtil.Factory notesBranchUtilFactory;
@Inject
@@ -77,24 +81,23 @@
Provider<IdentifiedUser> currentUser,
GitRepositoryManager repoManager,
@GerritPersonIdent PersonIdent gerritIdent,
- NotesBranchUtil.Factory notesBranchUtilFactory) {
+ NotesBranchUtil.Factory notesBranchUtilFactory,
+ PermissionBackend permissionBackend) {
this.currentUser = currentUser;
this.repoManager = repoManager;
this.notesBranchUtilFactory = notesBranchUtilFactory;
+ this.permissionBackend = permissionBackend;
this.tz = gerritIdent.getTimeZone();
}
public BanCommitResult ban(
- ProjectControl projectControl, List<ObjectId> commitsToBan, String reason)
- throws PermissionDeniedException, LockFailureException, IOException {
- if (!projectControl.isOwner()) {
- throw new PermissionDeniedException("Not project owner: not permitted to ban commits");
- }
+ Project.NameKey project, CurrentUser user, List<ObjectId> commitsToBan, String reason)
+ throws AuthException, LockFailureException, IOException, PermissionBackendException {
+ permissionBackend.user(user).project(project).check(ProjectPermission.BAN_COMMIT);
final BanCommitResult result = new BanCommitResult();
NoteMap banCommitNotes = NoteMap.newEmptyMap();
// Add a note for each banned commit to notes.
- final Project.NameKey project = projectControl.getProject().getNameKey();
try (Repository repo = repoManager.openRepository(project);
RevWalk revWalk = new RevWalk(repo);
ObjectInserter inserter = repo.newObjectInserter()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalMergeSuperSetComputation.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalMergeSuperSetComputation.java
new file mode 100644
index 0000000..e681145
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalMergeSuperSetComputation.java
@@ -0,0 +1,260 @@
+// 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.git;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.common.data.SubmitTypeRecord;
+import com.google.gerrit.extensions.client.SubmitType;
+import com.google.gerrit.extensions.registration.DynamicItem;
+import com.google.gerrit.reviewdb.client.Branch;
+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.MergeOpRepoManager.OpenRepo;
+import com.google.gerrit.server.permissions.ChangePermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevSort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Default implementation of MergeSuperSet that does the computation of the merge super set
+ * sequentially on the local Gerrit instance.
+ */
+public class LocalMergeSuperSetComputation implements MergeSuperSetComputation {
+ private static final Logger log = LoggerFactory.getLogger(LocalMergeSuperSetComputation.class);
+
+ public static class Module extends AbstractModule {
+ @Override
+ protected void configure() {
+ DynamicItem.bind(binder(), MergeSuperSetComputation.class)
+ .to(LocalMergeSuperSetComputation.class);
+ }
+ }
+
+ @AutoValue
+ abstract static class QueryKey {
+ private static QueryKey create(Branch.NameKey branch, Iterable<String> hashes) {
+ return new AutoValue_LocalMergeSuperSetComputation_QueryKey(
+ branch, ImmutableSet.copyOf(hashes));
+ }
+
+ abstract Branch.NameKey branch();
+
+ abstract ImmutableSet<String> hashes();
+ }
+
+ private final PermissionBackend permissionBackend;
+ private final Provider<InternalChangeQuery> queryProvider;
+ private final Map<QueryKey, List<ChangeData>> queryCache;
+ private final Map<Branch.NameKey, Optional<RevCommit>> heads;
+
+ @Inject
+ LocalMergeSuperSetComputation(
+ PermissionBackend permissionBackend, Provider<InternalChangeQuery> queryProvider) {
+ this.permissionBackend = permissionBackend;
+ this.queryProvider = queryProvider;
+ this.queryCache = new HashMap<>();
+ this.heads = new HashMap<>();
+ }
+
+ @Override
+ public ChangeSet completeWithoutTopic(
+ ReviewDb db, MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
+ throws OrmException, IOException, PermissionBackendException {
+ Collection<ChangeData> visibleChanges = new ArrayList<>();
+ Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
+
+ // For each target branch we run a separate rev walk to find open changes
+ // reachable from changes already in the merge super set.
+ ImmutableListMultimap<Branch.NameKey, ChangeData> bc =
+ byBranch(Iterables.concat(changeSet.changes(), changeSet.nonVisibleChanges()));
+ for (Branch.NameKey b : bc.keySet()) {
+ OpenRepo or = getRepo(orm, b.getParentKey());
+ List<RevCommit> visibleCommits = new ArrayList<>();
+ List<RevCommit> nonVisibleCommits = new ArrayList<>();
+ for (ChangeData cd : bc.get(b)) {
+ boolean visible = isVisible(db, changeSet, cd, user);
+
+ if (submitType(cd) == SubmitType.CHERRY_PICK) {
+ if (visible) {
+ visibleChanges.add(cd);
+ } else {
+ nonVisibleChanges.add(cd);
+ }
+
+ continue;
+ }
+
+ // Get the underlying git commit object
+ String objIdStr = cd.currentPatchSet().getRevision().get();
+ RevCommit commit = or.rw.parseCommit(ObjectId.fromString(objIdStr));
+
+ // Always include the input, even if merged. This allows
+ // SubmitStrategyOp to correct the situation later, assuming it gets
+ // returned by byCommitsOnBranchNotMerged below.
+ if (visible) {
+ visibleCommits.add(commit);
+ } else {
+ nonVisibleCommits.add(commit);
+ }
+ }
+
+ Set<String> visibleHashes =
+ walkChangesByHashes(visibleCommits, Collections.emptySet(), or, b);
+ Iterables.addAll(visibleChanges, byCommitsOnBranchNotMerged(or, db, b, visibleHashes));
+
+ Set<String> nonVisibleHashes = walkChangesByHashes(nonVisibleCommits, visibleHashes, or, b);
+ Iterables.addAll(nonVisibleChanges, byCommitsOnBranchNotMerged(or, db, b, nonVisibleHashes));
+ }
+
+ return new ChangeSet(visibleChanges, nonVisibleChanges);
+ }
+
+ private static ImmutableListMultimap<Branch.NameKey, ChangeData> byBranch(
+ Iterable<ChangeData> changes) throws OrmException {
+ ImmutableListMultimap.Builder<Branch.NameKey, ChangeData> builder =
+ ImmutableListMultimap.builder();
+ for (ChangeData cd : changes) {
+ builder.put(cd.change().getDest(), cd);
+ }
+ return builder.build();
+ }
+
+ private OpenRepo getRepo(MergeOpRepoManager orm, Project.NameKey project) throws IOException {
+ try {
+ OpenRepo or = orm.getRepo(project);
+ checkState(or.rw.hasRevSort(RevSort.TOPO));
+ return or;
+ } catch (NoSuchProjectException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private boolean isVisible(ReviewDb db, ChangeSet changeSet, ChangeData cd, CurrentUser user)
+ throws PermissionBackendException {
+ boolean visible = changeSet.ids().contains(cd.getId());
+ if (visible
+ && !permissionBackend.user(user).change(cd).database(db).test(ChangePermission.READ)) {
+ // We thought the change was visible, but it isn't.
+ // This can happen if the ACL changes during the
+ // completeChangeSet computation, for example.
+ visible = false;
+ }
+ return visible;
+ }
+
+ private SubmitType submitType(ChangeData cd) throws OrmException {
+ SubmitTypeRecord str = cd.submitTypeRecord();
+ if (!str.isOk()) {
+ logErrorAndThrow("Failed to get submit type for " + cd.getId() + ": " + str.errorMessage);
+ }
+ return str.type;
+ }
+
+ private List<ChangeData> byCommitsOnBranchNotMerged(
+ OpenRepo or, ReviewDb db, Branch.NameKey branch, Set<String> hashes)
+ throws OrmException, IOException {
+ if (hashes.isEmpty()) {
+ return ImmutableList.of();
+ }
+ QueryKey k = QueryKey.create(branch, hashes);
+ List<ChangeData> cached = queryCache.get(k);
+ if (cached != null) {
+ return cached;
+ }
+
+ List<ChangeData> result = new ArrayList<>();
+ Iterable<ChangeData> destChanges =
+ MergeSuperSet.query(queryProvider.get())
+ .byCommitsOnBranchNotMerged(or.repo, db, branch, hashes);
+ for (ChangeData chd : destChanges) {
+ result.add(chd);
+ }
+ queryCache.put(k, result);
+ return result;
+ }
+
+ private Set<String> walkChangesByHashes(
+ Collection<RevCommit> sourceCommits, Set<String> ignoreHashes, OpenRepo or, Branch.NameKey b)
+ throws IOException {
+ Set<String> destHashes = new HashSet<>();
+ or.rw.reset();
+ markHeadUninteresting(or, b);
+ for (RevCommit c : sourceCommits) {
+ String name = c.name();
+ if (ignoreHashes.contains(name)) {
+ continue;
+ }
+ destHashes.add(name);
+ or.rw.markStart(c);
+ }
+ for (RevCommit c : or.rw) {
+ String name = c.name();
+ if (ignoreHashes.contains(name)) {
+ continue;
+ }
+ destHashes.add(name);
+ }
+
+ return destHashes;
+ }
+
+ private void markHeadUninteresting(OpenRepo or, Branch.NameKey b) throws IOException {
+ Optional<RevCommit> head = heads.get(b);
+ if (head == null) {
+ Ref ref = or.repo.getRefDatabase().exactRef(b.get());
+ head = ref != null ? Optional.of(or.rw.parseCommit(ref.getObjectId())) : Optional.empty();
+ heads.put(b, head);
+ }
+ if (head.isPresent()) {
+ or.rw.markUninteresting(head.get());
+ }
+ }
+
+ private void logErrorAndThrow(String msg) throws OrmException {
+ if (log.isErrorEnabled()) {
+ log.error(msg);
+ }
+ throw new OrmException(msg);
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSet.java
index 58c183b..29627e3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSet.java
@@ -17,29 +17,18 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
-import com.google.auto.value.AutoValue;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.gerrit.common.data.SubmitTypeRecord;
-import com.google.gerrit.extensions.client.SubmitType;
-import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.reviewdb.client.Change;
-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.change.Submit;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.MergeOpRepoManager.OpenRepo;
import com.google.gerrit.server.index.change.ChangeField;
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.NoSuchProjectException;
-import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
@@ -47,21 +36,10 @@
import com.google.inject.Provider;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevSort;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Calculates the minimal superset of changes required to be merged.
@@ -74,36 +52,36 @@
* included.
*/
public class MergeSuperSet {
- private static final Logger log = LoggerFactory.getLogger(MergeSuperSet.class);
- public static void reloadChanges(ChangeSet cs) throws OrmException {
- // Clear exactly the fields requested by query() below.
- for (ChangeData cd : cs.changes()) {
+ public static void reloadChanges(ChangeSet changeSet) throws OrmException {
+ // Clear exactly the fields requested by query(InternalChangeQuery) below.
+ for (ChangeData cd : changeSet.changes()) {
cd.reloadChange();
cd.setPatchSets(null);
cd.setMergeable(null);
}
}
- @AutoValue
- abstract static class QueryKey {
- private static QueryKey create(Branch.NameKey branch, Iterable<String> hashes) {
- return new AutoValue_MergeSuperSet_QueryKey(branch, ImmutableSet.copyOf(hashes));
- }
-
- abstract Branch.NameKey branch();
-
- abstract ImmutableSet<String> hashes();
+ public static InternalChangeQuery query(InternalChangeQuery q) {
+ // Request fields required for completing the ChangeSet and converting to
+ // ChangeInfo without having to touch the database or opening the repository
+ // more than necessary. This provides reasonable performance when loading
+ // the change screen; callers that care about reading the latest value of
+ // these fields should clear them explicitly using reloadChanges().
+ Set<String> fields =
+ ImmutableSet.of(
+ ChangeField.CHANGE.getName(),
+ ChangeField.PATCH_SET.getName(),
+ ChangeField.MERGEABLE.getName());
+ return q.setRequestedFields(fields);
}
private final ChangeData.Factory changeDataFactory;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<MergeOpRepoManager> repoManagerProvider;
+ private final DynamicItem<MergeSuperSetComputation> mergeSuperSetComputation;
private final PermissionBackend permissionBackend;
private final Config cfg;
- private final Map<QueryKey, List<ChangeData>> queryCache;
- private final Map<Branch.NameKey, Optional<RevCommit>> heads;
- private final SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory;
private MergeOpRepoManager orm;
private boolean closeOrm;
@@ -114,16 +92,14 @@
ChangeData.Factory changeDataFactory,
Provider<InternalChangeQuery> queryProvider,
Provider<MergeOpRepoManager> repoManagerProvider,
- PermissionBackend permissionBackend,
- SubmitRuleEvaluator.Factory submitRuleEvaluatorFactory) {
+ DynamicItem<MergeSuperSetComputation> mergeSuperSetComputation,
+ PermissionBackend permissionBackend) {
this.cfg = cfg;
this.changeDataFactory = changeDataFactory;
this.queryProvider = queryProvider;
this.repoManagerProvider = repoManagerProvider;
+ this.mergeSuperSetComputation = mergeSuperSetComputation;
this.permissionBackend = permissionBackend;
- this.submitRuleEvaluatorFactory = submitRuleEvaluatorFactory;
- queryCache = new HashMap<>();
- heads = new HashMap<>();
}
public MergeSuperSet setMergeOpRepoManager(MergeOpRepoManager orm) {
@@ -136,14 +112,19 @@
public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
throws IOException, OrmException, PermissionBackendException {
try {
+ if (orm == null) {
+ orm = repoManagerProvider.get();
+ closeOrm = true;
+ }
+
ChangeData cd = changeDataFactory.create(db, change.getProject(), change.getId());
- ChangeSet cs =
+ ChangeSet changeSet =
new ChangeSet(
cd, permissionBackend.user(user).change(cd).database(db).test(ChangePermission.READ));
if (Submit.wholeTopicEnabled(cfg)) {
- return completeChangeSetIncludingTopics(db, cs, user);
+ return completeChangeSetIncludingTopics(db, changeSet, user);
}
- return completeChangeSetWithoutTopic(db, cs, user);
+ return mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
} finally {
if (closeOrm && orm != null) {
orm.close();
@@ -152,177 +133,13 @@
}
}
- private SubmitType submitType(CurrentUser user, ChangeData cd, PatchSet ps) throws OrmException {
- // Submit type prolog rules mean that the submit type can depend on the
- // submitting user and the content of the change.
- //
- // If the current user can see the change, run that evaluation to get a
- // preview of what would happen on submit. If the current user can't see
- // the change, instead of guessing who would do the submitting, rely on the
- // project configuration and ignore the prolog rule. If the prolog rule
- // doesn't match that, we may pick the wrong submit type and produce a
- // misleading (but still nonzero) count of the non visible changes that
- // would be submitted together with the visible ones.
- SubmitTypeRecord str =
- ps == cd.currentPatchSet()
- ? cd.submitTypeRecord()
- : submitRuleEvaluatorFactory.create(user, cd).setPatchSet(ps).getSubmitType();
- if (!str.isOk()) {
- logErrorAndThrow("Failed to get submit type for " + cd.getId() + ": " + str.errorMessage);
- }
- return str.type;
- }
-
- private static ImmutableListMultimap<Branch.NameKey, ChangeData> byBranch(
- Iterable<ChangeData> changes) throws OrmException {
- ImmutableListMultimap.Builder<Branch.NameKey, ChangeData> builder =
- ImmutableListMultimap.builder();
- for (ChangeData cd : changes) {
- builder.put(cd.change().getDest(), cd);
- }
- return builder.build();
- }
-
- private Set<String> walkChangesByHashes(
- Collection<RevCommit> sourceCommits, Set<String> ignoreHashes, OpenRepo or, Branch.NameKey b)
- throws IOException {
- Set<String> destHashes = new HashSet<>();
- or.rw.reset();
- markHeadUninteresting(or, b);
- for (RevCommit c : sourceCommits) {
- String name = c.name();
- if (ignoreHashes.contains(name)) {
- continue;
- }
- destHashes.add(name);
- or.rw.markStart(c);
- }
- for (RevCommit c : or.rw) {
- String name = c.name();
- if (ignoreHashes.contains(name)) {
- continue;
- }
- destHashes.add(name);
- }
-
- return destHashes;
- }
-
- private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes, CurrentUser user)
- throws IOException, OrmException, PermissionBackendException {
- Collection<ChangeData> visibleChanges = new ArrayList<>();
- Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
-
- // For each target branch we run a separate rev walk to find open changes
- // reachable from changes already in the merge super set.
- ImmutableListMultimap<Branch.NameKey, ChangeData> bc =
- byBranch(Iterables.concat(changes.changes(), changes.nonVisibleChanges()));
- for (Branch.NameKey b : bc.keySet()) {
- OpenRepo or = getRepo(b.getParentKey());
- List<RevCommit> visibleCommits = new ArrayList<>();
- List<RevCommit> nonVisibleCommits = new ArrayList<>();
- for (ChangeData cd : bc.get(b)) {
- boolean visible = changes.ids().contains(cd.getId());
- if (visible && !canRead(db, user, cd)) {
- // We thought the change was visible, but it isn't.
- // This can happen if the ACL changes during the
- // completeChangeSet computation, for example.
- visible = false;
- }
-
- // Pick a revision to use for traversal. If any of the patch sets
- // is visible, we use the most recent one. Otherwise, use the current
- // patch set.
- PatchSet ps = cd.currentPatchSet();
- if (submitType(user, cd, ps) == SubmitType.CHERRY_PICK) {
- if (visible) {
- visibleChanges.add(cd);
- } else {
- nonVisibleChanges.add(cd);
- }
-
- continue;
- }
-
- // Get the underlying git commit object
- String objIdStr = ps.getRevision().get();
- RevCommit commit = or.rw.parseCommit(ObjectId.fromString(objIdStr));
-
- // Always include the input, even if merged. This allows
- // SubmitStrategyOp to correct the situation later, assuming it gets
- // returned by byCommitsOnBranchNotMerged below.
- if (visible) {
- visibleCommits.add(commit);
- } else {
- nonVisibleCommits.add(commit);
- }
- }
-
- Set<String> visibleHashes =
- walkChangesByHashes(visibleCommits, Collections.emptySet(), or, b);
- Iterables.addAll(visibleChanges, byCommitsOnBranchNotMerged(or, db, b, visibleHashes));
-
- Set<String> nonVisibleHashes = walkChangesByHashes(nonVisibleCommits, visibleHashes, or, b);
- Iterables.addAll(nonVisibleChanges, byCommitsOnBranchNotMerged(or, db, b, nonVisibleHashes));
- }
-
- return new ChangeSet(visibleChanges, nonVisibleChanges);
- }
-
- private OpenRepo getRepo(Project.NameKey project) throws IOException {
- if (orm == null) {
- orm = repoManagerProvider.get();
- closeOrm = true;
- }
- try {
- OpenRepo or = orm.getRepo(project);
- checkState(or.rw.hasRevSort(RevSort.TOPO));
- return or;
- } catch (NoSuchProjectException e) {
- throw new IOException(e);
- }
- }
-
- private void markHeadUninteresting(OpenRepo or, Branch.NameKey b) throws IOException {
- Optional<RevCommit> head = heads.get(b);
- if (head == null) {
- Ref ref = or.repo.getRefDatabase().exactRef(b.get());
- head = ref != null ? Optional.of(or.rw.parseCommit(ref.getObjectId())) : Optional.empty();
- heads.put(b, head);
- }
- if (head.isPresent()) {
- or.rw.markUninteresting(head.get());
- }
- }
-
- private List<ChangeData> byCommitsOnBranchNotMerged(
- OpenRepo or, ReviewDb db, Branch.NameKey branch, Set<String> hashes)
- throws OrmException, IOException {
- if (hashes.isEmpty()) {
- return ImmutableList.of();
- }
- QueryKey k = QueryKey.create(branch, hashes);
- List<ChangeData> cached = queryCache.get(k);
- if (cached != null) {
- return cached;
- }
-
- List<ChangeData> result = new ArrayList<>();
- Iterable<ChangeData> destChanges =
- query().byCommitsOnBranchNotMerged(or.repo, db, branch, hashes);
- for (ChangeData chd : destChanges) {
- result.add(chd);
- }
- queryCache.put(k, result);
- return result;
- }
-
/**
- * Completes {@code cs} with any additional changes from its topics
+ * Completes {@code changeSet} with any additional changes from its topics
*
* <p>{@link #completeChangeSetIncludingTopics} calls this repeatedly, alternating with {@link
- * #completeChangeSetWithoutTopic}, to discover what additional changes should be submitted with a
- * change until the set stops growing.
+ * MergeSuperSetComputation#completeWithoutTopic(ReviewDb, MergeOpRepoManager, ChangeSet,
+ * CurrentUser)}, to discover what additional changes should be submitted with a change until the
+ * set stops growing.
*
* <p>{@code topicsSeen} and {@code visibleTopicsSeen} keep track of topics already explored to
* avoid wasted work.
@@ -331,7 +148,7 @@
*/
private ChangeSet topicClosure(
ReviewDb db,
- ChangeSet cs,
+ ChangeSet changeSet,
CurrentUser user,
Set<String> topicsSeen,
Set<String> visibleTopicsSeen)
@@ -339,13 +156,13 @@
List<ChangeData> visibleChanges = new ArrayList<>();
List<ChangeData> nonVisibleChanges = new ArrayList<>();
- for (ChangeData cd : cs.changes()) {
+ for (ChangeData cd : changeSet.changes()) {
visibleChanges.add(cd);
String topic = cd.change().getTopic();
if (Strings.isNullOrEmpty(topic) || visibleTopicsSeen.contains(topic)) {
continue;
}
- for (ChangeData topicCd : query().byTopicOpen(topic)) {
+ for (ChangeData topicCd : byTopicOpen(topic)) {
if (canRead(db, user, topicCd)) {
visibleChanges.add(topicCd);
} else {
@@ -355,13 +172,13 @@
topicsSeen.add(topic);
visibleTopicsSeen.add(topic);
}
- for (ChangeData cd : cs.nonVisibleChanges()) {
+ for (ChangeData cd : changeSet.nonVisibleChanges()) {
nonVisibleChanges.add(cd);
String topic = cd.change().getTopic();
if (Strings.isNullOrEmpty(topic) || topicsSeen.contains(topic)) {
continue;
}
- for (ChangeData topicCd : query().byTopicOpen(topic)) {
+ for (ChangeData topicCd : byTopicOpen(topic)) {
nonVisibleChanges.add(topicCd);
}
topicsSeen.add(topic);
@@ -370,7 +187,7 @@
}
private ChangeSet completeChangeSetIncludingTopics(
- ReviewDb db, ChangeSet changes, CurrentUser user)
+ ReviewDb db, ChangeSet changeSet, CurrentUser user)
throws IOException, OrmException, PermissionBackendException {
Set<String> topicsSeen = new HashSet<>();
Set<String> visibleTopicsSeen = new HashSet<>();
@@ -380,37 +197,16 @@
do {
oldSeen = seen;
- changes = completeChangeSetWithoutTopic(db, changes, user);
- changes = topicClosure(db, changes, user, topicsSeen, visibleTopicsSeen);
+ changeSet = topicClosure(db, changeSet, user, topicsSeen, visibleTopicsSeen);
+ changeSet = mergeSuperSetComputation.get().completeWithoutTopic(db, orm, changeSet, user);
seen = topicsSeen.size() + visibleTopicsSeen.size();
} while (seen != oldSeen);
- return changes;
+ return changeSet;
}
- private InternalChangeQuery query() {
- // Request fields required for completing the ChangeSet and converting to
- // ChangeInfo without having to touch the database or opening the repository
- // more than necessary. This provides reasonable performance when loading
- // the change screen; callers that care about reading the latest value of
- // these fields should clear them explicitly using reloadChanges().
- Set<String> fields =
- ImmutableSet.of(
- ChangeField.CHANGE.getName(),
- ChangeField.PATCH_SET.getName(),
- ChangeField.MERGEABLE.getName());
- return queryProvider.get().setRequestedFields(fields);
- }
-
- private void logError(String msg) {
- if (log.isErrorEnabled()) {
- log.error(msg);
- }
- }
-
- private void logErrorAndThrow(String msg) throws OrmException {
- logError(msg);
- throw new OrmException(msg);
+ private List<ChangeData> byTopicOpen(String topic) throws OrmException {
+ return query(queryProvider.get()).byTopicOpen(topic);
}
private boolean canRead(ReviewDb db, CurrentUser user, ChangeData cd)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSetComputation.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSetComputation.java
new file mode 100644
index 0000000..63405ba
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeSuperSetComputation.java
@@ -0,0 +1,53 @@
+// 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.git;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gwtorm.server.OrmException;
+import java.io.IOException;
+
+/**
+ * Interface to compute the merge super set to detect changes that should be submitted together.
+ *
+ * <p>E.g. to speed up performance implementations could decide to do the computation in batches in
+ * parallel on different server nodes.
+ */
+@ExtensionPoint
+public interface MergeSuperSetComputation {
+
+ /**
+ * Compute the set of changes that should be submitted together. As input a set of changes is
+ * provided for which it is known that they should be submitted together. This method should
+ * complete the set by including open predecessor changes that need to be submitted as well. To
+ * decide whether open predecessor changes should be included the method must take the submit type
+ * into account (e.g. for changes with submit type "Cherry-Pick" open predecessor changes must not
+ * be included).
+ *
+ * <p>This method is invoked iteratively while new changes to be submitted together are discovered
+ * by expanding the topics of the changes. This method must not do any topic expansion on its own.
+ *
+ * @param db {@link ReviewDb} instance
+ * @param orm {@link MergeOpRepoManager} that should be used to access repositories
+ * @param changeSet A set of changes for which it is known that they should be submitted together
+ * @param user The user for which the visibility checks should be performed
+ * @return the completed set of changes that should be submitted together
+ */
+ ChangeSet completeWithoutTopic(
+ ReviewDb db, MergeOpRepoManager orm, ChangeSet changeSet, CurrentUser user)
+ throws OrmException, IOException, PermissionBackendException;
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
index 22834f3..cfd9595 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.project.ContributorAgreementsChecker;
+import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -73,7 +74,8 @@
public interface Factory {
AsyncReceiveCommits create(
- ProjectControl projectControl,
+ ProjectState projectState,
+ IdentifiedUser user,
Repository repository,
@Nullable MessageSender messageSender,
SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers);
@@ -106,7 +108,7 @@
private Worker(Collection<ReceiveCommand> commands) {
this.commands = commands;
- rc = factory.create(projectControl, rp, allRefsWatcher, extraReviewers);
+ rc = factory.create(projectState, user, rp, allRefsWatcher, extraReviewers);
rc.init();
rc.setMessageSender(messageSender);
progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes");
@@ -170,8 +172,10 @@
private final RequestScopePropagator scopePropagator;
private final ReceiveConfig receiveConfig;
private final ContributorAgreementsChecker contributorAgreements;
+ private final ProjectControl.GenericFactory projectControlFactory;
private final long timeoutMillis;
- private final ProjectControl projectControl;
+ private final ProjectState projectState;
+ private final IdentifiedUser user;
private final Repository repo;
private final MessageSender messageSender;
private final SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers;
@@ -189,8 +193,10 @@
TransferConfig transferConfig,
Provider<LazyPostReceiveHookChain> lazyPostReceive,
ContributorAgreementsChecker contributorAgreements,
+ ProjectControl.GenericFactory projectControlFactory,
@Named(TIMEOUT_NAME) long timeoutMillis,
- @Assisted ProjectControl projectControl,
+ @Assisted ProjectState projectState,
+ @Assisted IdentifiedUser user,
@Assisted Repository repo,
@Assisted @Nullable MessageSender messageSender,
@Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers)
@@ -200,23 +206,23 @@
this.scopePropagator = scopePropagator;
this.receiveConfig = receiveConfig;
this.contributorAgreements = contributorAgreements;
+ this.projectControlFactory = projectControlFactory;
this.timeoutMillis = timeoutMillis;
- this.projectControl = projectControl;
+ this.projectState = projectState;
+ this.user = user;
this.repo = repo;
this.messageSender = messageSender;
this.extraReviewers = extraReviewers;
- IdentifiedUser user = projectControl.getUser().asIdentifiedUser();
- ProjectState state = projectControl.getProjectState();
- Project.NameKey projectName = projectControl.getProject().getNameKey();
+ Project.NameKey projectName = projectState.getNameKey();
rp = new ReceivePack(repo);
rp.setAllowCreates(true);
rp.setAllowDeletes(true);
rp.setAllowNonFastForwards(true);
rp.setRefLogIdent(user.newRefLogIdent());
rp.setTimeout(transferConfig.getTimeout());
- rp.setMaxObjectSizeLimit(transferConfig.getEffectiveMaxObjectSizeLimit(state));
- rp.setCheckReceivedObjects(state.getConfig().getCheckReceivedObjects());
+ rp.setMaxObjectSizeLimit(transferConfig.getEffectiveMaxObjectSizeLimit(projectState));
+ rp.setCheckReceivedObjects(projectState.getConfig().getCheckReceivedObjects());
rp.setRefFilter(new ReceiveRefFilter());
rp.setAllowPushOptions(true);
rp.setPreReceiveHook(this);
@@ -233,7 +239,7 @@
List<AdvertiseRefsHook> advHooks = new ArrayList<>(4);
allRefsWatcher = new AllRefsWatcher();
advHooks.add(allRefsWatcher);
- advHooks.add(refFilterFactory.create(state, repo).setShowMetadata(false));
+ advHooks.add(refFilterFactory.create(projectState, repo).setShowMetadata(false));
advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName));
advHooks.add(new HackPushNegotiateHook());
rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
@@ -241,20 +247,27 @@
/** Determine if the user can upload commits. */
public Capable canUpload() throws IOException {
+ // TODO(hiesel): Remove dependency on ProjectControl
+ ProjectControl projectControl;
+ try {
+ projectControl = projectControlFactory.controlFor(projectState.getNameKey(), user);
+ } catch (NoSuchProjectException e) {
+ throw new IOException(e);
+ }
+
Capable result = projectControl.canPushToAtLeastOneRef();
if (result != Capable.OK) {
return result;
}
try {
- contributorAgreements.check(
- projectControl.getProject().getNameKey(), projectControl.getUser());
+ contributorAgreements.check(projectState.getNameKey(), user);
} catch (AuthException e) {
return new Capable(e.getMessage());
}
if (receiveConfig.checkMagicRefs) {
- return MagicBranch.checkMagicBranchRefs(repo, projectControl.getProject());
+ return MagicBranch.checkMagicBranchRefs(repo, projectState.getProject());
}
return Capable.OK;
}
@@ -269,7 +282,7 @@
log.warn(
String.format(
"Error in ReceiveCommits while processing changes for project %s",
- projectControl.getProject().getName()),
+ projectState.getName()),
e);
rp.sendError("internal error while processing changes");
// ReceiveCommits has tried its best to catch errors, so anything at this
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index aaed2e7..553087f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -128,12 +128,12 @@
import com.google.gerrit.server.permissions.GlobalPermission;
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.permissions.RefPermission;
import com.google.gerrit.server.project.CreateRefControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
@@ -228,7 +228,8 @@
interface Factory {
ReceiveCommits create(
- ProjectControl projectControl,
+ ProjectState projectState,
+ IdentifiedUser user,
ReceivePack receivePack,
AllRefsWatcher allRefsWatcher,
SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers);
@@ -300,7 +301,6 @@
private final CommitValidators.Factory commitValidatorsFactory;
private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
private final DynamicSet<ReceivePackInitializer> initializers;
- private final IdentifiedUser user;
private final MergedByPushOp.Factory mergedByPushOpFactory;
private final NotesMigration notesMigration;
private final PatchSetInfoFactory patchSetInfoFactory;
@@ -325,7 +325,8 @@
// Assisted injected fields.
private final AllRefsWatcher allRefsWatcher;
private final ImmutableSetMultimap<ReviewerStateInternal, Account.Id> extraReviewers;
- private final ProjectControl projectControl;
+ private final ProjectState projectState;
+ private final IdentifiedUser user;
private final ReceivePack rp;
// Immutable fields derived from constructor arguments.
@@ -406,7 +407,8 @@
TagCache tagCache,
CreateRefControl createRefControl,
DynamicItem<ChangeReportFormatter> changeFormatterProvider,
- @Assisted ProjectControl projectControl,
+ @Assisted ProjectState projectState,
+ @Assisted IdentifiedUser user,
@Assisted ReceivePack rp,
@Assisted AllRefsWatcher allRefsWatcher,
@Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers)
@@ -419,7 +421,6 @@
this.changeInserterFactory = changeInserterFactory;
this.commitValidatorsFactory = commitValidatorsFactory;
this.changeFormatter = changeFormatterProvider.get();
- this.user = projectControl.getUser().asIdentifiedUser();
this.db = db;
this.editUtil = editUtil;
this.hashtagsFactory = hashtagsFactory;
@@ -450,13 +451,14 @@
// Assisted injected fields.
this.allRefsWatcher = allRefsWatcher;
this.extraReviewers = ImmutableSetMultimap.copyOf(extraReviewers);
- this.projectControl = projectControl;
+ this.projectState = projectState;
+ this.user = user;
this.rp = rp;
// Immutable fields derived from constructor arguments.
repo = rp.getRepository();
- project = projectControl.getProject();
- labelTypes = projectControl.getProjectState().getLabelTypes();
+ project = projectState.getProject();
+ labelTypes = projectState.getLabelTypes();
permissions = permissionBackend.user(user).project(project.getNameKey());
receiveId = RequestId.forProject(project.getNameKey());
rejectCommits = BanCommit.loadRejectCommitsMap(rp.getRepository(), rp.getRevWalk());
@@ -474,8 +476,7 @@
newChanges = Collections.emptyList();
// Other settings populated during processing.
- newChangeForAllNotInTarget =
- projectControl.getProjectState().isCreateNewChangeForAllNotInTarget();
+ newChangeForAllNotInTarget = projectState.isCreateNewChangeForAllNotInTarget();
// Handles for outputting back over the wire to the end user.
messageSender = new ReceivePackMessageSender();
@@ -483,7 +484,7 @@
void init() {
for (ReceivePackInitializer i : initializers) {
- i.init(projectControl.getProject().getNameKey(), rp);
+ i.init(projectState.getNameKey(), rp);
}
}
@@ -818,8 +819,7 @@
continue;
}
- if (projectControl.getProjectState().isAllUsers()
- && RefNames.REFS_USERS_SELF.equals(cmd.getRefName())) {
+ if (projectState.isAllUsers() && RefNames.REFS_USERS_SELF.equals(cmd.getRefName())) {
String newName = RefNames.refsUsers(user.getAccountId());
logDebug("Swapping out command for {} to {}", RefNames.REFS_USERS_SELF, newName);
final ReceiveCommand orgCmd = cmd;
@@ -870,8 +870,14 @@
if (isConfig(cmd)) {
logDebug("Processing {} command", cmd.getRefName());
- if (!projectControl.isOwner()) {
- reject(cmd, "not project owner");
+ try {
+ permissions.check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException e) {
+ reject(
+ cmd,
+ String.format(
+ "must be either project owner or have %s permission",
+ ProjectPermission.WRITE_CONFIG.describeForException()));
continue;
}
@@ -926,16 +932,14 @@
ProjectConfigEntry configEntry = e.getProvider().get();
String value = pluginCfg.getString(e.getExportName());
String oldValue =
- projectControl
- .getProjectState()
+ projectState
.getConfig()
.getPluginConfig(e.getPluginName())
.getString(e.getExportName());
if (configEntry.getType() == ProjectConfigEntryType.ARRAY) {
oldValue =
Arrays.stream(
- projectControl
- .getProjectState()
+ projectState
.getConfig()
.getPluginConfig(e.getPluginName())
.getStringList(e.getExportName()))
@@ -943,7 +947,7 @@
}
if ((value == null ? oldValue != null : !value.equals(oldValue))
- && !configEntry.isEditable(projectControl.getProjectState())) {
+ && !configEntry.isEditable(projectState)) {
reject(
cmd,
String.format(
@@ -1450,7 +1454,7 @@
reject(cmd, "see help");
return;
}
- if (projectControl.getProjectState().isAllUsers() && RefNames.REFS_USERS_SELF.equals(ref)) {
+ if (projectState.isAllUsers() && RefNames.REFS_USERS_SELF.equals(ref)) {
logDebug("Handling {}", RefNames.REFS_USERS_SELF);
ref = RefNames.refsUsers(user.getAccountId());
}
@@ -1469,7 +1473,7 @@
magicBranch.dest = new Branch.NameKey(project.getNameKey(), ref);
magicBranch.perm = permissions.ref(ref);
- if (!projectControl.getProject().getState().permitsWrite()) {
+ if (!projectState.getProject().getState().permitsWrite()) {
reject(cmd, "project state does not permit write");
return;
}
@@ -2491,7 +2495,7 @@
replaceOp =
replaceOpFactory
.create(
- projectControl,
+ projectState,
notes.getChange().getDest(),
checkMergedInto,
priorPatchSet,
@@ -2793,8 +2797,7 @@
// TODO(dborowitz): Combine this BatchUpdate with the main one in
// insertChangesAndPatchSets.
try (BatchUpdate bu =
- batchUpdateFactory.create(
- db, projectControl.getProject().getNameKey(), user, TimeUtil.nowTs());
+ batchUpdateFactory.create(db, projectState.getNameKey(), user, TimeUtil.nowTs());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 4455aed..9220bc9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -54,7 +54,7 @@
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
@@ -84,7 +84,7 @@
public class ReplaceOp implements BatchUpdateOp {
public interface Factory {
ReplaceOp create(
- ProjectControl projectControl,
+ ProjectState projectState,
Branch.NameKey dest,
boolean checkMergedInto,
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
@@ -117,7 +117,7 @@
private final ReplacePatchSetSender.Factory replacePatchSetFactory;
private final ProjectCache projectCache;
- private final ProjectControl projectControl;
+ private final ProjectState projectState;
private final Branch.NameKey dest;
private final boolean checkMergedInto;
private final PatchSet.Id priorPatchSetId;
@@ -159,7 +159,7 @@
ReplacePatchSetSender.Factory replacePatchSetFactory,
ProjectCache projectCache,
@SendEmailExecutor ExecutorService sendEmailExecutor,
- @Assisted ProjectControl projectControl,
+ @Assisted ProjectState projectState,
@Assisted Branch.NameKey dest,
@Assisted boolean checkMergedInto,
@Assisted("priorPatchSetId") PatchSet.Id priorPatchSetId,
@@ -186,7 +186,7 @@
this.projectCache = projectCache;
this.sendEmailExecutor = sendEmailExecutor;
- this.projectControl = projectControl;
+ this.projectState = projectState;
this.dest = dest;
this.checkMergedInto = checkMergedInto;
this.priorPatchSetId = priorPatchSetId;
@@ -205,7 +205,7 @@
ctx.getRevWalk().parseBody(commit);
changeKind =
changeKindCache.getChangeKind(
- projectControl.getProject().getNameKey(),
+ projectState.getNameKey(),
ctx.getRevWalk(),
ctx.getRepoView().getConfig(),
priorCommitId,
@@ -299,7 +299,7 @@
approvalsUtil.addApprovalsForNewPatchSet(
ctx.getDb(),
update,
- projectControl.getProjectState().getLabelTypes(),
+ projectState.getLabelTypes(),
newPatchSet,
ctx.getUser(),
approvals);
@@ -314,7 +314,7 @@
approvalsUtil.addReviewers(
ctx.getDb(),
update,
- projectControl.getProjectState().getLabelTypes(),
+ projectState.getLabelTypes(),
change,
newPatchSet,
info,
@@ -406,7 +406,7 @@
continue;
}
- LabelType lt = projectControl.getProjectState().getLabelTypes().byLabel(a.getLabelId());
+ LabelType lt = projectState.getLabelTypes().byLabel(a.getLabelId());
if (lt != null) {
current.put(lt.getName(), a);
}
@@ -496,8 +496,7 @@
public void run() {
try {
ReplacePatchSetSender cm =
- replacePatchSetFactory.create(
- projectControl.getProject().getNameKey(), notes.getChangeId());
+ replacePatchSetFactory.create(projectState.getNameKey(), notes.getChangeId());
cm.setFrom(ctx.getAccount().getId());
cm.setPatchSet(newPatchSet, info);
cm.setChangeMessage(msg.getMessage(), ctx.getWhen());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
index 910468f..6fbfd67 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/group/ListGroups.java
@@ -28,6 +28,7 @@
import com.google.gerrit.extensions.client.ListGroupsOption;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.extensions.restapi.Url;
@@ -41,7 +42,6 @@
import com.google.gerrit.server.account.GroupBackend;
import com.google.gerrit.server.account.GroupCache;
import com.google.gerrit.server.account.GroupControl;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -56,6 +56,7 @@
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.kohsuke.args4j.Option;
@@ -67,7 +68,7 @@
protected final GroupCache groupCache;
- private final List<ProjectControl> projects = new ArrayList<>();
+ private final List<ProjectState> projects = new ArrayList<>();
private final Set<AccountGroup.UUID> groupsToInspect = new HashSet<>();
private final GroupControl.Factory groupControlFactory;
private final GroupControl.GenericFactory genericGroupControlFactory;
@@ -77,6 +78,7 @@
private final GroupJson json;
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);
@@ -88,13 +90,14 @@
private String matchSubstring;
private String matchRegex;
private String suggest;
+ private String ownedBy;
@Option(
name = "--project",
aliases = {"-p"},
usage = "projects for which the groups should be listed"
)
- public void addProject(ProjectControl project) {
+ public void addProject(ProjectState project) {
projects.add(project);
}
@@ -209,6 +212,11 @@
options.addAll(ListGroupsOption.fromBits(Integer.parseInt(hex, 16)));
}
+ @Option(name = "--owned-by", usage = "list groups owned by the given group uuid")
+ public void setOwnedBy(String ownedBy) {
+ this.ownedBy = ownedBy;
+ }
+
@Inject
protected ListGroups(
final GroupCache groupCache,
@@ -217,6 +225,7 @@
final Provider<IdentifiedUser> identifiedUser,
final IdentifiedUser.GenericFactory userFactory,
final GetGroups accountGetGroups,
+ final GroupsCollection groupsCollection,
GroupJson json,
GroupBackend groupBackend,
Groups groups,
@@ -230,6 +239,7 @@
this.json = json;
this.groupBackend = groupBackend;
this.groups = groups;
+ this.groupsCollection = groupsCollection;
this.db = db;
}
@@ -241,13 +251,13 @@
return user;
}
- public List<ProjectControl> getProjects() {
+ public List<ProjectState> getProjects() {
return projects;
}
@Override
public SortedMap<String, GroupInfo> apply(TopLevelResource resource)
- throws OrmException, BadRequestException {
+ throws OrmException, RestApiException {
SortedMap<String, GroupInfo> output = new TreeMap<>();
for (GroupInfo info : get()) {
output.put(MoreObjects.firstNonNull(info.name, "Group " + Url.decode(info.id)), info);
@@ -256,7 +266,7 @@
return output;
}
- public List<GroupInfo> get() throws OrmException, BadRequestException {
+ public List<GroupInfo> get() throws OrmException, RestApiException {
if (!Strings.isNullOrEmpty(suggest)) {
return suggestGroups();
}
@@ -265,6 +275,10 @@
throw new BadRequestException("Specify one of m/r");
}
+ if (ownedBy != null) {
+ return getGroupsOwnedBy(ownedBy);
+ }
+
if (owned) {
return getGroupsOwnedBy(user != null ? userFactory.create(user) : identifiedUser.get());
}
@@ -298,7 +312,6 @@
if (!projects.isEmpty()) {
return projects
.stream()
- .map(ProjectControl::getProjectState)
.map(ProjectState::getAllGroups)
.flatMap(Collection::stream)
.map(GroupReference::getUUID)
@@ -318,9 +331,7 @@
List<GroupReference> groupRefs =
Lists.newArrayList(
Iterables.limit(
- groupBackend.suggest(
- suggest,
- projects.stream().findFirst().map(pc -> pc.getProjectState()).orElse(null)),
+ groupBackend.suggest(suggest, projects.stream().findFirst().orElse(null)),
limit <= 0 ? 10 : Math.min(limit, 10)));
List<GroupInfo> groupInfos = Lists.newArrayListWithCapacity(groupRefs.size());
@@ -349,6 +360,9 @@
if (owned) {
return true;
}
+ if (ownedBy != null) {
+ return true;
+ }
if (start != 0) {
return true;
}
@@ -364,14 +378,15 @@
return false;
}
- private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) throws OrmException {
+ private List<GroupInfo> filterGroupsOwnedBy(Predicate<GroupDescription.Internal> filter)
+ throws OrmException {
Pattern pattern = getRegexPattern();
Stream<GroupDescription.Internal> foundGroups =
groups
.getAll(db.get())
.map(GroupDescriptions::forAccountGroup)
.filter(group -> !isNotRelevant(pattern, group))
- .filter(group -> isOwner(user, group))
+ .filter(filter)
.sorted(GROUP_COMPARATOR)
.skip(start);
if (limit > 0) {
@@ -385,6 +400,15 @@
return groupInfos;
}
+ private List<GroupInfo> getGroupsOwnedBy(String id) throws OrmException, RestApiException {
+ String uuid = groupsCollection.parse(id).getGroupUUID().get();
+ return filterGroupsOwnedBy(group -> group.getOwnerGroupUUID().get().equals(uuid));
+ }
+
+ private List<GroupInfo> getGroupsOwnedBy(IdentifiedUser user) throws OrmException {
+ return filterGroupsOwnedBy(group -> isOwner(user, group));
+ }
+
private boolean isOwner(CurrentUser user, GroupDescription.Internal group) {
try {
return genericGroupControlFactory.controlFor(user, group.getGroupUUID()).isOwner();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/ProjectPermission.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/ProjectPermission.java
index d0abf9a..2a6bb8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/ProjectPermission.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/ProjectPermission.java
@@ -76,7 +76,19 @@
RUN_RECEIVE_PACK,
/** Can run upload pack. */
- RUN_UPLOAD_PACK;
+ RUN_UPLOAD_PACK,
+
+ /** Allow read access to refs/meta/config. */
+ READ_CONFIG,
+
+ /** Allow write access to refs/meta/config. */
+ WRITE_CONFIG,
+
+ /** Allow banning commits from Gerrit preventing pushes of these commits. */
+ BAN_COMMIT,
+
+ /** Allow accessing the project's reflog. */
+ READ_REFLOG;
private final String name;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/RefPermission.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/RefPermission.java
index 9f3dda1..607162e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/RefPermission.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/RefPermission.java
@@ -24,6 +24,7 @@
DELETE(Permission.DELETE),
UPDATE(Permission.PUSH),
FORCE_UPDATE,
+ SET_HEAD,
FORGE_AUTHOR(Permission.FORGE_AUTHOR),
FORGE_COMMITTER(Permission.FORGE_COMMITTER),
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BanCommit.java
index 278b2af..040e7ca 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BanCommit.java
@@ -15,11 +15,10 @@
package com.google.gerrit.server.project;
import com.google.common.collect.Lists;
-import com.google.gerrit.common.errors.PermissionDeniedException;
-import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
import com.google.gerrit.server.git.BanCommitResult;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.BanCommit.BanResultInfo;
import com.google.gerrit.server.project.BanCommit.Input;
import com.google.gerrit.server.update.BatchUpdate;
@@ -61,7 +60,7 @@
@Override
protected BanResultInfo applyImpl(
BatchUpdate.Factory updateFactory, ProjectResource rsrc, Input input)
- throws RestApiException, UpdateException, IOException {
+ throws RestApiException, UpdateException, IOException, PermissionBackendException {
BanResultInfo r = new BanResultInfo();
if (input != null && input.commits != null && !input.commits.isEmpty()) {
List<ObjectId> commitsToBan = new ArrayList<>(input.commits.size());
@@ -73,14 +72,11 @@
}
}
- try {
- BanCommitResult result = banCommit.ban(rsrc.getControl(), commitsToBan, input.reason);
- r.newlyBanned = transformCommits(result.getNewlyBannedCommits());
- r.alreadyBanned = transformCommits(result.getAlreadyBannedCommits());
- r.ignored = transformCommits(result.getIgnoredObjectIds());
- } catch (PermissionDeniedException e) {
- throw new AuthException(e.getMessage());
- }
+ BanCommitResult result =
+ banCommit.ban(rsrc.getNameKey(), rsrc.getUser(), commitsToBan, input.reason);
+ r.newlyBanned = transformCommits(result.getNewlyBannedCommits());
+ r.alreadyBanned = transformCommits(result.getAlreadyBannedCommits());
+ r.ignored = transformCommits(result.getIgnoredObjectIds());
}
return r;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
index 2e81af3..622b1dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchResource.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Branch;
+import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
import org.eclipse.jgit.lib.Ref;
@@ -26,8 +27,8 @@
private final String refName;
private final String revision;
- public BranchResource(ProjectControl control, Ref ref) {
- super(control);
+ public BranchResource(ProjectState projectState, CurrentUser user, Ref ref) {
+ super(projectState, user);
this.refName = ref.getName();
this.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
index a40eabb..52072d8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/BranchesCollection.java
@@ -85,7 +85,7 @@
.project(project)
.ref(ref.isSymbolic() ? ref.getTarget().getName() : ref.getName())
.check(RefPermission.READ);
- return new BranchResource(parent.getControl(), ref);
+ return new BranchResource(parent.getProjectState(), parent.getUser(), ref);
} catch (AuthException notAllowed) {
throw new ResourceNotFoundException(id);
} catch (RepositoryNotFoundException noRepo) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
index 0cd7d19..e008d66 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChildProjectsCollection.java
@@ -53,7 +53,7 @@
public ChildProjectResource parse(ProjectResource parent, IdString id)
throws ResourceNotFoundException, IOException, PermissionBackendException {
ProjectResource p = projectsCollection.parse(TopLevelResource.INSTANCE, id);
- for (ProjectState pp : p.getControl().getProjectState().parents()) {
+ for (ProjectState pp : p.getProjectState().parents()) {
if (parent.getNameKey().equals(pp.getProject().getNameKey())) {
return new ChildProjectResource(parent, p.getProjectState());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
index eb0dde4..db0787c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ConfigInfoImpl.java
@@ -25,6 +25,7 @@
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.extensions.webui.UiAction;
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.config.PluginConfig;
import com.google.gerrit.server.config.PluginConfigFactory;
@@ -39,15 +40,15 @@
public class ConfigInfoImpl extends ConfigInfo {
public ConfigInfoImpl(
boolean serverEnableSignedPush,
- ProjectControl control,
+ ProjectState projectState,
+ CurrentUser user,
TransferConfig config,
DynamicMap<ProjectConfigEntry> pluginConfigEntries,
PluginConfigFactory cfgFactory,
AllProjectsName allProjects,
UiActions uiActions,
DynamicMap<RestView<ProjectResource>> views) {
- ProjectState projectState = control.getProjectState();
- Project p = control.getProject();
+ Project p = projectState.getProject();
this.description = Strings.emptyToNull(p.getDescription());
InheritedBooleanInfo useContributorAgreements = new InheritedBooleanInfo();
@@ -130,11 +131,10 @@
this.commentlinks.put(cl.name, cl);
}
- pluginConfig =
- getPluginConfig(control.getProjectState(), pluginConfigEntries, cfgFactory, allProjects);
+ pluginConfig = getPluginConfig(projectState, pluginConfigEntries, cfgFactory, allProjects);
actions = new TreeMap<>();
- for (UiAction.Description d : uiActions.from(views, new ProjectResource(control))) {
+ for (UiAction.Description d : uiActions.from(views, new ProjectResource(projectState, user))) {
actions.put(d.getId(), new ActionInfo(d));
}
this.theme = projectState.getTheme();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java
index 326d395..31b48cd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateAccessChange.java
@@ -38,6 +38,7 @@
import com.google.gerrit.server.git.ProjectConfig;
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.permissions.RefPermission;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
@@ -89,25 +90,23 @@
throws PermissionBackendException, PermissionDeniedException, IOException,
ConfigInvalidException, OrmException, InvalidNameException, UpdateException,
RestApiException {
- MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
- List<AccessSection> removals = setAccess.getAccessSections(input.remove);
- List<AccessSection> additions = setAccess.getAccessSections(input.add);
-
- PermissionBackend.ForRef metaRef =
- permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey()).ref(RefNames.REFS_CONFIG);
- try {
- metaRef.check(RefPermission.READ);
- } catch (AuthException denied) {
+ PermissionBackend.ForProject forProject =
+ permissionBackend.user(rsrc.getUser()).project(rsrc.getNameKey());
+ if (!check(forProject, ProjectPermission.READ_CONFIG)) {
throw new PermissionDeniedException(RefNames.REFS_CONFIG + " not visible");
}
- if (!rsrc.getControl().isOwner()) {
+ if (!check(forProject, ProjectPermission.WRITE_CONFIG)) {
try {
- metaRef.check(RefPermission.CREATE_CHANGE);
+ forProject.ref(RefNames.REFS_CONFIG).check(RefPermission.CREATE_CHANGE);
} catch (AuthException denied) {
throw new PermissionDeniedException("cannot create change for " + RefNames.REFS_CONFIG);
}
}
+ MetaDataUpdate.User metaDataUpdateUser = metaDataUpdateFactory.get();
+ List<AccessSection> removals = setAccess.getAccessSections(input.remove);
+ List<AccessSection> additions = setAccess.getAccessSections(input.add);
+
Project.NameKey newParentProjectName =
input.parent == null ? null : new Project.NameKey(input.parent);
@@ -159,4 +158,14 @@
.setValidate(false)
.setUpdateRef(false);
}
+
+ private boolean check(PermissionBackend.ForProject perm, ProjectPermission p)
+ throws PermissionBackendException {
+ try {
+ perm.check(p);
+ return true;
+ } catch (AuthException denied) {
+ return false;
+ }
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateTag.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateTag.java
index 61548c4..8e706a2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateTag.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CreateTag.java
@@ -66,6 +66,7 @@
private final TagCache tagCache;
private final GitReferenceUpdated referenceUpdated;
private final WebLinks links;
+ private final ProjectControl.GenericFactory projectControlFactory;
private String ref;
@Inject
@@ -76,6 +77,7 @@
TagCache tagCache,
GitReferenceUpdated referenceUpdated,
WebLinks webLinks,
+ ProjectControl.GenericFactory projectControlFactory,
@Assisted String ref) {
this.permissionBackend = permissionBackend;
this.identifiedUser = identifiedUser;
@@ -83,12 +85,13 @@
this.tagCache = tagCache;
this.referenceUpdated = referenceUpdated;
this.links = webLinks;
+ this.projectControlFactory = projectControlFactory;
this.ref = ref;
}
@Override
public TagInfo apply(ProjectResource resource, TagInput input)
- throws RestApiException, IOException, PermissionBackendException {
+ throws RestApiException, IOException, PermissionBackendException, NoSuchProjectException {
if (input == null) {
input = new TagInput();
}
@@ -101,7 +104,11 @@
ref = RefUtil.normalizeTagRef(ref);
- RefControl refControl = resource.getControl().controlForRef(ref);
+ // TODO(hiesel): Remove dependency on RefControl
+ RefControl refControl =
+ projectControlFactory
+ .controlFor(resource.getNameKey(), resource.getUser())
+ .controlForRef(ref);
PermissionBackend.ForRef perm =
permissionBackend.user(identifiedUser).project(resource.getNameKey()).ref(ref);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardResource.java
index a3fd09e..87b6fdf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardResource.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.restapi.RestResource;
import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
import org.eclipse.jgit.lib.Config;
@@ -23,31 +24,38 @@
public static final TypeLiteral<RestView<DashboardResource>> DASHBOARD_KIND =
new TypeLiteral<RestView<DashboardResource>>() {};
- public static DashboardResource projectDefault(ProjectControl ctl) {
- return new DashboardResource(ctl, null, null, null, true);
+ public static DashboardResource projectDefault(ProjectState projectState, CurrentUser user) {
+ return new DashboardResource(projectState, user, null, null, null, true);
}
- private final ProjectControl control;
+ private final ProjectState projectState;
+ private final CurrentUser user;
private final String refName;
private final String pathName;
private final Config config;
private final boolean projectDefault;
public DashboardResource(
- ProjectControl control,
+ ProjectState projectState,
+ CurrentUser user,
String refName,
String pathName,
Config config,
boolean projectDefault) {
- this.control = control;
+ this.projectState = projectState;
+ this.user = user;
this.refName = refName;
this.pathName = pathName;
this.config = config;
this.projectDefault = projectDefault;
}
- public ProjectControl getControl() {
- return control;
+ public ProjectState getProjectState() {
+ return projectState;
+ }
+
+ public CurrentUser getUser() {
+ return user;
}
public String getRefName() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
index a9e8fd3..d5c591f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DashboardsCollection.java
@@ -106,9 +106,8 @@
public DashboardResource parse(ProjectResource parent, IdString id)
throws ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
- ProjectControl myCtl = parent.getControl();
if (isDefaultDashboard(id)) {
- return DashboardResource.projectDefault(myCtl);
+ return DashboardResource.projectDefault(parent.getProjectState(), parent.getUser());
}
DashboardInfo info;
@@ -118,10 +117,9 @@
throw new ResourceNotFoundException(id);
}
- CurrentUser user = myCtl.getUser();
- for (ProjectState ps : myCtl.getProjectState().tree()) {
+ for (ProjectState ps : parent.getProjectState().tree()) {
try {
- return parse(ps.controlFor(user), info, myCtl);
+ return parse(ps, parent.getProjectState(), parent.getUser(), info);
} catch (AmbiguousObjectException | ConfigInvalidException | IncorrectObjectTypeException e) {
throw new ResourceNotFoundException(id);
} catch (ResourceNotFoundException e) {
@@ -138,16 +136,13 @@
return ref;
}
- private DashboardResource parse(ProjectControl ctl, DashboardInfo info, ProjectControl myCtl)
+ private DashboardResource parse(
+ ProjectState parent, ProjectState current, CurrentUser user, DashboardInfo info)
throws ResourceNotFoundException, IOException, AmbiguousObjectException,
IncorrectObjectTypeException, ConfigInvalidException, PermissionBackendException {
String ref = normalizeDashboardRef(info.ref);
try {
- permissionBackend
- .user(ctl.getUser())
- .project(ctl.getProject().getNameKey())
- .ref(ref)
- .check(RefPermission.READ);
+ permissionBackend.user(user).project(parent.getNameKey()).ref(ref).check(RefPermission.READ);
} catch (AuthException e) {
// Don't leak the project's existence
throw new ResourceNotFoundException(info.id);
@@ -156,13 +151,13 @@
throw new ResourceNotFoundException(info.id);
}
- try (Repository git = gitManager.openRepository(ctl.getProject().getNameKey())) {
+ try (Repository git = gitManager.openRepository(parent.getNameKey())) {
ObjectId objId = git.resolve(ref + ":" + info.path);
if (objId == null) {
throw new ResourceNotFoundException(info.id);
}
BlobBasedConfig cfg = new BlobBasedConfig(null, git, objId);
- return new DashboardResource(myCtl, ref, info.path, cfg, false);
+ return new DashboardResource(current, user, ref, info.path, cfg, false);
} catch (RepositoryNotFoundException e) {
throw new ResourceNotFoundException(info.id);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
index ef5e41d..f577436 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/DefaultPermissionBackend.java
@@ -75,7 +75,7 @@
if (state != null) {
return state.controlFor(user).asForProject().database(db);
}
- return FailedPermissionBackend.project("not found");
+ return FailedPermissionBackend.project("not found", new NoSuchProjectException(project));
} catch (IOException e) {
return FailedPermissionBackend.project("unavailable", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetAccess.java
index 07c52f9..6b05ca1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetAccess.java
@@ -18,6 +18,7 @@
import static com.google.gerrit.server.permissions.ProjectPermission.CREATE_REF;
import static com.google.gerrit.server.permissions.RefPermission.CREATE_CHANGE;
import static com.google.gerrit.server.permissions.RefPermission.READ;
+import static com.google.gerrit.server.permissions.RefPermission.WRITE_CONFIG;
import static java.util.stream.Collectors.toMap;
import com.google.common.collect.ImmutableBiMap;
@@ -49,6 +50,7 @@
import com.google.gerrit.server.permissions.GlobalPermission;
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.permissions.RefPermission;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -90,7 +92,6 @@
private final ProjectJson projectJson;
private final ProjectCache projectCache;
private final MetaDataUpdate.Server metaDataUpdateFactory;
- private final ProjectControl.GenericFactory projectControlFactory;
private final GroupBackend groupBackend;
private final GroupJson groupJson;
@@ -103,7 +104,6 @@
ProjectCache projectCache,
MetaDataUpdate.Server metaDataUpdateFactory,
ProjectJson projectJson,
- ProjectControl.GenericFactory projectControlFactory,
GroupBackend groupBackend,
GroupJson groupJson) {
this.user = self;
@@ -112,7 +112,6 @@
this.allProjectsName = allProjectsName;
this.projectJson = projectJson;
this.projectCache = projectCache;
- this.projectControlFactory = projectControlFactory;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.groupBackend = groupBackend;
this.groupJson = groupJson;
@@ -121,11 +120,11 @@
public ProjectAccessInfo apply(Project.NameKey nameKey)
throws ResourceNotFoundException, ResourceConflictException, IOException,
PermissionBackendException, OrmException {
- try {
- return apply(new ProjectResource(projectControlFactory.controlFor(nameKey, user.get())));
- } catch (NoSuchProjectException e) {
+ ProjectState state = projectCache.checkedGet(nameKey);
+ if (state == null) {
throw new ResourceNotFoundException(nameKey.get());
}
+ return apply(new ProjectResource(state, user.get()));
}
@Override
@@ -138,7 +137,7 @@
Project.NameKey projectName = rsrc.getNameKey();
ProjectAccessInfo info = new ProjectAccessInfo();
- ProjectControl pc = createProjectControl(projectName);
+ ProjectState projectState = projectCache.checkedGet(projectName);
PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectName);
ProjectConfig config;
@@ -149,12 +148,12 @@
md.setMessage("Update group names\n");
config.commit(md);
projectCache.evict(config.getProject());
- pc = createProjectControl(projectName);
+ projectState = projectCache.checkedGet(projectName);
perm = permissionBackend.user(user).project(projectName);
} else if (config.getRevision() != null
- && !config.getRevision().equals(pc.getProjectState().getConfig().getRevision())) {
+ && !config.getRevision().equals(projectState.getConfig().getRevision())) {
projectCache.evict(config.getProject());
- pc = createProjectControl(projectName);
+ projectState = projectCache.checkedGet(projectName);
perm = permissionBackend.user(user).project(projectName);
}
} catch (ConfigInvalidException e) {
@@ -166,25 +165,26 @@
info.local = new HashMap<>();
info.ownerOf = new HashSet<>();
Map<AccountGroup.UUID, GroupInfo> visibleGroups = new HashMap<>();
- boolean checkReadConfig = check(perm, RefNames.REFS_CONFIG, READ);
+ boolean canReadConfig = check(perm, ProjectPermission.READ_CONFIG);
+ boolean canWriteConfig = check(perm, ProjectPermission.WRITE_CONFIG);
for (AccessSection section : config.getAccessSections()) {
String name = section.getName();
if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) {
- if (pc.isOwner()) {
+ if (canWriteConfig) {
info.local.put(name, createAccessSection(visibleGroups, section));
info.ownerOf.add(name);
- } else if (checkReadConfig) {
+ } else if (canReadConfig) {
info.local.put(section.getName(), createAccessSection(visibleGroups, section));
}
} else if (RefConfigSection.isValid(name)) {
- if (pc.controlForRef(name).isOwner()) {
+ if (check(perm, name, WRITE_CONFIG)) {
info.local.put(name, createAccessSection(visibleGroups, section));
info.ownerOf.add(name);
- } else if (checkReadConfig) {
+ } else if (canReadConfig) {
info.local.put(name, createAccessSection(visibleGroups, section));
} else if (check(perm, name, READ)) {
@@ -232,7 +232,7 @@
info.revision = config.getRevision().name();
}
- ProjectState parent = Iterables.getFirst(pc.getProjectState().parents(), null);
+ ProjectState parent = Iterables.getFirst(projectState.parents(), null);
if (parent != null) {
info.inheritsFrom = projectJson.format(parent.getProject());
}
@@ -242,13 +242,13 @@
info.ownerOf.add(AccessSection.GLOBAL_CAPABILITIES);
}
- info.isOwner = toBoolean(pc.isOwner());
+ info.isOwner = toBoolean(canWriteConfig);
info.canUpload =
toBoolean(
- pc.isOwner()
- || (checkReadConfig && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)));
+ canWriteConfig
+ || (canReadConfig && perm.ref(RefNames.REFS_CONFIG).testOrFalse(CREATE_CHANGE)));
info.canAdd = toBoolean(perm.testOrFalse(CREATE_REF));
- info.configVisible = checkReadConfig || pc.isOwner();
+ info.configVisible = canReadConfig || canWriteConfig;
info.groups =
visibleGroups
@@ -291,6 +291,16 @@
}
}
+ private static boolean check(PermissionBackend.ForProject ctx, ProjectPermission perm)
+ throws PermissionBackendException {
+ try {
+ ctx.check(perm);
+ return true;
+ } catch (AuthException denied) {
+ return false;
+ }
+ }
+
private AccessSectionInfo createAccessSection(
Map<AccountGroup.UUID, GroupInfo> groups, AccessSection section) throws OrmException {
AccessSectionInfo accessSectionInfo = new AccessSectionInfo();
@@ -316,15 +326,6 @@
return accessSectionInfo;
}
- private ProjectControl createProjectControl(Project.NameKey projectName)
- throws IOException, ResourceNotFoundException {
- try {
- return projectControlFactory.controlFor(projectName, user.get());
- } catch (NoSuchProjectException e) {
- throw new ResourceNotFoundException(projectName.get());
- }
- }
-
private static Boolean toBoolean(boolean value) {
return value ? true : null;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
index b1ba281..c2f816e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetConfig.java
@@ -59,7 +59,8 @@
public ConfigInfo apply(ProjectResource resource) {
return new ConfigInfoImpl(
serverEnableSignedPush,
- resource.getControl(),
+ resource.getProjectState(),
+ resource.getUser(),
config,
pluginConfigEntries,
cfgFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
index cdf23bb..d4d9a54 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetDashboard.java
@@ -28,6 +28,7 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import java.io.IOException;
@@ -52,64 +53,63 @@
}
@Override
- public DashboardInfo apply(DashboardResource resource)
+ public DashboardInfo apply(DashboardResource rsrc)
throws RestApiException, IOException, PermissionBackendException {
- if (inherited && !resource.isProjectDefault()) {
+ if (inherited && !rsrc.isProjectDefault()) {
throw new BadRequestException("inherited flag can only be used with default");
}
- String project = resource.getControl().getProject().getName();
- if (resource.isProjectDefault()) {
+ if (rsrc.isProjectDefault()) {
// The default is not resolved to a definition yet.
try {
- resource = defaultOf(resource.getControl());
+ rsrc = defaultOf(rsrc.getProjectState(), rsrc.getUser());
} catch (ConfigInvalidException e) {
throw new ResourceConflictException(e.getMessage());
}
}
return DashboardsCollection.parse(
- resource.getControl().getProject(),
- resource.getRefName().substring(REFS_DASHBOARDS.length()),
- resource.getPathName(),
- resource.getConfig(),
- project,
+ rsrc.getProjectState().getProject(),
+ rsrc.getRefName().substring(REFS_DASHBOARDS.length()),
+ rsrc.getPathName(),
+ rsrc.getConfig(),
+ rsrc.getProjectState().getName(),
true);
}
- private DashboardResource defaultOf(ProjectControl ctl)
+ private DashboardResource defaultOf(ProjectState projectState, CurrentUser user)
throws ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
- String id = ctl.getProject().getLocalDefaultDashboard();
+ String id = projectState.getProject().getLocalDefaultDashboard();
if (Strings.isNullOrEmpty(id)) {
- id = ctl.getProject().getDefaultDashboard();
+ id = projectState.getProject().getDefaultDashboard();
}
if (isDefaultDashboard(id)) {
throw new ResourceNotFoundException();
} else if (!Strings.isNullOrEmpty(id)) {
- return parse(ctl, id);
+ return parse(projectState, user, id);
} else if (!inherited) {
throw new ResourceNotFoundException();
}
- for (ProjectState ps : ctl.getProjectState().tree()) {
+ for (ProjectState ps : projectState.tree()) {
id = ps.getProject().getDefaultDashboard();
if (isDefaultDashboard(id)) {
throw new ResourceNotFoundException();
} else if (!Strings.isNullOrEmpty(id)) {
- ctl = ps.controlFor(ctl.getUser());
- return parse(ctl, id);
+ return parse(projectState, user, id);
}
}
throw new ResourceNotFoundException();
}
- private DashboardResource parse(ProjectControl ctl, String id)
+ private DashboardResource parse(ProjectState projectState, CurrentUser user, String id)
throws ResourceNotFoundException, IOException, ConfigInvalidException,
PermissionBackendException {
List<String> p = Lists.newArrayList(Splitter.on(':').limit(2).split(id));
String ref = Url.encode(p.get(0));
String path = Url.encode(p.get(1));
- return dashboards.parse(new ProjectResource(ctl), IdString.fromUrl(ref + ':' + path));
+ return dashboards.parse(
+ new ProjectResource(projectState, user), IdString.fromUrl(ref + ':' + path));
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
index 31dc7bf..daaf4ef 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetHead.java
@@ -20,6 +20,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
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.permissions.RefPermission;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -72,10 +73,14 @@
}
throw new AuthException("not allowed to see HEAD");
} catch (MissingObjectException | IncorrectObjectTypeException e) {
- if (rsrc.getControl().isOwner()) {
- return head.getObjectId().name();
+ try {
+ permissionBackend
+ .user(rsrc.getUser())
+ .project(rsrc.getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
+ } catch (AuthException ae) {
+ throw new AuthException("not allowed to see HEAD");
}
- throw new AuthException("not allowed to see HEAD");
}
}
throw new ResourceNotFoundException(Constants.HEAD);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
index 44d6a4f..9643e09 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/GetReflog.java
@@ -16,7 +16,6 @@
import com.google.common.collect.Lists;
import com.google.gerrit.extensions.api.projects.ReflogEntryInfo;
-import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -24,6 +23,9 @@
import com.google.gerrit.server.CommonConverters;
import com.google.gerrit.server.args4j.TimestampHandler;
import com.google.gerrit.server.git.GitRepositoryManager;
+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 java.io.IOException;
import java.sql.Timestamp;
@@ -40,6 +42,7 @@
private static final Logger log = LoggerFactory.getLogger(GetReflog.class);
private final GitRepositoryManager repoManager;
+ private final PermissionBackend permissionBackend;
@Option(
name = "--limit",
@@ -83,15 +86,18 @@
private Timestamp to;
@Inject
- public GetReflog(GitRepositoryManager repoManager) {
+ public GetReflog(GitRepositoryManager repoManager, PermissionBackend permissionBackend) {
this.repoManager = repoManager;
+ this.permissionBackend = permissionBackend;
}
@Override
- public List<ReflogEntryInfo> apply(BranchResource rsrc) throws RestApiException, IOException {
- if (!rsrc.getControl().isOwner()) {
- throw new AuthException("not project owner");
- }
+ public List<ReflogEntryInfo> apply(BranchResource rsrc)
+ throws RestApiException, IOException, PermissionBackendException {
+ permissionBackend
+ .user(rsrc.getUser())
+ .project(rsrc.getNameKey())
+ .check(ProjectPermission.READ_REFLOG);
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
ReflogReader r;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
index b2edc6b..645058f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListBranches.java
@@ -179,7 +179,6 @@
}
}
- ProjectControl pctl = rsrc.getControl();
PermissionBackend.ForProject perm = permissionBackend.user(user).project(rsrc.getNameKey());
List<BranchInfo> branches = new ArrayList<>(refs.size());
for (Ref ref : refs) {
@@ -207,7 +206,9 @@
}
if (perm.ref(ref.getName()).test(RefPermission.READ)) {
- branches.add(createBranchInfo(perm.ref(ref.getName()), ref, pctl, targets));
+ branches.add(
+ createBranchInfo(
+ perm.ref(ref.getName()), ref, rsrc.getProjectState(), rsrc.getUser(), targets));
}
}
Collections.sort(branches, new BranchComparator());
@@ -234,14 +235,18 @@
}
private BranchInfo createBranchInfo(
- PermissionBackend.ForRef perm, Ref ref, ProjectControl pctl, Set<String> targets) {
+ PermissionBackend.ForRef perm,
+ Ref ref,
+ ProjectState projectState,
+ CurrentUser user,
+ Set<String> targets) {
BranchInfo info = new BranchInfo();
info.ref = ref.getName();
info.revision = ref.getObjectId() != null ? ref.getObjectId().name() : null;
info.canDelete =
!targets.contains(ref.getName()) && perm.testOrFalse(RefPermission.DELETE) ? true : null;
- BranchResource rsrc = new BranchResource(pctl, ref);
+ BranchResource rsrc = new BranchResource(projectState, user, ref);
for (UiAction.Description d : uiActions.from(branchViews, rsrc)) {
if (info.actions == null) {
info.actions = new TreeMap<>();
@@ -249,7 +254,7 @@
info.actions.put(d.getId(), new ActionInfo(d));
}
- List<WebLinkInfo> links = webLinks.getBranchLinks(pctl.getProject().getName(), ref.getName());
+ List<WebLinkInfo> links = webLinks.getBranchLinks(projectState.getName(), ref.getName());
info.webLinks = links.isEmpty() ? null : links;
return info;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
index dc3610c..afb796e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -355,17 +355,15 @@
continue;
}
- final ProjectControl pctl = e.controlFor(currentUser);
if (groupUuid != null
- && !pctl.getProjectState()
- .getLocalGroups()
+ && !e.getLocalGroups()
.contains(GroupReference.forGroup(groupsCollection.parseId(groupUuid.get())))) {
continue;
}
ProjectInfo info = new ProjectInfo();
if (showTree && !format.isJson()) {
- treeMap.put(projectName, projectNodeFactory.create(pctl.getProject(), true));
+ treeMap.put(projectName, projectNodeFactory.create(e.getProject(), true));
continue;
}
@@ -396,8 +394,17 @@
if (!type.matches(git)) {
continue;
}
-
- List<Ref> refs = getBranchRefs(projectName, pctl);
+ boolean canReadAllRefs;
+ try {
+ permissionBackend
+ .user(currentUser)
+ .project(e.getNameKey())
+ .check(ProjectPermission.READ);
+ canReadAllRefs = true;
+ } catch (AuthException ae) {
+ canReadAllRefs = false;
+ }
+ List<Ref> refs = getBranchRefs(projectName, canReadAllRefs);
if (!hasValidRef(refs)) {
continue;
}
@@ -592,13 +599,13 @@
stdout.flush();
}
- private List<Ref> getBranchRefs(Project.NameKey projectName, ProjectControl projectControl) {
+ private List<Ref> getBranchRefs(Project.NameKey projectName, boolean canReadAllRefs) {
Ref[] result = new Ref[showBranch.size()];
try (Repository git = repoManager.openRepository(projectName)) {
PermissionBackend.ForProject perm = permissionBackend.user(currentUser).project(projectName);
for (int i = 0; i < showBranch.size(); i++) {
Ref ref = git.findRef(showBranch.get(i));
- if (all && projectControl.isOwner()) {
+ if (all && canReadAllRefs) {
result[i] = ref;
} else if (ref != null && ref.getObjectId() != null) {
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index 1166970..f189fb7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -201,7 +201,7 @@
}
/** Is this user a project owner? */
- public boolean isOwner() {
+ boolean isOwner() {
return (isDeclaredOwner() && !controlForRef("refs/*").isBlocked(Permission.OWNER)) || isAdmin();
}
@@ -474,6 +474,12 @@
return canRunReceivePack();
case RUN_UPLOAD_PACK:
return canRunUploadPack();
+
+ case BAN_COMMIT:
+ case READ_REFLOG:
+ case READ_CONFIG:
+ case WRITE_CONFIG:
+ return isOwner();
}
throw new PermissionBackendException(perm + " unsupported");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
index a91ba62..22b7bd9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectResource.java
@@ -24,33 +24,32 @@
public static final TypeLiteral<RestView<ProjectResource>> PROJECT_KIND =
new TypeLiteral<RestView<ProjectResource>>() {};
- private final ProjectControl control;
+ private final ProjectState projectState;
+ private final CurrentUser user;
- public ProjectResource(ProjectControl control) {
- this.control = control;
+ public ProjectResource(ProjectState projectState, CurrentUser user) {
+ this.projectState = projectState;
+ this.user = user;
}
ProjectResource(ProjectResource rsrc) {
- this.control = rsrc.getControl();
+ this.projectState = rsrc.getProjectState();
+ this.user = rsrc.getUser();
}
public String getName() {
- return control.getProject().getName();
+ return projectState.getName();
}
public Project.NameKey getNameKey() {
- return control.getProject().getNameKey();
+ return projectState.getNameKey();
}
public ProjectState getProjectState() {
- return control.getProjectState();
+ return projectState;
}
public CurrentUser getUser() {
- return getControl().getUser();
- }
-
- public ProjectControl getControl() {
- return control;
+ return user;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
index 2a79470..8d7b156 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectsCollection.java
@@ -47,7 +47,7 @@
private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<ListProjects> list;
private final Provider<QueryProjects> queryProjects;
- private final ProjectControl.GenericFactory controlFactory;
+ private final ProjectCache projectCache;
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final CreateProject.Factory createProjectFactory;
@@ -59,14 +59,14 @@
DynamicMap<RestView<ProjectResource>> views,
Provider<ListProjects> list,
Provider<QueryProjects> queryProjects,
- ProjectControl.GenericFactory controlFactory,
+ ProjectCache projectCache,
PermissionBackend permissionBackend,
CreateProject.Factory factory,
Provider<CurrentUser> user) {
this.views = views;
this.list = list;
this.queryProjects = queryProjects;
- this.controlFactory = controlFactory;
+ this.projectCache = projectCache;
this.permissionBackend = permissionBackend;
this.user = user;
this.createProjectFactory = factory;
@@ -139,10 +139,8 @@
}
Project.NameKey nameKey = new Project.NameKey(id);
- ProjectControl ctl;
- try {
- ctl = controlFactory.controlFor(nameKey, user.get());
- } catch (NoSuchProjectException e) {
+ ProjectState state = projectCache.checkedGet(nameKey);
+ if (state == null) {
return null;
}
@@ -153,7 +151,7 @@
return null; // Pretend like not found on access denied.
}
}
- return new ProjectResource(ctl);
+ return new ProjectResource(state, user.get());
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
index c4a7eb4..9dd8b43 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutConfig.java
@@ -22,7 +22,6 @@
import com.google.gerrit.extensions.api.projects.ConfigValue;
import com.google.gerrit.extensions.api.projects.ProjectConfigEntryType;
import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -40,6 +39,9 @@
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.git.TransferConfig;
+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;
@@ -68,6 +70,7 @@
private final UiActions uiActions;
private final DynamicMap<RestView<ProjectResource>> views;
private final Provider<CurrentUser> user;
+ private final PermissionBackend permissionBackend;
@Inject
PutConfig(
@@ -81,7 +84,8 @@
AllProjectsName allProjects,
UiActions uiActions,
DynamicMap<RestView<ProjectResource>> views,
- Provider<CurrentUser> user) {
+ Provider<CurrentUser> user,
+ PermissionBackend permissionBackend) {
this.serverEnableSignedPush = serverEnableSignedPush;
this.metaDataUpdateFactory = metaDataUpdateFactory;
this.projectCache = projectCache;
@@ -93,13 +97,13 @@
this.uiActions = uiActions;
this.views = views;
this.user = user;
+ this.permissionBackend = permissionBackend;
}
@Override
- public ConfigInfo apply(ProjectResource rsrc, ConfigInput input) throws RestApiException {
- if (!rsrc.getControl().isOwner()) {
- throw new AuthException("restricted to project owner");
- }
+ public ConfigInfo apply(ProjectResource rsrc, ConfigInput input)
+ throws RestApiException, PermissionBackendException {
+ permissionBackend.user(user).project(rsrc.getNameKey()).check(ProjectPermission.WRITE_CONFIG);
return apply(rsrc.getProjectState(), input);
}
@@ -192,7 +196,8 @@
ProjectState state = projectStateFactory.create(projectConfig);
return new ConfigInfoImpl(
serverEnableSignedPush,
- state.controlFor(user.get()),
+ state,
+ user.get(),
config,
pluginConfigEntries,
cfgFactory,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
index 78230bd..a2808fc 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/PutDescription.java
@@ -26,6 +26,9 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
+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.Singleton;
import java.io.IOException;
@@ -36,25 +39,31 @@
public class PutDescription implements RestModifyView<ProjectResource, DescriptionInput> {
private final ProjectCache cache;
private final MetaDataUpdate.Server updateFactory;
+ private final PermissionBackend permissionBackend;
@Inject
- PutDescription(ProjectCache cache, MetaDataUpdate.Server updateFactory) {
+ PutDescription(
+ ProjectCache cache,
+ MetaDataUpdate.Server updateFactory,
+ PermissionBackend permissionBackend) {
this.cache = cache;
this.updateFactory = updateFactory;
+ this.permissionBackend = permissionBackend;
}
@Override
public Response<String> apply(ProjectResource resource, DescriptionInput input)
- throws AuthException, ResourceConflictException, ResourceNotFoundException, IOException {
+ throws AuthException, ResourceConflictException, ResourceNotFoundException, IOException,
+ PermissionBackendException {
if (input == null) {
input = new DescriptionInput(); // Delete would set description to null.
}
- ProjectControl ctl = resource.getControl();
- IdentifiedUser user = ctl.getUser().asIdentifiedUser();
- if (!ctl.isOwner()) {
- throw new AuthException("not project owner");
- }
+ IdentifiedUser user = resource.getUser().asIdentifiedUser();
+ permissionBackend
+ .user(user)
+ .project(resource.getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
try (MetaDataUpdate md = updateFactory.create(resource.getNameKey())) {
ProjectConfig config = ProjectConfig.read(md);
@@ -70,7 +79,7 @@
md.setAuthor(user);
md.setMessage(msg);
config.commit(md);
- cache.evict(ctl.getProject());
+ cache.evict(resource.getProjectState().getProject());
md.getRepository().setGitwebDescription(project.getDescription());
return Strings.isNullOrEmpty(project.getDescription())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index f693bf5..9a4fe96 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -551,6 +551,8 @@
return canUpdate();
case FORCE_UPDATE:
return canForceUpdate();
+ case SET_HEAD:
+ return projectControl.isOwner();
case FORGE_AUTHOR:
return canForgeAuthor();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefResource.java
index 124439f..ac2735d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefResource.java
@@ -14,10 +14,12 @@
package com.google.gerrit.server.project;
+import com.google.gerrit.server.CurrentUser;
+
public abstract class RefResource extends ProjectResource {
- public RefResource(ProjectControl control) {
- super(control);
+ public RefResource(ProjectState projectState, CurrentUser user) {
+ super(projectState, user);
}
/** @return the ref's name */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RemoveReviewerControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RemoveReviewerControl.java
index 757f327..8f980ee 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RemoveReviewerControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RemoveReviewerControl.java
@@ -95,6 +95,7 @@
// Users with the remove reviewer permission, the branch owner, project
// owner and site admin can remove anyone
+ // TODO(hiesel): Remove all Control usage
ProjectControl ctl = projectControlFactory.controlFor(change.getProject(), currentUser);
if (ctl.controlForRef(change.getDest()).isOwner() // branch owner
|| ctl.isOwner() // project owner
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
index e875388..c768315 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetAccess.java
@@ -34,6 +34,7 @@
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -93,9 +94,12 @@
permissionBackend.user(identifiedUser).check(GlobalPermission.ADMINISTRATE_SERVER);
checkedAdmin = true;
}
- } else if (!rsrc.getControl().controlForRef(section.getName()).isOwner()) {
- throw new AuthException(
- "You are not allowed to edit permissions for ref: " + section.getName());
+ } else {
+ permissionBackend
+ .user(identifiedUser)
+ .project(rsrc.getNameKey())
+ .ref(section.getName())
+ .check(RefPermission.WRITE_CONFIG);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
index 9aa9ae7..0dd5f85 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetDefaultDashboard.java
@@ -18,7 +18,6 @@
import com.google.common.base.Strings;
import com.google.gerrit.extensions.api.projects.DashboardInfo;
import com.google.gerrit.extensions.common.SetDashboardInput;
-import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
@@ -29,7 +28,9 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
+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 java.io.IOException;
@@ -42,6 +43,7 @@
private final MetaDataUpdate.Server updateFactory;
private final DashboardsCollection dashboards;
private final Provider<GetDashboard> get;
+ private final PermissionBackend permissionBackend;
@Option(name = "--inherited", usage = "set dashboard inherited by children")
private boolean inherited;
@@ -51,30 +53,35 @@
ProjectCache cache,
MetaDataUpdate.Server updateFactory,
DashboardsCollection dashboards,
- Provider<GetDashboard> get) {
+ Provider<GetDashboard> get,
+ PermissionBackend permissionBackend) {
this.cache = cache;
this.updateFactory = updateFactory;
this.dashboards = dashboards;
this.get = get;
+ this.permissionBackend = permissionBackend;
}
@Override
- public Response<DashboardInfo> apply(DashboardResource resource, SetDashboardInput input)
+ public Response<DashboardInfo> apply(DashboardResource rsrc, SetDashboardInput input)
throws RestApiException, IOException, PermissionBackendException {
if (input == null) {
input = new SetDashboardInput(); // Delete would set input to null.
}
input.id = Strings.emptyToNull(input.id);
- ProjectControl ctl = resource.getControl();
- if (!ctl.isOwner()) {
- throw new AuthException("not project owner");
- }
+ permissionBackend
+ .user(rsrc.getUser())
+ .project(rsrc.getProjectState().getNameKey())
+ .check(ProjectPermission.WRITE_CONFIG);
DashboardResource target = null;
if (input.id != null) {
try {
- target = dashboards.parse(new ProjectResource(ctl), IdString.fromUrl(input.id));
+ target =
+ dashboards.parse(
+ new ProjectResource(rsrc.getProjectState(), rsrc.getUser()),
+ IdString.fromUrl(input.id));
} catch (ResourceNotFoundException e) {
throw new BadRequestException("dashboard " + input.id + " not found");
} catch (ConfigInvalidException e) {
@@ -82,7 +89,7 @@
}
}
- try (MetaDataUpdate md = updateFactory.create(ctl.getProject().getNameKey())) {
+ try (MetaDataUpdate md = updateFactory.create(rsrc.getProjectState().getNameKey())) {
ProjectConfig config = ProjectConfig.read(md);
Project project = config.getProject();
if (inherited) {
@@ -100,10 +107,10 @@
if (!msg.endsWith("\n")) {
msg += "\n";
}
- md.setAuthor(ctl.getUser().asIdentifiedUser());
+ md.setAuthor(rsrc.getUser().asIdentifiedUser());
md.setMessage(msg);
config.commit(md);
- cache.evict(ctl.getProject());
+ cache.evict(rsrc.getProjectState().getProject());
if (target != null) {
DashboardInfo info = get.get().apply(target);
@@ -112,7 +119,7 @@
}
return Response.none();
} catch (RepositoryNotFoundException notFound) {
- throw new ResourceNotFoundException(ctl.getProject().getName());
+ throw new ResourceNotFoundException(rsrc.getProjectState().getProject().getName());
} catch (ConfigInvalidException e) {
throw new ResourceConflictException(
String.format("invalid project.config: %s", e.getMessage()));
@@ -135,7 +142,8 @@
throws RestApiException, IOException, PermissionBackendException {
SetDefaultDashboard set = setDefault.get();
set.inherited = inherited;
- return set.apply(DashboardResource.projectDefault(resource.getControl()), input);
+ return set.apply(
+ DashboardResource.projectDefault(resource.getProjectState(), resource.getUser()), input);
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
index eeb47df..b1a6999 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
@@ -28,6 +28,9 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.extensions.events.AbstractNoNotifyEvent;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.permissions.RefPermission;
import com.google.gerrit.server.project.SetHead.Input;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -53,29 +56,35 @@
private final GitRepositoryManager repoManager;
private final Provider<IdentifiedUser> identifiedUser;
private final DynamicSet<HeadUpdatedListener> headUpdatedListeners;
+ private final PermissionBackend permissionBackend;
@Inject
SetHead(
GitRepositoryManager repoManager,
Provider<IdentifiedUser> identifiedUser,
- DynamicSet<HeadUpdatedListener> headUpdatedListeners) {
+ DynamicSet<HeadUpdatedListener> headUpdatedListeners,
+ PermissionBackend permissionBackend) {
this.repoManager = repoManager;
this.identifiedUser = identifiedUser;
this.headUpdatedListeners = headUpdatedListeners;
+ this.permissionBackend = permissionBackend;
}
@Override
public String apply(ProjectResource rsrc, Input input)
throws AuthException, ResourceNotFoundException, BadRequestException,
- UnprocessableEntityException, IOException {
- if (!rsrc.getControl().isOwner()) {
- throw new AuthException("restricted to project owner");
- }
+ UnprocessableEntityException, IOException, PermissionBackendException {
if (input == null || Strings.isNullOrEmpty(input.ref)) {
throw new BadRequestException("ref required");
}
String ref = RefNames.fullName(input.ref);
+ permissionBackend
+ .user(rsrc.getUser())
+ .project(rsrc.getNameKey())
+ .ref(ref)
+ .check(RefPermission.SET_HEAD);
+
try (Repository repo = repoManager.openRepository(rsrc.getNameKey())) {
Map<String, Ref> cur = repo.getRefDatabase().exactRef(Constants.HEAD, ref);
if (!cur.containsKey(ref)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java
index fe4d68d..08ef669 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagResource.java
@@ -16,6 +16,7 @@
import com.google.gerrit.extensions.api.projects.TagInfo;
import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.CurrentUser;
import com.google.inject.TypeLiteral;
public class TagResource extends RefResource {
@@ -24,8 +25,8 @@
private final TagInfo tagInfo;
- public TagResource(ProjectControl control, TagInfo tagInfo) {
- super(control);
+ public TagResource(ProjectState projectState, CurrentUser user, TagInfo tagInfo) {
+ super(projectState, user);
this.tagInfo = tagInfo;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagsCollection.java
index 78670ad..7ee0a8e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/TagsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/TagsCollection.java
@@ -48,9 +48,9 @@
}
@Override
- public TagResource parse(ProjectResource resource, IdString id)
+ public TagResource parse(ProjectResource rsrc, IdString id)
throws ResourceNotFoundException, IOException {
- return new TagResource(resource.getControl(), list.get().get(resource, id));
+ return new TagResource(rsrc.getProjectState(), rsrc.getUser(), list.get().get(rsrc, id));
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
index 17296bc..19549d9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeIsVisibleToPredicate.java
@@ -23,10 +23,15 @@
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class ChangeIsVisibleToPredicate extends IsVisibleToPredicate<ChangeData> {
+ private static final Logger logger = LoggerFactory.getLogger(ChangeIsVisibleToPredicate.class);
+
protected final Provider<ReviewDb> db;
protected final ChangeNotes.Factory notesFactory;
protected final CurrentUser user;
@@ -64,6 +69,10 @@
.database(db)
.test(ChangePermission.READ);
} catch (PermissionBackendException e) {
+ if (e.getCause() instanceof NoSuchProjectException) {
+ logger.info("No such project: {}", cd.project());
+ return false;
+ }
throw new OrmException("unable to check permissions", e);
}
if (visible) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index c7a85f0..ccc11aa 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -476,7 +476,10 @@
new ChangeIdPredicate(parseChangeId(triplet.get().id().get())));
}
if (PAT_LEGACY_ID.matcher(query).matches()) {
- return new LegacyChangeIdPredicate(Change.Id.parse(query));
+ Integer id = Ints.tryParse(query);
+ if (id != null) {
+ return new LegacyChangeIdPredicate(new Change.Id(id));
+ }
} else if (PAT_CHANGE_ID.matcher(query).matches()) {
return new ChangeIdPredicate(parseChangeId(query));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index 1917d6f..785ae38 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -79,7 +79,7 @@
for (PatchSetApproval p : object.currentApprovals()) {
if (labelType.matches(p)) {
hasVote = true;
- if (match(object, p.getValue(), p.getAccountId(), labelType)) {
+ if (match(object, p.getValue(), p.getAccountId())) {
return true;
}
}
@@ -105,7 +105,7 @@
return null;
}
- protected boolean match(ChangeData cd, short value, Account.Id approver, LabelType type) {
+ protected boolean match(ChangeData cd, short value, Account.Id approver) {
if (value != expVal) {
return false;
}
@@ -119,11 +119,11 @@
return false;
}
- // Double check the value is still permitted for the user.
+ // Check the user has 'READ' permission.
try {
PermissionBackend.ForChange perm =
permissionBackend.user(reviewer).database(dbProvider).change(cd);
- return perm.test(ChangePermission.READ) && expVal == perm.squashByTest(type, value);
+ return perm.test(ChangePermission.READ);
} catch (PermissionBackendException e) {
return false;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
index 19c0515..e3ff21a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ParentProjectPredicate.java
@@ -58,7 +58,7 @@
List<Predicate<ChangeData>> r = new ArrayList<>();
r.add(new ProjectPredicate(projectState.getName()));
try {
- ProjectResource proj = new ProjectResource(projectState.controlFor(self.get()));
+ ProjectResource proj = new ProjectResource(projectState, self.get());
ListChildProjects children = listChildProjects.get();
children.setRecursive(true);
for (ProjectInfo p : children.apply(proj)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index 5c658a8..c274e56 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -151,7 +151,7 @@
return "org.postgresql.Driver";
}
if (url.contains(MYSQL)) {
- return "com.mysql.cj.jdbc.Driver";
+ return "com.mysql.jdbc.Driver";
}
if (url.contains(MARIADB)) {
return "org.mariadb.jdbc.Driver";
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/MySql.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/MySql.java
index ffa2293..f4f19f0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/MySql.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/MySql.java
@@ -28,7 +28,7 @@
@Inject
MySql(@GerritServerConfig Config cfg) {
- super("com.mysql.cj.jdbc.Driver");
+ super("com.mysql.jdbc.Driver");
this.cfg = cfg;
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
index e036495..3299e11 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryModule.java
@@ -47,6 +47,7 @@
import com.google.gerrit.server.config.TrackingFootersProvider;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.LocalMergeSuperSetComputation;
import com.google.gerrit.server.git.PerThreadRequestScope;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.SendEmailExecutor;
@@ -202,7 +203,7 @@
return CanonicalWebUrlProvider.class;
}
});
- //Replacement of DiffExecutorModule to not use thread pool in the tests
+ // Replacement of DiffExecutorModule to not use thread pool in the tests
install(
new AbstractModule() {
@Override
@@ -220,6 +221,7 @@
install(new SignedTokenEmailTokenVerifier.Module());
install(new GpgModule(cfg));
install(new InMemoryAccountPatchReviewStore.Module());
+ install(new LocalMergeSuperSetComputation.Module());
bind(AllAccountsIndexer.class).toProvider(Providers.of(null));
bind(AllChangesIndexer.class).toProvider(Providers.of(null));
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AbstractGitCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AbstractGitCommand.java
index b9a98b9..710b3dc 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AbstractGitCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/AbstractGitCommand.java
@@ -19,7 +19,6 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.inject.Inject;
@@ -31,7 +30,7 @@
public abstract class AbstractGitCommand extends BaseCommand {
@Argument(index = 0, metaVar = "PROJECT.git", required = true, usage = "project name")
- protected ProjectControl projectControl;
+ protected ProjectState projectState;
@Inject private SshScope sshScope;
@@ -41,12 +40,9 @@
@Inject private SshScope.Context context;
- @Inject private IdentifiedUser user;
-
@Inject private IdentifiedUser.GenericFactory userFactory;
protected Repository repo;
- protected ProjectState state;
protected Project.NameKey projectName;
protected Project project;
@@ -69,7 +65,7 @@
@Override
public Project.NameKey getProjectName() {
- return projectControl.getProjectState().getNameKey();
+ return projectState.getNameKey();
}
});
} finally {
@@ -88,8 +84,7 @@
}
private void service() throws IOException, PermissionBackendException, Failure {
- state = projectControl.getProjectState();
- project = state.getProject();
+ project = projectState.getProject();
projectName = project.getNameKey();
try {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
index 6923ad1..fa3a0f5 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/BaseCommand.java
@@ -79,6 +79,8 @@
private ExitCallback exit;
+ @Inject protected CurrentUser user;
+
@Inject private SshScope sshScope;
@Inject private CmdLineParser.Factory cmdLineParserFactory;
@@ -88,7 +90,6 @@
@Inject @CommandExecutor private ScheduledThreadPoolExecutor executor;
@Inject private PermissionBackend permissionBackend;
- @Inject private CurrentUser user;
@Inject private SshScope.Context context;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index 1c55f48..d5fc4547 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -28,7 +28,7 @@
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -67,15 +67,15 @@
}
public void addChange(
- String id, Map<Change.Id, ChangeResource> changes, ProjectControl projectControl)
+ String id, Map<Change.Id, ChangeResource> changes, ProjectState projectState)
throws UnloggedFailure, OrmException, PermissionBackendException {
- addChange(id, changes, projectControl, true);
+ addChange(id, changes, projectState, true);
}
public void addChange(
String id,
Map<Change.Id, ChangeResource> changes,
- ProjectControl projectControl,
+ ProjectState projectState,
boolean useIndex)
throws UnloggedFailure, OrmException, PermissionBackendException {
List<ChangeNotes> matched = useIndex ? changeFinder.find(id) : changeFromNotesFactory(id);
@@ -89,7 +89,7 @@
}
for (ChangeNotes notes : matched) {
if (!changes.containsKey(notes.getChangeId())
- && inProject(projectControl, notes.getProjectName())
+ && inProject(projectState, notes.getProjectName())
&& (canMaintainServer
|| permissionBackend
.user(currentUser)
@@ -127,9 +127,9 @@
}
}
- private boolean inProject(ProjectControl projectControl, Project.NameKey project) {
- if (projectControl != null) {
- return projectControl.getProject().getNameKey().equals(project);
+ private boolean inProject(ProjectState projectState, Project.NameKey project) {
+ if (projectState != null) {
+ return projectState.getNameKey().equals(project);
}
// No --project option, so they want every project.
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
index 6649fcb..c6e00aa 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -14,6 +14,8 @@
package com.google.gerrit.sshd.commands;
+import static java.util.stream.Collectors.toList;
+
import com.google.gerrit.common.data.GlobalCapability;
import com.google.gerrit.extensions.annotations.RequiresCapability;
import com.google.gerrit.extensions.common.ProjectInfo;
@@ -24,7 +26,6 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ListChildProjects;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.CommandMetaData;
@@ -57,21 +58,21 @@
metaVar = "NAME",
usage = "new parent project"
)
- private ProjectControl newParent;
+ private ProjectState newParent;
@Option(
name = "--children-of",
metaVar = "NAME",
usage = "parent project for which the child projects should be reparented"
)
- private ProjectControl oldParent;
+ private ProjectState oldParent;
@Option(
name = "--exclude",
metaVar = "NAME",
usage = "child project of old parent project which should not be reparented"
)
- private List<ProjectControl> excludedChildren = new ArrayList<>();
+ private List<ProjectState> excludedChildren = new ArrayList<>();
@Argument(
index = 0,
@@ -80,7 +81,7 @@
metaVar = "NAME",
usage = "projects to modify"
)
- private List<ProjectControl> children = new ArrayList<>();
+ private List<ProjectState> children = new ArrayList<>();
@Inject private ProjectCache projectCache;
@@ -125,10 +126,8 @@
}
}
- final List<Project.NameKey> childProjects = new ArrayList<>();
- for (ProjectControl pc : children) {
- childProjects.add(pc.getProject().getNameKey());
- }
+ final List<Project.NameKey> childProjects =
+ children.stream().map(ProjectState::getNameKey).collect(toList());
if (oldParent != null) {
try {
childProjects.addAll(getChildrenForReparenting(oldParent));
@@ -196,18 +195,18 @@
* list of child projects does not contain projects that were specified to be excluded from
* reparenting.
*/
- private List<Project.NameKey> getChildrenForReparenting(ProjectControl parent)
+ private List<Project.NameKey> getChildrenForReparenting(ProjectState parent)
throws PermissionBackendException {
final List<Project.NameKey> childProjects = new ArrayList<>();
final List<Project.NameKey> excluded = new ArrayList<>(excludedChildren.size());
- for (ProjectControl excludedChild : excludedChildren) {
+ for (ProjectState excludedChild : excludedChildren) {
excluded.add(excludedChild.getProject().getNameKey());
}
final List<Project.NameKey> automaticallyExcluded = new ArrayList<>(excludedChildren.size());
if (newParentKey != null) {
automaticallyExcluded.addAll(getAllParents(newParentKey));
}
- for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent))) {
+ for (ProjectInfo child : listChildProjects.apply(new ProjectResource(parent, user))) {
final Project.NameKey childName = new Project.NameKey(child.name);
if (!excluded.contains(childName)) {
if (!automaticallyExcluded.contains(childName)) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index 22d7e9a..81347d8 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -20,8 +20,8 @@
import com.google.common.collect.Lists;
import com.google.gerrit.server.project.BanCommit;
import com.google.gerrit.server.project.BanCommit.BanResultInfo;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -51,7 +51,7 @@
metaVar = "PROJECT",
usage = "name of the project for which the commit should be banned"
)
- private ProjectControl projectControl;
+ private ProjectState projectState;
@Argument(
index = 1,
@@ -71,7 +71,7 @@
BanCommit.Input.fromCommits(Lists.transform(commitsToBan, ObjectId::getName));
input.reason = reason;
- BanResultInfo r = banCommit.apply(new ProjectResource(projectControl), input);
+ BanResultInfo r = banCommit.apply(new ProjectResource(projectState, user), input);
printCommits(r.newlyBanned, "The following commits were banned");
printCommits(r.alreadyBanned, "The following commits were already banned");
printCommits(r.ignored, "The following ids do not represent commits and were ignored");
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
index 5962faa..fd1e189 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateBranchCommand.java
@@ -17,7 +17,7 @@
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -28,7 +28,7 @@
public final class CreateBranchCommand extends SshCommand {
@Argument(index = 0, required = true, metaVar = "PROJECT", usage = "name of the project")
- private ProjectControl project;
+ private ProjectState project;
@Argument(index = 1, required = true, metaVar = "NAME", usage = "name of branch to be created")
private String name;
@@ -48,7 +48,7 @@
try {
BranchInput in = new BranchInput();
in.revision = revision;
- gApi.projects().name(project.getProject().getNameKey().get()).branch(name).create(in);
+ gApi.projects().name(project.getName()).branch(name).create(in);
} catch (RestApiException e) {
throw die(e);
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
index 0df2a80..d6ecb0a 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProjectCommand.java
@@ -28,7 +28,7 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SuggestParentCandidates;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -68,7 +68,7 @@
metaVar = "NAME",
usage = "parent project"
)
- private ProjectControl newParent;
+ private ProjectState newParent;
@Option(name = "--permissions-only", usage = "create project for use only as parent")
private boolean permissionsOnly;
@@ -188,7 +188,7 @@
input.owners = Lists.transform(ownerIds, AccountGroup.UUID::get);
}
if (newParent != null) {
- input.parent = newParent.getProject().getName();
+ input.parent = newParent.getName();
}
input.permissionsOnly = permissionsOnly;
input.description = projectDescription;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
index b0b26fa..25f0e77 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/GarbageCollectionCommand.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.common.data.GlobalCapability.MAINTAIN_SERVER;
import static com.google.gerrit.common.data.GlobalCapability.RUN_GC;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
+import static java.util.stream.Collectors.toList;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.GarbageCollectionResult;
@@ -24,7 +25,7 @@
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.git.GarbageCollection;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -54,7 +55,7 @@
metaVar = "NAME",
usage = "projects for which the Git garbage collection should be run"
)
- private List<ProjectControl> projects = new ArrayList<>();
+ private List<ProjectState> projects = new ArrayList<>();
@Inject private ProjectCache projectCache;
@@ -80,10 +81,7 @@
if (all) {
projectNames = Lists.newArrayList(projectCache.all());
} else {
- projectNames = Lists.newArrayListWithCapacity(projects.size());
- for (ProjectControl pc : projects) {
- projectNames.add(pc.getProject().getNameKey());
- }
+ projectNames = projects.stream().map(ProjectState::getNameKey).collect(toList());
}
GarbageCollectionResult result =
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
index 476c25b..ba937a2 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexProjectCommand.java
@@ -18,8 +18,8 @@
import com.google.gerrit.extensions.annotations.RequiresAnyCapability;
import com.google.gerrit.server.project.Index;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
@@ -40,7 +40,7 @@
metaVar = "PROJECT",
usage = "projects for which the changes should be indexed"
)
- private List<ProjectControl> projects = new ArrayList<>();
+ private List<ProjectState> projects = new ArrayList<>();
@Override
protected void run() throws UnloggedFailure, Failure, Exception {
@@ -50,14 +50,12 @@
projects.stream().forEach(this::index);
}
- private void index(ProjectControl projectControl) {
+ private void index(ProjectState projectState) {
try {
- index.apply(new ProjectResource(projectControl), null);
+ index.apply(new ProjectResource(projectState, user), null);
} catch (Exception e) {
writeError(
- "error",
- String.format(
- "Unable to index %s: %s", projectControl.getProject().getName(), e.getMessage()));
+ "error", String.format("Unable to index %s: %s", projectState.getName(), e.getMessage()));
}
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
index 275da7c..e467cc4 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/LsUserRefs.java
@@ -25,7 +25,7 @@
import com.google.gerrit.server.account.AccountResolver;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.VisibleRefFilter;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gerrit.sshd.CommandMetaData;
@@ -59,7 +59,7 @@
required = true,
usage = "project for which the refs should be listed"
)
- private ProjectControl projectControl;
+ private ProjectState projectState;
@Option(
name = "--user",
@@ -87,13 +87,13 @@
return;
}
- Project.NameKey projectName = projectControl.getProject().getNameKey();
+ Project.NameKey projectName = projectState.getNameKey();
try (Repository repo = repoManager.openRepository(projectName);
ManualRequestContext ctx = requestContext.openAs(userAccount.getId())) {
try {
Map<String, Ref> refsMap =
refFilterFactory
- .create(projectControl.getProjectState(), repo)
+ .create(projectState, repo)
.filter(repo.getRefDatabase().getRefs(ALL), false);
for (String ref : refsMap.keySet()) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
index c3613b1..9fcd201 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PatchSetParser.java
@@ -24,7 +24,7 @@
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.project.NoSuchChangeException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.sshd.BaseCommand.UnloggedFailure;
@@ -57,15 +57,15 @@
this.changeFinder = changeFinder;
}
- public PatchSet parsePatchSet(String token, ProjectControl projectControl, String branch)
+ public PatchSet parsePatchSet(String token, ProjectState projectState, String branch)
throws UnloggedFailure, OrmException {
// By commit?
//
if (token.matches("^([0-9a-fA-F]{4," + RevId.LEN + "})$")) {
InternalChangeQuery query = queryProvider.get();
List<ChangeData> cds;
- if (projectControl != null) {
- Project.NameKey p = projectControl.getProject().getNameKey();
+ if (projectState != null) {
+ Project.NameKey p = projectState.getNameKey();
if (branch != null) {
cds = query.byBranchCommit(p.get(), branch, token);
} else {
@@ -77,7 +77,7 @@
List<PatchSet> matches = new ArrayList<>(cds.size());
for (ChangeData cd : cds) {
Change c = cd.change();
- if (!(inProject(c, projectControl) && inBranch(c, branch))) {
+ if (!(inProject(c, projectState) && inBranch(c, branch))) {
continue;
}
for (PatchSet ps : cd.patchSets()) {
@@ -106,19 +106,15 @@
} catch (IllegalArgumentException e) {
throw error("\"" + token + "\" is not a valid patch set");
}
- ChangeNotes notes = getNotes(projectControl, patchSetId.getParentKey());
+ ChangeNotes notes = getNotes(projectState, patchSetId.getParentKey());
PatchSet patchSet = psUtil.get(db.get(), notes, patchSetId);
if (patchSet == null) {
throw error("\"" + token + "\" no such patch set");
}
- if (projectControl != null || branch != null) {
+ if (projectState != null || branch != null) {
Change change = notes.getChange();
- if (!inProject(change, projectControl)) {
- throw error(
- "change "
- + change.getId()
- + " not in project "
- + projectControl.getProject().getName());
+ if (!inProject(change, projectState)) {
+ throw error("change " + change.getId() + " not in project " + projectState.getName());
}
if (!inBranch(change, branch)) {
throw error("change " + change.getId() + " not in branch " + branch);
@@ -130,10 +126,10 @@
throw error("\"" + token + "\" is not a valid patch set");
}
- private ChangeNotes getNotes(@Nullable ProjectControl projectControl, Change.Id changeId)
+ private ChangeNotes getNotes(@Nullable ProjectState projectState, Change.Id changeId)
throws OrmException, UnloggedFailure {
- if (projectControl != null) {
- return notesFactory.create(db.get(), projectControl.getProject().getNameKey(), changeId);
+ if (projectState != null) {
+ return notesFactory.create(db.get(), projectState.getNameKey(), changeId);
}
try {
ChangeNotes notes = changeFinder.findOne(changeId);
@@ -143,12 +139,12 @@
}
}
- private static boolean inProject(Change change, ProjectControl projectControl) {
- if (projectControl == null) {
+ private static boolean inProject(Change change, ProjectState projectState) {
+ if (projectState == null) {
// No --project option, so they want every project.
return true;
}
- return projectControl.getProject().getNameKey().equals(change.getProject());
+ return projectState.getNameKey().equals(change.getProject());
}
private static boolean inBranch(Change change, String branch) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
index 0f68d61..06fbcfc 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Receive.java
@@ -93,7 +93,7 @@
throw new Failure(1, "fatal: unable to check permissions " + e);
}
- AsyncReceiveCommits arc = factory.create(projectControl, repo, null, reviewers);
+ AsyncReceiveCommits arc = factory.create(projectState, currentUser, repo, null, reviewers);
Capable r = arc.canUpload();
if (r != Capable.OK) {
@@ -110,9 +110,7 @@
// we want to present this error to the user
if (badStream.getCause() instanceof TooLargeObjectInPackException) {
StringBuilder msg = new StringBuilder();
- msg.append("Receive error on project \"")
- .append(projectControl.getProject().getName())
- .append("\"");
+ msg.append("Receive error on project \"").append(projectState.getName()).append("\"");
msg.append(" (user ");
msg.append(currentUser.getAccount().getUserName());
msg.append(" account ");
@@ -127,9 +125,7 @@
// Log what the heck is going on, as detailed as we can.
//
StringBuilder msg = new StringBuilder();
- msg.append("Unpack error on project \"")
- .append(projectControl.getProject().getName())
- .append("\":\n");
+ msg.append("Unpack error on project \"").append(projectState.getName()).append("\":\n");
msg.append(" AdvertiseRefsHook: ").append(rp.getAdvertiseRefsHook());
if (rp.getAdvertiseRefsHook() == AdvertiseRefsHook.DEFAULT) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 2a82a26..1d764b9 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -34,7 +34,6 @@
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.LabelVote;
import com.google.gerrit.sshd.CommandMetaData;
@@ -81,7 +80,7 @@
)
void addPatchSetId(String token) {
try {
- PatchSet ps = psParser.parsePatchSet(token, projectControl, branch);
+ PatchSet ps = psParser.parsePatchSet(token, projectState, branch);
patchSets.add(ps);
} catch (UnloggedFailure e) {
throw new IllegalArgumentException(e.getMessage(), e);
@@ -95,7 +94,7 @@
aliases = "-p",
usage = "project containing the specified patch set(s)"
)
- private ProjectControl projectControl;
+ private ProjectState projectState;
@Option(name = "--branch", aliases = "-b", usage = "branch containing the specified patch set(s)")
private String branch;
@@ -135,12 +134,6 @@
private boolean json;
@Option(
- name = "--strict-labels",
- usage = "Strictly check if the labels specified can be applied to the given patch set(s)"
- )
- private boolean strictLabels;
-
- @Option(
name = "--tag",
aliases = "-t",
usage = "applies a tag to the given review",
@@ -274,7 +267,6 @@
review.notify = notify;
review.labels = new TreeMap<>();
review.drafts = ReviewInput.DraftHandling.PUBLISH;
- review.strictLabels = strictLabels;
for (ApproveOption ao : optionList) {
Short v = ao.value();
if (v != null) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 656d377..466e8f0 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -146,7 +146,6 @@
@Inject private DeleteSshKey deleteSshKey;
- private IdentifiedUser user;
private AccountResource rsrc;
@Override
@@ -182,7 +181,7 @@
throws OrmException, IOException, UnloggedFailure, ConfigInvalidException,
PermissionBackendException {
user = genericUserFactory.create(id);
- rsrc = new AccountResource(user);
+ rsrc = new AccountResource(user.asIdentifiedUser());
try {
for (String email : addEmails) {
addEmail(email);
@@ -266,7 +265,7 @@
ConfigInvalidException, PermissionBackendException {
AccountSshKey sshKey =
new AccountSshKey(new AccountSshKey.Id(user.getAccountId(), i.seq), i.sshPublicKey);
- deleteSshKey.apply(new AccountResource.SshKey(user, sshKey), null);
+ deleteSshKey.apply(new AccountResource.SshKey(user.asIdentifiedUser(), sshKey), null);
}
private void addEmail(String email)
@@ -288,10 +287,10 @@
if (email.equals("ALL")) {
List<EmailInfo> emails = getEmails.apply(rsrc);
for (EmailInfo e : emails) {
- deleteEmail.apply(new AccountResource.Email(user, e.email), new Input());
+ deleteEmail.apply(new AccountResource.Email(user.asIdentifiedUser(), e.email), new Input());
}
} else {
- deleteEmail.apply(new AccountResource.Email(user, email), new Input());
+ deleteEmail.apply(new AccountResource.Email(user.asIdentifiedUser(), email), new Input());
}
}
@@ -300,7 +299,7 @@
ConfigInvalidException {
for (EmailInfo e : getEmails.apply(rsrc)) {
if (e.email.equals(email)) {
- putPreferred.apply(new AccountResource.Email(user, email), null);
+ putPreferred.apply(new AccountResource.Email(user.asIdentifiedUser(), email), null);
return;
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
index ce4116d..eea57cd 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetHeadCommand.java
@@ -15,8 +15,8 @@
package com.google.gerrit.sshd.commands;
import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
-import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.SetHead;
import com.google.gerrit.server.project.SetHead.Input;
import com.google.gerrit.sshd.CommandMetaData;
@@ -29,7 +29,7 @@
public class SetHeadCommand extends SshCommand {
@Argument(index = 0, required = true, metaVar = "NAME", usage = "name of the project")
- private ProjectControl project;
+ private ProjectState project;
@Option(name = "--new-head", required = true, metaVar = "REF", usage = "new HEAD reference")
private String newHead;
@@ -46,7 +46,7 @@
Input input = new SetHead.Input();
input.ref = newHead;
try {
- setHead.apply(new ProjectResource(project), input);
+ setHead.apply(new ProjectResource(project, user), input);
} catch (UnprocessableEntityException e) {
throw die(e);
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetProjectCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
index c275af8..a963a35 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetProjectCommand.java
@@ -17,11 +17,11 @@
import com.google.common.base.Strings;
import com.google.gerrit.extensions.api.projects.ConfigInput;
import com.google.gerrit.extensions.client.InheritableBoolean;
-import com.google.gerrit.extensions.client.ProjectState;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectResource;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.project.PutConfig;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -32,7 +32,7 @@
@CommandMetaData(name = "set-project", description = "Change a project's settings")
final class SetProjectCommand extends SshCommand {
@Argument(index = 0, required = true, metaVar = "NAME", usage = "name of the project")
- private ProjectControl projectControl;
+ private ProjectState projectState;
@Option(
name = "--description",
@@ -148,19 +148,19 @@
configInput.useContentMerge = contentMerge;
configInput.useContributorAgreements = contributorAgreements;
configInput.useSignedOffBy = signedOffBy;
- configInput.state = state;
+ configInput.state = state.getProject().getState();
configInput.maxObjectSizeLimit = maxObjectSizeLimit;
// Description is different to other parameters, null won't result in
// keeping the existing description, it would delete it.
if (Strings.emptyToNull(projectDescription) != null) {
configInput.description = projectDescription;
} else {
- configInput.description = projectControl.getProject().getDescription();
+ configInput.description = projectState.getProject().getDescription();
}
try {
- putConfig.apply(new ProjectResource(projectControl), configInput);
- } catch (RestApiException e) {
+ putConfig.apply(new ProjectResource(projectState, user), configInput);
+ } catch (RestApiException | PermissionBackendException e) {
throw die(e);
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
index 026f9b7..85cf467 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetReviewersCommand.java
@@ -24,7 +24,7 @@
import com.google.gerrit.server.change.PostReviewers;
import com.google.gerrit.server.change.ReviewerResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.sshd.ChangeArgumentParser;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -46,7 +46,7 @@
private static final Logger log = LoggerFactory.getLogger(SetReviewersCommand.class);
@Option(name = "--project", aliases = "-p", usage = "project containing the change")
- private ProjectControl projectControl;
+ private ProjectState projectState;
@Option(
name = "--add",
@@ -75,7 +75,7 @@
)
void addChange(String token) {
try {
- changeArgumentParser.addChange(token, changes, projectControl);
+ changeArgumentParser.addChange(token, changes, projectState);
} catch (UnloggedFailure e) {
throw new IllegalArgumentException(e.getMessage(), e);
} catch (OrmException e) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
index 7049c7f..0d78279 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/Upload.java
@@ -51,8 +51,8 @@
protected void runImpl() throws IOException, Failure {
try {
permissionBackend
- .user(projectControl.getUser())
- .project(projectControl.getProject().getNameKey())
+ .user(user)
+ .project(projectState.getNameKey())
.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
throw new Failure(1, "fatal: upload-pack not permitted on this server");
@@ -61,7 +61,7 @@
}
final UploadPack up = new UploadPack(repo);
- up.setAdvertiseRefsHook(refFilterFactory.create(projectControl.getProjectState(), repo));
+ up.setAdvertiseRefsHook(refFilterFactory.create(projectState, repo));
up.setPackConfig(config.getPackConfig());
up.setTimeout(config.getTimeout());
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
@@ -71,7 +71,7 @@
uploadValidatorsFactory.create(project, repo, session.getRemoteAddressAsString()));
up.setPreUploadHook(PreUploadHookChain.newChain(allPreUploadHooks));
for (UploadPackInitializer initializer : uploadPackInitializers) {
- initializer.init(projectControl.getProject().getNameKey(), up);
+ initializer.init(projectState.getNameKey(), up);
}
try {
up.upload(in, out, err);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
index 9a3e6ab..41cc485b 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/UploadArchive.java
@@ -18,7 +18,6 @@
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.AllowedFormats;
import com.google.gerrit.server.change.ArchiveFormat;
import com.google.gerrit.server.permissions.PermissionBackend;
@@ -122,7 +121,6 @@
@Inject private PermissionBackend permissionBackend;
@Inject private CommitsCollection commits;
- @Inject private IdentifiedUser user;
@Inject private AllowedFormats allowedFormats;
private Options options = new Options();
@@ -250,7 +248,7 @@
// Check reachability of the specific revision.
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(revId);
- return commits.canRead(state, repo, commit);
+ return commits.canRead(projectState, repo, commit);
}
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
index b44f0fc..1858f40 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/plugin/LfsPluginAuthCommand.java
@@ -20,7 +20,6 @@
import com.google.gerrit.sshd.CommandModule;
import com.google.gerrit.sshd.SshCommand;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.lib.Config;
@@ -55,15 +54,13 @@
}
private final DynamicItem<LfsSshPluginAuth> auth;
- private final Provider<CurrentUser> user;
@Argument(index = 0, multiValued = true, metaVar = "PARAMS")
private List<String> args = new ArrayList<>();
@Inject
- LfsPluginAuthCommand(DynamicItem<LfsSshPluginAuth> auth, Provider<CurrentUser> user) {
+ LfsPluginAuthCommand(DynamicItem<LfsSshPluginAuth> auth) {
this.auth = auth;
- this.user = user;
}
@Override
@@ -74,6 +71,6 @@
throw new UnloggedFailure(1, CONFIGURATION_ERROR);
}
- stdout.print(pluginAuth.authenticate(user.get(), args));
+ stdout.print(pluginAuth.authenticate(user, args));
}
}
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 9a02fcd..e2f3dcf 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -50,6 +50,7 @@
import com.google.gerrit.server.events.StreamEventsApiListener;
import com.google.gerrit.server.git.GarbageCollectionModule;
import com.google.gerrit.server.git.GitRepositoryManagerModule;
+import com.google.gerrit.server.git.LocalMergeSuperSetComputation;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
@@ -325,6 +326,7 @@
modules.add(cfgInjector.getInstance(MailReceiver.Module.class));
modules.add(new SmtpEmailSender.Module());
modules.add(new SignedTokenEmailTokenVerifier.Module());
+ modules.add(new LocalMergeSuperSetComputation.Module());
// Plugin module needs to be inserted *before* the index module.
// There is the concept of LifecycleModule, in Gerrit's own extension
diff --git a/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
index 9721e22..cb0a256 100644
--- a/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
+++ b/gerrit-war/src/main/webapp/WEB-INF/extra/jetty7/gerrit.xml
@@ -46,7 +46,7 @@
<Set name="password">secretkey</Set>
-->
<!-- MySQL
- <Set name="driverClassName">com.mysql.cj.jdbc.Driver</Set>
+ <Set name="driverClassName">com.mysql.jdbc.Driver</Set>
<Set name="url">jdbc:mysql://localhost/reviewdb?user=gerrit&password=secretkey</Set>
-->
<!-- MariaDB
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 412d67b..9b409e9 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -103,12 +103,30 @@
'j': '_handleJKey',
'k': '_handleKKey',
'n ]': '_handleNKey',
- 'o enter': '_handleEnterKey',
+ 'o': '_handleOKey',
'p [': '_handlePKey',
'shift+r': '_handleRKey',
's': '_handleSKey',
},
+ listeners: {
+ keydown: '_scopedKeydownHandler',
+ },
+
+ /**
+ * Iron-a11y-keys-behavior catches keyboard events globally. Some keyboard
+ * events must be scoped to a component level (e.g. `enter`) in order to not
+ * override native browser functionality.
+ *
+ * Context: Issue 7294
+ */
+ _scopedKeydownHandler(e) {
+ if (e.keyCode === 13) {
+ // Enter.
+ this._handleOKey(e);
+ }
+ },
+
attached() {
this._loadPreferences();
},
@@ -231,7 +249,7 @@
this.selectedIndex -= 1;
},
- _handleEnterKey(e) {
+ _handleOKey(e) {
if (this.shouldSuppressKeyboardShortcut(e) ||
this.modifierPressed(e)) { return; }
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 8f2468b..2cda515 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
@@ -666,7 +666,7 @@
_handleActionTap(e) {
e.preventDefault();
- const el = e.currentTarget;
+ const el = Polymer.dom(e).localTarget;
const key = el.getAttribute('data-action-key');
if (key.startsWith(ADDITIONAL_ACTION_KEY_PREFIX)) {
this.fire(`${key}-tap`, {node: el});
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index fd114e9..f09e23a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -158,9 +158,6 @@
.mobile {
display: none;
}
- .expandInline {
- padding-right: .25em;
- }
.reviewed {
margin-left: 2em;
width: 15em;
@@ -337,7 +334,7 @@
</template>
</div>
<div
- class$="row totalChanges [[_computeExpandInlineClass(_userPrefs)]]"
+ class="row totalChanges"
hidden$="[[_hideChangeTotals]]">
<div class="total-stats">
<span
@@ -358,7 +355,7 @@
<div class="editFileControls showOnEdit"></div>
</div>
<div
- class$="row totalChanges [[_computeExpandInlineClass(_userPrefs)]]"
+ class="row totalChanges"
hidden$="[[_hideBinaryChangeTotals]]">
<div class="total-stats">
<span class="added" aria-label="Total lines added">
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 70f1d4d..25b5686 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -612,8 +612,7 @@
},
_handleRKey(e) {
- if (this.shouldSuppressKeyboardShortcut(e) ||
- this.modifierPressed(e) && !this.isModifierPressed(e, 'shiftKey')) {
+ if (this.shouldSuppressKeyboardShortcut(e) || this.modifierPressed(e)) {
return;
}
@@ -717,10 +716,6 @@
return classes.join(' ');
},
- _computeExpandInlineClass(userPrefs) {
- return userPrefs.expand_inline_diffs ? 'expandInline' : '';
- },
-
_computePathClass(path, expandedFilesRecord) {
return this._isFileExpanded(path, expandedFilesRecord) ? 'path expanded' :
'path';
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index b01c105..f6e63b2b 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -580,10 +580,6 @@
assert.equal(element._computeClass('clazz', '/foo/bar/baz'), 'clazz');
assert.equal(element._computeClass('clazz', '/COMMIT_MSG'),
'clazz invisible');
- assert.equal(element._computeExpandInlineClass(
- {expand_inline_diffs: true}), 'expandInline');
- assert.equal(element._computeExpandInlineClass(
- {expand_inline_diffs: false}), '');
});
test('file review status', () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
index 7023e0b..81f44b7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
@@ -93,7 +93,11 @@
padding-top: 0;
}
.action {
- margin-left: .5em;
+ margin-left: 1em;
+ --gr-button: {
+ color: #212121;
+ }
+ --gr-button-hover-color: rgba(33, 33, 33, .75);
}
.rightActions {
display: flex;
@@ -282,18 +286,20 @@
Unresolved
</div>
<div class="rightActions">
- <gr-button class="action edit hideOnPublished" on-tap="_handleEdit">
- Edit</gr-button>
- <gr-button class="action save hideOnPublished" on-tap="_handleSave"
- disabled$="[[_computeSaveDisabled(_messageText)]]">Save</gr-button>
- <gr-button class="action cancel hideOnPublished"
+ <gr-button link class="action edit hideOnPublished"
+ on-tap="_handleEdit">Edit</gr-button>
+ <gr-button link class="action save hideOnPublished"
+ on-tap="_handleSave"
+ disabled$="[[_computeSaveDisabled(_messageText)]]">Save
+ </gr-button>
+ <gr-button link class="action cancel hideOnPublished"
on-tap="_handleCancel" hidden>Cancel</gr-button>
- <gr-button class="action discard hideOnPublished"
+ <gr-button link class="action discard hideOnPublished"
on-tap="_handleDiscard">Discard</gr-button>
</div>
</div>
<div class="actions robotActions" hidden$="[[!_showRobotActions]]">
- <gr-button class="action fix"
+ <gr-button link class="action fix"
on-tap="_handleFix"
disabled="[[robotButtonDisabled]]">
Please Fix
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
index f9ca92e..7e6d54d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
@@ -66,12 +66,15 @@
.actions {
border-top: 1px solid #ddd;
display: flex;
- justify-content: space-between;
+ justify-content: flex-end;
}
.beta {
font-family: var(--font-family-bold);
color: #888;
}
+ gr-button {
+ margin-left: 1em;
+ }
</style>
<gr-overlay id="prefsOverlay" with-backdrop>
@@ -140,8 +143,10 @@
</div>
</div>
<div class="actions">
- <gr-button id="saveButton" primary on-tap="_handleSave">Save</gr-button>
- <gr-button id="cancelButton" on-tap="_handleCancel">Cancel</gr-button>
+ <gr-button id="cancelButton" link on-tap="_handleCancel">
+ Cancel</gr-button>
+ <gr-button id="saveButton" link primary on-tap="_handleSave">
+ Save</gr-button>
</div>
</overlay>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
index 7ddf9a9..0f4119b 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.html
@@ -16,13 +16,15 @@
<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/paper-input/paper-input.html">
+<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
<link rel="import" href="../../shared/gr-button/gr-button.html">
<link rel="import" href="../../shared/gr-confirm-dialog/gr-confirm-dialog.html">
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -41,21 +43,30 @@
margin-left: 1em;
text-decoration: none;
}
- paper-input {
- --paper-input-container: {
- padding: 0;
- min-width: 15em;
- }
- --paper-input-container-input: {
- font-size: 1em;
- }
- }
gr-confirm-dialog {
width: 50em;
}
gr-confirm-dialog .main {
width: 100%;
}
+ gr-autocomplete {
+ --gr-autocomplete: {
+ border: 1px solid #d1d2d3;
+ border-radius: 2px;
+ font-size: 1em;
+ height: 2em;
+ padding: 0 .15em;
+ }
+ }
+ input {
+ border: 1px solid #d1d2d3;
+ border-radius: 2px;
+ font-size: 1em;
+ height: 2em;
+ margin: .5em 0;
+ padding: 0 .15em;
+ width: 100%;
+ }
</style>
<template is="dom-repeat" items="[[_actions]]" as="action">
<gr-button
@@ -66,21 +77,56 @@
<gr-overlay id="overlay" with-backdrop>
<gr-confirm-dialog
id="editDialog"
- class="invisible"
+ class="invisible dialog"
disabled$="[[!_isValidPath(_path)]]"
confirm-label="Edit"
on-confirm="_handleEditConfirm"
on-cancel="_handleDialogCancel">
<div class="header">Edit a file</div>
<div class="main">
- <!-- TODO(kaspern): Make this an autocomplete. -->
- <paper-input
- class="input"
- label="Enter an existing or new full file path."
- value="{{_path}}"></paper-input>
+ <gr-autocomplete
+ placeholder="Enter an existing or new full file path."
+ query="[[_query]]"
+ text="{{_path}}"></gr-autocomplete>
+ </div>
+ </gr-confirm-dialog>
+ <gr-confirm-dialog
+ id="deleteDialog"
+ class="invisible dialog"
+ disabled$="[[!_isValidPath(_path)]]"
+ confirm-label="Delete"
+ on-confirm="_handleDeleteConfirm"
+ on-cancel="_handleDialogCancel">
+ <div class="header">Delete a file</div>
+ <div class="main">
+ <gr-autocomplete
+ placeholder="Enter an existing full file path."
+ query="[[_query]]"
+ text="{{_path}}"></gr-autocomplete>
+ </div>
+ </gr-confirm-dialog>
+ <gr-confirm-dialog
+ id="renameDialog"
+ class="invisible dialog"
+ disabled$="[[!_computeRenameDisabled(_path, _newPath)]]"
+ confirm-label="Rename"
+ on-confirm="_handleRenameConfirm"
+ on-cancel="_handleDialogCancel">
+ <div class="header">Rename a file</div>
+ <div class="main">
+ <gr-autocomplete
+ placeholder="Enter an existing full file path."
+ query="[[_query]]"
+ text="{{_path}}"></gr-autocomplete>
+ <input
+ class="newPathInput"
+ is="iron-input"
+ bind-value="{{_newPath}}"
+ placeholder="Enter the new path."/>
</div>
</gr-confirm-dialog>
</gr-overlay>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-edit-controls.js"></script>
</dom-module>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
index 62e8c7a..fd56bd3 100644
--- a/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
+++ b/polygerrit-ui/app/elements/edit/gr-edit-controls/gr-edit-controls.js
@@ -16,9 +16,9 @@
const Actions = {
EDIT: {label: 'Edit', key: 'edit'},
- /* TODO(kaspern): Implement these actions.
DELETE: {label: 'Delete', key: 'delete'},
RENAME: {label: 'Rename', key: 'rename'},
+ /* TODO(kaspern): Implement these actions.
REVERT: {label: 'Revert', key: 'revert'},
CHECKOUT: {label: 'Check out', key: 'checkout'},
*/
@@ -37,8 +37,22 @@
type: String,
value: '',
},
+ _newPath: {
+ type: String,
+ value: '',
+ },
+ _query: {
+ type: Function,
+ value() {
+ return this._queryFiles.bind(this);
+ },
+ },
},
+ behaviors: [
+ Gerrit.PatchSetBehavior,
+ ],
+
_handleTap(e) {
e.preventDefault();
const action = Polymer.dom(e).localTarget.id;
@@ -47,6 +61,12 @@
case Actions.EDIT.key:
this.openEditDialog();
return;
+ case Actions.DELETE.key:
+ this.openDeleteDialog();
+ return;
+ case Actions.RENAME.key:
+ this.openRenameDialog();
+ return;
}
},
@@ -55,37 +75,96 @@
return this._showDialog(this.$.editDialog);
},
+ openDeleteDialog(opt_path) {
+ if (opt_path) { this._path = opt_path; }
+ return this._showDialog(this.$.deleteDialog);
+ },
+
+ openRenameDialog(opt_path) {
+ if (opt_path) { this._path = opt_path; }
+ return this._showDialog(this.$.renameDialog);
+ },
+
/**
* Given a path string, checks that it is a valid file path.
* @param {string} path
* @return {boolean}
*/
_isValidPath(path) {
- return path.length && !path.endsWith('/');
+ // Double negation needed for strict boolean return type.
+ return !!path.length && !path.endsWith('/');
+ },
+
+ _computeRenameDisabled(path, newPath) {
+ return this._isValidPath(path) && this._isValidPath(newPath);
+ },
+
+ /**
+ * Given a dom event, gets the dialog that lies along this event path.
+ * @param {!Event} e
+ * @return {!Element}
+ */
+ _getDialogFromEvent(e) {
+ return Polymer.dom(e).path.find(element => {
+ if (!element.classList) { return false; }
+ return element.classList.contains('dialog');
+ });
},
_showDialog(dialog) {
return this.$.overlay.open().then(() => {
dialog.classList.toggle('invisible', false);
- dialog.querySelector('.input').focus();
+ dialog.querySelector('gr-autocomplete').focus();
this.async(() => { this.$.overlay.center(); }, 1);
});
},
_closeDialog(dialog) {
- dialog.querySelectorAll('.input').forEach(input => { input.value = ''; });
+ // Dialog may have autocompletes and plain inputs -- as these have
+ // different properties representing their bound text, it is easier to
+ // just make two separate queries.
+ dialog.querySelectorAll('gr-autocomplete')
+ .forEach(input => { input.text = ''; });
+ dialog.querySelectorAll('input')
+ .forEach(input => { input.bindValue = ''; });
+
dialog.classList.toggle('invisible', true);
return this.$.overlay.close();
},
_handleDialogCancel(e) {
- this._closeDialog(Polymer.dom(e).localTarget);
+ this._closeDialog(this._getDialogFromEvent(e));
},
_handleEditConfirm(e) {
const url = Gerrit.Nav.getEditUrlForDiff(this.change, this._path);
Gerrit.Nav.navigateToRelativeUrl(url);
- this._closeDialog(Polymer.dom(e).localTarget);
+ this._closeDialog(this._getDialogFromEvent(e));
+ },
+
+ _handleDeleteConfirm(e) {
+ this.$.restAPI.deleteFileInChangeEdit(this.change._number, this._path)
+ .then(res => {
+ if (!res.ok) { return; }
+ this._closeDialog(this._getDialogFromEvent(e));
+ Gerrit.Nav.navigateToChange(this.change);
+ });
+ },
+
+ _handleRenameConfirm(e) {
+ return this.$.restAPI.renameFileInChangeEdit(this.change._number,
+ this._path, this._newPath).then(res => {
+ if (!res.ok) { return; }
+ this._closeDialog(this._getDialogFromEvent(e));
+ Gerrit.Nav.navigateToChange(this.change);
+ });
+ },
+
+ _queryFiles(input) {
+ return this.$.restAPI.queryChangeFiles(this.change._number,
+ this.EDIT_NAME, input).then(res => res.map(file => {
+ return {name: file};
+ }));
},
});
})();
\ No newline at end of file
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 6da4e32..1d5ab25 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
@@ -37,12 +37,16 @@
let sandbox;
let showDialogSpy;
let closeDialogSpy;
+ let queryStub;
setup(() => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
+ element.change = {_number: '42'};
showDialogSpy = sandbox.spy(element, '_showDialog');
closeDialogSpy = sandbox.spy(element, '_closeDialog');
+ queryStub = sandbox.stub(element.$.restAPI, 'queryChangeFiles')
+ .returns(Promise.resolve([]));
flushAsynchronousOperations();
});
@@ -50,7 +54,7 @@
test('all actions exist', () => {
assert.equal(Polymer.dom(element.root).querySelectorAll('gr-button').length,
- 1);
+ element._actions.length);
});
suite('edit button CUJ', () => {
@@ -63,11 +67,22 @@
];
});
+ test('_isValidPath', () => {
+ assert.isFalse(element._isValidPath(''));
+ assert.isFalse(element._isValidPath('test/'));
+ assert.isFalse(element._isValidPath('/'));
+ assert.isTrue(element._isValidPath('test/path.cpp'));
+ assert.isTrue(element._isValidPath('test.js'));
+ });
+
test('edit', () => {
MockInteractions.tap(element.$$('#edit'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.editDialog.disabled);
- element._path = 'src/test.cpp';
+ assert.isFalse(queryStub.called);
+ element.$.editDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isTrue(queryStub.called);
assert.isFalse(element.$.editDialog.disabled);
MockInteractions.tap(element.$.editDialog.$$('gr-button[primary]'));
for (const stub of navStubs) { assert.isTrue(stub.called); }
@@ -79,7 +94,8 @@
MockInteractions.tap(element.$$('#edit'));
return showDialogSpy.lastCall.returnValue.then(() => {
assert.isTrue(element.$.editDialog.disabled);
- element._path = 'src/test.cpp';
+ element.$.editDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
assert.isFalse(element.$.editDialog.disabled);
MockInteractions.tap(element.$.editDialog.$$('gr-button'));
for (const stub of navStubs) { assert.isFalse(stub.called); }
@@ -89,12 +105,183 @@
});
});
+ suite('delete button CUJ', () => {
+ let navStub;
+ let deleteStub;
+
+ setup(() => {
+ navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
+ deleteStub = sandbox.stub(element.$.restAPI, 'deleteFileInChangeEdit');
+ });
+
+ test('delete', () => {
+ deleteStub.returns(Promise.resolve({ok: true}));
+ MockInteractions.tap(element.$$('#delete'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.deleteDialog.disabled);
+ assert.isFalse(queryStub.called);
+ element.$.deleteDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isTrue(queryStub.called);
+ assert.isFalse(element.$.deleteDialog.disabled);
+ MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(deleteStub.called);
+
+ return deleteStub.lastCall.returnValue.then(() => {
+ assert.isTrue(navStub.called);
+ assert.isTrue(closeDialogSpy.called);
+ });
+ });
+ });
+
+ test('delete fails', () => {
+ deleteStub.returns(Promise.resolve({ok: false}));
+ MockInteractions.tap(element.$$('#delete'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.deleteDialog.disabled);
+ assert.isFalse(queryStub.called);
+ element.$.deleteDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isTrue(queryStub.called);
+ assert.isFalse(element.$.deleteDialog.disabled);
+ MockInteractions.tap(element.$.deleteDialog.$$('gr-button[primary]'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(deleteStub.called);
+
+ return deleteStub.lastCall.returnValue.then(() => {
+ assert.isFalse(navStub.called);
+ assert.isFalse(closeDialogSpy.called);
+ });
+ });
+ });
+
+ test('cancel', () => {
+ MockInteractions.tap(element.$$('#delete'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.deleteDialog.disabled);
+ element.$.deleteDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isFalse(element.$.deleteDialog.disabled);
+ MockInteractions.tap(element.$.deleteDialog.$$('gr-button'));
+ assert.isFalse(navStub.called);
+ assert.isTrue(closeDialogSpy.called);
+ assert.equal(element._path, '');
+ });
+ });
+ });
+
+ suite('rename button CUJ', () => {
+ let navStub;
+ let renameStub;
+
+ setup(() => {
+ navStub = sandbox.stub(Gerrit.Nav, 'navigateToChange');
+ renameStub = sandbox.stub(element.$.restAPI, 'renameFileInChangeEdit');
+ });
+
+ test('rename', () => {
+ renameStub.returns(Promise.resolve({ok: true}));
+ MockInteractions.tap(element.$$('#rename'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.renameDialog.disabled);
+ assert.isFalse(queryStub.called);
+ element.$.renameDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isTrue(queryStub.called);
+ assert.isTrue(element.$.renameDialog.disabled);
+
+ element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ 'src/test.newPath';
+
+ assert.isFalse(element.$.renameDialog.disabled);
+ MockInteractions.tap(element.$.renameDialog.$$('gr-button[primary]'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(renameStub.called);
+
+ return renameStub.lastCall.returnValue.then(() => {
+ assert.isTrue(navStub.called);
+ assert.isTrue(closeDialogSpy.called);
+ });
+ });
+ });
+
+ test('rename fails', () => {
+ renameStub.returns(Promise.resolve({ok: false}));
+ MockInteractions.tap(element.$$('#rename'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.renameDialog.disabled);
+ assert.isFalse(queryStub.called);
+ element.$.renameDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ assert.isTrue(queryStub.called);
+ assert.isTrue(element.$.renameDialog.disabled);
+
+ element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ 'src/test.newPath';
+
+ assert.isFalse(element.$.renameDialog.disabled);
+ MockInteractions.tap(element.$.renameDialog.$$('gr-button[primary]'));
+ flushAsynchronousOperations();
+
+ assert.isTrue(renameStub.called);
+
+ return renameStub.lastCall.returnValue.then(() => {
+ assert.isFalse(navStub.called);
+ assert.isFalse(closeDialogSpy.called);
+ });
+ });
+ });
+
+ test('cancel', () => {
+ MockInteractions.tap(element.$$('#rename'));
+ return showDialogSpy.lastCall.returnValue.then(() => {
+ assert.isTrue(element.$.renameDialog.disabled);
+ element.$.renameDialog.querySelector('gr-autocomplete').text =
+ 'src/test.cpp';
+ element.$.renameDialog.querySelector('.newPathInput').bindValue =
+ 'src/test.newPath';
+ assert.isFalse(element.$.renameDialog.disabled);
+ MockInteractions.tap(element.$.renameDialog.$$('gr-button'));
+ assert.isFalse(navStub.called);
+ assert.isTrue(closeDialogSpy.called);
+ assert.equal(element._path, '');
+ assert.equal(element._newPath, '');
+ });
+ });
+ });
+
test('openEditDialog', () => {
return element.openEditDialog('test/path.cpp').then(() => {
assert.isFalse(element.$.editDialog.hasAttribute('hidden'));
- assert.equal(element.$.editDialog.querySelector('.input').value,
+ assert.equal(element.$.editDialog.querySelector('gr-autocomplete').text,
'test/path.cpp');
});
});
+
+ test('_getDialogFromEvent', () => {
+ const spy = sandbox.spy(element, '_getDialogFromEvent');
+ element.addEventListener('tap', element._getDialogFromEvent);
+
+ MockInteractions.tap(element.$.editDialog);
+ flushAsynchronousOperations();
+ assert.equal(spy.lastCall.returnValue.id, 'editDialog');
+
+ MockInteractions.tap(element.$.deleteDialog);
+ flushAsynchronousOperations();
+ assert.equal(spy.lastCall.returnValue.id, 'deleteDialog');
+
+ MockInteractions.tap(
+ element.$.deleteDialog.querySelector('gr-autocomplete'));
+ flushAsynchronousOperations();
+ assert.equal(spy.lastCall.returnValue.id, 'deleteDialog');
+
+ MockInteractions.tap(element);
+ flushAsynchronousOperations();
+ assert.notOk(spy.lastCall.returnValue);
+ });
});
</script>
\ No newline at end of file
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
index c8d3319..2d3f1c3 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor.js
@@ -62,7 +62,8 @@
},
_handleDeleteButton(e) {
- const index = parseInt(e.target.getAttribute('data-index'), 10);
+ const index = parseInt(Polymer.dom(e).localTarget
+ .getAttribute('data-index'), 10);
const email = this._emails[index];
this.push('_emailsToRemove', email);
this.splice('_emails', index, 1);
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
index d5067aa..fb868e8 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor.js
@@ -58,7 +58,8 @@
},
_showKey(e) {
- const index = parseInt(e.target.getAttribute('data-index'), 10);
+ const el = Polymer.dom(e).localTarget;
+ const index = parseInt(el.getAttribute('data-index'), 10);
this._keyToView = this._keys[index];
this.$.viewKeyOverlay.open();
},
@@ -68,7 +69,8 @@
},
_handleDeleteKey(e) {
- const index = parseInt(e.target.getAttribute('data-index'), 10);
+ const el = Polymer.dom(e).localTarget;
+ const index = parseInt(el.getAttribute('data-index'), 10);
this.push('_keysToRemove', this._keys[index]);
this.splice('_keys', index, 1);
this.hasUnsavedChanges = true;
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
index c0449be..431ac0e 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown.js
@@ -46,6 +46,7 @@
},
suggestions: {
type: Array,
+ value: () => [],
observer: '_resetCursorStops',
},
_suggestionEls: {
@@ -151,8 +152,12 @@
},
_resetCursorStops() {
- Polymer.dom.flush();
- this._suggestionEls = this.$.suggestions.querySelectorAll('li');
+ if (this.suggestions.length > 0) {
+ Polymer.dom.flush();
+ this._suggestionEls = this.$.suggestions.querySelectorAll('li');
+ } else {
+ this._suggestionEls = [];
+ }
},
_resetCursorIndex() {
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
index 7aa7abf..ab847c4 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete.html
@@ -38,31 +38,29 @@
color: red;
}
</style>
- <div>
- <input
- id="input"
- class$="[[_computeClass(borderless)]]"
- is="iron-input"
- disabled$="[[disabled]]"
- bind-value="{{text}}"
- placeholder="[[placeholder]]"
- on-keydown="_handleKeydown"
- on-focus="_onInputFocus"
- on-blur="_onInputBlur"
- autocomplete="off"/>
- <gr-autocomplete-dropdown
- vertical-align="top"
- vertical-offset="20"
- horizontal-align="auto"
- id="suggestions"
- on-item-selected="_handleItemSelect"
- on-keydown="_handleKeydown"
- suggestions="[[_suggestions]]"
- role="listbox"
- index="[[_index]]"
- position-target="[[_inputElement]]">
- </gr-autocomplete-dropdown>
- </div>
+ <input
+ id="input"
+ class$="[[_computeClass(borderless)]]"
+ is="iron-input"
+ disabled$="[[disabled]]"
+ bind-value="{{text}}"
+ placeholder="[[placeholder]]"
+ on-keydown="_handleKeydown"
+ on-focus="_onInputFocus"
+ on-blur="_onInputBlur"
+ autocomplete="off"/>
+ <gr-autocomplete-dropdown
+ vertical-align="top"
+ vertical-offset="20"
+ horizontal-align="auto"
+ id="suggestions"
+ on-item-selected="_handleItemSelect"
+ on-keydown="_handleKeydown"
+ suggestions="[[_suggestions]]"
+ role="listbox"
+ index="[[_index]]"
+ position-target="[[_inputElement]]">
+ </gr-autocomplete-dropdown>
</template>
<script src="gr-autocomplete.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
index d5eaa0f..f2f218b 100644
--- a/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
+++ b/polygerrit-ui/app/elements/shared/gr-download-commands/gr-download-commands.js
@@ -64,7 +64,7 @@
_handleSchemeTap(e) {
e.preventDefault();
- const el = Polymer.dom(e).rootTarget;
+ const el = Polymer.dom(e).localTarget;
this.selectedScheme = el.getAttribute('data-scheme');
if (this._loggedIn) {
this.$.restAPI.savePreferences({download_scheme: this.selectedScheme});
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index c206b20..033d01b 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -904,6 +904,17 @@
patchRange.patchNum);
},
+ /**
+ * @param {number|string} changeNum
+ * @param {number|string} patchNum
+ * @param {string} query
+ * @return {!Promise<!Object>}
+ */
+ queryChangeFiles(changeNum, patchNum, query) {
+ return this._getChangeURLAndFetch(changeNum,
+ `/files?q=${encodeURIComponent(query)}`, patchNum);
+ },
+
getChangeFilesAsSpeciallySortedArray(changeNum, patchRange) {
return this.getChangeFiles(changeNum, patchRange).then(
this._normalizeChangeFilesResponse.bind(this));
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index eb4f418..aa7f9a0 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -701,6 +701,15 @@
assert.equal(sendStub.lastCall.args[1], '/projects/x%2Fy');
});
+ test('queryChangeFiles', () => {
+ const fetchStub = sandbox.stub(element, '_getChangeURLAndFetch')
+ .returns(Promise.resolve());
+ return element.queryChangeFiles('42', 'edit', 'test/path.js').then(() => {
+ assert.deepEqual(fetchStub.lastCall.args,
+ ['42', '/files?q=test%2Fpath.js', 'edit']);
+ });
+ });
+
test('getProjects', () => {
sandbox.stub(element, '_fetchSharedCacheURL');
element.getProjects('test', 25);
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 7cc37d8..a690192 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -26,6 +26,7 @@
const behaviorsPath = '../behaviors/';
// Elements tests.
+ /* eslint-disable max-len */
const elements = [
// This seemed to be flakey when it was farther down the list. Keep at the
// beginning.
@@ -158,6 +159,7 @@
'shared/gr-tooltip-content/gr-tooltip-content_test.html',
'shared/gr-tooltip/gr-tooltip_test.html',
];
+ /* eslint-enable max-len */
for (let file of elements) {
file = elementsPath + file;
testFiles.push(file);
@@ -165,6 +167,7 @@
}
// Behaviors tests.
+ /* eslint-disable max-len */
const behaviors = [
'async-foreach-behavior/async-foreach-behavior_test.html',
'base-url-behavior/base-url-behavior_test.html',
@@ -178,6 +181,7 @@
'gr-path-list-behavior/gr-path-list-behavior_test.html',
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
];
+ /* eslint-enable max-len */
for (let file of behaviors) {
// Behaviors do not utilize the DOM, so no shadow DOM test is necessary.
file = behaviorsPath + file;
diff --git a/gerrit-acceptance-framework/pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
similarity index 100%
rename from gerrit-acceptance-framework/pom.xml
rename to tools/maven/gerrit-acceptance-framework_pom.xml
diff --git a/gerrit-extension-api/pom.xml b/tools/maven/gerrit-extension-api_pom.xml
similarity index 100%
rename from gerrit-extension-api/pom.xml
rename to tools/maven/gerrit-extension-api_pom.xml
diff --git a/gerrit-plugin-api/pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
similarity index 100%
rename from gerrit-plugin-api/pom.xml
rename to tools/maven/gerrit-plugin-api_pom.xml
diff --git a/gerrit-plugin-gwtui/pom.xml b/tools/maven/gerrit-plugin-gwtui_pom.xml
similarity index 100%
rename from gerrit-plugin-gwtui/pom.xml
rename to tools/maven/gerrit-plugin-gwtui_pom.xml
diff --git a/gerrit-war/pom.xml b/tools/maven/gerrit-war_pom.xml
similarity index 100%
rename from gerrit-war/pom.xml
rename to tools/maven/gerrit-war_pom.xml
diff --git a/tools/maven/mvn.py b/tools/maven/mvn.py
index f7b5aa8..2e1c1a9 100755
--- a/tools/maven/mvn.py
+++ b/tools/maven/mvn.py
@@ -56,7 +56,7 @@
for spec in args.s:
artifact, packaging_type, src = spec.split(':')
exe = cmd + [
- '-DpomFile=%s' % path.join(root, '%s/pom.xml' % artifact),
+ '-DpomFile=%s' % path.join(root, 'tools', 'maven', '%s_pom.xml' % artifact),
'-Dpackaging=%s' % packaging_type,
'-Dfile=%s' % src,
]
diff --git a/tools/release-announcement-template.txt b/tools/release-announcement-template.txt
index 87f5d49..2702f57 100644
--- a/tools/release-announcement-template.txt
+++ b/tools/release-announcement-template.txt
@@ -7,7 +7,7 @@
http://gerrit-documentation.storage.googleapis.com/Documentation/{{ data.version }}/index.html
{% if data.previous %}
Log of changes since {{ data.previous }}:
-https://gerrit.googlesource.com/gerrit/+log/v{{ data.previous }}..v{{ data.version }}
+https://gerrit.googlesource.com/gerrit/+log/v{{ data.previous }}..v{{ data.version }}?no-merges
{% endif %}
Download:
https://gerrit-releases.storage.googleapis.com/gerrit-{{ data.version }}.war
diff --git a/tools/version.py b/tools/version.py
index fed6d5d..72b0134 100755
--- a/tools/version.py
+++ b/tools/version.py
@@ -48,7 +48,7 @@
for project in ['gerrit-acceptance-framework', 'gerrit-extension-api',
'gerrit-plugin-api', 'gerrit-plugin-gwtui',
'gerrit-war']:
- pom = os.path.join(project, 'pom.xml')
+ pom = os.path.join('tools', 'maven', '%s_pom.xml' % project)
replace_in_file(pom, src_pattern)
src_pattern = re.compile(r'^(GERRIT_VERSION = ")([-.\w]+)(")$', re.MULTILINE)