VisibleRefFilter: reuse PermissionBackend.ForProject across refs

This allows the PermissionBackend to perform caching at the project
level, which may allow it to perform faster ref evaluation.  The
DefaultPermissionBackend for example does this with ForProject
containing a ProjectControl, which caches relevant AccessSections to
quickly build the RefControl that backs ForRef.

Change-Id: I82c80ecb324765c7f0525b6817618790833d3948
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 07ea774..7241511 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
@@ -77,6 +77,7 @@
   private final Provider<ReviewDb> db;
   private final Provider<CurrentUser> user;
   private final PermissionBackend permissionBackend;
+  private final PermissionBackend.ForProject perm;
   private final ProjectState projectState;
   private final Repository git;
   private ProjectControl projectCtl;
@@ -100,6 +101,8 @@
     this.db = db;
     this.user = user;
     this.permissionBackend = permissionBackend;
+    this.perm =
+        permissionBackend.user(user).database(db).project(projectState.getProject().getNameKey());
     this.projectState = projectState;
     this.git = git;
   }
@@ -265,31 +268,25 @@
   }
 
   private Map<Change.Id, Branch.NameKey> visibleChangesBySearch() {
-    Project project = projectCtl.getProject();
+    Project.NameKey project = projectState.getProject().getNameKey();
     try {
       Map<Change.Id, Branch.NameKey> visibleChanges = new HashMap<>();
-      for (ChangeData cd : changeCache.getChangeData(db.get(), project.getNameKey())) {
-        if (permissionBackend
-            .user(user)
-            .indexedChange(cd, changeNotesFactory.createFromIndexedChange(cd.change()))
-            .database(db)
-            .test(ChangePermission.READ)) {
+      for (ChangeData cd : changeCache.getChangeData(db.get(), project)) {
+        ChangeNotes notes = changeNotesFactory.createFromIndexedChange(cd.change());
+        if (perm.indexedChange(cd, notes).test(ChangePermission.READ)) {
           visibleChanges.put(cd.getId(), cd.change().getDest());
         }
       }
       return visibleChanges;
     } catch (OrmException | PermissionBackendException e) {
       log.error(
-          "Cannot load changes for project "
-              + project.getName()
-              + ", assuming no changes are visible",
-          e);
+          "Cannot load changes for project " + project + ", assuming no changes are visible", e);
       return Collections.emptyMap();
     }
   }
 
   private Map<Change.Id, Branch.NameKey> visibleChangesByScan() {
-    Project.NameKey p = projectCtl.getProject().getNameKey();
+    Project.NameKey p = projectState.getProject().getNameKey();
     Stream<ChangeNotesResult> s;
     try {
       s = changeNotesFactory.scan(git, db.get(), p);
@@ -309,7 +306,7 @@
       return null;
     }
     try {
-      if (permissionBackend.user(user).change(r.notes()).database(db).test(ChangePermission.READ)) {
+      if (perm.change(r.notes()).test(ChangePermission.READ)) {
         return r.notes();
       }
     } catch (PermissionBackendException e) {
@@ -330,17 +327,13 @@
 
   private boolean canReadRef(String ref) {
     try {
-      permissionBackend
-          .user(user)
-          .project(projectCtl.getProject().getNameKey())
-          .ref(ref)
-          .check(RefPermission.READ);
+      perm.ref(ref).check(RefPermission.READ);
+      return true;
     } catch (AuthException e) {
       return false;
     } catch (PermissionBackendException e) {
       log.error("unable to check permissions", e);
       return false;
     }
-    return true;
   }
 }
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 5c0cf44..6db9357 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
@@ -268,6 +268,15 @@
       return ref(notes.getChange().getDest().get()).change(notes);
     }
 
+    /**
+     * @return instance scoped for the change loaded from index, and its destination ref and
+     *     project. This method should only be used when database access is harmful and potentially
+     *     stale data from the index is acceptable.
+     */
+    public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
+      return ref(notes.getChange().getDest().get()).indexedChange(cd, notes);
+    }
+
     /** Verify scoped user can {@code perm}, throwing if denied. */
     public abstract void check(ProjectPermission perm)
         throws AuthException, PermissionBackendException;