diff --git a/pom.xml b/pom.xml
index 65e0a86b174..9bd3a605cae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -861,7 +861,7 @@
1.5.8
1.0.1
0.7.0
- 3.3.3
+ 3.4.0-SNAPSHOT
0.0.1-SNAPSHOT
/usr
@@ -1404,6 +1404,9 @@
security
+
+ 0.20.205.1-7070-SNAPSHOT
+
${project.artifactId}-${project.version}-security
diff --git a/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java b/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
index fe6f4a5ad40..786ae210218 100644
--- a/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
+++ b/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
@@ -34,8 +34,10 @@ import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil;
-import org.apache.zookeeper.server.NIOServerCnxn;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.zookeeper.server.NIOServerCnxnFactory;
import org.apache.zookeeper.server.ZooKeeperServer;
import org.apache.zookeeper.server.persistence.FileTxnLog;
@@ -57,20 +59,26 @@ public class MiniZooKeeperCluster {
private int clientPort;
- private List standaloneServerFactoryList;
+ private List standaloneServerFactoryList;
private List zooKeeperServers;
private List clientPortList;
private int activeZKServerIndex;
private int tickTime = 0;
- /** Create mini ZooKeeper cluster. */
+ private Configuration configuration;
+
public MiniZooKeeperCluster() {
+ this(new Configuration());
+ }
+
+ public MiniZooKeeperCluster(Configuration configuration) {
this.started = false;
+ this.configuration = configuration;
activeZKServerIndex = -1;
zooKeeperServers = new ArrayList();
clientPortList = new ArrayList();
- standaloneServerFactoryList = new ArrayList();
+ standaloneServerFactoryList = new ArrayList();
}
public void setDefaultClientPort(int clientPort) {
@@ -148,11 +156,14 @@ public class MiniZooKeeperCluster {
tickTimeToUse = TICK_TIME;
}
ZooKeeperServer server = new ZooKeeperServer(dir, dir, tickTimeToUse);
- NIOServerCnxn.Factory standaloneServerFactory;
+ NIOServerCnxnFactory standaloneServerFactory;
while (true) {
try {
- standaloneServerFactory = new NIOServerCnxn.Factory(
- new InetSocketAddress(tentativePort));
+ standaloneServerFactory = new NIOServerCnxnFactory();
+ standaloneServerFactory.configure(
+ new InetSocketAddress(tentativePort),
+ configuration.getInt(HConstants.ZOOKEEPER_MAX_CLIENT_CNXNS,
+ 1000));
} catch (BindException e) {
LOG.debug("Failed binding ZK Server to client port: " +
tentativePort);
@@ -204,7 +215,7 @@ public class MiniZooKeeperCluster {
}
// shut down all the zk servers
for (int i = 0; i < standaloneServerFactoryList.size(); i++) {
- NIOServerCnxn.Factory standaloneServerFactory =
+ NIOServerCnxnFactory standaloneServerFactory =
standaloneServerFactoryList.get(i);
int clientPort = clientPortList.get(i);
@@ -236,7 +247,7 @@ public class MiniZooKeeperCluster {
}
// Shutdown the current active one
- NIOServerCnxn.Factory standaloneServerFactory =
+ NIOServerCnxnFactory standaloneServerFactory =
standaloneServerFactoryList.get(activeZKServerIndex);
int clientPort = clientPortList.get(activeZKServerIndex);
@@ -277,7 +288,7 @@ public class MiniZooKeeperCluster {
int backupZKServerIndex = activeZKServerIndex+1;
// Shutdown the current active one
- NIOServerCnxn.Factory standaloneServerFactory =
+ NIOServerCnxnFactory standaloneServerFactory =
standaloneServerFactoryList.get(backupZKServerIndex);
int clientPort = clientPortList.get(backupZKServerIndex);
diff --git a/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java b/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
index d3d9836e6a7..132d1c2ef89 100644
--- a/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
+++ b/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKUtil.java
@@ -40,9 +40,9 @@ import org.apache.zookeeper.AsyncCallback;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
-import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
/**
@@ -696,6 +696,41 @@ public class ZKUtil {
setData(zkw, znode, data, -1);
}
+ public static boolean isSecureZooKeeper(Configuration conf) {
+ // TODO: We need a better check for security enabled ZooKeeper. Currently
+ // the secure ZooKeeper client is set up using a supplied JaaS
+ // configuration file. But if the system property for the JaaS
+ // configuration file is set, this may not be an exclusive indication
+ // that HBase should set ACLs on znodes. As an alternative, we could do
+ // this more like Hadoop and build a JaaS configuration programmatically
+ // based on a site conf setting. The scope of such a change will be
+ // addressed in HBASE-4791.
+ return (System.getProperty("java.security.auth.login.config") != null);
+ }
+
+ private static ArrayList createACL(ZooKeeperWatcher zkw, String node) {
+ if (isSecureZooKeeper(zkw.getConfiguration())) {
+ // Certain znodes must be readable by non-authenticated clients
+ if ((node.equals(zkw.rootServerZNode) == true) ||
+ (node.equals(zkw.masterAddressZNode) == true) ||
+ (node.equals(zkw.clusterIdZNode) == true)) {
+ return ZooKeeperWatcher.CREATOR_ALL_AND_WORLD_READABLE;
+ }
+ return Ids.CREATOR_ALL_ACL;
+ } else {
+ return Ids.OPEN_ACL_UNSAFE;
+ }
+ }
+
+ public static void waitForZKConnectionIfAuthenticating(ZooKeeperWatcher zkw)
+ throws InterruptedException {
+ if (isSecureZooKeeper(zkw.getConfiguration())) {
+ LOG.debug("Waiting for ZooKeeperWatcher to authenticate");
+ zkw.saslLatch.await();
+ LOG.debug("Done waiting.");
+ }
+ }
+
//
// Node creation
//
@@ -722,7 +757,8 @@ public class ZKUtil {
String znode, byte [] data)
throws KeeperException {
try {
- zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+ waitForZKConnectionIfAuthenticating(zkw);
+ zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
CreateMode.EPHEMERAL);
} catch (KeeperException.NodeExistsException nee) {
if(!watchAndCheckExists(zkw, znode)) {
@@ -761,7 +797,8 @@ public class ZKUtil {
ZooKeeperWatcher zkw, String znode, byte [] data)
throws KeeperException {
try {
- zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+ waitForZKConnectionIfAuthenticating(zkw);
+ zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
CreateMode.PERSISTENT);
} catch (KeeperException.NodeExistsException nee) {
try {
@@ -798,7 +835,8 @@ public class ZKUtil {
String znode, byte [] data)
throws KeeperException, KeeperException.NodeExistsException {
try {
- zkw.getRecoverableZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
+ waitForZKConnectionIfAuthenticating(zkw);
+ zkw.getRecoverableZooKeeper().create(znode, data, createACL(zkw, znode),
CreateMode.PERSISTENT);
return zkw.getRecoverableZooKeeper().exists(znode, zkw).getVersion();
} catch (InterruptedException e) {
@@ -825,8 +863,13 @@ public class ZKUtil {
public static void asyncCreate(ZooKeeperWatcher zkw,
String znode, byte [] data, final AsyncCallback.StringCallback cb,
final Object ctx) {
- zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data, Ids.OPEN_ACL_UNSAFE,
- CreateMode.PERSISTENT, cb, ctx);
+ try {
+ waitForZKConnectionIfAuthenticating(zkw);
+ zkw.getRecoverableZooKeeper().getZooKeeper().create(znode, data,
+ createACL(zkw, znode), CreateMode.PERSISTENT, cb, ctx);
+ } catch (InterruptedException e) {
+ zkw.interruptedException(e);
+ }
}
/**
@@ -844,8 +887,9 @@ public class ZKUtil {
throws KeeperException {
try {
RecoverableZooKeeper zk = zkw.getRecoverableZooKeeper();
+ waitForZKConnectionIfAuthenticating(zkw);
if (zk.exists(znode, false) == null) {
- zk.create(znode, new byte[0], Ids.OPEN_ACL_UNSAFE,
+ zk.create(znode, new byte[0], createACL(zkw,znode),
CreateMode.PERSISTENT);
}
} catch(KeeperException.NodeExistsException nee) {
@@ -881,7 +925,8 @@ public class ZKUtil {
if(znode == null) {
return;
}
- zkw.getRecoverableZooKeeper().create(znode, new byte[0], Ids.OPEN_ACL_UNSAFE,
+ waitForZKConnectionIfAuthenticating(zkw);
+ zkw.getRecoverableZooKeeper().create(znode, new byte[0], createACL(zkw, znode),
CreateMode.PERSISTENT);
} catch(KeeperException.NodeExistsException nee) {
return;
diff --git a/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java b/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
index a75cf8760a6..cc90a14a7e1 100644
--- a/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
+++ b/src/main/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWatcher.java
@@ -20,8 +20,12 @@
package org.apache.hadoop.hbase.zookeeper;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,6 +37,8 @@ import org.apache.hadoop.hbase.util.Threads;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
/**
* Acts as the single ZooKeeper Watcher. One instance of this is instantiated
@@ -48,7 +54,7 @@ import org.apache.zookeeper.Watcher;
public class ZooKeeperWatcher implements Watcher, Abortable {
private static final Log LOG = LogFactory.getLog(ZooKeeperWatcher.class);
- // Identifiier for this watcher (for logging only). Its made of the prefix
+ // Identifier for this watcher (for logging only). It is made of the prefix
// passed on construction and the zookeeper sessionid.
private String identifier;
@@ -65,6 +71,13 @@ public class ZooKeeperWatcher implements Watcher, Abortable {
private final List listeners =
new CopyOnWriteArrayList();
+ // Used by ZKUtil:waitForZKConnectionIfAuthenticating to wait for SASL
+ // negotiation to complete
+ public CountDownLatch saslLatch = new CountDownLatch(1);
+
+ // set of unassigned nodes watched
+ private Set unassignedNodes = new HashSet();
+
// node names
// base znode for this cluster
@@ -88,11 +101,17 @@ public class ZooKeeperWatcher implements Watcher, Abortable {
// znode used for log splitting work assignment
public String splitLogZNode;
+ // Certain ZooKeeper nodes need to be world-readable
+ public static final ArrayList CREATOR_ALL_AND_WORLD_READABLE =
+ new ArrayList() { {
+ add(new ACL(ZooDefs.Perms.READ,ZooDefs.Ids.ANYONE_ID_UNSAFE));
+ add(new ACL(ZooDefs.Perms.ALL,ZooDefs.Ids.AUTH_IDS));
+ }};
+
private final Configuration conf;
private final Exception constructorCaller;
-
/**
* Instantiate a ZooKeeper connection and watcher.
* @param descriptor Descriptive string that is added to zookeeper sessionid
@@ -315,17 +334,38 @@ public class ZooKeeperWatcher implements Watcher, Abortable {
LOG.debug(this.identifier + " connected");
break;
+ case SaslAuthenticated:
+ if (ZKUtil.isSecureZooKeeper(this.conf)) {
+ // We are authenticated, clients can proceed.
+ saslLatch.countDown();
+ }
+ break;
+
+ case AuthFailed:
+ if (ZKUtil.isSecureZooKeeper(this.conf)) {
+ // We could not be authenticated, but clients should proceed anyway.
+ // Only access to znodes that require SASL authentication will be
+ // denied. The client may never need to access them.
+ saslLatch.countDown();
+ }
+ break;
+
// Abort the server if Disconnected or Expired
- // TODO: Åny reason to handle these two differently?
case Disconnected:
LOG.debug(prefix("Received Disconnected from ZooKeeper, ignoring"));
break;
case Expired:
+ if (ZKUtil.isSecureZooKeeper(this.conf)) {
+ // We consider Expired equivalent to AuthFailed for this
+ // connection. Authentication is never going to complete. The
+ // client should proceed to do cleanup.
+ saslLatch.countDown();
+ }
String msg = prefix(this.identifier + " received expired from " +
"ZooKeeper, aborting");
// TODO: One thought is to add call to ZooKeeperListener so say,
- // ZooKeperNodeTracker can zero out its data values.
+ // ZooKeeperNodeTracker can zero out its data values.
if (this.abortable != null) this.abortable.abort(msg,
new KeeperException.SessionExpiredException());
break;
@@ -396,6 +436,10 @@ public class ZooKeeperWatcher implements Watcher, Abortable {
}
}
+ public Configuration getConfiguration() {
+ return conf;
+ }
+
@Override
public void abort(String why, Throwable e) {
this.abortable.abort(why, e);
diff --git a/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java b/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
index d1b7647cb27..38f312e4cab 100644
--- a/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
+++ b/src/test/java/org/apache/hadoop/hbase/HBaseTestingUtility.java
@@ -420,7 +420,7 @@ public class HBaseTestingUtility {
throw new IOException("Cluster already running at " + dir);
}
this.passedZkCluster = false;
- this.zkCluster = new MiniZooKeeperCluster();
+ this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
int clientPort = this.zkCluster.startup(dir,zooKeeperServerNum);
this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT,
Integer.toString(clientPort));
diff --git a/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java b/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java
new file mode 100644
index 00000000000..814ce83c8f9
--- /dev/null
+++ b/src/test/java/org/apache/hadoop/hbase/zookeeper/TestZooKeeperACL.java
@@ -0,0 +1,266 @@
+/**
+ * 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.hadoop.hbase.zookeeper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.TestZooKeeper;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestZooKeeperACL {
+ private final static Log LOG = LogFactory.getLog(TestZooKeeperACL.class);
+ private final static HBaseTestingUtility TEST_UTIL =
+ new HBaseTestingUtility();
+
+ private static ZooKeeperWatcher zkw;
+ private static boolean secureZKAvailable;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ File saslConfFile = File.createTempFile("tmp", "jaas.conf");
+ FileWriter fwriter = new FileWriter(saslConfFile);
+
+ fwriter.write("" +
+ "Server {\n" +
+ "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+ "user_hbase=\"secret\";\n" +
+ "};\n" +
+ "Client {\n" +
+ "org.apache.zookeeper.server.auth.DigestLoginModule required\n" +
+ "username=\"hbase\"\n" +
+ "password=\"secret\";\n" +
+ "};" + "\n");
+ fwriter.close();
+ System.setProperty("java.security.auth.login.config",
+ saslConfFile.getAbsolutePath());
+ System.setProperty("zookeeper.authProvider.1",
+ "org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+
+ TEST_UTIL.getConfiguration().setBoolean("dfs.support.append", true);
+ TEST_UTIL.getConfiguration().setInt("hbase.zookeeper.property.maxClientCnxns", 1000);
+
+ // If Hadoop is missing HADOOP-7070 the cluster will fail to start due to
+ // the JAAS configuration required by ZK being clobbered by Hadoop
+ try {
+ TEST_UTIL.startMiniCluster();
+ } catch (IOException e) {
+ LOG.warn("Hadoop is missing HADOOP-7070", e);
+ secureZKAvailable = false;
+ return;
+ }
+ zkw = new ZooKeeperWatcher(
+ new Configuration(TEST_UTIL.getConfiguration()),
+ TestZooKeeper.class.getName(), null);
+ ZKUtil.waitForZKConnectionIfAuthenticating(zkw);
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+ TEST_UTIL.shutdownMiniCluster();
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+ TEST_UTIL.ensureSomeRegionServersAvailable(2);
+ }
+
+ /**
+ * Create a node and check its ACL. When authentication is enabled on
+ * Zookeeper, all nodes (except /hbase/root-region-server, /hbase/master
+ * and /hbase/hbaseid) should be created so that only the hbase server user
+ * (master or region server user) that created them can access them, and
+ * this user should have all permissions on this node. For
+ * /hbase/root-region-server, /hbase/master, and /hbase/hbaseid the
+ * permissions should be as above, but should also be world-readable. First
+ * we check the general case of /hbase nodes in the following test, and
+ * then check the subset of world-readable nodes in the three tests after
+ * that.
+ */
+ @Test (timeout=30000)
+ public void testHBaseRootZNodeACL() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+
+ List acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+ .getACL("/hbase", new Stat());
+ assertEquals(acls.size(),1);
+ assertEquals(acls.get(0).getId().getScheme(),"sasl");
+ assertEquals(acls.get(0).getId().getId(),"hbase");
+ assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
+ }
+
+ /**
+ * When authentication is enabled on Zookeeper, /hbase/root-region-server
+ * should be created with 2 ACLs: one specifies that the hbase user has
+ * full access to the node; the other, that it is world-readable.
+ */
+ @Test (timeout=30000)
+ public void testHBaseRootRegionServerZNodeACL() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+
+ List acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+ .getACL("/hbase/root-region-server", new Stat());
+ assertEquals(acls.size(),2);
+
+ boolean foundWorldReadableAcl = false;
+ boolean foundHBaseOwnerAcl = false;
+ for(int i = 0; i < 2; i++) {
+ if (acls.get(i).getId().getScheme().equals("world") == true) {
+ assertEquals(acls.get(0).getId().getId(),"anyone");
+ assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+ foundWorldReadableAcl = true;
+ }
+ else {
+ if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+ assertEquals(acls.get(1).getId().getId(),"hbase");
+ assertEquals(acls.get(1).getId().getScheme(),"sasl");
+ foundHBaseOwnerAcl = true;
+ } else { // error: should not get here: test fails.
+ assertTrue(false);
+ }
+ }
+ }
+ assertTrue(foundWorldReadableAcl);
+ assertTrue(foundHBaseOwnerAcl);
+ }
+
+ /**
+ * When authentication is enabled on Zookeeper, /hbase/master should be
+ * created with 2 ACLs: one specifies that the hbase user has full access
+ * to the node; the other, that it is world-readable.
+ */
+ @Test (timeout=30000)
+ public void testHBaseMasterServerZNodeACL() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+
+ List acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+ .getACL("/hbase/master", new Stat());
+ assertEquals(acls.size(),2);
+
+ boolean foundWorldReadableAcl = false;
+ boolean foundHBaseOwnerAcl = false;
+ for(int i = 0; i < 2; i++) {
+ if (acls.get(i).getId().getScheme().equals("world") == true) {
+ assertEquals(acls.get(0).getId().getId(),"anyone");
+ assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+ foundWorldReadableAcl = true;
+ } else {
+ if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+ assertEquals(acls.get(1).getId().getId(),"hbase");
+ assertEquals(acls.get(1).getId().getScheme(),"sasl");
+ foundHBaseOwnerAcl = true;
+ } else { // error: should not get here: test fails.
+ assertTrue(false);
+ }
+ }
+ }
+ assertTrue(foundWorldReadableAcl);
+ assertTrue(foundHBaseOwnerAcl);
+ }
+
+ /**
+ * When authentication is enabled on Zookeeper, /hbase/hbaseid should be
+ * created with 2 ACLs: one specifies that the hbase user has full access
+ * to the node; the other, that it is world-readable.
+ */
+ @Test (timeout=30000)
+ public void testHBaseIDZNodeACL() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+
+ List acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+ .getACL("/hbase/hbaseid", new Stat());
+ assertEquals(acls.size(),2);
+
+ boolean foundWorldReadableAcl = false;
+ boolean foundHBaseOwnerAcl = false;
+ for(int i = 0; i < 2; i++) {
+ if (acls.get(i).getId().getScheme().equals("world") == true) {
+ assertEquals(acls.get(0).getId().getId(),"anyone");
+ assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.READ);
+ foundWorldReadableAcl = true;
+ } else {
+ if (acls.get(i).getId().getScheme().equals("sasl") == true) {
+ assertEquals(acls.get(1).getId().getId(),"hbase");
+ assertEquals(acls.get(1).getId().getScheme(),"sasl");
+ foundHBaseOwnerAcl = true;
+ } else { // error: should not get here: test fails.
+ assertTrue(false);
+ }
+ }
+ }
+ assertTrue(foundWorldReadableAcl);
+ assertTrue(foundHBaseOwnerAcl);
+ }
+
+ /**
+ * Finally, we check the ACLs of a node outside of the /hbase hierarchy and
+ * verify that its ACL is simply 'hbase:Perms.ALL'.
+ */
+ @Test
+ public void testOutsideHBaseNodeACL() throws Exception {
+ if (!secureZKAvailable) {
+ return;
+ }
+
+ ZKUtil.createWithParents(zkw, "/testACLNode");
+ List acls = zkw.getRecoverableZooKeeper().getZooKeeper()
+ .getACL("/testACLNode", new Stat());
+ assertEquals(acls.size(),1);
+ assertEquals(acls.get(0).getId().getScheme(),"sasl");
+ assertEquals(acls.get(0).getId().getId(),"hbase");
+ assertEquals(acls.get(0).getPerms(), ZooDefs.Perms.ALL);
+ }
+}