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

cgivre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git


The following commit(s) were added to refs/heads/master by this push:
     new 6564098fd3 DRILL-8499: new util for creating random strings (#2918)
6564098fd3 is described below

commit 6564098fd310ee488c14783a70de4e5fc95abaa0
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Jun 26 16:32:04 2024 +0100

    DRILL-8499: new util for creating random strings (#2918)
---
 LICENSE                                            |   9 +
 .../drill/yarn/appMaster/http/WebServer.java       |   4 +-
 .../rest/ssl/SslContextFactoryConfigurator.java    |   4 +-
 .../drill/exec/util/SecureRandomStringUtils.java   | 216 +++++++++++++++++++++
 4 files changed, 229 insertions(+), 4 deletions(-)

diff --git a/LICENSE b/LICENSE
index d4d163861e..86f2a884bd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -201,6 +201,15 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 
+---------------------------------------------------------------------------
+This product includes code from Apache Commons Lang3, licensed under the
+Apache License 2.0.
+
+Apache Commons Lang
+Copyright 2001-2024 The Apache Software Foundation
+
+- 
exec/java-exec/src/main/java/org/apache/drill/exec/util/SecureRandomStringUtils.java
+
 ---------------------------------------------------------------------------
 This product includes source licensed under the MIT license.
 
diff --git 
a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java 
b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
index bc7ae1ec72..74861a7b9a 100644
--- 
a/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
+++ 
b/drill-yarn/src/main/java/org/apache/drill/yarn/appMaster/http/WebServer.java
@@ -37,12 +37,12 @@ import javax.servlet.http.HttpSession;
 import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionListener;
 
-import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.drill.exec.server.rest.CsrfTokenInjectFilter;
 import org.apache.drill.exec.server.rest.CsrfTokenValidateFilter;
 import com.google.common.collect.ImmutableSet;
+import org.apache.drill.exec.util.SecureRandomStringUtils;
 import org.apache.drill.yarn.appMaster.Dispatcher;
 import org.apache.drill.yarn.core.DrillOnYarnConfig;
 import org.bouncycastle.asn1.x500.X500NameBuilder;
@@ -428,7 +428,7 @@ public class WebServer implements AutoCloseable {
     certificate.verify(certificate.getPublicKey());
 
     // Generate a random password for keystore protection
-    final String keyStorePasswd = RandomStringUtils.random(20);
+    final String keyStorePasswd = SecureRandomStringUtils.random(20);
     final KeyStore keyStore = KeyStore.getInstance("JKS");
     keyStore.load(null, null);
     keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
index 89421c6919..bbd6897a0c 100644
--- 
a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
@@ -17,11 +17,11 @@
  */
 package org.apache.drill.exec.server.rest.ssl;
 
-import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.ssl.SSLConfig;
 import org.apache.drill.exec.ssl.SSLConfigBuilder;
+import org.apache.drill.exec.util.SecureRandomStringUtils;
 import org.bouncycastle.asn1.x500.X500NameBuilder;
 import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.cert.X509v3CertificateBuilder;
@@ -202,7 +202,7 @@ public class SslContextFactoryConfigurator {
     certificate.verify(certificate.getPublicKey());
 
     // Generate a random password for keystore protection
-    final String keyStorePasswd = RandomStringUtils.random(20);
+    final String keyStorePasswd = SecureRandomStringUtils.random(20);
     final KeyStore keyStore = KeyStore.getInstance("JKS");
     keyStore.load(null, null);
     keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),
diff --git 
a/exec/java-exec/src/main/java/org/apache/drill/exec/util/SecureRandomStringUtils.java
 
b/exec/java-exec/src/main/java/org/apache/drill/exec/util/SecureRandomStringUtils.java
new file mode 100644
index 0000000000..ea0eebc893
--- /dev/null
+++ 
b/exec/java-exec/src/main/java/org/apache/drill/exec/util/SecureRandomStringUtils.java
@@ -0,0 +1,216 @@
+/*
+ * 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.drill.exec.util;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+/**
+ * Based on Commons Lang3 RandomStringUtils, but with a SecureRandom.
+ *
+ * For internal Apache Drill use only.
+ */
+public class SecureRandomStringUtils {
+  // Based on 
https://github.com/apache/commons-lang/blob/5e07d873e6b45714d29bf47634adffa3b5aef098/src/main/java/org/apache/commons/lang3/RandomStringUtils.java
+
+  private static SecureRandom RANDOM_INSTANCE;
+
+  private static SecureRandom random() {
+    if (RANDOM_INSTANCE != null) {
+      return RANDOM_INSTANCE;
+    }
+    try {
+      RANDOM_INSTANCE = SecureRandom.getInstanceStrong();
+      return RANDOM_INSTANCE;
+    } catch (NoSuchAlgorithmException e) {
+      throw new IllegalStateException("Cannot create 
SecureRandom.getInstanceStrong()", e);
+    }
+  }
+
+  // Random
+  /**
+   * Creates a random string whose length is the number of characters
+   * specified.
+   *
+   * <p>Characters will be chosen from the set of all characters.</p>
+   *
+   * @param count  the length of random string to create
+   * @return the random string
+   * @throws IllegalArgumentException if {@code count} &lt; 0.
+   */
+  public static String random(final int count) {
+    return random(count, false, false);
+  }
+
+  /**
+   * Creates a random string whose length is the number of characters
+   * specified.
+   *
+   * <p>Characters will be chosen from the set of alpha-numeric
+   * characters as indicated by the arguments.</p>
+   *
+   * @param count  the length of random string to create
+   * @param letters  if {@code true}, generated string may include
+   *  alphabetic characters
+   * @param numbers  if {@code true}, generated string may include
+   *  numeric characters
+   * @return the random string
+   * @throws IllegalArgumentException if {@code count} &lt; 0.
+   */
+  private static String random(final int count, final boolean letters, final 
boolean numbers) {
+    return random(count, 0, 0, letters, numbers);
+  }
+
+  /**
+   * Creates a random string whose length is the number of characters
+   * specified.
+   *
+   * <p>Characters will be chosen from the set of alpha-numeric
+   * characters as indicated by the arguments.</p>
+   *
+   * @param count  the length of random string to create
+   * @param start  the position in set of chars to start at
+   * @param end  the position in set of chars to end before
+   * @param letters  if {@code true}, generated string may include
+   *  alphabetic characters
+   * @param numbers  if {@code true}, generated string may include
+   *  numeric characters
+   * @return the random string
+   * @throws IllegalArgumentException if {@code count} &lt; 0.
+   */
+  private static String random(final int count, final int start, final int 
end, final boolean letters, final boolean numbers) {
+    return random(count, start, end, letters, numbers, null, random());
+  }
+
+  /**
+   * Creates a random string based on a variety of options, using
+   * supplied source of randomness.
+   *
+   * <p>If start and end are both {@code 0}, start and end are set
+   * to {@code ' '} and {@code 'z'}, the ASCII printable
+   * characters, will be used, unless letters and numbers are both
+   * {@code false}, in which case, start and end are set to
+   * {@code 0} and {@link Character#MAX_CODE_POINT}.
+   *
+   * <p>If set is not {@code null}, characters between start and
+   * end are chosen.</p>
+   *
+   * <p>This method accepts a user-supplied {@link SecureRandom}
+   * instance to use as a source of randomness. By seeding a single
+   * {@link SecureRandom} instance with a fixed seed and using it for each 
call,
+   * the same random sequence of strings can be generated repeatedly
+   * and predictably.</p>
+   *
+   * @param count  the length of random string to create
+   * @param start  the position in set of chars to start at (inclusive)
+   * @param end  the position in set of chars to end before (exclusive)
+   * @param letters  if {@code true}, generated string may include
+   *  alphabetic characters
+   * @param numbers  if {@code true}, generated string may include
+   *  numeric characters
+   * @param chars  the set of chars to choose randoms from, must not be empty.
+   *  If {@code null}, then it will use the set of all chars.
+   * @param random  a source of randomness.
+   * @return the random string
+   * @throws ArrayIndexOutOfBoundsException if there are not
+   *  {@code (end - start) + 1} characters in the set array.
+   * @throws IllegalArgumentException if {@code count} &lt; 0 or the provided 
chars array is empty.
+   */
+  private static String random(int count, int start, int end, final boolean 
letters, final boolean numbers,
+      final char[] chars, final SecureRandom random) {
+    if (count == 0) {
+      return StringUtils.EMPTY;
+    }
+    if (count < 0) {
+      throw new IllegalArgumentException("Requested random string length " + 
count + " is less than 0.");
+    }
+    if (chars != null && chars.length == 0) {
+      throw new IllegalArgumentException("The chars array must not be empty");
+    }
+
+    if (start == 0 && end == 0) {
+      if (chars != null) {
+        end = chars.length;
+      } else if (!letters && !numbers) {
+        end = Character.MAX_CODE_POINT;
+      } else {
+        end = 'z' + 1;
+        start = ' ';
+      }
+    } else if (end <= start) {
+      throw new IllegalArgumentException("Parameter end (" + end + ") must be 
greater than start (" + start + ")");
+    }
+
+    final int zeroDigitAscii = 48;
+    final int firstLetterAscii = 65;
+
+    if (chars == null && (numbers && end <= zeroDigitAscii
+        || letters && end <= firstLetterAscii)) {
+      throw new IllegalArgumentException("Parameter end (" + end + ") must be 
greater then (" + zeroDigitAscii + ") for generating digits " +
+          "or greater then (" + firstLetterAscii + ") for generating 
letters.");
+    }
+
+    final StringBuilder builder = new StringBuilder(count);
+    final int gap = end - start;
+
+    while (count-- != 0) {
+      final int codePoint;
+      if (chars == null) {
+        codePoint = random.nextInt(gap) + start;
+
+        switch (Character.getType(codePoint)) {
+        case Character.UNASSIGNED:
+        case Character.PRIVATE_USE:
+        case Character.SURROGATE:
+          count++;
+          continue;
+        }
+
+      } else {
+        codePoint = chars[random.nextInt(gap) + start];
+      }
+
+      final int numberOfChars = Character.charCount(codePoint);
+      if (count == 0 && numberOfChars > 1) {
+        count++;
+        continue;
+      }
+
+      if (letters && Character.isLetter(codePoint)
+          || numbers && Character.isDigit(codePoint)
+          || !letters && !numbers) {
+        builder.appendCodePoint(codePoint);
+
+        if (numberOfChars == 2) {
+          count--;
+        }
+
+      } else {
+        count++;
+      }
+    }
+    return builder.toString();
+  }
+
+  private SecureRandomStringUtils() {
+    // empty
+  }
+
+}

Reply via email to