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