Merge branch 'stable-3.8'

* stable-3.8:
  Add support for virtual host based wizard flow

Change-Id: I260557af0d3bd4ee35d3373ae010f6026f72ed07
diff --git a/github-plugin/pom.xml b/github-plugin/pom.xml
index cc410e6..b77cd4e 100644
--- a/github-plugin/pom.xml
+++ b/github-plugin/pom.xml
@@ -157,5 +157,13 @@
       <artifactId>velocity-engine-core</artifactId>
       <version>2.3</version>
     </dependency>
+      <dependency>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+      </dependency>
+    <dependency>
+      <groupId>com.google.truth</groupId>
+      <artifactId>truth</artifactId>
+    </dependency>
   </dependencies>
 </project>
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GitHubConfig.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GitHubConfig.java
index 010660e..a22126d 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GitHubConfig.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/GitHubConfig.java
@@ -14,25 +14,25 @@
 package com.googlesource.gerrit.plugins.github;
 
 import com.google.common.base.MoreObjects;
-import com.google.common.collect.Maps;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
 import com.google.gerrit.entities.Account;
 import com.google.gerrit.httpd.CanonicalWebUrl;
-import com.google.gerrit.server.config.AllProjectsNameProvider;
+import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import com.googlesource.gerrit.plugins.github.oauth.GitHubOAuthConfig;
 import java.net.MalformedURLException;
 import java.nio.file.Path;
-import java.util.HashMap;
 import org.eclipse.jgit.lib.Config;
 
 @Singleton
 public class GitHubConfig extends GitHubOAuthConfig {
 
   private static final String CONF_WIZARD_FLOW = "wizardFlow";
-  private HashMap<String, NextPage> wizardFromTo = Maps.newHashMap();
   private static final String FROM_TO_SEPARATOR = "=>";
   private static final String FROM_TO_REDIRECT_SEPARATOR = "R>";
   private static final String CONF_JOB_POOL_LIMIT = "jobPoolLimit";
@@ -45,6 +45,7 @@
   private static final String CONF_WEBHOOK_SECRET = "webhookSecret";
   private static final String CONF_WEBHOOK_USER = "webhookUser";
   private static final String CONF_IMPORT_ACCOUNT_ID = "importAccountId";
+  private static final String DEFAULT_SERVER = "default";
 
   public final Path gitDir;
   public final int jobPoolLimit;
@@ -58,6 +59,7 @@
   public final String webhookSecret;
   public final String webhookUser;
   public final Account.Id importAccountId;
+  private final Table<String, String, NextPage> wizardFromTo = HashBasedTable.create();
 
   public static class NextPage {
     public final String uri;
@@ -73,19 +75,15 @@
   public GitHubConfig(
       @GerritServerConfig Config config,
       final SitePaths site,
-      AllProjectsNameProvider allProjectsNameProvider,
+      Provider<AllProjectsName> allProjectsNameProvider,
       CanonicalWebUrl canonicalWebUrl)
       throws MalformedURLException {
     super(config, canonicalWebUrl);
-    String[] wizardFlows = config.getStringList(CONF_SECTION, null, CONF_WIZARD_FLOW);
-    for (String fromTo : wizardFlows) {
-      boolean redirect = fromTo.indexOf(FROM_TO_REDIRECT_SEPARATOR) > 0;
-      int sepPos = getSepPos(fromTo, redirect);
-      String fromPage = fromTo.substring(0, sepPos).trim();
-      NextPage toPage =
-          new NextPage(
-              fromTo.substring(sepPos + getSeparator(redirect).length() + 1).trim(), redirect);
-      wizardFromTo.put(fromPage, toPage);
+    parseWizardFlow(config.getStringList(CONF_SECTION, null, CONF_WIZARD_FLOW), DEFAULT_SERVER);
+
+    // Virtual host specific sections
+    for (String server : config.getSubsections(CONF_SECTION)) {
+      parseWizardFlow(config.getStringList(CONF_SECTION, server, CONF_WIZARD_FLOW), server);
     }
 
     jobPoolLimit = config.getInt(CONF_SECTION, CONF_JOB_POOL_LIMIT, 5);
@@ -107,12 +105,24 @@
     importAccountId = Account.id(config.getInt(CONF_SECTION, CONF_IMPORT_ACCOUNT_ID, 1000000));
   }
 
-  private String getSeparator(boolean redirect) {
+  private void parseWizardFlow(String[] wizardFlows, String server) {
+    for (String fromTo : wizardFlows) {
+      boolean redirect = fromTo.indexOf(FROM_TO_REDIRECT_SEPARATOR) > 0;
+      int sepPos = getSepPos(fromTo, redirect);
+      String fromPage = fromTo.substring(0, sepPos).trim();
+      NextPage toPage =
+          new NextPage(
+              fromTo.substring(sepPos + getSeparator(redirect).length() + 1).trim(), redirect);
+      wizardFromTo.put(server, fromPage, toPage);
+    }
+  }
+
+  private static String getSeparator(boolean redirect) {
     String separator = redirect ? FROM_TO_REDIRECT_SEPARATOR : FROM_TO_SEPARATOR;
     return separator;
   }
 
-  private int getSepPos(String fromTo, boolean redirect) {
+  private static int getSepPos(String fromTo, boolean redirect) {
     int sepPos = fromTo.indexOf(getSeparator(redirect));
     if (sepPos < 0) {
       throw new InvalidGitHubConfigException(fromTo);
@@ -120,8 +130,11 @@
     return sepPos;
   }
 
-  public NextPage getNextPage(String sourcePage) {
-    return wizardFromTo.get(sourcePage);
+  public NextPage getNextPage(String serverName, String sourcePage) {
+    if (!wizardFromTo.containsRow(serverName)) {
+      return wizardFromTo.get(DEFAULT_SERVER, sourcePage);
+    }
+    return wizardFromTo.get(serverName, sourcePage);
   }
 
   public String getBaseProject(boolean isPrivateProject) {
diff --git a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/VelocityControllerServlet.java b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/VelocityControllerServlet.java
index 845399e..224fbb5 100644
--- a/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/VelocityControllerServlet.java
+++ b/github-plugin/src/main/java/com/googlesource/gerrit/plugins/github/wizard/VelocityControllerServlet.java
@@ -125,7 +125,7 @@
     if (queryStringStart > 0) {
       sourcePage = sourcePage.substring(0, queryStringStart);
     }
-    NextPage nextPage = githubConfig.getNextPage(sourcePage);
+    NextPage nextPage = githubConfig.getNextPage(req.getServerName(), sourcePage);
     if (nextPage != null) {
       if (nextPage.redirect) {
         resp.sendRedirect(nextPageURL(sourcePath, nextPage));
diff --git a/github-plugin/src/main/resources/Documentation/config.md b/github-plugin/src/main/resources/Documentation/config.md
index e2ea0a3..c9eebc5 100644
--- a/github-plugin/src/main/resources/Documentation/config.md
+++ b/github-plugin/src/main/resources/Documentation/config.md
@@ -95,6 +95,40 @@
     * h, hr, hour, hours
     Default value: 30 seconds
 
+github.wizardFlow
+:   Define the transition from one page to another during the initial
+    user setup wizard flow. The format of the value is the following:
+    `page => next page` or `page R> next page` for redirections.
+
+    The example below shows an initial wizard that guides through
+    the import of repositories, pull-requests and then redirects
+    to the Gerrit projects admin page.
+
+    **Example:**
+    ```
+    wizardFlow = account.gh => repositories.html
+    wizardFlow = repositories-next.gh => pullrequests.html
+    wizardFlow = pullrequests-next.gh R> / #/admin/projects/
+    ```
+
+github.<domain>.wizardFlow
+:   Allow to customise the GitHub wizard flow for the domain `<domain>`.
+    This setting is useful for multi-site setups where the GitHub
+    import Wizard can be different between sites.
+
+    The example below shows an initial wizard that guides through
+    the import of repositories for all sites, but redirects to
+    the Eclipse ECA sign page for the `eclipse.gerrithub.io` site.
+
+    **Example:**
+    ```
+    [github]
+      wizardFlow = account.gh => repositories.html
+
+    [github "eclipse.gerrithub.io"]
+      wizardFlow = account.gh R> eclipse-eca.html
+    ```
+
 Key Configuration
 -------------
 
diff --git a/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/GitHubConfigTest.java b/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/GitHubConfigTest.java
new file mode 100644
index 0000000..56f7b90
--- /dev/null
+++ b/github-plugin/src/test/java/com/googlesource/gerrit/plugins/github/GitHubConfigTest.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2023 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.googlesource.gerrit.plugins.github;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.SitePaths;
+import com.google.inject.Provider;
+import com.google.inject.util.Providers;
+import java.nio.file.Path;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GitHubConfigTest {
+  private static final Provider<AllProjectsName> ALL_PROJECTS_NAME_PROVIDER =
+      Providers.of(new AllProjectsName("All-Projects"));
+  public static final String TEST_DOMAIN = "anydomain.com";
+  public static final String SOURCE_PAGE = "sourcePage";
+  public static final String NEXT_PAGE = "nextPage";
+  public static final String CUSTOM_NEXT_PAGE = "customNextPage";
+  private SitePaths site;
+
+  @Before
+  public void setup() throws Exception {
+    site = new SitePaths(Path.of("/tmp"));
+  }
+
+  @Test
+  public void getNextPageDefault() throws Exception {
+    GitHubConfig.NextPage nextPage =
+        newGitHubConfig("wizardFlow = " + SOURCE_PAGE + " => " + NEXT_PAGE)
+            .getNextPage(TEST_DOMAIN, SOURCE_PAGE);
+
+    assertThat(nextPage.redirect).isFalse();
+    assertThat(nextPage.uri).isEqualTo(NEXT_PAGE);
+  }
+
+  @Test
+  public void getNextPageRedirectDefault() throws Exception {
+    GitHubConfig.NextPage nextPage =
+        newGitHubConfig("wizardFlow = " + SOURCE_PAGE + " R> " + NEXT_PAGE)
+            .getNextPage(TEST_DOMAIN, SOURCE_PAGE);
+
+    assertThat(nextPage.redirect).isTrue();
+    assertThat(nextPage.uri).isEqualTo(NEXT_PAGE);
+  }
+
+  @Test
+  public void getNextPageByDomain() throws Exception {
+    GitHubConfig.NextPage nextPage =
+        newGitHubConfig(
+                "wizardFlow = "
+                    + SOURCE_PAGE
+                    + " => "
+                    + NEXT_PAGE
+                    + "\n"
+                    + "[github \""
+                    + TEST_DOMAIN
+                    + "\"]\n"
+                    + "wizardFlow = "
+                    + SOURCE_PAGE
+                    + " => "
+                    + CUSTOM_NEXT_PAGE)
+            .getNextPage(TEST_DOMAIN, SOURCE_PAGE);
+
+    assertThat(nextPage.redirect).isFalse();
+    assertThat(nextPage.uri).isEqualTo(CUSTOM_NEXT_PAGE);
+  }
+
+  @Test
+  public void getNextPageRedirectByDomain() throws Exception {
+    GitHubConfig.NextPage nextPage =
+        newGitHubConfig(
+                "wizardFlow = "
+                    + SOURCE_PAGE
+                    + " R> "
+                    + CUSTOM_NEXT_PAGE
+                    + "\n"
+                    + "[github \""
+                    + TEST_DOMAIN
+                    + "\"]\n"
+                    + "wizardFlow = \""
+                    + SOURCE_PAGE
+                    + "\" R> "
+                    + CUSTOM_NEXT_PAGE)
+            .getNextPage(TEST_DOMAIN, SOURCE_PAGE);
+
+    assertThat(nextPage.redirect).isTrue();
+    assertThat(nextPage.uri).isEqualTo(CUSTOM_NEXT_PAGE);
+  }
+
+  private GitHubConfig newGitHubConfig(String configText) throws Exception {
+    Config gerritConfig = new Config();
+    gerritConfig.fromText(
+        "[auth]\n"
+            + "httpHeader = GITHUB\n"
+            + "type = HTTP\n"
+            + "[gerrit]\n"
+            + "basePath = /tmp\n"
+            + "[github-key \"default\"]\n"
+            + "current = true\n"
+            + "passwordDevice = /dev/zero\n"
+            + "[github]\n"
+            + "clientId = myclientid\n"
+            + "clientSecret = mysecret\n"
+            + configText);
+    return new GitHubConfig(gerritConfig, site, ALL_PROJECTS_NAME_PROVIDER, null);
+  }
+}
diff --git a/pom.xml b/pom.xml
index 326ce5c..5d6b07e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -310,6 +310,12 @@
       <version>4.13.2</version>
       <scope>test</scope>
     </dependency>
+        <dependency>
+            <groupId>com.google.truth</groupId>
+            <artifactId>truth</artifactId>
+            <version>1.1.4</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
   </dependencyManagement>
 </project>