View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.security.access;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.fail;
23  
24  import java.io.IOException;
25  import java.lang.reflect.UndeclaredThrowableException;
26  import java.security.PrivilegedActionException;
27  import java.security.PrivilegedExceptionAction;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.concurrent.Callable;
31  import java.util.concurrent.CountDownLatch;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.hbase.Coprocessor;
37  import org.apache.hadoop.hbase.HBaseTestingUtility;
38  import org.apache.hadoop.hbase.HConstants;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HTableDescriptor;
41  import org.apache.hadoop.hbase.HRegionInfo;
42  import org.apache.hadoop.hbase.MiniHBaseCluster;
43  import org.apache.hadoop.hbase.NamespaceDescriptor;
44  import org.apache.hadoop.hbase.TableName;
45  import org.apache.hadoop.hbase.TableNotEnabledException;
46  import org.apache.hadoop.hbase.Waiter.Predicate;
47  import org.apache.hadoop.hbase.client.Connection;
48  import org.apache.hadoop.hbase.client.ConnectionFactory;
49  import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
50  import org.apache.hadoop.hbase.client.Admin;
51  import org.apache.hadoop.hbase.client.Table;
52  import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
53  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
54  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
55  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
56  import org.apache.hadoop.hbase.io.hfile.HFile;
57  import org.apache.hadoop.hbase.master.HMaster;
58  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
59  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
60  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
61  import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
62  import org.apache.hadoop.hbase.regionserver.Region;
63  import org.apache.hadoop.hbase.security.AccessDeniedException;
64  import org.apache.hadoop.hbase.security.User;
65  import org.apache.hadoop.hbase.security.access.Permission.Action;
66  import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
67  
68  import com.google.common.collect.Lists;
69  import com.google.common.collect.Maps;
70  import com.google.protobuf.BlockingRpcChannel;
71  import com.google.protobuf.ServiceException;
72  
73  /**
74   * Utility methods for testing security
75   */
76  public class SecureTestUtil {
77  
78    private static final Log LOG = LogFactory.getLog(SecureTestUtil.class);
79    private static final int WAIT_TIME = 10000;
80  
81    public static void configureSuperuser(Configuration conf) throws IOException {
82      // The secure minicluster creates separate service principals based on the
83      // current user's name, one for each slave. We need to add all of these to
84      // the superuser list or security won't function properly. We expect the
85      // HBase service account(s) to have superuser privilege.
86      String currentUser = User.getCurrent().getName();
87      StringBuffer sb = new StringBuffer();
88      sb.append("admin,");
89      sb.append(currentUser);
90      // Assumes we won't ever have a minicluster with more than 5 slaves
91      for (int i = 0; i < 5; i++) {
92        sb.append(',');
93        sb.append(currentUser); sb.append(".hfs."); sb.append(i);
94      }
95      conf.set("hbase.superuser", sb.toString());
96    }
97  
98    public static void enableSecurity(Configuration conf) throws IOException {
99      conf.set("hadoop.security.authorization", "false");
100     conf.set("hadoop.security.authentication", "simple");
101     conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName() +
102       "," + MasterSyncObserver.class.getName());
103     conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName() +
104       "," + SecureBulkLoadEndpoint.class.getName());
105     conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
106     // Need HFile V3 for tags for security features
107     conf.setInt(HFile.FORMAT_VERSION_KEY, 3);
108     configureSuperuser(conf);
109   }
110 
111   public static void verifyConfiguration(Configuration conf) {
112     String coprocs = conf.get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY);
113     boolean accessControllerLoaded = false;
114     for (String coproc : coprocs.split(",")) {
115       try {
116         accessControllerLoaded = AccessController.class.isAssignableFrom(Class.forName(coproc));
117         if (accessControllerLoaded) break;
118       } catch (ClassNotFoundException cnfe) {
119       }
120     }
121     if (!(conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY).contains(
122         AccessController.class.getName())
123         && accessControllerLoaded && conf.get(
124         CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY).contains(
125         AccessController.class.getName()))) {
126       throw new RuntimeException("AccessController is missing from a system coprocessor list");
127     }
128     if (conf.getInt(HFile.FORMAT_VERSION_KEY, 2) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
129       throw new RuntimeException("Post 0.96 security features require HFile version >= 3");
130     }
131   }
132 
133   public static void checkTablePerms(Configuration conf, TableName table, byte[] family, byte[] column,
134       Permission.Action... actions) throws IOException {
135     Permission[] perms = new Permission[actions.length];
136     for (int i = 0; i < actions.length; i++) {
137       perms[i] = new TablePermission(table, family, column, actions[i]);
138     }
139 
140     checkTablePerms(conf, table, perms);
141   }
142 
143   public static void checkTablePerms(Configuration conf, TableName table, Permission... perms)
144   throws IOException {
145     CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
146     for (Permission p : perms) {
147       request.addPermission(ProtobufUtil.toPermission(p));
148     }
149     try (Connection connection = ConnectionFactory.createConnection(conf)) {
150       try (Table acl = connection.getTable(table)) {
151         AccessControlService.BlockingInterface protocol =
152             AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
153         try {
154           protocol.checkPermissions(null, request.build());
155         } catch (ServiceException se) {
156           ProtobufUtil.toIOException(se);
157         }
158       }
159     }
160   }
161 
162   /**
163    * An AccessTestAction performs an action that will be examined to confirm
164    * the results conform to expected access rights.
165    * <p>
166    * To indicate an action was allowed, return null or a non empty list of
167    * KeyValues.
168    * <p>
169    * To indicate the action was not allowed, either throw an AccessDeniedException
170    * or return an empty list of KeyValues.
171    */
172   public static interface AccessTestAction extends PrivilegedExceptionAction<Object> { }
173 
174   /** This fails only in case of ADE or empty list for any of the actions. */
175   public static void verifyAllowed(User user, AccessTestAction... actions) throws Exception {
176     for (AccessTestAction action : actions) {
177       try {
178         Object obj = user.runAs(action);
179         if (obj != null && obj instanceof List<?>) {
180           List<?> results = (List<?>) obj;
181           if (results != null && results.isEmpty()) {
182             fail("Empty non null results from action for user '" + user.getShortName() + "'");
183           }
184         }
185       } catch (AccessDeniedException ade) {
186         fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
187       }
188     }
189   }
190 
191   /** This fails only in case of ADE or empty list for any of the users. */
192   public static void verifyAllowed(AccessTestAction action, User... users) throws Exception {
193     for (User user : users) {
194       verifyAllowed(user, action);
195     }
196   }
197 
198   public static void verifyAllowed(User user, AccessTestAction action, int count) throws Exception {
199     try {
200       Object obj = user.runAs(action);
201       if (obj != null && obj instanceof List<?>) {
202         List<?> results = (List<?>) obj;
203         if (results != null && results.isEmpty()) {
204           fail("Empty non null results from action for user '" + user.getShortName() + "'");
205         }
206         assertEquals(count, results.size());
207       }
208     } catch (AccessDeniedException ade) {
209       fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
210     }
211   }
212 
213   /** This passes only in case of ADE for all users. */
214   public static void verifyDenied(AccessTestAction action, User... users) throws Exception {
215     for (User user : users) {
216       verifyDenied(user, action);
217     }
218   }
219 
220   /** This passes only in case of empty list for all users. */
221   public static void verifyIfEmptyList(AccessTestAction action, User... users) throws Exception {
222     for (User user : users) {
223       try {
224         Object obj = user.runAs(action);
225         if (obj != null && obj instanceof List<?>) {
226           List<?> results = (List<?>) obj;
227           if (results != null && !results.isEmpty()) {
228             fail("Unexpected action results: " +  results + " for user '"
229                 + user.getShortName() + "'");
230           }
231         } else {
232           fail("Unexpected results for user '" + user.getShortName() + "'");
233         }
234       } catch (AccessDeniedException ade) {
235         fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
236       }
237     }
238   }
239 
240   /** This passes only in case of null for all users. */
241   public static void verifyIfNull(AccessTestAction  action, User... users) throws Exception {
242     for (User user : users) {
243       try {
244         Object obj = user.runAs(action);
245         if (obj != null) {
246           fail("Non null results from action for user '" + user.getShortName() + "' : " + obj);
247         }
248       } catch (AccessDeniedException ade) {
249         fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
250       }
251     }
252   }
253 
254   /** This passes only in case of ADE for all actions. */
255   public static void verifyDenied(User user, AccessTestAction... actions) throws Exception {
256     for (AccessTestAction action : actions) {
257       try {
258         user.runAs(action);
259         fail("Expected exception was not thrown for user '" + user.getShortName() + "'");
260       } catch (IOException e) {
261         boolean isAccessDeniedException = false;
262         if(e instanceof RetriesExhaustedWithDetailsException) {
263           // in case of batch operations, and put, the client assembles a
264           // RetriesExhaustedWithDetailsException instead of throwing an
265           // AccessDeniedException
266           for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) {
267             if (ex instanceof AccessDeniedException) {
268               isAccessDeniedException = true;
269               break;
270             }
271           }
272         }
273         else {
274           // For doBulkLoad calls AccessDeniedException
275           // is buried in the stack trace
276           Throwable ex = e;
277           do {
278             if (ex instanceof AccessDeniedException) {
279               isAccessDeniedException = true;
280               break;
281             }
282           } while((ex = ex.getCause()) != null);
283         }
284         if (!isAccessDeniedException) {
285           fail("Expected exception was not thrown for user '" + user.getShortName() + "'");
286         }
287       } catch (UndeclaredThrowableException ute) {
288         // TODO why we get a PrivilegedActionException, which is unexpected?
289         Throwable ex = ute.getUndeclaredThrowable();
290         if (ex instanceof PrivilegedActionException) {
291           ex = ((PrivilegedActionException) ex).getException();
292         }
293         if (ex instanceof ServiceException) {
294           ServiceException se = (ServiceException)ex;
295           if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) {
296             // expected result
297             return;
298           }
299         }
300         fail("Expected exception was not thrown for user '" + user.getShortName() + "'");
301       }
302     }
303   }
304 
305   private static List<AccessController> getAccessControllers(MiniHBaseCluster cluster) {
306     List<AccessController> result = Lists.newArrayList();
307     for (RegionServerThread t: cluster.getLiveRegionServerThreads()) {
308       for (Region region: t.getRegionServer().getOnlineRegionsLocalContext()) {
309         Coprocessor cp = region.getCoprocessorHost()
310           .findCoprocessor(AccessController.class.getName());
311         if (cp != null) {
312           result.add((AccessController)cp);
313         }
314       }
315     }
316     return result;
317   }
318 
319   private static Map<AccessController,Long> getAuthManagerMTimes(MiniHBaseCluster cluster) {
320     Map<AccessController,Long> result = Maps.newHashMap();
321     for (AccessController ac: getAccessControllers(cluster)) {
322       result.put(ac, ac.getAuthManager().getMTime());
323     }
324     return result;
325   }
326 
327   @SuppressWarnings("rawtypes")
328   private static void updateACLs(final HBaseTestingUtility util, Callable c) throws Exception {
329     // Get the current mtimes for all access controllers
330     final Map<AccessController,Long> oldMTimes = getAuthManagerMTimes(util.getHBaseCluster());
331 
332     // Run the update action
333     c.call();
334 
335     // Wait until mtimes for all access controllers have incremented
336     util.waitFor(WAIT_TIME, 100, new Predicate<IOException>() {
337       @Override
338       public boolean evaluate() throws IOException {
339         Map<AccessController,Long> mtimes = getAuthManagerMTimes(util.getHBaseCluster());
340         for (Map.Entry<AccessController,Long> e: mtimes.entrySet()) {
341           if (!oldMTimes.containsKey(e.getKey())) {
342             LOG.error("Snapshot of AccessController state does not include instance on region " +
343               e.getKey().getRegion().getRegionInfo().getRegionNameAsString());
344             // Error out the predicate, we will try again
345             return false;
346           }
347           long old = oldMTimes.get(e.getKey());
348           long now = e.getValue();
349           if (now <= old) {
350             LOG.info("AccessController on region " +
351               e.getKey().getRegion().getRegionInfo().getRegionNameAsString() +
352               " has not updated: mtime=" + now);
353             return false;
354           }
355         }
356         return true;
357       }
358     });
359   }
360 
361   /**
362    * Grant permissions globally to the given user. Will wait until all active
363    * AccessController instances have updated their permissions caches or will
364    * throw an exception upon timeout (10 seconds).
365    */
366   public static void grantGlobal(final HBaseTestingUtility util, final String user,
367       final Permission.Action... actions) throws Exception {
368     SecureTestUtil.updateACLs(util, new Callable<Void>() {
369       @Override
370       public Void call() throws Exception {
371         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
372           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
373             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
374             AccessControlService.BlockingInterface protocol =
375                 AccessControlService.newBlockingStub(service);
376             ProtobufUtil.grant(null, protocol, user, actions);
377           }
378         }
379         return null;
380       }
381     });
382   }
383 
384   /**
385    * Revoke permissions globally from the given user. Will wait until all active
386    * AccessController instances have updated their permissions caches or will
387    * throw an exception upon timeout (10 seconds).
388    */
389   public static void revokeGlobal(final HBaseTestingUtility util, final String user,
390       final Permission.Action... actions) throws Exception {
391     SecureTestUtil.updateACLs(util, new Callable<Void>() {
392       @Override
393       public Void call() throws Exception {
394         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
395           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
396             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
397             AccessControlService.BlockingInterface protocol =
398                 AccessControlService.newBlockingStub(service);
399             ProtobufUtil.revoke(null, protocol, user, actions);
400           }
401         }
402         return null;
403       }
404     });
405   }
406 
407   /**
408    * Grant permissions on a namespace to the given user. Will wait until all active
409    * AccessController instances have updated their permissions caches or will
410    * throw an exception upon timeout (10 seconds).
411    */
412   public static void grantOnNamespace(final HBaseTestingUtility util, final String user,
413       final String namespace, final Permission.Action... actions) throws Exception {
414     SecureTestUtil.updateACLs(util, new Callable<Void>() {
415       @Override
416       public Void call() throws Exception {
417         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
418           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
419             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
420             AccessControlService.BlockingInterface protocol =
421                 AccessControlService.newBlockingStub(service);
422             ProtobufUtil.grant(null, protocol, user, namespace, actions);
423           }
424         }
425         return null;
426       }
427     });
428   }
429 
430   /**
431    * Grant permissions on a namespace to the given user using AccessControl Client.
432    * Will wait until all active AccessController instances have updated their permissions caches
433    * or will throw an exception upon timeout (10 seconds).
434    */
435   public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtility util,
436       final Connection connection, final String user, final String namespace,
437       final Permission.Action... actions) throws Exception {
438     SecureTestUtil.updateACLs(util, new Callable<Void>() {
439       @Override
440       public Void call() throws Exception {
441         try {
442           AccessControlClient.grant(connection, namespace, user, actions);
443         } catch (Throwable t) {
444           LOG.error("grant failed: ", t);
445         }
446         return null;
447       }
448     });
449   }
450 
451   /**
452    * Revoke permissions on a namespace from the given user using AccessControl Client.
453    * Will wait until all active AccessController instances have updated their permissions caches
454    * or will throw an exception upon timeout (10 seconds).
455    */
456   public static void revokeFromNamespaceUsingAccessControlClient(final HBaseTestingUtility util,
457       final Connection connection, final String user, final String namespace,
458       final Permission.Action... actions) throws Exception {
459     SecureTestUtil.updateACLs(util, new Callable<Void>() {
460       @Override
461       public Void call() throws Exception {
462         try {
463           AccessControlClient.revoke(connection, namespace, user, actions);
464         } catch (Throwable t) {
465           LOG.error("revoke failed: ", t);
466         }
467         return null;
468       }
469     });
470   }
471 
472   /**
473    * Revoke permissions on a namespace from the given user. Will wait until all active
474    * AccessController instances have updated their permissions caches or will
475    * throw an exception upon timeout (10 seconds).
476    */
477   public static void revokeFromNamespace(final HBaseTestingUtility util, final String user,
478       final String namespace, final Permission.Action... actions) throws Exception {
479     SecureTestUtil.updateACLs(util, new Callable<Void>() {
480       @Override
481       public Void call() throws Exception {
482         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
483           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
484             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
485             AccessControlService.BlockingInterface protocol =
486                 AccessControlService.newBlockingStub(service);
487             ProtobufUtil.revoke(null, protocol, user, namespace, actions);
488           }
489         }
490         return null;
491       }
492     });
493   }
494 
495   /**
496    * Grant permissions on a table to the given user. Will wait until all active
497    * AccessController instances have updated their permissions caches or will
498    * throw an exception upon timeout (10 seconds).
499    */
500   public static void grantOnTable(final HBaseTestingUtility util, final String user,
501       final TableName table, final byte[] family, final byte[] qualifier,
502       final Permission.Action... actions) throws Exception {
503     SecureTestUtil.updateACLs(util, new Callable<Void>() {
504       @Override
505       public Void call() throws Exception {
506         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
507           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
508             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
509             AccessControlService.BlockingInterface protocol =
510                 AccessControlService.newBlockingStub(service);
511             ProtobufUtil.grant(null, protocol, user, table, family, qualifier, actions);
512           }
513         }
514         return null;
515       }
516     });
517   }
518 
519   /**
520    * Grant permissions on a table to the given user using AccessControlClient. Will wait until all
521    * active AccessController instances have updated their permissions caches or will
522    * throw an exception upon timeout (10 seconds).
523    */
524   public static void grantOnTableUsingAccessControlClient(final HBaseTestingUtility util,
525       final Connection connection, final String user, final TableName table, final byte[] family,
526       final byte[] qualifier, final Permission.Action... actions) throws Exception {
527     SecureTestUtil.updateACLs(util, new Callable<Void>() {
528       @Override
529       public Void call() throws Exception {
530         try {
531           AccessControlClient.grant(connection, table, user, family, qualifier, actions);
532         } catch (Throwable t) {
533           LOG.error("grant failed: ", t);
534         }
535         return null;
536       }
537     });
538   }
539 
540   /**
541    * Grant global permissions to the given user using AccessControlClient. Will wait until all
542    * active AccessController instances have updated their permissions caches or will
543    * throw an exception upon timeout (10 seconds).
544    */
545   public static void grantGlobalUsingAccessControlClient(final HBaseTestingUtility util,
546       final Connection connection, final String user, final Permission.Action... actions)
547       throws Exception {
548     SecureTestUtil.updateACLs(util, new Callable<Void>() {
549       @Override
550       public Void call() throws Exception {
551         try {
552           AccessControlClient.grant(connection, user, actions);
553         } catch (Throwable t) {
554           LOG.error("grant failed: ", t);
555         }
556         return null;
557       }
558     });
559   }
560 
561   /**
562    * Revoke permissions on a table from the given user. Will wait until all active
563    * AccessController instances have updated their permissions caches or will
564    * throw an exception upon timeout (10 seconds).
565    */
566   public static void revokeFromTable(final HBaseTestingUtility util, final String user,
567       final TableName table, final byte[] family, final byte[] qualifier,
568       final Permission.Action... actions) throws Exception {
569     SecureTestUtil.updateACLs(util, new Callable<Void>() {
570       @Override
571       public Void call() throws Exception {
572         try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
573           try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
574             BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
575             AccessControlService.BlockingInterface protocol =
576                 AccessControlService.newBlockingStub(service);
577             ProtobufUtil.revoke(null, protocol, user, table, family, qualifier, actions);
578           }
579         }
580         return null;
581       }
582     });
583   }
584 
585   /**
586    * Revoke permissions on a table from the given user using AccessControlClient. Will wait until
587    * all active AccessController instances have updated their permissions caches or will
588    * throw an exception upon timeout (10 seconds).
589    */
590   public static void revokeFromTableUsingAccessControlClient(final HBaseTestingUtility util,
591       final Connection connection, final String user, final TableName table, final byte[] family,
592       final byte[] qualifier, final Permission.Action... actions) throws Exception {
593     SecureTestUtil.updateACLs(util, new Callable<Void>() {
594       @Override
595       public Void call() throws Exception {
596         try {
597           AccessControlClient.revoke(connection, table, user, family, qualifier, actions);
598         } catch (Throwable t) {
599           LOG.error("revoke failed: ", t);
600         }
601         return null;
602       }
603     });
604   }
605 
606   /**
607    * Revoke global permissions from the given user using AccessControlClient. Will wait until
608    * all active AccessController instances have updated their permissions caches or will
609    * throw an exception upon timeout (10 seconds).
610    */
611   public static void revokeGlobalUsingAccessControlClient(final HBaseTestingUtility util,
612       final Connection connection, final String user,final Permission.Action... actions)
613       throws Exception {
614     SecureTestUtil.updateACLs(util, new Callable<Void>() {
615       @Override
616       public Void call() throws Exception {
617         try {
618           AccessControlClient.revoke(connection, user, actions);
619         } catch (Throwable t) {
620           LOG.error("revoke failed: ", t);
621         }
622         return null;
623       }
624     });
625   }
626 
627   public static class MasterSyncObserver extends BaseMasterObserver {
628     volatile CountDownLatch tableCreationLatch = null;
629     volatile CountDownLatch tableDeletionLatch = null;
630 
631     @Override
632     public void postCreateTableHandler(
633         final ObserverContext<MasterCoprocessorEnvironment> ctx,
634         HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
635       // the AccessController test, some times calls only and directly the postCreateTableHandler()
636       if (tableCreationLatch != null) {
637         tableCreationLatch.countDown();
638       }
639     }
640 
641     @Override
642     public void postDeleteTableHandler(
643         final ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName)
644         throws IOException {
645       // the AccessController test, some times calls only and directly the postDeleteTableHandler()
646       if (tableDeletionLatch != null) {
647         tableDeletionLatch.countDown();
648       }
649     }
650   }
651 
652   public static Table createTable(HBaseTestingUtility testUtil, TableName tableName,
653       byte[][] families) throws Exception {
654     HTableDescriptor htd = new HTableDescriptor(tableName);
655     for (byte[] family : families) {
656       HColumnDescriptor hcd = new HColumnDescriptor(family);
657       htd.addFamily(hcd);
658     }
659     createTable(testUtil, testUtil.getHBaseAdmin(), htd);
660     return testUtil.getConnection().getTable(htd.getTableName());
661   }
662 
663   public static void createTable(HBaseTestingUtility testUtil, HTableDescriptor htd)
664       throws Exception {
665     createTable(testUtil, testUtil.getHBaseAdmin(), htd);
666   }
667 
668   public static void createTable(HBaseTestingUtility testUtil, HTableDescriptor htd,
669       byte[][] splitKeys) throws Exception {
670     createTable(testUtil, testUtil.getHBaseAdmin(), htd, splitKeys);
671   }
672 
673   public static void createTable(HBaseTestingUtility testUtil, Admin admin, HTableDescriptor htd)
674       throws Exception {
675     createTable(testUtil, admin, htd, null);
676   }
677 
678   public static void createTable(HBaseTestingUtility testUtil, Admin admin, HTableDescriptor htd,
679       byte[][] splitKeys) throws Exception {
680     // NOTE: We need a latch because admin is not sync,
681     // so the postOp coprocessor method may be called after the admin operation returned.
682     MasterSyncObserver observer = (MasterSyncObserver)testUtil.getHBaseCluster().getMaster()
683       .getMasterCoprocessorHost().findCoprocessor(MasterSyncObserver.class.getName());
684     observer.tableCreationLatch = new CountDownLatch(1);
685     if (splitKeys != null) {
686       admin.createTable(htd, splitKeys);
687     } else {
688       admin.createTable(htd);
689     }
690     observer.tableCreationLatch.await();
691     observer.tableCreationLatch = null;
692     testUtil.waitUntilAllRegionsAssigned(htd.getTableName());
693   }
694 
695   public static void deleteTable(HBaseTestingUtility testUtil, TableName tableName)
696       throws Exception {
697     deleteTable(testUtil, testUtil.getHBaseAdmin(), tableName);
698   }
699 
700   public static void createNamespace(HBaseTestingUtility testUtil, NamespaceDescriptor nsDesc)
701       throws Exception {
702     testUtil.getHBaseAdmin().createNamespace(nsDesc);
703   }
704 
705   public static void deleteNamespace(HBaseTestingUtility testUtil, String namespace)
706       throws Exception {
707     testUtil.getHBaseAdmin().deleteNamespace(namespace);
708   }
709 
710   public static void deleteTable(HBaseTestingUtility testUtil, Admin admin, TableName tableName)
711       throws Exception {
712     // NOTE: We need a latch because admin is not sync,
713     // so the postOp coprocessor method may be called after the admin operation returned.
714     MasterSyncObserver observer = null;
715     HMaster master = testUtil.getHBaseCluster().getMaster();
716     if (master != null) {
717       observer = (MasterSyncObserver)master.getMasterCoprocessorHost()
718           .findCoprocessor(MasterSyncObserver.class.getName());
719       if (observer != null) {
720         observer.tableDeletionLatch = new CountDownLatch(1);
721       }
722     }
723     try {
724       admin.disableTable(tableName);
725     } catch (TableNotEnabledException e) {
726       LOG.debug("Table: " + tableName + " already disabled, so just deleting it.");
727     }
728     admin.deleteTable(tableName);
729     if (observer != null) {
730       observer.tableDeletionLatch.await();
731       observer.tableDeletionLatch = null;
732     }
733   }
734 
735   public static String convertToNamespace(String namespace) {
736     return AccessControlLists.NAMESPACE_PREFIX + namespace;
737   }
738 
739   public static void checkGlobalPerms(HBaseTestingUtility testUtil, Permission.Action... actions)
740       throws IOException {
741     Permission[] perms = new Permission[actions.length];
742     for (int i = 0; i < actions.length; i++) {
743       perms[i] = new Permission(actions[i]);
744     }
745     CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
746     for (Action a : actions) {
747       request.addPermission(AccessControlProtos.Permission.newBuilder()
748           .setType(AccessControlProtos.Permission.Type.Global)
749           .setGlobalPermission(
750               AccessControlProtos.GlobalPermission.newBuilder()
751                   .addAction(ProtobufUtil.toPermissionAction(a)).build()));
752     }
753     try(Connection conn = ConnectionFactory.createConnection(testUtil.getConfiguration());
754         Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) {
755       BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
756       AccessControlService.BlockingInterface protocol =
757         AccessControlService.newBlockingStub(channel);
758       try {
759         protocol.checkPermissions(null, request.build());
760       } catch (ServiceException se) {
761         ProtobufUtil.toIOException(se);
762       }
763     }
764   }
765 
766   public static void checkTablePerms(HBaseTestingUtility testUtil, TableName table, byte[] family,
767       byte[] column, Permission.Action... actions) throws IOException {
768     Permission[] perms = new Permission[actions.length];
769     for (int i = 0; i < actions.length; i++) {
770       perms[i] = new TablePermission(table, family, column, actions[i]);
771     }
772     checkTablePerms(testUtil, table, perms);
773   }
774 
775   public static void checkTablePerms(HBaseTestingUtility testUtil, TableName table,
776       Permission... perms) throws IOException {
777     CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
778     for (Permission p : perms) {
779       request.addPermission(ProtobufUtil.toPermission(p));
780     }
781 
782     try(Connection conn = ConnectionFactory.createConnection(testUtil.getConfiguration());
783         Table acl = conn.getTable(table)) {
784       AccessControlService.BlockingInterface protocol =
785         AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
786       try {
787         protocol.checkPermissions(null, request.build());
788       } catch (ServiceException se) {
789         ProtobufUtil.toIOException(se);
790       }
791     }
792   }
793 
794 }