HBASE-15780 Make AuthUtil public and rely on it for talking to secure HBase.
Signed-off-by: Gary Helmling <garyh@apache.org> Signed-off-by: Mikhail Antonov <antonov@apache.org> Signed-off-by: stack <stack@apache.org>
This commit is contained in:
parent
0042d6d4c8
commit
e365191896
|
@ -915,7 +915,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 {
|
||||
|
|
|
@ -354,7 +354,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) {
|
||||
|
@ -383,7 +383,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -130,14 +138,6 @@ public class ChoreService implements ChoreServicer {
|
|||
choresMissingStartTime = new HashMap<ScheduledChore, Boolean>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
@ -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;
|
||||
|
|
|
@ -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: "
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -235,7 +235,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;
|
||||
|
@ -266,7 +266,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());
|
||||
|
@ -344,7 +344,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);
|
||||
|
@ -370,7 +370,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);
|
||||
|
@ -717,7 +717,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;
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue