Merge "PolyGerrit: Update project, plugin and group admin elements to update the title correctly"
diff --git a/Documentation/cmd-flush-caches.txt b/Documentation/cmd-flush-caches.txt
index 4716f3b..9ba4808 100644
--- a/Documentation/cmd-flush-caches.txt
+++ b/Documentation/cmd-flush-caches.txt
@@ -60,7 +60,6 @@
----
$ ssh -p 29418 review.example.com gerrit flush-caches --list
accounts
- accounts_byemail
diff
groups
ldap_groups
diff --git a/Documentation/cmd-show-caches.txt b/Documentation/cmd-show-caches.txt
index 59abc1c..5286d43 100644
--- a/Documentation/cmd-show-caches.txt
+++ b/Documentation/cmd-show-caches.txt
@@ -58,7 +58,6 @@
| Mem Disk Space| |Mem Disk|
--------------------------------+---------------------+---------+---------+
accounts | 4096 | 3.4ms | 99% |
- accounts_byemail | 1024 | 7.6ms | 98% |
accounts_byname | 4096 | 11.3ms | 99% |
adv_bases | | | |
changes | | 27.1ms | 0% |
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index ec2e046..71474f5 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -714,12 +714,6 @@
If direct updates are made to any of these database tables, this
cache should be flushed.
-cache `"accounts_byemail"`::
-+
-Caches account identities keyed by email address, which is scanned
-from the `account_external_ids` database table. If updates are
-made to this table, this cache should be flushed.
-
cache `"adv_bases"`::
+
Used only for push over smart HTTP when branch level access controls
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 68c1963..4471645 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -5,7 +5,7 @@
. link:linux-quickstart.html[Quickstart for Installing Gerrit on Linux]
== About Gerrit
-. link:intro-product-overview.html[Product Overview]
+. link:intro-quick.html[Product Overview]
. link:intro-how-gerrit-works.html[How Gerrit Works]
. link:intro-gerrit-walkthrough.html[Basic Gerrit Walkthrough]
diff --git a/Documentation/intro-product-overview.txt b/Documentation/intro-product-overview.txt
deleted file mode 100644
index afd0109..0000000
--- a/Documentation/intro-product-overview.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-= Gerrit Product Overview
-
-Gerrit is a web-based code review tool built on top of the
-https://git-scm.com/[Git version control system]. This introduction provides
-an overview of Gerrit and describes how Gerrit integrates into a typical
-development workflow. It also provides a brief tutorial that shows how to manage
-a change using Gerrit.
-
-== What is Gerrit?
-
-Gerrit makes code review easy by providing a lightweight framework for reviewing
-commits before they are accepted by the codebase. Gerrit works equally well for
-projects where approving changes is restricted to selected users, as is typical
-for Open Source software development, as well as projects where all contributors
-are trusted.
-
-== Learn About Gerrit
-
-If you're new to Gerrit and want to know more about how it can improve your
-developer workflow, see the following topics:
-
-. link:intro-product-overview.html[Product Overview]
-. link:intro-gerrit-walkthrough.html[Basic Gerrit Walkthrough]
-
-== Getting Started
-
-This documentation contains several guides to help you learn about the Gerrit
-features most relevant to you:
-
-. link:intro-user.html[User Guide]
-. link:intro-project-owner.html[Project Owner Guide]
-. link:http://source.android.com/submit-patches/workflow[Default Android Workflow] (external)
-
-GERRIT
-------
-Part of link:index.html[Gerrit Code Review]
-
-SEARCHBOX
----------
-
diff --git a/Documentation/intro-quick.txt b/Documentation/intro-quick.txt
index 39c2ec39..e6b1e43 100644
--- a/Documentation/intro-quick.txt
+++ b/Documentation/intro-quick.txt
@@ -1,7 +1,7 @@
-= Gerrit Code Review - A Quick Introduction
+= Gerrit Product Overview
Gerrit is a web-based code review tool built on top of the
-https://git-scm.com/[git version control system]. This introduction provides
+https://git-scm.com/[Git version control system]. This introduction provides
an overview of Gerrit and describes how Gerrit integrates into a typical
development workflow. It also provides a brief tutorial that shows how to manage
a change using Gerrit.
@@ -14,318 +14,22 @@
for Open Source software development, as well as projects where all contributors
are trusted.
-== Gerrit and the developer workflow
+== Learn About Gerrit
-To understand how Gerrit fits into and enhances the developer workflow, consider
-a typical project. This project has a central source repository, which serves as
-the authoritative copy of the project's contents.
+If you're new to Gerrit and want to know more about how it can improve your
+developer workflow, see the following topics:
-.Central Source Repository
-image::images/intro-quick-central-repo.png[Authoritative Source Repository]
+. link:intro-how-gerrit-works.html[How Gerrit Works]
+. link:intro-gerrit-walkthrough.html[Basic Gerrit Walkthrough]
-Gerrit takes the place of this central repository and adds an additional
-concept: a _store of pending changes_.
+== Getting Started
-.Gerrit in place of Central Repository
-image::images/intro-quick-central-gerrit.png[Gerrit in place of Central Repository]
+This documentation contains several guides to help you learn about the Gerrit
+features most relevant to you:
-With Gerrit, when a developer makes a change, it is sent to this store of
-pending changes, where other developers can review, discuss and approve the
-change. After enough reviewers grant their approval, the change becomes an
-official part of the codebase.
-
-In addition to this store of pending changes, Gerrit captures notes
-and comments about each change. These features allow developers to review
-changes at their convenience, or when conversations about a change can't
-happen face to face. They also help to create a record of the conversation
-around a given change, which can provide a history of when a change was made and
-why.
-
-Like any repository hosting solution, Gerrit has a powerful
-link:access-control.html[access control model.] This model allows you to
-fine-tune access to your repository.
-
-== Working with Gerrit: An example
-
-To understand how Gerrit works, let's follow a change through its entire
-life cycle. This example uses a Gerrit server configured as follows:
-
-* *Hostname*: gerrithost
-* *HTTP interface port*: 8080
-* *SSH interface port*: 29418
-
-In this walkthrough, we'll follow two developers, Max and Hannah, as they make
-and review a change to a +RecipeBook+ project. We'll follow the change through
-these stages:
-
-. Making the change.
-. Creating the review.
-. Reviewing the change.
-. Reworking the change.
-. Verifying the change.
-. Submitting the change.
-
-NOTE: The project and commands used in this section are for demonstration
-purposes only.
-
-=== Making the Change
-
-Our first developer, Max, has decided to make a change to the +RecipeBook+
-project he works on. His first step is to get the source code that he wants to
-modify. To get this code, he runs the following `git clone` command:
-
-----
-$ git clone ssh://gerrithost:29418/RecipeBook.git RecipeBook
-Cloning into RecipeBook...
-----
-
-After he clones the repository, he makes a change to the file and commits it
-locally.
-
-NOTE: At this point, the workflow is exactly the same as it would be if Max was
-not using Gerrit.
-
-Max is ready to create a commit message for his change. When he does, he
-includes a link:user-changeid.html[Change-Id]. This ID allows Gerrit to link
-together different versions of the same change being reviewed.
-
-=== Creating the Review
-
-Max's next step is to push his change to Gerrit so other contributors can review
-it. He does this using the `git push origin HEAD:refs/for/master` command, as
-follows:
-
-----
-$ <work>
-$ git commit
-[master 9651f22] Change to a proper, yeast based pizza dough.
- 1 files changed, 3 insertions(+), 2 deletions(-)
-$ git push origin HEAD:refs/for/master
-Counting objects: 5, done.
-Delta compression using up to 8 threads.
-Compressing objects: 100% (2/2), done.
-Writing objects: 100% (3/3), 542 bytes, done.
-Total 3 (delta 0), reused 0 (delta 0)
-remote:
-remote: New Changes:
-remote: http://gerrithost:8080/68
-remote:
-To ssh://gerrithost:29418/RecipeBook.git
- * [new branch] HEAD -> refs/for/master
-----
-
-The `refs/for/master` branch is a symbolic branch that Gerrit uses to create
-reviews for the master branch. If Max opted to push to a different branch, he
-would have modified his command to
-`git push origin HEAD:refs/for/<branch_name>`. Gerrit automatically creates a
-`refs/for/<branch_name>` for every branch that it tracks.
-
-The output of this command also contains a link to a web page Max can use to
-review this commit. Clicking on that link takes him to a screen similar to
-the following.
-
-.Gerrit Code Review Screen
-image::images/intro-quick-new-review.jpg[Gerrit Review Screen]
-
-This is the Gerrit code review screen, where other contributors can review
-his change. Max can also perform tasks such as:
-
-* Looking at the link:user-review-ui.html#diff-preferences[diff] of his change
-* Writing link:user-review-ui.html#inline-comments[inline] or
- link:user-review-ui.html#reply[summary] comments to explain what he did and
- why
-* link:intro-user.html#adding-reviewers[Adding a list of people] that should
- review the change
-
-In this case, Max opts to manually add the senior developer on his team, Hannah,
-to review his change.
-
-=== Reviewing the Change
-
-Let's now switch to Hannah, the senior developer who will review Max's change.
-
-As mentioned previously, Max chose to manually add Hannah as a reviewer. Gerrit
-offers other ways for reviewers to find changes, including:
-
-* Using the link:user-search.html[search] feature that to find changes
-* Setting up link:user-notify.html[email notifications] to stay informed of
- changes even if you are not added as a reviewer
-
-Because Max added Hannah as a reviewer, she receives an email telling her about
-his change. She opens up the Gerrit code review screen and selects Max's change.
-
-.Gerrit Code Review Screen
-image::images/intro-quick-new-review.jpg[Gerrit Review Screen]
-
-Notice the two "Need" lines:
-
-----
-* Need Verified
-* Need Code-Review
-----
-
-These two lines indicate what checks must be completed before the change is
-accepted. The default Gerrit workflow requires two checks:
-
-* *Code-Review*. This check requires that someone look at the code and ensures
- that it meets project guidelines, styles, and other criteria.
-* *Verified*. This check means that the code actually compiles, passes any unit
- tests, and performs as expected.
-
-In general, the *Code-Review* check requires an individual to look at the code,
-while the *Verified* check is done by an automated build server, through a
-mechanism such as the
-link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[Gerrit Trigger
-Jenkins Plugin].
-
-IMPORTANT: The Code-Review and Verified checks require different permissions
-in Gerrit. This requirement allows teams to separate these tasks. For example,
-an automated process can have the rights to verify a change, but not perform a
-code review.
-
-With the code review screen open, Hannah can begin to review Max's change. She
-can choose one of two ways to review the change: unified or side-by-side.
-Both views allow her to perform tasks such as add
-link:user-review-ui.html#inline-comments[inline] or
-link:user-review-ui.html#reply[summary] comments.
-
-Hannah opts to view the change using Gerrit's side-by-side view:
-
-.Side By Side Patch View
-image::images/intro-quick-review-line-comment.jpg[Adding a Comment]
-
-Hannah reviews the change and is ready to provide her feedback. She clicks the
-*Review* button on the change screen. This allows her to vote on the change.
-
-.Reviewing the Change
-image::images/intro-quick-reviewing-the-change.jpg[Reviewing the Change]
-
-A code review vote is essentially a numerical score between -2 and 2. The
-possible options are:
-
-* `+2 Looks good to me, approved`
-* `+1 Looks good to me, but someone else must approve`
-* `0 No score`
-* `-1 I would prefer that you didn't submit this`
-* `-2 Do not submit`
-
-By default, a change must have at least one `+2` vote and no `-2` votes before
-it can be submitted.
-
-IMPORTANT: Although votes use numerical values, they do not accumulate. Two
-`+1` votes do not equate to a `+2`.
-
-Hannah notices a possible issue with Max's change, so she selects a `-1` vote.
-She uses the *Cover Message* text box to provide Max with some additional
-feedback. When she is satisfied with her review, Hannah clicks the
-*Published Comments* button. At this point, her vote and cover message become
-visible to to all users.
-
-=== Reworking the Change
-
-Later in the day, Max decides to check on his change and notices Hannah's
-feedback. He opens up the source file and incorporates her feedback. Because
-Max set up the link:user-changeid.html[Change-Id commit-msg hook]
-before he uploaded the change, all he has to do to upload the re-worked change
-is push another commit with the same Change-Id in the message. To accomplish
-this, Max needs only perform the following tasks:
-
-* Check out the commit
-* Amend the commit
-* Push the commit to Gerrit
-
-----
-$ <checkout first commit>
-$ <rework>
-$ git commit --amend
-$ git push origin HEAD:refs/for/master
-Counting objects: 5, done.
-Delta compression using up to 8 threads.
-Compressing objects: 100% (2/2), done.
-Writing objects: 100% (3/3), 546 bytes, done.
-Total 3 (delta 0), reused 0 (delta 0)
-remote: Processing changes: updated: 1, done
-remote:
-remote: Updated Changes:
-remote: http://gerrithost:8080/68
-remote:
-To ssh://gerrithost:29418/RecipeBook.git
- * [new branch] HEAD -> refs/for/master
-----
-
-Notice that the output of this command is slightly different from Max's first
-commit. This time, the output verifies that the change was updated.
-
-Having uploaded the reworked commit, Max can go back to the Gerrit web
-interface and look at his change.
-
-.Reviewing the Rework
-image::images/intro-quick-review-2-patches.jpg[Reviewing the Rework]
-
-Notice that there are now two patch sets associated with this change: the
-initial submission and the rework.
-
-When Hannah next looks at Max's change, she sees that he incorporated her
-feedback. The change looks good to her, so she changes her vote to a `+2`.
-
-=== Verifying the Change
-
-Hannah's `+2` vote means that Max's change satisfies the *Needs Review*
-check. It has to pass one more check before it can be accepted: the *Needs
-Verified* check.
-
-The Verified check means that the change was confirmed to work. This type of
-check typically involves tasks such as checking that the code compiles, unit
-tests pass, and other actions. You can configure a Verified check to consist
-of as many or as few tasks as needed.
-
-NOTE: Remember that this walkthrough uses Gerrit's default workflow. Projects
-can add custom checks or even remove the Verified check entirely.
-
-Verification is typically an automated process using the
-link:https://wiki.jenkins-ci.org/display/JENKINS/Gerrit+Trigger[Gerrit Trigger Jenkins Plugin]
-or a similar mechanism. However, there are still times when a change requires
-manual verification, or a reviewer needs to check how or if a change works.
-To accommodate these and other similar circumstances, Gerrit exposes each change
-as a git branch. The Gerrit UI includes a
-link:user-review-us.html#download[*download*] link in the Gerrit Code Review
-Screen to make it easy for reviewers to fetch a branch for a specific change.
-To manually verify a change, a reviewer must have the
-link:config-labels.html#label_Verified[Verified] permission. Then, the reviewer
-can fetch and checkout that branch from Gerrit. Hannah has this permission, so
-she is authorized to manually verify Max's change.
-
-NOTE: The Verifier can be the same person as the code reviewer or a
-different person entirely.
-
-.Verifying the Change
-image::images/intro-quick-verifying.jpg[Verifying the Change]
-
-Unlike the code review check, the verify check is pass/fail. Hannah can provide
-a score of either `+1` or `-1`. A change must have at least one `+1` and no
-`-1`.
-
-Hannah selects a `+1` for her verified check. Max's change is now ready to be
-submitted.
-
-=== Submitting the Change
-
-Max is now ready to submit his change. He opens up the change in the Code Review
-screen and clicks the *Publish and Submit* button.
-
-At this point, Max's change is merged into the main part of the repository and
-becomes an accepted part of the project.
-
-== Next Steps
-
-This walkthrough provided a quick overview of how a change moves
-through the default Gerrit workflow. At this point, you can:
-
-* Read the link:intro-user.html[Users guide] to get a better sense of how to
- make changes using Gerrit
-* Review the link:intro-project-owner.html[Project Owners guide] to learn more
- about configuring projects in Gerrit, including setting user permissions and
- configuring verification checks
+. link:intro-user.html[User Guide]
+. link:intro-project-owner.html[Project Owner Guide]
+. link:https://source.android.com/source/life-of-a-patch[Default Android Workflow] (external)
GERRIT
------
@@ -333,3 +37,4 @@
SEARCHBOX
---------
+
diff --git a/Documentation/pgm-daemon.txt b/Documentation/pgm-daemon.txt
index 76a26e1..0b1a3e5 100644
--- a/Documentation/pgm-daemon.txt
+++ b/Documentation/pgm-daemon.txt
@@ -94,8 +94,6 @@
----
[cache "accounts"]
maxAge = 5 min
-[cache "accounts_byemail"]
- maxAge = 5 min
[cache "diff"]
maxAge = 5 min
[cache "groups"]
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index b8c6025..557fb3c 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1312,6 +1312,9 @@
}
----
+Note that this endpoint will not update the change's parents, which is
+different from the link:#cherry-pick[cherry-pick] endpoint.
+
If the change cannot be moved because the change state doesn't
allow moving the change, the response is "`409 Conflict`" and
the error message is contained in the response body.
diff --git a/Documentation/rest-api-config.txt b/Documentation/rest-api-config.txt
index 6505f27..6c8848b 100644
--- a/Documentation/rest-api-config.txt
+++ b/Documentation/rest-api-config.txt
@@ -298,16 +298,6 @@
"mem": 94
}
},
- "accounts_byemail": {
- "type": "MEM",
- "entries": {
- "mem": 4
- },
- "average_get": "771.8us",
- "hit_ratio": {
- "mem": 95
- }
- },
"accounts_byname": {
"type": "MEM",
"entries": {
@@ -514,7 +504,6 @@
)]}'
[
"accounts",
- "accounts_byemail",
"accounts_byname",
"adv_bases",
"change_kind",
diff --git a/gerrit-acceptance-framework/BUILD b/gerrit-acceptance-framework/BUILD
index b351c27..465dcc6 100644
--- a/gerrit-acceptance-framework/BUILD
+++ b/gerrit-acceptance-framework/BUILD
@@ -10,6 +10,7 @@
"//gerrit-lucene:lucene",
"//gerrit-pgm:init",
"//gerrit-reviewdb:server",
+ "//gerrit-server:receive",
"//gerrit-server:server",
"//lib:gson",
"//lib:jsch",
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
index bfd012b..806469e 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/AccountCreator.java
@@ -23,7 +23,6 @@
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountsUpdate;
import com.google.gerrit.server.account.VersionedAuthorizedKeys;
@@ -60,7 +59,6 @@
private final Provider<GroupsUpdate> groupsUpdateProvider;
private final SshKeyCache sshKeyCache;
private final AccountCache accountCache;
- private final AccountByEmailCache byEmailCache;
private final ExternalIdsUpdate.Server externalIdsUpdate;
private final boolean sshEnabled;
@@ -74,7 +72,6 @@
@ServerInitiated Provider<GroupsUpdate> groupsUpdateProvider,
SshKeyCache sshKeyCache,
AccountCache accountCache,
- AccountByEmailCache byEmailCache,
ExternalIdsUpdate.Server externalIdsUpdate,
@SshEnabled boolean sshEnabled) {
accounts = new HashMap<>();
@@ -86,7 +83,6 @@
this.groupsUpdateProvider = groupsUpdateProvider;
this.sshKeyCache = sshKeyCache;
this.accountCache = accountCache;
- this.byEmailCache = byEmailCache;
this.externalIdsUpdate = externalIdsUpdate;
this.sshEnabled = sshEnabled;
}
@@ -148,7 +144,6 @@
if (username != null) {
accountCache.evictByUsername(username);
}
- byEmailCache.evict(email);
account = new TestAccount(id, username, email, fullName, sshKey, httpPass);
if (username != null) {
diff --git a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index 256be82..a95ac09 100644
--- a/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-framework/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -27,7 +27,7 @@
import com.google.gerrit.pgm.Daemon;
import com.google.gerrit.pgm.Init;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.ssh.NoSshModule;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
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 1219b0a..3174090 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
@@ -14,6 +14,7 @@
package com.google.gerrit.acceptance;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.acceptance.InProcessProtocol.Context;
import com.google.gerrit.common.data.Capable;
@@ -28,11 +29,10 @@
import com.google.gerrit.server.RequestCleanup;
import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.config.RequestScopedReviewDbProvider;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
-import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.git.ReceivePackInitializer;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
@@ -297,10 +297,10 @@
throw new ServiceNotAuthorizedException();
}
- ReceiveCommits rc = factory.create(ctl, db).getReceiveCommits();
- ReceivePack rp = rc.getReceivePack();
+ AsyncReceiveCommits arc = factory.create(ctl, db, null, ImmutableSetMultimap.of());
+ ReceivePack rp = arc.getReceivePack();
- Capable r = rc.canUpload();
+ Capable r = arc.canUpload();
if (r != Capable.OK) {
throw new ServiceNotAuthorizedException();
}
diff --git a/gerrit-acceptance-tests/BUILD b/gerrit-acceptance-tests/BUILD
index 0ad9002..91b90e3 100644
--- a/gerrit-acceptance-tests/BUILD
+++ b/gerrit-acceptance-tests/BUILD
@@ -20,6 +20,7 @@
"//gerrit-pgm:util",
"//gerrit-reviewdb:server",
"//gerrit-server:prolog-common",
+ "//gerrit-server:receive",
"//gerrit-server:server",
"//gerrit-server:testutil",
"//gerrit-sshd:sshd",
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 06fe021..6c1fb39 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -78,9 +78,9 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountConfig;
import com.google.gerrit.server.account.AccountsUpdate;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.account.WatchConfig;
import com.google.gerrit.server.account.WatchConfig.NotifyType;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -144,8 +144,6 @@
@Inject private AccountsUpdate.Server accountsUpdate;
- @Inject private AccountByEmailCache byEmailCache;
-
@Inject private ExternalIds externalIds;
@Inject private ExternalIdsUpdate.User externalIdsUpdateFactory;
@@ -156,6 +154,8 @@
@Inject private InternalAccountQuery accountQuery;
+ @Inject protected Emails emails;
+
private AccountIndexedCounter accountIndexedCounter;
private RegistrationHandle accountIndexEventCounterHandle;
private ExternalIdsUpdate externalIdsUpdate;
@@ -677,52 +677,44 @@
}
@Test
- public void lookUpFromCacheByEmail() throws Exception {
+ public void lookUpByEmail() throws Exception {
// exact match with scheme "mailto:"
- assertEmail(byEmailCache.get(admin.email), admin);
+ assertEmail(emails.getAccountFor(admin.email), admin);
// exact match with other scheme
String email = "foo.bar@example.com";
externalIdsUpdateFactory
.create()
.insert(ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email));
- assertEmail(byEmailCache.get(email), admin);
+ assertEmail(emails.getAccountFor(email), admin);
// wrong case doesn't match
- assertThat(byEmailCache.get(admin.email.toUpperCase(Locale.US))).isEmpty();
+ assertThat(emails.getAccountFor(admin.email.toUpperCase(Locale.US))).isEmpty();
// prefix doesn't match
- assertThat(byEmailCache.get(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty();
+ assertThat(emails.getAccountFor(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty();
// non-existing doesn't match
- assertThat(byEmailCache.get("non-existing@example.com")).isEmpty();
+ assertThat(emails.getAccountFor("non-existing@example.com")).isEmpty();
+
+ // lookup several accounts by email at once
+ ImmutableSetMultimap<String, Account.Id> byEmails =
+ emails.getAccountsFor(admin.email, user.email);
+ assertEmail(byEmails.get(admin.email), admin);
+ assertEmail(byEmails.get(user.email), user);
}
@Test
- public void lookUpByEmail() throws Exception {
- // exact match with scheme "mailto:"
- assertEmail(accounts.byEmail(admin.email), admin);
+ public void lookUpByPreferredEmail() throws Exception {
+ // create an inconsistent account that has a preferred email without external ID
+ String prefEmail = "foo.preferred@example.com";
+ TestAccount foo = accountCreator.create(name("foo"));
+ accountsUpdate.create().update(db, foo.id, a -> a.setPreferredEmail(prefEmail));
- // exact match with other scheme
- String email = "foo.bar@example.com";
- externalIdsUpdateFactory
- .create()
- .insert(ExternalId.createWithEmail(ExternalId.Key.parse("foo:bar"), admin.id, email));
- assertEmail(accounts.byEmail(email), admin);
-
- // wrong case doesn't match
- assertThat(accounts.byEmail(admin.email.toUpperCase(Locale.US))).isEmpty();
-
- // prefix doesn't match
- assertThat(accounts.byEmail(admin.email.substring(0, admin.email.indexOf('@')))).isEmpty();
-
- // non-existing doesn't match
- assertThat(accounts.byEmail("non-existing@example.com")).isEmpty();
-
- // lookup several accounts by email at once
- ImmutableSetMultimap<String, Account.Id> byEmails = accounts.byEmails(admin.email, user.email);
- assertEmail(byEmails.get(admin.email), admin);
- assertEmail(byEmails.get(user.email), user);
+ // verify that the account is still found when using the preferred email to lookup the account
+ ImmutableSet<Account.Id> accountsByPrefEmail = emails.getAccountFor(prefEmail);
+ assertThat(accountsByPrefEmail).hasSize(1);
+ assertThat(Iterables.getOnlyElement(accountsByPrefEmail)).isEqualTo(foo.id);
}
@Test
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 854ab47..91cbc6d 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -64,7 +64,7 @@
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.git.ReceiveCommits;
+import com.google.gerrit.server.git.receive.ReceiveConstants;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.project.Util;
import com.google.gerrit.server.query.change.ChangeData;
@@ -490,7 +490,7 @@
GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
testRepo.reset("ps");
r = amendChange(r.getChangeId(), "refs/for/master%ready", admin, testRepo);
- r.assertErrorStatus(ReceiveCommits.ONLY_OWNER_CAN_MODIFY_WIP);
+ r.assertErrorStatus(ReceiveConstants.ONLY_OWNER_CAN_MODIFY_WIP);
// Other user trying to move from WIP to WIP should succeed.
r = amendChange(r.getChangeId(), "refs/for/master%wip", admin, testRepo);
@@ -506,7 +506,7 @@
GitUtil.fetch(testRepo, r.getPatchSet().getRefName() + ":ps");
testRepo.reset("ps");
r = amendChange(r.getChangeId(), "refs/for/master%wip", admin, testRepo);
- r.assertErrorStatus(ReceiveCommits.ONLY_OWNER_CAN_MODIFY_WIP);
+ r.assertErrorStatus(ReceiveConstants.ONLY_OWNER_CAN_MODIFY_WIP);
// Other user trying to move from ready to ready should succeed.
r = amendChange(r.getChangeId(), "refs/for/master%ready", admin, testRepo);
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
index d79095f..5affacf 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/RefAdvertisementIT.java
@@ -39,8 +39,8 @@
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AnonymousCowardName;
import com.google.gerrit.server.git.ProjectConfig;
-import com.google.gerrit.server.git.ReceiveCommitsAdvertiseRefsHook;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.ReceiveCommitsAdvertiseRefsHook;
import com.google.gerrit.server.notedb.ChangeNoteUtil;
import com.google.gerrit.server.notedb.NoteDbChangeState.PrimaryStorage;
import com.google.gerrit.server.project.Util;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 6cbc532..e361091 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -479,7 +479,7 @@
assertThat(commitsInRepo)
.containsAllOf("Initial empty repository", "Change 1", "Change 2", "Change 3");
if (getSubmitType() == SubmitType.MERGE_ALWAYS) {
- assertThat(commitsInRepo).contains("Merge changes from topic '" + expectedTopic + "'");
+ assertThat(commitsInRepo).contains("Merge changes from topic \"" + expectedTopic + "\"");
}
}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
index 308c9a5..bb7da11 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/SubmitResolvingMergeCommitIT.java
@@ -26,6 +26,7 @@
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.git.ChangeSet;
import com.google.gerrit.server.git.MergeSuperSet;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gwtorm.server.OrmException;
@@ -304,7 +305,8 @@
}
private void assertChangeSetMergeable(ChangeData change, boolean expected)
- throws MissingObjectException, IncorrectObjectTypeException, IOException, OrmException {
+ throws MissingObjectException, IncorrectObjectTypeException, IOException, OrmException,
+ PermissionBackendException {
ChangeSet cs = mergeSuperSet.get().completeChangeSet(db, change.change(), user(admin));
assertThat(submit.unmergeableChanges(cs).isEmpty()).isEqualTo(expected);
}
diff --git a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
index 2755b91..21a5b6e 100644
--- a/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
+++ b/gerrit-gpg/src/main/java/com/google/gerrit/gpg/SignedPushPreReceiveHook.java
@@ -28,9 +28,8 @@
/**
* Pre-receive hook to check signed pushes.
*
- * <p>If configured, prior to processing any push using {@link
- * com.google.gerrit.server.git.ReceiveCommits}, requires that any push certificate present must be
- * valid.
+ * <p>If configured, prior to processing any push using {@code ReceiveCommits}, requires that any
+ * push certificate present must be valid.
*/
@Singleton
public class SignedPushPreReceiveHook implements PreReceiveHook {
diff --git a/gerrit-httpd/BUILD b/gerrit-httpd/BUILD
index 9b1b610..57ecd26 100644
--- a/gerrit-httpd/BUILD
+++ b/gerrit-httpd/BUILD
@@ -26,6 +26,7 @@
"//gerrit-patch-jgit:server",
"//gerrit-prettify:server",
"//gerrit-reviewdb:server",
+ "//gerrit-server:receive",
"//gerrit-server:server",
"//gerrit-util-cli:cli",
"//gerrit-util-http:http",
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 a89e2d9..f073acf 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
@@ -15,6 +15,7 @@
package com.google.gerrit.httpd;
import com.google.common.cache.Cache;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Lists;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -24,11 +25,10 @@
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.cache.CacheModule;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
import com.google.gerrit.server.git.GitRepositoryManager;
-import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.git.validators.UploadValidators;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -80,7 +80,7 @@
private static final long serialVersionUID = 1L;
private static final String ATT_CONTROL = ProjectControl.class.getName();
- private static final String ATT_RC = ReceiveCommits.class.getName();
+ private static final String ATT_ARC = AsyncReceiveCommits.class.getName();
private static final String ID_CACHE = "adv_bases";
public static final String URL_REGEX;
@@ -295,11 +295,9 @@
throw new ServiceNotAuthorizedException();
}
- ReceiveCommits rc = factory.create(pc, db).getReceiveCommits();
- rc.init();
-
- ReceivePack rp = rc.getReceivePack();
- req.setAttribute(ATT_RC, rc);
+ AsyncReceiveCommits arc = factory.create(pc, db, null, ImmutableSetMultimap.of());
+ ReceivePack rp = arc.getReceivePack();
+ req.setAttribute(ATT_ARC, arc);
return rp;
}
}
@@ -325,8 +323,8 @@
throws IOException, ServletException {
boolean isGet = "GET".equalsIgnoreCase(((HttpServletRequest) request).getMethod());
- ReceiveCommits rc = (ReceiveCommits) request.getAttribute(ATT_RC);
- ReceivePack rp = rc.getReceivePack();
+ 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();
@@ -340,7 +338,7 @@
return;
}
- final Capable s = rc.canUpload();
+ Capable s = arc.canUpload();
if (s != Capable.OK) {
GitSmartHttpTools.sendError(
(HttpServletRequest) request,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 9967af6..538d605 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -28,7 +28,7 @@
import com.google.gerrit.server.config.GerritOptions;
import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.config.GitwebCgiConfig;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.util.GuiceRequestScopePropagator;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.inject.Inject;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
index e721b7a..365789c 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/auth/become/BecomeAnyAccountLoginServlet.java
@@ -44,6 +44,7 @@
import java.io.OutputStream;
import java.io.Writer;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@@ -215,8 +216,15 @@
private AuthResult byPreferredEmail(String email) {
try (ReviewDb db = schema.open()) {
- List<Account> matches = db.accounts().byPreferredEmail(email).toList();
- return matches.size() == 1 ? auth(matches.get(0)) : null;
+ Optional<Account> match =
+ accountQuery
+ .byPreferredEmail(email)
+ .stream()
+ // the index query also matches prefixes, filter those out
+ .filter(a -> email.equalsIgnoreCase(a.getAccount().getPreferredEmail()))
+ .map(AccountState::getAccount)
+ .findFirst();
+ return match.isPresent() ? auth(match.get()) : null;
} catch (OrmException e) {
getServletContext().log("cannot query database", e);
return null;
diff --git a/gerrit-pgm/BUILD b/gerrit-pgm/BUILD
index 6056805..24a19d4 100644
--- a/gerrit-pgm/BUILD
+++ b/gerrit-pgm/BUILD
@@ -29,6 +29,8 @@
DEPS = BASE_JETTY_DEPS + [
"//gerrit-reviewdb:server",
+ "//gerrit-server:module",
+ "//gerrit-server:receive",
"//lib:gwtorm",
"//lib/log:jsonevent-layout",
]
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 3ef5f2b..72920d0 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
@@ -65,9 +65,9 @@
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.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.index.DummyIndexModule;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index 788f7ce..d56a5a6 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -23,11 +23,11 @@
import com.google.gerrit.extensions.config.FactoryModule;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicSet;
+import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.rules.PrologModule;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountByEmailCacheImpl;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountVisibility;
import com.google.gerrit.server.account.AccountVisibilityProvider;
@@ -53,16 +53,17 @@
import com.google.gerrit.server.config.GitReceivePackGroups;
import com.google.gerrit.server.config.GitUploadPackGroups;
import com.google.gerrit.server.git.MergeUtil;
-import com.google.gerrit.server.git.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.group.GroupModule;
import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
import com.google.gerrit.server.notedb.NoteDbModule;
import com.google.gerrit.server.patch.DiffExecutorModule;
import com.google.gerrit.server.patch.PatchListCacheImpl;
import com.google.gerrit.server.project.CommentLinkProvider;
+import com.google.gerrit.server.project.CommitResource;
import com.google.gerrit.server.project.DefaultPermissionBackendModule;
import com.google.gerrit.server.project.ProjectCacheImpl;
import com.google.gerrit.server.project.ProjectState;
@@ -115,6 +116,8 @@
.in(SINGLETON);
bind(new TypeLiteral<DynamicMap<ChangeQueryProcessor.ChangeAttributeFactory>>() {})
.toInstance(DynamicMap.<ChangeQueryProcessor.ChangeAttributeFactory>emptyMap());
+ bind(new TypeLiteral<DynamicMap<RestView<CommitResource>>>() {})
+ .toInstance(DynamicMap.<RestView<CommitResource>>emptyMap());
bind(String.class)
.annotatedWith(CanonicalWebUrl.class)
.toProvider(CanonicalWebUrlProvider.class);
@@ -153,7 +156,6 @@
install(new GroupModule());
install(new NoteDbModule(cfg));
install(new PrologModule());
- install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
install(GroupCacheImpl.module());
install(GroupIncludeCacheImpl.module());
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountAccess.java
index db74caa..9f371f2 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountAccess.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/server/AccountAccess.java
@@ -28,9 +28,6 @@
@PrimaryKey("accountId")
Account get(Account.Id key) throws OrmException;
- @Query("WHERE preferredEmail = ? LIMIT 2")
- ResultSet<Account> byPreferredEmail(String email) throws OrmException;
-
@Query("ORDER BY accountId")
ResultSet<Account> all() throws OrmException;
}
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
index 5a83dc4..8f87503 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_generic.sql
@@ -5,13 +5,6 @@
--
-- *********************************************************************
--- AccountAccess
--- covers: byPreferredEmail
-CREATE INDEX accounts_byPreferredEmail
-ON accounts (preferred_email);
-
-
--- *********************************************************************
-- AccountGroupMemberAccess
-- @PrimaryKey covers: byAccount
CREATE INDEX account_group_members_byGroup
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
index 98f05ca..57b1a4a 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_maxdb.sql
@@ -6,14 +6,6 @@
--
-- *********************************************************************
--- AccountAccess
--- covers: byPreferredEmail
-CREATE INDEX accounts_byPreferredEmail
-ON accounts (preferred_email)
-#
-
-
--- *********************************************************************
-- AccountGroupMemberAccess
-- @PrimaryKey covers: byAccount
CREATE INDEX account_group_members_byGroup
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
index dde86a4..e1d88ef 100644
--- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
+++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/server/index_postgres.sql
@@ -52,13 +52,6 @@
--
-- *********************************************************************
--- AccountAccess
--- covers: byPreferredEmail
-CREATE INDEX accounts_byPreferredEmail
-ON accounts (preferred_email);
-
-
--- *********************************************************************
-- AccountGroupMemberAccess
-- @PrimaryKey covers: byAccount
CREATE INDEX account_group_members_byGroup
diff --git a/gerrit-server/BUILD b/gerrit-server/BUILD
index aa7962e..567ec6c 100644
--- a/gerrit-server/BUILD
+++ b/gerrit-server/BUILD
@@ -5,9 +5,15 @@
"src/main/java/com/google/gerrit/server/documentation/Constants.java",
]
+GERRIT_GLOBAL_MODULE_SRC = [
+ "src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java",
+]
+
+RECEIVE_SRCS = glob(["src/main/java/com/google/gerrit/server/git/receive/**/*.java"])
+
SRCS = glob(
["src/main/java/**/*.java"],
- exclude = CONSTANTS_SRC,
+ exclude = CONSTANTS_SRC + GERRIT_GLOBAL_MODULE_SRC + RECEIVE_SRCS,
)
RESOURCES = glob(["src/main/resources/**/*"])
@@ -25,6 +31,12 @@
deps = [":server"],
)
+# Giant kitchen-sink target.
+#
+# The only reason this hasn't been split up further is because we have too many
+# tangled dependencies (and Guice unfortunately makes it quite easy to get into
+# this state). Which means if you see an opportunity to split something off, you
+# should seize it.
java_library(
name = "server",
srcs = SRCS,
@@ -94,7 +106,49 @@
],
)
+# Large modules that import things from all across the server package
+# hierarchy, so they need lots of dependencies.
+java_library(
+ name = "module",
+ srcs = GERRIT_GLOBAL_MODULE_SRC,
+ visibility = ["//visibility:public"],
+ deps = [
+ ":receive",
+ ":server",
+ "//gerrit-extension-api:api",
+ "//lib:blame-cache",
+ "//lib:guava",
+ "//lib:soy",
+ "//lib:velocity",
+ "//lib/guice",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ ],
+)
+
+java_library(
+ name = "receive",
+ srcs = RECEIVE_SRCS,
+ visibility = ["//visibility:public"],
+ deps = [
+ ":server",
+ "//gerrit-common:annotations",
+ "//gerrit-common:server",
+ "//gerrit-extension-api:api",
+ "//gerrit-reviewdb:server",
+ "//gerrit-util-cli:cli",
+ "//lib:args4j",
+ "//lib:guava",
+ "//lib:gwtorm",
+ "//lib/auto:auto-value",
+ "//lib/guice",
+ "//lib/guice:guice-assistedinject",
+ "//lib/jgit/org.eclipse.jgit:jgit",
+ "//lib/log:api",
+ ],
+)
+
TESTUTIL_DEPS = [
+ ":module",
":server",
"//gerrit-common:annotations",
"//gerrit-common:server",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/EventBroker.java b/gerrit-server/src/main/java/com/google/gerrit/common/EventBroker.java
index 3cc7335..8bfd880 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/EventBroker.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/EventBroker.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.events.ProjectEvent;
import com.google.gerrit.server.events.RefEvent;
import com.google.gerrit.server.notedb.ChangeNotes;
+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.permissions.ProjectPermission;
@@ -86,7 +87,8 @@
}
@Override
- public void postEvent(Change change, ChangeEvent event) throws OrmException {
+ public void postEvent(Change change, ChangeEvent event)
+ throws OrmException, PermissionBackendException {
fireEvent(change, event);
}
@@ -101,7 +103,7 @@
}
@Override
- public void postEvent(Event event) throws OrmException {
+ public void postEvent(Event event) throws OrmException, PermissionBackendException {
fireEvent(event);
}
@@ -111,7 +113,8 @@
}
}
- protected void fireEvent(Change change, ChangeEvent event) throws OrmException {
+ protected void fireEvent(Change change, ChangeEvent event)
+ throws OrmException, PermissionBackendException {
for (UserScopedEventListener listener : listeners) {
if (isVisibleTo(change, listener.getUser())) {
listener.onEvent(event);
@@ -138,7 +141,7 @@
fireEventForUnrestrictedListeners(event);
}
- protected void fireEvent(Event event) throws OrmException {
+ protected void fireEvent(Event event) throws OrmException, PermissionBackendException {
for (UserScopedEventListener listener : listeners) {
if (isVisibleTo(event, listener.getUser())) {
listener.onEvent(event);
@@ -156,7 +159,8 @@
}
}
- protected boolean isVisibleTo(Change change, CurrentUser user) throws OrmException {
+ protected boolean isVisibleTo(Change change, CurrentUser user)
+ throws OrmException, PermissionBackendException {
if (change == null) {
return false;
}
@@ -164,9 +168,12 @@
if (pe == null) {
return false;
}
- ProjectControl pc = pe.controlFor(user);
ReviewDb db = dbProvider.get();
- return pc.controlFor(db, change).isVisible(db);
+ return permissionBackend
+ .user(user)
+ .change(notesFactory.createChecked(db, change))
+ .database(db)
+ .test(ChangePermission.READ);
}
protected boolean isVisibleTo(Branch.NameKey branchName, CurrentUser user) {
@@ -178,7 +185,8 @@
return pc.controlForRef(branchName).isVisible();
}
- protected boolean isVisibleTo(Event event, CurrentUser user) throws OrmException {
+ protected boolean isVisibleTo(Event event, CurrentUser user)
+ throws OrmException, PermissionBackendException {
if (event instanceof RefEvent) {
RefEvent refEvent = (RefEvent) event;
String ref = refEvent.getRefName();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/EventDispatcher.java b/gerrit-server/src/main/java/com/google/gerrit/common/EventDispatcher.java
index 20d55d6..947b656 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/common/EventDispatcher.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/common/EventDispatcher.java
@@ -21,6 +21,7 @@
import com.google.gerrit.server.events.Event;
import com.google.gerrit.server.events.ProjectEvent;
import com.google.gerrit.server.events.RefEvent;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gwtorm.server.OrmException;
/** Interface for posting (dispatching) Events */
@@ -31,8 +32,9 @@
* @param change The change that the event is related to
* @param event The event to post
* @throws OrmException on failure to post the event due to DB error
+ * @throws PermissionBackendException on failure of permission checks
*/
- void postEvent(Change change, ChangeEvent event) throws OrmException;
+ void postEvent(Change change, ChangeEvent event) throws OrmException, PermissionBackendException;
/**
* Post a stream event that is related to a branch
@@ -58,6 +60,7 @@
*
* @param event The event to post.
* @throws OrmException on failure to post the event due to DB error
+ * @throws PermissionBackendException on failure of permission checks
*/
- void postEvent(Event event) throws OrmException;
+ void postEvent(Event event) throws OrmException, PermissionBackendException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
index 2d3b41f..eb4665f 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/rules/StoredValues.java
@@ -28,6 +28,7 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.patch.PatchList;
import com.google.gerrit.server.patch.PatchListCache;
@@ -50,6 +51,7 @@
public final class StoredValues {
public static final StoredValue<Accounts> ACCOUNTS = create(Accounts.class);
public static final StoredValue<AccountCache> ACCOUNT_CACHE = create(AccountCache.class);
+ public static final StoredValue<Emails> EMAILS = create(Emails.class);
public static final StoredValue<ReviewDb> REVIEW_DB = create(ReviewDb.class);
public static final StoredValue<ChangeData> CHANGE_DATA = create(ChangeData.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
index e4cca59..7750729 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ApprovalsUtil.java
@@ -47,6 +47,9 @@
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
+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.ChangeControl;
import com.google.gerrit.server.util.LabelVote;
import com.google.gwtorm.server.OrmException;
@@ -106,20 +109,20 @@
private final NotesMigration migration;
private final IdentifiedUser.GenericFactory userFactory;
- private final ChangeControl.GenericFactory changeControlFactory;
private final ApprovalCopier copier;
+ private final PermissionBackend permissionBackend;
@VisibleForTesting
@Inject
public ApprovalsUtil(
NotesMigration migration,
IdentifiedUser.GenericFactory userFactory,
- ChangeControl.GenericFactory changeControlFactory,
- ApprovalCopier copier) {
+ ApprovalCopier copier,
+ PermissionBackend permissionBackend) {
this.migration = migration;
this.userFactory = userFactory;
- this.changeControlFactory = changeControlFactory;
this.copier = copier;
+ this.permissionBackend = permissionBackend;
}
/**
@@ -262,8 +265,8 @@
private boolean canSee(ReviewDb db, ChangeNotes notes, Account.Id accountId) {
try {
IdentifiedUser user = userFactory.create(accountId);
- return changeControlFactory.controlFor(notes, user).isVisible(db);
- } catch (OrmException e) {
+ return permissionBackend.user(user).change(notes).database(db).test(ChangePermission.READ);
+ } catch (PermissionBackendException e) {
log.warn(
String.format(
"Failed to check if account %d can see change %d",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
deleted file mode 100644
index 255078a..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCacheImpl.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (C) 2009 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.account;
-
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.server.account.externalids.ExternalIds;
-import com.google.gerrit.server.cache.CacheModule;
-import com.google.inject.Inject;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import com.google.inject.TypeLiteral;
-import com.google.inject.name.Named;
-import java.util.Collections;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Translates an email address to a set of matching accounts. */
-@Singleton
-public class AccountByEmailCacheImpl implements AccountByEmailCache {
- private static final Logger log = LoggerFactory.getLogger(AccountByEmailCacheImpl.class);
- private static final String CACHE_NAME = "accounts_byemail";
-
- public static Module module() {
- return new CacheModule() {
- @Override
- protected void configure() {
- cache(CACHE_NAME, String.class, new TypeLiteral<Set<Account.Id>>() {}).loader(Loader.class);
- bind(AccountByEmailCacheImpl.class);
- bind(AccountByEmailCache.class).to(AccountByEmailCacheImpl.class);
- }
- };
- }
-
- private final LoadingCache<String, Set<Account.Id>> cache;
-
- @Inject
- AccountByEmailCacheImpl(@Named(CACHE_NAME) LoadingCache<String, Set<Account.Id>> cache) {
- this.cache = cache;
- }
-
- @Override
- public Set<Account.Id> get(String email) {
- try {
- return cache.get(email);
- } catch (ExecutionException e) {
- log.warn("Cannot resolve accounts by email", e);
- return Collections.emptySet();
- }
- }
-
- @Override
- public void evict(String email) {
- if (email != null) {
- cache.invalidate(email);
- }
- }
-
- static class Loader extends CacheLoader<String, Set<Account.Id>> {
- // This must be a provider to prevent a cyclic dependency within Google-internal glue code.
- private final Provider<ExternalIds> externalIds;
-
- @Inject
- Loader(Provider<ExternalIds> externalIds) {
- this.externalIds = externalIds;
- }
-
- @Override
- public Set<Account.Id> load(String email) throws Exception {
- return externalIds
- .get()
- .byEmail(email)
- .stream()
- .map(e -> e.accountId())
- .collect(toImmutableSet());
- }
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index d493cf5..f247d86 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -62,7 +62,6 @@
private final Accounts accounts;
private final AccountsUpdate.Server accountsUpdateFactory;
private final AccountCache byIdCache;
- private final AccountByEmailCache byEmailCache;
private final Realm realm;
private final IdentifiedUser.GenericFactory userFactory;
private final ChangeUserName.Factory changeUserNameFactory;
@@ -81,7 +80,6 @@
Accounts accounts,
AccountsUpdate.Server accountsUpdateFactory,
AccountCache byIdCache,
- AccountByEmailCache byEmailCache,
Realm accountMapper,
IdentifiedUser.GenericFactory userFactory,
ChangeUserName.Factory changeUserNameFactory,
@@ -95,7 +93,6 @@
this.accounts = accounts;
this.accountsUpdateFactory = accountsUpdateFactory;
this.byIdCache = byIdCache;
- this.byEmailCache = byEmailCache;
this.realm = accountMapper;
this.userFactory = userFactory;
this.changeUserNameFactory = changeUserNameFactory;
@@ -197,11 +194,6 @@
throw new OrmException("Account " + user.getAccountId() + " has been deleted");
}
}
-
- if (newEmail != null && !newEmail.equals(oldEmail)) {
- byEmailCache.evict(oldEmail);
- byEmailCache.evict(newEmail);
- }
}
private static boolean eq(String a, String b) {
@@ -300,7 +292,6 @@
}
}
- byEmailCache.evict(account.getPreferredEmail());
realm.onCreateAccount(who, account);
return new AuthResult(newId, extId.key(), true);
}
@@ -383,7 +374,6 @@
a.setPreferredEmail(who.getEmailAddress());
}
});
- byEmailCache.evict(who.getEmailAddress());
}
}
@@ -481,7 +471,6 @@
}
}
});
- extIds.stream().forEach(e -> byEmailCache.evict(e.email()));
}
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
index 7f66b9c..894f7a1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResolver.java
@@ -36,22 +36,22 @@
public class AccountResolver {
private final Realm realm;
private final Accounts accounts;
- private final AccountByEmailCache byEmail;
private final AccountCache byId;
private final Provider<InternalAccountQuery> accountQueryProvider;
+ private final Emails emails;
@Inject
AccountResolver(
Realm realm,
Accounts accounts,
- AccountByEmailCache byEmail,
AccountCache byId,
- Provider<InternalAccountQuery> accountQueryProvider) {
+ Provider<InternalAccountQuery> accountQueryProvider,
+ Emails emails) {
this.realm = realm;
this.accounts = accounts;
- this.byEmail = byEmail;
this.byId = byId;
this.accountQueryProvider = accountQueryProvider;
+ this.emails = emails;
}
/**
@@ -136,7 +136,8 @@
* @return the single account that matches; null if no account matches or there are multiple
* candidates.
*/
- public Account findByNameOrEmail(ReviewDb db, String nameOrEmail) throws OrmException {
+ public Account findByNameOrEmail(ReviewDb db, String nameOrEmail)
+ throws OrmException, IOException {
Set<Account.Id> r = findAllByNameOrEmail(db, nameOrEmail);
return r.size() == 1 ? byId.get(r.iterator().next()).getAccount() : null;
}
@@ -149,11 +150,12 @@
* address ("email@example"), a full name ("Full Name").
* @return the accounts that match, empty collection if none. Never null.
*/
- public Set<Account.Id> findAllByNameOrEmail(ReviewDb db, String nameOrEmail) throws OrmException {
+ public Set<Account.Id> findAllByNameOrEmail(ReviewDb db, String nameOrEmail)
+ throws OrmException, IOException {
int lt = nameOrEmail.indexOf('<');
int gt = nameOrEmail.indexOf('>');
if (lt >= 0 && gt > lt && nameOrEmail.contains("@")) {
- Set<Account.Id> ids = byEmail.get(nameOrEmail.substring(lt + 1, gt));
+ Set<Account.Id> ids = emails.getAccountFor(nameOrEmail.substring(lt + 1, gt));
if (ids.isEmpty() || ids.size() == 1) {
return ids;
}
@@ -171,7 +173,7 @@
}
if (nameOrEmail.contains("@")) {
- return byEmail.get(nameOrEmail);
+ return emails.getAccountFor(nameOrEmail);
}
Account.Id id = realm.lookup(nameOrEmail);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Accounts.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Accounts.java
index c3d0b81..28ed422 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Accounts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Accounts.java
@@ -14,18 +14,13 @@
package com.google.gerrit.server.account;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
-import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.account.externalids.ExternalIds;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -37,7 +32,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
@@ -56,20 +50,17 @@
private final GitRepositoryManager repoManager;
private final AllUsersName allUsersName;
private final OutgoingEmailValidator emailValidator;
- private final ExternalIds externalIds;
@Inject
Accounts(
@GerritServerConfig Config cfg,
GitRepositoryManager repoManager,
AllUsersName allUsersName,
- OutgoingEmailValidator emailValidator,
- ExternalIds externalIds) {
+ OutgoingEmailValidator emailValidator) {
this.readFromGit = cfg.getBoolean("user", null, "readAccountsFromGit", false);
this.repoManager = repoManager;
this.allUsersName = allUsersName;
this.emailValidator = emailValidator;
- this.externalIds = externalIds;
}
public Account get(ReviewDb db, Account.Id accountId)
@@ -99,44 +90,6 @@
}
/**
- * Returns the accounts with the given email.
- *
- * <p>Each email should belong to a single account only. This means if more than one account is
- * returned there is an inconsistency in the external IDs.
- *
- * <p>The accounts are retrieved via the external ID cache. Each access to the external ID cache
- * requires reading the SHA1 of the refs/meta/external-ids branch. If accounts for multiple emails
- * are needed it is more efficient to use {@link #byEmails(String...)} as this method reads the
- * SHA1 of the refs/meta/external-ids branch only once (and not once per email).
- *
- * @see #byEmails(String...)
- */
- public ImmutableSet<Account.Id> byEmail(String email) throws IOException {
- return externalIds.byEmail(email).stream().map(e -> e.accountId()).collect(toImmutableSet());
- }
-
- /**
- * Returns the accounts for the given emails.
- *
- * <p>Each email should belong to a single account only. This means if more than one account for
- * an email is returned there is an inconsistency in the external IDs.
- *
- * <p>The accounts are retrieved via the external ID cache. Each access to the external ID cache
- * requires reading the SHA1 of the refs/meta/external-ids branch. If accounts for multiple emails
- * are needed it is more efficient to use this method instead of {@link #byEmail(String)} as this
- * method reads the SHA1 of the refs/meta/external-ids branch only once (and not once per email).
- *
- * @see #byEmail(String)
- */
- public ImmutableSetMultimap<String, Account.Id> byEmails(String... emails) throws IOException {
- return externalIds
- .byEmails(emails)
- .entries()
- .stream()
- .collect(toImmutableSetMultimap(Map.Entry::getKey, e -> e.getValue().accountId()));
- }
-
- /**
* Returns all accounts.
*
* @return all accounts
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
index 77d45f8..1812ef4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/CreateAccount.java
@@ -69,7 +69,6 @@
private final SshKeyCache sshKeyCache;
private final AccountCache accountCache;
private final AccountsUpdate.User accountsUpdate;
- private final AccountByEmailCache byEmailCache;
private final AccountLoader.Factory infoLoader;
private final DynamicSet<AccountExternalIdCreator> externalIdCreators;
private final ExternalIds externalIds;
@@ -87,7 +86,6 @@
SshKeyCache sshKeyCache,
AccountCache accountCache,
AccountsUpdate.User accountsUpdate,
- AccountByEmailCache byEmailCache,
AccountLoader.Factory infoLoader,
DynamicSet<AccountExternalIdCreator> externalIdCreators,
ExternalIds externalIds,
@@ -102,7 +100,6 @@
this.sshKeyCache = sshKeyCache;
this.accountCache = accountCache;
this.accountsUpdate = accountsUpdate;
- this.byEmailCache = byEmailCache;
this.infoLoader = infoLoader;
this.externalIdCreators = externalIdCreators;
this.externalIds = externalIds;
@@ -202,7 +199,6 @@
}
accountCache.evictByUsername(username);
- byEmailCache.evict(input.email);
AccountLoader loader = infoLoader.create(true);
AccountInfo info = loader.get(id);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
index d8e46f4..9d9cf23 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -19,20 +19,23 @@
import com.google.gerrit.extensions.client.AuthType;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.config.AuthConfig;
+import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.Set;
@Singleton
public class DefaultRealm extends AbstractRealm {
private final EmailExpander emailExpander;
- private final AccountByEmailCache byEmail;
+ private final Provider<Emails> emails;
private final AuthConfig authConfig;
@Inject
- DefaultRealm(EmailExpander emailExpander, AccountByEmailCache byEmail, AuthConfig authConfig) {
+ DefaultRealm(EmailExpander emailExpander, Provider<Emails> emails, AuthConfig authConfig) {
this.emailExpander = emailExpander;
- this.byEmail = byEmail;
+ this.emails = emails;
this.authConfig = authConfig;
}
@@ -75,11 +78,15 @@
public void onCreateAccount(AuthRequest who, Account account) {}
@Override
- public Account.Id lookup(String accountName) {
+ public Account.Id lookup(String accountName) throws IOException {
if (emailExpander.canExpand(accountName)) {
- final Set<Account.Id> c = byEmail.get(emailExpander.expand(accountName));
- if (1 == c.size()) {
- return c.iterator().next();
+ try {
+ Set<Account.Id> c = emails.get().getAccountFor(emailExpander.expand(accountName));
+ if (1 == c.size()) {
+ return c.iterator().next();
+ }
+ } catch (OrmException e) {
+ throw new IOException("Failed to query accounts by email", e);
}
}
return null;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
index e31f481..15f4509 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Emails.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2013 The Android Open Source Project
+// 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.
@@ -14,80 +14,85 @@
package com.google.gerrit.server.account;
-import com.google.common.base.Strings;
-import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.AcceptsCreate;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.ChildCollection;
-import com.google.gerrit.extensions.restapi.IdString;
-import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
-import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.account.AccountResource.Email;
-import com.google.gerrit.server.permissions.GlobalPermission;
-import com.google.gerrit.server.permissions.PermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.Streams;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.server.account.externalids.ExternalIds;
+import com.google.gerrit.server.query.account.InternalAccountQuery;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.io.IOException;
+import java.util.List;
+
+/** Class to access accounts by email. */
@Singleton
-public class Emails
- implements ChildCollection<AccountResource, AccountResource.Email>,
- AcceptsCreate<AccountResource> {
- private final DynamicMap<RestView<AccountResource.Email>> views;
- private final GetEmails list;
- private final Provider<CurrentUser> self;
- private final PermissionBackend permissionBackend;
- private final CreateEmail.Factory createEmailFactory;
+public class Emails {
+ private final ExternalIds externalIds;
+ private final InternalAccountQuery accountQuery;
@Inject
- Emails(
- DynamicMap<RestView<AccountResource.Email>> views,
- GetEmails list,
- Provider<CurrentUser> self,
- PermissionBackend permissionBackend,
- CreateEmail.Factory createEmailFactory) {
- this.views = views;
- this.list = list;
- this.self = self;
- this.permissionBackend = permissionBackend;
- this.createEmailFactory = createEmailFactory;
+ public Emails(ExternalIds externalIds, InternalAccountQuery accountQuery) {
+ this.externalIds = externalIds;
+ this.accountQuery = accountQuery;
}
- @Override
- public RestView<AccountResource> list() {
- return list;
+ /**
+ * Returns the accounts with the given email.
+ *
+ * <p>Each email should belong to a single account only. This means if more than one account is
+ * returned there is an inconsistency in the external IDs.
+ *
+ * <p>The accounts are retrieved via the external ID cache. Each access to the external ID cache
+ * requires reading the SHA1 of the refs/meta/external-ids branch. If accounts for multiple emails
+ * are needed it is more efficient to use {@link #getAccountsFor(String...)} as this method reads
+ * the SHA1 of the refs/meta/external-ids branch only once (and not once per email).
+ *
+ * <p>In addition accounts are included that have the given email as preferred email even if they
+ * have no external ID for the preferred email. Having accounts with a preferred email that does
+ * not exist as external ID is an inconsistency, but existing functionality relies on still
+ * getting those accounts, which is why they are included. Accounts by preferred email are fetched
+ * from the account index.
+ *
+ * @see #getAccountsFor(String...)
+ */
+ public ImmutableSet<Account.Id> getAccountFor(String email) throws IOException, OrmException {
+ List<AccountState> byPreferredEmail = accountQuery.byPreferredEmail(email);
+ return Streams.concat(
+ externalIds.byEmail(email).stream().map(e -> e.accountId()),
+ byPreferredEmail
+ .stream()
+ // the index query also matches prefixes and emails with other case,
+ // filter those out
+ .filter(a -> email.equals(a.getAccount().getPreferredEmail()))
+ .map(a -> a.getAccount().getId()))
+ .collect(toImmutableSet());
}
- @Override
- public AccountResource.Email parse(AccountResource rsrc, IdString id)
- throws ResourceNotFoundException, PermissionBackendException, AuthException {
- if (self.get() != rsrc.getUser()) {
- permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
- }
-
- if ("preferred".equals(id.get())) {
- String email = rsrc.getUser().getAccount().getPreferredEmail();
- if (Strings.isNullOrEmpty(email)) {
- throw new ResourceNotFoundException(id);
- }
- return new AccountResource.Email(rsrc.getUser(), email);
- } else if (rsrc.getUser().hasEmailAddress(id.get())) {
- return new AccountResource.Email(rsrc.getUser(), id.get());
- } else {
- throw new ResourceNotFoundException(id);
- }
- }
-
- @Override
- public DynamicMap<RestView<Email>> views() {
- return views;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public CreateEmail create(AccountResource parent, IdString email) {
- return createEmailFactory.create(email.get());
+ /**
+ * Returns the accounts for the given emails.
+ *
+ * @see #getAccountFor(String)
+ */
+ public ImmutableSetMultimap<String, Account.Id> getAccountsFor(String... emails)
+ throws IOException, OrmException {
+ ImmutableSetMultimap.Builder<String, Account.Id> builder = ImmutableSetMultimap.builder();
+ externalIds
+ .byEmails(emails)
+ .entries()
+ .stream()
+ .forEach(e -> builder.put(e.getKey(), e.getValue().accountId()));
+ accountQuery
+ .byPreferredEmail(emails)
+ .entries()
+ .stream()
+ // the index query also matches prefixes and emails with other case,
+ // filter those out
+ .filter(e -> e.getKey().equals(e.getValue().getAccount().getPreferredEmail()))
+ .forEach(e -> builder.put(e.getKey(), e.getValue().getAccount().getId()));
+ return builder.build();
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/EmailsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/EmailsCollection.java
new file mode 100644
index 0000000..1c329bb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/EmailsCollection.java
@@ -0,0 +1,93 @@
+// Copyright (C) 2013 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.account;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AcceptsCreate;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.ChildCollection;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.AccountResource.Email;
+import com.google.gerrit.server.permissions.GlobalPermission;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackendException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+@Singleton
+public class EmailsCollection
+ implements ChildCollection<AccountResource, AccountResource.Email>,
+ AcceptsCreate<AccountResource> {
+ private final DynamicMap<RestView<AccountResource.Email>> views;
+ private final GetEmails list;
+ private final Provider<CurrentUser> self;
+ private final PermissionBackend permissionBackend;
+ private final CreateEmail.Factory createEmailFactory;
+
+ @Inject
+ EmailsCollection(
+ DynamicMap<RestView<AccountResource.Email>> views,
+ GetEmails list,
+ Provider<CurrentUser> self,
+ PermissionBackend permissionBackend,
+ CreateEmail.Factory createEmailFactory) {
+ this.views = views;
+ this.list = list;
+ this.self = self;
+ this.permissionBackend = permissionBackend;
+ this.createEmailFactory = createEmailFactory;
+ }
+
+ @Override
+ public RestView<AccountResource> list() {
+ return list;
+ }
+
+ @Override
+ public AccountResource.Email parse(AccountResource rsrc, IdString id)
+ throws ResourceNotFoundException, PermissionBackendException, AuthException {
+ if (self.get() != rsrc.getUser()) {
+ permissionBackend.user(self).check(GlobalPermission.ADMINISTRATE_SERVER);
+ }
+
+ if ("preferred".equals(id.get())) {
+ String email = rsrc.getUser().getAccount().getPreferredEmail();
+ if (Strings.isNullOrEmpty(email)) {
+ throw new ResourceNotFoundException(id);
+ }
+ return new AccountResource.Email(rsrc.getUser(), email);
+ } else if (rsrc.getUser().hasEmailAddress(id.get())) {
+ return new AccountResource.Email(rsrc.getUser(), id.get());
+ } else {
+ throw new ResourceNotFoundException(id);
+ }
+ }
+
+ @Override
+ public DynamicMap<RestView<Email>> views() {
+ return views;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public CreateEmail create(AccountResource parent, IdString email) {
+ return createEmailFactory.create(email.get());
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java
index 8df8c6b..1099d6b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetOAuthToken.java
@@ -27,11 +27,14 @@
import com.google.inject.Singleton;
import java.net.URI;
import java.net.URISyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Singleton
class GetOAuthToken implements RestReadView<AccountResource> {
private static final String BEARER_TYPE = "bearer";
+ private static final Logger log = LoggerFactory.getLogger(GetOAuthToken.class);
private final Provider<CurrentUser> self;
private final OAuthTokenCache tokenCache;
@@ -69,9 +72,15 @@
}
private static String getHostName(String canonicalWebUrl) {
+ if (canonicalWebUrl == null) {
+ log.error("No canonicalWebUrl defined in gerrit.config, OAuth may not work properly");
+ return null;
+ }
+
try {
return new URI(canonicalWebUrl).getHost();
} catch (URISyntaxException e) {
+ log.error("Invalid canonicalWebUrl '" + canonicalWebUrl + "'", e);
return null;
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 775ce6d..44060be 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -51,7 +51,7 @@
get(ACCOUNT_KIND, "active").to(GetActive.class);
put(ACCOUNT_KIND, "active").to(PutActive.class);
delete(ACCOUNT_KIND, "active").to(DeleteActive.class);
- child(ACCOUNT_KIND, "emails").to(Emails.class);
+ child(ACCOUNT_KIND, "emails").to(EmailsCollection.class);
get(EMAIL_KIND).to(GetEmail.class);
put(EMAIL_KIND).to(PutEmail.class);
delete(EMAIL_KIND).to(DeleteEmail.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
index 5d551bc..b5e4cba 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -17,6 +17,7 @@
import com.google.gerrit.extensions.client.AccountFieldName;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
+import java.io.IOException;
import java.util.Set;
public interface Realm {
@@ -43,5 +44,5 @@
* where there is an {@link EmailExpander} configured that knows how to convert the accountName
* into an email address, and then locate the user by that email address.
*/
- Account.Id lookup(String accountName);
+ Account.Id lookup(String accountName) throws IOException;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
index 995aaa5..a6c844b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
@@ -32,6 +32,7 @@
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.QueryChanges;
import com.google.gwtorm.server.OrmDuplicateKeyException;
import com.google.gwtorm.server.OrmException;
@@ -67,7 +68,7 @@
@Override
public AccountResource.StarredChange parse(AccountResource parent, IdString id)
- throws ResourceNotFoundException, OrmException {
+ throws ResourceNotFoundException, OrmException, PermissionBackendException {
IdentifiedUser user = parent.getUser();
ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
if (starredChangesUtil
@@ -104,7 +105,7 @@
return createProvider.get().setChange(changes.parse(TopLevelResource.INSTANCE, id));
} catch (ResourceNotFoundException e) {
throw new UnprocessableEntityException(String.format("change %s not found", id.get()));
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("cannot resolve change", e);
throw new UnprocessableEntityException("internal server error");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Stars.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Stars.java
index 52c6cdf..860f396 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Stars.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Stars.java
@@ -33,6 +33,7 @@
import com.google.gerrit.server.account.AccountResource.Star;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.QueryChanges;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -65,7 +66,7 @@
@Override
public Star parse(AccountResource parent, IdString id)
- throws ResourceNotFoundException, OrmException {
+ throws ResourceNotFoundException, OrmException, PermissionBackendException {
IdentifiedUser user = parent.getUser();
ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
Set<String> labels = starredChangesUtil.getLabels(user.getAccountId(), change.getId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index 3e8a146..d400999 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -53,7 +53,9 @@
import com.google.gerrit.server.notedb.ChangeUpdate;
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
+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.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
@@ -475,8 +477,12 @@
accountId -> {
try {
IdentifiedUser user = userFactory.create(accountId);
- return changeControlFactory.controlFor(notes, user).isVisible(db);
- } catch (OrmException e) {
+ return permissionBackend
+ .user(user)
+ .change(notes)
+ .database(db)
+ .test(ChangePermission.READ);
+ } catch (PermissionBackendException e) {
log.warn(
String.format(
"Failed to check if account %d can see change %d",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index eeb1ab3..d967ab8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -26,6 +26,9 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ChangeFinder;
import com.google.gerrit.server.CurrentUser;
+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.ChangeControl;
import com.google.gerrit.server.query.change.QueryChanges;
import com.google.gwtorm.server.OrmException;
@@ -44,6 +47,7 @@
private final ChangeFinder changeFinder;
private final CreateChange createChange;
private final ChangeResource.Factory changeResourceFactory;
+ private final PermissionBackend permissionBackend;
@Inject
ChangesCollection(
@@ -53,7 +57,8 @@
DynamicMap<RestView<ChangeResource>> views,
ChangeFinder changeFinder,
CreateChange createChange,
- ChangeResource.Factory changeResourceFactory) {
+ ChangeResource.Factory changeResourceFactory,
+ PermissionBackend permissionBackend) {
this.db = db;
this.user = user;
this.queryFactory = queryFactory;
@@ -61,6 +66,7 @@
this.changeFinder = changeFinder;
this.createChange = createChange;
this.changeResourceFactory = changeResourceFactory;
+ this.permissionBackend = permissionBackend;
}
@Override
@@ -75,7 +81,7 @@
@Override
public ChangeResource parse(TopLevelResource root, IdString id)
- throws ResourceNotFoundException, OrmException {
+ throws ResourceNotFoundException, OrmException, PermissionBackendException {
List<ChangeControl> ctls = changeFinder.find(id.encoded(), user.get());
if (ctls.isEmpty()) {
throw new ResourceNotFoundException(id);
@@ -84,13 +90,14 @@
}
ChangeControl ctl = ctls.get(0);
- if (!ctl.isVisible(db.get())) {
+ if (!canRead(ctl)) {
throw new ResourceNotFoundException(id);
}
return changeResourceFactory.create(ctl);
}
- public ChangeResource parse(Change.Id id) throws ResourceNotFoundException, OrmException {
+ public ChangeResource parse(Change.Id id)
+ throws ResourceNotFoundException, OrmException, PermissionBackendException {
List<ChangeControl> ctls = changeFinder.find(id, user.get());
if (ctls.isEmpty()) {
throw new ResourceNotFoundException(toIdString(id));
@@ -99,7 +106,7 @@
}
ChangeControl ctl = ctls.get(0);
- if (!ctl.isVisible(db.get())) {
+ if (!canRead(ctl)) {
throw new ResourceNotFoundException(toIdString(id));
}
return changeResourceFactory.create(ctl);
@@ -118,4 +125,12 @@
public CreateChange post(TopLevelResource parent) throws RestApiException {
return createChange;
}
+
+ private boolean canRead(ChangeControl ctl) throws PermissionBackendException {
+ return permissionBackend
+ .user(user)
+ .change(ctl.getNotes())
+ .database(db)
+ .test(ChangePermission.READ);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
index 92ad46d..2a0a412 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateChange.java
@@ -52,13 +52,16 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
+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.permissions.RefPermission;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.server.project.InvalidChangeOperationException;
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.ProjectsCollection;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
@@ -100,6 +103,7 @@
private final PermissionBackend permissionBackend;
private final Provider<CurrentUser> user;
private final ProjectsCollection projectsCollection;
+ private final CommitsCollection commits;
private final ChangeInserter.Factory changeInserterFactory;
private final ChangeJson.Factory jsonFactory;
private final ChangeFinder changeFinder;
@@ -121,6 +125,7 @@
PermissionBackend permissionBackend,
Provider<CurrentUser> user,
ProjectsCollection projectsCollection,
+ CommitsCollection commits,
ChangeInserter.Factory changeInserterFactory,
ChangeJson.Factory json,
ChangeFinder changeFinder,
@@ -139,6 +144,7 @@
this.permissionBackend = permissionBackend;
this.user = user;
this.projectsCollection = projectsCollection;
+ this.commits = commits;
this.changeInserterFactory = changeInserterFactory;
this.jsonFactory = json;
this.changeFinder = changeFinder;
@@ -195,7 +201,11 @@
throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
}
ChangeControl ctl = Iterables.getOnlyElement(ctls);
- if (!ctl.isVisible(db.get())) {
+ if (!permissionBackend
+ .user(user)
+ .change(ctl.getNotes())
+ .database(db)
+ .test(ChangePermission.READ)) {
throw new UnprocessableEntityException("Base change not found: " + input.baseChange);
}
PatchSet ps = psUtil.current(db.get(), ctl.getNotes());
@@ -311,12 +321,13 @@
throw new BadRequestException("merge.source must be non-empty");
}
+ ProjectState state = projectControl.getProjectState();
RevCommit sourceCommit = MergeUtil.resolveCommit(repo, rw, merge.source);
- if (!projectControl.canReadCommit(db.get(), repo, sourceCommit)) {
+ if (!commits.canRead(state, repo, sourceCommit)) {
throw new BadRequestException("do not have read permission for: " + merge.source);
}
- MergeUtil mergeUtil = mergeUtilFactory.create(projectControl.getProjectState());
+ MergeUtil mergeUtil = mergeUtilFactory.create(state);
// default merge strategy from project settings
String mergeStrategy =
MoreObjects.firstNonNull(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
index b53bdd9..0b7d495 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateMergePatchSet.java
@@ -43,8 +43,10 @@
import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
@@ -69,9 +71,9 @@
@Singleton
public class CreateMergePatchSet
extends RetryingRestModifyView<ChangeResource, MergePatchSetInput, Response<ChangeInfo>> {
-
private final Provider<ReviewDb> db;
private final GitRepositoryManager gitManager;
+ private final CommitsCollection commits;
private final TimeZone serverTimeZone;
private final Provider<CurrentUser> user;
private final ChangeJson.Factory jsonFactory;
@@ -83,6 +85,7 @@
CreateMergePatchSet(
Provider<ReviewDb> db,
GitRepositoryManager gitManager,
+ CommitsCollection commits,
@GerritPersonIdent PersonIdent myIdent,
Provider<CurrentUser> user,
ChangeJson.Factory json,
@@ -93,6 +96,7 @@
super(retryHelper);
this.db = db;
this.gitManager = gitManager;
+ this.commits = commits;
this.serverTimeZone = myIdent.getTimeZone();
this.user = user;
this.jsonFactory = json;
@@ -116,6 +120,7 @@
ChangeControl ctl = rsrc.getControl();
PatchSet ps = psUtil.current(db.get(), ctl.getNotes());
ProjectControl projectControl = ctl.getProjectControl();
+ ProjectState state = projectControl.getProjectState();
Change change = ctl.getChange();
Project.NameKey project = change.getProject();
Branch.NameKey dest = change.getDest();
@@ -125,7 +130,7 @@
RevWalk rw = new RevWalk(reader)) {
RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, merge.source);
- if (!projectControl.canReadCommit(db.get(), git, sourceCommit)) {
+ if (!commits.canRead(state, git, sourceCommit)) {
throw new ResourceNotFoundException(
"cannot find source commit: " + merge.source + " to merge.");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java
index 8a6a1ab..cb77fd1 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetRevisionActions.java
@@ -24,6 +24,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.ChangeSet;
import com.google.gerrit.server.git.MergeSuperSet;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.OrmRuntimeException;
@@ -74,7 +75,7 @@
changeResourceFactory.create(cd.changeControl()).prepareETag(h, user);
}
h.putBoolean(cs.furtherHiddenChanges());
- } catch (IOException | OrmException e) {
+ } catch (IOException | OrmException | PermissionBackendException e) {
throw new OrmRuntimeException(e);
}
return h.hash().toString();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index d2fb95a..cafd73b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -28,6 +28,7 @@
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -65,6 +66,7 @@
private final GitRepositoryManager gitManager;
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ProjectCache projectCache;
private final MergeUtil.Factory mergeUtilFactory;
private final ChangeData.Factory changeDataFactory;
@@ -77,6 +79,7 @@
GitRepositoryManager gitManager,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeData.Factory changeDataFactory,
@@ -86,6 +89,7 @@
this.gitManager = gitManager;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.changeDataFactory = changeDataFactory;
@@ -148,7 +152,9 @@
private SubmitType getSubmitType(ChangeData cd, PatchSet patchSet) throws OrmException {
SubmitTypeRecord rec =
- new SubmitRuleEvaluator(accountCache, accounts, cd).setPatchSet(patchSet).getSubmitType();
+ new SubmitRuleEvaluator(accountCache, accounts, emails, cd)
+ .setPatchSet(patchSet)
+ .getSubmitType();
if (rec.status != SubmitTypeRecord.Status.OK) {
throw new OrmException("Submit type rule failed: " + rec);
}
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 a7711b6..f7e4469 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
@@ -56,6 +56,7 @@
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration;
+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.permissions.RefPermission;
@@ -338,8 +339,12 @@
ReviewerState state,
NotifyHandling notify,
ListMultimap<RecipientType, Account.Id> accountsToNotify)
- throws OrmException {
- if (!rsrc.getControl().forUser(anonymousProvider.get()).isVisible(dbProvider.get())) {
+ throws PermissionBackendException {
+ if (!permissionBackend
+ .user(anonymousProvider)
+ .change(rsrc.getNotes())
+ .database(dbProvider)
+ .test(ChangePermission.READ)) {
return fail(
reviewer, MessageFormat.format(ChangeMessages.get().reviewerCantSeeChange, reviewer));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
index 9ef445d..5724941 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PreviewSubmit.java
@@ -34,6 +34,7 @@
import com.google.gerrit.server.git.MergeOp;
import com.google.gerrit.server.git.MergeOpRepoManager;
import com.google.gerrit.server.git.MergeOpRepoManager.OpenRepo;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.update.UpdateException;
@@ -83,7 +84,8 @@
@Override
public BinaryResult apply(RevisionResource rsrc)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
+ throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ PermissionBackendException {
if (Strings.isNullOrEmpty(format)) {
throw new BadRequestException("format is not specified");
}
@@ -111,7 +113,8 @@
}
private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
+ throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ PermissionBackendException {
ReviewDb db = dbProvider.get();
ChangeControl control = rsrc.getControl();
IdentifiedUser caller = control.getUser().asIdentifiedUser();
@@ -131,7 +134,8 @@
| UpdateException
| IOException
| ConfigInvalidException
- | RuntimeException e) {
+ | RuntimeException
+ | PermissionBackendException e) {
op.close();
throw e;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
index fa7fdfd..5551320 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ReviewerJson.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.permissions.LabelPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -52,6 +53,7 @@
private final ChangeData.Factory changeDataFactory;
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ApprovalsUtil approvalsUtil;
private final AccountLoader.Factory accountLoaderFactory;
@@ -62,6 +64,7 @@
ChangeData.Factory changeDataFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ApprovalsUtil approvalsUtil,
AccountLoader.Factory accountLoaderFactory) {
this.db = db;
@@ -69,6 +72,7 @@
this.changeDataFactory = changeDataFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.approvalsUtil = approvalsUtil;
this.accountLoaderFactory = accountLoaderFactory;
}
@@ -137,7 +141,7 @@
PatchSet ps = cd.currentPatchSet();
if (ps != null) {
for (SubmitRecord rec :
- new SubmitRuleEvaluator(accountCache, accounts, cd)
+ new SubmitRuleEvaluator(accountCache, accounts, emails, cd)
.setFastEvalLabels(true)
.setAllowDraft(true)
.evaluate()) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index 3dd467a..81f830c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -217,7 +217,8 @@
}
public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
- throws OrmException, RestApiException, IOException, UpdateException, ConfigInvalidException {
+ throws OrmException, RestApiException, IOException, UpdateException, ConfigInvalidException,
+ PermissionBackendException {
Change change = rsrc.getChange();
if (!change.getStatus().isOpen()) {
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
@@ -340,7 +341,7 @@
ChangeSet cs;
try {
cs = mergeSuperSet.get().completeChangeSet(db, cd.change(), resource.getControl().getUser());
- } catch (OrmException | IOException e) {
+ } catch (OrmException | IOException | PermissionBackendException e) {
throw new OrmRuntimeException(
"Could not determine complete set of changes to be submitted", e);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/SubmittedTogether.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/SubmittedTogether.java
index 568b50a..ea53dc3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/SubmittedTogether.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/SubmittedTogether.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.change.WalkSorter.PatchSetData;
import com.google.gerrit.server.git.ChangeSet;
import com.google.gerrit.server.git.MergeSuperSet;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gwtorm.server.OrmException;
@@ -107,7 +108,7 @@
@Override
public Object apply(ChangeResource resource)
throws AuthException, BadRequestException, ResourceConflictException, IOException,
- OrmException {
+ OrmException, PermissionBackendException {
SubmittedTogetherInfo info = applyInfo(resource);
if (options.isEmpty()) {
return info.changes;
@@ -116,7 +117,7 @@
}
public SubmittedTogetherInfo applyInfo(ChangeResource resource)
- throws AuthException, IOException, OrmException {
+ throws AuthException, IOException, OrmException, PermissionBackendException {
Change c = resource.getChange();
try {
List<ChangeData> cds;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
index 81027c0..9e93465 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitRule.java
@@ -27,6 +27,7 @@
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountLoader;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
@@ -44,6 +45,7 @@
private final AccountCache accountCache;
private final AccountLoader.Factory accountInfoFactory;
private final Accounts accounts;
+ private final Emails emails;
@Option(name = "--filters", usage = "impact of filters in parent projects")
private Filters filters = Filters.RUN;
@@ -54,14 +56,16 @@
ChangeData.Factory changeDataFactory,
RulesCache rules,
AccountCache accountCache,
+ AccountLoader.Factory infoFactory,
Accounts accounts,
- AccountLoader.Factory infoFactory) {
+ Emails emails) {
this.db = db;
this.changeDataFactory = changeDataFactory;
this.rules = rules;
this.accountCache = accountCache;
this.accountInfoFactory = infoFactory;
this.accounts = accounts;
+ this.emails = emails;
}
@Override
@@ -76,7 +80,7 @@
input.filters = MoreObjects.firstNonNull(input.filters, filters);
SubmitRuleEvaluator evaluator =
new SubmitRuleEvaluator(
- accountCache, accounts, changeDataFactory.create(db.get(), rsrc.getControl()));
+ accountCache, accounts, emails, changeDataFactory.create(db.get(), rsrc.getControl()));
List<SubmitRecord> records =
evaluator
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
index ee93923..5fb37e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/TestSubmitType.java
@@ -27,6 +27,7 @@
import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
@@ -38,6 +39,7 @@
private final Provider<ReviewDb> db;
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ChangeData.Factory changeDataFactory;
private final RulesCache rules;
@@ -49,11 +51,13 @@
Provider<ReviewDb> db,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ChangeData.Factory changeDataFactory,
RulesCache rules) {
this.db = db;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.changeDataFactory = changeDataFactory;
this.rules = rules;
}
@@ -70,7 +74,7 @@
input.filters = MoreObjects.firstNonNull(input.filters, filters);
SubmitRuleEvaluator evaluator =
new SubmitRuleEvaluator(
- accountCache, accounts, changeDataFactory.create(db.get(), rsrc.getControl()));
+ accountCache, accounts, emails, changeDataFactory.create(db.get(), rsrc.getControl()));
SubmitTypeRecord rec =
evaluator
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 31989e3..43afcc8 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
@@ -79,7 +79,6 @@
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PluginUser;
import com.google.gerrit.server.Sequences;
-import com.google.gerrit.server.account.AccountByEmailCacheImpl;
import com.google.gerrit.server.account.AccountCacheImpl;
import com.google.gerrit.server.account.AccountControl;
import com.google.gerrit.server.account.AccountManager;
@@ -120,10 +119,10 @@
import com.google.gerrit.server.git.MergedByPushOp;
import com.google.gerrit.server.git.NotesBranchUtil;
import com.google.gerrit.server.git.ReceivePackInitializer;
-import com.google.gerrit.server.git.ReplaceOp;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.git.TransferConfig;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.ReceiveCommitsModule;
import com.google.gerrit.server.git.strategy.SubmitStrategy;
import com.google.gerrit.server.git.validators.CommitValidationListener;
import com.google.gerrit.server.git.validators.MergeValidationListener;
@@ -215,7 +214,6 @@
bind(BlameCache.class).to(BlameCacheImpl.class);
bind(Sequences.class);
install(authModule);
- install(AccountByEmailCacheImpl.module());
install(AccountCacheImpl.module());
install(BatchUpdate.module());
install(ChangeKindCacheImpl.module());
@@ -239,6 +237,7 @@
install(new GroupModule());
install(new NoteDbModule(cfg));
install(new PrologModule());
+ install(new ReceiveCommitsModule());
install(new SshAddressesModule());
install(ThreadLocalRequestContext.module());
@@ -400,7 +399,6 @@
factory(MergeValidators.Factory.class);
factory(ProjectConfigValidator.Factory.class);
factory(NotesBranchUtil.Factory.class);
- factory(ReplaceOp.Factory.class);
factory(MergedByPushOp.Factory.class);
factory(GitModules.Factory.class);
factory(VersionedAuthorizedKeys.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
index ce04f26..d31c26d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -35,8 +35,8 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.GerritPersonIdent;
-import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.change.ChangeKindCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.data.AccountAttribute;
@@ -83,9 +83,9 @@
private static final Logger log = LoggerFactory.getLogger(EventFactory.class);
private final AccountCache accountCache;
+ private final Emails emails;
private final Provider<String> urlProvider;
private final PatchListCache patchListCache;
- private final AccountByEmailCache byEmailCache;
private final PersonIdent myIdent;
private final ChangeData.Factory changeDataFactory;
private final ApprovalsUtil approvalsUtil;
@@ -96,8 +96,8 @@
@Inject
EventFactory(
AccountCache accountCache,
+ Emails emails,
@CanonicalWebUrl @Nullable Provider<String> urlProvider,
- AccountByEmailCache byEmailCache,
PatchListCache patchListCache,
@GerritPersonIdent PersonIdent myIdent,
ChangeData.Factory changeDataFactory,
@@ -106,9 +106,9 @@
Provider<InternalChangeQuery> queryProvider,
SchemaFactory<ReviewDb> schema) {
this.accountCache = accountCache;
+ this.emails = emails;
this.urlProvider = urlProvider;
this.patchListCache = patchListCache;
- this.byEmailCache = byEmailCache;
this.myIdent = myIdent;
this.changeDataFactory = changeDataFactory;
this.approvalsUtil = approvalsUtil;
@@ -497,7 +497,7 @@
}
}
p.kind = changeKindCache.getChangeKind(db, change, patchSet);
- } catch (IOException e) {
+ } catch (IOException | OrmException e) {
log.error("Cannot load patch set data for " + patchSet.getId(), e);
} catch (PatchListNotAvailableException e) {
log.error(String.format("Cannot get size information for %s.", pId), e);
@@ -507,7 +507,7 @@
// TODO: The same method exists in PatchSetInfoFactory, find a common place
// for it
- private UserIdentity toUserIdentity(PersonIdent who) {
+ private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
UserIdentity u = new UserIdentity();
u.setName(who.getName());
u.setEmail(who.getEmailAddress());
@@ -517,7 +517,7 @@
// If only one account has access to this email address, select it
// as the identity of the user.
//
- Set<Account.Id> a = byEmailCache.get(u.getEmail());
+ Set<Account.Id> a = emails.getAccountFor(u.getEmail());
if (a.size() == 1) {
u.setAccount(a.iterator().next());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/StreamEventsApiListener.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/StreamEventsApiListener.java
index b79f137..318c251 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/events/StreamEventsApiListener.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/StreamEventsApiListener.java
@@ -53,6 +53,7 @@
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gwtorm.server.OrmException;
@@ -261,7 +262,7 @@
event.oldAssignee = accountAttributeSupplier(ev.getOldAssignee());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -277,7 +278,7 @@
event.oldTopic = ev.getOldTopic();
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -295,7 +296,7 @@
event.uploader = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -315,7 +316,7 @@
approvalsAttributeSupplier(change, ev.getNewApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -333,7 +334,7 @@
event.reviewer = accountAttributeSupplier(reviewer);
dispatcher.get().postEvent(change, event);
}
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -360,7 +361,7 @@
event.removed = hashtagArray(ev.getRemovedHashtags());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -399,7 +400,7 @@
event.uploader = accountAttributeSupplier(ev.getWho());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -419,7 +420,7 @@
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -437,7 +438,7 @@
event.reason = ev.getReason();
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -455,7 +456,7 @@
event.newRev = ev.getNewRevisionId();
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -473,7 +474,7 @@
event.reason = ev.getReason();
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
@@ -493,7 +494,7 @@
event.approvals = approvalsAttributeSupplier(change, ev.getApprovals(), ev.getOldApprovals());
dispatcher.get().postEvent(change, event);
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Failed to dispatch event", e);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java
deleted file mode 100644
index f615321..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/AsyncReceiveCommits.java
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright (C) 2012 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.reviewdb.client.Project;
-import com.google.gerrit.server.config.ConfigUtil;
-import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.project.ProjectControl;
-import com.google.gerrit.server.util.RequestScopePropagator;
-import com.google.inject.Inject;
-import com.google.inject.PrivateModule;
-import com.google.inject.Provides;
-import com.google.inject.Singleton;
-import com.google.inject.assistedinject.Assisted;
-import com.google.inject.assistedinject.FactoryModuleBuilder;
-import com.google.inject.name.Named;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.transport.PreReceiveHook;
-import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.transport.ReceiveCommand.Result;
-import org.eclipse.jgit.transport.ReceivePack;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Hook that delegates to {@link ReceiveCommits} in a worker thread. */
-public class AsyncReceiveCommits implements PreReceiveHook {
- private static final Logger log = LoggerFactory.getLogger(AsyncReceiveCommits.class);
-
- private static final String TIMEOUT_NAME = "ReceiveCommitsOverallTimeout";
-
- public interface Factory {
- AsyncReceiveCommits create(ProjectControl projectControl, Repository repository);
- }
-
- public static class Module extends PrivateModule {
- @Override
- public void configure() {
- install(new FactoryModuleBuilder().build(AsyncReceiveCommits.Factory.class));
- expose(AsyncReceiveCommits.Factory.class);
- // Don't expose the binding for ReceiveCommits.Factory. All callers should
- // be using AsyncReceiveCommits.Factory instead.
- install(new FactoryModuleBuilder().build(ReceiveCommits.Factory.class));
- }
-
- @Provides
- @Singleton
- @Named(TIMEOUT_NAME)
- long getTimeoutMillis(@GerritServerConfig Config cfg) {
- return ConfigUtil.getTimeUnit(
- cfg, "receive", null, "timeout", TimeUnit.MINUTES.toMillis(4), TimeUnit.MILLISECONDS);
- }
- }
-
- private class Worker implements ProjectRunnable {
- private final Collection<ReceiveCommand> commands;
-
- private Worker(Collection<ReceiveCommand> commands) {
- this.commands = commands;
- }
-
- @Override
- public void run() {
- rc.processCommands(commands, progress);
- }
-
- @Override
- public Project.NameKey getProjectNameKey() {
- return rc.getProject().getNameKey();
- }
-
- @Override
- public String getRemoteName() {
- return null;
- }
-
- @Override
- public boolean hasCustomizedPrint() {
- return true;
- }
-
- @Override
- public String toString() {
- return "receive-commits";
- }
- }
-
- private class MessageSenderOutputStream extends OutputStream {
- @Override
- public void write(int b) {
- rc.getMessageSender().sendBytes(new byte[] {(byte) b});
- }
-
- @Override
- public void write(byte[] what, int off, int len) {
- rc.getMessageSender().sendBytes(what, off, len);
- }
-
- @Override
- public void write(byte[] what) {
- rc.getMessageSender().sendBytes(what);
- }
-
- @Override
- public void flush() {
- rc.getMessageSender().flush();
- }
- }
-
- private final ReceiveCommits rc;
- private final ExecutorService executor;
- private final RequestScopePropagator scopePropagator;
- private final MultiProgressMonitor progress;
- private final long timeoutMillis;
-
- @Inject
- AsyncReceiveCommits(
- ReceiveCommits.Factory factory,
- @ReceiveCommitsExecutor ExecutorService executor,
- RequestScopePropagator scopePropagator,
- @Named(TIMEOUT_NAME) long timeoutMillis,
- @Assisted ProjectControl projectControl,
- @Assisted Repository repo) {
- this.executor = executor;
- this.scopePropagator = scopePropagator;
- rc = factory.create(projectControl, repo);
- rc.getReceivePack().setPreReceiveHook(this);
-
- progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes");
- this.timeoutMillis = timeoutMillis;
- }
-
- @Override
- public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
- try {
- progress.waitFor(
- executor.submit(scopePropagator.wrap(new Worker(commands))),
- timeoutMillis,
- TimeUnit.MILLISECONDS);
- } catch (ExecutionException e) {
- log.warn(
- String.format(
- "Error in ReceiveCommits while processing changes for project %s",
- rc.getProject().getName()),
- e);
- rc.addError("internal error while processing changes");
- // ReceiveCommits has tried its best to catch errors, so anything at this
- // point is very bad.
- for (ReceiveCommand c : commands) {
- if (c.getResult() == Result.NOT_ATTEMPTED) {
- c.setResult(Result.REJECTED_OTHER_REASON, "internal error");
- }
- }
- } finally {
- rc.sendMessages();
- }
- }
-
- public ReceiveCommits getReceiveCommits() {
- return rc;
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
index 36805d6..15a9d74 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
@@ -25,7 +25,6 @@
factory(RenameGroupOp.Factory.class);
factory(MetaDataUpdate.InternalFactory.class);
bind(MetaDataUpdate.Server.class);
- bind(ReceiveConfig.class);
DynamicSet.bind(binder(), PostUploadHook.class).to(UploadPackMetricsHook.class);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
index 960c72a..4a7c7e9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GroupCollector.java
@@ -48,7 +48,7 @@
import org.slf4j.LoggerFactory;
/**
- * Helper for assigning groups to commits during {@link ReceiveCommits}.
+ * Helper for assigning groups to commits during {@code ReceiveCommits}.
*
* <p>For each commit encountered along a walk between the branch tip and the tip of the push, the
* group of a commit is defined as follows:
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index ea28fa9..5572db2 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -63,6 +63,7 @@
import com.google.gerrit.server.git.strategy.SubmitStrategyListener;
import com.google.gerrit.server.git.validators.MergeValidationException;
import com.google.gerrit.server.git.validators.MergeValidators;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.SubmitRuleOptions;
@@ -423,6 +424,8 @@
* @param submitInput parameters regarding the merge
* @throws OrmException an error occurred reading or writing the database.
* @throws RestApiException if an error occurred.
+ * @throws PermissionBackendException if permissions can't be checked
+ * @throws IOException an error occurred reading from NoteDb.
*/
public void merge(
ReviewDb db,
@@ -431,7 +434,8 @@
boolean checkSubmitRules,
SubmitInput submitInput,
boolean dryrun)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
+ throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ PermissionBackendException {
this.submitInput = submitInput;
this.accountsToNotify = notifyUtil.resolveAccounts(submitInput.notifyDetails);
this.dryrun = dryrun;
@@ -487,7 +491,12 @@
}
return null;
},
- retryTracker);
+ RetryHelper.options()
+ .listener(retryTracker)
+ // Up to the entire submit operation is retried, including possibly many projects.
+ // Multiply the timeout by the number of projects we're actually attempting to submit.
+ .timeout(retryHelper.getDefaultTimeout().multipliedBy(cs.projects().size()))
+ .build());
if (projects > 1) {
topicMetrics.topicSubmissionsCompleted.increment();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
index ad205f8..6d20864 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOpRepoManager.java
@@ -173,7 +173,7 @@
openRepos = new HashMap<>();
}
- void setContext(ReviewDb db, Timestamp ts, IdentifiedUser caller, RequestId submissionId) {
+ public void setContext(ReviewDb db, Timestamp ts, IdentifiedUser caller, RequestId submissionId) {
this.db = db;
this.ts = ts;
this.caller = caller;
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 e880543..978ef3e 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
@@ -33,10 +33,14 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
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.NoSuchChangeException;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
@@ -98,9 +102,11 @@
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ChangeData.Factory changeDataFactory;
private final Provider<InternalChangeQuery> queryProvider;
private final Provider<MergeOpRepoManager> repoManagerProvider;
+ private final PermissionBackend permissionBackend;
private final Config cfg;
private final Map<QueryKey, List<ChangeData>> queryCache;
private final Map<Branch.NameKey, Optional<RevCommit>> heads;
@@ -113,15 +119,19 @@
@GerritServerConfig Config cfg,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ChangeData.Factory changeDataFactory,
Provider<InternalChangeQuery> queryProvider,
- Provider<MergeOpRepoManager> repoManagerProvider) {
+ Provider<MergeOpRepoManager> repoManagerProvider,
+ PermissionBackend permissionBackend) {
this.cfg = cfg;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.changeDataFactory = changeDataFactory;
this.queryProvider = queryProvider;
this.repoManagerProvider = repoManagerProvider;
+ this.permissionBackend = permissionBackend;
queryCache = new HashMap<>();
heads = new HashMap<>();
}
@@ -134,11 +144,13 @@
}
public ChangeSet completeChangeSet(ReviewDb db, Change change, CurrentUser user)
- throws IOException, OrmException {
+ throws IOException, OrmException, PermissionBackendException {
try {
ChangeData cd = changeDataFactory.create(db, change.getProject(), change.getId());
cd.changeControl(user);
- ChangeSet cs = new ChangeSet(cd, cd.changeControl().isVisible(db, cd));
+ ChangeSet cs =
+ new ChangeSet(
+ cd, permissionBackend.user(user).change(cd).database(db).test(ChangePermission.READ));
if (Submit.wholeTopicEnabled(cfg)) {
return completeChangeSetIncludingTopics(db, cs, user);
}
@@ -169,7 +181,9 @@
SubmitTypeRecord str =
ps == cd.currentPatchSet()
? cd.submitTypeRecord()
- : new SubmitRuleEvaluator(accountCache, accounts, cd).setPatchSet(ps).getSubmitType();
+ : new SubmitRuleEvaluator(accountCache, accounts, emails, cd)
+ .setPatchSet(ps)
+ .getSubmitType();
if (!str.isOk()) {
logErrorAndThrow("Failed to get submit type for " + cd.getId() + ": " + str.errorMessage);
}
@@ -212,7 +226,7 @@
}
private ChangeSet completeChangeSetWithoutTopic(ReviewDb db, ChangeSet changes, CurrentUser user)
- throws IOException, OrmException {
+ throws IOException, OrmException, PermissionBackendException {
Collection<ChangeData> visibleChanges = new ArrayList<>();
Collection<ChangeData> nonVisibleChanges = new ArrayList<>();
@@ -231,7 +245,7 @@
+ " at ChangeData creation time");
boolean visible = changes.ids().contains(cd.getId());
- if (visible && !cd.changeControl().isVisible(db, cd)) {
+ 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.
@@ -357,7 +371,7 @@
CurrentUser user,
Set<String> topicsSeen,
Set<String> visibleTopicsSeen)
- throws OrmException {
+ throws OrmException, PermissionBackendException {
List<ChangeData> visibleChanges = new ArrayList<>();
List<ChangeData> nonVisibleChanges = new ArrayList<>();
@@ -370,7 +384,7 @@
for (ChangeData topicCd : query().byTopicOpen(topic)) {
try {
topicCd.changeControl(user);
- if (topicCd.changeControl().isVisible(db, topicCd)) {
+ if (canRead(db, user, topicCd)) {
visibleChanges.add(topicCd);
} else {
nonVisibleChanges.add(topicCd);
@@ -402,7 +416,8 @@
}
private ChangeSet completeChangeSetIncludingTopics(
- ReviewDb db, ChangeSet changes, CurrentUser user) throws IOException, OrmException {
+ ReviewDb db, ChangeSet changes, CurrentUser user)
+ throws IOException, OrmException, PermissionBackendException {
Set<String> topicsSeen = new HashSet<>();
Set<String> visibleTopicsSeen = new HashSet<>();
int oldSeen;
@@ -443,4 +458,9 @@
logError(msg);
throw new OrmException(msg);
}
+
+ private boolean canRead(ReviewDb db, CurrentUser user, ChangeData cd)
+ throws PermissionBackendException {
+ return permissionBackend.user(user).change(cd).database(db).test(ChangePermission.READ);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index 8026cae..1cabc53 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -689,9 +689,9 @@
}
if (topics.size() == 1) {
- return String.format("Merge changes from topic '%s'", Iterables.getFirst(topics, null));
+ return String.format("Merge changes from topic \"%s\"", Iterables.getFirst(topics, null));
} else if (topics.size() > 1) {
- return String.format("Merge changes from topics '%s'", Joiner.on("', '").join(topics));
+ return String.format("Merge changes from topics \"%s\"", Joiner.on("\", \"").join(topics));
} else {
return String.format(
"Merge changes %s%s",
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergedByPushOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergedByPushOp.java
index 59017e7..a44d21c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergedByPushOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergedByPushOp.java
@@ -198,7 +198,7 @@
change, patchSet, ctx.getAccount(), patchSet.getRevision().get(), ctx.getWhen());
}
- private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException {
+ private PatchSetInfo getPatchSetInfo(ChangeContext ctx) throws IOException, OrmException {
RevWalk rw = ctx.getRevWalk();
RevCommit commit =
rw.parseCommit(ObjectId.fromString(checkNotNull(patchSet).getRevision().get()));
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
index 93aa361..2afea25 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VisibleRefFilter.java
@@ -30,8 +30,10 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.ChangePermission;
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.server.query.change.ChangeData;
@@ -262,12 +264,16 @@
try {
Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
for (ChangeData cd : changeCache.getChangeData(db.get(), project.getNameKey())) {
- if (projectCtl.controlForIndexedChange(cd.change()).isVisible(db.get(), cd)) {
+ if (permissionBackend
+ .user(user)
+ .indexedChange(cd, changeNotesFactory.createFromIndexedChange(cd.change()))
+ .database(db)
+ .test(ChangePermission.READ)) {
visibleChanges.put(cd.getId(), cd.change().getDest());
}
}
return visibleChanges;
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error(
"Cannot load changes for project "
+ project.getName()
@@ -282,12 +288,12 @@
try {
Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
for (ChangeNotes cn : changeNotesFactory.scan(git, db.get(), project)) {
- if (projectCtl.controlFor(cn).isVisible(db.get())) {
+ if (permissionBackend.user(user).change(cn).database(db).test(ChangePermission.READ)) {
visibleChanges.put(cn.getChangeId(), cn.getChange().getDest());
}
}
return visibleChanges;
- } catch (IOException | OrmException e) {
+ } catch (IOException | OrmException | PermissionBackendException e) {
log.error(
"Cannot load changes for project " + project + ", assuming no changes are visible", e);
return Collections.emptyMap();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java
new file mode 100644
index 0000000..4afaacd
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AllRefsWatcher.java
@@ -0,0 +1,49 @@
+// 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.receive;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.transport.AdvertiseRefsHook;
+import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+import org.eclipse.jgit.transport.UploadPack;
+
+/**
+ * Hook that scans all refs and holds onto the results reference.
+ *
+ * <p>This allows a caller who has an {@code AllRefsWatcher} instance to get the full map of refs in
+ * the repo, even if refs are filtered by a later hook or filter.
+ */
+class AllRefsWatcher implements AdvertiseRefsHook {
+ private Map<String, Ref> allRefs;
+
+ @Override
+ public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
+ allRefs = HookUtil.ensureAllRefsAdvertised(rp);
+ }
+
+ @Override
+ public void advertiseRefs(UploadPack uploadPack) {
+ throw new UnsupportedOperationException();
+ }
+
+ Map<String, Ref> getAllRefs() {
+ checkState(allRefs != null, "getAllRefs() only valid after refs were advertised");
+ return allRefs;
+ }
+}
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
new file mode 100644
index 0000000..71d8f63
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/AsyncReceiveCommits.java
@@ -0,0 +1,277 @@
+// Copyright (C) 2012 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.git.receive;
+
+import com.google.common.collect.SetMultimap;
+import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.data.Capable;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.config.ConfigUtil;
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.HackPushNegotiateHook;
+import com.google.gerrit.server.git.MultiProgressMonitor;
+import com.google.gerrit.server.git.ProjectRunnable;
+import com.google.gerrit.server.git.TransferConfig;
+import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.notedb.ReviewerStateInternal;
+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.ProjectControl;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.query.change.InternalChangeQuery;
+import com.google.gerrit.server.util.MagicBranch;
+import com.google.gerrit.server.util.RequestScopePropagator;
+import com.google.inject.Inject;
+import com.google.inject.PrivateModule;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Named;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.AdvertiseRefsHook;
+import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
+import org.eclipse.jgit.transport.PreReceiveHook;
+import org.eclipse.jgit.transport.ReceiveCommand;
+import org.eclipse.jgit.transport.ReceiveCommand.Result;
+import org.eclipse.jgit.transport.ReceivePack;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Hook that delegates to {@link ReceiveCommits} in a worker thread. */
+public class AsyncReceiveCommits implements PreReceiveHook {
+ private static final Logger log = LoggerFactory.getLogger(AsyncReceiveCommits.class);
+
+ private static final String TIMEOUT_NAME = "ReceiveCommitsOverallTimeout";
+
+ public interface Factory {
+ AsyncReceiveCommits create(
+ ProjectControl projectControl,
+ Repository repository,
+ @Nullable MessageSender messageSender,
+ SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers);
+ }
+
+ public static class Module extends PrivateModule {
+ @Override
+ public void configure() {
+ install(new FactoryModuleBuilder().build(AsyncReceiveCommits.Factory.class));
+ expose(AsyncReceiveCommits.Factory.class);
+ // Don't expose the binding for ReceiveCommits.Factory. All callers should
+ // be using AsyncReceiveCommits.Factory instead.
+ install(new FactoryModuleBuilder().build(ReceiveCommits.Factory.class));
+ }
+
+ @Provides
+ @Singleton
+ @Named(TIMEOUT_NAME)
+ long getTimeoutMillis(@GerritServerConfig Config cfg) {
+ return ConfigUtil.getTimeUnit(
+ cfg, "receive", null, "timeout", TimeUnit.MINUTES.toMillis(4), TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private class Worker implements ProjectRunnable {
+ final MultiProgressMonitor progress;
+
+ private final Collection<ReceiveCommand> commands;
+ private final ReceiveCommits rc;
+
+ private Worker(Collection<ReceiveCommand> commands) {
+ this.commands = commands;
+ rc = factory.create(projectControl, rp, allRefsWatcher, extraReviewers);
+ rc.init();
+ rc.setMessageSender(messageSender);
+ progress = new MultiProgressMonitor(new MessageSenderOutputStream(), "Processing changes");
+ }
+
+ @Override
+ public void run() {
+ rc.processCommands(commands, progress);
+ }
+
+ @Override
+ public Project.NameKey getProjectNameKey() {
+ return rc.getProject().getNameKey();
+ }
+
+ @Override
+ public String getRemoteName() {
+ return null;
+ }
+
+ @Override
+ public boolean hasCustomizedPrint() {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "receive-commits";
+ }
+
+ void sendMessages() {
+ rc.sendMessages();
+ }
+
+ private class MessageSenderOutputStream extends OutputStream {
+ @Override
+ public void write(int b) {
+ rc.getMessageSender().sendBytes(new byte[] {(byte) b});
+ }
+
+ @Override
+ public void write(byte[] what, int off, int len) {
+ rc.getMessageSender().sendBytes(what, off, len);
+ }
+
+ @Override
+ public void write(byte[] what) {
+ rc.getMessageSender().sendBytes(what);
+ }
+
+ @Override
+ public void flush() {
+ rc.getMessageSender().flush();
+ }
+ }
+ }
+
+ private final ReceiveCommits.Factory factory;
+ private final ReceivePack rp;
+ private final ExecutorService executor;
+ private final RequestScopePropagator scopePropagator;
+ private final ReceiveConfig receiveConfig;
+ private final long timeoutMillis;
+ private final ProjectControl projectControl;
+ private final Repository repo;
+ private final MessageSender messageSender;
+ private final SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers;
+ private final AllRefsWatcher allRefsWatcher;
+
+ @Inject
+ AsyncReceiveCommits(
+ ReceiveCommits.Factory factory,
+ PermissionBackend permissionBackend,
+ VisibleRefFilter.Factory refFilterFactory,
+ Provider<InternalChangeQuery> queryProvider,
+ @ReceiveCommitsExecutor ExecutorService executor,
+ RequestScopePropagator scopePropagator,
+ ReceiveConfig receiveConfig,
+ TransferConfig transferConfig,
+ Provider<LazyPostReceiveHookChain> lazyPostReceive,
+ @Named(TIMEOUT_NAME) long timeoutMillis,
+ @Assisted ProjectControl projectControl,
+ @Assisted Repository repo,
+ @Assisted @Nullable MessageSender messageSender,
+ @Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers)
+ throws PermissionBackendException {
+ this.factory = factory;
+ this.executor = executor;
+ this.scopePropagator = scopePropagator;
+ this.receiveConfig = receiveConfig;
+ this.timeoutMillis = timeoutMillis;
+ this.projectControl = projectControl;
+ this.repo = repo;
+ this.messageSender = messageSender;
+ this.extraReviewers = extraReviewers;
+
+ IdentifiedUser user = projectControl.getUser().asIdentifiedUser();
+ ProjectState state = projectControl.getProjectState();
+ Project.NameKey projectName = projectControl.getProject().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.setRefFilter(new ReceiveRefFilter());
+ rp.setAllowPushOptions(true);
+ rp.setPreReceiveHook(this);
+ rp.setPostReceiveHook(lazyPostReceive.get());
+
+ // If the user lacks READ permission, some references may be filtered and hidden from view.
+ // Check objects mentioned inside the incoming pack file are reachable from visible refs.
+ try {
+ permissionBackend.user(user).project(projectName).check(ProjectPermission.READ);
+ } catch (AuthException e) {
+ rp.setCheckReferencedObjectsAreReachable(receiveConfig.checkReferencedObjectsAreReachable);
+ }
+
+ List<AdvertiseRefsHook> advHooks = new ArrayList<>(4);
+ allRefsWatcher = new AllRefsWatcher();
+ advHooks.add(allRefsWatcher);
+ advHooks.add(refFilterFactory.create(state, repo).setShowMetadata(false));
+ advHooks.add(new ReceiveCommitsAdvertiseRefsHook(queryProvider, projectName));
+ advHooks.add(new HackPushNegotiateHook());
+ rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
+ }
+
+ /** Determine if the user can upload commits. */
+ public Capable canUpload() {
+ Capable result = projectControl.canPushToAtLeastOneRef();
+ if (result != Capable.OK) {
+ return result;
+ }
+ if (receiveConfig.checkMagicRefs) {
+ result = MagicBranch.checkMagicBranchRefs(repo, projectControl.getProject());
+ }
+ return result;
+ }
+
+ @Override
+ public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
+ Worker w = new Worker(commands);
+ try {
+ w.progress.waitFor(
+ executor.submit(scopePropagator.wrap(w)), timeoutMillis, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException e) {
+ log.warn(
+ String.format(
+ "Error in ReceiveCommits while processing changes for project %s",
+ projectControl.getProject().getName()),
+ e);
+ rp.sendError("internal error while processing changes");
+ // ReceiveCommits has tried its best to catch errors, so anything at this
+ // point is very bad.
+ for (ReceiveCommand c : commands) {
+ if (c.getResult() == Result.NOT_ATTEMPTED) {
+ c.setResult(Result.REJECTED_OTHER_REASON, "internal error");
+ }
+ }
+ } finally {
+ w.sendMessages();
+ }
+ }
+
+ public ReceivePack getReceivePack() {
+ return rp;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeProgressOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ChangeProgressOp.java
similarity index 95%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeProgressOp.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ChangeProgressOp.java
index 1a39a76..6774465 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ChangeProgressOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ChangeProgressOp.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/HookUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/HookUtil.java
new file mode 100644
index 0000000..90b220a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/HookUtil.java
@@ -0,0 +1,54 @@
+// 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.receive;
+
+import java.io.IOException;
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
+import org.eclipse.jgit.transport.BaseReceivePack;
+import org.eclipse.jgit.transport.ServiceMayNotContinueException;
+
+/** Static utilities for writing {@link ReceiveCommits}-related hooks. */
+class HookUtil {
+ /**
+ * Scan and advertise all refs in the repo if refs have not already been advertised; otherwise,
+ * just return the advertised map.
+ *
+ * @param rp receive-pack handler.
+ * @return map of refs that were advertised.
+ * @throws ServiceMayNotContinueException if a problem occurred.
+ */
+ static Map<String, Ref> ensureAllRefsAdvertised(BaseReceivePack rp)
+ throws ServiceMayNotContinueException {
+ Map<String, Ref> refs = rp.getAdvertisedRefs();
+ if (refs != null) {
+ return refs;
+ }
+ try {
+ refs = rp.getRepository().getRefDatabase().getRefs(RefDatabase.ALL);
+ } catch (ServiceMayNotContinueException e) {
+ throw e;
+ } catch (IOException e) {
+ ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
+ ex.initCause(e);
+ throw ex;
+ }
+ rp.setAdvertisedRefs(refs, rp.getAdvertisedObjects());
+ return refs;
+ }
+
+ private HookUtil() {}
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LazyPostReceiveHookChain.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
similarity index 96%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/LazyPostReceiveHookChain.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
index bc12e02..7adb21b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LazyPostReceiveHookChain.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/LazyPostReceiveHookChain.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.inject.Inject;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/MessageSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/MessageSender.java
new file mode 100644
index 0000000..a338021
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/MessageSender.java
@@ -0,0 +1,31 @@
+// 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.receive;
+
+/**
+ * Interface used by {@link ReceiveCommits} for send messages over the wire during {@code
+ * receive-pack}.
+ */
+public interface MessageSender {
+ void sendMessage(String what);
+
+ void sendError(String what);
+
+ void sendBytes(byte[] what);
+
+ void sendBytes(byte[] what, int off, int len);
+
+ void flush();
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
similarity index 92%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 42fb1b3..7f909d4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -21,12 +21,15 @@
import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
import static com.google.gerrit.server.change.HashtagsUtil.cleanupHashtag;
import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
+import static com.google.gerrit.server.git.receive.ReceiveConstants.COMMAND_REJECTION_MESSAGE_FOOTER;
+import static com.google.gerrit.server.git.receive.ReceiveConstants.ONLY_OWNER_CAN_MODIFY_WIP;
+import static com.google.gerrit.server.git.receive.ReceiveConstants.SAME_CHANGE_ID_IN_MULTIPLE_CHANGES;
+import static com.google.gerrit.server.git.validators.CommitValidators.NEW_PATCHSET_PATTERN;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_MISSING_OBJECT;
@@ -40,17 +43,18 @@
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.SortedSetMultimap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
-import com.google.gerrit.common.data.Capable;
import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.LabelTypes;
import com.google.gerrit.common.data.Permission;
@@ -94,7 +98,19 @@
import com.google.gerrit.server.edit.ChangeEdit;
import com.google.gerrit.server.edit.ChangeEditUtil;
import com.google.gerrit.server.events.CommitReceivedEvent;
+import com.google.gerrit.server.git.BanCommit;
+import com.google.gerrit.server.git.GroupCollector;
+import com.google.gerrit.server.git.MergeOp;
+import com.google.gerrit.server.git.MergeOpRepoManager;
+import com.google.gerrit.server.git.MergedByPushOp;
+import com.google.gerrit.server.git.MultiProgressMonitor;
import com.google.gerrit.server.git.MultiProgressMonitor.Task;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.git.ReceivePackInitializer;
+import com.google.gerrit.server.git.SubmoduleException;
+import com.google.gerrit.server.git.SubmoduleOp;
+import com.google.gerrit.server.git.TagCache;
+import com.google.gerrit.server.git.ValidationError;
import com.google.gerrit.server.git.validators.CommitValidationException;
import com.google.gerrit.server.git.validators.CommitValidationMessage;
import com.google.gerrit.server.git.validators.CommitValidators;
@@ -105,12 +121,12 @@
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.NotesMigration;
+import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.permissions.ChangePermission;
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.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
@@ -153,7 +169,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
@@ -171,40 +186,19 @@
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.revwalk.filter.RevFilter;
-import org.eclipse.jgit.transport.AdvertiseRefsHook;
-import org.eclipse.jgit.transport.AdvertiseRefsHookChain;
-import org.eclipse.jgit.transport.BaseReceivePack;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Result;
import org.eclipse.jgit.transport.ReceivePack;
-import org.eclipse.jgit.transport.RefFilter;
-import org.eclipse.jgit.transport.ServiceMayNotContinueException;
-import org.eclipse.jgit.transport.UploadPack;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Receives change upload using the Git receive-pack protocol. */
-public class ReceiveCommits {
+class ReceiveCommits {
private static final Logger log = LoggerFactory.getLogger(ReceiveCommits.class);
private static final String BYPASS_REVIEW = "bypass-review";
- public static final Pattern NEW_PATCHSET =
- Pattern.compile("^" + REFS_CHANGES + "(?:[0-9][0-9]/)?([1-9][0-9]*)(?:/new)?$");
-
- private static final String COMMAND_REJECTION_MESSAGE_FOOTER =
- "Please read the documentation and contact an administrator\n"
- + "if you feel the configuration is incorrect";
-
- private static final String SAME_CHANGE_ID_IN_MULTIPLE_CHANGES =
- "same Change-Id in multiple changes.\n"
- + "Squash the commits with the same Change-Id or "
- + "ensure Change-Ids are unique for each commit";
-
- public static final String ONLY_OWNER_CAN_MODIFY_WIP =
- "only change owner can modify Work-in-Progress";
-
private enum Error {
CONFIG_UPDATE(
"You are not allowed to perform this operation.\n"
@@ -226,25 +220,17 @@
this.value = value;
}
- public String get() {
+ String get() {
return value;
}
}
interface Factory {
- ReceiveCommits create(ProjectControl projectControl, Repository repository);
- }
-
- public interface MessageSender {
- void sendMessage(String what);
-
- void sendError(String what);
-
- void sendBytes(byte[] what);
-
- void sendBytes(byte[] what, int off, int len);
-
- void flush();
+ ReceiveCommits create(
+ ProjectControl projectControl,
+ ReceivePack receivePack,
+ AllRefsWatcher allRefsWatcher,
+ SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers);
}
private class ReceivePackMessageSender implements MessageSender {
@@ -296,64 +282,66 @@
}
};
- private Set<Account.Id> reviewersFromCommandLine = Sets.newLinkedHashSet();
- private Set<Account.Id> ccFromCommandLine = Sets.newLinkedHashSet();
+ // ReceiveCommits has a lot of fields, sorry. Here and in the constructor they are split up
+ // somewhat, and kept sorted lexicographically within sections, except where later assignments
+ // depend on previous ones.
- private final IdentifiedUser user;
- private final ReviewDb db;
- private final Sequences seq;
- private final Provider<InternalChangeQuery> queryProvider;
- private final ChangeNotes.Factory notesFactory;
- private final AccountsUpdate.Server accountsUpdate;
+ // Injected fields.
private final AccountResolver accountResolver;
- private final PermissionBackend permissionBackend;
- private final PermissionBackend.ForProject permissions;
- private final CmdLineParser.Factory optionParserFactory;
- private final PatchSetInfoFactory patchSetInfoFactory;
- private final PatchSetUtil psUtil;
- private final ProjectCache projectCache;
- private final String canonicalWebUrl;
- private final CommitValidators.Factory commitValidatorsFactory;
- private final RefOperationValidators.Factory refValidatorsFactory;
- private final TagCache tagCache;
- private final ChangeInserter.Factory changeInserterFactory;
- private final RequestScopePropagator requestScopePropagator;
- private final SshInfo sshInfo;
+ private final AccountsUpdate.Server accountsUpdate;
private final AllProjectsName allProjectsName;
- private final ReceiveConfig receiveConfig;
- private final DynamicSet<ReceivePackInitializer> initializers;
private final BatchUpdate.Factory batchUpdateFactory;
- private final SetHashtagsOp.Factory hashtagsFactory;
- private final ReplaceOp.Factory replaceOpFactory;
- private final MergedByPushOp.Factory mergedByPushOpFactory;
-
- private final ProjectControl projectControl;
- private final Project project;
- private final LabelTypes labelTypes;
- private final Repository repo;
- private final ReceivePack rp;
- private final NoteMap rejectCommits;
- private final RequestId receiveId;
- private MagicBranchInput magicBranch;
- private boolean newChangeForAllNotInTarget;
- private final ListMultimap<String, String> pushOptions = LinkedListMultimap.create();
-
- private List<CreateRequest> newChanges = Collections.emptyList();
- private final Map<Change.Id, ReplaceRequest> replaceByChange = new LinkedHashMap<>();
- private final List<UpdateGroupsRequest> updateGroups = new ArrayList<>();
- private final Set<ObjectId> validCommits = new HashSet<>();
-
- private ListMultimap<Change.Id, Ref> refsByChange;
- private ListMultimap<ObjectId, Ref> refsById;
- private Map<String, Ref> allRefs;
-
- private final SubmoduleOp.Factory subOpFactory;
- private final Provider<MergeOp> mergeOpProvider;
- private final Provider<MergeOpRepoManager> ormProvider;
- private final DynamicMap<ProjectConfigEntry> pluginConfigEntries;
- private final NotesMigration notesMigration;
private final ChangeEditUtil editUtil;
private final ChangeIndexer indexer;
+ private final ChangeInserter.Factory changeInserterFactory;
+ private final ChangeNotes.Factory notesFactory;
+ private final CmdLineParser.Factory optionParserFactory;
+ 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;
+ private final PatchSetUtil psUtil;
+ private final PermissionBackend permissionBackend;
+ private final ProjectCache projectCache;
+ private final Provider<InternalChangeQuery> queryProvider;
+ private final Provider<MergeOp> mergeOpProvider;
+ private final Provider<MergeOpRepoManager> ormProvider;
+ private final ReceiveConfig receiveConfig;
+ private final RefOperationValidators.Factory refValidatorsFactory;
+ private final ReplaceOp.Factory replaceOpFactory;
+ private final RequestScopePropagator requestScopePropagator;
+ private final ReviewDb db;
+ private final Sequences seq;
+ private final SetHashtagsOp.Factory hashtagsFactory;
+ private final SshInfo sshInfo;
+ private final String canonicalWebUrl;
+ private final SubmoduleOp.Factory subOpFactory;
+ private final TagCache tagCache;
+
+ // Assisted injected fields.
+ private final AllRefsWatcher allRefsWatcher;
+ private final ImmutableSetMultimap<ReviewerStateInternal, Account.Id> extraReviewers;
+ private final ProjectControl projectControl;
+ private final ReceivePack rp;
+
+ // Immutable fields derived from constructor arguments.
+ private final LabelTypes labelTypes;
+ private final NoteMap rejectCommits;
+ private final PermissionBackend.ForProject permissions;
+ private final Project project;
+ private final Repository repo;
+ private final RequestId receiveId;
+
+ // Collections populated during processing.
+ private final List<UpdateGroupsRequest> updateGroups;
+ private final List<ValidationMessage> messages;
+ private final ListMultimap<Error, String> errors;
+ private final ListMultimap<String, String> pushOptions;
+ private final Map<Change.Id, ReplaceRequest> replaceByChange;
+ private final Set<ObjectId> validCommits;
/**
* Actual commands to be executed, as opposed to the mix of actual and magic commands that were
@@ -362,10 +350,18 @@
* <p>Excludes commands executed implicitly as part of other {@link BatchUpdateOp}s, such as
* creating patch set refs.
*/
- private final List<ReceiveCommand> actualCommands = new ArrayList<>();
+ private final List<ReceiveCommand> actualCommands;
- private final List<ValidationMessage> messages = new ArrayList<>();
- private ListMultimap<Error, String> errors = LinkedListMultimap.create();
+ // Collections lazily populated during processing.
+ private List<CreateRequest> newChanges;
+ private ListMultimap<Change.Id, Ref> refsByChange;
+ private ListMultimap<ObjectId, Ref> refsById;
+
+ // Other settings populated during processing.
+ private MagicBranchInput magicBranch;
+ private boolean newChangeForAllNotInTarget;
+
+ // Handles for outputting back over the wire to the end user.
private Task newProgress;
private Task replaceProgress;
private Task closeProgress;
@@ -374,179 +370,120 @@
@Inject
ReceiveCommits(
- ReviewDb db,
- Sequences seq,
- Provider<InternalChangeQuery> queryProvider,
- ChangeNotes.Factory notesFactory,
- AccountsUpdate.Server accountsUpdate,
- AccountResolver accountResolver,
- PermissionBackend permissionBackend,
- CmdLineParser.Factory optionParserFactory,
- PatchSetInfoFactory patchSetInfoFactory,
- PatchSetUtil psUtil,
- ProjectCache projectCache,
- TagCache tagCache,
- VisibleRefFilter.Factory refFilterFactory,
- ChangeInserter.Factory changeInserterFactory,
- CommitValidators.Factory commitValidatorsFactory,
- RefOperationValidators.Factory refValidatorsFactory,
@CanonicalWebUrl String canonicalWebUrl,
- RequestScopePropagator requestScopePropagator,
- SshInfo sshInfo,
+ AccountResolver accountResolver,
+ AccountsUpdate.Server accountsUpdate,
AllProjectsName allProjectsName,
- ReceiveConfig receiveConfig,
- TransferConfig transferConfig,
- DynamicSet<ReceivePackInitializer> initializers,
- Provider<LazyPostReceiveHookChain> lazyPostReceive,
- @Assisted ProjectControl projectControl,
- @Assisted Repository repo,
- SubmoduleOp.Factory subOpFactory,
- Provider<MergeOp> mergeOpProvider,
- Provider<MergeOpRepoManager> ormProvider,
- DynamicMap<ProjectConfigEntry> pluginConfigEntries,
- NotesMigration notesMigration,
+ BatchUpdate.Factory batchUpdateFactory,
ChangeEditUtil editUtil,
ChangeIndexer indexer,
- BatchUpdate.Factory batchUpdateFactory,
- SetHashtagsOp.Factory hashtagsFactory,
+ ChangeInserter.Factory changeInserterFactory,
+ ChangeNotes.Factory notesFactory,
+ CmdLineParser.Factory optionParserFactory,
+ CommitValidators.Factory commitValidatorsFactory,
+ DynamicMap<ProjectConfigEntry> pluginConfigEntries,
+ DynamicSet<ReceivePackInitializer> initializers,
+ MergedByPushOp.Factory mergedByPushOpFactory,
+ NotesMigration notesMigration,
+ PatchSetInfoFactory patchSetInfoFactory,
+ PatchSetUtil psUtil,
+ PermissionBackend permissionBackend,
+ ProjectCache projectCache,
+ Provider<InternalChangeQuery> queryProvider,
+ Provider<MergeOp> mergeOpProvider,
+ Provider<MergeOpRepoManager> ormProvider,
+ ReceiveConfig receiveConfig,
+ RefOperationValidators.Factory refValidatorsFactory,
ReplaceOp.Factory replaceOpFactory,
- MergedByPushOp.Factory mergedByPushOpFactory)
- throws IOException, PermissionBackendException {
- this.user = projectControl.getUser().asIdentifiedUser();
- this.db = db;
- this.seq = seq;
- this.queryProvider = queryProvider;
- this.notesFactory = notesFactory;
- this.accountsUpdate = accountsUpdate;
+ RequestScopePropagator requestScopePropagator,
+ ReviewDb db,
+ Sequences seq,
+ SetHashtagsOp.Factory hashtagsFactory,
+ SshInfo sshInfo,
+ SubmoduleOp.Factory subOpFactory,
+ TagCache tagCache,
+ @Assisted ProjectControl projectControl,
+ @Assisted ReceivePack rp,
+ @Assisted AllRefsWatcher allRefsWatcher,
+ @Assisted SetMultimap<ReviewerStateInternal, Account.Id> extraReviewers)
+ throws IOException {
+ // Injected fields.
this.accountResolver = accountResolver;
- this.permissionBackend = permissionBackend;
- this.optionParserFactory = optionParserFactory;
- this.patchSetInfoFactory = patchSetInfoFactory;
- this.psUtil = psUtil;
- this.projectCache = projectCache;
+ this.accountsUpdate = accountsUpdate;
+ this.allProjectsName = allProjectsName;
+ this.batchUpdateFactory = batchUpdateFactory;
this.canonicalWebUrl = canonicalWebUrl;
- this.tagCache = tagCache;
this.changeInserterFactory = changeInserterFactory;
this.commitValidatorsFactory = commitValidatorsFactory;
- this.refValidatorsFactory = refValidatorsFactory;
- this.requestScopePropagator = requestScopePropagator;
- this.sshInfo = sshInfo;
- this.allProjectsName = allProjectsName;
- this.receiveConfig = receiveConfig;
- this.initializers = initializers;
- this.batchUpdateFactory = batchUpdateFactory;
- this.hashtagsFactory = hashtagsFactory;
- this.replaceOpFactory = replaceOpFactory;
- this.mergedByPushOpFactory = mergedByPushOpFactory;
-
- this.projectControl = projectControl;
- this.labelTypes = projectControl.getLabelTypes();
- this.project = projectControl.getProject();
- this.repo = repo;
- this.rp = new ReceivePack(repo);
- this.rejectCommits = BanCommit.loadRejectCommitsMap(repo, rp.getRevWalk());
- this.receiveId = RequestId.forProject(project.getNameKey());
-
- this.subOpFactory = subOpFactory;
- this.mergeOpProvider = mergeOpProvider;
- this.ormProvider = ormProvider;
- this.pluginConfigEntries = pluginConfigEntries;
- this.notesMigration = notesMigration;
-
+ this.db = db;
this.editUtil = editUtil;
+ this.hashtagsFactory = hashtagsFactory;
this.indexer = indexer;
+ this.initializers = initializers;
+ this.mergeOpProvider = mergeOpProvider;
+ this.mergedByPushOpFactory = mergedByPushOpFactory;
+ this.notesFactory = notesFactory;
+ this.notesMigration = notesMigration;
+ this.optionParserFactory = optionParserFactory;
+ this.ormProvider = ormProvider;
+ this.patchSetInfoFactory = patchSetInfoFactory;
+ this.permissionBackend = permissionBackend;
+ this.pluginConfigEntries = pluginConfigEntries;
+ this.projectCache = projectCache;
+ this.psUtil = psUtil;
+ this.queryProvider = queryProvider;
+ this.receiveConfig = receiveConfig;
+ this.refValidatorsFactory = refValidatorsFactory;
+ this.replaceOpFactory = replaceOpFactory;
+ this.requestScopePropagator = requestScopePropagator;
+ this.seq = seq;
+ this.sshInfo = sshInfo;
+ this.subOpFactory = subOpFactory;
+ this.tagCache = tagCache;
- this.messageSender = new ReceivePackMessageSender();
+ // Assisted injected fields.
+ this.allRefsWatcher = allRefsWatcher;
+ this.extraReviewers = ImmutableSetMultimap.copyOf(extraReviewers);
+ this.projectControl = projectControl;
+ this.rp = rp;
- ProjectState ps = projectControl.getProjectState();
-
- this.newChangeForAllNotInTarget = ps.isCreateNewChangeForAllNotInTarget();
- rp.setAllowCreates(true);
- rp.setAllowDeletes(true);
- rp.setAllowNonFastForwards(true);
- rp.setRefLogIdent(user.newRefLogIdent());
- rp.setTimeout(transferConfig.getTimeout());
- rp.setMaxObjectSizeLimit(
- transferConfig.getEffectiveMaxObjectSizeLimit(projectControl.getProjectState()));
- rp.setCheckReceivedObjects(ps.getConfig().getCheckReceivedObjects());
- rp.setRefFilter(
- new RefFilter() {
- @Override
- public Map<String, Ref> filter(Map<String, Ref> refs) {
- Map<String, Ref> filteredRefs = Maps.newHashMapWithExpectedSize(refs.size());
- for (Map.Entry<String, Ref> e : refs.entrySet()) {
- String name = e.getKey();
- if (!name.startsWith(REFS_CHANGES)
- && !name.startsWith(RefNames.REFS_CACHE_AUTOMERGE)) {
- filteredRefs.put(name, e.getValue());
- }
- }
- return filteredRefs;
- }
- });
-
+ // Immutable fields derived from constructor arguments.
+ repo = rp.getRepository();
+ user = projectControl.getUser().asIdentifiedUser();
+ project = projectControl.getProject();
+ labelTypes = projectControl.getLabelTypes();
permissions = permissionBackend.user(user).project(project.getNameKey());
- // If the user lacks READ permission, some references may be filtered and hidden from view.
- // Check objects mentioned inside the incoming pack file are reachable from visible refs.
- try {
- permissionBackend.user(user).project(project.getNameKey()).check(ProjectPermission.READ);
- } catch (AuthException e) {
- rp.setCheckReferencedObjectsAreReachable(receiveConfig.checkReferencedObjectsAreReachable);
- }
+ receiveId = RequestId.forProject(project.getNameKey());
+ rejectCommits = BanCommit.loadRejectCommitsMap(rp.getRepository(), rp.getRevWalk());
- rp.setAdvertiseRefsHook(
- refFilterFactory.create(projectControl.getProjectState(), repo).setShowMetadata(false));
- List<AdvertiseRefsHook> advHooks = new ArrayList<>(3);
- advHooks.add(
- new AdvertiseRefsHook() {
- @Override
- public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
- allRefs = rp.getAdvertisedRefs();
- if (allRefs == null) {
- try {
- allRefs = rp.getRepository().getRefDatabase().getRefs(ALL);
- } catch (ServiceMayNotContinueException e) {
- throw e;
- } catch (IOException e) {
- ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
- ex.initCause(e);
- throw ex;
- }
- }
- rp.setAdvertisedRefs(allRefs, rp.getAdvertisedObjects());
- }
+ // Collections populated during processing.
+ actualCommands = new ArrayList<>();
+ errors = LinkedListMultimap.create();
+ messages = new ArrayList<>();
+ pushOptions = LinkedListMultimap.create();
+ replaceByChange = new LinkedHashMap<>();
+ updateGroups = new ArrayList<>();
+ validCommits = new HashSet<>();
- @Override
- public void advertiseRefs(UploadPack uploadPack) {}
- });
- advHooks.add(rp.getAdvertiseRefsHook());
- advHooks.add(
- new ReceiveCommitsAdvertiseRefsHook(
- queryProvider, projectControl.getProject().getNameKey()));
- advHooks.add(new HackPushNegotiateHook());
- rp.setAdvertiseRefsHook(AdvertiseRefsHookChain.newChain(advHooks));
- rp.setPostReceiveHook(lazyPostReceive.get());
- rp.setAllowPushOptions(true);
+ // Collections lazily populated during processing.
+ newChanges = Collections.emptyList();
+
+ // Other settings populated during processing.
+ newChangeForAllNotInTarget =
+ projectControl.getProjectState().isCreateNewChangeForAllNotInTarget();
+
+ // Handles for outputting back over the wire to the end user.
+ messageSender = new ReceivePackMessageSender();
}
- public void init() {
+ void init() {
for (ReceivePackInitializer i : initializers) {
i.init(projectControl.getProject().getNameKey(), rp);
}
}
- /** Add reviewers for new (or updated) changes. */
- public void addReviewers(Collection<Account.Id> who) {
- reviewersFromCommandLine.addAll(who);
- }
-
- /** Add reviewers for new (or updated) changes. */
- public void addExtraCC(Collection<Account.Id> who) {
- ccFromCommandLine.addAll(who);
- }
-
/** Set a message sender for this operation. */
- public void setMessageSender(MessageSender ms) {
+ void setMessageSender(MessageSender ms) {
messageSender = ms != null ? ms : new ReceivePackMessageSender();
}
@@ -561,23 +498,6 @@
return project;
}
- /** @return the ReceivePack instance to speak the native Git protocol. */
- public ReceivePack getReceivePack() {
- return rp;
- }
-
- /** Determine if the user can upload commits. */
- public Capable canUpload() {
- Capable result = projectControl.canPushToAtLeastOneRef();
- if (result != Capable.OK) {
- return result;
- }
- if (receiveConfig.checkMagicRefs) {
- result = MagicBranch.checkMagicBranchRefs(repo, project);
- }
- return result;
- }
-
private void addMessage(String message) {
messages.add(new CommitValidationMessage(message, false));
}
@@ -819,7 +739,8 @@
| OrmException
| UpdateException
| IOException
- | ConfigInvalidException e) {
+ | ConfigInvalidException
+ | PermissionBackendException e) {
logError("Error submitting changes to " + project.getName(), e);
reject(magicBranchCmd, "error during submit");
}
@@ -897,7 +818,7 @@
};
}
- Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
+ Matcher m = NEW_PATCHSET_PATTERN.matcher(cmd.getRefName());
if (m.matches()) {
// The referenced change must exist and must still be open.
//
@@ -1437,7 +1358,7 @@
return ref.substring(0, split);
}
- public NotifyHandling getNotify() {
+ NotifyHandling getNotify() {
if (notify != null) {
return notify;
}
@@ -1447,7 +1368,7 @@
return NotifyHandling.ALL;
}
- public NotifyHandling getNotify(ChangeNotes notes) {
+ NotifyHandling getNotify(ChangeNotes notes) {
if (notify != null) {
return notify;
}
@@ -1467,7 +1388,7 @@
* @return an unmodifiable view of pushOptions.
*/
@Nullable
- public ListMultimap<String, String> getPushOptions() {
+ ListMultimap<String, String> getPushOptions() {
return ImmutableListMultimap.copyOf(pushOptions);
}
@@ -1480,8 +1401,8 @@
logDebug("Found magic branch {}", cmd.getRefName());
magicBranch = new MagicBranchInput(user, cmd, labelTypes, notesMigration);
- magicBranch.reviewer.addAll(reviewersFromCommandLine);
- magicBranch.cc.addAll(ccFromCommandLine);
+ magicBranch.reviewer.addAll(extraReviewers.get(ReviewerStateInternal.REVIEWER));
+ magicBranch.cc.addAll(extraReviewers.get(ReviewerStateInternal.CC));
String ref;
CmdLineParser clp = optionParserFactory.create(magicBranch);
@@ -1700,7 +1621,7 @@
}
private RevCommit readBranchTip(ReceiveCommand cmd, Branch.NameKey branch) throws IOException {
- Ref r = allRefs.get(branch.get());
+ Ref r = allRefs().get(branch.get());
if (r == null) {
reject(cmd, branch.get() + " not found");
return null;
@@ -2073,7 +1994,7 @@
for (RevCommit c : magicBranch.baseCommit) {
rp.getRevWalk().markUninteresting(c);
}
- Ref targetRef = allRefs.get(magicBranch.ctl.getRefName());
+ Ref targetRef = allRefs().get(magicBranch.ctl.getRefName());
if (targetRef != null) {
logDebug(
"Marking target ref {} ({}) uninteresting",
@@ -2085,7 +2006,7 @@
private void rejectImplicitMerges(Set<RevCommit> mergedParents) throws IOException {
if (!mergedParents.isEmpty()) {
- Ref targetRef = allRefs.get(magicBranch.ctl.getRefName());
+ Ref targetRef = allRefs().get(magicBranch.ctl.getRefName());
if (targetRef != null) {
RevWalk rw = rp.getRevWalk();
RevCommit tip = rw.parseCommit(targetRef.getObjectId());
@@ -2119,7 +2040,7 @@
private void markHeadsAsUninteresting(RevWalk rw, @Nullable String forRef) {
int i = 0;
- for (Ref ref : allRefs.values()) {
+ for (Ref ref : allRefs().values()) {
if ((ref.getName().startsWith(R_HEADS) || ref.getName().equals(forRef))
&& ref.getObjectId() != null) {
try {
@@ -2264,7 +2185,8 @@
}
private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
- throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
+ throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException,
+ PermissionBackendException {
Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
for (CreateRequest r : create) {
checkNotNull(r.change, "cannot submit new change %s; op may not have run", r.changeId);
@@ -2544,10 +2466,10 @@
RefNames.refsEdit(user.getAccountId(), notes.getChangeId(), psId));
}
- private void newPatchSet() throws IOException {
+ private void newPatchSet() throws IOException, OrmException {
RevCommit newCommit = rp.getRevWalk().parseCommit(newCommitId);
psId =
- ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs, notes.getChange().currentPatchSetId());
+ ChangeUtil.nextPatchSetIdFromAllRefsMap(allRefs(), notes.getChange().currentPatchSetId());
info = patchSetInfoFactory.get(rp.getRevWalk(), newCommit, psId);
cmd = new ReceiveCommand(ObjectId.zeroId(), newCommitId, psId.toRefName());
}
@@ -2679,10 +2601,10 @@
int estRefsPerChange = 4;
refsById = MultimapBuilder.hashKeys().arrayListValues().build();
refsByChange =
- MultimapBuilder.hashKeys(allRefs.size() / estRefsPerChange)
+ MultimapBuilder.hashKeys(allRefs().size() / estRefsPerChange)
.arrayListValues(estRefsPerChange)
.build();
- for (Ref ref : allRefs.values()) {
+ for (Ref ref : allRefs().values()) {
ObjectId obj = ref.getObjectId();
if (obj != null) {
PatchSet.Id psId = PatchSet.Id.fromRef(ref.getName());
@@ -2760,7 +2682,7 @@
PermissionBackend.ForRef perm = permissions.ref(ctl.getRefName());
if (!RefNames.REFS_CONFIG.equals(cmd.getRefName())
&& !(MagicBranch.isMagicBranch(cmd.getRefName())
- || NEW_PATCHSET.matcher(cmd.getRefName()).matches())
+ || NEW_PATCHSET_PATTERN.matcher(cmd.getRefName()).matches())
&& pushOptions.containsKey(BYPASS_REVIEW)) {
try {
perm.check(RefPermission.BYPASS_REVIEW);
@@ -2965,6 +2887,10 @@
return r;
}
+ private Map<String, Ref> allRefs() {
+ return allRefsWatcher.getAllRefs();
+ }
+
private void reject(@Nullable ReceiveCommand cmd, String why) {
if (cmd != null) {
cmd.setResult(REJECTED_OTHER_REASON, why);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
similarity index 89%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
index 2316782..3645392 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsAdvertiseRefsHook.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsAdvertiseRefsHook.java
@@ -12,9 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
-
-import static org.eclipse.jgit.lib.RefDatabase.ALL;
+package com.google.gerrit.server.git.receive;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
@@ -30,7 +28,6 @@
import com.google.gerrit.server.util.MagicBranch;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Provider;
-import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -72,19 +69,7 @@
@Override
public void advertiseRefs(BaseReceivePack rp) throws ServiceMayNotContinueException {
- Map<String, Ref> oldRefs = rp.getAdvertisedRefs();
- if (oldRefs == null) {
- try {
- oldRefs = rp.getRepository().getRefDatabase().getRefs(ALL);
- } catch (ServiceMayNotContinueException e) {
- throw e;
- } catch (IOException e) {
- ServiceMayNotContinueException ex = new ServiceMayNotContinueException();
- ex.initCause(e);
- throw ex;
- }
- }
- Result r = advertiseRefs(oldRefs);
+ Result r = advertiseRefs(HookUtil.ensureAllRefsAdvertised(rp));
rp.setAdvertisedRefs(r.allRefs(), r.additionalHaves());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutor.java
similarity index 95%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutor.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutor.java
index bc69a08..ee83a2c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutor.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutorModule.java
similarity index 87%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutorModule.java
index 90bdd52..4eb760d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommitsExecutorModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsExecutorModule.java
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.gerrit.server.git.SendEmailExecutor;
+import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.update.ChangeUpdateExecutor;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
@@ -28,7 +30,12 @@
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config;
-/** Module providing the {@link ReceiveCommitsExecutor}. */
+/**
+ * Module providing the {@link ReceiveCommitsExecutor}.
+ *
+ * <p>Unlike {@link ReceiveCommitsModule}, this module is intended to be installed only in top-level
+ * injectors like in {@code Daemon}, not in the {@code sysInjector}.
+ */
public class ReceiveCommitsExecutorModule extends AbstractModule {
@Override
protected void configure() {}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsModule.java
similarity index 61%
rename from gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsModule.java
index e73d82b..a973460 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountByEmailCache.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveCommitsModule.java
@@ -1,4 +1,4 @@
-// Copyright (C) 2009 The Android Open Source Project
+// 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.
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.account;
+package com.google.gerrit.server.git.receive;
-import com.google.gerrit.reviewdb.client.Account;
-import java.util.Set;
+import com.google.gerrit.extensions.config.FactoryModule;
-/** Translates an email address to a set of matching accounts. */
-public interface AccountByEmailCache {
- Set<Account.Id> get(String email);
-
- void evict(String email);
+public class ReceiveCommitsModule extends FactoryModule {
+ @Override
+ protected void configure() {
+ bind(ReceiveConfig.class);
+ factory(ReplaceOp.Factory.class);
+ }
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
similarity index 97%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
index a3f2a31..39b6d8b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConfig.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import static com.google.gerrit.common.data.GlobalCapability.BATCH_CHANGES_LIMIT;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConstants.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConstants.java
new file mode 100644
index 0000000..99742f3
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveConstants.java
@@ -0,0 +1,34 @@
+// 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.receive;
+
+import com.google.common.annotations.VisibleForTesting;
+
+public final class ReceiveConstants {
+ @VisibleForTesting
+ public static final String ONLY_OWNER_CAN_MODIFY_WIP =
+ "only change owner can modify Work-in-Progress";
+
+ static final String COMMAND_REJECTION_MESSAGE_FOOTER =
+ "Please read the documentation and contact an administrator\n"
+ + "if you feel the configuration is incorrect";
+
+ static final String SAME_CHANGE_ID_IN_MULTIPLE_CHANGES =
+ "same Change-Id in multiple changes.\n"
+ + "Squash the commits with the same Change-Id or "
+ + "ensure Change-Ids are unique for each commit";
+
+ private ReceiveConstants() {}
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java
new file mode 100644
index 0000000..16cba53
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReceiveRefFilter.java
@@ -0,0 +1,37 @@
+// 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.receive;
+
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_CACHE_AUTOMERGE;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.transport.RefFilter;
+
+class ReceiveRefFilter implements RefFilter {
+ @Override
+ public Map<String, Ref> filter(Map<String, Ref> refs) {
+ Map<String, Ref> filteredRefs = Maps.newHashMapWithExpectedSize(refs.size());
+ for (Map.Entry<String, Ref> e : refs.entrySet()) {
+ String name = e.getKey();
+ if (!name.startsWith(REFS_CHANGES) && !name.startsWith(REFS_CACHE_AUTOMERGE)) {
+ filteredRefs.put(name, e.getValue());
+ }
+ }
+ return filteredRefs;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
similarity index 98%
rename from gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
rename to gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 8f2d121..a558c6c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package com.google.gerrit.server.git;
+package com.google.gerrit.server.git.receive;
import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
@@ -45,7 +45,9 @@
import com.google.gerrit.server.change.EmailReviewComments;
import com.google.gerrit.server.extensions.events.CommentAdded;
import com.google.gerrit.server.extensions.events.RevisionCreated;
-import com.google.gerrit.server.git.ReceiveCommits.MagicBranchInput;
+import com.google.gerrit.server.git.MergedByPushOp;
+import com.google.gerrit.server.git.SendEmailExecutor;
+import com.google.gerrit.server.git.receive.ReceiveCommits.MagicBranchInput;
import com.google.gerrit.server.mail.MailUtil.MailRecipients;
import com.google.gerrit.server.mail.send.ReplacePatchSetSender;
import com.google.gerrit.server.notedb.ChangeNotes;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
index 49399ef..1b01c26 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/CherryPick.java
@@ -89,7 +89,8 @@
}
@Override
- protected void updateRepoImpl(RepoContext ctx) throws IntegrationException, IOException {
+ protected void updateRepoImpl(RepoContext ctx)
+ throws IntegrationException, IOException, OrmException {
// If there is only one parent, a cherry-pick can be done by taking the
// delta relative to that one parent and redoing that on the current merge
// tip.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitDryRun.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitDryRun.java
index 0d012e5..c76a59a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitDryRun.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/strategy/SubmitDryRun.java
@@ -14,8 +14,8 @@
package com.google.gerrit.server.git.strategy;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
import com.google.gerrit.extensions.client.SubmitType;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.server.git.CodeReviewCommit;
@@ -31,6 +31,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
+import java.util.stream.Collectors;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -38,6 +39,7 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,10 +62,13 @@
}
}
- public static Iterable<ObjectId> getAlreadyAccepted(Repository repo) throws IOException {
- return FluentIterable.from(repo.getRefDatabase().getRefs(Constants.R_HEADS).values())
- .append(repo.getRefDatabase().getRefs(Constants.R_TAGS).values())
- .transform(Ref::getObjectId);
+ public static Set<ObjectId> getAlreadyAccepted(Repository repo) throws IOException {
+ return Streams.concat(
+ repo.getRefDatabase().getRefs(Constants.R_HEADS).values().stream(),
+ repo.getRefDatabase().getRefs(Constants.R_TAGS).values().stream())
+ .map(Ref::getObjectId)
+ .filter(o -> o != null)
+ .collect(Collectors.toSet());
}
public static Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw) throws IOException {
@@ -76,6 +81,9 @@
throws IOException {
for (ObjectId id : ids) {
RevObject obj = rw.parseAny(id);
+ if (obj instanceof RevTag) {
+ obj = rw.peel(obj);
+ }
if (obj instanceof RevCommit) {
out.add((RevCommit) obj);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 1d20264..4b96578 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -15,8 +15,8 @@
package com.google.gerrit.server.git.validators;
import static com.google.gerrit.reviewdb.client.Change.CHANGE_ID_PATTERN;
+import static com.google.gerrit.reviewdb.client.RefNames.REFS_CHANGES;
import static com.google.gerrit.reviewdb.client.RefNames.REFS_CONFIG;
-import static com.google.gerrit.server.git.ReceiveCommits.NEW_PATCHSET;
import static java.util.stream.Collectors.toList;
import com.google.common.base.CharMatcher;
@@ -78,6 +78,9 @@
public class CommitValidators {
private static final Logger log = LoggerFactory.getLogger(CommitValidators.class);
+ public static final Pattern NEW_PATCHSET_PATTERN =
+ Pattern.compile("^" + REFS_CHANGES + "(?:[0-9][0-9]/)?([1-9][0-9]*)(?:/new)?$");
+
@Singleton
public static class Factory {
private final PersonIdent gerritIdent;
@@ -266,7 +269,7 @@
private static boolean shouldValidateChangeId(CommitReceivedEvent event) {
return MagicBranch.isMagicBranch(event.command.getRefName())
- || NEW_PATCHSET.matcher(event.command.getRefName()).matches();
+ || NEW_PATCHSET_PATTERN.matcher(event.command.getRefName()).matches();
}
private CommitValidationMessage getMissingChangeIdErrorMsg(String errMsg, RevCommit c) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
index e1513b3..11c9d83 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeIndexRewriter.java
@@ -200,6 +200,7 @@
BitSet isIndexed = new BitSet(n);
BitSet notIndexed = new BitSet(n);
BitSet rewritten = new BitSet(n);
+ BitSet changeSource = new BitSet(n);
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
for (int i = 0; i < n; i++) {
Predicate<ChangeData> c = in.getChild(i);
@@ -211,6 +212,9 @@
notIndexed.set(i);
newChildren.add(c);
} else {
+ if (nc instanceof ChangeDataSource) {
+ changeSource.set(i);
+ }
rewritten.set(i);
newChildren.add(nc);
}
@@ -221,7 +225,11 @@
} else if (notIndexed.cardinality() == n) {
return null; // Can't rewrite any children, so cannot rewrite in.
} else if (rewritten.cardinality() == n) {
- return in.copy(newChildren); // All children were rewritten.
+ // All children were rewritten.
+ if (changeSource.cardinality() == n) {
+ return copy(in, newChildren);
+ }
+ return in.copy(newChildren);
}
return partitionChildren(in, newChildren, isIndexed, index, opts);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailUtil.java
index 5dae659..a752324 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/MailUtil.java
@@ -24,6 +24,7 @@
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.account.AccountResolver;
import com.google.gwtorm.server.OrmException;
+import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.HashSet;
@@ -42,7 +43,7 @@
AccountResolver accountResolver,
boolean draftPatchSet,
List<FooterLine> footerLines)
- throws OrmException {
+ throws OrmException, IOException {
MailRecipients recipients = new MailRecipients();
if (!draftPatchSet) {
for (FooterLine footerLine : footerLines) {
@@ -70,7 +71,7 @@
private static Account.Id toAccountId(
ReviewDb db, AccountResolver accountResolver, String nameOrEmail)
- throws OrmException, NoSuchAccountException {
+ throws OrmException, NoSuchAccountException, IOException {
Account a = accountResolver.findByNameOrEmail(db, nameOrEmail);
if (a == null) {
throw new NoSuchAccountException("\"" + nameOrEmail + "\" is not registered");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
index 24bae37..68bcda4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/receive/MailProcessor.java
@@ -36,8 +36,8 @@
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.change.EmailReviewComments;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.extensions.events.CommentAdded;
@@ -58,6 +58,7 @@
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -73,7 +74,7 @@
public class MailProcessor {
private static final Logger log = LoggerFactory.getLogger(MailProcessor.class);
- private final AccountByEmailCache accountByEmailCache;
+ private final Emails emails;
private final RetryHelper retryHelper;
private final ChangeMessagesUtil changeMessagesUtil;
private final CommentsUtil commentsUtil;
@@ -90,7 +91,7 @@
@Inject
public MailProcessor(
- AccountByEmailCache accountByEmailCache,
+ Emails emails,
RetryHelper retryHelper,
ChangeMessagesUtil changeMessagesUtil,
CommentsUtil commentsUtil,
@@ -104,7 +105,7 @@
CommentAdded commentAdded,
AccountCache accountCache,
@CanonicalWebUrl Provider<String> canonicalUrl) {
- this.accountByEmailCache = accountByEmailCache;
+ this.emails = emails;
this.retryHelper = retryHelper;
this.changeMessagesUtil = changeMessagesUtil;
this.commentsUtil = commentsUtil;
@@ -134,7 +135,7 @@
}
private void processImpl(BatchUpdate.Factory buf, MailMessage message)
- throws OrmException, UpdateException, RestApiException {
+ throws OrmException, UpdateException, RestApiException, IOException {
for (DynamicMap.Entry<MailFilter> filter : mailFilters) {
if (!filter.getProvider().get().shouldProcessMessage(message)) {
log.warn(
@@ -154,15 +155,15 @@
return;
}
- Set<Account.Id> accounts = accountByEmailCache.get(metadata.author);
- if (accounts.size() != 1) {
+ Set<Account.Id> accountIds = emails.getAccountFor(metadata.author);
+ if (accountIds.size() != 1) {
log.error(
String.format(
"Address %s could not be matched to a unique account. It was matched to %s. Will delete message.",
- metadata.author, accounts));
+ metadata.author, accountIds));
return;
}
- Account.Id account = accounts.iterator().next();
+ Account.Id account = accountIds.iterator().next();
if (!accountCache.get(account).getAccount().isActive()) {
log.warn(String.format("Mail: Account %s is inactive. Will delete message.", account));
return;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
index 15a4b13..7bef0a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/ChangeEmail.java
@@ -38,6 +38,7 @@
import com.google.gerrit.server.patch.PatchListEntry;
import com.google.gerrit.server.patch.PatchListNotAvailableException;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
+import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.ProjectState;
@@ -405,12 +406,12 @@
}
@Override
- protected boolean isVisibleTo(Account.Id to) throws OrmException {
- return projectState == null
- || projectState
- .controlFor(args.identifiedUserFactory.create(to))
- .controlFor(args.db.get(), change)
- .isVisible(args.db.get());
+ protected boolean isVisibleTo(Account.Id to) throws OrmException, PermissionBackendException {
+ return args.permissionBackend
+ .user(args.identifiedUserFactory.create(to))
+ .change(changeData)
+ .database(args.db.get())
+ .test(ChangePermission.READ);
}
/** Find all users who are authors of any part of this change. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
index ce68cca..7d83c5c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/CommentSender.java
@@ -435,21 +435,43 @@
}
/**
- * @return a shortened version of the given comment's message. Will be shortened to 75 characters
- * or the first line, whichever is shorter.
+ * @return a shortened version of the given comment's message. Will be shortened to 100 characters
+ * or the first line, or following the last period within the first 100 characters, whichever
+ * is shorter. If the message is shortened, an ellipsis is appended.
*/
- private String getShortenedCommentMessage(Comment comment) {
- String msg = comment.message.trim();
- if (msg.length() > 75) {
- msg = msg.substring(0, 75);
+ protected static String getShortenedCommentMessage(String message) {
+ int threshold = 100;
+ String fullMessage = message.trim();
+ String msg = fullMessage;
+
+ if (msg.length() > threshold) {
+ msg = msg.substring(0, threshold);
}
+
int lf = msg.indexOf('\n');
+ int period = msg.lastIndexOf('.');
+
if (lf > 0) {
+ // Truncate if a line feed appears within the threshold.
msg = msg.substring(0, lf);
+
+ } else if (period > 0) {
+ // Otherwise truncate if there is a period within the threshold.
+ msg = msg.substring(0, period + 1);
}
+
+ // Append an ellipsis if the message has been truncated.
+ if (!msg.equals(fullMessage)) {
+ msg += " […]";
+ }
+
return msg;
}
+ protected static String getShortenedCommentMessage(Comment comment) {
+ return getShortenedCommentMessage(comment.message);
+ }
+
/**
* @return grouped inline comment data mapped to data structures that are suitable for passing
* into Soy.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 4e204ce..e569adf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -31,6 +31,7 @@
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.mail.Address;
import com.google.gerrit.server.mail.send.EmailHeader.AddressList;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmException;
@@ -479,7 +480,7 @@
rcptTo.add(to);
add(rt, toAddress(to), override);
}
- } catch (OrmException e) {
+ } catch (OrmException | PermissionBackendException e) {
log.error("Error reading database for account: " + to, e);
}
}
@@ -487,9 +488,10 @@
/**
* @param to account.
* @throws OrmException
+ * @throws PermissionBackendException
* @return whether this email is visible to the given account.
*/
- protected boolean isVisibleTo(Account.Id to) throws OrmException {
+ protected boolean isVisibleTo(Account.Id to) throws OrmException, PermissionBackendException {
return true;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
index cd9c4c3..41bade6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PatchSetInfoFactory.java
@@ -22,7 +22,7 @@
import com.google.gerrit.reviewdb.client.UserIdentity;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.account.AccountByEmailCache;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gwtorm.server.OrmException;
@@ -45,17 +45,17 @@
public class PatchSetInfoFactory {
private final GitRepositoryManager repoManager;
private final PatchSetUtil psUtil;
- private final AccountByEmailCache byEmailCache;
+ private final Emails emails;
@Inject
- public PatchSetInfoFactory(
- GitRepositoryManager repoManager, PatchSetUtil psUtil, AccountByEmailCache byEmailCache) {
+ public PatchSetInfoFactory(GitRepositoryManager repoManager, PatchSetUtil psUtil, Emails emails) {
this.repoManager = repoManager;
this.psUtil = psUtil;
- this.byEmailCache = byEmailCache;
+ this.emails = emails;
}
- public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi) throws IOException {
+ public PatchSetInfo get(RevWalk rw, RevCommit src, PatchSet.Id psi)
+ throws IOException, OrmException {
rw.parseBody(src);
PatchSetInfo info = new PatchSetInfo(psi);
info.setSubject(src.getShortMessage());
@@ -84,13 +84,13 @@
PatchSetInfo info = get(rw, src, patchSet.getId());
info.setParents(toParentInfos(src.getParents(), rw));
return info;
- } catch (IOException e) {
+ } catch (IOException | OrmException e) {
throw new PatchSetInfoNotAvailableException(e);
}
}
// TODO: The same method exists in EventFactory, find a common place for it
- private UserIdentity toUserIdentity(PersonIdent who) {
+ private UserIdentity toUserIdentity(PersonIdent who) throws IOException, OrmException {
final UserIdentity u = new UserIdentity();
u.setName(who.getName());
u.setEmail(who.getEmailAddress());
@@ -100,7 +100,7 @@
// If only one account has access to this email address, select it
// as the identity of the user.
//
- final Set<Account.Id> a = byEmailCache.get(u.getEmail());
+ Set<Account.Id> a = emails.getAccountFor(u.getEmail());
if (a.size() == 1) {
u.setAccount(a.iterator().next());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
index 24f5164..4c6e6753 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/FailedPermissionBackend.java
@@ -119,7 +119,12 @@
}
@Override
- public ForChange change(ChangeNotes cd) {
+ public ForChange change(ChangeNotes notes) {
+ return new FailedChange(message, cause);
+ }
+
+ @Override
+ public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
return new FailedChange(message, cause);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
index 93db963..522eccb 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/permissions/PermissionBackend.java
@@ -139,6 +139,15 @@
return ref(notes.getChange().getDest()).change(notes);
}
+ /**
+ * @return instance scoped for the change loaded from index, and its destination ref and
+ * project. This method should only be used when database access is harmful and potentially
+ * stale data from the index is acceptable.
+ */
+ public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
+ return ref(notes.getChange().getDest()).indexedChange(cd, notes);
+ }
+
/** Verify scoped user can {@code perm}, throwing if denied. */
public abstract void check(GlobalOrPluginPermission perm)
throws AuthException, PermissionBackendException;
@@ -269,6 +278,12 @@
/** @return instance scoped to change. */
public abstract ForChange change(ChangeNotes notes);
+ /**
+ * @return instance scoped to change loaded from index. This method should only be used when
+ * database access is harmful and potentially stale data from the index is acceptable.
+ */
+ public abstract ForChange indexedChange(ChangeData cd, ChangeNotes notes);
+
/** Verify scoped user can {@code perm}, throwing if denied. */
public abstract void check(RefPermission perm) throws AuthException, PermissionBackendException;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 94f5ebf..d0b7ad4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -136,18 +136,6 @@
return create(refControl, notesFactory.create(db, project, changeId));
}
- /**
- * Create a change control for a change that was loaded from index. This method should only be
- * used when database access is harmful and potentially stale data from the index is acceptable.
- *
- * @param refControl ref control
- * @param change change loaded from secondary index
- * @return change control
- */
- ChangeControl createForIndexedChange(RefControl refControl, Change change) {
- return create(refControl, notesFactory.createFromIndexedChange(change));
- }
-
ChangeControl create(RefControl refControl, ChangeNotes notes) {
return new ChangeControl(changeDataFactory, approvalsUtil, refControl, notes, patchSetUtil);
}
@@ -209,12 +197,12 @@
}
/** Can this user see this change? */
- public boolean isVisible(ReviewDb db) throws OrmException {
+ boolean isVisible(ReviewDb db) throws OrmException {
return isVisible(db, null);
}
/** Can this user see this change? */
- public boolean isVisible(ReviewDb db, @Nullable ChangeData cd) throws OrmException {
+ private boolean isVisible(ReviewDb db, @Nullable ChangeData cd) throws OrmException {
if (getChange().isPrivate() && !isPrivateVisible(db, cd)) {
return false;
}
@@ -226,6 +214,7 @@
/** Can this user see the given patchset? */
public boolean isPatchVisible(PatchSet ps, ReviewDb db) throws OrmException {
+ // TODO(hiesel) These don't need to be migrated, just remove after support for drafts is removed
if (ps != null && ps.isDraft() && !isDraftVisible(db, null)) {
return false;
}
@@ -234,6 +223,7 @@
/** Can this user see the given patchset? */
public boolean isPatchVisible(PatchSet ps, ChangeData cd) throws OrmException {
+ // TODO(hiesel) These don't need to be migrated, just remove after support for drafts is removed
checkArgument(
cd.getId().equals(ps.getId().getParentKey()), "%s not for change %s", ps, cd.getId());
if (ps.isDraft() && !isDraftVisible(cd.db(), cd)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CheckMergeability.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CheckMergeability.java
index 1a81726..1ab2dbd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CheckMergeability.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CheckMergeability.java
@@ -19,13 +19,11 @@
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.InMemoryInserter;
import com.google.gerrit.server.git.MergeUtil;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import java.io.IOException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ObjectInserter;
@@ -39,11 +37,9 @@
/** Check the mergeability at current branch for a git object references expression. */
public class CheckMergeability implements RestReadView<BranchResource> {
-
private String source;
private String strategy;
private SubmitType submitType;
- private final Provider<ReviewDb> db;
@Option(
name = "--source",
@@ -68,14 +64,15 @@
}
private final GitRepositoryManager gitManager;
+ private final CommitsCollection commits;
@Inject
CheckMergeability(
- GitRepositoryManager gitManager, @GerritServerConfig Config cfg, Provider<ReviewDb> db) {
+ GitRepositoryManager gitManager, CommitsCollection commits, @GerritServerConfig Config cfg) {
this.gitManager = gitManager;
+ this.commits = commits;
this.strategy = MergeUtil.getMergeStrategy(cfg).getName();
this.submitType = cfg.getEnum("project", null, "submitType", SubmitType.MERGE_IF_NECESSARY);
- this.db = db;
}
@Override
@@ -102,7 +99,7 @@
RevCommit targetCommit = rw.parseCommit(destRef.getObjectId());
RevCommit sourceCommit = MergeUtil.resolveCommit(git, rw, source);
- if (!resource.getControl().canReadCommit(db.get(), git, sourceCommit)) {
+ if (!commits.canRead(resource.getProjectState(), git, sourceCommit)) {
throw new BadRequestException("do not have read permission for: " + source);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitsCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitsCollection.java
index d481c014..e38f442 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitsCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/CommitsCollection.java
@@ -19,33 +19,48 @@
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.change.IncludedInResolver;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.VisibleRefFilter;
+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.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.List;
+import java.util.Map;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
@Singleton
public class CommitsCollection implements ChildCollection<ProjectResource, CommitResource> {
+ private static final Logger log = LoggerFactory.getLogger(CommitsCollection.class);
+
private final DynamicMap<RestView<CommitResource>> views;
private final GitRepositoryManager repoManager;
- private final Provider<ReviewDb> db;
+ private final VisibleRefFilter.Factory refFilter;
+ private final Provider<InternalChangeQuery> queryProvider;
@Inject
public CommitsCollection(
DynamicMap<RestView<CommitResource>> views,
GitRepositoryManager repoManager,
- Provider<ReviewDb> db) {
+ VisibleRefFilter.Factory refFilter,
+ Provider<InternalChangeQuery> queryProvider) {
this.views = views;
this.repoManager = repoManager;
- this.db = db;
+ this.refFilter = refFilter;
+ this.queryProvider = queryProvider;
}
@Override
@@ -67,7 +82,7 @@
RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(objectId);
rw.parseBody(commit);
- if (!parent.getControl().canReadCommit(db.get(), repo, commit)) {
+ if (!canRead(parent.getProjectState(), repo, commit)) {
throw new ResourceNotFoundException(id);
}
for (int i = 0; i < commit.getParentCount(); i++) {
@@ -83,4 +98,37 @@
public DynamicMap<RestView<CommitResource>> views() {
return views;
}
+
+ /** @return true if {@code commit} is visible to the caller. */
+ public boolean canRead(ProjectState state, Repository repo, RevCommit commit) {
+ Project.NameKey project = state.getProject().getNameKey();
+
+ // Look for changes associated with the commit.
+ try {
+ List<ChangeData> changes =
+ queryProvider.get().enforceVisibility(true).byProjectCommit(project, commit);
+ if (!changes.isEmpty()) {
+ return true;
+ }
+ } catch (OrmException e) {
+ log.error("Cannot look up change for commit " + commit.name() + " in " + project, e);
+ }
+
+ return isReachableFrom(state, repo, commit, repo.getAllRefs());
+ }
+
+ public boolean isReachableFrom(
+ ProjectState state, Repository repo, RevCommit commit, Map<String, Ref> refs) {
+ try (RevWalk rw = new RevWalk(repo)) {
+ refs = refFilter.create(state, repo).filter(refs, true);
+ return IncludedInResolver.includedInAny(repo, rw, commit, refs.values());
+ } catch (IOException e) {
+ log.error(
+ String.format(
+ "Cannot verify permissions to commit object %s in repository %s",
+ commit.name(), state.getProject().getNameKey()),
+ e);
+ return false;
+ }
+ }
}
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 03db4f6..58a8987 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
@@ -17,10 +17,8 @@
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.inject.Inject;
-import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@@ -34,13 +32,13 @@
@Singleton
public class GetHead implements RestReadView<ProjectResource> {
- private GitRepositoryManager repoManager;
- private Provider<ReviewDb> db;
+ private final GitRepositoryManager repoManager;
+ private final CommitsCollection commits;
@Inject
- GetHead(GitRepositoryManager repoManager, Provider<ReviewDb> db) {
+ GetHead(GitRepositoryManager repoManager, CommitsCollection commits) {
this.repoManager = repoManager;
- this.db = db;
+ this.commits = commits;
}
@Override
@@ -59,7 +57,7 @@
} else if (head.getObjectId() != null) {
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(head.getObjectId());
- if (rsrc.getControl().canReadCommit(db.get(), repo, commit)) {
+ if (commits.canRead(rsrc.getProjectState(), repo, commit)) {
return head.getObjectId().name();
}
throw new AuthException("not allowed to see HEAD");
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 a1ab177..ff0070d 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
@@ -16,6 +16,7 @@
import static com.google.common.base.Preconditions.checkArgument;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.PageLinks;
@@ -39,11 +40,9 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.GroupMembership;
-import com.google.gerrit.server.change.IncludedInResolver;
import com.google.gerrit.server.config.CanonicalWebUrl;
import com.google.gerrit.server.config.GitReceivePackGroups;
import com.google.gerrit.server.config.GitUploadPackGroups;
-import com.google.gerrit.server.git.VisibleRefFilter;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.permissions.FailedPermissionBackend;
@@ -55,7 +54,6 @@
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
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.Inject;
import com.google.inject.Provider;
@@ -71,10 +69,11 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -132,16 +131,14 @@
private final Set<AccountGroup.UUID> uploadGroups;
private final Set<AccountGroup.UUID> receiveGroups;
-
private final String canonicalWebUrl;
private final PermissionBackend.WithUser perm;
private final CurrentUser user;
private final ProjectState state;
+ private final CommitsCollection commits;
private final ChangeControl.Factory changeControlFactory;
private final PermissionCollection.Factory permissionFilter;
- private final VisibleRefFilter.Factory refFilter;
private final Collection<ContributorAgreement> contributorAgreements;
- private final Provider<InternalChangeQuery> queryProvider;
private final Metrics metrics;
private List<SectionMatcher> allSections;
@@ -156,22 +153,20 @@
@GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
ProjectCache pc,
PermissionCollection.Factory permissionFilter,
+ CommitsCollection commits,
ChangeControl.Factory changeControlFactory,
- VisibleRefFilter.Factory refFilter,
- Provider<InternalChangeQuery> queryProvider,
@CanonicalWebUrl @Nullable String canonicalWebUrl,
PermissionBackend permissionBackend,
@Assisted CurrentUser who,
@Assisted ProjectState ps,
Metrics metrics) {
this.changeControlFactory = changeControlFactory;
- this.refFilter = refFilter;
this.uploadGroups = uploadGroups;
this.receiveGroups = receiveGroups;
this.permissionFilter = permissionFilter;
+ this.commits = commits;
this.contributorAgreements = pc.getAllProjects().getConfig().getContributorAgreements();
this.canonicalWebUrl = canonicalWebUrl;
- this.queryProvider = queryProvider;
this.metrics = metrics;
this.perm = permissionBackend.user(who);
user = who;
@@ -190,17 +185,6 @@
controlForRef(change.getDest()), db, change.getProject(), change.getId());
}
- /**
- * Create a change control for a change that was loaded from index. This method should only be
- * used when database access is harmful and potentially stale data from the index is acceptable.
- *
- * @param change change loaded from secondary index
- * @return change control
- */
- public ChangeControl controlForIndexedChange(Change change) {
- return changeControlFactory.createForIndexedChange(controlForRef(change.getDest()), change);
- }
-
public ChangeControl controlFor(ChangeNotes notes) {
return changeControlFactory.create(controlForRef(notes.getChange().getDest()), notes);
}
@@ -483,50 +467,26 @@
return false;
}
- /** @return whether a commit is visible to user. */
- public boolean canReadCommit(ReviewDb db, Repository repo, RevCommit commit) {
- // Look for changes associated with the commit.
+ boolean isReachableFromHeadsOrTags(Repository repo, RevCommit commit) {
try {
- List<ChangeData> changes =
- queryProvider.get().byProjectCommit(getProject().getNameKey(), commit);
- for (ChangeData change : changes) {
- if (controlFor(db, change.change()).isVisible(db)) {
- return true;
- }
+ RefDatabase refdb = repo.getRefDatabase();
+ Collection<Ref> heads = refdb.getRefs(Constants.R_HEADS).values();
+ Collection<Ref> tags = refdb.getRefs(Constants.R_TAGS).values();
+ Map<String, Ref> refs = Maps.newHashMapWithExpectedSize(heads.size() + tags.size());
+ for (Ref r : Iterables.concat(heads, tags)) {
+ refs.put(r.getName(), r);
}
- } catch (OrmException e) {
- log.error(
- "Cannot look up change for commit " + commit.name() + " in " + getProject().getName(), e);
- }
- // Scan all visible refs.
- return canReadCommitFromVisibleRef(repo, commit);
- }
-
- private boolean canReadCommitFromVisibleRef(Repository repo, RevCommit commit) {
- try (RevWalk rw = new RevWalk(repo)) {
- return isMergedIntoVisibleRef(repo, rw, commit, repo.getAllRefs().values());
+ return commits.isReachableFrom(state, repo, commit, refs);
} catch (IOException e) {
- String msg =
+ log.error(
String.format(
"Cannot verify permissions to commit object %s in repository %s",
- commit.name(), getProject().getNameKey());
- log.error(msg, e);
+ commit.name(), getProject().getNameKey()),
+ e);
return false;
}
}
- boolean isMergedIntoVisibleRef(
- Repository repo, RevWalk rw, RevCommit commit, Collection<Ref> unfilteredRefs)
- throws IOException {
- VisibleRefFilter filter = refFilter.create(state, repo);
- Map<String, Ref> m = Maps.newHashMapWithExpectedSize(unfilteredRefs.size());
- for (Ref r : unfilteredRefs) {
- m.put(r.getName(), r);
- }
- Map<String, Ref> refs = filter.filter(m, true);
- return IncludedInResolver.includedInAny(repo, rw, commit, refs.values());
- }
-
ForProject asForProject() {
return new ForProjectImpl();
}
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 4bb823e..1ab1dbb 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
@@ -45,9 +45,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
@@ -347,7 +345,7 @@
// If the user has push permissions, they can create the ref regardless
// of whether they are pushing any new objects along with the create.
return null;
- } else if (isMergedIntoBranchOrTag(repo, commit)) {
+ } else if (projectControl.isReachableFromHeadsOrTags(repo, commit)) {
// If the user has no push permissions, check whether the object is
// merged into a branch or tag readable by this user. If so, they are
// not effectively "pushing" more objects, so they can create the ref
@@ -357,21 +355,6 @@
return userId + " lacks permission " + Permission.PUSH + " for creating new commit object";
}
- private boolean isMergedIntoBranchOrTag(Repository repo, RevCommit commit) {
- try (RevWalk rw = new RevWalk(repo)) {
- List<Ref> refs = new ArrayList<>(repo.getRefDatabase().getRefs(Constants.R_HEADS).values());
- refs.addAll(repo.getRefDatabase().getRefs(Constants.R_TAGS).values());
- return projectControl.isMergedIntoVisibleRef(repo, rw, commit, refs);
- } catch (IOException e) {
- String msg =
- String.format(
- "Cannot verify permissions to commit object %s in repository %s",
- commit.name(), projectControl.getProject().getNameKey());
- log.error(msg, e);
- }
- return false;
- }
-
/**
* Determines whether the user can delete the Git ref controlled by this object.
*
@@ -713,6 +696,11 @@
}
@Override
+ public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
+ return getProjectControl().controlFor(notes).asForChange(cd, db);
+ }
+
+ @Override
public void check(RefPermission perm) throws AuthException, PermissionBackendException {
if (!can(perm)) {
throw new AuthException(perm.describeForException() + " not permitted");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
index 9f63b16..7274100 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SubmitRuleEvaluator.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.googlecode.prolog_cafe.exceptions.CompileException;
@@ -85,6 +86,7 @@
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ChangeData cd;
private final ChangeControl control;
@@ -96,10 +98,12 @@
private Term submitRule;
- public SubmitRuleEvaluator(AccountCache accountCache, Accounts accounts, ChangeData cd)
+ public SubmitRuleEvaluator(
+ AccountCache accountCache, Accounts accounts, Emails emails, ChangeData cd)
throws OrmException {
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.cd = cd;
this.control = cd.changeControl();
}
@@ -573,6 +577,7 @@
}
env.set(StoredValues.ACCOUNTS, accounts);
env.set(StoredValues.ACCOUNT_CACHE, accountCache);
+ env.set(StoredValues.EMAILS, emails);
env.set(StoredValues.REVIEW_DB, cd.db());
env.set(StoredValues.CHANGE_DATA, cd);
env.set(StoredValues.CHANGE_CONTROL, control);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
index 70d8484..8fa29753 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/account/InternalAccountQuery.java
@@ -14,8 +14,12 @@
package com.google.gerrit.server.query.account;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.account.externalids.ExternalId;
@@ -24,6 +28,7 @@
import com.google.gerrit.server.query.InternalQuery;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
+import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
@@ -106,6 +111,17 @@
return query(AccountPredicates.preferredEmail(email));
}
+ public Multimap<String, AccountState> byPreferredEmail(String... emails) throws OrmException {
+ List<String> emailList = Arrays.asList(emails);
+ List<List<AccountState>> r =
+ query(emailList.stream().map(e -> AccountPredicates.preferredEmail(e)).collect(toList()));
+ Multimap<String, AccountState> accountsByEmail = ArrayListMultimap.create();
+ for (int i = 0; i < emailList.size(); i++) {
+ accountsByEmail.putAll(emailList.get(i), r.get(i));
+ }
+ return accountsByEmail;
+ }
+
public List<AccountState> byWatchedProject(Project.NameKey project) throws OrmException {
return query(AccountPredicates.watchedProject(project));
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 96f6b5d..af23b8d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -60,6 +60,7 @@
import com.google.gerrit.server.StarredChangesUtil.StarRef;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.change.MergeabilityCache;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
@@ -304,7 +305,7 @@
ChangeData cd =
new ChangeData(
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, project, id);
+ null, null, null, null, project, id);
cd.currentPatchSet = new PatchSet(new PatchSet.Id(id, currentPatchSetId));
return cd;
}
@@ -316,6 +317,7 @@
private final IdentifiedUser.GenericFactory userFactory;
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final ProjectCache projectCache;
private final MergeUtil.Factory mergeUtilFactory;
private final ChangeNotes.Factory notesFactory;
@@ -376,6 +378,7 @@
IdentifiedUser.GenericFactory userFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
@@ -396,6 +399,7 @@
this.userFactory = userFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
@@ -418,6 +422,7 @@
IdentifiedUser.GenericFactory userFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
@@ -437,6 +442,7 @@
this.userFactory = userFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
@@ -460,6 +466,7 @@
IdentifiedUser.GenericFactory userFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
@@ -479,6 +486,7 @@
this.userFactory = userFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
@@ -503,6 +511,7 @@
IdentifiedUser.GenericFactory userFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
@@ -522,6 +531,7 @@
this.userFactory = userFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
@@ -547,6 +557,7 @@
IdentifiedUser.GenericFactory userFactory,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
ProjectCache projectCache,
MergeUtil.Factory mergeUtilFactory,
ChangeNotes.Factory notesFactory,
@@ -569,6 +580,7 @@
this.userFactory = userFactory;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.projectCache = projectCache;
this.mergeUtilFactory = mergeUtilFactory;
this.notesFactory = notesFactory;
@@ -1117,7 +1129,9 @@
return Collections.emptyList();
}
records =
- new SubmitRuleEvaluator(accountCache, accounts, this).setOptions(options).evaluate();
+ new SubmitRuleEvaluator(accountCache, accounts, emails, this)
+ .setOptions(options)
+ .evaluate();
submitRecords.put(options, records);
}
return records;
@@ -1134,7 +1148,8 @@
public SubmitTypeRecord submitTypeRecord() throws OrmException {
if (submitTypeRecord == null) {
- submitTypeRecord = new SubmitRuleEvaluator(accountCache, accounts, this).getSubmitType();
+ submitTypeRecord =
+ new SubmitRuleEvaluator(accountCache, accounts, emails, this).getSubmitType();
}
return submitTypeRecord;
}
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 fa08f53..15fd190 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
@@ -18,6 +18,9 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.notedb.ChangeNotes;
+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.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.IsVisibleToPredicate;
@@ -29,17 +32,20 @@
protected final ChangeNotes.Factory notesFactory;
protected final ChangeControl.GenericFactory changeControl;
protected final CurrentUser user;
+ protected final PermissionBackend permissionBackend;
public ChangeIsVisibleToPredicate(
Provider<ReviewDb> db,
ChangeNotes.Factory notesFactory,
ChangeControl.GenericFactory changeControlFactory,
- CurrentUser user) {
+ CurrentUser user,
+ PermissionBackend permissionBackend) {
super(ChangeQueryBuilder.FIELD_VISIBLETO, describe(user));
this.db = db;
this.notesFactory = notesFactory;
this.changeControl = changeControlFactory;
this.user = user;
+ this.permissionBackend = permissionBackend;
}
@Override
@@ -47,21 +53,35 @@
if (cd.fastIsVisibleTo(user)) {
return true;
}
+ Change change;
try {
- Change c = cd.change();
- if (c == null) {
+ change = cd.change();
+ if (change == null) {
return false;
}
-
- ChangeNotes notes = notesFactory.createFromIndexedChange(c);
- ChangeControl cc = changeControl.controlFor(notes, user);
- if (cc.isVisible(db.get(), cd)) {
- cd.cacheVisibleTo(cc);
- return true;
- }
} catch (NoSuchChangeException e) {
// Ignored
+ return false;
}
+
+ ChangeNotes notes = notesFactory.createFromIndexedChange(change);
+ ChangeControl cc = changeControl.controlFor(notes, user);
+ boolean visible;
+ try {
+ visible =
+ permissionBackend
+ .user(user)
+ .indexedChange(cd, notes)
+ .database(db)
+ .test(ChangePermission.READ);
+ } catch (PermissionBackendException e) {
+ throw new OrmException("unable to check permissions", e);
+ }
+ if (visible) {
+ cd.cacheVisibleTo(cc);
+ return true;
+ }
+
return false;
}
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 aecfc42..52802de 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
@@ -80,6 +80,7 @@
import com.google.inject.ProvisionException;
import com.google.inject.util.Providers;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -648,7 +649,12 @@
@Operator
public Predicate<ChangeData> conflicts(String value) throws OrmException, QueryParseException {
- return new ConflictsPredicate(args, value, parseChange(value));
+ List<Change> changes = parseChange(value);
+ List<Predicate<ChangeData>> or = new ArrayList<>(changes.size());
+ for (Change c : changes) {
+ or.add(ConflictsPredicate.create(args, value, c));
+ }
+ return Predicate.or(or);
}
@Operator
@@ -940,7 +946,7 @@
public Predicate<ChangeData> visibleto(CurrentUser user) {
return new ChangeIsVisibleToPredicate(
- args.db, args.notesFactory, args.changeControlGenericFactory, user);
+ args.db, args.notesFactory, args.changeControlGenericFactory, user, args.permissionBackend);
}
public Predicate<ChangeData> is_visible() throws QueryParseException {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
index f4064f5..eeb6d01 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryProcessor.java
@@ -30,6 +30,7 @@
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.index.change.IndexedChangeQuery;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryProcessor;
@@ -55,6 +56,7 @@
private final ChangeControl.GenericFactory changeControlFactory;
private final ChangeNotes.Factory notesFactory;
private final DynamicMap<ChangeAttributeFactory> attributeFactories;
+ private final PermissionBackend permissionBackend;
static {
// It is assumed that basic rewrites do not touch visibleto predicates.
@@ -74,7 +76,8 @@
Provider<ReviewDb> db,
ChangeControl.GenericFactory changeControlFactory,
ChangeNotes.Factory notesFactory,
- DynamicMap<ChangeAttributeFactory> attributeFactories) {
+ DynamicMap<ChangeAttributeFactory> attributeFactories,
+ PermissionBackend permissionBackend) {
super(
userProvider,
limitsFactory,
@@ -88,6 +91,7 @@
this.changeControlFactory = changeControlFactory;
this.notesFactory = notesFactory;
this.attributeFactories = attributeFactories;
+ this.permissionBackend = permissionBackend;
}
@Override
@@ -130,7 +134,8 @@
protected Predicate<ChangeData> enforceVisibility(Predicate<ChangeData> pred) {
return new AndChangeSource(
pred,
- new ChangeIsVisibleToPredicate(db, notesFactory, changeControlFactory, userProvider.get()),
+ new ChangeIsVisibleToPredicate(
+ db, notesFactory, changeControlFactory, userProvider.get(), permissionBackend),
start);
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
index 9e8f77b..8212d64 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ConflictsPredicate.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.query.change;
-import com.google.common.collect.Lists;
import com.google.gerrit.common.data.SubmitTypeRecord;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -25,7 +24,6 @@
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.query.OrPredicate;
import com.google.gerrit.server.query.Predicate;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
@@ -45,130 +43,48 @@
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
-public class ConflictsPredicate extends OrPredicate<ChangeData> {
+public class ConflictsPredicate {
// UI code may depend on this string, so use caution when changing.
protected static final String TOO_MANY_FILES = "too many files to find conflicts";
- protected final String value;
+ private ConflictsPredicate() {}
- public ConflictsPredicate(Arguments args, String value, List<Change> changes)
+ public static Predicate<ChangeData> create(Arguments args, String value, Change c)
throws QueryParseException, OrmException {
- super(predicates(args, value, changes));
- this.value = value;
- }
-
- public static List<Predicate<ChangeData>> predicates(
- final Arguments args, String value, List<Change> changes)
- throws QueryParseException, OrmException {
- int indexTerms = 0;
-
- List<Predicate<ChangeData>> changePredicates = Lists.newArrayListWithCapacity(changes.size());
- final Provider<ReviewDb> db = args.db;
- for (Change c : changes) {
- final ChangeDataCache changeDataCache =
- new ChangeDataCache(c, db, args.changeDataFactory, args.projectCache);
- List<String> files = listFiles(c, args, changeDataCache);
- indexTerms += 3 + files.size();
- if (indexTerms > args.indexConfig.maxTerms()) {
- // Short-circuit with a nice error message if we exceed the index
- // backend's term limit. This assumes that "conflicts:foo" is the entire
- // query; if there are more terms in the input, we might not
- // short-circuit here, which will result in a more generic error message
- // later on in the query parsing.
- throw new QueryParseException(TOO_MANY_FILES);
- }
-
- List<Predicate<ChangeData>> filePredicates = Lists.newArrayListWithCapacity(files.size());
- for (String file : files) {
- filePredicates.add(new EqualsPathPredicate(ChangeQueryBuilder.FIELD_PATH, file));
- }
-
- List<Predicate<ChangeData>> predicatesForOneChange = Lists.newArrayListWithCapacity(5);
- predicatesForOneChange.add(not(new LegacyChangeIdPredicate(c.getId())));
- predicatesForOneChange.add(new ProjectPredicate(c.getProject().get()));
- predicatesForOneChange.add(new RefPredicate(c.getDest().get()));
-
- predicatesForOneChange.add(or(or(filePredicates), new IsMergePredicate(args, value)));
-
- predicatesForOneChange.add(
- new ChangeOperatorPredicate(ChangeQueryBuilder.FIELD_CONFLICTS, value) {
-
- @Override
- public boolean match(ChangeData object) throws OrmException {
- Change otherChange = object.change();
- if (otherChange == null) {
- return false;
- }
- if (!otherChange.getDest().equals(c.getDest())) {
- return false;
- }
- SubmitTypeRecord str = object.submitTypeRecord();
- if (!str.isOk()) {
- return false;
- }
- ObjectId other = ObjectId.fromString(object.currentPatchSet().getRevision().get());
- ConflictKey conflictsKey =
- new ConflictKey(
- changeDataCache.getTestAgainst(),
- other,
- str.type,
- changeDataCache.getProjectState().isUseContentMerge());
- Boolean conflicts = args.conflictsCache.getIfPresent(conflictsKey);
- if (conflicts != null) {
- return conflicts;
- }
- try (Repository repo = args.repoManager.openRepository(otherChange.getProject());
- CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
- conflicts =
- !args.submitDryRun.run(
- str.type,
- repo,
- rw,
- otherChange.getDest(),
- changeDataCache.getTestAgainst(),
- other,
- getAlreadyAccepted(repo, rw));
- args.conflictsCache.put(conflictsKey, conflicts);
- return conflicts;
- } catch (IntegrationException | NoSuchProjectException | IOException e) {
- throw new OrmException(e);
- }
- }
-
- @Override
- public int getCost() {
- return 5;
- }
-
- private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw)
- throws IntegrationException {
- try {
- Set<RevCommit> accepted = new HashSet<>();
- SubmitDryRun.addCommits(changeDataCache.getAlreadyAccepted(repo), rw, accepted);
- ObjectId tip = changeDataCache.getTestAgainst();
- if (tip != null) {
- accepted.add(rw.parseCommit(tip));
- }
- return accepted;
- } catch (OrmException | IOException e) {
- throw new IntegrationException("Failed to determine already accepted commits.", e);
- }
- }
- });
- changePredicates.add(and(predicatesForOneChange));
+ ChangeDataCache changeDataCache =
+ new ChangeDataCache(c, args.db, args.changeDataFactory, args.projectCache);
+ List<String> files = listFiles(c, args, changeDataCache);
+ if (3 + files.size() > args.indexConfig.maxTerms()) {
+ // Short-circuit with a nice error message if we exceed the index
+ // backend's term limit. This assumes that "conflicts:foo" is the entire
+ // query; if there are more terms in the input, we might not
+ // short-circuit here, which will result in a more generic error message
+ // later on in the query parsing.
+ throw new QueryParseException(TOO_MANY_FILES);
}
- return changePredicates;
+
+ List<Predicate<ChangeData>> filePredicates = new ArrayList<>(files.size());
+ for (String file : files) {
+ filePredicates.add(new EqualsPathPredicate(ChangeQueryBuilder.FIELD_PATH, file));
+ }
+
+ List<Predicate<ChangeData>> and = new ArrayList<>(5);
+ and.add(new ProjectPredicate(c.getProject().get()));
+ and.add(new RefPredicate(c.getDest().get()));
+ and.add(Predicate.not(new LegacyChangeIdPredicate(c.getId())));
+ and.add(Predicate.or(filePredicates));
+ and.add(new CheckConflict(ChangeQueryBuilder.FIELD_CONFLICTS, value, args, c, changeDataCache));
+ return Predicate.and(and);
}
- public static List<String> listFiles(Change c, Arguments args, ChangeDataCache changeDataCache)
+ private static List<String> listFiles(Change c, Arguments args, ChangeDataCache changeDataCache)
throws OrmException {
try (Repository repo = args.repoManager.openRepository(c.getProject());
RevWalk rw = new RevWalk(repo)) {
RevCommit ps = rw.parseCommit(changeDataCache.getTestAgainst());
if (ps.getParentCount() > 1) {
String dest = c.getDest().get();
- Ref destBranch = repo.getRefDatabase().getRef(dest);
- destBranch.getObjectId();
+ Ref destBranch = repo.getRefDatabase().exactRef(dest);
rw.setRevFilter(RevFilter.MERGE_BASE);
rw.markStart(rw.parseCommit(destBranch.getObjectId()));
rw.markStart(ps);
@@ -195,9 +111,80 @@
}
}
- @Override
- public String toString() {
- return ChangeQueryBuilder.FIELD_CONFLICTS + ":" + value;
+ private static final class CheckConflict extends ChangeOperatorPredicate {
+ private final Arguments args;
+ private final Change c;
+ private final ChangeDataCache changeDataCache;
+
+ CheckConflict(
+ String field, String value, Arguments args, Change c, ChangeDataCache changeDataCache) {
+ super(field, value);
+ this.args = args;
+ this.c = c;
+ this.changeDataCache = changeDataCache;
+ }
+
+ @Override
+ public boolean match(ChangeData object) throws OrmException {
+ Change otherChange = object.change();
+ if (otherChange == null || !otherChange.getDest().equals(c.getDest())) {
+ return false;
+ }
+
+ SubmitTypeRecord str = object.submitTypeRecord();
+ if (!str.isOk()) {
+ return false;
+ }
+
+ ObjectId other = ObjectId.fromString(object.currentPatchSet().getRevision().get());
+ ConflictKey conflictsKey =
+ new ConflictKey(
+ changeDataCache.getTestAgainst(),
+ other,
+ str.type,
+ changeDataCache.getProjectState().isUseContentMerge());
+ Boolean conflicts = args.conflictsCache.getIfPresent(conflictsKey);
+ if (conflicts != null) {
+ return conflicts;
+ }
+
+ try (Repository repo = args.repoManager.openRepository(otherChange.getProject());
+ CodeReviewRevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
+ conflicts =
+ !args.submitDryRun.run(
+ str.type,
+ repo,
+ rw,
+ otherChange.getDest(),
+ changeDataCache.getTestAgainst(),
+ other,
+ getAlreadyAccepted(repo, rw));
+ args.conflictsCache.put(conflictsKey, conflicts);
+ return conflicts;
+ } catch (IntegrationException | NoSuchProjectException | IOException e) {
+ throw new OrmException(e);
+ }
+ }
+
+ @Override
+ public int getCost() {
+ return 5;
+ }
+
+ private Set<RevCommit> getAlreadyAccepted(Repository repo, RevWalk rw)
+ throws IntegrationException {
+ try {
+ Set<RevCommit> accepted = new HashSet<>();
+ SubmitDryRun.addCommits(changeDataCache.getAlreadyAccepted(repo), rw, accepted);
+ ObjectId tip = changeDataCache.getTestAgainst();
+ if (tip != null) {
+ accepted.add(rw.parseCommit(tip));
+ }
+ return accepted;
+ } catch (OrmException | IOException e) {
+ throw new IntegrationException("Failed to determine already accepted commits.", e);
+ }
+ }
}
public static class ChangeDataCache {
@@ -208,7 +195,7 @@
protected ObjectId testAgainst;
protected ProjectState projectState;
- protected Iterable<ObjectId> alreadyAccepted;
+ protected Set<ObjectId> alreadyAccepted;
public ChangeDataCache(
Change change,
@@ -240,7 +227,7 @@
return projectState;
}
- protected Iterable<ObjectId> getAlreadyAccepted(Repository repo) throws IOException {
+ Set<ObjectId> getAlreadyAccepted(Repository repo) throws IOException {
if (alreadyAccepted == null) {
alreadyAccepted = SubmitDryRun.getAlreadyAccepted(repo);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java
deleted file mode 100644
index 28fb7cf..0000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/IsMergePredicate.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2015 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.query.change;
-
-import com.google.gerrit.server.git.CodeReviewCommit;
-import com.google.gerrit.server.query.change.ChangeQueryBuilder.Arguments;
-import com.google.gwtorm.server.OrmException;
-import java.io.IOException;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class IsMergePredicate extends ChangeOperatorPredicate {
- protected final Arguments args;
-
- public IsMergePredicate(Arguments args, String value) {
- super(ChangeQueryBuilder.FIELD_MERGE, value);
- this.args = args;
- }
-
- @Override
- public boolean match(ChangeData cd) throws OrmException {
- ObjectId id = ObjectId.fromString(cd.currentPatchSet().getRevision().get());
- try (Repository repo = args.repoManager.openRepository(cd.change().getProject());
- RevWalk rw = CodeReviewCommit.newRevWalk(repo)) {
- RevCommit commit = rw.parseCommit(id);
- return commit.getParentCount() > 1;
- } catch (IOException e) {
- throw new OrmException(e);
- }
- }
-
- @Override
- public int getCost() {
- return 2;
- }
-}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index b6756c8..3753600 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -25,6 +25,7 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.Accounts;
+import com.google.gerrit.server.account.Emails;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.PatchSetAttribute;
@@ -72,6 +73,7 @@
private final ReviewDb db;
private final AccountCache accountCache;
private final Accounts accounts;
+ private final Emails emails;
private final GitRepositoryManager repoManager;
private final ChangeQueryBuilder queryBuilder;
private final ChangeQueryProcessor queryProcessor;
@@ -98,6 +100,7 @@
ReviewDb db,
AccountCache accountCache,
Accounts accounts,
+ Emails emails,
GitRepositoryManager repoManager,
ChangeQueryBuilder queryBuilder,
ChangeQueryProcessor queryProcessor,
@@ -107,6 +110,7 @@
this.db = db;
this.accountCache = accountCache;
this.accounts = accounts;
+ this.emails = emails;
this.repoManager = repoManager;
this.queryBuilder = queryBuilder;
this.queryProcessor = queryProcessor;
@@ -253,7 +257,7 @@
if (includeSubmitRecords) {
eventFactory.addSubmitRecords(
c,
- new SubmitRuleEvaluator(accountCache, accounts, d)
+ new SubmitRuleEvaluator(accountCache, accounts, emails, d)
.setAllowClosed(true)
.setAllowDraft(true)
.evaluate());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
index 2a74aee..e3b9b3a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -35,7 +35,7 @@
/** A version of the database schema. */
public abstract class SchemaVersion {
/** The current schema version. */
- public static final Class<Schema_156> C = Schema_156.class;
+ public static final Class<Schema_157> C = Schema_157.class;
public static int getBinaryVersion() {
return guessVersion(C);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_157.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_157.java
new file mode 100644
index 0000000..e232e7a
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_157.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.schema;
+
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.jdbc.JdbcSchema;
+import com.google.gwtorm.schema.sql.SqlDialect;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.StatementExecutor;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import java.sql.SQLException;
+
+/** Drop unused indexes from accounts table. */
+public class Schema_157 extends SchemaVersion {
+ @Inject
+ Schema_157(Provider<Schema_156> prior) {
+ super(prior);
+ }
+
+ @Override
+ protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
+ JdbcSchema schema = (JdbcSchema) db;
+ SqlDialect dialect = schema.getDialect();
+ try (StatementExecutor e = newExecutor(db)) {
+ dialect.dropIndex(e, "accounts", "accounts_byPreferredEmail");
+ }
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/ChangeUpdateExecutor.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/ChangeUpdateExecutor.java
index 42199e9..1d957cf 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/ChangeUpdateExecutor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/ChangeUpdateExecutor.java
@@ -17,13 +17,11 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.gerrit.server.git.ReceiveCommits;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.Retention;
/**
- * Marker on the global {@link ListeningExecutorService} used by {@link ReceiveCommits} to create or
- * replace changes.
+ * Marker on the global {@link ListeningExecutorService} used by asynchronous {@link BatchUpdate}s.
*/
@Retention(RUNTIME)
@BindingAnnotation
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java b/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
index 7a1de31..d8a5278 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/update/RetryHelper.java
@@ -14,6 +14,7 @@
package com.google.gerrit.server.update;
+import static com.google.common.base.MoreObjects.firstNonNull;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -21,9 +22,9 @@
import com.github.rholder.retry.RetryListener;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
-import com.github.rholder.retry.StopStrategy;
import com.github.rholder.retry.WaitStrategies;
import com.github.rholder.retry.WaitStrategy;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Throwables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -32,6 +33,7 @@
import com.google.gerrit.server.notedb.NotesMigration;
import com.google.inject.Inject;
import com.google.inject.Singleton;
+import java.time.Duration;
import java.util.concurrent.ExecutionException;
import org.eclipse.jgit.lib.Config;
@@ -41,9 +43,49 @@
T call(BatchUpdate.Factory updateFactory) throws Exception;
}
+ /**
+ * Options for retrying a single operation.
+ *
+ * <p>This class is similar in function to upstream's {@link RetryerBuilder}, but it exists as its
+ * own class in Gerrit for several reasons:
+ *
+ * <ul>
+ * <li>Gerrit needs to support defaults for some of the options, such as a default timeout.
+ * {@code RetryerBuilder} doesn't support calling the same setter multiple times, so doing
+ * this with {@code RetryerBuilder} directly would not be easy.
+ * <li>Gerrit explicitly does not want callers to have full control over all possible options,
+ * so this class exposes a curated subset.
+ * </ul>
+ */
+ @AutoValue
+ public abstract static class Options {
+ @Nullable
+ abstract RetryListener listener();
+
+ @Nullable
+ abstract Duration timeout();
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder listener(RetryListener listener);
+
+ public abstract Builder timeout(Duration timeout);
+
+ public abstract Options build();
+ }
+ }
+
+ public static Options.Builder options() {
+ return new AutoValue_RetryHelper_Options.Builder();
+ }
+
+ public static Options defaults() {
+ return options().build();
+ }
+
private final NotesMigration migration;
private final BatchUpdate.Factory updateFactory;
- private final StopStrategy stopStrategy;
+ private final Duration defaultTimeout;
private final WaitStrategy waitStrategy;
@Inject
@@ -55,33 +97,37 @@
this.migration = migration;
this.updateFactory =
new BatchUpdate.Factory(migration, reviewDbBatchUpdateFactory, noteDbBatchUpdateFactory);
- this.stopStrategy =
- StopStrategies.stopAfterDelay(
- cfg.getTimeUnit("noteDb", null, "retryTimeout", SECONDS.toMillis(5), MILLISECONDS),
- MILLISECONDS);
+ this.defaultTimeout =
+ Duration.ofMillis(
+ cfg.getTimeUnit("noteDb", null, "retryTimeout", SECONDS.toMillis(20), MILLISECONDS));
this.waitStrategy =
WaitStrategies.join(
WaitStrategies.exponentialWait(
- cfg.getTimeUnit("noteDb", null, "retryMaxWait", SECONDS.toMillis(20), MILLISECONDS),
+ cfg.getTimeUnit("noteDb", null, "retryMaxWait", SECONDS.toMillis(5), MILLISECONDS),
MILLISECONDS),
WaitStrategies.randomWait(50, MILLISECONDS));
}
- public <T> T execute(Action<T> action) throws RestApiException, UpdateException {
- return execute(action, null);
+ public Duration getDefaultTimeout() {
+ return defaultTimeout;
}
- public <T> T execute(Action<T> action, @Nullable RetryListener listener)
- throws RestApiException, UpdateException {
+ public <T> T execute(Action<T> action) throws RestApiException, UpdateException {
+ return execute(action, defaults());
+ }
+
+ public <T> T execute(Action<T> action, Options opts) throws RestApiException, UpdateException {
try {
RetryerBuilder<T> builder = RetryerBuilder.newBuilder();
if (migration.disableChangeReviewDb()) {
builder
- .withStopStrategy(stopStrategy)
+ .withStopStrategy(
+ StopStrategies.stopAfterDelay(
+ firstNonNull(opts.timeout(), defaultTimeout).toMillis(), MILLISECONDS))
.withWaitStrategy(waitStrategy)
.retryIfException(RetryHelper::isLockFailure);
- if (listener != null) {
- builder.withRetryListener(listener);
+ if (opts.listener() != null) {
+ builder.withRetryListener(opts.listener());
}
} else {
// Either we aren't full-NoteDb, or the underlying ref storage doesn't support atomic
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/CommentSenderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/CommentSenderTest.java
new file mode 100644
index 0000000..6b6632c
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/send/CommentSenderTest.java
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 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.mail.send;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gwtorm.server.OrmException;
+import java.util.Collections;
+import org.junit.Test;
+
+public class CommentSenderTest {
+ private static class TestSender extends CommentSender {
+ TestSender() throws OrmException {
+ super(null, null, null, null, null);
+ }
+ }
+
+ // A 100-character long string.
+ private static String chars100 = String.join("", Collections.nCopies(25, "abcd"));
+
+ @Test
+ public void shortMessageNotShortened() {
+ String message = "foo bar baz";
+ assertThat(TestSender.getShortenedCommentMessage(message)).isEqualTo(message);
+
+ message = "foo bar baz.";
+ assertThat(TestSender.getShortenedCommentMessage(message)).isEqualTo(message);
+ }
+
+ @Test
+ public void longMessageIsShortened() {
+ String message = chars100 + "x";
+ String expected = chars100 + " […]";
+ assertThat(TestSender.getShortenedCommentMessage(message)).isEqualTo(expected);
+ }
+
+ @Test
+ public void shortenedToFirstLine() {
+ String message = "abc\n" + chars100;
+ String expected = "abc […]";
+ assertThat(TestSender.getShortenedCommentMessage(message)).isEqualTo(expected);
+ }
+
+ @Test
+ public void shortenedToFirstSentence() {
+ String message = "foo bar baz. " + chars100;
+ String expected = "foo bar baz. […]";
+ assertThat(TestSender.getShortenedCommentMessage(message)).isEqualTo(expected);
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/CommitsCollectionTest.java
similarity index 84%
rename from gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java
rename to gerrit-server/src/test/java/com/google/gerrit/server/project/CommitsCollectionTest.java
index 92d7a52..0d8080f 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/ProjectControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/CommitsCollectionTest.java
@@ -55,19 +55,19 @@
import org.junit.Before;
import org.junit.Test;
-/** Unit tests for {@link ProjectControl}. */
-public class ProjectControlTest {
+/** Unit tests for {@link CommitsCollection}. */
+public class CommitsCollectionTest {
@Inject private AccountManager accountManager;
@Inject private IdentifiedUser.GenericFactory userFactory;
@Inject private InMemoryDatabase schemaFactory;
@Inject private InMemoryRepositoryManager repoManager;
- @Inject private ProjectControl.GenericFactory projectControlFactory;
@Inject private SchemaCreator schemaCreator;
@Inject private ThreadLocalRequestContext requestContext;
@Inject protected ProjectCache projectCache;
@Inject protected MetaDataUpdate.Server metaDataUpdateFactory;
@Inject protected AllProjectsName allProjects;
@Inject protected GroupCache groupCache;
+ @Inject private CommitsCollection commits;
private LifecycleManager lifecycle;
private ReviewDb db;
@@ -135,11 +135,11 @@
public void canReadCommitWhenAllRefsVisible() throws Exception {
allow(project, READ, REGISTERED_USERS, "refs/*");
ObjectId id = repo.branch("master").commit().create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id)));
}
@Test
@@ -150,12 +150,12 @@
ObjectId id1 = repo.branch("branch1").commit().create();
ObjectId id2 = repo.branch("branch2").commit().create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id1)));
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id2)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id2)));
}
@Test
@@ -166,12 +166,12 @@
ObjectId id1 = repo.branch("branch1").commit().create();
ObjectId id2 = repo.branch("branch2").commit().create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id1)));
- assertFalse(pc.canReadCommit(db, r, rw.parseCommit(id2)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id1)));
+ assertFalse(commits.canRead(state, r, rw.parseCommit(id2)));
}
@Test
@@ -185,11 +185,11 @@
RevCommit parent2 = repo.commit().create();
repo.branch("branch2").commit().parent(parent2).create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(parent1)));
- assertFalse(pc.canReadCommit(db, r, rw.parseCommit(parent2)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(parent1)));
+ assertFalse(commits.canRead(state, r, rw.parseCommit(parent2)));
}
@Test
@@ -199,16 +199,16 @@
RevCommit parent1 = repo.commit().create();
ObjectId id1 = repo.branch("branch1").commit().parent(parent1).create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(parent1)));
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(parent1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id1)));
repo.branch("branch1").update(parent1);
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(parent1)));
- assertFalse(pc.canReadCommit(db, r, rw.parseCommit(id1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(parent1)));
+ assertFalse(commits.canRead(state, r, rw.parseCommit(id1)));
}
@Test
@@ -218,20 +218,20 @@
RevCommit parent1 = repo.commit().create();
ObjectId id1 = repo.branch("branch1").commit().parent(parent1).create();
- ProjectControl pc = newProjectControl();
+ ProjectState state = readProjectState();
RevWalk rw = repo.getRevWalk();
Repository r = repo.getRepository();
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(parent1)));
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(id1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(parent1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(id1)));
repo.branch("branch1").update(parent1);
- assertTrue(pc.canReadCommit(db, r, rw.parseCommit(parent1)));
- assertFalse(pc.canReadCommit(db, r, rw.parseCommit(id1)));
+ assertTrue(commits.canRead(state, r, rw.parseCommit(parent1)));
+ assertFalse(commits.canRead(state, r, rw.parseCommit(id1)));
}
- private ProjectControl newProjectControl() throws Exception {
- return projectControlFactory.controlFor(project.getName(), user);
+ private ProjectState readProjectState() throws Exception {
+ return projectCache.get(project.getName());
}
protected void allow(ProjectConfig project, String permission, AccountGroup.UUID id, String ref)
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index 9b9cfff..f15227e 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -60,7 +60,6 @@
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.ProjectPermission;
import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gerrit.server.query.change.InternalChangeQuery;
import com.google.gerrit.server.schema.SchemaCreator;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
@@ -212,7 +211,6 @@
@Inject private SingleVersionListener singleVersionListener;
@Inject private InMemoryDatabase schemaFactory;
@Inject private ThreadLocalRequestContext requestContext;
- @Inject private Provider<InternalChangeQuery> queryProvider;
@Inject private ProjectControl.Metrics metrics;
@Before
@@ -899,17 +897,14 @@
}
private ProjectControl user(ProjectConfig local, String name, AccountGroup.UUID... memberOf) {
- String canonicalWebUrl = "http://localhost";
-
return new ProjectControl(
Collections.<AccountGroup.UUID>emptySet(),
Collections.<AccountGroup.UUID>emptySet(),
projectCache,
sectionSorter,
+ null, // commitsCollection
changeControlFactory,
- null, // refFilter
- queryProvider,
- canonicalWebUrl,
+ "http://localhost", // canonicalWebUrl
permissionBackend,
new MockUser(name, memberOf),
newProjectState(local),
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/Schema_150_to_151_Test.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
index 52acd9f..dcd1ae5 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/Schema_150_to_151_Test.java
@@ -21,94 +21,38 @@
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.TopLevelResource;
-import com.google.gerrit.lifecycle.LifecycleManager;
-import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.Id;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.account.AccountManager;
-import com.google.gerrit.server.account.AuthRequest;
import com.google.gerrit.server.group.CreateGroup;
-import com.google.gerrit.server.util.RequestContext;
-import com.google.gerrit.server.util.ThreadLocalRequestContext;
-import com.google.gerrit.testutil.InMemoryDatabase;
-import com.google.gerrit.testutil.InMemoryModule;
+import com.google.gerrit.testutil.SchemaUpgradeTestEnvironment;
+import com.google.gerrit.testutil.TestUpdateUI;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
-import com.google.gwtorm.server.SchemaFactory;
-import com.google.gwtorm.server.StatementExecutor;
-import com.google.inject.Guice;
import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.google.inject.Provider;
-import com.google.inject.util.Providers;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
-import java.util.List;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class Schema_150_to_151_Test {
- @Inject private AccountManager accountManager;
- @Inject private IdentifiedUser.GenericFactory userFactory;
- @Inject private SchemaFactory<ReviewDb> schemaFactory;
- @Inject private SchemaCreator schemaCreator;
- @Inject private ThreadLocalRequestContext requestContext;
- @Inject private Schema_151 schema151;
+
+ @Rule public SchemaUpgradeTestEnvironment testEnv = new SchemaUpgradeTestEnvironment();
+
@Inject private CreateGroup.Factory createGroupFactory;
+ @Inject private Schema_151 schema151;
- // Only for use in setting up/tearing down injector.
- @Inject private InMemoryDatabase inMemoryDatabase;
-
- private LifecycleManager lifecycle;
private ReviewDb db;
@Before
public void setUp() throws Exception {
- Injector injector = Guice.createInjector(new InMemoryModule());
- injector.injectMembers(this);
- lifecycle = new LifecycleManager();
- lifecycle.add(injector);
- lifecycle.start();
-
- try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
- schemaCreator.create(underlyingDb);
- }
- db = schemaFactory.open();
- Account.Id userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
- IdentifiedUser user = userFactory.create(userId);
-
- requestContext.setContext(
- new RequestContext() {
- @Override
- public CurrentUser getUser() {
- return user;
- }
-
- @Override
- public Provider<ReviewDb> getReviewDbProvider() {
- return Providers.of(db);
- }
- });
- }
-
- @After
- public void tearDown() {
- if (lifecycle != null) {
- lifecycle.stop();
- }
- requestContext.setContext(null);
- if (db != null) {
- db.close();
- }
- InMemoryDatabase.drop(inMemoryDatabase);
+ testEnv.getInjector().injectMembers(this);
+ db = testEnv.getDb();
}
@Test
@@ -155,23 +99,4 @@
db.accountGroupMembersAudit().byGroup(groupId);
db.accountGroupMembersAudit().delete(groupMemberAudits);
}
-
- private static class TestUpdateUI implements UpdateUI {
-
- @Override
- public void message(String msg) {}
-
- @Override
- public boolean yesno(boolean def, String msg) {
- return false;
- }
-
- @Override
- public boolean isBatch() {
- return false;
- }
-
- @Override
- public void pruneSchema(StatementExecutor e, List<String> pruneList) throws OrmException {}
- }
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/SchemaUpgradeTestEnvironment.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/SchemaUpgradeTestEnvironment.java
new file mode 100644
index 0000000..060d5a1
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/SchemaUpgradeTestEnvironment.java
@@ -0,0 +1,113 @@
+// 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.testutil;
+
+import com.google.gerrit.lifecycle.LifecycleManager;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.account.AccountManager;
+import com.google.gerrit.server.account.AuthRequest;
+import com.google.gerrit.server.schema.SchemaCreator;
+import com.google.gerrit.server.util.RequestContext;
+import com.google.gerrit.server.util.ThreadLocalRequestContext;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public final class SchemaUpgradeTestEnvironment implements TestRule {
+ @Inject private AccountManager accountManager;
+ @Inject private IdentifiedUser.GenericFactory userFactory;
+ @Inject private SchemaFactory<ReviewDb> schemaFactory;
+ @Inject private SchemaCreator schemaCreator;
+ @Inject private ThreadLocalRequestContext requestContext;
+ // Only for use in setting up/tearing down injector.
+ @Inject private InMemoryDatabase inMemoryDatabase;
+
+ private ReviewDb db;
+ private Injector injector;
+ private LifecycleManager lifecycle;
+
+ @Override
+ public Statement apply(Statement statement, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ try {
+ setUp();
+ statement.evaluate();
+ } finally {
+ tearDown();
+ }
+ }
+ };
+ }
+
+ public ReviewDb getDb() {
+ return db;
+ }
+
+ public Injector getInjector() {
+ return injector;
+ }
+
+ private void setUp() throws Exception {
+ injector = Guice.createInjector(new InMemoryModule());
+ injector.injectMembers(this);
+ lifecycle = new LifecycleManager();
+ lifecycle.add(injector);
+ lifecycle.start();
+
+ try (ReviewDb underlyingDb = inMemoryDatabase.getDatabase().open()) {
+ schemaCreator.create(underlyingDb);
+ }
+ db = schemaFactory.open();
+ Account.Id userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
+ IdentifiedUser user = userFactory.create(userId);
+
+ requestContext.setContext(
+ new RequestContext() {
+ @Override
+ public CurrentUser getUser() {
+ return user;
+ }
+
+ @Override
+ public Provider<ReviewDb> getReviewDbProvider() {
+ return Providers.of(db);
+ }
+ });
+ }
+
+ private void tearDown() {
+ if (lifecycle != null) {
+ lifecycle.stop();
+ }
+ if (requestContext != null) {
+ requestContext.setContext(null);
+ }
+ if (db != null) {
+ db.close();
+ }
+ InMemoryDatabase.drop(inMemoryDatabase);
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/TestUpdateUI.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestUpdateUI.java
new file mode 100644
index 0000000..644f8e2
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/TestUpdateUI.java
@@ -0,0 +1,38 @@
+// 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.testutil;
+
+import com.google.gerrit.server.schema.UpdateUI;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.StatementExecutor;
+import java.util.List;
+
+public class TestUpdateUI implements UpdateUI {
+ @Override
+ public void message(String msg) {}
+
+ @Override
+ public boolean yesno(boolean def, String msg) {
+ return false;
+ }
+
+ @Override
+ public boolean isBatch() {
+ return false;
+ }
+
+ @Override
+ public void pruneSchema(StatementExecutor e, List<String> pruneList) throws OrmException {}
+}
diff --git a/gerrit-sshd/BUILD b/gerrit-sshd/BUILD
index db27f02..1ae0376 100644
--- a/gerrit-sshd/BUILD
+++ b/gerrit-sshd/BUILD
@@ -14,6 +14,7 @@
"//gerrit-lucene:lucene",
"//gerrit-patch-jgit:server",
"//gerrit-reviewdb:server",
+ "//gerrit-server:receive",
"//gerrit-server:server",
"//gerrit-util-cli:cli",
"//lib:args4j",
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 318e67f..58492b2 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
@@ -20,6 +20,7 @@
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;
import java.io.IOException;
@@ -45,6 +46,8 @@
@Inject private IdentifiedUser.GenericFactory userFactory;
protected Repository repo;
+ protected ProjectState state;
+ protected Project.NameKey projectName;
protected Project project;
@Override
@@ -86,10 +89,12 @@
}
private void service() throws IOException, PermissionBackendException, Failure {
- project = projectControl.getProjectState().getProject();
+ state = projectControl.getProjectState();
+ project = state.getProject();
+ projectName = project.getNameKey();
try {
- repo = repoManager.openRepository(project.getNameKey());
+ repo = repoManager.openRepository(projectName);
} catch (RepositoryNotFoundException e) {
throw new Failure(1, "fatal: '" + project.getName() + "': not a git archive", e);
}
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 e64ab0e..0801447 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
@@ -25,6 +25,7 @@
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.ChangesCollection;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.permissions.ChangePermission;
import com.google.gerrit.server.permissions.GlobalPermission;
import com.google.gerrit.server.permissions.PermissionBackend;
import com.google.gerrit.server.permissions.PermissionBackendException;
@@ -68,13 +69,13 @@
}
public void addChange(String id, Map<Change.Id, ChangeResource> changes)
- throws UnloggedFailure, OrmException {
+ throws UnloggedFailure, OrmException, PermissionBackendException {
addChange(id, changes, null);
}
public void addChange(
String id, Map<Change.Id, ChangeResource> changes, ProjectControl projectControl)
- throws UnloggedFailure, OrmException {
+ throws UnloggedFailure, OrmException, PermissionBackendException {
addChange(id, changes, projectControl, true);
}
@@ -83,7 +84,7 @@
Map<Change.Id, ChangeResource> changes,
ProjectControl projectControl,
boolean useIndex)
- throws UnloggedFailure, OrmException {
+ throws UnloggedFailure, OrmException, PermissionBackendException {
List<ChangeControl> matched =
useIndex ? changeFinder.find(id, currentUser) : changeFromNotesFactory(id, currentUser);
List<ChangeControl> toAdd = new ArrayList<>(changes.size());
@@ -97,7 +98,12 @@
for (ChangeControl ctl : matched) {
if (!changes.containsKey(ctl.getId())
&& inProject(projectControl, ctl.getProject())
- && (canMaintainServer || ctl.isVisible(db))) {
+ && (canMaintainServer
+ || permissionBackend
+ .user(currentUser)
+ .change(ctl.getNotes())
+ .database(db)
+ .test(ChangePermission.READ))) {
toAdd.add(ctl);
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
index b911044..748277e 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -24,8 +24,8 @@
import com.google.gerrit.server.RemotePeer;
import com.google.gerrit.server.config.GerritRequestModule;
import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
import com.google.gerrit.server.git.QueueProvider;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
import com.google.gerrit.server.plugins.ModuleGenerator;
import com.google.gerrit.server.plugins.ReloadPluginListener;
import com.google.gerrit.server.plugins.StartPluginListener;
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
index 86209fe..821257c 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/IndexChangesCommand.java
@@ -17,6 +17,7 @@
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.Index;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.sshd.ChangeArgumentParser;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshCommand;
@@ -42,7 +43,7 @@
void addChange(String token) {
try {
changeArgumentParser.addChange(token, changes, null, false);
- } catch (UnloggedFailure | OrmException e) {
+ } catch (UnloggedFailure | OrmException | PermissionBackendException e) {
writeError("warning", e.getMessage());
}
}
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 1fbcc17..aa8c562 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
@@ -14,22 +14,22 @@
package com.google.gerrit.sshd.commands;
+import com.google.common.collect.MultimapBuilder;
+import com.google.common.collect.SetMultimap;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.gerrit.server.git.AsyncReceiveCommits;
-import com.google.gerrit.server.git.ReceiveCommits;
import com.google.gerrit.server.git.VisibleRefFilter;
+import com.google.gerrit.server.git.receive.AsyncReceiveCommits;
+import com.google.gerrit.server.notedb.ReviewerStateInternal;
import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.gerrit.sshd.CommandMetaData;
import com.google.gerrit.sshd.SshSession;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.eclipse.jgit.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.errors.UnpackException;
import org.eclipse.jgit.lib.Ref;
@@ -52,8 +52,8 @@
@Inject private IdentifiedUser currentUser;
@Inject private SshSession session;
- private final Set<Account.Id> reviewerId = new HashSet<>();
- private final Set<Account.Id> ccId = new HashSet<>();
+ private final SetMultimap<ReviewerStateInternal, Account.Id> reviewers =
+ MultimapBuilder.hashKeys(2).hashSetValues().build();
@Option(
name = "--reviewer",
@@ -62,7 +62,7 @@
usage = "request reviewer for change(s)"
)
void addReviewer(Account.Id id) {
- reviewerId.add(id);
+ reviewers.put(ReviewerStateInternal.REVIEWER, id);
}
@Option(
@@ -72,7 +72,7 @@
usage = "CC user on change(s)"
)
void addCC(Account.Id id) {
- ccId.add(id);
+ reviewers.put(ReviewerStateInternal.CC, id);
}
@Override
@@ -81,17 +81,14 @@
throw new Failure(1, "fatal: receive-pack not permitted on this server");
}
- final ReceiveCommits receive = factory.create(projectControl, repo).getReceiveCommits();
+ AsyncReceiveCommits arc = factory.create(projectControl, repo, null, reviewers);
- Capable r = receive.canUpload();
+ Capable r = arc.canUpload();
if (r != Capable.OK) {
throw die(r.getMessage());
}
- receive.init();
- receive.addReviewers(reviewerId);
- receive.addExtraCC(ccId);
- ReceivePack rp = receive.getReceivePack();
+ ReceivePack rp = arc.getReceivePack();
try {
rp.receive(in, out, err);
session.setPeerAgent(rp.getPeerUserAgent());
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 5ea5bf7..026f9b7 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
@@ -23,6 +23,7 @@
import com.google.gerrit.server.change.DeleteReviewer;
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.sshd.ChangeArgumentParser;
import com.google.gerrit.sshd.CommandMetaData;
@@ -79,6 +80,8 @@
throw new IllegalArgumentException(e.getMessage(), e);
} catch (OrmException e) {
throw new IllegalArgumentException("database is down", e);
+ } catch (PermissionBackendException e) {
+ throw new IllegalArgumentException("can't check permissions", e);
}
}
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 093808c..9a3e6ab 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,13 +18,13 @@
import com.google.common.collect.ImmutableMap;
import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.server.ReviewDb;
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;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.permissions.ProjectPermission;
+import com.google.gerrit.server.project.CommitsCollection;
import com.google.gerrit.sshd.AbstractGitCommand;
import com.google.inject.Inject;
import java.io.IOException;
@@ -121,9 +121,9 @@
}
@Inject private PermissionBackend permissionBackend;
+ @Inject private CommitsCollection commits;
@Inject private IdentifiedUser user;
@Inject private AllowedFormats allowedFormats;
- @Inject private ReviewDb db;
private Options options = new Options();
/**
@@ -244,13 +244,13 @@
private boolean canRead(ObjectId revId) throws IOException, PermissionBackendException {
try {
- permissionBackend.user(user).project(project.getNameKey()).check(ProjectPermission.READ);
+ permissionBackend.user(user).project(projectName).check(ProjectPermission.READ);
return true;
} catch (AuthException e) {
// Check reachability of the specific revision.
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(revId);
- return projectControl.canReadCommit(db, repo, commit);
+ return commits.canRead(state, repo, commit);
}
}
}
diff --git a/gerrit-war/BUILD b/gerrit-war/BUILD
index 865f940..f2efb5f 100644
--- a/gerrit-war/BUILD
+++ b/gerrit-war/BUILD
@@ -18,7 +18,9 @@
"//gerrit-pgm:init-api",
"//gerrit-pgm:util",
"//gerrit-reviewdb:server",
+ "//gerrit-server:module",
"//gerrit-server:prolog-common",
+ "//gerrit-server:receive",
"//gerrit-server:server",
"//gerrit-sshd:sshd",
"//lib:guava",
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 cb8860c..2d4c1d1 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
@@ -49,9 +49,9 @@
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.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.git.SearchingChangeCacheImpl;
import com.google.gerrit.server.git.WorkQueue;
+import com.google.gerrit.server.git.receive.ReceiveCommitsExecutorModule;
import com.google.gerrit.server.index.IndexModule;
import com.google.gerrit.server.index.IndexModule.IndexType;
import com.google.gerrit.server.mail.SignedTokenEmailTokenVerifier;
diff --git a/plugins/replication b/plugins/replication
index fae5360..0721b20 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit fae5360380023e8351f39be3d4effd4bb2cd8906
+Subproject commit 0721b208ad863ff2f2c7fe1c89950dc2b921abaa
diff --git a/plugins/reviewnotes b/plugins/reviewnotes
index be803eb..fe99eb4 160000
--- a/plugins/reviewnotes
+++ b/plugins/reviewnotes
@@ -1 +1 @@
-Subproject commit be803eb40fdcd5bfa11d9a808863585f86228e06
+Subproject commit fe99eb4399054d3fd67ff2330aa92841d76e53bb
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
index 3ef1450..4f93e98 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list.js
@@ -127,7 +127,7 @@
},
_readOnly(item) {
- return item.state === 'READ_ONLY' ? 'Y' : 'N';
+ return item.state === 'READ_ONLY' ? 'Y' : '';
},
_computeWeblink(project) {
diff --git a/polygerrit-ui/app/elements/admin/gr-create-project-dialog/gr-create-project-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-project-dialog/gr-create-project-dialog.html
index 19eadce..5381b0e 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-project-dialog/gr-create-project-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-project-dialog/gr-create-project-dialog.html
@@ -60,12 +60,14 @@
</section>
<section>
<span class="title">Rights inherit from</span>
- <gr-autocomplete
- id="rightsInheritFromInput"
- text="{{_projectConfig.parent}}"
- query="[[_query]]"
- placeholder="Optional, defaults to 'All-Projects'">
- </gr-autocomplete>
+ <span class="value">
+ <gr-autocomplete
+ id="rightsInheritFromInput"
+ text="{{_projectConfig.parent}}"
+ query="[[_query]]"
+ placeholder="Optional, defaults to 'All-Projects'">
+ </gr-autocomplete>
+ </span>
</section>
<section>
<span class="title">Create initial empty commit</span>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
index 947bbcb..ee910b4 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.html
@@ -47,10 +47,10 @@
<td class="type">[[itemType(item.type)]]</td>
<td class="member">
<a href$="[[_computeGroupUrl(item.member._account_id)]]">
- [[item.member.name]]
+ [[_getName(item.member)]]
</a>
</td>
- <td class="by-user">[[item.user.username]]</td>
+ <td class="by-user">[[_getName(item.user)]]</td>
</tr>
</template>
</table>
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
index f6a3880..e73e765 100644
--- a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log.js
@@ -73,5 +73,13 @@
}
return item;
},
+
+ _getName(account) {
+ if (account.username) {
+ return account.username + ' (' + account._account_id + ')';
+ } else if (account.name) {
+ return account.name + ' (' + account._account_id + ')';
+ }
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
new file mode 100644
index 0000000..e0cdad2
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-group-audit-log/gr-group-audit-log_test.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-group-audit-log</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-group-audit-log.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-group-audit-log></gr-group-audit-log>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-group-audit-log tests', () => {
+ let element;
+
+ setup(() => {
+ element = fixture('basic');
+ });
+
+ test('test _getName', () => {
+ let account;
+ account = {
+ username: 'test-user',
+ name: 'test-name',
+ _account_id: 12,
+ };
+ assert.deepEqual(element._getName(account), 'test-user (12)');
+
+ account = {
+ name: 'test-name',
+ _account_id: 12,
+ };
+ assert.deepEqual(element._getName(account), 'test-name (12)');
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index e48ac44..4e39304 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -16,13 +16,17 @@
const DEFAULT_SECTIONS = [
{
+ name: 'Work in progress',
+ query: 'is:open owner:self is:wip',
+ },
+ {
name: 'Outgoing reviews',
- query: 'is:open owner:self',
+ query: 'is:open owner:self -is:wip',
},
{
name: 'Incoming reviews',
query: 'is:open ((reviewer:self -owner:self -is:ignored) OR ' +
- 'assignee:self)',
+ 'assignee:self) -is:wip',
},
{
name: 'Recently closed',
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index cb304d8..b1cbe3e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -523,7 +523,7 @@
messages="[[_change.messages]]"
reviewer-updates="[[_change.reviewer_updates]]"
comments="[[_comments]]"
- project-config="[[_projectConfig]]"
+ project-name="[[_change.project]]"
show-reply-buttons="[[_loggedIn]]"
on-reply="_handleMessageReply"></gr-messages-list>
</div>
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
index 9094933..2b7b0fd 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.html
@@ -18,6 +18,7 @@
<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
<link rel="import" href="../../shared/gr-formatted-text/gr-formatted-text.html">
<link rel="import" href="../../../styles/shared-styles.html">
@@ -76,7 +77,7 @@
class="message"
no-trailing-margin
content="[[comment.message]]"
- config="[[projectConfig.commentlinks]]"></gr-formatted-text>
+ config="[[commentLinks]]"></gr-formatted-text>
</div>
</template>
</template>
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
index d8cdb02..703f386 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list.js
@@ -30,7 +30,8 @@
changeNum: Number,
comments: Object,
patchNum: Number,
- projectConfig: Object,
+ commentLinks: Object,
+ projectName: String,
},
_computeFilesFromComments(comments) {
@@ -39,8 +40,8 @@
},
_computeFileDiffURL(file, changeNum, patchNum) {
- return this.getBaseUrl() + '/c/' + changeNum + '/' + patchNum + '/' +
- this.encodeURL(file);
+ return Gerrit.Nav.getUrlForDiffById(this.changeNum, this.projectName,
+ file, patchNum);
},
_computeFileDisplayName(path) {
@@ -57,13 +58,8 @@
},
_computeDiffLineURL(file, changeNum, patchNum, comment) {
- let diffURL = this._computeFileDiffURL(file, changeNum, patchNum);
- if (comment.line) {
- diffURL += '#';
- if (this._isOnParent(comment)) { diffURL += 'b'; }
- diffURL += comment.line;
- }
- return diffURL;
+ return Gerrit.Nav.getUrlForDiffById(this.changeNum, this.projectName,
+ file, patchNum, null, comment.line, this._isOnParent(comment));
},
_computeCommentsForFile(comments, file) {
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index d25664e..ed1ece6 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -60,12 +60,6 @@
assert.deepEqual(element._computeFilesFromComments(null), []);
});
- test('_computeFileDiffURL', () => {
- const expected = '/c/change/patch/file';
- const actual = element._computeFileDiffURL('file', 'change', 'patch');
- assert.equal(actual, expected);
- });
-
test('_computeFileDisplayName', () => {
assert.equal(element._computeFileDisplayName('/COMMIT_MSG'),
'Commit message');
@@ -75,27 +69,6 @@
'/foo/bar/baz');
});
- test('_computeDiffLineURL', () => {
- const comment = {line: 123, side: 'REVISION', patch_set: 10};
- let expected = '/c/change/patch/file#123';
- let actual = element._computeDiffLineURL('file', 'change', 'patch',
- comment);
- assert.equal(actual, expected);
-
- comment.line = 321;
- comment.side = 'PARENT';
-
- expected = '/c/change/patch/file#b321';
- actual = element._computeDiffLineURL('file', 'change', 'patch', comment);
- });
-
- test('_computeDiffLineURL encoding', () => {
- const comment = {line: 123, side: 'REVISION', patch_set: 10};
- const expected = '/c/123/2/x%252By.md#123';
- const actual = element._computeDiffLineURL('x+y.md', '123', '2', comment);
- assert.equal(actual, expected);
- });
-
test('_computePatchDisplayName', () => {
const comment = {line: 123, side: 'REVISION', patch_set: 10};
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.html b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
index d034f16..78f59db 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.html
@@ -152,12 +152,13 @@
<gr-formatted-text
class="message hideOnCollapsed"
content="[[message.message]]"
- config="[[projectConfig.commentlinks]]"></gr-formatted-text>
+ config="[[_commentLinks]]"></gr-formatted-text>
<gr-comment-list
comments="[[comments]]"
change-num="[[changeNum]]"
patch-num="[[message._revision_number]]"
- project-config="[[projectConfig]]"></gr-comment-list>
+ project-name="[[projectName]]"
+ comment-links="[[_commentLinks]]"></gr-comment-list>
</div>
</template>
<template is="dom-if" if="[[_computeIsReviewerUpdate(message)]]">
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message.js b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
index 3433201..2593869 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message.js
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message.js
@@ -74,7 +74,11 @@
type: Boolean,
computed: '_computeShowReplyButton(message, _loggedIn)',
},
- projectConfig: Object,
+ projectName: {
+ type: String,
+ observer: '_projectNameChanged',
+ },
+ _commentLinks: Object,
// Computed property needed to trigger Polymer value observing.
_expanded: {
type: Object,
@@ -240,5 +244,11 @@
return this.getAnonymousName(this.config);
},
+
+ _projectNameChanged(name) {
+ this.$.restAPI.getProjectConfig(name).then(config => {
+ this._commentLinks = config.commentlinks;
+ });
+ },
});
})();
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index 95ca60e..2eed88b 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -95,7 +95,7 @@
message="[[message]]"
comments="[[_computeCommentsForMessage(comments, message)]]"
hide-automated="[[_hideAutomated]]"
- project-config="[[projectConfig]]"
+ project-name="[[projectName]]"
show-reply-button="[[showReplyButtons]]"
on-scroll-to="_handleScrollTo"
data-message-id$="[[message.id]]"></gr-message>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index ebdb4dc..58e56a4 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -36,7 +36,7 @@
value() { return []; },
},
comments: Object,
- projectConfig: Object,
+ projectName: String,
showReplyButtons: {
type: Boolean,
value: false,
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index dd9709d..3fc2c22 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -43,6 +43,10 @@
// - `basePatchNum`, optional, Number, the patch for the left-hand-side
// of the diff. If `basePatchNum` is provided, then `patchNum` must
// also be provided.
+ // - `lineNum`, optional, Number, the line number to be selected on load.
+ // - `leftSide`, optional, Boolean, if a `lineNum` is provided, a value
+ // of true selects the line from base of the patch range. False by
+ // default.
window.Gerrit = window.Gerrit || {};
@@ -166,9 +170,9 @@
/**
* @param {!Object} change The change object.
- * @param {number} opt_patchNum
- * @param {number|string} opt_basePatchNum The string 'PARENT' can be used
- * for none.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
* @return {string}
*/
getUrlForChange(change, opt_patchNum, opt_basePatchNum) {
@@ -187,9 +191,9 @@
/**
* @param {!Object} change The change object.
- * @param {number} opt_patchNum
- * @param {number|string} opt_basePatchNum The string 'PARENT' can be used
- * for none.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
* @return {string}
*/
navigateToChange(change, opt_patchNum, opt_basePatchNum) {
@@ -199,13 +203,30 @@
/**
* @param {!Object} change The change object.
- * @param {!String} path The file path.
- * @param {number} opt_patchNum
- * @param {number|string} opt_basePatchNum The string 'PARENT' can be used
- * for none.
+ * @param {!string} path The file path.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
* @return {string}
*/
getUrlForDiff(change, path, opt_patchNum, opt_basePatchNum) {
+ return this.getUrlForDiffById(change._number, change.project, path,
+ opt_patchNum, opt_basePatchNum);
+ },
+
+ /**
+ * @param {!number} change The change object.
+ * @param {!string} projectName The name of the project.
+ * @param {!string} path The file path.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
+ * @param {number=} opt_lineNum
+ * @param {boolean=} opt_leftSide
+ * @return {string}
+ */
+ getUrlForDiffById(changeNum, projectName, path, opt_patchNum,
+ opt_basePatchNum, opt_lineNum, opt_leftSide) {
if (opt_basePatchNum === PARENT_PATCHNUM) {
opt_basePatchNum = undefined;
}
@@ -213,19 +234,22 @@
this._checkPatchRange(opt_patchNum, opt_basePatchNum);
return this._getUrlFor({
view: Gerrit.Nav.View.DIFF,
- changeNum: change._number,
+ changeNum,
+ projectName,
path,
patchNum: opt_patchNum,
basePatchNum: opt_basePatchNum,
+ lineNum: opt_lineNum,
+ leftSide: opt_leftSide,
});
},
/**
* @param {!Object} change The change object.
- * @param {!String} path The file path.
- * @param {number} opt_patchNum
- * @param {number|string} opt_basePatchNum The string 'PARENT' can be used
- * for none.
+ * @param {!string} path The file path.
+ * @param {number=} opt_patchNum
+ * @param {number|string=} opt_basePatchNum The string 'PARENT' can be
+ * used for none.
*/
navigateToDiff(change, path, opt_patchNum, opt_basePatchNum) {
this._navigate(this.getUrlForDiff(change, path, opt_patchNum,
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index f290dd2..f393445 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -580,6 +580,12 @@
if (range.length) { range = '/' + range; }
url = `/c/${params.changeNum}${range}/${encode(params.path, true)}`;
+
+ if (params.lineNum) {
+ url += '#';
+ if (params.leftSide) { url += 'b'; }
+ url += params.lineNum;
+ }
} else {
throw new Error('Can\'t generate');
}
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
index ff1a0ca..92d0fde 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.html
@@ -93,6 +93,13 @@
delete params.basePatchNum;
assert.equal(element._generateUrl(params),
'/c/42/2/foo+bar/my%252Bfile.txt%2525');
+
+ params.path = 'file.cpp';
+ params.lineNum = 123;
+ assert.equal(element._generateUrl(params), '/c/42/2/file.cpp#123');
+
+ params.leftSide = true;
+ assert.equal(element._generateUrl(params), '/c/42/2/file.cpp#b123');
});
});
});
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
index ca385c2..a1c80ae 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
@@ -37,8 +37,12 @@
width: 100%;
will-change: top;
}
+ .fixedAtTop {
+ border-bottom: 1px solid #a4a4a4;
+ box-shadow: 0 4px 4px rgba(0,0,0,0.1);
+ }
</style>
- <header id="header" class$="[[_computeHeaderClass(_headerFloating)]]">
+ <header id="header" class$="[[_computeHeaderClass(_headerFloating, _topLast)]]">
<content></content>
</header>
</template>
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
index dfbaa8a..f4f1a93 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
@@ -31,8 +31,17 @@
type: Boolean,
value: false,
},
+
+ /**
+ * Initial offset from the top of the document, in pixels.
+ */
_topInitial: Number,
+
+ /**
+ * Current offset from the top of the window, in pixels.
+ */
_topLast: Number,
+
_headerHeight: Number,
_headerFloating: {
type: Boolean,
@@ -73,8 +82,12 @@
}
},
- _computeHeaderClass(headerFloating) {
- return headerFloating ? 'floating' : '';
+ _computeHeaderClass(headerFloating, topLast) {
+ const fixedAtTop = this.keepOnScroll && topLast === 0;
+ return [
+ headerFloating ? 'floating' : '',
+ fixedAtTop ? 'fixedAtTop' : '',
+ ].join(' ');
},
_getScrollY() {
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
index d813a44..408b7c9 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
@@ -98,6 +98,14 @@
emulateScrollY(120);
assert.equal(getHeaderTop(), '0px');
});
+
+ test('drops a shadow when fixed to the top', () => {
+ element.keepOnScroll = true;
+ emulateScrollY(5);
+ assert.isFalse(element.$.header.classList.contains('fixedAtTop'));
+ emulateScrollY(120);
+ assert.isTrue(element.$.header.classList.contains('fixedAtTop'));
+ });
});
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js
new file mode 100644
index 0000000..e99ea20
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth.js
@@ -0,0 +1,172 @@
+// 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.
+(function(window) {
+ 'use strict';
+
+ // Prevent redefinition.
+ if (window.Gerrit.Auth) { return; }
+
+ const MAX_GET_TOKEN_RETRIES = 2;
+
+ Gerrit.Auth = {
+ TYPE: {
+ XSRF_TOKEN: 'xsrf_token',
+ ACCESS_TOKEN: 'access_token',
+ },
+
+ _type: null,
+ _cachedTokenPromise: null,
+ _defaultOptions: {},
+ _retriesLeft: MAX_GET_TOKEN_RETRIES,
+
+ _getToken() {
+ return Promise.resolve(this._cachedTokenPromise);
+ },
+
+ /**
+ * Enable cross-domain authentication using OAuth access token.
+ *
+ * @param {
+ * function(): !Promise<{
+ * access_token: string,
+ * expires_at: number
+ * }>
+ * } getToken
+ * @param {?{credentials:string}} defaultOptions
+ */
+ setup(getToken, defaultOptions) {
+ this._retriesLeft = MAX_GET_TOKEN_RETRIES;
+ if (getToken) {
+ this._type = Gerrit.Auth.TYPE.ACCESS_TOKEN;
+ this._cachedTokenPromise = null;
+ this._getToken = getToken;
+ }
+ this._defaultOptions = {};
+ if (defaultOptions) {
+ for (const p of ['credentials']) {
+ this._defaultOptions[p] = defaultOptions[p];
+ }
+ }
+ },
+
+ /**
+ * Perform network fetch with authentication.
+ *
+ * @param {string} url
+ * @param {Object=} opt_options
+ * @return {!Promise<!Response>}
+ */
+ fetch(url, opt_options) {
+ const options = Object.assign({}, this._defaultOptions, opt_options);
+ if (this._type === Gerrit.Auth.TYPE.ACCESS_TOKEN) {
+ return this._getAccessToken().then(
+ accessToken => this._fetchWithAccessToken(url, options, accessToken)
+ );
+ } else {
+ return this._fetchWithXsrfToken(url, options);
+ }
+ },
+
+ _getCookie(name) {
+ const key = name + '=';
+ let result = '';
+ document.cookie.split(';').some(c => {
+ c = c.trim();
+ if (c.startsWith(key)) {
+ result = c.substring(key.length);
+ return true;
+ }
+ });
+ return result;
+ },
+
+ _isTokenValid(token) {
+ if (!token) { return false; }
+ if (!token.access_token || !token.expires_at) { return false; }
+
+ const expiration = new Date(parseInt(token.expires_at, 10) * 1000);
+ if (Date.now() >= expiration.getTime()) { return false; }
+
+ return true;
+ },
+
+ _fetchWithXsrfToken(url, options) {
+ if (options.method && options.method !== 'GET') {
+ const token = this._getCookie('XSRF_TOKEN');
+ if (token) {
+ options.headers = options.headers || new Headers();
+ options.headers.append('X-Gerrit-Auth', token);
+ }
+ }
+ options.credentials = 'same-origin';
+ return fetch(url, options);
+ },
+
+ /**
+ * @return {!Promise<string>}
+ */
+ _getAccessToken() {
+ if (!this._cachedTokenPromise) {
+ this._cachedTokenPromise = this._getToken();
+ }
+ return this._cachedTokenPromise.then(token => {
+ if (this._isTokenValid(token)) {
+ this._retriesLeft = MAX_GET_TOKEN_RETRIES;
+ return token.access_token;
+ }
+ if (this._retriesLeft > 0) {
+ this._retriesLeft--;
+ this._cachedTokenPromise = null;
+ return this._getAccessToken();
+ }
+ // Fall back to anonymous access.
+ return null;
+ });
+ },
+
+ _fetchWithAccessToken(url, options, accessToken) {
+ const method = options.method || 'GET';
+ const params = [];
+ if (accessToken) {
+ params.push(`access_token=${accessToken}`);
+ const baseUrl = Gerrit.BaseUrlBehavior.getBaseUrl();
+ const pathname = baseUrl ?
+ url.substring(url.indexOf(baseUrl) + baseUrl.length) : url;
+ if (!pathname.startsWith('/a/')) {
+ url = url.replace(pathname, '/a' + pathname);
+ }
+ }
+ let contentType = options.headers && options.headers.get('Content-Type');
+ if (contentType) {
+ options.headers.set('Content-Type', 'text/plain');
+ }
+ if (method !== 'GET') {
+ options.method = 'POST';
+ params.push(`$m=${method}`);
+ if (!contentType) {
+ contentType = 'application/json';
+ }
+ }
+ if (contentType) {
+ params.push(`$ct=${encodeURIComponent(contentType)}`);
+ }
+ if (params.length) {
+ url = url + (url.indexOf('?') === -1 ? '?' : '&') + params.join('&');
+ }
+ return fetch(url, options);
+ },
+ };
+
+ window.Gerrit.Auth = Gerrit.Auth;
+})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
new file mode 100644
index 0000000..7bbc0ca
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-auth_test.html
@@ -0,0 +1,171 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-auth</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
+
+<script src="gr-auth.js"></script>
+
+<script>
+ suite('gr-auth', () => {
+ let auth;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
+ auth = Gerrit.Auth;
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ suite('default (xsrf token header)', () => {
+ test('GET', () => {
+ return auth.fetch('/url', {bar: 'bar'}).then(() => {
+ const [url, options] = fetch.lastCall.args;
+ assert.equal(url, '/url');
+ assert.equal(options.credentials, 'same-origin');
+ });
+ });
+
+ test('POST', () => {
+ sandbox.stub(auth, '_getCookie')
+ .withArgs('XSRF_TOKEN')
+ .returns('foobar');
+ return auth.fetch('/url', {method: 'POST'}).then(() => {
+ const [url, options] = fetch.lastCall.args;
+ assert.equal(url, '/url');
+ assert.equal(options.credentials, 'same-origin');
+ assert.equal(options.headers.get('X-Gerrit-Auth'), 'foobar');
+ });
+ });
+ });
+
+ suite('cors (access token)', () => {
+ let getToken;
+
+ const makeToken = opt_accessToken => {
+ return {
+ access_token: opt_accessToken || 'zbaz',
+ expires_at: new Date(Date.now() + 10e8).getTime(),
+ };
+ };
+
+ setup(() => {
+ getToken = sandbox.stub();
+ getToken.returns(Promise.resolve(makeToken()));
+ auth.setup(getToken);
+ });
+
+ test('base url support', () => {
+ const baseUrl = 'http://foo';
+ sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
+ return auth.fetch(baseUrl + '/url', {bar: 'bar'}).then(() => {
+ const [url] = fetch.lastCall.args;
+ assert.equal(url, 'http://foo/a/url?access_token=zbaz');
+ });
+ });
+
+ test('fetch not signed in', () => {
+ getToken.returns(Promise.resolve());
+ return auth.fetch('/url', {bar: 'bar'}).then(() => {
+ const [url, options] = fetch.lastCall.args;
+ assert.equal(url, '/url');
+ assert.equal(options.bar, 'bar');
+ assert.isUndefined(options.headers);
+ });
+ });
+
+ test('fetch signed in', () => {
+ return auth.fetch('/url', {bar: 'bar'}).then(() => {
+ const [url, options] = fetch.lastCall.args;
+ assert.equal(url, '/a/url?access_token=zbaz');
+ assert.equal(options.bar, 'bar');
+ });
+ });
+
+ test('getToken calls are cached', () => {
+ return Promise.all([
+ auth.fetch('/url-one'), auth.fetch('/url-two')]).then(() => {
+ assert.equal(getToken.callCount, 1);
+ });
+ });
+
+ test('getToken refreshes token', () => {
+ sandbox.stub(auth, '_isTokenValid');
+ auth._isTokenValid
+ .onFirstCall().returns(true)
+ .onSecondCall().returns(false)
+ .onThirdCall().returns(true);
+ return auth.fetch('/url-one').then(() => {
+ getToken.returns(Promise.resolve(makeToken('bzzbb')));
+ return auth.fetch('/url-two');
+ }).then(() => {
+ const [[firstUrl], [secondUrl]] = fetch.args;
+ assert.equal(firstUrl, '/a/url-one?access_token=zbaz');
+ assert.equal(secondUrl, '/a/url-two?access_token=bzzbb');
+ });
+ });
+
+ test('signed in token error falls back to anonymous', () => {
+ getToken.returns(Promise.resolve('rubbish'));
+ return auth.fetch('/url', {bar: 'bar'}).then(() => {
+ const [url, options] = fetch.lastCall.args;
+ assert.equal(url, '/url');
+ assert.equal(options.bar, 'bar');
+ });
+ });
+
+ test('_isTokenValid', () => {
+ assert.isFalse(auth._isTokenValid());
+ assert.isFalse(auth._isTokenValid({}));
+ assert.isFalse(auth._isTokenValid({access_token: 'foo'}));
+ assert.isFalse(auth._isTokenValid({
+ access_token: 'foo',
+ expires_at: Date.now()/1000 - 1,
+ }));
+ assert.isTrue(auth._isTokenValid({
+ access_token: 'foo',
+ expires_at: Date.now()/1000 + 1,
+ }));
+ });
+
+ test('HTTP PUT', () => {
+ const originalOptions = {
+ method: 'PUT',
+ headers: new Headers({'Content-Type': 'mail/pigeon'}),
+ };
+ return auth.fetch('/url', originalOptions).then(() => {
+ assert.isTrue(getToken.called);
+ const [url, options] = fetch.lastCall.args;
+ assert.include(url, '$ct=mail%2Fpigeon');
+ assert.include(url, '$m=PUT');
+ assert.include(url, 'access_token=zbaz');
+ assert.equal(options.method, 'POST');
+ assert.equal(options.headers.get('Content-Type'), 'text/plain');
+ });
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth.js
deleted file mode 100644
index 8cf2096..0000000
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth.js
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-(function(window) {
- 'use strict';
-
- // Prevent redefinition.
- if (window.GrGapiAuth) { return; }
-
- const EMAIL_SCOPE = 'email';
-
- function GrGapiAuth() {}
-
- GrGapiAuth._loadGapiPromise = null;
- GrGapiAuth._setupPromise = null;
- GrGapiAuth._refreshTokenPromise = null;
- GrGapiAuth._sharedAuthToken = null;
- GrGapiAuth._oauthClientId = null;
- GrGapiAuth._oauthEmail = null;
-
- GrGapiAuth.prototype.fetch = function(url, options) {
- options = Object.assign({}, options);
- return this._getAccessToken().then(
- token => window.FASTER_GERRIT_CORS ?
- this._fasterGerritCors(url, options, token) :
- this._defaultFetch(url, options, token)
- );
- };
-
- GrGapiAuth.prototype._defaultFetch = function(url, options, token) {
- if (token) {
- options.headers = options.headers || new Headers();
- options.headers.append('Authorization', `Bearer ${token}`);
- const baseUrl = Gerrit.BaseUrlBehavior.getBaseUrl();
- const pathname = baseUrl ?
- url.substring(url.indexOf(baseUrl) + baseUrl.length) : url;
- if (!pathname.startsWith('/a/')) {
- url = url.replace(pathname, '/a' + pathname);
- }
- }
- return fetch(url, options);
- };
-
- GrGapiAuth.prototype._fasterGerritCors = function(url, options, token) {
- const method = options.method || 'GET';
- if (method === 'GET') {
- return fetch(url, options);
- }
- const params = [];
- if (token) {
- params.push(`access_token=${token}`);
- }
- const contentType = options.headers && options.headers.get('Content-Type');
- if (contentType) {
- options.headers.set('Content-Type', 'text/plain');
- params.push(`$ct=${encodeURIComponent(contentType)}`);
- }
- params.push(`$m=${method}`);
- url = url + (url.indexOf('?') === -1 ? '?' : '') + params.join('&');
- options.method = 'POST';
- return fetch(url, options);
- };
-
- GrGapiAuth.prototype._getAccessToken = function() {
- if (this._isTokenValid(GrGapiAuth._sharedAuthToken)) {
- return Promise.resolve(GrGapiAuth._sharedAuthToken.access_token);
- }
- if (!GrGapiAuth._refreshTokenPromise) {
- GrGapiAuth._refreshTokenPromise = this._loadGapi()
- .then(() => this._configureOAuthLibrary())
- .then(() => this._refreshToken())
- .then(token => {
- GrGapiAuth._sharedAuthToken = token;
- GrGapiAuth._refreshTokenPromise = null;
- return this._getAccessToken();
- }).catch(err => {
- console.error(err);
- });
- }
- return GrGapiAuth._refreshTokenPromise;
- };
-
- GrGapiAuth.prototype._isTokenValid = function(token) {
- if (!token) { return false; }
- if (!token.access_token || !token.expires_at) { return false; }
-
- const expiration = new Date(parseInt(token.expires_at, 10) * 1000);
- if (Date.now() >= expiration.getTime()) { return false; }
-
- return true;
- };
-
- GrGapiAuth.prototype._loadGapi = function() {
- if (!GrGapiAuth._loadGapiPromise) {
- GrGapiAuth._loadGapiPromise = new Promise((resolve, reject) => {
- const scriptEl = document.createElement('script');
- scriptEl.defer = true;
- scriptEl.async = true;
- scriptEl.src = 'https://apis.google.com/js/platform.js';
- scriptEl.onerror = reject;
- scriptEl.onload = resolve;
- document.body.appendChild(scriptEl);
- });
- }
- return GrGapiAuth._loadGapiPromise;
- };
-
- GrGapiAuth.prototype._configureOAuthLibrary = function() {
- if (!GrGapiAuth._setupPromise) {
- GrGapiAuth._setupPromise = new Promise(
- resolve => gapi.load('config_min', resolve)
- )
- .then(() => this._getOAuthConfig())
- .then(config => {
- if (config.hasOwnProperty('auth_url') && config.auth_url) {
- gapi.config.update('oauth-flow/authUrl', config.auth_url);
- }
- if (config.hasOwnProperty('proxy_url') && config.proxy_url) {
- gapi.config.update('oauth-flow/proxyUrl', config.proxy_url);
- }
- GrGapiAuth._oauthClientId = config.client_id;
- GrGapiAuth._oauthEmail = config.email;
-
- // Loading auth has a side-effect. The URLs should be set before
- // loading it.
- return new Promise(
- resolve => gapi.load('auth', () => gapi.auth.init(resolve))
- );
- });
- }
- return GrGapiAuth._setupPromise;
- };
-
- GrGapiAuth.prototype._refreshToken = function() {
- const opts = {
- client_id: GrGapiAuth._oauthClientId,
- immediate: true,
- scope: EMAIL_SCOPE,
- login_hint: GrGapiAuth._oauthEmail,
- };
- return new Promise((resolve, reject) => {
- gapi.auth.authorize(opts, token => {
- if (!token) {
- reject('No token returned');
- } else if (token.error) {
- reject(token.error);
- } else {
- resolve(token);
- }
- });
- });
- };
-
- GrGapiAuth.prototype._getOAuthConfig = function() {
- const baseUrl = Gerrit.BaseUrlBehavior.getBaseUrl();
- const authConfigURL = baseUrl + '/accounts/self/oauthconfig';
- const opts = {
- headers: new Headers({Accept: 'application/json'}),
- credentials: 'same-origin',
- };
- return fetch(authConfigURL, opts).then(response => {
- if (!response.ok) {
- console.error(response.statusText);
- if (response.body && response.body.then) {
- return response.body.then(text => {
- return Promise.reject(text);
- });
- }
- if (response.statusText) {
- return Promise.reject(response.statusText);
- } else {
- return Promise.reject('_getOAuthConfig' + response.status);
- }
- }
- return this._getResponseObject(response);
- });
- };
-
- GrGapiAuth.prototype._getResponseObject = function(response) {
- const JSON_PREFIX = ')]}\'';
- return response.text().then(text => {
- return JSON.parse(text.substring(JSON_PREFIX.length));
- });
- },
-
- window.GrGapiAuth = GrGapiAuth;
-})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth_test.html
deleted file mode 100644
index f5c0d18..0000000
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gapi-auth_test.html
+++ /dev/null
@@ -1,218 +0,0 @@
-<!DOCTYPE html>
-<!--
-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.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-gapi-auth</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
-<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
-
-<script src="gr-gapi-auth.js"></script>
-
-<script>
- suite('gr-rest-api-interface tests', () => {
- let auth;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- auth = new GrGapiAuth();
- window.gapi = {
- load: sandbox.stub().callsArg(1),
- config: {
- update: sandbox.stub(),
- },
- auth: {
- init: sandbox.stub().callsArg(0),
- authorize: sandbox.stub(),
- },
- };
- sandbox.stub(window, 'fetch').returns(Promise.resolve({ok: true}));
- });
-
- teardown(() => {
- delete window.gapi;
- sandbox.restore();
- });
-
- test('exists', () => {
- assert.isOk(auth);
- });
-
- test('base url support', () => {
- const baseUrl = 'http://foo';
- sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns(baseUrl);
- sandbox.stub(auth, '_getAccessToken').returns(Promise.resolve('baz'));
- return auth.fetch(baseUrl + '/url', {bar: 'bar'}).then(() => {
- const [url] = fetch.lastCall.args;
- assert.equal(url, 'http://foo/a/url');
- });
- });
-
- test('fetch signed in', () => {
- sandbox.stub(auth, '_getAccessToken').returns(Promise.resolve('foo'));
- return auth.fetch('/url', {bar: 'bar'}).then(() => {
- assert.isTrue(auth._getAccessToken.called);
- const [url, options] = fetch.lastCall.args;
- assert.equal(url, '/a/url');
- assert.equal(options.bar, 'bar');
- assert.equal(options.headers.get('Authorization'), 'Bearer foo');
- });
- });
-
- test('fetch not signed in', () => {
- sandbox.stub(auth, '_getAccessToken').returns(Promise.resolve());
- return auth.fetch('/url', {bar: 'bar'}).then(() => {
- assert.isTrue(auth._getAccessToken.called);
- const [url, options] = fetch.lastCall.args;
- assert.equal(url, '/url');
- assert.equal(options.bar, 'bar');
- assert.isUndefined(options.headers);
- });
- });
-
- test('_getAccessToken returns valid shared token', () => {
- GrGapiAuth._sharedAuthToken = {access_token: 'foo'};
- sandbox.stub(auth, '_isTokenValid').returns(true);
- return auth._getAccessToken().then(token => {
- assert.equal(token, 'foo');
- });
- });
-
- test('_getAccessToken refreshes token', () => {
- const token = {access_token: 'foo'};
- sandbox.stub(auth, '_loadGapi').returns(Promise.resolve());
- sandbox.stub(auth, '_configureOAuthLibrary').returns(Promise.resolve());
- sandbox.stub(auth, '_refreshToken').returns(Promise.resolve(token));
- sandbox.stub(auth, '_isTokenValid').returns(true)
- .onFirstCall().returns(false);
- return auth._getAccessToken().then(token => {
- assert.isTrue(auth._loadGapi.called);
- assert.isTrue(auth._configureOAuthLibrary.called);
- assert.isTrue(auth._refreshToken.called);
- assert.equal(token, 'foo');
- });
- });
-
- test('_isTokenValid', () => {
- assert.isFalse(auth._isTokenValid());
- assert.isFalse(auth._isTokenValid({}));
- assert.isFalse(auth._isTokenValid({access_token: 'foo'}));
- assert.isFalse(auth._isTokenValid({
- access_token: 'foo',
- expires_at: Date.now()/1000 - 1,
- }));
- assert.isTrue(auth._isTokenValid({
- access_token: 'foo',
- expires_at: Date.now()/1000 + 1,
- }));
- });
-
- test('_configureOAuthLibrary', () => {
- sandbox.stub(auth, '_getOAuthConfig').returns({
- auth_url: 'some_auth_url',
- proxy_url: 'some_proxy_url',
- client_id: 'some_client_id',
- email: 'some_email',
- });
- return auth._configureOAuthLibrary().then(() => {
- assert.isTrue(gapi.load.calledWith('config_min'));
- assert.isTrue(auth._getOAuthConfig.called);
- assert.isTrue(gapi.config.update.calledWith(
- 'oauth-flow/authUrl', 'some_auth_url'));
- assert.isTrue(gapi.config.update.calledWith(
- 'oauth-flow/proxyUrl', 'some_proxy_url'));
- assert.equal(GrGapiAuth._oauthClientId, 'some_client_id');
- assert.equal(GrGapiAuth._oauthEmail, 'some_email');
- assert.isTrue(gapi.auth.init.called);
- assert.isTrue(gapi.load.calledWith('auth'));
- });
- });
-
- test('_refreshToken no token', () => {
- gapi.auth.authorize.callsArgWith(1, null);
- return auth._refreshToken().catch(reason => {
- assert.equal(reason, 'No token returned');
- });
- });
-
- test('_refreshToken error', () => {
- gapi.auth.authorize.callsArgWith(1, {error: 'some error'});
- return auth._refreshToken().catch(reason => {
- assert.equal(reason, 'some error');
- });
- });
-
- test('_refreshToken', () => {
- const token = {};
- gapi.auth.authorize.callsArgWith(1, token);
- return auth._refreshToken().then(t => {
- assert.strictEqual(token, t);
- });
- });
-
- test('_getOAuthConfig', () => {
- const config = {};
- sandbox.stub(auth, '_getResponseObject').returns(config);
- return auth._getOAuthConfig().then(c => {
- const [url, options] = fetch.lastCall.args;
- assert.equal(url, '/accounts/self/oauthconfig');
- assert.equal(options.credentials, 'same-origin');
- assert.equal(options.headers.get('Accept'), 'application/json');
- assert.strictEqual(c, config);
- });
- });
-
- test('BaseUrlBehavior', () => {
- sandbox.stub(Gerrit.BaseUrlBehavior, 'getBaseUrl').returns('http://foo');
- sandbox.stub(auth, '_getResponseObject').returns({});
- return auth._getOAuthConfig().then(c => {
- const [url] = fetch.lastCall.args;
- assert.equal(url, 'http://foo/accounts/self/oauthconfig');
- });
- });
-
- suite('faster gerrit cors', () => {
- setup(() => {
- window.FASTER_GERRIT_CORS = true;
- });
-
- teardown(() => {
- delete window.FASTER_GERRIT_CORS;
- });
-
- test('PUT works', () => {
- sandbox.stub(auth, '_getAccessToken').returns(Promise.resolve('foo'));
- const originalOptions = {
- method: 'PUT',
- headers: new Headers({'Content-Type': 'mail/pigeon'}),
- };
- return auth.fetch('/url', originalOptions).then(() => {
- assert.isTrue(auth._getAccessToken.called);
- const [url, options] = fetch.lastCall.args;
- assert.include(url, '$ct=mail%2Fpigeon');
- assert.include(url, '$m=PUT');
- assert.include(url, 'access_token=foo');
- assert.equal(options.method, 'POST');
- assert.equal(options.headers.get('Content-Type'), 'text/plain');
- });
- });
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gerrit-auth.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gerrit-auth.js
deleted file mode 100644
index b70790d..0000000
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-gerrit-auth.js
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-(function(window) {
- 'use strict';
-
- // Prevent redefinition.
- if (window.GrGerritAuth) { return; }
-
- function GrGerritAuth() {}
-
- GrGerritAuth.prototype._getCookie = function(name) {
- const key = name + '=';
- let result = '';
- document.cookie.split(';').some(c => {
- c = c.trim();
- if (c.startsWith(key)) {
- result = c.substring(key.length);
- return true;
- }
- });
- return result;
- };
-
- GrGerritAuth.prototype.fetch = function(url, opt_options) {
- const options = Object.assign({}, opt_options);
- if (options.method && options.method !== 'GET') {
- const token = this._getCookie('XSRF_TOKEN');
- if (token) {
- options.headers = options.headers || new Headers();
- options.headers.append('X-Gerrit-Auth', token);
- }
- }
- options.credentials = 'same-origin';
- return fetch(url, options);
- };
-
- window.GrGerritAuth = GrGerritAuth;
-})(window);
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index b8ed52a..5afed95 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -26,8 +26,7 @@
<dom-module id="gr-rest-api-interface">
<!-- NB: Order is important, because of namespaced classes. -->
- <script src="gr-gerrit-auth.js"></script>
- <script src="gr-gapi-auth.js"></script>
+ <script src="gr-auth.js"></script>
<script src="gr-reviewer-updates-parser.js"></script>
<script src="gr-rest-api-interface.js"></script>
</dom-module>
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 f20255b..9317425 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
@@ -30,8 +30,6 @@
SEND_DIFF_DRAFT: 'sendDiffDraft',
};
- let auth = null;
-
Polymer({
is: 'gr-rest-api-interface',
@@ -82,14 +80,14 @@
type: Object,
value: {}, // Intentional to share the object across instances.
},
+ _auth: {
+ type: Object,
+ value: Gerrit.Auth, // Share across instances.
+ },
},
JSON_PREFIX,
- created() {
- auth = window.USE_GAPI_AUTH ? new GrGapiAuth() : new GrGerritAuth();
- },
-
/**
* Fetch JSON from url provided.
* Returns a Promise that resolves to a native Response.
@@ -104,7 +102,7 @@
_fetchRawJSON(url, opt_errFn, opt_cancelCondition, opt_params,
opt_options) {
const urlWithParams = this._urlWithParams(url, opt_params);
- return auth.fetch(urlWithParams, opt_options).then(response => {
+ return this._auth.fetch(urlWithParams, opt_options).then(response => {
if (opt_cancelCondition && opt_cancelCondition()) {
response.body.cancel();
return;
@@ -984,23 +982,24 @@
}
options.body = opt_body;
}
- return auth.fetch(this.getBaseUrl() + url, options).then(response => {
- if (!response.ok) {
- if (opt_errFn) {
- return opt_errFn.call(opt_ctx || null, response);
- }
- this.fire('server-error', {response});
- }
+ return this._auth.fetch(this.getBaseUrl() + url, options)
+ .then(response => {
+ if (!response.ok) {
+ if (opt_errFn) {
+ return opt_errFn.call(opt_ctx || null, response);
+ }
+ this.fire('server-error', {response});
+ }
- return response;
- }).catch(err => {
- this.fire('network-error', {error: err});
- if (opt_errFn) {
- return opt_errFn.call(opt_ctx, null, err);
- } else {
- throw err;
- }
- });
+ return response;
+ }).catch(err => {
+ this.fire('network-error', {error: err});
+ if (opt_errFn) {
+ return opt_errFn.call(opt_ctx, null, err);
+ } else {
+ throw err;
+ }
+ });
},
getDiff(changeNum, basePatchNum, patchNum, path,
@@ -1187,7 +1186,7 @@
},
_fetchB64File(url) {
- return auth.fetch(this.getBaseUrl() + url)
+ return this._auth.fetch(this.getBaseUrl() + url)
.then(response => {
if (!response.ok) { return Promise.reject(response.statusText); }
const type = response.headers.get('X-FYI-Content-Type');
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 8248d5b..2cda640 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
@@ -736,19 +736,10 @@
.calledWithExactly('/groups/?n=26&S=0&r=%5Etest.*'));
});
- test('gerrit auth is used by default', () => {
- sandbox.stub(GrGerritAuth.prototype, 'fetch').returns(Promise.resolve());
+ test('gerrit auth is used', () => {
+ sandbox.stub(Gerrit.Auth, 'fetch').returns(Promise.resolve());
element.fetchJSON('foo');
- assert(GrGerritAuth.prototype.fetch.called);
- });
-
- test('gapi auth is enabled with USE_GAPI_AUTH', () => {
- window.USE_GAPI_AUTH = true;
- sandbox.stub(GrGapiAuth.prototype, 'fetch').returns(Promise.resolve());
- element = fixture('basic');
- element.fetchJSON('foo');
- assert(GrGapiAuth.prototype.fetch.called);
- delete window.USE_GAPI_AUTH;
+ assert(Gerrit.Auth.fetch.called);
});
test('getSuggestedAccounts does not return fetchJSON', () => {
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index ed898f9..c9035be 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -38,6 +38,7 @@
'admin/gr-create-pointer-dialog/gr-create-pointer-dialog_test.html',
'admin/gr-create-project-dialog/gr-create-project-dialog_test.html',
'admin/gr-group/gr-group_test.html',
+ 'admin/gr-group-audit-log/gr-group-audit-log_test.html',
'admin/gr-plugin-list/gr-plugin-list_test.html',
'admin/gr-project/gr-project_test.html',
'admin/gr-project-detail-list/gr-project-detail-list_test.html',
@@ -129,7 +130,7 @@
'shared/gr-linked-chip/gr-linked-chip_test.html',
'shared/gr-linked-text/gr-linked-text_test.html',
'shared/gr-list-view/gr-list-view_test.html',
- 'shared/gr-rest-api-interface/gr-gapi-auth_test.html',
+ 'shared/gr-rest-api-interface/gr-auth_test.html',
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
'shared/gr-select/gr-select_test.html',