drill-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From prog...@apache.org
Subject [1/2] drill git commit: DRILL-5819: Default value of security.admin.user_groups and security.admin.users is true
Date Thu, 12 Oct 2017 00:44:49 GMT
Repository: drill
Updated Branches:
  refs/heads/master fe79a633a -> 6ad983833


DRILL-5819: Default value of security.admin.user_groups and security.admin.users is true

closes #983


Project: http://git-wip-us.apache.org/repos/asf/drill/repo
Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/7aeaf0e3
Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/7aeaf0e3
Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/7aeaf0e3

Branch: refs/heads/master
Commit: 7aeaf0e3d8ff1099131df7eb4331cdadf299b297
Parents: fe79a63
Author: karthik <kmanivannan@maprtech.com>
Authored: Wed Oct 4 11:23:04 2017 -0700
Committer: Paul Rogers <progers@maprtech.com>
Committed: Wed Oct 11 16:50:36 2017 -0700

----------------------------------------------------------------------
 .../drill/common/util/DrillStringUtils.java     | 432 ++++++++++---------
 .../org/apache/drill/exec/ExecConstants.java    |   9 +-
 .../planner/sql/handlers/SetOptionHandler.java  |   4 +-
 .../exec/server/options/TypeValidators.java     |  51 +++
 .../drill/exec/server/rest/DrillRoot.java       |  48 ++-
 .../server/rest/auth/DrillRestLoginService.java |   4 +-
 .../server/rest/auth/DrillUserPrincipal.java    |   2 +
 .../src/main/resources/drill-module.conf        |   4 +-
 .../java-exec/src/main/resources/rest/index.ftl |  22 +
 .../exec/server/TestOptionsAuthEnabled.java     |  94 ++++
 .../org/apache/drill/test/ClientFixture.java    |  15 +
 11 files changed, 470 insertions(+), 215 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/common/src/main/java/org/apache/drill/common/util/DrillStringUtils.java
----------------------------------------------------------------------
diff --git a/common/src/main/java/org/apache/drill/common/util/DrillStringUtils.java b/common/src/main/java/org/apache/drill/common/util/DrillStringUtils.java
index 4e4042f..1ad0051 100644
--- a/common/src/main/java/org/apache/drill/common/util/DrillStringUtils.java
+++ b/common/src/main/java/org/apache/drill/common/util/DrillStringUtils.java
@@ -1,203 +1,229 @@
-/**
- * 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.common.util;
-
-import io.netty.buffer.ByteBuf;
-
-import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.commons.lang3.StringUtils;
-
-public class DrillStringUtils {
-
-  /**
-   * Converts the long number into more human readable string.
-   */
-  public static String readable(long bytes) {
-    int unit = 1024;
-    long absBytes = Math.abs(bytes);
-    if (absBytes < unit) {
-      return bytes + " B";
-    }
-    int exp = (int) (Math.log(absBytes) / Math.log(unit));
-    char pre = ("KMGTPE").charAt(exp-1);
-    return String.format("%s%.1f %ciB", (bytes == absBytes ? "" : "-"), absBytes / Math.pow(unit,
exp), pre);
-  }
-
-
-  /**
-   * Unescapes any Java literals found in the {@code String}.
-   * For example, it will turn a sequence of {@code '\'} and
-   * {@code 'n'} into a newline character, unless the {@code '\'}
-   * is preceded by another {@code '\'}.
-   *
-   * @param input  the {@code String} to unescape, may be null
-   * @return a new unescaped {@code String}, {@code null} if null string input
-   */
-  public static final String unescapeJava(String input) {
-    return StringEscapeUtils.unescapeJava(input);
-  }
-
-  /**
-   * Escapes the characters in a {@code String} according to Java string literal
-   * rules.
-   *
-   * Deals correctly with quotes and control-chars (tab, backslash, cr, ff,
-   * etc.) so, for example, a tab becomes the characters {@code '\\'} and
-   * {@code 't'}.
-   *
-   * Example:
-   * <pre>
-   * input string: He didn't say, "Stop!"
-   * output string: He didn't say, \"Stop!\"
-   * </pre>
-   *
-   * @param input  String to escape values in, may be null
-   * @return String with escaped values, {@code null} if null string input
-   */
-  public static final String escapeJava(String input) {
-    return StringEscapeUtils.escapeJava(input);
-  }
-
-  public static final String escapeNewLines(String input) {
-    if (input == null) {
-      return null;
-    }
-    StringBuilder result = new StringBuilder();
-    boolean sawNewline = false;
-    for (int i = 0; i < input.length(); i++) {
-      char curChar = input.charAt(i);
-      if (curChar == '\r' || curChar == '\n') {
-        if (sawNewline) {
-          continue;
-        }
-        sawNewline = true;
-        result.append("\\n");
-      } else {
-        sawNewline = false;
-        result.append(curChar);
-      }
-    }
-    return result.toString();
-  }
-
-  /**
-   * Copied form commons-lang 2.x code as common-lang 3.x has this API removed.
-   * (http://commons.apache.org/proper/commons-lang/article3_0.html#StringEscapeUtils.escapeSql)
-   * @param str
-   * @return
-   */
-  public static String escapeSql(String str) {
-    return (str == null) ? null : StringUtils.replace(str, "'", "''");
-  }
-
-  /**
-   * Return a printable representation of a byte buffer, escaping the non-printable
-   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
-   *
-   * This function does not modify  the {@code readerIndex} and {@code writerIndex}
-   * of the byte buffer.
-   */
-  public static String toBinaryString(ByteBuf buf, int strStart, int strEnd) {
-    StringBuilder result = new StringBuilder();
-    for (int i = strStart; i < strEnd ; ++i) {
-      appendByte(result, buf.getByte(i));
-    }
-    return result.toString();
-  }
-
-  /**
-   * Return a printable representation of a byte array, escaping the non-printable
-   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
-   */
-  public static String toBinaryString(byte[] buf) {
-    return toBinaryString(buf, 0, buf.length);
-  }
-
-  /**
-   * Return a printable representation of a byte array, escaping the non-printable
-   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
-   */
-  public static String toBinaryString(byte[] buf, int strStart, int strEnd) {
-    StringBuilder result = new StringBuilder();
-    for (int i = strStart; i < strEnd; ++i) {
-      appendByte(result, buf[i]);
-    }
-    return result.toString();
-  }
-
-  private static void appendByte(StringBuilder result, byte b) {
-    int ch = b & 0xFF;
-    if (   (ch >= '0' && ch <= '9')
-        || (ch >= 'A' && ch <= 'Z')
-        || (ch >= 'a' && ch <= 'z')
-        || " `~!@#$%^&*()-_=+[]{}|;:'\",.<>/?".indexOf(ch) >= 0 ) {
-        result.append((char)ch);
-    } else {
-      result.append(String.format("\\x%02X", ch));
-    }
-  }
-
-  /**
-   * parsing a hex encoded binary string and write to an output buffer.
-   *
-   * This function does not modify  the {@code readerIndex} and {@code writerIndex}
-   * of the byte buffer.
-   *
-   * @return Index in the byte buffer just after the last written byte.
-   */
-  public static int parseBinaryString(ByteBuf str, int strStart, int strEnd, ByteBuf out)
{
-    int dstEnd = 0;
-    for (int i = strStart; i < strEnd; i++) {
-      byte b = str.getByte(i);
-      if (b == '\\'
-          && strEnd > i+3
-          && (str.getByte(i+1) == 'x' || str.getByte(i+1) == 'X')) {
-        // ok, take next 2 hex digits.
-        byte hd1 = str.getByte(i+2);
-        byte hd2 = str.getByte(i+3);
-        if (isHexDigit(hd1) && isHexDigit(hd2)) { // [a-fA-F0-9]
-          // turn hex ASCII digit -> number
-          b = (byte) ((toBinaryFromHex(hd1) << 4) + toBinaryFromHex(hd2));
-          i += 3; // skip 3
-        }
-      }
-      out.setByte(dstEnd++, b);
-    }
-    return dstEnd;
-  }
-
-  /**
-   * Takes a ASCII digit in the range A-F0-9 and returns
-   * the corresponding integer/ordinal value.
-   * @param ch  The hex digit.
-   * @return The converted hex value as a byte.
-   */
-  private static byte toBinaryFromHex(byte ch) {
-    if ( ch >= 'A' && ch <= 'F' ) {
-      return (byte) ((byte)10 + (byte) (ch - 'A'));
-    } else if ( ch >= 'a' && ch <= 'f' ) {
-      return (byte) ((byte)10 + (byte) (ch - 'a'));
-    }
-    return (byte) (ch - '0');
-  }
-
-  private static boolean isHexDigit(byte c) {
-    return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
(c >= '0' && c <= '9');
-  }
-
-}
+/**
+ * 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.common.util;
+
+import com.google.common.base.Joiner;
+import io.netty.buffer.ByteBuf;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+
+public class DrillStringUtils {
+
+  /**
+   * Converts the long number into more human readable string.
+   */
+  public static String readable(long bytes) {
+    int unit = 1024;
+    long absBytes = Math.abs(bytes);
+    if (absBytes < unit) {
+      return bytes + " B";
+    }
+    int exp = (int) (Math.log(absBytes) / Math.log(unit));
+    char pre = ("KMGTPE").charAt(exp-1);
+    return String.format("%s%.1f %ciB", (bytes == absBytes ? "" : "-"), absBytes / Math.pow(unit,
exp), pre);
+  }
+
+
+  /**
+   * Unescapes any Java literals found in the {@code String}.
+   * For example, it will turn a sequence of {@code '\'} and
+   * {@code 'n'} into a newline character, unless the {@code '\'}
+   * is preceded by another {@code '\'}.
+   *
+   * @param input  the {@code String} to unescape, may be null
+   * @return a new unescaped {@code String}, {@code null} if null string input
+   */
+  public static final String unescapeJava(String input) {
+    return StringEscapeUtils.unescapeJava(input);
+  }
+
+  /**
+   * Escapes the characters in a {@code String} according to Java string literal
+   * rules.
+   *
+   * Deals correctly with quotes and control-chars (tab, backslash, cr, ff,
+   * etc.) so, for example, a tab becomes the characters {@code '\\'} and
+   * {@code 't'}.
+   *
+   * Example:
+   * <pre>
+   * input string: He didn't say, "Stop!"
+   * output string: He didn't say, \"Stop!\"
+   * </pre>
+   *
+   * @param input  String to escape values in, may be null
+   * @return String with escaped values, {@code null} if null string input
+   */
+  public static final String escapeJava(String input) {
+    return StringEscapeUtils.escapeJava(input);
+  }
+
+  public static final String escapeNewLines(String input) {
+    if (input == null) {
+      return null;
+    }
+    StringBuilder result = new StringBuilder();
+    boolean sawNewline = false;
+    for (int i = 0; i < input.length(); i++) {
+      char curChar = input.charAt(i);
+      if (curChar == '\r' || curChar == '\n') {
+        if (sawNewline) {
+          continue;
+        }
+        sawNewline = true;
+        result.append("\\n");
+      } else {
+        sawNewline = false;
+        result.append(curChar);
+      }
+    }
+    return result.toString();
+  }
+
+  /**
+   * Copied form commons-lang 2.x code as common-lang 3.x has this API removed.
+   * (http://commons.apache.org/proper/commons-lang/article3_0.html#StringEscapeUtils.escapeSql)
+   * @param str
+   * @return
+   */
+  public static String escapeSql(String str) {
+    return (str == null) ? null : StringUtils.replace(str, "'", "''");
+  }
+
+  /**
+   * Return a printable representation of a byte buffer, escaping the non-printable
+   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
+   *
+   * This function does not modify  the {@code readerIndex} and {@code writerIndex}
+   * of the byte buffer.
+   */
+  public static String toBinaryString(ByteBuf buf, int strStart, int strEnd) {
+    StringBuilder result = new StringBuilder();
+    for (int i = strStart; i < strEnd ; ++i) {
+      appendByte(result, buf.getByte(i));
+    }
+    return result.toString();
+  }
+
+  /**
+   * Return a printable representation of a byte array, escaping the non-printable
+   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
+   */
+  public static String toBinaryString(byte[] buf) {
+    return toBinaryString(buf, 0, buf.length);
+  }
+
+  /**
+   * Return a printable representation of a byte array, escaping the non-printable
+   * bytes as '\\xNN' where NN is the hexadecimal representation of such bytes.
+   */
+  public static String toBinaryString(byte[] buf, int strStart, int strEnd) {
+    StringBuilder result = new StringBuilder();
+    for (int i = strStart; i < strEnd; ++i) {
+      appendByte(result, buf[i]);
+    }
+    return result.toString();
+  }
+
+  private static void appendByte(StringBuilder result, byte b) {
+    int ch = b & 0xFF;
+    if (   (ch >= '0' && ch <= '9')
+        || (ch >= 'A' && ch <= 'Z')
+        || (ch >= 'a' && ch <= 'z')
+        || " `~!@#$%^&*()-_=+[]{}|;:'\",.<>/?".indexOf(ch) >= 0 ) {
+        result.append((char)ch);
+    } else {
+      result.append(String.format("\\x%02X", ch));
+    }
+  }
+
+  /**
+   * parsing a hex encoded binary string and write to an output buffer.
+   *
+   * This function does not modify  the {@code readerIndex} and {@code writerIndex}
+   * of the byte buffer.
+   *
+   * @return Index in the byte buffer just after the last written byte.
+   */
+  public static int parseBinaryString(ByteBuf str, int strStart, int strEnd, ByteBuf out)
{
+    int dstEnd = 0;
+    for (int i = strStart; i < strEnd; i++) {
+      byte b = str.getByte(i);
+      if (b == '\\'
+          && strEnd > i+3
+          && (str.getByte(i+1) == 'x' || str.getByte(i+1) == 'X')) {
+        // ok, take next 2 hex digits.
+        byte hd1 = str.getByte(i+2);
+        byte hd2 = str.getByte(i+3);
+        if (isHexDigit(hd1) && isHexDigit(hd2)) { // [a-fA-F0-9]
+          // turn hex ASCII digit -> number
+          b = (byte) ((toBinaryFromHex(hd1) << 4) + toBinaryFromHex(hd2));
+          i += 3; // skip 3
+        }
+      }
+      out.setByte(dstEnd++, b);
+    }
+    return dstEnd;
+  }
+
+  /**
+   * Takes a ASCII digit in the range A-F0-9 and returns
+   * the corresponding integer/ordinal value.
+   * @param ch  The hex digit.
+   * @return The converted hex value as a byte.
+   */
+  private static byte toBinaryFromHex(byte ch) {
+    if ( ch >= 'A' && ch <= 'F' ) {
+      return (byte) ((byte)10 + (byte) (ch - 'A'));
+    } else if ( ch >= 'a' && ch <= 'f' ) {
+      return (byte) ((byte)10 + (byte) (ch - 'a'));
+    }
+    return (byte) (ch - '0');
+  }
+
+  private static boolean isHexDigit(byte c) {
+    return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
(c >= '0' && c <= '9');
+  }
+
+  /**
+   * Removes extra spaces and empty values in a CSV String
+   * @param csv The CSV string to be sanitized
+   * @return The sanitized CSV string
+   */
+  public static String sanitizeCSV(String csv) {
+    // tokenize
+    String[] tokens = csv.split(",");
+    ArrayList<String> sanitizedTokens = new ArrayList<String>(tokens.length);
+    // check for empties
+    for (String s : tokens) {
+      String trimmedToken = s.trim();
+      if (trimmedToken.length() != 0) {
+        sanitizedTokens.add(trimmedToken);
+      }
+    }
+    String result = "";
+    if (sanitizedTokens.size() != 0) {
+      result = Joiner.on(",").join(sanitizedTokens);
+    }
+    return result;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
index 9890b45..9b125cb 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
@@ -31,6 +31,8 @@ import org.apache.drill.exec.server.options.TypeValidators.RangeDoubleValidator;
 import org.apache.drill.exec.server.options.TypeValidators.RangeLongValidator;
 import org.apache.drill.exec.server.options.TypeValidators.StringValidator;
 import org.apache.drill.exec.server.options.TypeValidators.MaxWidthValidator;
+import org.apache.drill.exec.server.options.TypeValidators.AdminUsersValidator;
+import org.apache.drill.exec.server.options.TypeValidators.AdminUserGroupsValidator;
 import org.apache.drill.exec.testing.ExecutionControls;
 
 public final class ExecConstants {
@@ -439,16 +441,15 @@ public final class ExecConstants {
    * Option whose value is a comma separated list of admin usernames. Admin users are users
who have special privileges
    * such as changing system options.
    */
-
   public static final String ADMIN_USERS_KEY = "security.admin.users";
-  public static final StringValidator ADMIN_USERS_VALIDATOR = new StringValidator(ADMIN_USERS_KEY);
+  public static final AdminUsersValidator ADMIN_USERS_VALIDATOR = new AdminUsersValidator(ADMIN_USERS_KEY);
 
   /**
    * Option whose value is a comma separated list of admin usergroups.
    */
-
   public static final String ADMIN_USER_GROUPS_KEY = "security.admin.user_groups";
-  public static final StringValidator ADMIN_USER_GROUPS_VALIDATOR = new StringValidator(ADMIN_USER_GROUPS_KEY);
+  public static final AdminUserGroupsValidator ADMIN_USER_GROUPS_VALIDATOR =
+          new AdminUserGroupsValidator(ADMIN_USER_GROUPS_KEY);
   /**
    * Option whose value is a string representing list of inbound impersonation policies.
    *

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java
index 0216163..5e87f44 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java
@@ -91,8 +91,8 @@ public class SetOptionHandler extends AbstractSqlHandler {
       if (context.isUserAuthenticationEnabled() &&
           !ImpersonationUtil.hasAdminPrivileges(
             context.getQueryUserName(),
-            options.getOption(ExecConstants.ADMIN_USERS_VALIDATOR),
-            options.getOption(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR))) {
+            ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(options),
+            ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(options))) {
         throw UserException.permissionError()
             .message("Not authorized to change SYSTEM options.")
             .build(logger);

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/TypeValidators.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/TypeValidators.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/TypeValidators.java
index fc4c673..aa63b5a 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/TypeValidators.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/options/TypeValidators.java
@@ -19,10 +19,13 @@ package org.apache.drill.exec.server.options;
 
 import java.util.Set;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.Sets;
 import org.apache.drill.common.exceptions.UserException;
+import org.apache.drill.common.util.DrillStringUtils;
 import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.server.options.OptionValue.Kind;
+import org.apache.drill.exec.util.ImpersonationUtil;
 
 public class TypeValidators {
   private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(TypeValidators.class);
@@ -198,6 +201,54 @@ public class TypeValidators {
     }
   }
 
+  /**
+   * Unless explicitly changed by the user previously, the admin user
+   * can only be determined at runtime
+   */
+  public static class AdminUsersValidator extends StringValidator {
+
+    public final String DEFAULT_ADMIN_USERS = "%drill_process_user%";
+
+    public AdminUsersValidator(String name) {
+      super(name);
+    }
+
+    public String getAdminUsers(OptionManager optionManager) {
+      String adminUsers = optionManager.getOption(ExecConstants.ADMIN_USERS_VALIDATOR);
+      // if this option has not been changed by the user then return the
+      // process user
+      if (adminUsers.equals(DEFAULT_ADMIN_USERS)) {
+        adminUsers = ImpersonationUtil.getProcessUserName();
+      }
+      adminUsers = DrillStringUtils.sanitizeCSV(adminUsers);
+      return adminUsers;
+    }
+  }
+
+  /**
+   * Unless explicitly changed by the user previously, the admin user
+   * groups can only be determined at runtime
+   */
+  public static class AdminUserGroupsValidator extends StringValidator {
+
+    public final String DEFAULT_ADMIN_USER_GROUPS = "%drill_process_user_groups%";
+
+    public AdminUserGroupsValidator(String name) {
+      super(name);
+    }
+
+    public String getAdminUserGroups(OptionManager optionManager) {
+      String adminUserGroups = optionManager.getOption(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR);
+      // if this option has not been changed by the user then return the
+      // process user groups
+      if (adminUserGroups.equals(DEFAULT_ADMIN_USER_GROUPS)) {
+        adminUserGroups = Joiner.on(",").join(ImpersonationUtil.getProcessUserGroupNames());
+      }
+      adminUserGroups = DrillStringUtils.sanitizeCSV(adminUserGroups);
+      return adminUserGroups;
+    }
+  }
+
   /** Max width is a special validator which computes and validates
    *  the maxwidth. If the maxwidth is already set in system/session
    * the value is returned or else it is computed dynamically based on

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
index 70b82ca..d8d2163 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRoot.java
@@ -27,13 +27,17 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.SecurityContext;
 import javax.xml.bind.annotation.XmlRootElement;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Strings;
 import com.google.common.collect.Sets;
 import org.apache.drill.common.config.DrillConfig;
 import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.proto.CoordinationProtos.DrillbitEndpoint;
+import org.apache.drill.exec.server.options.OptionManager;
 import org.apache.drill.exec.server.DrillbitContext;
 import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
+import org.apache.drill.exec.server.rest.auth.AuthDynamicFeature;
+import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.apache.drill.exec.work.WorkManager;
 import org.apache.drill.exec.work.foreman.rm.DistributedQueryQueue;
 import org.apache.drill.exec.work.foreman.rm.DistributedQueryQueue.ZKQueueInfo;
@@ -75,6 +79,29 @@ public class DrillRoot {
     final DrillConfig config = dbContext.getConfig();
     final boolean userEncryptionEnabled = config.getBoolean(ExecConstants.USER_ENCRYPTION_SASL_ENABLED);
     final boolean bitEncryptionEnabled = config.getBoolean(ExecConstants.BIT_ENCRYPTION_SASL_ENABLED);
+    // If the user is logged in and is admin user then show the admin user info
+    // For all other cases the user info need-not or should-not be displayed
+    OptionManager optionManager = work.getContext().getOptionManager();
+    final boolean isUserLoggedIn = AuthDynamicFeature.isUserLoggedIn(sc);
+    String adminUsers = isUserLoggedIn ?
+            ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(optionManager) : null;
+    String adminUserGroups = isUserLoggedIn ?
+            ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager) :
null;
+
+    // separate groups by comma + space
+    if (adminUsers != null) {
+      String[] groups = adminUsers.split(",");
+      adminUsers = Joiner.on(", ").join(groups);
+    }
+
+    // separate groups by comma + space
+    if (adminUserGroups != null) {
+      String[] groups = adminUserGroups.split(",");
+      adminUserGroups = Joiner.on(", ").join(groups);
+    }
+
+    final boolean shouldShowUserInfo = isUserLoggedIn &&
+            ((DrillUserPrincipal)sc.getUserPrincipal()).isAdminUser();
 
     for (DrillbitEndpoint endpoint : work.getContext().getBits()) {
       final DrillbitInfo drillbit = new DrillbitInfo(endpoint,
@@ -85,9 +112,11 @@ public class DrillRoot {
       }
       drillbits.add(drillbit);
     }
+    logger.debug("Admin info: user: "  + adminUsers +  " user group: " + adminUserGroups
+
+            " userLoggedIn "  + isUserLoggedIn + " shouldShowUserInfo: " + shouldShowUserInfo
);
 
-     return new ClusterInfo(drillbits, currentVersion, mismatchedVersions,
-      userEncryptionEnabled, bitEncryptionEnabled,
+    return new ClusterInfo(drillbits, currentVersion, mismatchedVersions,
+      userEncryptionEnabled, bitEncryptionEnabled, adminUsers, adminUserGroups, shouldShowUserInfo,
       QueueInfo.build(dbContext.getResourceManager()));
   }
 
@@ -179,6 +208,9 @@ public class DrillRoot {
     private final Collection<String> mismatchedVersions;
     private final boolean userEncryptionEnabled;
     private final boolean bitEncryptionEnabled;
+    private final String adminUsers;
+    private final String adminUserGroups;
+    private final boolean shouldShowUserInfo;
     private final QueueInfo queueInfo;
 
     @JsonCreator
@@ -187,12 +219,18 @@ public class DrillRoot {
                        Collection<String> mismatchedVersions,
                        boolean userEncryption,
                        boolean bitEncryption,
+                       String adminUsers,
+                       String adminUserGroups,
+                       boolean shouldShowUserInfo,
                        QueueInfo queueInfo) {
       this.drillbits = Sets.newTreeSet(drillbits);
       this.currentVersion = currentVersion;
       this.mismatchedVersions = Sets.newTreeSet(mismatchedVersions);
       this.userEncryptionEnabled = userEncryption;
       this.bitEncryptionEnabled = bitEncryption;
+      this.adminUsers = adminUsers;
+      this.adminUserGroups = adminUserGroups;
+      this.shouldShowUserInfo = shouldShowUserInfo;
       this.queueInfo = queueInfo;
     }
 
@@ -212,6 +250,12 @@ public class DrillRoot {
 
     public boolean isBitEncryptionEnabled() { return bitEncryptionEnabled; }
 
+    public String getAdminUsers() { return adminUsers; }
+
+    public String getAdminUserGroups() { return adminUserGroups; }
+
+    public boolean shouldShowUserInfo() { return shouldShowUserInfo; }
+
     public QueueInfo queueInfo() { return queueInfo; }
   }
 

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
index 2231ac7..998026b 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillRestLoginService.java
@@ -82,8 +82,8 @@ public class DrillRestLoginService implements LoginService {
       final SystemOptionManager sysOptions = drillbitContext.getOptionManager();
 
       final boolean isAdmin = ImpersonationUtil.hasAdminPrivileges(username,
-          sysOptions.getOption(ExecConstants.ADMIN_USERS_KEY).string_val,
-          sysOptions.getOption(ExecConstants.ADMIN_USER_GROUPS_KEY).string_val);
+              ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(sysOptions),
+              ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(sysOptions));
 
       // Create the UserPrincipal corresponding to logged in user.
       final Principal userPrincipal = new DrillUserPrincipal(username, isAdmin);

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
index 6d8f301..a21977f 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/auth/DrillUserPrincipal.java
@@ -51,6 +51,8 @@ public class DrillUserPrincipal implements Principal {
     this.isAdmin = isAdmin;
   }
 
+  public boolean isAdminUser() { return isAdmin; }
+
   @Override
   public String getName() {
     return userName;

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/resources/drill-module.conf
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/drill-module.conf b/exec/java-exec/src/main/resources/drill-module.conf
index 7ebdd5a..bfdb74c 100644
--- a/exec/java-exec/src/main/resources/drill-module.conf
+++ b/exec/java-exec/src/main/resources/drill-module.conf
@@ -489,8 +489,8 @@ drill.exec.options:  {
     planner.width.max_per_node: 0,
     planner.width.max_per_query: 1000,
     prepare.statement.create_timeout_ms: 10000,
-    security.admin.user_groups: true,
-    security.admin.users: true,
+    security.admin.user_groups: "%drill_process_user_groups%",
+    security.admin.users: "%drill_process_user%",
     store.format: "parquet",
     store.hive.optimize_scan_with_native_readers: false,
     store.json.all_text_mode: false,

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/main/resources/rest/index.ftl
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/main/resources/rest/index.ftl b/exec/java-exec/src/main/resources/rest/index.ftl
index 04effba..38ce6ba 100644
--- a/exec/java-exec/src/main/resources/rest/index.ftl
+++ b/exec/java-exec/src/main/resources/rest/index.ftl
@@ -97,6 +97,28 @@
       </div>
   </div>
 
+   <#if model.shouldShowUserInfo()>
+       <div class="row">
+            <div class="col-md-12">
+              <h3>User Info </h3>
+              <div class="table-responsive">
+                <table class="table table-hover" style="width: auto;">
+                  <tbody>
+                      <tr>
+                        <td>Admin Users</td>
+                        <td class="list-value">${model.getAdminUsers()}</td>
+                      </tr>
+                      <tr>
+                        <td>Admin User Groups</td>
+                        <td class="list-value">${model.getAdminUserGroups()}</td>
+                      </tr>
+                  </tbody>
+                </table>
+              </div>
+            </div>
+        </div>
+   </#if>
+
   <#assign queueInfo = model.queueInfo() />
   <div class="row">
       <div class="col-md-12">

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/test/java/org/apache/drill/exec/server/TestOptionsAuthEnabled.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/server/TestOptionsAuthEnabled.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/TestOptionsAuthEnabled.java
index f660a27..82a524a 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/exec/server/TestOptionsAuthEnabled.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/server/TestOptionsAuthEnabled.java
@@ -17,14 +17,21 @@
  */
 package org.apache.drill.exec.server;
 
+import com.google.common.base.Joiner;
 import com.typesafe.config.ConfigValueFactory;
 import org.apache.drill.BaseTestQuery;
 import org.apache.drill.common.config.DrillProperties;
 import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.common.util.DrillStringUtils;
 import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl;
+import org.apache.drill.exec.server.options.OptionManager;
+import org.apache.drill.exec.util.ImpersonationUtil;
+import org.apache.drill.test.ClientFixture;
+import org.apache.drill.test.ClusterFixture;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import static org.junit.Assert.*;
 
 import static org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl.ADMIN_GROUP;
 import static org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl.ADMIN_USER;
@@ -36,6 +43,9 @@ import static org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorT
 import static org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl.TEST_USER_2;
 import static org.apache.drill.exec.rpc.user.security.testing.UserAuthenticatorTestImpl.TEST_USER_2_PASSWORD;
 
+
+
+
 import java.util.Properties;
 
 /**
@@ -115,4 +125,88 @@ public class TestOptionsAuthEnabled extends BaseTestQuery {
       test(String.format("ALTER SYSTEM SET `%s` = %d;", ExecConstants.SLICE_TARGET, ExecConstants.SLICE_TARGET_DEFAULT));
     }
   }
+
+  @Test
+  public void testAdminUserOptions() throws Exception {
+
+    try (ClusterFixture cluster = ClusterFixture.standardCluster();
+         ClientFixture client = cluster.clientFixture()) {
+      OptionManager optionManager = cluster.drillbit().getContext().getOptionManager();
+
+      // Admin Users Tests
+      // config file should have the 'fake' default admin user and it should be returned
+      // by the option manager if the option has not been set by the user
+      String configAdminUser =  optionManager.getOption(ExecConstants.ADMIN_USERS_VALIDATOR);
+      assertEquals(configAdminUser, ExecConstants.ADMIN_USERS_VALIDATOR.DEFAULT_ADMIN_USERS);
+
+      // Option accessor should never return the 'fake' default from the config
+      String adminUser1 = ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(optionManager);
+      assertNotEquals(adminUser1, ExecConstants.ADMIN_USERS_VALIDATOR.DEFAULT_ADMIN_USERS);
+
+      // Change testAdminUser if necessary
+      String testAdminUser = "ronswanson";
+      if (adminUser1.equals(testAdminUser)) {
+        testAdminUser += "thefirst";
+      }
+      // Check if the admin option accessor honors a user-supplied value
+      client.alterSystem(ExecConstants.ADMIN_USERS_KEY, testAdminUser);
+      String adminUser2 = ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(optionManager);
+      assertEquals(adminUser2, testAdminUser);
+
+      // Ensure that the default admin users have admin privileges
+      client.resetSystem(ExecConstants.ADMIN_USERS_KEY);
+      client.resetSystem(ExecConstants.ADMIN_USER_GROUPS_KEY);
+      String systemAdminUsersList0 = ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(optionManager);
+      String systemAdminUserGroupsList0 = ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager);
+      for (String user : systemAdminUsersList0.split(",")) {
+        assertTrue(ImpersonationUtil.hasAdminPrivileges(user, systemAdminUsersList0, systemAdminUserGroupsList0));
+      }
+
+      // test if admin users, set by the user, have admin privileges
+      // test if we can handle a user-supplied list that is not well formatted
+      String crummyTestAdminUsersList = " alice, bob bob, charlie  ,, dave ";
+      client.alterSystem(ExecConstants.ADMIN_USERS_KEY, crummyTestAdminUsersList);
+      String[] sanitizedAdminUsers = {"alice", "bob bob", "charlie", "dave"};
+      // also test the CSV sanitizer
+      assertEquals(Joiner.on(",").join(sanitizedAdminUsers), DrillStringUtils.sanitizeCSV(crummyTestAdminUsersList));
+      String systemAdminUsersList1 = ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(optionManager);
+      String systemAdminUserGroupsList1 = ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager);
+      for (String user : sanitizedAdminUsers) {
+        assertTrue(ImpersonationUtil.hasAdminPrivileges(user, systemAdminUsersList1, systemAdminUserGroupsList1));
+      }
+
+      // Admin User Groups Tests
+      // config file should have the 'fake' default admin user and it should be returned
+      // by the option manager if the option has not been set by the user
+      String configAdminUserGroups =  optionManager.getOption(ExecConstants.ADMIN_USER_GROUPS_VALIDATOR);
+      assertEquals(configAdminUserGroups, ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.DEFAULT_ADMIN_USER_GROUPS);
+
+      // Option accessor should never return the 'fake' default from the config
+      String adminUserGroups1 = ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager);
+      assertNotEquals(adminUserGroups1, ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.DEFAULT_ADMIN_USER_GROUPS);
+
+      // Change testAdminUserGroups if necessary
+      String testAdminUserGroups = "yakshavers";
+      if (adminUserGroups1.equals(testAdminUserGroups)) {
+        testAdminUserGroups += ",wormracers";
+      }
+      // Check if the admin option accessor honors a user-supplied values
+      client.alterSystem(ExecConstants.ADMIN_USER_GROUPS_KEY, testAdminUserGroups);
+      String adminUserGroups2 = ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager);
+      assertEquals(adminUserGroups2, testAdminUserGroups);
+
+      // Test if we can handle a user-supplied admin user groups list that is not well formatted
+      String crummyTestAdminUserGroupsList = " g1, g 2, g4 ,, g5 ";
+      client.alterSystem(ExecConstants.ADMIN_USER_GROUPS_KEY, crummyTestAdminUserGroupsList);
+      String systemAdminUserGroupsList2 = ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(optionManager);
+      // test if all the group tokens are well-formed
+      // Note: A hasAdminPrivilege() test cannot be done here, like in the tests for handling
a crummy admin user list.
+      // This is because ImpersonationUtil currently does not implement an API that takes
a group as an input to check
+      // for admin privileges
+      for (String group : systemAdminUserGroupsList2.split(",")) {
+        assertTrue(group.length() != 0);
+        assertTrue(group.trim().equals(group));
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/drill/blob/7aeaf0e3/exec/java-exec/src/test/java/org/apache/drill/test/ClientFixture.java
----------------------------------------------------------------------
diff --git a/exec/java-exec/src/test/java/org/apache/drill/test/ClientFixture.java b/exec/java-exec/src/test/java/org/apache/drill/test/ClientFixture.java
index 4e44464..8ad0fbb 100644
--- a/exec/java-exec/src/test/java/org/apache/drill/test/ClientFixture.java
+++ b/exec/java-exec/src/test/java/org/apache/drill/test/ClientFixture.java
@@ -128,6 +128,21 @@ public class ClientFixture implements AutoCloseable {
   }
 
   /**
+   * Reset a system option
+   * @param key
+   */
+
+  public void resetSystem(String key) {
+    String sql = "ALTER SYSTEM RESET `" + key + "`";
+    runSqlSilently(sql);
+  }
+
+  public void resetSession(String key) {
+    String sql = "ALTER SESSION RESET `" + key + "`";
+    runSqlSilently(sql);
+  }
+
+  /**
    * Run SQL silently (discard results.)
    *
    * @param sql


Mime
View raw message