Allow configuring Zookeeper authentication

This change enables authentication when the plugin connects to
Zookeeper cluster.

Bug: Issue 340582184
Change-Id: I4f15a73dbdfbe87b6e887d64e702755260e15f05
diff --git a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
index aa62862..b6abb40 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/validation/dfsrefdb/zookeeper/ZookeeperConfig.java
@@ -20,12 +20,16 @@
 import com.google.gerrit.extensions.annotations.PluginName;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Inject;
+import java.util.List;
 import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.curator.RetryPolicy;
 import org.apache.curator.framework.CuratorFramework;
 import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.ACLProvider;
 import org.apache.curator.retry.BoundedExponentialBackoffRetry;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,6 +56,8 @@
 
   public static final String SUBSECTION = "zookeeper";
   public static final String KEY_CONNECT_STRING = "connectString";
+  public static final String KEY_USERNAME = "username";
+  public static final String KEY_PASSWORD = "password";
   public static final String KEY_SESSION_TIMEOUT_MS = "sessionTimeoutMs";
   public static final String KEY_CONNECTION_TIMEOUT_MS = "connectionTimeoutMs";
   public static final String KEY_RETRY_POLICY_BASE_SLEEP_TIME_MS = "retryPolicyBaseSleepTimeMs";
@@ -69,8 +75,9 @@
   public final String KEY_CAS_RETRY_POLICY_MAX_SLEEP_TIME_MS = "casRetryPolicyMaxSleepTimeMs";
   public final String KEY_CAS_RETRY_POLICY_MAX_RETRIES = "casRetryPolicyMaxRetries";
   public final String TRANSACTION_LOCK_TIMEOUT_KEY = "transactionLockTimeoutMs";
-
   private final String connectionString;
+  private Optional<String> zkUsername;
+  private Optional<String> zkPassword;
   private final String root;
   private final int sessionTimeoutMs;
   private final int connectionTimeoutMs;
@@ -100,6 +107,8 @@
   public ZookeeperConfig(Config zkConfig) {
     connectionString =
         getString(zkConfig, SECTION, SUBSECTION, KEY_CONNECT_STRING, DEFAULT_ZK_CONNECT);
+    zkUsername = getOptionalString(zkConfig, SECTION, SUBSECTION, KEY_USERNAME);
+    zkPassword = getOptionalString(zkConfig, SECTION, SUBSECTION, KEY_PASSWORD);
     root = getString(zkConfig, SECTION, SUBSECTION, KEY_ROOT_NODE, "gerrit/multi-site");
     sessionTimeoutMs =
         getInt(zkConfig, SECTION, SUBSECTION, KEY_SESSION_TIMEOUT_MS, defaultSessionTimeoutMs);
@@ -200,21 +209,45 @@
     }
 
     if (build == null) {
-      this.build =
+      CuratorFrameworkFactory.Builder builder =
           CuratorFrameworkFactory.builder()
               .connectString(connectionString)
               .sessionTimeoutMs(sessionTimeoutMs)
               .connectionTimeoutMs(connectionTimeoutMs)
               .retryPolicy(
                   new BoundedExponentialBackoffRetry(baseSleepTimeMs, maxSleepTimeMs, maxRetries))
-              .namespace(root)
-              .build();
+              .namespace(root);
+      if (zkUsername.isPresent() != zkPassword.isPresent()) {
+        throw new IllegalArgumentException(
+            "Only one between password or username for Zookeeper was set, please set both to successfully authenticate");
+      } else {
+        zkUsername
+            .flatMap(usr -> zkPassword.map(pwd -> usr + ":" + pwd))
+            .ifPresent(auth -> configureAuth(builder, auth));
+      }
+      this.build = builder.build();
       this.build.start();
     }
-
     return this.build;
   }
 
+  private void configureAuth(CuratorFrameworkFactory.Builder builder, String authString) {
+    builder
+        .authorization("digest", authString.getBytes())
+        .aclProvider(
+            new ACLProvider() {
+              @Override
+              public List<ACL> getDefaultAcl() {
+                return ZooDefs.Ids.CREATOR_ALL_ACL;
+              }
+
+              @Override
+              public List<ACL> getAclForPath(String path) {
+                return ZooDefs.Ids.CREATOR_ALL_ACL;
+              }
+            });
+  }
+
   public Long getZkInterProcessLockTimeOut() {
     return transactionLockTimeOut;
   }
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 00a2468..fadf623 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -112,3 +112,11 @@
 
 ```ref-database.zookeeper.sslTrustStorePassword```
 :   Optional configuration for the password to the ssl trust store.
+
+```ref-database.zookeeper.username```
+:   Optional, if authentication is required, configuration for the username to the zookeeper node.
+
+```ref-database.zookeeper.password```
+:   Optional, if authentication is required, configuration for the password to the zookeeper node.
+
+