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