Merge branch 'stable-3.0'

* stable-3.0:
  Bump Bazel version to 2.2.0
  Re-add support for projects with anonymous HTTP access

Adapt to project cache returning Optional in latest master. Switch to
snapshot API so this builds again.

Change-Id: I86e5acd26de085a6e82c59d3560d1ad9cc6793b8
diff --git a/.bazelversion b/.bazelversion
index 7ec1d6d..ccbccc3 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-2.1.0
+2.2.0
diff --git a/WORKSPACE b/WORKSPACE
index 2257fb2..4faf63f 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -8,19 +8,19 @@
 )
 
 # Snapshot Plugin API
-#load(
-#    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
-#    "gerrit_api_maven_local",
-#)
-
-# Load snapshot Plugin API
-#gerrit_api_maven_local()
-
-# Release Plugin API
 load(
-    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
-    "gerrit_api",
+    "@com_googlesource_gerrit_bazlets//:gerrit_api_maven_local.bzl",
+    "gerrit_api_maven_local",
 )
 
+# Load snapshot Plugin API
+gerrit_api_maven_local()
+
+# Release Plugin API
+#load(
+#    "@com_googlesource_gerrit_bazlets//:gerrit_api.bzl",
+#    "gerrit_api",
+#)
+
 # Load release Plugin API
-gerrit_api()
+#gerrit_api()
diff --git a/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java b/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
index 697938f..93e4ba2 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
@@ -16,14 +16,20 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Strings;
+import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.httpd.AllRequestFilter;
 import com.google.gerrit.httpd.HtmlDomUtil;
+import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.config.CanonicalWebUrl;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.gerrit.util.http.CacheHeaders;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -31,6 +37,7 @@
 import java.net.URISyntaxException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Optional;
 import java.util.regex.Pattern;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
@@ -74,13 +81,21 @@
           + "</html>";
 
   private static final Pattern AUTHENTICATED_REQ = Pattern.compile("^/a/.*");
+  private final Provider<AnonymousUser> anonProvider;
+  private final PermissionBackend permissions;
   private final ProjectCache projectCache;
   final String webUrl;
   final String projectPrefix;
 
   @Inject
-  GoImportFilter(ProjectCache projectCache, @CanonicalWebUrl String webUrl)
+  GoImportFilter(
+      Provider<AnonymousUser> anonProvider,
+      PermissionBackend permissions,
+      ProjectCache projectCache,
+      @CanonicalWebUrl String webUrl)
       throws URISyntaxException {
+    this.anonProvider = anonProvider;
+    this.permissions = permissions;
     this.projectCache = projectCache;
     this.webUrl = webUrl.replaceFirst("/?$", "/");
     this.projectPrefix = generateProjectPrefix();
@@ -160,13 +175,25 @@
         + (authenticated ? "a/" : "")
         + projectName
         + " git "
-        + webUrl
-        + "a/"
-        + projectName;
+        + getRepoRoot(projectName, authenticated);
+  }
+
+  private String getRepoRoot(String projectName, boolean authenticated) {
+    if (allowsAnonymousAccess(projectName) && !authenticated) {
+      return webUrl + projectName;
+    }
+    return webUrl + "a/" + projectName;
+  }
+
+  private boolean allowsAnonymousAccess(String projectName) {
+    AnonymousUser anonymous = anonProvider.get();
+    BranchNameKey heads = BranchNameKey.create(Project.nameKey(projectName), RefNames.REFS_HEADS);
+
+    return permissions.user(anonymous).ref(heads).testOrFalse(RefPermission.READ);
   }
 
   private boolean projectExists(String projectName) {
-    ProjectState p = projectCache.get(Project.nameKey(projectName));
+    Optional<ProjectState> p = projectCache.get(Project.nameKey(projectName));
     return p != null;
   }
 }
diff --git a/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java b/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
index c65fb5e..39f01df 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
@@ -27,11 +27,13 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.server.AnonymousUser;
 import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.RefPermission;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
 import com.google.inject.Provider;
 import java.io.IOException;
 import java.net.URISyntaxException;
+import java.util.Optional;
 import javax.servlet.FilterChain;
 import javax.servlet.ServletException;
 import javax.servlet.ServletOutputStream;
@@ -51,6 +53,8 @@
   private static final String CONTENT_FORMAT = "%1$s/%3$s git %2$s/%3$s";
   private static final String CONTENT =
       String.format(CONTENT_FORMAT, PROD_FQDN, auth(PROD_URL), PROJECT_NAME);
+  private static final String ANON_CONTENT =
+      String.format(CONTENT_FORMAT, PROD_FQDN, PROD_URL, PROJECT_NAME);
   private static final String AUTH_CONTENT =
       String.format(CONTENT_FORMAT, auth(PROD_FQDN), auth(PROD_URL), PROJECT_NAME);
 
@@ -59,10 +63,13 @@
     return baseUrl + "/a";
   }
 
-  private static byte[] response200(boolean authenticated) {
-    return authenticated
-        ? PAGE_200.replace(CONTENT_PLH, AUTH_CONTENT).getBytes()
-        : PAGE_200.replace(CONTENT_PLH, CONTENT).getBytes();
+  private static byte[] response200(boolean allowAnon, boolean authenticated) {
+    if (authenticated) {
+      return PAGE_200.replace(CONTENT_PLH, AUTH_CONTENT).getBytes();
+    } else if (allowAnon) {
+      return PAGE_200.replace(CONTENT_PLH, ANON_CONTENT).getBytes();
+    }
+    return PAGE_200.replace(CONTENT_PLH, CONTENT).getBytes();
   }
 
   private GoImportFilter unitUnderTest;
@@ -81,23 +88,31 @@
 
   @Before
   public void setUp() throws Exception {
-    unitUnderTest = new GoImportFilter(mockProjectCache, PROD_URL);
+    unitUnderTest = new GoImportFilter(mockAnonProvider, mockPerms, mockProjectCache, PROD_URL);
     assertThat(unitUnderTest).isNotNull();
     when(mockResponse.getOutputStream()).thenReturn(mockOutputStream);
+
+    when(mockAnonProvider.get()).thenReturn(mockAnon);
+    when(mockPerms.user(mockAnon)).thenReturn(mockPermsWithUser);
+    when(mockPermsWithUser.ref(any())).thenReturn(mockPermsForRef);
   }
 
   @Test
   public void testConstructor() throws Exception {
     assertThat(unitUnderTest.webUrl.endsWith("/")).isTrue();
     unitUnderTest =
-        new GoImportFilter(mockProjectCache, "http://gerrit-review.googlesource.com:8080/");
+        new GoImportFilter(
+            mockAnonProvider,
+            mockPerms,
+            mockProjectCache,
+            "http://gerrit-review.googlesource.com:8080/");
     assertThat(unitUnderTest.webUrl.endsWith("/")).isTrue();
     assertThat(unitUnderTest.projectPrefix).isNotNull();
   }
 
   @Test(expected = URISyntaxException.class)
   public void testConstructorWithURISyntaxException() throws Exception {
-    unitUnderTest = new GoImportFilter(mockProjectCache, "\\\\");
+    unitUnderTest = new GoImportFilter(mockAnonProvider, mockPerms, mockProjectCache, "\\\\");
   }
 
   @Test
@@ -126,33 +141,39 @@
   public void testDoFilterWithExistingProject() throws Exception {
     when(mockRequest.getServletPath()).thenReturn("/" + PROJECT_NAME);
     when(mockRequest.getParameter("go-get")).thenReturn("1");
-    when(mockProjectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(mockProjectState);
+    when(mockProjectCache.get(Project.nameKey(PROJECT_NAME)))
+        .thenReturn(Optional.of(mockProjectState));
+    when(mockPermsForRef.testOrFalse(RefPermission.READ)).thenReturn(false);
     unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
-    verify(mockOutputStream, times(1)).write(response200(false));
+    verify(mockOutputStream, times(1)).write(response200(false, false));
     verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
     verify(mockProjectCache, times(1)).get(any(Project.NameKey.class));
     verify(mockResponse, times(1)).setStatus(200);
+    verify(mockPermsForRef, times(1)).testOrFalse(RefPermission.READ);
   }
 
   @Test
   public void testDoFilterAuthenticatedWithExistingProject() throws Exception {
     when(mockRequest.getServletPath()).thenReturn("/a/" + PROJECT_NAME);
     when(mockRequest.getParameter("go-get")).thenReturn("1");
-    when(mockProjectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(mockProjectState);
+    when(mockProjectCache.get(Project.nameKey(PROJECT_NAME)))
+        .thenReturn(Optional.of(mockProjectState));
+    when(mockPermsForRef.testOrFalse(RefPermission.READ)).thenReturn(false);
     unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
-    verify(mockOutputStream, times(1)).write(response200(true));
+    verify(mockOutputStream, times(1)).write(response200(false, true));
     verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
     verify(mockProjectCache, times(1)).get(any(Project.NameKey.class));
     verify(mockResponse, times(1)).setStatus(200);
+    verify(mockPermsForRef, times(1)).testOrFalse(RefPermission.READ);
   }
 
   @Test
   public void testDoFilterWithExistingProjectAndPackage() throws Exception {
     when(mockRequest.getServletPath()).thenReturn("/" + PROJECT_NAME + "/my/package");
     when(mockRequest.getParameter("go-get")).thenReturn("1");
-    when(mockProjectCache.get(Project.nameKey(PROJECT_NAME))).thenReturn(mockProjectState);
+    when(mockPermsForRef.testOrFalse(RefPermission.READ)).thenReturn(false);
     unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
-    verify(mockOutputStream, times(1)).write(response200(false));
+    verify(mockOutputStream, times(1)).write(response200(false, false));
     verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
     verify(mockProjectCache, times(3)).get(any(Project.NameKey.class));
     verify(mockResponse, times(1)).setStatus(200);
@@ -162,13 +183,15 @@
   public void testDoFilterWithAnonymousAccessibleProject() throws Exception {
     when(mockRequest.getServletPath()).thenReturn("/projectName");
     when(mockRequest.getParameter("go-get")).thenReturn("1");
-    when(mockProjectCache.get(Project.nameKey("projectName"))).thenReturn(mockProjectState);
-    when(mockProjectCache.get(Project.nameKey("projectName"))).thenReturn(mockProjectState);
+    when(mockProjectCache.get(Project.nameKey("projectName")))
+        .thenReturn(Optional.of(mockProjectState));
+    when(mockPermsForRef.testOrFalse(RefPermission.READ)).thenReturn(true);
     unitUnderTest.doFilter(mockRequest, mockResponse, mockChain);
-    verify(mockOutputStream, times(1)).write(response200(false));
+    verify(mockOutputStream, times(1)).write(response200(true, false));
     verify(mockChain, times(0)).doFilter(mockRequest, mockResponse);
     verify(mockProjectCache, times(1)).get(any(Project.NameKey.class));
     verify(mockResponse, times(1)).setStatus(200);
+    verify(mockPermsForRef, times(1)).testOrFalse(RefPermission.READ);
   }
 
   @Test
@@ -189,7 +212,8 @@
     when(mockRequest.getServletPath()).thenReturn("/" + PROJECT_NAME);
     when(mockRequest.getParameter("go-get")).thenReturn("1");
     doThrow(new IOException(msg)).when(mockOutputStream).write(any(byte[].class));
-    when(mockProjectCache.get(any(Project.NameKey.class))).thenReturn(mockProjectState);
+    when(mockProjectCache.get(any(Project.NameKey.class)))
+        .thenReturn(Optional.of(mockProjectState));
     IOException thrown =
         assertThrows(
             IOException.class, () -> unitUnderTest.doFilter(mockRequest, mockResponse, mockChain));