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();