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