Re-add support for projects with anonymous HTTP access This was originally added by go-import#190630, but was reverted by the merging of go-import#241958 into master. Enforcing authentication in all cases is problematic for those who wish to make their public Gerrit hosted Go projects easily installable via the standard Go toolchain, either as standalone applications or as libraries. Anonymous repo URLs are now used as long as the anonymous user is granted read access to the project's `refs/heads/*`. Change-Id: I15aa7251483877aae6552df0d11032d63af4b9e2
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 ea8a4dc..d321774 100644 --- a/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java +++ b/src/main/java/com/ericsson/gerrit/plugins/goimport/GoImportFilter.java
@@ -18,13 +18,19 @@ import com.google.common.base.Strings; import com.google.gerrit.httpd.AllRequestFilter; import com.google.gerrit.httpd.HtmlDomUtil; +import com.google.gerrit.reviewdb.client.Branch; import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; +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.Singleton; +import com.google.inject.Provider; import java.io.IOException; import java.io.OutputStream; import java.net.URI; @@ -74,13 +80,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,9 +174,22 @@ + (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(); + Branch.NameKey heads = + new Branch.NameKey(new Project.NameKey(projectName), RefNames.REFS_HEADS); + + return permissions.user(anonymous).ref(heads).testOrFalse(RefPermission.READ); } private boolean projectExists(String projectName) {
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 4ecd9e9..b659ba5 100644 --- a/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java +++ b/src/test/java/com/ericsson/gerrit/plugins/goimport/GoImportFilterTest.java
@@ -25,8 +25,12 @@ import static org.mockito.Mockito.when; import com.google.gerrit.reviewdb.client.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 javax.servlet.FilterChain; @@ -48,6 +52,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); @@ -56,14 +62,22 @@ 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; + @Mock private Provider<AnonymousUser> mockAnonProvider; + @Mock private AnonymousUser mockAnon; + @Mock private PermissionBackend mockPerms; + @Mock private PermissionBackend.WithUser mockPermsWithUser; + @Mock private PermissionBackend.ForRef mockPermsForRef; @Mock private ProjectCache mockProjectCache; @Mock private HttpServletRequest mockRequest; @Mock private HttpServletResponse mockResponse; @@ -73,23 +87,34 @@ @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/"); + unitUnderTest = 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 @@ -119,11 +144,13 @@ when(mockRequest.getServletPath()).thenReturn("/" + PROJECT_NAME); when(mockRequest.getParameter("go-get")).thenReturn("1"); when(mockProjectCache.get(new 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(1)).get(any(Project.NameKey.class)); verify(mockResponse, times(1)).setStatus(200); + verify(mockPermsForRef, times(1)).testOrFalse(RefPermission.READ); } @Test @@ -131,11 +158,13 @@ when(mockRequest.getServletPath()).thenReturn("/a/" + PROJECT_NAME); when(mockRequest.getParameter("go-get")).thenReturn("1"); when(mockProjectCache.get(new Project.NameKey(PROJECT_NAME))).thenReturn(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 @@ -143,11 +172,27 @@ when(mockRequest.getServletPath()).thenReturn("/" + PROJECT_NAME + "/my/package"); when(mockRequest.getParameter("go-get")).thenReturn("1"); when(mockProjectCache.get(new 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); + verify(mockPermsForRef, times(1)).testOrFalse(RefPermission.READ); + } + + @Test + public void testDoFilterWithAnonymousAccessibleProject() throws Exception { + when(mockRequest.getServletPath()).thenReturn("/projectName"); + when(mockRequest.getParameter("go-get")).thenReturn("1"); + when(mockProjectCache.get(new Project.NameKey("projectName"))).thenReturn(mockProjectState); + when(mockPermsForRef.testOrFalse(RefPermission.READ)).thenReturn(true); + unitUnderTest.doFilter(mockRequest, mockResponse, mockChain); + 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
diff --git a/src/test/java/com/ericsson/gerrit/plugins/goimport/HttpModuleTest.java b/src/test/java/com/ericsson/gerrit/plugins/goimport/HttpModuleTest.java index 547c159..a0a107d 100644 --- a/src/test/java/com/ericsson/gerrit/plugins/goimport/HttpModuleTest.java +++ b/src/test/java/com/ericsson/gerrit/plugins/goimport/HttpModuleTest.java
@@ -16,7 +16,9 @@ import static com.google.common.truth.Truth.assertThat; +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.project.ProjectCache; import com.google.inject.AbstractModule; import com.google.inject.Guice; @@ -32,6 +34,8 @@ public class HttpModuleTest { private HttpModule unitUnderTest; + @Mock private AnonymousUser mockAnon; + @Mock private PermissionBackend mockPerms; @Mock private ProjectCache mockProjectCache; @Before @@ -53,6 +57,8 @@ public class TestModule extends AbstractModule { @Override protected void configure() { + bind(AnonymousUser.class).toInstance(mockAnon); + bind(PermissionBackend.class).toInstance(mockPerms); bind(ProjectCache.class).toInstance(mockProjectCache); }