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