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