001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase.security.access; 020 021import java.io.IOException; 022import java.lang.reflect.UndeclaredThrowableException; 023import java.security.PrivilegedActionException; 024import java.security.PrivilegedExceptionAction; 025import java.util.List; 026import java.util.Map; 027import java.util.Optional; 028import java.util.concurrent.Callable; 029import java.util.concurrent.CountDownLatch; 030 031import com.google.protobuf.BlockingRpcChannel; 032import com.google.protobuf.ServiceException; 033 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.Coprocessor; 036import org.apache.hadoop.hbase.HBaseTestingUtility; 037import org.apache.hadoop.hbase.HConstants; 038import org.apache.hadoop.hbase.MiniHBaseCluster; 039import org.apache.hadoop.hbase.NamespaceDescriptor; 040import org.apache.hadoop.hbase.TableName; 041import org.apache.hadoop.hbase.TableNotEnabledException; 042import org.apache.hadoop.hbase.Waiter.Predicate; 043import org.apache.hadoop.hbase.client.Admin; 044import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 045import org.apache.hadoop.hbase.client.Connection; 046import org.apache.hadoop.hbase.client.ConnectionFactory; 047import org.apache.hadoop.hbase.client.RegionInfo; 048import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; 049import org.apache.hadoop.hbase.client.Table; 050import org.apache.hadoop.hbase.client.TableDescriptor; 051import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 052import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 053import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 054import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 055import org.apache.hadoop.hbase.coprocessor.MasterObserver; 056import org.apache.hadoop.hbase.coprocessor.ObserverContext; 057import org.apache.hadoop.hbase.io.hfile.HFile; 058import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 059import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; 060import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService; 061import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest; 062import org.apache.hadoop.hbase.regionserver.HRegion; 063import org.apache.hadoop.hbase.security.AccessDeniedException; 064import org.apache.hadoop.hbase.security.User; 065import org.apache.hadoop.hbase.security.access.Permission.Action; 066import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 067import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 068import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 069import org.slf4j.Logger; 070import org.slf4j.LoggerFactory; 071 072import static org.junit.Assert.assertEquals; 073import static org.junit.Assert.fail; 074 075/** 076 * Utility methods for testing security 077 */ 078public class SecureTestUtil { 079 080 private static final Logger LOG = LoggerFactory.getLogger(SecureTestUtil.class); 081 private static final int WAIT_TIME = 10000; 082 083 public static void configureSuperuser(Configuration conf) throws IOException { 084 // The secure minicluster creates separate service principals based on the 085 // current user's name, one for each slave. We need to add all of these to 086 // the superuser list or security won't function properly. We expect the 087 // HBase service account(s) to have superuser privilege. 088 String currentUser = User.getCurrent().getName(); 089 StringBuilder sb = new StringBuilder(); 090 sb.append("admin,"); 091 sb.append(currentUser); 092 // Assumes we won't ever have a minicluster with more than 5 slaves 093 for (int i = 0; i < 5; i++) { 094 sb.append(','); 095 sb.append(currentUser); sb.append(".hfs."); sb.append(i); 096 } 097 conf.set("hbase.superuser", sb.toString()); 098 } 099 100 public static void enableSecurity(Configuration conf) throws IOException { 101 conf.set("hadoop.security.authorization", "false"); 102 conf.set("hadoop.security.authentication", "simple"); 103 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName() + 104 "," + MasterSyncObserver.class.getName()); 105 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.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.set(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 2.0.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(AccessControlUtil.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 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 AccessDeniedException) { 286 isAccessDeniedException = true; 287 break; 288 } 289 } while((ex = ex.getCause()) != null); 290 } 291 if (!isAccessDeniedException) { 292 fail("Expected exception was not thrown for user '" + user.getShortName() + "'"); 293 } 294 } catch (UndeclaredThrowableException ute) { 295 // TODO why we get a PrivilegedActionException, which is unexpected? 296 Throwable ex = ute.getUndeclaredThrowable(); 297 if (ex instanceof PrivilegedActionException) { 298 ex = ((PrivilegedActionException) ex).getException(); 299 } 300 if (ex instanceof ServiceException) { 301 ServiceException se = (ServiceException)ex; 302 if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) { 303 // expected result 304 return; 305 } 306 } 307 fail("Expected exception was not thrown for user '" + user.getShortName() + "'"); 308 } 309 } 310 } 311 312 private static List<AccessController> getAccessControllers(MiniHBaseCluster cluster) { 313 List<AccessController> result = Lists.newArrayList(); 314 for (RegionServerThread t: cluster.getLiveRegionServerThreads()) { 315 for (HRegion region: t.getRegionServer().getOnlineRegionsLocalContext()) { 316 Coprocessor cp = region.getCoprocessorHost().findCoprocessor(AccessController.class); 317 if (cp != null) { 318 result.add((AccessController)cp); 319 } 320 } 321 } 322 return result; 323 } 324 325 private static Map<AccessController,Long> getAuthManagerMTimes(MiniHBaseCluster cluster) { 326 Map<AccessController,Long> result = Maps.newHashMap(); 327 for (AccessController ac: getAccessControllers(cluster)) { 328 result.put(ac, ac.getAuthManager().getMTime()); 329 } 330 return result; 331 } 332 333 @SuppressWarnings("rawtypes") 334 private static void updateACLs(final HBaseTestingUtility util, Callable c) throws Exception { 335 // Get the current mtimes for all access controllers 336 final Map<AccessController,Long> oldMTimes = getAuthManagerMTimes(util.getHBaseCluster()); 337 338 // Run the update action 339 c.call(); 340 341 // Wait until mtimes for all access controllers have incremented 342 util.waitFor(WAIT_TIME, 100, new Predicate<IOException>() { 343 @Override 344 public boolean evaluate() throws IOException { 345 Map<AccessController,Long> mtimes = getAuthManagerMTimes(util.getHBaseCluster()); 346 for (Map.Entry<AccessController,Long> e: mtimes.entrySet()) { 347 if (!oldMTimes.containsKey(e.getKey())) { 348 LOG.error("Snapshot of AccessController state does not include instance on region " + 349 e.getKey().getRegion().getRegionInfo().getRegionNameAsString()); 350 // Error out the predicate, we will try again 351 return false; 352 } 353 long old = oldMTimes.get(e.getKey()); 354 long now = e.getValue(); 355 if (now <= old) { 356 LOG.info("AccessController on region " + 357 e.getKey().getRegion().getRegionInfo().getRegionNameAsString() + 358 " has not updated: mtime=" + now); 359 return false; 360 } 361 } 362 return true; 363 } 364 }); 365 } 366 367 /** 368 * Grant permissions globally to the given user. Will wait until all active 369 * AccessController instances have updated their permissions caches or will 370 * throw an exception upon timeout (10 seconds). 371 */ 372 public static void grantGlobal(final HBaseTestingUtility util, final String user, 373 final Permission.Action... actions) throws Exception { 374 SecureTestUtil.updateACLs(util, new Callable<Void>() { 375 @Override 376 public Void call() throws Exception { 377 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 378 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 379 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 380 AccessControlService.BlockingInterface protocol = 381 AccessControlService.newBlockingStub(service); 382 AccessControlUtil.grant(null, protocol, user, false, actions); 383 } 384 } 385 return null; 386 } 387 }); 388 } 389 390 /** 391 * Revoke permissions globally from the given user. Will wait until all active 392 * AccessController instances have updated their permissions caches or will 393 * throw an exception upon timeout (10 seconds). 394 */ 395 public static void revokeGlobal(final HBaseTestingUtility util, final String user, 396 final Permission.Action... actions) throws Exception { 397 SecureTestUtil.updateACLs(util, new Callable<Void>() { 398 @Override 399 public Void call() throws Exception { 400 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 401 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 402 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 403 AccessControlService.BlockingInterface protocol = 404 AccessControlService.newBlockingStub(service); 405 AccessControlUtil.revoke(null, protocol, user, actions); 406 } 407 } 408 return null; 409 } 410 }); 411 } 412 413 /** 414 * Grant permissions on a namespace to the given user. Will wait until all active 415 * AccessController instances have updated their permissions caches or will 416 * throw an exception upon timeout (10 seconds). 417 */ 418 public static void grantOnNamespace(final HBaseTestingUtility util, final String user, 419 final String namespace, final Permission.Action... actions) throws Exception { 420 SecureTestUtil.updateACLs(util, new Callable<Void>() { 421 @Override 422 public Void call() throws Exception { 423 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 424 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 425 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 426 AccessControlService.BlockingInterface protocol = 427 AccessControlService.newBlockingStub(service); 428 AccessControlUtil.grant(null, protocol, user, namespace, false, actions); 429 } 430 } 431 return null; 432 } 433 }); 434 } 435 436 /** 437 * Grant permissions on a namespace to the given user using AccessControl Client. 438 * Will wait until all active AccessController instances have updated their permissions caches 439 * or will throw an exception upon timeout (10 seconds). 440 */ 441 public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtility util, 442 final Connection connection, final String user, final String namespace, 443 final Permission.Action... actions) throws Exception { 444 SecureTestUtil.updateACLs(util, new Callable<Void>() { 445 @Override 446 public Void call() throws Exception { 447 try { 448 AccessControlClient.grant(connection, namespace, user, actions); 449 } catch (Throwable t) { 450 LOG.error("grant failed: ", t); 451 } 452 return null; 453 } 454 }); 455 } 456 457 /** 458 * Revoke permissions on a namespace from the given user using AccessControl Client. 459 * Will wait until all active AccessController instances have updated their permissions caches 460 * or will throw an exception upon timeout (10 seconds). 461 */ 462 public static void revokeFromNamespaceUsingAccessControlClient(final HBaseTestingUtility util, 463 final Connection connection, final String user, final String namespace, 464 final Permission.Action... actions) throws Exception { 465 SecureTestUtil.updateACLs(util, new Callable<Void>() { 466 @Override 467 public Void call() throws Exception { 468 try { 469 AccessControlClient.revoke(connection, namespace, user, actions); 470 } catch (Throwable t) { 471 LOG.error("revoke failed: ", t); 472 } 473 return null; 474 } 475 }); 476 } 477 478 /** 479 * Revoke permissions on a namespace from the given user. Will wait until all active 480 * AccessController instances have updated their permissions caches or will 481 * throw an exception upon timeout (10 seconds). 482 */ 483 public static void revokeFromNamespace(final HBaseTestingUtility util, final String user, 484 final String namespace, final Permission.Action... actions) throws Exception { 485 SecureTestUtil.updateACLs(util, new Callable<Void>() { 486 @Override 487 public Void call() throws Exception { 488 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 489 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 490 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 491 AccessControlService.BlockingInterface protocol = 492 AccessControlService.newBlockingStub(service); 493 AccessControlUtil.revoke(null, protocol, user, namespace, actions); 494 } 495 } 496 return null; 497 } 498 }); 499 } 500 501 /** 502 * Grant permissions on a table to the given user. Will wait until all active 503 * AccessController instances have updated their permissions caches or will 504 * throw an exception upon timeout (10 seconds). 505 */ 506 public static void grantOnTable(final HBaseTestingUtility util, final String user, 507 final TableName table, final byte[] family, final byte[] qualifier, 508 final Permission.Action... actions) throws Exception { 509 SecureTestUtil.updateACLs(util, new Callable<Void>() { 510 @Override 511 public Void call() throws Exception { 512 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 513 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 514 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 515 AccessControlService.BlockingInterface protocol = 516 AccessControlService.newBlockingStub(service); 517 AccessControlUtil.grant(null, protocol, user, table, family, qualifier, false, actions); 518 } 519 } 520 return null; 521 } 522 }); 523 } 524 525 /** 526 * Grant permissions on a table to the given user using AccessControlClient. Will wait until all 527 * active AccessController instances have updated their permissions caches or will 528 * throw an exception upon timeout (10 seconds). 529 */ 530 public static void grantOnTableUsingAccessControlClient(final HBaseTestingUtility util, 531 final Connection connection, final String user, final TableName table, final byte[] family, 532 final byte[] qualifier, final Permission.Action... actions) throws Exception { 533 SecureTestUtil.updateACLs(util, new Callable<Void>() { 534 @Override 535 public Void call() throws Exception { 536 try { 537 AccessControlClient.grant(connection, table, user, family, qualifier, actions); 538 } catch (Throwable t) { 539 LOG.error("grant failed: ", t); 540 } 541 return null; 542 } 543 }); 544 } 545 546 /** 547 * Grant global permissions to the given user using AccessControlClient. Will wait until all 548 * active AccessController instances have updated their permissions caches or will 549 * throw an exception upon timeout (10 seconds). 550 */ 551 public static void grantGlobalUsingAccessControlClient(final HBaseTestingUtility util, 552 final Connection connection, final String user, final Permission.Action... actions) 553 throws Exception { 554 SecureTestUtil.updateACLs(util, new Callable<Void>() { 555 @Override 556 public Void call() throws Exception { 557 try { 558 AccessControlClient.grant(connection, user, actions); 559 } catch (Throwable t) { 560 LOG.error("grant failed: ", t); 561 } 562 return null; 563 } 564 }); 565 } 566 567 /** 568 * Revoke permissions on a table from the given user. Will wait until all active 569 * AccessController instances have updated their permissions caches or will 570 * throw an exception upon timeout (10 seconds). 571 */ 572 public static void revokeFromTable(final HBaseTestingUtility util, final String user, 573 final TableName table, final byte[] family, final byte[] qualifier, 574 final Permission.Action... actions) throws Exception { 575 SecureTestUtil.updateACLs(util, new Callable<Void>() { 576 @Override 577 public Void call() throws Exception { 578 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 579 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) { 580 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); 581 AccessControlService.BlockingInterface protocol = 582 AccessControlService.newBlockingStub(service); 583 AccessControlUtil.revoke(null, protocol, user, table, family, qualifier, actions); 584 } 585 } 586 return null; 587 } 588 }); 589 } 590 591 /** 592 * Revoke permissions on a table from the given user using AccessControlClient. Will wait until 593 * all active AccessController instances have updated their permissions caches or will 594 * throw an exception upon timeout (10 seconds). 595 */ 596 public static void revokeFromTableUsingAccessControlClient(final HBaseTestingUtility util, 597 final Connection connection, final String user, final TableName table, final byte[] family, 598 final byte[] qualifier, final Permission.Action... actions) throws Exception { 599 SecureTestUtil.updateACLs(util, new Callable<Void>() { 600 @Override 601 public Void call() throws Exception { 602 try { 603 AccessControlClient.revoke(connection, table, user, family, qualifier, actions); 604 } catch (Throwable t) { 605 LOG.error("revoke failed: ", t); 606 } 607 return null; 608 } 609 }); 610 } 611 612 /** 613 * Revoke global permissions from the given user using AccessControlClient. Will wait until 614 * all active AccessController instances have updated their permissions caches or will 615 * throw an exception upon timeout (10 seconds). 616 */ 617 public static void revokeGlobalUsingAccessControlClient(final HBaseTestingUtility util, 618 final Connection connection, final String user,final Permission.Action... actions) 619 throws Exception { 620 SecureTestUtil.updateACLs(util, new Callable<Void>() { 621 @Override 622 public Void call() throws Exception { 623 try { 624 AccessControlClient.revoke(connection, user, actions); 625 } catch (Throwable t) { 626 LOG.error("revoke failed: ", t); 627 } 628 return null; 629 } 630 }); 631 } 632 633 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 634 volatile CountDownLatch tableCreationLatch = null; 635 volatile CountDownLatch tableDeletionLatch = null; 636 637 @Override 638 public Optional<MasterObserver> getMasterObserver() { 639 return Optional.of(this); 640 } 641 642 @Override 643 public void postCompletedCreateTableAction( 644 final ObserverContext<MasterCoprocessorEnvironment> ctx, 645 TableDescriptor desc, RegionInfo[] regions) throws IOException { 646 // the AccessController test, some times calls only and directly the 647 // postCompletedCreateTableAction() 648 if (tableCreationLatch != null) { 649 tableCreationLatch.countDown(); 650 } 651 } 652 653 @Override 654 public void postCompletedDeleteTableAction( 655 final ObserverContext<MasterCoprocessorEnvironment> ctx, 656 final TableName tableName) throws IOException { 657 // the AccessController test, some times calls only and directly the 658 // postCompletedDeleteTableAction() 659 if (tableDeletionLatch != null) { 660 tableDeletionLatch.countDown(); 661 } 662 } 663 } 664 665 public static Table createTable(HBaseTestingUtility testUtil, TableName tableName, 666 byte[][] families) throws Exception { 667 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 668 for (byte[] family : families) { 669 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 670 } 671 createTable(testUtil, testUtil.getAdmin(), builder.build()); 672 return testUtil.getConnection().getTable(tableName); 673 } 674 675 public static void createTable(HBaseTestingUtility testUtil, TableDescriptor htd) 676 throws Exception { 677 createTable(testUtil, testUtil.getAdmin(), htd); 678 } 679 680 public static void createTable(HBaseTestingUtility testUtil, TableDescriptor htd, 681 byte[][] splitKeys) throws Exception { 682 createTable(testUtil, testUtil.getAdmin(), htd, splitKeys); 683 } 684 685 public static void createTable(HBaseTestingUtility testUtil, Admin admin, TableDescriptor htd) 686 throws Exception { 687 createTable(testUtil, admin, htd, null); 688 } 689 690 public static void createTable(HBaseTestingUtility testUtil, Admin admin, TableDescriptor htd, 691 byte[][] splitKeys) throws Exception { 692 // NOTE: We need a latch because admin is not sync, 693 // so the postOp coprocessor method may be called after the admin operation returned. 694 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster() 695 .getMasterCoprocessorHost().findCoprocessor(MasterSyncObserver.class); 696 observer.tableCreationLatch = new CountDownLatch(1); 697 if (splitKeys != null) { 698 admin.createTable(htd, splitKeys); 699 } else { 700 admin.createTable(htd); 701 } 702 observer.tableCreationLatch.await(); 703 observer.tableCreationLatch = null; 704 testUtil.waitUntilAllRegionsAssigned(htd.getTableName()); 705 } 706 707 public static void deleteTable(HBaseTestingUtility testUtil, TableName tableName) 708 throws Exception { 709 deleteTable(testUtil, testUtil.getAdmin(), tableName); 710 } 711 712 public static void createNamespace(HBaseTestingUtility testUtil, NamespaceDescriptor nsDesc) 713 throws Exception { 714 testUtil.getAdmin().createNamespace(nsDesc); 715 } 716 717 public static void deleteNamespace(HBaseTestingUtility testUtil, String namespace) 718 throws Exception { 719 testUtil.getAdmin().deleteNamespace(namespace); 720 } 721 722 public static void deleteTable(HBaseTestingUtility testUtil, Admin admin, TableName tableName) 723 throws Exception { 724 // NOTE: We need a latch because admin is not sync, 725 // so the postOp coprocessor method may be called after the admin operation returned. 726 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster() 727 .getMasterCoprocessorHost().findCoprocessor(MasterSyncObserver.class); 728 observer.tableDeletionLatch = new CountDownLatch(1); 729 try { 730 admin.disableTable(tableName); 731 } catch (TableNotEnabledException e) { 732 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 733 } 734 admin.deleteTable(tableName); 735 observer.tableDeletionLatch.await(); 736 observer.tableDeletionLatch = null; 737 } 738 739 public static String convertToNamespace(String namespace) { 740 return AccessControlLists.NAMESPACE_PREFIX + namespace; 741 } 742 743 public static void checkGlobalPerms(HBaseTestingUtility testUtil, Permission.Action... actions) 744 throws IOException { 745 Permission[] perms = new Permission[actions.length]; 746 for (int i = 0; i < actions.length; i++) { 747 perms[i] = new Permission(actions[i]); 748 } 749 CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder(); 750 for (Action a : actions) { 751 request.addPermission(AccessControlProtos.Permission.newBuilder() 752 .setType(AccessControlProtos.Permission.Type.Global) 753 .setGlobalPermission( 754 AccessControlProtos.GlobalPermission.newBuilder() 755 .addAction(AccessControlUtil.toPermissionAction(a)).build())); 756 } 757 try(Connection conn = ConnectionFactory.createConnection(testUtil.getConfiguration()); 758 Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) { 759 BlockingRpcChannel channel = acl.coprocessorService(new byte[0]); 760 AccessControlService.BlockingInterface protocol = 761 AccessControlService.newBlockingStub(channel); 762 try { 763 protocol.checkPermissions(null, request.build()); 764 } catch (ServiceException se) { 765 ProtobufUtil.toIOException(se); 766 } 767 } 768 } 769 770 public static void checkTablePerms(HBaseTestingUtility testUtil, TableName table, byte[] family, 771 byte[] column, Permission.Action... actions) throws IOException { 772 Permission[] perms = new Permission[actions.length]; 773 for (int i = 0; i < actions.length; i++) { 774 perms[i] = new TablePermission(table, family, column, actions[i]); 775 } 776 checkTablePerms(testUtil, table, perms); 777 } 778 779 public static void checkTablePerms(HBaseTestingUtility testUtil, TableName table, 780 Permission... perms) throws IOException { 781 CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder(); 782 for (Permission p : perms) { 783 request.addPermission(AccessControlUtil.toPermission(p)); 784 } 785 786 try(Connection conn = ConnectionFactory.createConnection(testUtil.getConfiguration()); 787 Table acl = conn.getTable(table)) { 788 AccessControlService.BlockingInterface protocol = 789 AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0])); 790 try { 791 protocol.checkPermissions(null, request.build()); 792 } catch (ServiceException se) { 793 ProtobufUtil.toIOException(se); 794 } 795 } 796 } 797 798}