Merge "Add example using broker notification (Kafka)" into stable-3.4
diff --git a/BUILD b/BUILD
index e1f435d..f6a6e48 100644
--- a/BUILD
+++ b/BUILD
@@ -9,6 +9,7 @@
         "Implementation-URL: https://gerrit-review.googlesource.com/#/admin/projects/plugins/pull-replication",
         "Gerrit-PluginName: pull-replication",
         "Gerrit-Module: com.googlesource.gerrit.plugins.replication.pull.PullReplicationModule",
+        "Gerrit-InitStep: com.googlesource.gerrit.plugins.replication.pull.InitPlugin",
         "Gerrit-SshModule: com.googlesource.gerrit.plugins.replication.pull.SshModule",
         "Gerrit-HttpModule: com.googlesource.gerrit.plugins.replication.pull.api.HttpModule",
     ],
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/InitPlugin.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/InitPlugin.java
new file mode 100644
index 0000000..1702948
--- /dev/null
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/InitPlugin.java
@@ -0,0 +1,99 @@
+// 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.replication.pull;
+
+import static com.googlesource.gerrit.plugins.replication.pull.auth.PullReplicationGroupBackend.INTERNAL_GROUP_NAME;
+import static com.googlesource.gerrit.plugins.replication.pull.auth.PullReplicationGroupBackend.INTERNAL_GROUP_UUID;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.entities.GroupReference;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.pgm.init.api.AllProjectsConfig;
+import com.google.gerrit.pgm.init.api.ConsoleUI;
+import com.google.gerrit.pgm.init.api.InitStep;
+import com.google.inject.Inject;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
+
+public class InitPlugin implements InitStep {
+
+  private final String pluginName;
+  private final BearerTokenProvider bearerTokenProvider;
+  private final ConsoleUI ui;
+  private final AllProjectsConfig allProjectsConfig;
+
+  private static final String CAPABILITY_SECTION = "capability";
+
+  @Inject
+  InitPlugin(
+      @PluginName String pluginName,
+      BearerTokenProvider bearerTokenProvider,
+      ConsoleUI ui,
+      AllProjectsConfig allProjectsConfig) {
+    this.pluginName = pluginName;
+    this.bearerTokenProvider = bearerTokenProvider;
+    this.ui = ui;
+    this.allProjectsConfig = allProjectsConfig;
+  }
+
+  @Override
+  public void run() throws Exception {
+    ui.header("%s initialization", pluginName);
+
+    if (!bearerTokenProvider.get().isPresent()) {
+      ui.message(
+          "The %s plugin is not configured to use bearer token. If you are using basic auth, remember to grant the '%s' global capability to all relevant users\n",
+          pluginName, GlobalCapability.ACCESS_DATABASE);
+      return;
+    }
+
+    ui.message(
+        "Granting '%s' global capability to the '%s' user\n",
+        GlobalCapability.ACCESS_DATABASE, INTERNAL_GROUP_NAME);
+
+    upsertCapability();
+
+    ui.message(
+        "'%s' global capability granted to user '%s'\n",
+        GlobalCapability.ACCESS_DATABASE, INTERNAL_GROUP_NAME);
+  }
+
+  private void upsertCapability() throws ConfigInvalidException, IOException {
+    String pullReplicationGroup = "group " + INTERNAL_GROUP_NAME;
+
+    Config cfg = allProjectsConfig.load().getConfig();
+    String[] groupsWithAccessDatabase =
+        cfg.getStringList(CAPABILITY_SECTION, null, GlobalCapability.ACCESS_DATABASE);
+
+    if (Arrays.stream(groupsWithAccessDatabase).noneMatch(pullReplicationGroup::equals)) {
+      cfg.setStringList(
+          CAPABILITY_SECTION,
+          null,
+          GlobalCapability.ACCESS_DATABASE,
+          Arrays.stream(ArrayUtils.add(groupsWithAccessDatabase, pullReplicationGroup))
+              .collect(Collectors.toList()));
+
+      allProjectsConfig
+          .getGroups()
+          .put(
+              INTERNAL_GROUP_UUID, GroupReference.create(INTERNAL_GROUP_UUID, INTERNAL_GROUP_NAME));
+
+      allProjectsConfig.save(pluginName, "Init step");
+    }
+  }
+}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/auth/PullReplicationGroupBackend.java b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/auth/PullReplicationGroupBackend.java
index 9521004..2492c81 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/replication/pull/auth/PullReplicationGroupBackend.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/replication/pull/auth/PullReplicationGroupBackend.java
@@ -30,7 +30,7 @@
 
 /** Backend to expose the pull-replication internal user group membership. */
 @Singleton
-class PullReplicationGroupBackend extends AbstractGroupBackend {
+public class PullReplicationGroupBackend extends AbstractGroupBackend {
   public static final AccountGroup.UUID INTERNAL_GROUP_UUID =
       AccountGroup.uuid("pullreplication:internal-user");
   public static final String INTERNAL_GROUP_NAME = "Pull-replication Internal User";