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