HBASE-15780 Make AuthUtil public and rely on it for talking to secure HBase.

Signed-off-by: Gary Helmling <ga...@apache.org>
Signed-off-by: Mikhail Antonov <anto...@apache.org>
Signed-off-by: stack <st...@apache.org>


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

Branch: refs/heads/HBASE-14850
Commit: e0aff109018918514c8f27a8a90159e695e8b542
Parents: c47511b
Author: Sean Busbey <bus...@apache.org>
Authored: Fri May 13 08:48:45 2016 -0700
Committer: Sean Busbey <bus...@apache.org>
Committed: Mon May 16 00:50:14 2016 -0500

----------------------------------------------------------------------
 .../apache/hadoop/hbase/zookeeper/ZKUtil.java   |  2 +-
 .../hbase/zookeeper/ZooKeeperWatcher.java       |  4 +-
 .../java/org/apache/hadoop/hbase/AuthUtil.java  | 46 ++++++++++++++++++--
 .../org/apache/hadoop/hbase/ChoreService.java   | 23 ++++++----
 .../org/apache/hadoop/hbase/ScheduledChore.java | 15 ++++++-
 .../java/org/apache/hadoop/hbase/Stoppable.java |  5 ++-
 .../apache/hadoop/hbase/TestChoreService.java   | 10 ++---
 .../hbase/security/access/TableAuthManager.java |  4 +-
 8 files changed, 82 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
index ff2e21f..ae26400 100644
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
+++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
@@ -913,7 +913,7 @@ public class ZKUtil {
       if (superUsers != null) {
         List<String> groups = new ArrayList<String>();
         for (String user : superUsers) {
-          if (user.startsWith(AuthUtil.GROUP_PREFIX)) {
+          if (AuthUtil.isGroupPrincipal(user)) {
             // TODO: Set node ACL for groups when ZK supports this feature
             groups.add(user);
           } else {

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
----------------------------------------------------------------------
diff --git 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
index dcbcf30..93828eb 100644
--- 
a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
+++ 
b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
@@ -341,7 +341,7 @@ public class ZooKeeperWatcher implements Watcher, 
Abortable, Closeable {
     for (String user : superUsers) {
       boolean hasAccess = false;
       // TODO: Validate super group members also when ZK supports setting node 
ACL for groups.
-      if (!user.startsWith(AuthUtil.GROUP_PREFIX)) {
+      if (!AuthUtil.isGroupPrincipal(user)) {
         for (ACL acl : acls) {
           if (user.equals(acl.getId().getId())) {
             if (acl.getPerms() == Perms.ALL) {
@@ -370,7 +370,7 @@ public class ZooKeeperWatcher implements Watcher, 
Abortable, Closeable {
   public static boolean isSuperUserId(String[] superUsers, Id id) {
     for (String user : superUsers) {
       // TODO: Validate super group members also when ZK supports setting node 
ACL for groups.
-      if (!user.startsWith(AuthUtil.GROUP_PREFIX) && new Id("sasl", 
user).equals(id)) {
+      if (!AuthUtil.isGroupPrincipal(user) && new Id("sasl", user).equals(id)) 
{
         return true;
       }
     }

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-common/src/main/java/org/apache/hadoop/hbase/AuthUtil.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/AuthUtil.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/AuthUtil.java
index 52f872c..bbed218 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/AuthUtil.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/AuthUtil.java
@@ -32,15 +32,48 @@ import org.apache.hadoop.hbase.util.Strings;
 import org.apache.hadoop.security.UserGroupInformation;
 
 /**
- * Utility methods for helping with security tasks.
+ * Utility methods for helping with security tasks. Downstream users
+ * may rely on this class to handle authenticating via keytab where
+ * long running services need access to a secure HBase cluster.
+ *
+ * Callers must ensure:
+ *
+ * <ul>
+ *   <li>HBase configuration files are in the Classpath
+ *   <li>hbase.client.keytab.file points to a valid keytab on the local 
filesystem
+ *   <li>hbase.client.kerberos.principal gives the Kerberos principal to use
+ * </ul>
+ *
+ * <pre>
+ * {@code
+ *   ChoreService choreService = null;
+ *   // Presumes HBase configuration files are on the classpath
+ *   final Configuration conf = HBaseConfiguration.create();
+ *   final ScheduledChore authChore = AuthUtil.getAuthChore(conf);
+ *   if (authChore != null) {
+ *     choreService = new ChoreService("MY_APPLICATION");
+ *     choreService.scheduleChore(authChore);
+ *   }
+ *   try {
+ *     // do application work
+ *   } finally {
+ *     if (choreService != null) {
+ *       choreService.shutdown();
+ *     }
+ *   }
+ * }
+ * </pre>
+ *
+ * See the "Running Canary in a Kerberos-enabled Cluster" section of the HBase 
Reference Guide for
+ * an example of configuring a user of this Auth Chore to run on a secure 
cluster.
  */
-@InterfaceAudience.Private
-@InterfaceStability.Evolving
+@InterfaceAudience.Public
+@InterfaceStability.Stable
 public class AuthUtil {
   private static final Log LOG = LogFactory.getLog(AuthUtil.class);
 
   /** Prefix character to denote group names */
-  public static final String GROUP_PREFIX = "@";
+  private static final String GROUP_PREFIX = "@";
 
   private AuthUtil() {
     super();
@@ -48,6 +81,8 @@ public class AuthUtil {
 
   /**
    * Checks if security is enabled and if so, launches chore for refreshing 
kerberos ticket.
+   * @param conf the hbase service configuration
+   * @return a ScheduledChore for renewals, if needed, and null otherwise.
    */
   public static ScheduledChore getAuthChore(Configuration conf) throws 
IOException {
     UserProvider userProvider = UserProvider.instantiate(conf);
@@ -109,6 +144,7 @@ public class AuthUtil {
    * principal.  Currently this simply checks if the name starts with the
    * special group prefix character ("@").
    */
+  @InterfaceAudience.Private
   public static boolean isGroupPrincipal(String name) {
     return name != null && name.startsWith(GROUP_PREFIX);
   }
@@ -117,6 +153,7 @@ public class AuthUtil {
    * Returns the actual name for a group principal (stripped of the
    * group prefix).
    */
+  @InterfaceAudience.Private
   public static String getGroupName(String aclKey) {
     if (!isGroupPrincipal(aclKey)) {
       return aclKey;
@@ -128,6 +165,7 @@ public class AuthUtil {
   /**
    * Returns the group entry with the group prefix for a group principal.
    */
+  @InterfaceAudience.Private
   public static String toGroupEntry(String name) {
     return GROUP_PREFIX + name;
   }

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-common/src/main/java/org/apache/hadoop/hbase/ChoreService.java
----------------------------------------------------------------------
diff --git 
a/hbase-common/src/main/java/org/apache/hadoop/hbase/ChoreService.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/ChoreService.java
index 5c3d215..1623c10 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/ChoreService.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/ChoreService.java
@@ -32,6 +32,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hbase.ScheduledChore.ChoreServicer;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
 
 /**
  * ChoreService is a service that can be used to schedule instances of {@link 
ScheduledChore} to run
@@ -52,13 +53,15 @@ import 
org.apache.hadoop.hbase.classification.InterfaceAudience;
  * When finished with a ChoreService it is good practice to call {@link 
ChoreService#shutdown()}.
  * Calling this method ensures that all scheduled chores are cancelled and 
cleaned up properly.
  */
-@InterfaceAudience.Private
+@InterfaceAudience.Public
+@InterfaceStability.Stable
 public class ChoreService implements ChoreServicer {
   private static final Log LOG = LogFactory.getLog(ChoreService.class);
 
   /**
    * The minimum number of threads in the core pool of the underlying 
ScheduledThreadPoolExecutor
    */
+  @InterfaceAudience.Private
   public final static int MIN_CORE_POOL_SIZE = 1;
 
   /**
@@ -92,12 +95,15 @@ public class ChoreService implements ChoreServicer {
    * @param coreThreadPoolPrefix Prefix that will be applied to the Thread 
name of all threads
    *          spawned by this service
    */
+  @InterfaceAudience.Private
   @VisibleForTesting
   public ChoreService(final String coreThreadPoolPrefix) {
     this(coreThreadPoolPrefix, MIN_CORE_POOL_SIZE, false);
   }
 
   /**
+   * @param coreThreadPoolPrefix Prefix that will be applied to the Thread 
name of all threads
+   *          spawned by this service
    * @param jitter Should chore service add some jitter for all of the 
scheduled chores. When set
    *               to true this will add -10% to 10% jitter.
    */
@@ -111,6 +117,8 @@ public class ChoreService implements ChoreServicer {
    * @param corePoolSize The initial size to set the core pool of the 
ScheduledThreadPoolExecutor 
    *          to during initialization. The default size is 1, but specifying 
a larger size may be
    *          beneficial if you know that 1 thread will not be enough.
+   * @param jitter Should chore service add some jitter for all of the 
scheduled chores. When set
+   *               to true this will add -10% to 10% jitter.
    */
   public ChoreService(final String coreThreadPoolPrefix, int corePoolSize, 
boolean jitter) {
     this.coreThreadPoolPrefix = coreThreadPoolPrefix;
@@ -131,14 +139,6 @@ public class ChoreService implements ChoreServicer {
   }
 
   /**
-   * @param coreThreadPoolPrefix Prefix that will be applied to the Thread 
name of all threads
-   *          spawned by this service
-   */
-  public static ChoreService getInstance(final String coreThreadPoolPrefix) {
-    return new ChoreService(coreThreadPoolPrefix);
-  }
-
-  /**
    * @param chore Chore to be scheduled. If the chore is already scheduled 
with another ChoreService
    *          instance, that schedule will be cancelled (i.e. a Chore can only 
ever be scheduled
    *          with a single ChoreService instance).
@@ -179,11 +179,13 @@ public class ChoreService implements ChoreServicer {
     scheduleChore(chore);
   }
 
+  @InterfaceAudience.Private
   @Override
   public synchronized void cancelChore(ScheduledChore chore) {
     cancelChore(chore, true);
   }
 
+  @InterfaceAudience.Private
   @Override
   public synchronized void cancelChore(ScheduledChore chore, boolean 
mayInterruptIfRunning) {
     if (chore != null && scheduledChores.containsKey(chore)) {
@@ -200,12 +202,14 @@ public class ChoreService implements ChoreServicer {
     }
   }
 
+  @InterfaceAudience.Private
   @Override
   public synchronized boolean isChoreScheduled(ScheduledChore chore) {
     return chore != null && scheduledChores.containsKey(chore)
         && !scheduledChores.get(chore).isDone();
   }
 
+  @InterfaceAudience.Private
   @Override
   public synchronized boolean triggerNow(ScheduledChore chore) {
     if (chore == null) {
@@ -293,6 +297,7 @@ public class ChoreService implements ChoreServicer {
     }
   }
 
+  @InterfaceAudience.Private
   @Override
   public synchronized void onChoreMissedStartTime(ScheduledChore chore) {
     if (chore == null || !scheduledChores.containsKey(chore)) return;

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-common/src/main/java/org/apache/hadoop/hbase/ScheduledChore.java
----------------------------------------------------------------------
diff --git 
a/hbase-common/src/main/java/org/apache/hadoop/hbase/ScheduledChore.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/ScheduledChore.java
index 5c5bcd8..422ca1a 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/ScheduledChore.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/ScheduledChore.java
@@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -40,7 +41,8 @@ import com.google.common.annotations.VisibleForTesting;
  * Don't subclass ScheduledChore if the task relies on being woken up for 
something to do, such as
  * an entry being added to a queue, etc.
  */
-@InterfaceAudience.Private
+@InterfaceAudience.Public
+@InterfaceStability.Stable
 public abstract class ScheduledChore implements Runnable {
   private static final Log LOG = LogFactory.getLog(ScheduledChore.class);
 
@@ -116,6 +118,8 @@ public abstract class ScheduledChore implements Runnable {
   /**
    * This constructor is for test only. It allows us to create an object and 
to call chore() on it.
    */
+  @InterfaceAudience.Private
+  @VisibleForTesting
   protected ScheduledChore() {
     this.name = null;
     this.stopper = null;
@@ -165,7 +169,7 @@ public abstract class ScheduledChore implements Runnable {
   }
 
   /**
-   * @see java.lang.Thread#run()
+   * @see java.lang.Runnable#run()
    */
   @Override
   public void run() {
@@ -327,6 +331,7 @@ public abstract class ScheduledChore implements Runnable {
     return choreServicer != null && choreServicer.isChoreScheduled(this);
   }
 
+  @InterfaceAudience.Private
   @VisibleForTesting
   public synchronized void choreForTesting() {
     chore();
@@ -352,6 +357,12 @@ public abstract class ScheduledChore implements Runnable {
   protected synchronized void cleanup() {
   }
 
+  /**
+   * A summation of this chore in human readable format. Downstream users 
should not presume
+   * parsing of this string can relaibly be done between versions. Instead, 
they should rely
+   * on the public accessor methods to get the information they desire.
+   */
+  @InterfaceAudience.Private
   @Override
   public String toString() {
     return "[ScheduledChore: Name: " + getName() + " Period: " + getPeriod() + 
" Unit: "

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java 
b/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
index fc83ba3..9adaa1a 100644
--- a/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
@@ -24,11 +24,12 @@ import 
org.apache.hadoop.hbase.classification.InterfaceStability;
 /**
  * Implementers are Stoppable.
  */
-@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
-@InterfaceStability.Evolving
+@InterfaceAudience.Public
+@InterfaceStability.Stable
 public interface Stoppable {
   /**
    * Stop this service.
+   * Implementers should favor logging errors over throwing RuntimeExceptions.
    * @param why Why we're stopping.
    */
   void stop(String why);

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-common/src/test/java/org/apache/hadoop/hbase/TestChoreService.java
----------------------------------------------------------------------
diff --git 
a/hbase-common/src/test/java/org/apache/hadoop/hbase/TestChoreService.java 
b/hbase-common/src/test/java/org/apache/hadoop/hbase/TestChoreService.java
index d1a6c19..f290e1d 100644
--- a/hbase-common/src/test/java/org/apache/hadoop/hbase/TestChoreService.java
+++ b/hbase-common/src/test/java/org/apache/hadoop/hbase/TestChoreService.java
@@ -233,7 +233,7 @@ public class TestChoreService {
 
   @Test (timeout=20000)
   public void testInitialChorePrecedence() throws InterruptedException {
-    ChoreService service = 
ChoreService.getInstance("testInitialChorePrecedence");
+    ChoreService service = new ChoreService("testInitialChorePrecedence");
 
     final int period = 100;
     final int failureThreshold = 5;
@@ -264,7 +264,7 @@ public class TestChoreService {
   public void testCancelChore() throws InterruptedException {
     final int period = 100;
     ScheduledChore chore1 = new DoNothingChore("chore1", period);
-    ChoreService service = ChoreService.getInstance("testCancelChore");
+    ChoreService service = new ChoreService("testCancelChore");
     try {
       service.scheduleChore(chore1);
       assertTrue(chore1.isScheduled());
@@ -342,7 +342,7 @@ public class TestChoreService {
     final int period = 100;
     // Small delta that acts as time buffer (allowing chores to complete if 
running slowly)
     final int delta = 5;
-    ChoreService service = ChoreService.getInstance("testFrequencyOfChores");
+    ChoreService service = new ChoreService("testFrequencyOfChores");
     CountingChore chore = new CountingChore("countingChore", period);
     try {
       service.scheduleChore(chore);
@@ -368,7 +368,7 @@ public class TestChoreService {
   public void testForceTrigger() throws InterruptedException {
     final int period = 100;
     final int delta = 5;
-    ChoreService service = ChoreService.getInstance("testForceTrigger");
+    ChoreService service = new ChoreService("testForceTrigger");
     final CountingChore chore = new CountingChore("countingChore", period);
     try {
       service.scheduleChore(chore);
@@ -715,7 +715,7 @@ public class TestChoreService {
 
   @Test (timeout=20000)
   public void testStopperForScheduledChores() throws InterruptedException {
-    ChoreService service = 
ChoreService.getInstance("testStopperForScheduledChores");
+    ChoreService service = new ChoreService("testStopperForScheduledChores");
     Stoppable stopperForGroup1 = new SampleStopper();
     Stoppable stopperForGroup2 = new SampleStopper();
     final int period = 100;

http://git-wip-us.apache.org/repos/asf/hbase/blob/e0aff109/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java
----------------------------------------------------------------------
diff --git 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java
 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java
index f7479cf..25cfc8b 100644
--- 
a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java
+++ 
b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java
@@ -82,8 +82,8 @@ public class TableAuthManager implements Closeable {
     }
 
     /**
-     * Returns a combined map of user and group permissions, with group names 
prefixed by
-     * {@link AuthUtil#GROUP_PREFIX}.
+     * Returns a combined map of user and group permissions, with group names
+     * distinguished according to {@link AuthUtil.isGroupPrincipal}
      */
     public ListMultimap<String,T> getAllPermissions() {
       ListMultimap<String,T> tmp = ArrayListMultimap.create();

Reply via email to