This is an automated email from the ASF dual-hosted git repository.

mdrob pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new efc7331  SOLR-15249 Properly set ZK ACLs on /security.json
efc7331 is described below

commit efc73319a7c29d8c2c96a287941f740a27fe0a76
Author: Mike Drob <[email protected]>
AuthorDate: Mon Mar 15 15:50:45 2021 -0500

    SOLR-15249 Properly set ZK ACLs on /security.json
---
 .../randomization/policies/solr-tests.policy       |   4 +
 solr/CHANGES.txt                                   |   2 +
 .../java/org/apache/solr/cloud/ZkController.java   |   7 +-
 .../apache/solr/cloud/KerberosTestServices.java    |  52 ++++-----
 .../src/test/org/apache/solr/cloud/LocaleTest.java |  90 +++++++++++++++
 .../apache/solr/cloud/SaslZkACLProviderTest.java   |  12 +-
 .../VMParamsZkACLAndCredentialsProvidersTest.java  | 122 +++++++--------------
 .../authentication-and-authorization-plugins.adoc  |   2 +
 .../src/basic-authentication-plugin.adoc           |   9 +-
 .../src/zookeeper-access-control.adoc              |  38 +++++--
 .../solr/common/cloud/SaslZkACLProvider.java       |  17 ++-
 .../common/cloud/SecurityAwareZkACLProvider.java   |   9 +-
 .../org/apache/solr/common/cloud/SolrZkClient.java |  15 ++-
 .../VMParamsAllAndReadonlyDigestZkACLProvider.java |   4 +-
 .../apache/solr/common/cloud/ZkCmdExecutor.java    |  30 ++++-
 .../java/org/apache/solr/cloud/ZkTestServer.java   |  37 +++++--
 16 files changed, 274 insertions(+), 176 deletions(-)

diff --git a/gradle/testing/randomization/policies/solr-tests.policy 
b/gradle/testing/randomization/policies/solr-tests.policy
index fa51ca4..5330bb2 100644
--- a/gradle/testing/randomization/policies/solr-tests.policy
+++ b/gradle/testing/randomization/policies/solr-tests.policy
@@ -147,6 +147,10 @@ grant {
 
   // SASL/Kerberos related properties for Solr tests
   permission javax.security.auth.PrivateCredentialPermission 
"javax.security.auth.kerberos.KerberosTicket * \"*\"", "read";
+
+  // Needed by zookeeper to configure SASL Auth in tests
+  permission javax.security.auth.AuthPermission "createLoginContext.Server";
+  permission javax.security.auth.AuthPermission "createLoginContext.Client";
   
   // may only be necessary with Java 7?
   permission javax.security.auth.PrivateCredentialPermission 
"javax.security.auth.kerberos.KeyTab * \"*\"", "read";
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 0cc414e..0b0e237 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -240,6 +240,8 @@ Bug Fixes
 
 * SOLR-15162: Allow readOnly parameter to be used with v2 modify collection 
command (Eric Pugh)
 
+* SOLR-15249: Properly set ZK ACLs on /security.json (Mike Drob)
+
 ==================  8.9.0 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this 
release.
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkController.java 
b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
index 232b045..0a7e733 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkController.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkController.java
@@ -109,6 +109,7 @@ import static 
org.apache.solr.common.cloud.ZkStateReader.ELECTION_NODE_PROP;
 import static org.apache.solr.common.cloud.ZkStateReader.NODE_NAME_PROP;
 import static org.apache.solr.common.cloud.ZkStateReader.REJOIN_AT_HEAD_PROP;
 import static org.apache.solr.common.cloud.ZkStateReader.SHARD_ID_PROP;
+import static org.apache.zookeeper.ZooDefs.Ids.OPEN_ACL_UNSAFE;
 
 /**
  * Handle ZooKeeper interactions.
@@ -885,7 +886,11 @@ public class ZkController implements Closeable {
     cmdExecutor.ensureExists(ZkStateReader.COLLECTIONS_ZKNODE, zkClient);
     cmdExecutor.ensureExists(ZkStateReader.ALIASES, zkClient);
     byte[] emptyJson = "{}".getBytes(StandardCharsets.UTF_8);
-    cmdExecutor.ensureExists(ZkStateReader.SOLR_SECURITY_CONF_PATH, emptyJson, 
CreateMode.PERSISTENT, zkClient);
+    cmdExecutor.ensureExists(ZkStateReader.SOLR_SECURITY_CONF_PATH, emptyJson, 
zkClient);
+    if (zkClient.getACL(ZkStateReader.SOLR_SECURITY_CONF_PATH, null, 
true).equals(OPEN_ACL_UNSAFE)) {
+      log.warn("Contents of zookeeper /security.json are world-readable;" +
+          " consider setting up ACLs as described in 
https://solr.apache.org/guide/zookeeper-access-control.html";);
+    }
     bootstrapDefaultConfigSet(zkClient);
   }
 
diff --git a/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java 
b/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
index 0e9587b..35ce634 100644
--- a/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
+++ b/solr/core/src/test/org/apache/solr/cloud/KerberosTestServices.java
@@ -39,19 +39,17 @@ public class KerberosTestServices {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   
   private volatile MiniKdc kdc;
-  private volatile JaasConfiguration jaasConfiguration;
-  private volatile Configuration savedConfig;
+  private final JaasConfiguration jaasConfiguration;
+  private final Configuration savedConfig;
   private volatile Locale savedLocale;
 
-  private volatile File workDir;
+  private final File workDir;
 
   private KerberosTestServices(File workDir,
                                JaasConfiguration jaasConfiguration,
-                               Configuration savedConfig,
-                               Locale savedLocale) {
+                               Configuration savedConfig) {
     this.jaasConfiguration = jaasConfiguration;
     this.savedConfig = savedConfig;
-    this.savedLocale = savedLocale;
     this.workDir = workDir;
   }
 
@@ -60,7 +58,8 @@ public class KerberosTestServices {
   }
 
   public void start() throws Exception {
-    if 
(brokenLanguagesWithMiniKdc.contains(Locale.getDefault().getLanguage())) {
+    if 
(incompatibleLanguagesWithMiniKdc.contains(Locale.getDefault().getLanguage())) {
+      savedLocale = Locale.getDefault();
       Locale.setDefault(Locale.US);
     }
 
@@ -94,7 +93,7 @@ public class KerberosTestServices {
     if (kdc != null) kdc.stop();
     Configuration.setConfiguration(savedConfig);
     Krb5HttpClientBuilder.regenerateJaasConfiguration();
-    Locale.setDefault(savedLocale);
+    if (savedLocale != null) Locale.setDefault(savedLocale);
   }
 
   public static Builder builder() {
@@ -144,7 +143,7 @@ public class KerberosTestServices {
         clientOptions.put("debug", "true");
       }
       clientEntry = new AppConfigurationEntry[]{
-          new AppConfigurationEntry(getKrb5LoginModuleName(),
+          new AppConfigurationEntry(krb5LoginModuleName,
               AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
               clientOptions)};
       if(serverPrincipal!=null && serverKeytab!=null) {
@@ -152,7 +151,7 @@ public class KerberosTestServices {
         serverOptions.put("principal", serverPrincipal);
         serverOptions.put("keytab", serverKeytab.getAbsolutePath());
         serverEntry =  new AppConfigurationEntry[]{
-            new AppConfigurationEntry(getKrb5LoginModuleName(),
+            new AppConfigurationEntry(krb5LoginModuleName,
                 AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                 serverOptions)};
       }
@@ -181,27 +180,19 @@ public class KerberosTestServices {
       }
       return null;
     }
-
-    private String getKrb5LoginModuleName() {
-      String krb5LoginModuleName;
-      if (System.getProperty("java.vendor").contains("IBM")) {
-        krb5LoginModuleName = "com.ibm.security.auth.module.Krb5LoginModule";
-      } else {
-        krb5LoginModuleName = "com.sun.security.auth.module.Krb5LoginModule";
-      }
-      return krb5LoginModuleName;
-    }
   }
 
+  public final static String krb5LoginModuleName =
+    System.getProperty("java.vendor").contains("IBM") ?
+      "com.ibm.security.auth.module.Krb5LoginModule" :
+      "com.sun.security.auth.module.Krb5LoginModule";
+
   /**
-   *  These Locales don't generate dates that are compatibile with Hadoop 
MiniKdc.
+   *  These Locales don't generate dates that are compatible with Hadoop 
MiniKdc.
+   *  See LocaleTest.java and https://issues.apache.org/jira/browse/DIRKRB-753
    */
-  private final static List<String> brokenLanguagesWithMiniKdc =
-      Arrays.asList(
-          new Locale("th").getLanguage(),
-          new Locale("ja").getLanguage(),
-          new Locale("hi").getLanguage()
-      );
+  private final static List<String> incompatibleLanguagesWithMiniKdc = 
Arrays.asList(
+    "mzn", "ps", "mr", "uz", "ks", "bn", "my", "sd", "pa", "ar", "th", "dz", 
"ja", "ne", "ckb", "fa", "lrc", "ur", "ig");
 
   public static class Builder {
     private File kdcWorkDir;
@@ -210,11 +201,8 @@ public class KerberosTestServices {
     private String serverPrincipal;
     private File serverKeytab;
     private String appName;
-    private Locale savedLocale;
 
-    public Builder() {
-      savedLocale = Locale.getDefault();
-    }
+    public Builder() { }
 
     public Builder withKdc(File kdcWorkDir) {
       this.kdcWorkDir = kdcWorkDir;
@@ -248,7 +236,7 @@ public class KerberosTestServices {
             new JaasConfiguration(clientPrincipal, clientKeytab, 
serverPrincipal, serverKeytab) :
             new JaasConfiguration(clientPrincipal, clientKeytab, appName);
       }
-      return new KerberosTestServices(kdcWorkDir, jaasConfiguration, 
oldConfig, savedLocale);
+      return new KerberosTestServices(kdcWorkDir, jaasConfiguration, 
oldConfig);
     }
   }
 }
diff --git a/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java 
b/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java
new file mode 100644
index 0000000..040fe97
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/LocaleTest.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.solr.cloud;
+
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.util.LogLevel;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
[email protected](bugUrl = 
"https://issues.apache.org/jira/browse/DIRKRB-753";)
+@LogLevel("org.apache.kerby=WARN")
+public class LocaleTest extends SolrTestCase {
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @Test
+  public void testWithKdc() throws Exception {
+    final String principal = "server";
+
+    Path kdcDir = createTempDir().resolve("miniKdc");
+    File keytabFile = kdcDir.resolve("keytabs").toFile();
+
+    Properties conf = MiniKdc.createConf();
+    conf.setProperty("kdc.port", "0");
+    MiniKdc kdc = new MiniKdc(conf, kdcDir.toFile());
+    kdc.start();
+    kdc.createPrincipal(keytabFile, principal);
+
+    AppConfigurationEntry appConfigEntry = new AppConfigurationEntry(
+        KerberosTestServices.krb5LoginModuleName,
+        AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+        Map.of("principal", principal,
+            "storeKey", "true",
+            "useKeyTab", "true",
+            "useTicketCache", "false",
+            "refreshKrb5Config", "true",
+            "keyTab", keytabFile.getAbsolutePath(),
+            "keytab", keytabFile.getAbsolutePath())
+    );
+    Configuration configuration = new Configuration() {
+      @Override
+      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+        return new AppConfigurationEntry[]{appConfigEntry};
+      }
+    };
+
+    List<Locale> locales = new ArrayList<>();
+    for (Locale locale : Locale.getAvailableLocales()) {
+      try {
+        Locale.setDefault(locale);
+        new LoginContext("Server", null, null, configuration).login();
+      } catch (LoginException e) {
+        locales.add(locale);
+      }
+    }
+    log.error("Could not login with locales {}", 
locales.stream().collect(Collectors.groupingBy(Locale::getLanguage)));
+
+    kdc.stop();
+  }
+}
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java 
b/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
index 0bdb149..f1875d6 100644
--- a/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/SaslZkACLProviderTest.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 
 import org.apache.lucene.util.Constants;
@@ -50,14 +51,12 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
 
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private static final Charset DATA_ENCODING = Charset.forName("UTF-8");
+  private static final Charset DATA_ENCODING = StandardCharsets.UTF_8;
 
   protected ZkTestServer zkServer;
 
   @BeforeClass
   public static void beforeClass() {
-    assumeFalse("FIXME: This test fails on Java 9 
(https://issues.apache.org/jira/browse/SOLR-8052)", 
Constants.JRE_IS_MINIMUM_JAVA9);
-    
     assumeFalse("FIXME: SOLR-7040: This test fails under IBM J9",
                 Constants.JAVA_VENDOR.startsWith("IBM"));
     System.setProperty("solrcloud.skip.autorecovery", "true");
@@ -122,7 +121,6 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
   }
 
   @Test
-  @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/SOLR-13075";)
   public void testSaslZkACLProvider() throws Exception {
     // Test with Sasl enabled
     SolrZkClient zkClient = new SolrZkClientWithACLs(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT);
@@ -182,7 +180,7 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
    * A ZkTestServer with Sasl support
    */
   public static class SaslZkTestServer extends ZkTestServer {
-    private Path kdcDir;
+    private final Path kdcDir;
     private KerberosTestServices kerberosTestServices;
 
     public SaslZkTestServer(Path zkDir, Path kdcDir) throws Exception {
@@ -204,7 +202,6 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
         kerberosTestServices = KerberosTestServices.builder()
             .withKdc(kdcDir.toFile())
             .withJaasConfiguration(zkClientPrincipal, keytabFile, 
zkServerPrincipal, keytabFile)
-           
             .build();
         kerberosTestServices.start();
 
@@ -221,7 +218,8 @@ public class SaslZkACLProviderTest extends SolrTestCaseJ4 {
       System.clearProperty("zookeeper.kerberos.removeRealmFromPrincipal");
       System.clearProperty("zookeeper.kerberos.removeHostFromPrincipal");
       super.shutdown();
-      kerberosTestServices.stop();
+      if (kerberosTestServices != null)
+        kerberosTestServices.stop();
     }
   }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
 
b/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
index b9db03d..a8a3c74 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/VMParamsZkACLAndCredentialsProvidersTest.java
@@ -38,7 +38,7 @@ public class VMParamsZkACLAndCredentialsProvidersTest extends 
SolrTestCaseJ4 {
   
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   
-  private static final Charset DATA_ENCODING = Charset.forName("UTF-8");
+  private static final Charset DATA_ENCODING = StandardCharsets.UTF_8;
   
   protected ZkTestServer zkServer;
   
@@ -111,28 +111,22 @@ public class VMParamsZkACLAndCredentialsProvidersTest 
extends SolrTestCaseJ4 {
   @Test
   public void testNoCredentials() throws Exception {
     useNoCredentials();
-    
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT);
-    try {
+
+    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT)) {
       doTest(zkClient,
-          false, false, false, false, false,
-          false, false, false, false, false);
-    } finally {
-      zkClient.close();
+              false, false, false, false, false,
+              false, false, false, false, false);
     }
   }
 
   @Test
   public void testWrongCredentials() throws Exception {
     useWrongCredentials();
-    
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT);
-    try {
+
+    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT)) {
       doTest(zkClient,
-          false, false, false, false, false,
-          false, false, false, false, false);
-    } finally {
-      zkClient.close();
+              false, false, false, false, false,
+              false, false, false, false, false);
     }
   }
 
@@ -140,13 +134,10 @@ public class VMParamsZkACLAndCredentialsProvidersTest 
extends SolrTestCaseJ4 {
   public void testAllCredentials() throws Exception {
     useAllCredentials();
 
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT);
-    try {
+    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT)) {
       doTest(zkClient,
-          true, true, true, true, true,
-          true, true, true, true, true);
-    } finally {
-      zkClient.close();
+              true, true, true, true, true,
+              true, true, true, true, true);
     }
   }
   
@@ -154,13 +145,10 @@ public class VMParamsZkACLAndCredentialsProvidersTest 
extends SolrTestCaseJ4 {
   public void testReadonlyCredentials() throws Exception {
     useReadonlyCredentials();
 
-    SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT);
-    try {
+    try (SolrZkClient zkClient = new SolrZkClient(zkServer.getZkAddress(), 
AbstractZkTestCase.TIMEOUT)) {
       doTest(zkClient,
-          true, true, false, false, false,
-          false, false, false, false, false);
-    } finally {
-      zkClient.close();
+              true, true, false, false, false,
+              false, false, false, false, false);
     }
   }
     
@@ -176,65 +164,35 @@ public class VMParamsZkACLAndCredentialsProvidersTest 
extends SolrTestCaseJ4 {
   }
   
   protected static void doTest(SolrZkClient zkClient, String path, boolean 
getData, boolean list, boolean create, boolean setData, boolean delete) throws 
Exception {
-    try {
-      zkClient.getData(path, null, null, false);
-      if (!getData) fail("NoAuthException expected ");
-    } catch (NoAuthException nae) {
-      if (getData) fail("No NoAuthException expected");
-      // expected
-    }
-    
-    try {
-      zkClient.getChildren(path, null, false);
-      if (!list) fail("NoAuthException expected ");
-    } catch (NoAuthException nae) {
-      if (list) fail("No NoAuthException expected");
-      // expected
-    }
-    
-    try {
+    doTest(getData, () -> zkClient.getData(path, null, null, false));
+    doTest(list, () -> zkClient.getChildren(path, null, false));
+
+    doTest(create, () -> {
       zkClient.create(path + "/subnode", null, CreateMode.PERSISTENT, false);
-      if (!create) fail("NoAuthException expected ");
-      else {
-        zkClient.delete(path + "/subnode", -1, false);
-      }
-    } catch (NoAuthException nae) {
-      if (create) {
-        nae.printStackTrace();
-        fail("No NoAuthException expected");
-      }
-      // expected
-    }
-    
-    try {
+      zkClient.delete(path + "/subnode", -1, false);
+    });
+    doTest(create, () -> {
       zkClient.makePath(path + "/subnode/subsubnode", false);
-      if (!create) fail("NoAuthException expected ");
-      else {
-        zkClient.delete(path + "/subnode/subsubnode", -1, false);
-        zkClient.delete(path + "/subnode", -1, false);
-      }
-    } catch (NoAuthException nae) {
-      if (create) fail("No NoAuthException expected");
-      // expected
-    }
-    
-    try {
-      zkClient.setData(path, (byte[])null, false);
-      if (!setData) fail("NoAuthException expected ");
-    } catch (NoAuthException nae) {
-      if (setData) fail("No NoAuthException expected");
-      // expected
-    }
+      zkClient.delete(path + "/subnode/subsubnode", -1, false);
+      zkClient.delete(path + "/subnode", -1, false);
+    });
 
-    try {
-      // Actually about the ACLs on /solr, but that is protected
-      zkClient.delete(path, -1, false);
-      if (!delete) fail("NoAuthException expected ");
-    } catch (NoAuthException nae) {
-      if (delete) fail("No NoAuthException expected");
-      // expected
-    }
+    doTest(setData, () -> zkClient.setData(path, (byte[])null, false));
 
+    // Actually about the ACLs on /solr, but that is protected
+    doTest(delete, () -> zkClient.delete(path, -1, false));
+  }
+
+  interface ExceptingRunnable {
+    void run() throws Exception;
+  }
+
+  private static void doTest(boolean shouldSucceed, ExceptingRunnable action) 
throws Exception {
+    if (shouldSucceed) {
+      action.run();
+    } else {
+      expectThrows(NoAuthException.class, action::run);
+    }
   }
   
   private void useNoCredentials() {
diff --git 
a/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc 
b/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
index 5b237c6..965006b 100644
--- a/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
+++ b/solr/solr-ref-guide/src/authentication-and-authorization-plugins.adoc
@@ -88,6 +88,8 @@ Then use the `bin/solr zk` command to upload the file:
 >bin/solr zk cp ./security.json zk:security.json -z localhost:2181
 ----
 
+NOTE: If you have defined `ZK_HOST` in `solr.in.sh`/`solr.in.cmd` (see 
<<setting-up-an-external-zookeeper-ensemble#updating-solr-include-files,instructions>>)
 you can omit `-z <zk host string>` from the above command.
+
 [WARNING]
 ====
 Whenever you use any security plugins and store `security.json` in ZooKeeper, 
we highly recommend that you implement access control in your ZooKeeper nodes. 
Information about how to enable this is available in the section 
<<zookeeper-access-control.adoc#,ZooKeeper Access Control>>.
diff --git a/solr/solr-ref-guide/src/basic-authentication-plugin.adoc 
b/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
index c6c3b37..28ca11a 100644
--- a/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
+++ b/solr/solr-ref-guide/src/basic-authentication-plugin.adoc
@@ -64,14 +64,7 @@ If `blockUnknown` does not appear in the `security.json` 
file, it will default t
 
 If `realm` is not defined, it will default to `solr`.
 
-If you are using SolrCloud, you must upload `security.json` to ZooKeeper. You 
can use this example command, ensuring that the ZooKeeper port is correct:
-
-[source,bash]
-----
-$ bin/solr zk cp file:path_to_local_security.json zk:/security.json -z 
localhost:9983
-----
-
-NOTE: If you have defined `ZK_HOST` in `solr.in.sh`/`solr.in.cmd` (see 
<<setting-up-an-external-zookeeper-ensemble#updating-solr-include-files,instructions>>)
 you can omit `-z <zk host string>` from the above command.
+If you are using SolrCloud, you must upload `security.json` to ZooKeeper. An 
example command and more information about securing your setup can be found at 
<<authentication-and-authorization-plugins#in-solrcloud-mode,Authentication and 
Authorization Plugins In SolrCloud Mode>>.
 
 === Caveats
 
diff --git a/solr/solr-ref-guide/src/zookeeper-access-control.adoc 
b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
index 57e9af5..9a90458 100644
--- a/solr/solr-ref-guide/src/zookeeper-access-control.adoc
+++ b/solr/solr-ref-guide/src/zookeeper-access-control.adoc
@@ -73,7 +73,7 @@ You control which ACLs will be added by configuring 
`zkACLProvider` property in
 You can always make you own implementation, but Solr comes with:
 
 * `org.apache.solr.common.cloud.DefaultZkACLProvider`: It returns a list of 
length one for all `zNodePath`-s. The single ACL entry in the list is 
"open-unsafe". This is the default.
-* `org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider`: 
This lets you define your ACLs using system properties. Its `getACLsToAdd()` 
implementation does not use `zNodePath` for anything, so all znodes will get 
the same set of ACLs. It supports adding one or both of these options:
+* `org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider`: 
This lets you define your ACLs using system properties. Its `getACLsToAdd()` 
implementation will apply only admin ACLs to pre-defined sensitive paths as 
defined by `SecurityAwareZkACLProvider` (`/security.json` and `/security/*`) 
and both admin and user ACLs to the rest of the contents. The two sets of roles 
will be defined as:
 ** A user that is allowed to do everything.
 *** The permission is `ALL` (corresponding to all of `CREATE`, `READ`, 
`WRITE`, `DELETE`, and `ADMIN`), and the schema is "digest".
 *** The username and password are defined by system properties 
`zkDigestUsername` and `zkDigestPassword`, respectively.
@@ -97,8 +97,17 @@ There are two scripts that impact ZooKeeper ACLs:
 * For *nix systems: `bin/solr` & `server/scripts/cloud-scripts/zkcli.sh`
 * For Windows systems: `bin/solr.cmd` & 
`server/scripts/cloud-scripts/zkcli.bat`
 
+[IMPORTANT]
+Both the solr.in.* and the zkcli.* files will need to be updated with the same 
password for everything to work. The contents may appear redundant, but the 
scripts will not consult each other during operations.
+
 These Solr scripts can enable use of ZooKeeper ACLs by setting the appropriate 
system properties: uncomment the following and replace the passwords with ones 
you choose to enable the above-described VM parameters ACL and credentials 
providers in the following files:
 
+[.dynamic-tabs]
+--
+[example.tab-pane#nix]
+====
+[.tab-label]**nix*
+
 .solr.in.sh
 [source,bash]
 ----
@@ -110,6 +119,21 @@ These Solr scripts can enable use of ZooKeeper ACLs by 
setting the appropriate s
 #SOLR_OPTS="$SOLR_OPTS $SOLR_ZK_CREDS_AND_ACLS"
 ----
 
+.zkcli.sh
+[source,bash]
+----
+# Settings for ZK ACL
+#SOLR_ZK_CREDS_AND_ACLS="-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider
 \
+#  
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider
 \
+#  -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD \
+#  -DzkDigestReadonlyUsername=readonly-user 
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD"
+----
+====
+
+[example.tab-pane#windows]
+====
+[.tab-label]*Windows*
+
 .solr.in.cmd
 [source,powershell]
 ----
@@ -121,16 +145,6 @@ REM  -DzkDigestReadonlyUsername=readonly-user 
-DzkDigestReadonlyPassword=CHANGEM
 REM set SOLR_OPTS=%SOLR_OPTS% %SOLR_ZK_CREDS_AND_ACLS%
 ----
 
-.zkcli.sh
-[source,bash]
-----
-# Settings for ZK ACL
-#SOLR_ZK_CREDS_AND_ACLS="-DzkACLProvider=org.apache.solr.common.cloud.VMParamsAllAndReadonlyDigestZkACLProvider
 \
-#  
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCredentialsDigestZkCredentialsProvider
 \
-#  -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD \
-#  -DzkDigestReadonlyUsername=readonly-user 
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD"
-----
-
 .zkcli.bat
 [source,powershell]
 ----
@@ -140,6 +154,8 @@ REM  
-DzkCredentialsProvider=org.apache.solr.common.cloud.VMParamsSingleSetCrede
 REM  -DzkDigestUsername=admin-user -DzkDigestPassword=CHANGEME-ADMIN-PASSWORD ^
 REM  -DzkDigestReadonlyUsername=readonly-user 
-DzkDigestReadonlyPassword=CHANGEME-READONLY-PASSWORD
 ----
+====
+--
 
 == Changing ACL Schemes
 
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
index c67ad12..793169b 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/SaslZkACLProvider.java
@@ -16,7 +16,8 @@
  */
 package org.apache.solr.common.cloud;
 
-import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.zookeeper.ZooDefs;
@@ -32,20 +33,18 @@ import org.apache.zookeeper.data.Id;
  */
 public class SaslZkACLProvider extends SecurityAwareZkACLProvider {
 
-  private static String superUser = 
System.getProperty("solr.authorization.superuser", "solr");
+  private static final String superUser = 
System.getProperty("solr.authorization.superuser", "solr");
 
   @Override
   protected List<ACL> createNonSecurityACLsToAdd() {
-    List<ACL> ret = new ArrayList<ACL>();
-    ret.add(new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)));
-    ret.add(new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE));
-    return ret;
+    return Arrays.asList(
+      new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)),
+      new ACL(ZooDefs.Perms.READ, ZooDefs.Ids.ANYONE_ID_UNSAFE)
+    );
   }
 
   @Override
   protected List<ACL> createSecurityACLsToAdd() {
-    List<ACL> ret = new ArrayList<ACL>();
-    ret.add(new ACL(ZooDefs.Perms.ALL, new Id("sasl", superUser)));
-    return ret;
+    return Collections.singletonList(new ACL(ZooDefs.Perms.ALL, new Id("sasl", 
superUser)));
   }
 }
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
 
b/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
index 1c74d94..2fe2da9 100644
--- 
a/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/common/cloud/SecurityAwareZkACLProvider.java
@@ -22,7 +22,7 @@ import org.apache.zookeeper.data.ACL;
 
 /**
  * {@link ZkACLProvider} capable of returning a different set of
- * {@link ACL}s for security-related znodes (default: subtree under /security)
+ * {@link ACL}s for security-related znodes (default: subtree under /security 
and security.json)
  * vs non-security-related znodes.
  */
 public abstract class SecurityAwareZkACLProvider implements ZkACLProvider {
@@ -42,11 +42,8 @@ public abstract class SecurityAwareZkACLProvider implements 
ZkACLProvider {
   }
 
   protected boolean isSecurityZNodePath(String zNodePath) {
-    if (zNodePath != null
-        && (zNodePath.equals(SECURITY_ZNODE_PATH) || 
zNodePath.startsWith(SECURITY_ZNODE_PATH + "/"))) {
-      return true;
-    }
-    return false;
+    return zNodePath != null
+        && (zNodePath.equals(ZkStateReader.SOLR_SECURITY_CONF_PATH) || 
zNodePath.equals(SECURITY_ZNODE_PATH) || 
zNodePath.startsWith(SECURITY_ZNODE_PATH + "/"));
   }
 
   /**
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
index 7a12c92..01078b8 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/SolrZkClient.java
@@ -786,11 +786,23 @@ public class SolrZkClient implements Closeable {
   }
 
   /**
+   * @return the ACLs on a single node in ZooKeeper.
+   */
+  public List<ACL> getACL(String path, Stat stat, boolean retryOnConnLoss) 
throws KeeperException, InterruptedException {
+    if (retryOnConnLoss) {
+      return zkCmdExecutor.retryOperation(() -> keeper.getACL(path, stat));
+    } else {
+      return keeper.getACL(path, stat);
+    }
+  }
+
+  /**
    * Set the ACL on a single node in ZooKeeper. This will replace all existing 
ACL on that node.
    *
    * @param path path to set ACL on e.g. /solr/conf/solrconfig.xml
    * @param acls a list of {@link ACL}s to be applied
    * @param retryOnConnLoss true if the command should be retried on 
connection loss
+   * @return the stat of the node
    */
   public Stat setACL(String path, List<ACL> acls, boolean retryOnConnLoss) 
throws InterruptedException, KeeperException  {
     if (retryOnConnLoss) {
@@ -809,9 +821,8 @@ public class SolrZkClient implements Closeable {
       try {
         setACL(path, getZkACLProvider().getACLsToAdd(path), true);
         log.debug("Updated ACL on {}", path);
-      } catch (NoNodeException e) {
+      } catch (NoNodeException ignored) {
         // If a node was deleted, don't bother trying to set ACLs on it.
-        return;
       }
     });
   }
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
 
b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
index 8a41d06..041762a 100644
--- 
a/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
+++ 
b/solr/solrj/src/java/org/apache/solr/common/cloud/VMParamsAllAndReadonlyDigestZkACLProvider.java
@@ -88,7 +88,7 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider 
extends SecurityAwareZkAC
                                       String digestReadonlyUsername, String 
digestReadonlyPassword) {
 
       try {
-      List<ACL> result = new ArrayList<ACL>();
+      List<ACL> result = new ArrayList<>(2);
   
       // Not to have to provide too much credentials and ACL information to 
the process it is assumed that you want "ALL"-acls
       // added to the user you are using to connect to ZK (if you are using 
VMParamsSingleSetCredentialsDigestZkCredentialsProvider)
@@ -109,7 +109,7 @@ public class VMParamsAllAndReadonlyDigestZkACLProvider 
extends SecurityAwareZkAC
       
       return result;
     } catch (NoSuchAlgorithmException e) {
-      throw new RuntimeException(e);
+      throw new RuntimeException("JVM mis-configured: missing SHA-1 
algorithm", e);
     }
   }
 
diff --git 
a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java 
b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
index 3598b0a..61be129 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/ZkCmdExecutor.java
@@ -101,19 +101,38 @@ public class ZkCmdExecutor {
     return isClosed != null && isClosed.isClosed();
   }
 
+  /**
+   * Create a persistent znode with no data if it does not already exist
+   * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+   */
   public void ensureExists(String path, final SolrZkClient zkClient) throws 
KeeperException, InterruptedException {
     ensureExists(path, null, CreateMode.PERSISTENT, zkClient, 0);
   }
-  
-  
+
+  /**
+   * Create a persistent znode with the given data if it does not already exist
+   * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+   */
   public void ensureExists(String path, final byte[] data, final SolrZkClient 
zkClient) throws KeeperException, InterruptedException {
     ensureExists(path, data, CreateMode.PERSISTENT, zkClient, 0);
   }
-  
+
+  /**
+   * Create a znode with the given mode and data if it does not already exist
+   * @see #ensureExists(String, byte[], CreateMode, SolrZkClient, int)
+   */
   public void ensureExists(String path, final byte[] data, CreateMode 
createMode, final SolrZkClient zkClient) throws KeeperException, 
InterruptedException {
     ensureExists(path, data, createMode, zkClient, 0);
   }
-  
+
+  /**
+   * Create a node if it does not exist
+   * @param path the path at which to create the znode
+   * @param data the optional data to set on the znode
+   * @param createMode the mode with which to create the znode
+   * @param zkClient the client to use to check and create
+   * @param skipPathParts how many path elements to skip
+   */
   public void ensureExists(final String path, final byte[] data,
       CreateMode createMode, final SolrZkClient zkClient, int skipPathParts) 
throws KeeperException, InterruptedException {
     
@@ -122,10 +141,9 @@ public class ZkCmdExecutor {
     }
     try {
       zkClient.makePath(path, data, createMode, null, true, true, 
skipPathParts);
-    } catch (NodeExistsException e) {
+    } catch (NodeExistsException ignored) {
       // it's okay if another beats us creating the node
     }
-    
   }
   
   /**
diff --git 
a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java 
b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
index dbb3916..5112aab 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/ZkTestServer.java
@@ -69,6 +69,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class ZkTestServer {
 
@@ -388,11 +389,17 @@ public class ZkTestServer {
           zkDb.close();
         }
 
-        if (cnxnFactory != null && cnxnFactory.getLocalPort() != 0) {
-          waitForServerDown(getZkHost(), 30000);
+        if (cnxnFactory != null) {
+          try {
+            int port = cnxnFactory.getLocalPort();
+            if (port > 0) {
+              waitForServerDown(getZkHost(), 30000);
+            }
+          } catch (NullPointerException ignored) {
+            // server never successfully started
+          }
         }
       } finally {
-
         ObjectReleaseTracker.release(this);
       }
     }
@@ -532,10 +539,12 @@ public class ZkTestServer {
 
   public void run(boolean solrFormat) throws InterruptedException, IOException 
{
     log.info("STARTING ZK TEST SERVER");
+    AtomicReference<Throwable> zooError = new AtomicReference<>();
     try {
       if (zooThread != null) {
         throw new IllegalStateException("ZK TEST SERVER IS ALREADY RUNNING");
       }
+      Thread parentThread = Thread.currentThread();
       // we don't call super.distribSetUp
       zooThread = new Thread("ZkTestServer Run Thread") {
 
@@ -569,7 +578,8 @@ public class ZkTestServer {
           try {
             zkServer.runFromConfig(config);
           } catch (Throwable t) {
-            log.error("zkServer error", t);
+            zooError.set(t);
+            parentThread.interrupt();
           }
         }
       };
@@ -581,15 +591,15 @@ public class ZkTestServer {
       int port = -1;
       try {
         port = getPort();
-      } catch (IllegalStateException e) {
-
+      } catch (IllegalStateException ignored) {
+        // Possibly fix this API to return null instead of throwing
       }
       while (port < 1) {
         Thread.sleep(100);
         try {
           port = getPort();
-        } catch (IllegalStateException e) {
-
+        } catch (IllegalStateException ignored) {
+          // Possibly fix this API to return null instead of throwing
         }
         if (cnt == 500) {
           throw new RuntimeException("Could not get the port for ZooKeeper 
server");
@@ -602,8 +612,15 @@ public class ZkTestServer {
 
       init(solrFormat);
     } catch (Exception e) {
-      log.error("Error trying to run ZK Test Server", e);
-      throw new RuntimeException(e);
+      RuntimeException toThrow = new RuntimeException("Could not get ZK port");
+      Throwable t = zooError.get();
+      if (t != null) {
+        toThrow.initCause(t);
+        toThrow.addSuppressed(e);
+      } else {
+        toThrow.initCause(e);
+      }
+      throw toThrow;
     }
   }
 

Reply via email to