001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.security.access;
019
020import static org.apache.hadoop.hbase.AuthUtil.toGroupEntry;
021import static org.junit.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertFalse;
023import static org.junit.jupiter.api.Assertions.assertNotNull;
024import static org.junit.jupiter.api.Assertions.assertTrue;
025
026import java.util.Arrays;
027import java.util.List;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.Coprocessor;
030import org.apache.hadoop.hbase.HBaseTestingUtil;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.NamespaceDescriptor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.TableNameTestExtension;
035import org.apache.hadoop.hbase.TableNotFoundException;
036import org.apache.hadoop.hbase.client.Admin;
037import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
038import org.apache.hadoop.hbase.client.Connection;
039import org.apache.hadoop.hbase.client.ConnectionFactory;
040import org.apache.hadoop.hbase.client.Put;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.ResultScanner;
043import org.apache.hadoop.hbase.client.Scan;
044import org.apache.hadoop.hbase.client.Table;
045import org.apache.hadoop.hbase.client.TableDescriptor;
046import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
047import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
048import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
049import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
050import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
051import org.apache.hadoop.hbase.security.User;
052import org.apache.hadoop.hbase.security.access.Permission.Action;
053import org.apache.hadoop.hbase.testclassification.MediumTests;
054import org.apache.hadoop.hbase.testclassification.SecurityTests;
055import org.apache.hadoop.hbase.util.Bytes;
056import org.apache.hadoop.hbase.zookeeper.ZKUtil;
057import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
058import org.junit.jupiter.api.AfterAll;
059import org.junit.jupiter.api.AfterEach;
060import org.junit.jupiter.api.BeforeAll;
061import org.junit.jupiter.api.BeforeEach;
062import org.junit.jupiter.api.Tag;
063import org.junit.jupiter.api.Test;
064import org.junit.jupiter.api.extension.RegisterExtension;
065import org.slf4j.Logger;
066import org.slf4j.LoggerFactory;
067
068@Tag(SecurityTests.TAG)
069@Tag(MediumTests.TAG)
070public class TestAccessController2 extends SecureTestUtil {
071
072  private static final Logger LOG = LoggerFactory.getLogger(TestAccessController2.class);
073
074  private static final byte[] TEST_ROW = Bytes.toBytes("test");
075  private static final byte[] TEST_FAMILY = Bytes.toBytes("f");
076  private static final byte[] TEST_QUALIFIER = Bytes.toBytes("q");
077  private static final byte[] TEST_VALUE = Bytes.toBytes("value");
078
079  private static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
080  private static Configuration conf;
081
082  /**
083   * The systemUserConnection created here is tied to the system user. In case, you are planning to
084   * create AccessTestAction, DON'T use this systemUserConnection as the 'doAs' user gets eclipsed
085   * by the system user.
086   */
087  private static Connection systemUserConnection;
088
089  private final static byte[] Q1 = Bytes.toBytes("q1");
090  private final static byte[] value1 = Bytes.toBytes("value1");
091
092  private static byte[] TEST_FAMILY_2 = Bytes.toBytes("f2");
093  private static byte[] TEST_ROW_2 = Bytes.toBytes("r2");
094  private final static byte[] Q2 = Bytes.toBytes("q2");
095  private final static byte[] value2 = Bytes.toBytes("value2");
096
097  private static byte[] TEST_ROW_3 = Bytes.toBytes("r3");
098
099  private static final String TESTGROUP_1 = "testgroup_1";
100  private static final String TESTGROUP_2 = "testgroup_2";
101
102  private static User TESTGROUP1_USER1;
103  private static User TESTGROUP2_USER1;
104
105  @RegisterExtension
106  public TableNameTestExtension testTable = new TableNameTestExtension();
107  private String namespace = "testNamespace";
108  private String tname = namespace + ":testtable1";
109  private TableName tableName = TableName.valueOf(tname);
110  private static String TESTGROUP_1_NAME;
111
112  @BeforeAll
113  public static void setupBeforeClass() throws Exception {
114    conf = TEST_UTIL.getConfiguration();
115    // Up the handlers; this test needs more than usual.
116    conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10);
117    // Enable security
118    enableSecurity(conf);
119    // Verify enableSecurity sets up what we require
120    verifyConfiguration(conf);
121    TEST_UTIL.startMiniCluster();
122    // Wait for the ACL table to become available
123    TEST_UTIL.waitUntilAllRegionsAssigned(PermissionStorage.ACL_TABLE_NAME);
124
125    TESTGROUP_1_NAME = toGroupEntry(TESTGROUP_1);
126    TESTGROUP1_USER1 =
127      User.createUserForTesting(conf, "testgroup1_user1", new String[] { TESTGROUP_1 });
128    TESTGROUP2_USER1 =
129      User.createUserForTesting(conf, "testgroup2_user2", new String[] { TESTGROUP_2 });
130
131    systemUserConnection = ConnectionFactory.createConnection(conf);
132  }
133
134  @BeforeEach
135  public void setUp() throws Exception {
136    createNamespace(TEST_UTIL, NamespaceDescriptor.create(namespace).build());
137    try (Table table =
138      createTable(TEST_UTIL, tableName, new byte[][] { TEST_FAMILY, TEST_FAMILY_2 })) {
139      TEST_UTIL.waitTableEnabled(tableName);
140
141      // Ingesting test data.
142      table.put(Arrays.asList(new Put(TEST_ROW).addColumn(TEST_FAMILY, Q1, value1),
143        new Put(TEST_ROW_2).addColumn(TEST_FAMILY, Q2, value2),
144        new Put(TEST_ROW_3).addColumn(TEST_FAMILY_2, Q1, value1)));
145    }
146
147    assertEquals(1, PermissionStorage.getTablePermissions(conf, tableName).size());
148    try {
149      assertEquals(1,
150        AccessControlClient.getUserPermissions(systemUserConnection, tableName.toString()).size());
151    } catch (Throwable e) {
152      LOG.error("Error during call of AccessControlClient.getUserPermissions. ", e);
153    }
154
155  }
156
157  @AfterAll
158  public static void tearDownAfterClass() throws Exception {
159    systemUserConnection.close();
160    TEST_UTIL.shutdownMiniCluster();
161  }
162
163  @AfterEach
164  public void tearDown() throws Exception {
165    // Clean the _acl_ table
166    try {
167      deleteTable(TEST_UTIL, tableName);
168    } catch (TableNotFoundException ex) {
169      // Test deleted the table, no problem
170      LOG.info("Test deleted table " + tableName);
171    }
172    deleteNamespace(TEST_UTIL, namespace);
173    // Verify all table/namespace permissions are erased
174    assertEquals(0, PermissionStorage.getTablePermissions(conf, tableName).size());
175    assertEquals(0, PermissionStorage.getNamespacePermissions(conf, namespace).size());
176  }
177
178  @Test
179  public void testCreateWithCorrectOwner() throws Exception {
180    // Create a test user
181    final User testUser =
182      User.createUserForTesting(TEST_UTIL.getConfiguration(), "TestUser", new String[0]);
183    // Grant the test user the ability to create tables
184    SecureTestUtil.grantGlobal(TEST_UTIL, testUser.getShortName(), Action.CREATE);
185    verifyAllowed(new AccessTestAction() {
186      @Override
187      public Object run() throws Exception {
188        TableDescriptor tableDescriptor =
189          TableDescriptorBuilder.newBuilder(testTable.getTableName())
190            .setColumnFamily(ColumnFamilyDescriptorBuilder.of(TEST_FAMILY)).build();
191        try (Connection connection =
192          ConnectionFactory.createConnection(TEST_UTIL.getConfiguration(), testUser)) {
193          try (Admin admin = connection.getAdmin()) {
194            createTable(TEST_UTIL, admin, tableDescriptor);
195          }
196        }
197        return null;
198      }
199    }, testUser);
200    TEST_UTIL.waitTableAvailable(testTable.getTableName());
201    // Verify that owner permissions have been granted to the test user on the
202    // table just created
203    List<UserPermission> perms = PermissionStorage
204      .getTablePermissions(conf, testTable.getTableName()).get(testUser.getShortName());
205    assertNotNull(perms);
206    assertFalse(perms.isEmpty());
207    // Should be RWXCA
208    assertTrue(perms.get(0).getPermission().implies(Permission.Action.READ));
209    assertTrue(perms.get(0).getPermission().implies(Permission.Action.WRITE));
210    assertTrue(perms.get(0).getPermission().implies(Permission.Action.EXEC));
211    assertTrue(perms.get(0).getPermission().implies(Permission.Action.CREATE));
212    assertTrue(perms.get(0).getPermission().implies(Permission.Action.ADMIN));
213  }
214
215  @Test
216  public void testCreateTableWithGroupPermissions() throws Exception {
217    grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
218    try {
219      AccessTestAction createAction = new AccessTestAction() {
220        @Override
221        public Object run() throws Exception {
222          TableDescriptor tableDescriptor =
223            TableDescriptorBuilder.newBuilder(testTable.getTableName())
224              .setColumnFamily(ColumnFamilyDescriptorBuilder.of(TEST_FAMILY)).build();
225          try (Connection connection =
226            ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
227            try (Admin admin = connection.getAdmin()) {
228              admin.createTable(tableDescriptor);
229            }
230          }
231          return null;
232        }
233      };
234      verifyAllowed(createAction, TESTGROUP1_USER1);
235      verifyDenied(createAction, TESTGROUP2_USER1);
236    } finally {
237      revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
238    }
239  }
240
241  @Test
242  public void testACLTableAccess() throws Exception {
243    final Configuration conf = TEST_UTIL.getConfiguration();
244
245    // Superuser
246    User superUser = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
247
248    // Global users
249    User globalRead = User.createUserForTesting(conf, "globalRead", new String[0]);
250    User globalWrite = User.createUserForTesting(conf, "globalWrite", new String[0]);
251    User globalCreate = User.createUserForTesting(conf, "globalCreate", new String[0]);
252    User globalAdmin = User.createUserForTesting(conf, "globalAdmin", new String[0]);
253    SecureTestUtil.grantGlobal(TEST_UTIL, globalRead.getShortName(), Action.READ);
254    SecureTestUtil.grantGlobal(TEST_UTIL, globalWrite.getShortName(), Action.WRITE);
255    SecureTestUtil.grantGlobal(TEST_UTIL, globalCreate.getShortName(), Action.CREATE);
256    SecureTestUtil.grantGlobal(TEST_UTIL, globalAdmin.getShortName(), Action.ADMIN);
257
258    // Namespace users
259    User nsRead = User.createUserForTesting(conf, "nsRead", new String[0]);
260    User nsWrite = User.createUserForTesting(conf, "nsWrite", new String[0]);
261    User nsCreate = User.createUserForTesting(conf, "nsCreate", new String[0]);
262    User nsAdmin = User.createUserForTesting(conf, "nsAdmin", new String[0]);
263    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsRead.getShortName(),
264      testTable.getTableName().getNamespaceAsString(), Action.READ);
265    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsWrite.getShortName(),
266      testTable.getTableName().getNamespaceAsString(), Action.WRITE);
267    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsCreate.getShortName(),
268      testTable.getTableName().getNamespaceAsString(), Action.CREATE);
269    SecureTestUtil.grantOnNamespace(TEST_UTIL, nsAdmin.getShortName(),
270      testTable.getTableName().getNamespaceAsString(), Action.ADMIN);
271
272    // Table users
273    User tableRead = User.createUserForTesting(conf, "tableRead", new String[0]);
274    User tableWrite = User.createUserForTesting(conf, "tableWrite", new String[0]);
275    User tableCreate = User.createUserForTesting(conf, "tableCreate", new String[0]);
276    User tableAdmin = User.createUserForTesting(conf, "tableAdmin", new String[0]);
277    SecureTestUtil.grantOnTable(TEST_UTIL, tableRead.getShortName(), testTable.getTableName(), null,
278      null, Action.READ);
279    SecureTestUtil.grantOnTable(TEST_UTIL, tableWrite.getShortName(), testTable.getTableName(),
280      null, null, Action.WRITE);
281    SecureTestUtil.grantOnTable(TEST_UTIL, tableCreate.getShortName(), testTable.getTableName(),
282      null, null, Action.CREATE);
283    SecureTestUtil.grantOnTable(TEST_UTIL, tableAdmin.getShortName(), testTable.getTableName(),
284      null, null, Action.ADMIN);
285
286    grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
287    try {
288      // Write tests
289
290      AccessTestAction writeAction = new AccessTestAction() {
291        @Override
292        public Object run() throws Exception {
293
294          try (Connection conn = ConnectionFactory.createConnection(conf);
295            Table t = conn.getTable(PermissionStorage.ACL_TABLE_NAME)) {
296            t.put(new Put(TEST_ROW).addColumn(PermissionStorage.ACL_LIST_FAMILY, TEST_QUALIFIER,
297              TEST_VALUE));
298            return null;
299          } finally {
300          }
301        }
302      };
303
304      // All writes to ACL table denied except for GLOBAL WRITE permission and superuser
305
306      verifyDenied(writeAction, globalAdmin, globalCreate, globalRead, TESTGROUP2_USER1);
307      verifyDenied(writeAction, nsAdmin, nsCreate, nsRead, nsWrite);
308      verifyDenied(writeAction, tableAdmin, tableCreate, tableRead, tableWrite);
309      verifyAllowed(writeAction, superUser, globalWrite, TESTGROUP1_USER1);
310    } finally {
311      revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
312    }
313
314    grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
315    try {
316      // Read tests
317
318      AccessTestAction scanAction = new AccessTestAction() {
319        @Override
320        public Object run() throws Exception {
321          try (Connection conn = ConnectionFactory.createConnection(conf);
322            Table t = conn.getTable(PermissionStorage.ACL_TABLE_NAME)) {
323            ResultScanner s = t.getScanner(new Scan());
324            try {
325              for (Result r = s.next(); r != null; r = s.next()) {
326                // do nothing
327              }
328            } finally {
329              s.close();
330            }
331            return null;
332          }
333        }
334      };
335
336      // All reads from ACL table denied except for GLOBAL READ and superuser
337
338      verifyDenied(scanAction, globalAdmin, globalCreate, globalWrite, TESTGROUP2_USER1);
339      verifyDenied(scanAction, nsCreate, nsAdmin, nsRead, nsWrite);
340      verifyDenied(scanAction, tableCreate, tableAdmin, tableRead, tableWrite);
341      verifyAllowed(scanAction, superUser, globalRead, TESTGROUP1_USER1);
342    } finally {
343      revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
344    }
345  }
346
347  /*
348   * Test table scan operation at table, column family and column qualifier level.
349   */
350  @Test
351  public void testPostGrantAndRevokeScanAction() throws Exception {
352    AccessTestAction scanTableActionForGroupWithTableLevelAccess = new AccessTestAction() {
353      @Override
354      public Void run() throws Exception {
355        try (Connection connection = ConnectionFactory.createConnection(conf);
356          Table table = connection.getTable(tableName)) {
357          Scan s1 = new Scan();
358          try (ResultScanner scanner1 = table.getScanner(s1)) {
359            Result[] next1 = scanner1.next(5);
360            assertTrue(next1.length == 3,
361              "User having table level access should be able to scan all "
362                + "the data in the table.");
363          }
364        }
365        return null;
366      }
367    };
368
369    AccessTestAction scanTableActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
370      @Override
371      public Void run() throws Exception {
372        try (Connection connection = ConnectionFactory.createConnection(conf);
373          Table table = connection.getTable(tableName)) {
374          Scan s1 = new Scan();
375          try (ResultScanner scanner1 = table.getScanner(s1)) {
376            Result[] next1 = scanner1.next(5);
377            assertTrue(next1.length == 2,
378              "User having column family level access should be able to scan all "
379                + "the data belonging to that family.");
380          }
381        }
382        return null;
383      }
384    };
385
386    AccessTestAction scanFamilyActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
387      @Override
388      public Void run() throws Exception {
389        try (Connection connection = ConnectionFactory.createConnection(conf);
390          Table table = connection.getTable(tableName)) {
391          Scan s1 = new Scan();
392          s1.addFamily(TEST_FAMILY_2);
393          try (ResultScanner scanner1 = table.getScanner(s1)) {
394            scanner1.next();
395          }
396        }
397        return null;
398      }
399    };
400
401    AccessTestAction scanTableActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
402      @Override
403      public Void run() throws Exception {
404        try (Connection connection = ConnectionFactory.createConnection(conf);
405          Table table = connection.getTable(tableName)) {
406          Scan s1 = new Scan();
407          try (ResultScanner scanner1 = table.getScanner(s1)) {
408            Result[] next1 = scanner1.next(5);
409            assertTrue(next1.length == 1,
410              "User having column qualifier level access should be able to scan "
411                + "that column family qualifier data.");
412          }
413        }
414        return null;
415      }
416    };
417
418    AccessTestAction scanFamilyActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
419      @Override
420      public Void run() throws Exception {
421        try (Connection connection = ConnectionFactory.createConnection(conf);
422          Table table = connection.getTable(tableName)) {
423          Scan s1 = new Scan();
424          s1.addFamily(TEST_FAMILY_2);
425          try (ResultScanner scanner1 = table.getScanner(s1)) {
426            scanner1.next();
427          }
428        }
429        return null;
430      }
431    };
432
433    AccessTestAction scanQualifierActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
434      @Override
435      public Void run() throws Exception {
436        try (Connection connection = ConnectionFactory.createConnection(conf);
437          Table table = connection.getTable(tableName)) {
438          Scan s1 = new Scan();
439          s1.addColumn(TEST_FAMILY, Q2);
440          try (ResultScanner scanner1 = table.getScanner(s1)) {
441            scanner1.next();
442          }
443        }
444        return null;
445      }
446    };
447
448    // Verify user from a group which has table level access can read all the data and group which
449    // has no access can't read any data.
450    grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, null, null, Action.READ);
451    verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
452    verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithTableLevelAccess);
453
454    // Verify user from a group whose table level access has been revoked can't read any data.
455    revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, null, null);
456    verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
457
458    // Verify user from a group which has column family level access can read all the data
459    // belonging to that family and group which has no access can't read any data.
460    grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, null, Permission.Action.READ);
461    verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
462    verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
463    verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithFamilyLevelAccess);
464    verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
465
466    // Verify user from a group whose column family level access has been revoked can't read any
467    // data from that family.
468    revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, null);
469    verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
470
471    // Verify user from a group which has column qualifier level access can read data that has this
472    // family and qualifier, and group which has no access can't read any data.
473    grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, Q1, Action.READ);
474    verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
475    verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
476    verifyDenied(TESTGROUP1_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
477    verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithQualifierLevelAccess);
478    verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
479    verifyDenied(TESTGROUP2_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
480
481    // Verify user from a group whose column qualifier level access has been revoked can't read the
482    // data having this column family and qualifier.
483    revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, Q1);
484    verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
485  }
486
487  public static class MyAccessController extends AccessController {
488  }
489
490  @Test
491  public void testCoprocessorLoading() throws Exception {
492    MasterCoprocessorHost cpHost =
493      TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost();
494    cpHost.load(MyAccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
495    AccessController ACCESS_CONTROLLER = cpHost.findCoprocessor(MyAccessController.class);
496    MasterCoprocessorEnvironment CP_ENV =
497      cpHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf);
498    RegionServerCoprocessorHost rsHost =
499      TEST_UTIL.getMiniHBaseCluster().getRegionServer(0).getRegionServerCoprocessorHost();
500    RegionServerCoprocessorEnvironment RSCP_ENV =
501      rsHost.createEnvironment(ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf);
502  }
503
504  @Test
505  public void testACLZNodeDeletion() throws Exception {
506    String baseAclZNode = "/hbase/acl/";
507    String ns = "testACLZNodeDeletionNamespace";
508    NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
509    createNamespace(TEST_UTIL, desc);
510
511    final TableName table = TableName.valueOf(ns, "testACLZNodeDeletionTable");
512    final byte[] family = Bytes.toBytes("f1");
513    TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(table)
514      .setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)).build();
515    createTable(TEST_UTIL, tableDescriptor);
516
517    // Namespace needs this, as they follow the lazy creation of ACL znode.
518    grantOnNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
519    ZKWatcher zkw = TEST_UTIL.getMiniHBaseCluster().getMaster().getZooKeeper();
520    assertTrue(ZKUtil.checkExists(zkw, baseAclZNode + table.getNameAsString()) != -1,
521      "The acl znode for table should exist");
522    assertTrue(ZKUtil.checkExists(zkw, baseAclZNode + convertToNamespace(ns)) != -1,
523      "The acl znode for namespace should exist");
524
525    revokeFromNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
526    deleteTable(TEST_UTIL, table);
527    deleteNamespace(TEST_UTIL, ns);
528
529    assertTrue(ZKUtil.checkExists(zkw, baseAclZNode + table.getNameAsString()) == -1,
530      "The acl znode for table should have been deleted");
531    assertTrue(ZKUtil.checkExists(zkw, baseAclZNode + convertToNamespace(ns)) == -1,
532      "The acl znode for namespace should have been deleted");
533  }
534}