From 456a2c2f44ffcd1dc550d119df21b59835d4065f Mon Sep 17 00:00:00 2001 From: David Ostrovsky Date: Sun, 20 Nov 2022 04:23:58 +0100 Subject: GitwebServlet: Fix project root computation If9da362140 introduced regression in project root computation. Also add test coverage for Gitweb servlet so that a similar regresssion could be avoided in the future. Test Plan: bazel test javatests/com/google/gerrit/httpd/... Bug: Issue 16449 Release-Notes: Fix project root computation in Gitweb servlet Change-Id: Ia1a7bdea55db4deb55a3b82a292890e7febbe675 --- .../google/gerrit/httpd/gitweb/GitwebServlet.java | 32 ++++-- .../gerrit/httpd/gitweb/GitwebServletTest.java | 107 +++++++++++++++++++++ 2 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 javatests/com/google/gerrit/httpd/gitweb/GitwebServletTest.java diff --git a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java index 0875317542..4c18afda4f 100644 --- a/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java +++ b/java/com/google/gerrit/httpd/gitweb/GitwebServlet.java @@ -32,6 +32,7 @@ package com.google.gerrit.httpd.gitweb; import static java.nio.charset.StandardCharsets.ISO_8859_1; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.flogger.FluentLogger; @@ -640,20 +641,39 @@ class GitwebServlet extends HttpServlet { return env.getEnvArray(); } - private String getProjectRoot(Project.NameKey nameKey) - throws RepositoryNotFoundException, IOException { + /** + * Return the project root under which the specified project is stored. + * + * @param nameKey the name of the project + * @return base directory + */ + @VisibleForTesting + String getProjectRoot(Project.NameKey nameKey) throws RepositoryNotFoundException, IOException { try (Repository repo = repoManager.openRepository(nameKey)) { - return getProjectRoot(repo); + return getRepositoryRoot(repo, nameKey).toString(); } } - private String getProjectRoot(Repository repo) { + /** + * Return the repository root under which the specified repository is stored. + * + * @param repo the name of the repository + * @param nameKey project name + * @return base path + * @throws ProvisionException if the repo is not DelegateRepository or FileRepository. + */ + private static Path getRepositoryRoot(Repository repo, Project.NameKey nameKey) { if (repo instanceof DelegateRepository) { - return getProjectRoot(((DelegateRepository) repo).delegate()); + return getRepositoryRoot(((DelegateRepository) repo).delegate(), nameKey); } if (repo instanceof FileRepository) { - return repo.getDirectory().getAbsolutePath(); + String name = nameKey.get(); + Path current = repo.getDirectory().toPath(); + for (int i = 0; i <= CharMatcher.is('/').countIn(name); i++) { + current = current.getParent(); + } + return current; } throw new ProvisionException("Gitweb can only be used with FileRepository"); diff --git a/javatests/com/google/gerrit/httpd/gitweb/GitwebServletTest.java b/javatests/com/google/gerrit/httpd/gitweb/GitwebServletTest.java new file mode 100644 index 0000000000..d1598b9b90 --- /dev/null +++ b/javatests/com/google/gerrit/httpd/gitweb/GitwebServletTest.java @@ -0,0 +1,107 @@ +// Copyright (C) 2022 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.httpd.gitweb; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; + +import com.google.gerrit.entities.Project; +import com.google.gerrit.server.config.AllProjectsName; +import com.google.gerrit.server.config.AllProjectsNameProvider; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.GitwebCgiConfig; +import com.google.gerrit.server.config.GitwebConfig; +import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; +import com.google.gerrit.server.permissions.PermissionBackend; +import com.google.gerrit.server.project.ProjectCache; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Repository; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class GitwebServletTest { + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private Config cfg; + private SitePaths site; + private LocalDiskRepositoryManager repoManager; + private ProjectCache projectCache; + private PermissionBackend permissionBackendMock; + private GitwebCgiConfig gitWebCgiConfig; + private GitwebConfig gitWebConfig; + private GitwebServlet servlet; + private AllProjectsName allProjectsName; + + @Before + public void setUp() throws Exception { + site = new SitePaths(temporaryFolder.newFolder().toPath()); + site.resolve("git").toFile().mkdir(); + cfg = new Config(); + cfg.setString("gerrit", null, "basePath", "git"); + repoManager = + Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(SitePaths.class).toInstance(site); + bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(cfg); + } + }) + .getInstance(LocalDiskRepositoryManager.class); + projectCache = mock(ProjectCache.class); + permissionBackendMock = mock(PermissionBackend.class); + gitWebCgiConfig = mock(GitwebCgiConfig.class); + gitWebConfig = mock(GitwebConfig.class); + allProjectsName = new AllProjectsName(AllProjectsNameProvider.DEFAULT); + // All-Projects must exist prior to calling GitwebServlet ctor + repoManager.createRepository(allProjectsName); + servlet = + new GitwebServlet( + repoManager, + projectCache, + permissionBackendMock, + null, + site, + cfg, + null, + null, + gitWebConfig, + gitWebCgiConfig, + allProjectsName); + } + + @Test + public void projectRootSetToBasePathForSimpleRepository() throws Exception { + Project.NameKey foo = Project.nameKey("foo"); + try (Repository repo = repoManager.createRepository(foo)) { + assertThat(servlet.getProjectRoot(foo)) + .isEqualTo(repoManager.getBasePath(foo).toAbsolutePath().toString()); + } + } + + @Test + public void projectRootSetToBasePathForNestedRepository() throws Exception { + Project.NameKey baz = Project.nameKey("foo/bar/baz"); + try (Repository repo = repoManager.createRepository(baz)) { + assertThat(servlet.getProjectRoot(baz)) + .isEqualTo(repoManager.getBasePath(baz).toAbsolutePath().toString()); + } + } +} -- cgit v1.2.3