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 java.io.IOException; 024import java.lang.reflect.UndeclaredThrowableException; 025import java.security.PrivilegedActionException; 026import java.security.PrivilegedExceptionAction; 027import java.util.List; 028import java.util.Map; 029import java.util.Optional; 030import java.util.concurrent.Callable; 031import java.util.concurrent.CountDownLatch; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.hbase.Coprocessor; 034import org.apache.hadoop.hbase.HBaseTestingUtil; 035import org.apache.hadoop.hbase.NamespaceDescriptor; 036import org.apache.hadoop.hbase.SingleProcessHBaseCluster; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.TableNotEnabledException; 039import org.apache.hadoop.hbase.Waiter.Predicate; 040import org.apache.hadoop.hbase.client.Admin; 041import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 042import org.apache.hadoop.hbase.client.Connection; 043import org.apache.hadoop.hbase.client.ConnectionFactory; 044import org.apache.hadoop.hbase.client.RegionInfo; 045import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; 046import org.apache.hadoop.hbase.client.Table; 047import org.apache.hadoop.hbase.client.TableDescriptor; 048import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 049import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; 050import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 051import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 052import org.apache.hadoop.hbase.coprocessor.MasterObserver; 053import org.apache.hadoop.hbase.coprocessor.ObserverContext; 054import org.apache.hadoop.hbase.io.hfile.HFile; 055import org.apache.hadoop.hbase.ipc.RemoteWithExtrasException; 056import org.apache.hadoop.hbase.regionserver.HRegion; 057import org.apache.hadoop.hbase.security.AccessDeniedException; 058import org.apache.hadoop.hbase.security.User; 059import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 060import org.slf4j.Logger; 061import org.slf4j.LoggerFactory; 062 063import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 064import org.apache.hbase.thirdparty.com.google.common.collect.Maps; 065import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException; 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(SingleProcessHBaseCluster 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> 299 getAuthManagerMTimes(SingleProcessHBaseCluster cluster) { 300 Map<AccessController, Long> result = Maps.newHashMap(); 301 for (AccessController ac : getAccessControllers(cluster)) { 302 result.put(ac, ac.getAuthManager().getMTime()); 303 } 304 return result; 305 } 306 307 @SuppressWarnings("rawtypes") 308 private static void updateACLs(final HBaseTestingUtil util, Callable c) throws Exception { 309 // Get the current mtimes for all access controllers 310 final Map<AccessController, Long> oldMTimes = getAuthManagerMTimes(util.getHBaseCluster()); 311 312 // Run the update action 313 c.call(); 314 315 // Wait until mtimes for all access controllers have incremented 316 util.waitFor(WAIT_TIME, 100, new Predicate<IOException>() { 317 @Override 318 public boolean evaluate() throws IOException { 319 Map<AccessController, Long> mtimes = getAuthManagerMTimes(util.getHBaseCluster()); 320 for (Map.Entry<AccessController, Long> e : mtimes.entrySet()) { 321 if (!oldMTimes.containsKey(e.getKey())) { 322 LOG.error("Snapshot of AccessController state does not include instance on region " 323 + e.getKey().getRegion().getRegionInfo().getRegionNameAsString()); 324 // Error out the predicate, we will try again 325 return false; 326 } 327 long old = oldMTimes.get(e.getKey()); 328 long now = e.getValue(); 329 if (now <= old) { 330 LOG.info("AccessController on region " 331 + e.getKey().getRegion().getRegionInfo().getRegionNameAsString() 332 + " has not updated: mtime=" + now); 333 return false; 334 } 335 } 336 return true; 337 } 338 }); 339 } 340 341 /** 342 * Grant permissions globally to the given user. Will wait until all active AccessController 343 * instances have updated their permissions caches or will throw an exception upon timeout (10 344 * seconds). 345 */ 346 public static void grantGlobal(final HBaseTestingUtil util, final String user, 347 final Permission.Action... actions) throws Exception { 348 SecureTestUtil.updateACLs(util, new Callable<Void>() { 349 @Override 350 public Void call() throws Exception { 351 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 352 connection.getAdmin().grant( 353 new UserPermission(user, Permission.newBuilder().withActions(actions).build()), false); 354 } 355 return null; 356 } 357 }); 358 } 359 360 /** 361 * Grant permissions globally to the given user. Will wait until all active AccessController 362 * instances have updated their permissions caches or will throw an exception upon timeout (10 363 * seconds). 364 */ 365 public static void grantGlobal(final User caller, final HBaseTestingUtil 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 Configuration conf = util.getConfiguration(); 371 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 372 connection.getAdmin().grant( 373 new UserPermission(user, Permission.newBuilder().withActions(actions).build()), false); 374 } 375 return null; 376 } 377 }); 378 } 379 380 /** 381 * Revoke permissions globally from the given user. Will wait until all active AccessController 382 * instances have updated their permissions caches or will throw an exception upon timeout (10 383 * seconds). 384 */ 385 public static void revokeGlobal(final HBaseTestingUtil util, final String user, 386 final Permission.Action... actions) throws Exception { 387 SecureTestUtil.updateACLs(util, new Callable<Void>() { 388 @Override 389 public Void call() throws Exception { 390 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 391 connection.getAdmin() 392 .revoke(new UserPermission(user, Permission.newBuilder().withActions(actions).build())); 393 } 394 return null; 395 } 396 }); 397 } 398 399 /** 400 * Revoke permissions globally from the given user. Will wait until all active AccessController 401 * instances have updated their permissions caches or will throw an exception upon timeout (10 402 * seconds). 403 */ 404 public static void revokeGlobal(final User caller, final HBaseTestingUtil util, final String user, 405 final Permission.Action... actions) throws Exception { 406 SecureTestUtil.updateACLs(util, new Callable<Void>() { 407 @Override 408 public Void call() throws Exception { 409 Configuration conf = util.getConfiguration(); 410 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 411 connection.getAdmin() 412 .revoke(new UserPermission(user, Permission.newBuilder().withActions(actions).build())); 413 } 414 return null; 415 } 416 }); 417 } 418 419 /** 420 * Grant permissions on a namespace to the given user. Will wait until all active AccessController 421 * instances have updated their permissions caches or will throw an exception upon timeout (10 422 * seconds). 423 */ 424 public static void grantOnNamespace(final HBaseTestingUtil util, final String user, 425 final String namespace, final Permission.Action... actions) throws Exception { 426 SecureTestUtil.updateACLs(util, new Callable<Void>() { 427 @Override 428 public Void call() throws Exception { 429 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 430 connection.getAdmin().grant( 431 new UserPermission(user, Permission.newBuilder(namespace).withActions(actions).build()), 432 false); 433 } 434 return null; 435 } 436 }); 437 } 438 439 /** 440 * Grant permissions on a namespace to the given user. Will wait until all active AccessController 441 * instances have updated their permissions caches or will throw an exception upon timeout (10 442 * seconds). 443 */ 444 public static void grantOnNamespace(final User caller, final HBaseTestingUtil util, 445 final String user, final String namespace, final Permission.Action... actions) 446 throws Exception { 447 SecureTestUtil.updateACLs(util, new Callable<Void>() { 448 @Override 449 public Void call() throws Exception { 450 Configuration conf = util.getConfiguration(); 451 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 452 connection.getAdmin().grant( 453 new UserPermission(user, Permission.newBuilder(namespace).withActions(actions).build()), 454 false); 455 } 456 return null; 457 } 458 }); 459 } 460 461 /** 462 * Grant permissions on a namespace to the given user using AccessControl Client. Will wait until 463 * all active AccessController instances have updated their permissions caches or will throw an 464 * exception upon timeout (10 seconds). 465 */ 466 public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtil util, 467 final Connection connection, final String user, final String namespace, 468 final Permission.Action... actions) throws Exception { 469 SecureTestUtil.updateACLs(util, new Callable<Void>() { 470 @Override 471 public Void call() throws Exception { 472 try { 473 AccessControlClient.grant(connection, namespace, user, actions); 474 } catch (Throwable t) { 475 LOG.error("grant failed: ", t); 476 } 477 return null; 478 } 479 }); 480 } 481 482 /** 483 * Revoke permissions on a namespace from the given user using AccessControl Client. Will wait 484 * until all active AccessController instances have updated their permissions caches or will throw 485 * an exception upon timeout (10 seconds). 486 */ 487 public static void revokeFromNamespaceUsingAccessControlClient(final HBaseTestingUtil util, 488 final Connection connection, final String user, final String namespace, 489 final Permission.Action... actions) throws Exception { 490 SecureTestUtil.updateACLs(util, new Callable<Void>() { 491 @Override 492 public Void call() throws Exception { 493 try { 494 AccessControlClient.revoke(connection, namespace, user, actions); 495 } catch (Throwable t) { 496 LOG.error("revoke failed: ", t); 497 } 498 return null; 499 } 500 }); 501 } 502 503 /** 504 * Revoke permissions on a namespace from the given user. Will wait until all active 505 * AccessController instances have updated their permissions caches or will throw an exception 506 * upon timeout (10 seconds). 507 */ 508 public static void revokeFromNamespace(final HBaseTestingUtil util, final String user, 509 final String namespace, final Permission.Action... actions) throws Exception { 510 SecureTestUtil.updateACLs(util, new Callable<Void>() { 511 @Override 512 public Void call() throws Exception { 513 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 514 connection.getAdmin().revoke(new UserPermission(user, 515 Permission.newBuilder(namespace).withActions(actions).build())); 516 } 517 return null; 518 } 519 }); 520 } 521 522 /** 523 * Revoke permissions on a namespace from the given user. Will wait until all active 524 * AccessController instances have updated their permissions caches or will throw an exception 525 * upon timeout (10 seconds). 526 */ 527 public static void revokeFromNamespace(final User caller, final HBaseTestingUtil util, 528 final String user, final String namespace, final Permission.Action... actions) 529 throws Exception { 530 SecureTestUtil.updateACLs(util, new Callable<Void>() { 531 @Override 532 public Void call() throws Exception { 533 Configuration conf = util.getConfiguration(); 534 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 535 connection.getAdmin().revoke(new UserPermission(user, 536 Permission.newBuilder(namespace).withActions(actions).build())); 537 } 538 return null; 539 } 540 }); 541 } 542 543 /** 544 * Grant permissions on a table to the given user. Will wait until all active AccessController 545 * instances have updated their permissions caches or will throw an exception upon timeout (10 546 * seconds). 547 */ 548 public static void grantOnTable(final HBaseTestingUtil util, final String user, 549 final TableName table, final byte[] family, final byte[] qualifier, 550 final Permission.Action... actions) throws Exception { 551 SecureTestUtil.updateACLs(util, new Callable<Void>() { 552 @Override 553 public Void call() throws Exception { 554 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 555 connection.getAdmin().grant(new UserPermission(user, Permission.newBuilder(table) 556 .withFamily(family).withQualifier(qualifier).withActions(actions).build()), false); 557 } 558 return null; 559 } 560 }); 561 } 562 563 /** 564 * Grant permissions on a table to the given user. Will wait until all active AccessController 565 * instances have updated their permissions caches or will throw an exception upon timeout (10 566 * seconds). 567 */ 568 public static void grantOnTable(final User caller, final HBaseTestingUtil util, final String user, 569 final TableName table, final byte[] family, final byte[] qualifier, 570 final Permission.Action... actions) throws Exception { 571 SecureTestUtil.updateACLs(util, new Callable<Void>() { 572 @Override 573 public Void call() throws Exception { 574 Configuration conf = util.getConfiguration(); 575 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 576 connection.getAdmin().grant(new UserPermission(user, Permission.newBuilder(table) 577 .withFamily(family).withQualifier(qualifier).withActions(actions).build()), false); 578 } 579 return null; 580 } 581 }); 582 } 583 584 /** 585 * Grant permissions on a table to the given user using AccessControlClient. Will wait until all 586 * active AccessController instances have updated their permissions caches or will throw an 587 * exception upon timeout (10 seconds). 588 */ 589 public static void grantOnTableUsingAccessControlClient(final HBaseTestingUtil 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.grant(connection, table, user, family, qualifier, actions); 597 } catch (Throwable t) { 598 LOG.error("grant failed: ", t); 599 } 600 return null; 601 } 602 }); 603 } 604 605 /** 606 * Grant global permissions to the given user using AccessControlClient. Will wait until all 607 * active AccessController instances have updated their permissions caches or will throw an 608 * exception upon timeout (10 seconds). 609 */ 610 public static void grantGlobalUsingAccessControlClient(final HBaseTestingUtil 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.grant(connection, user, actions); 618 } catch (Throwable t) { 619 LOG.error("grant failed: ", t); 620 } 621 return null; 622 } 623 }); 624 } 625 626 /** 627 * Revoke permissions on a table from the given user. Will wait until all active AccessController 628 * instances have updated their permissions caches or will throw an exception upon timeout (10 629 * seconds). 630 */ 631 public static void revokeFromTable(final HBaseTestingUtil util, final String user, 632 final TableName table, final byte[] family, final byte[] qualifier, 633 final Permission.Action... actions) throws Exception { 634 SecureTestUtil.updateACLs(util, new Callable<Void>() { 635 @Override 636 public Void call() throws Exception { 637 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) { 638 connection.getAdmin().revoke(new UserPermission(user, Permission.newBuilder(table) 639 .withFamily(family).withQualifier(qualifier).withActions(actions).build())); 640 } 641 return null; 642 } 643 }); 644 } 645 646 /** 647 * Revoke permissions on a table from the given user. Will wait until all active AccessController 648 * instances have updated their permissions caches or will throw an exception upon timeout (10 649 * seconds). 650 */ 651 public static void revokeFromTable(final User caller, final HBaseTestingUtil util, 652 final String user, final TableName table, final byte[] family, final byte[] qualifier, 653 final Permission.Action... actions) throws Exception { 654 SecureTestUtil.updateACLs(util, new Callable<Void>() { 655 @Override 656 public Void call() throws Exception { 657 Configuration conf = util.getConfiguration(); 658 try (Connection connection = ConnectionFactory.createConnection(conf, caller)) { 659 connection.getAdmin().revoke(new UserPermission(user, Permission.newBuilder(table) 660 .withFamily(family).withQualifier(qualifier).withActions(actions).build())); 661 } 662 return null; 663 } 664 }); 665 } 666 667 /** 668 * Revoke permissions on a table from the given user using AccessControlClient. Will wait until 669 * all active AccessController instances have updated their permissions caches or will throw an 670 * exception upon timeout (10 seconds). 671 */ 672 public static void revokeFromTableUsingAccessControlClient(final HBaseTestingUtil util, 673 final Connection connection, final String user, final TableName table, final byte[] family, 674 final byte[] qualifier, final Permission.Action... actions) throws Exception { 675 SecureTestUtil.updateACLs(util, new Callable<Void>() { 676 @Override 677 public Void call() throws Exception { 678 try { 679 AccessControlClient.revoke(connection, table, user, family, qualifier, actions); 680 } catch (Throwable t) { 681 LOG.error("revoke failed: ", t); 682 } 683 return null; 684 } 685 }); 686 } 687 688 /** 689 * Revoke global permissions from the given user using AccessControlClient. Will wait until all 690 * active AccessController instances have updated their permissions caches or will throw an 691 * exception upon timeout (10 seconds). 692 */ 693 public static void revokeGlobalUsingAccessControlClient(final HBaseTestingUtil util, 694 final Connection connection, final String user, final Permission.Action... actions) 695 throws Exception { 696 SecureTestUtil.updateACLs(util, new Callable<Void>() { 697 @Override 698 public Void call() throws Exception { 699 try { 700 AccessControlClient.revoke(connection, user, actions); 701 } catch (Throwable t) { 702 LOG.error("revoke failed: ", t); 703 } 704 return null; 705 } 706 }); 707 } 708 709 public static class MasterSyncObserver implements MasterCoprocessor, MasterObserver { 710 volatile CountDownLatch tableCreationLatch = null; 711 volatile CountDownLatch tableDeletionLatch = null; 712 713 @Override 714 public Optional<MasterObserver> getMasterObserver() { 715 return Optional.of(this); 716 } 717 718 @Override 719 public void postCompletedCreateTableAction( 720 final ObserverContext<MasterCoprocessorEnvironment> ctx, TableDescriptor desc, 721 RegionInfo[] regions) throws IOException { 722 // the AccessController test, some times calls only and directly the 723 // postCompletedCreateTableAction() 724 if (tableCreationLatch != null) { 725 tableCreationLatch.countDown(); 726 } 727 } 728 729 @Override 730 public void postCompletedDeleteTableAction( 731 final ObserverContext<MasterCoprocessorEnvironment> ctx, final TableName tableName) 732 throws IOException { 733 // the AccessController test, some times calls only and directly the 734 // postCompletedDeleteTableAction() 735 if (tableDeletionLatch != null) { 736 tableDeletionLatch.countDown(); 737 } 738 } 739 } 740 741 public static Table createTable(HBaseTestingUtil testUtil, TableName tableName, byte[][] families) 742 throws Exception { 743 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 744 for (byte[] family : families) { 745 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 746 } 747 createTable(testUtil, testUtil.getAdmin(), builder.build()); 748 return testUtil.getConnection().getTable(tableName); 749 } 750 751 public static void createTable(HBaseTestingUtil testUtil, TableDescriptor htd) throws Exception { 752 createTable(testUtil, testUtil.getAdmin(), htd); 753 } 754 755 public static void createTable(HBaseTestingUtil testUtil, TableDescriptor htd, byte[][] splitKeys) 756 throws Exception { 757 createTable(testUtil, testUtil.getAdmin(), htd, splitKeys); 758 } 759 760 public static void createTable(HBaseTestingUtil testUtil, Admin admin, TableDescriptor htd) 761 throws Exception { 762 createTable(testUtil, admin, htd, null); 763 } 764 765 public static void createTable(HBaseTestingUtil 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 createTable(HBaseTestingUtil testUtil, User user, TableDescriptor htd) 783 throws Exception { 784 createTable(testUtil, user, htd, null); 785 } 786 787 public static void createTable(HBaseTestingUtil testUtil, User user, TableDescriptor htd, 788 byte[][] splitKeys) throws Exception { 789 try (Connection con = testUtil.getConnection(user); Admin admin = con.getAdmin()) { 790 createTable(testUtil, admin, htd, splitKeys); 791 } 792 } 793 794 public static void deleteTable(HBaseTestingUtil testUtil, TableName tableName) throws Exception { 795 deleteTable(testUtil, testUtil.getAdmin(), tableName); 796 } 797 798 public static void createNamespace(HBaseTestingUtil testUtil, NamespaceDescriptor nsDesc) 799 throws Exception { 800 testUtil.getAdmin().createNamespace(nsDesc); 801 } 802 803 public static void deleteNamespace(HBaseTestingUtil testUtil, String namespace) throws Exception { 804 testUtil.getAdmin().deleteNamespace(namespace); 805 } 806 807 public static void deleteTable(HBaseTestingUtil testUtil, Admin admin, TableName tableName) 808 throws Exception { 809 // NOTE: We need a latch because admin is not sync, 810 // so the postOp coprocessor method may be called after the admin operation returned. 811 MasterSyncObserver observer = testUtil.getHBaseCluster().getMaster().getMasterCoprocessorHost() 812 .findCoprocessor(MasterSyncObserver.class); 813 observer.tableDeletionLatch = new CountDownLatch(1); 814 try { 815 admin.disableTable(tableName); 816 } catch (TableNotEnabledException e) { 817 LOG.debug("Table: " + tableName + " already disabled, so just deleting it."); 818 } 819 admin.deleteTable(tableName); 820 observer.tableDeletionLatch.await(); 821 observer.tableDeletionLatch = null; 822 } 823 824 public static String convertToNamespace(String namespace) { 825 return PermissionStorage.NAMESPACE_PREFIX + namespace; 826 } 827 828 public static void checkGlobalPerms(HBaseTestingUtil testUtil, Permission.Action... actions) 829 throws IOException { 830 Permission[] perms = new Permission[actions.length]; 831 for (int i = 0; i < actions.length; i++) { 832 perms[i] = new Permission(actions[i]); 833 } 834 checkPermissions(testUtil.getConfiguration(), perms); 835 } 836 837 public static void checkTablePerms(HBaseTestingUtil testUtil, TableName table, byte[] family, 838 byte[] column, Permission.Action... actions) throws IOException { 839 Permission[] perms = new Permission[actions.length]; 840 for (int i = 0; i < actions.length; i++) { 841 perms[i] = Permission.newBuilder(table).withFamily(family).withQualifier(column) 842 .withActions(actions[i]).build(); 843 } 844 checkTablePerms(testUtil, perms); 845 } 846 847 public static void checkTablePerms(HBaseTestingUtil testUtil, Permission... perms) 848 throws IOException { 849 checkPermissions(testUtil.getConfiguration(), perms); 850 } 851 852 private static void checkPermissions(Configuration conf, Permission... perms) throws IOException { 853 try (Connection conn = ConnectionFactory.createConnection(conf)) { 854 List<Boolean> hasUserPermissions = 855 conn.getAdmin().hasUserPermissions(Lists.newArrayList(perms)); 856 for (int i = 0; i < hasUserPermissions.size(); i++) { 857 if (!hasUserPermissions.get(i).booleanValue()) { 858 throw new AccessDeniedException("Insufficient permissions " + perms[i]); 859 } 860 } 861 } 862 } 863}