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