1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security.access;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.fail;
23
24 import java.io.IOException;
25 import java.lang.reflect.UndeclaredThrowableException;
26 import java.security.PrivilegedActionException;
27 import java.security.PrivilegedExceptionAction;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.Callable;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.hbase.Coprocessor;
36 import org.apache.hadoop.hbase.HBaseTestingUtility;
37 import org.apache.hadoop.hbase.HConstants;
38 import org.apache.hadoop.hbase.MiniHBaseCluster;
39 import org.apache.hadoop.hbase.NamespaceDescriptor;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.Waiter.Predicate;
42 import org.apache.hadoop.hbase.client.Connection;
43 import org.apache.hadoop.hbase.client.ConnectionFactory;
44 import org.apache.hadoop.hbase.client.HTable;
45 import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
46 import org.apache.hadoop.hbase.client.Table;
47 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
48 import org.apache.hadoop.hbase.io.hfile.HFile;
49 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
50 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
51 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
52 import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
53 import org.apache.hadoop.hbase.regionserver.HRegion;
54 import org.apache.hadoop.hbase.security.AccessDeniedException;
55 import org.apache.hadoop.hbase.security.User;
56 import org.apache.hadoop.hbase.security.access.Permission.Action;
57 import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
58
59 import com.google.common.collect.Lists;
60 import com.google.common.collect.Maps;
61 import com.google.protobuf.BlockingRpcChannel;
62 import com.google.protobuf.ServiceException;
63
64
65
66
67 public class SecureTestUtil {
68
69 private static final Log LOG = LogFactory.getLog(SecureTestUtil.class);
70 private static final int WAIT_TIME = 10000;
71
72 public static void configureSuperuser(Configuration conf) throws IOException {
73
74
75
76
77 String currentUser = User.getCurrent().getName();
78 StringBuffer sb = new StringBuffer();
79 sb.append("admin,");
80 sb.append(currentUser);
81
82 for (int i = 0; i < 5; i++) {
83 sb.append(',');
84 sb.append(currentUser); sb.append(".hfs."); sb.append(i);
85 }
86 conf.set("hbase.superuser", sb.toString());
87 }
88
89 public static void enableSecurity(Configuration conf) throws IOException {
90 conf.set("hadoop.security.authorization", "false");
91 conf.set("hadoop.security.authentication", "simple");
92 conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
93 conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName() +
94 "," + SecureBulkLoadEndpoint.class.getName());
95 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
96
97 conf.setInt(HFile.FORMAT_VERSION_KEY, 3);
98 configureSuperuser(conf);
99 }
100
101 public static void verifyConfiguration(Configuration conf) {
102 if (!(conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY).contains(
103 AccessController.class.getName())
104 && conf.get(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY).contains(
105 AccessController.class.getName()) && conf.get(
106 CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY).contains(
107 AccessController.class.getName()))) {
108 throw new RuntimeException("AccessController is missing from a system coprocessor list");
109 }
110 if (conf.getInt(HFile.FORMAT_VERSION_KEY, 2) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) {
111 throw new RuntimeException("Post 0.96 security features require HFile version >= 3");
112 }
113 }
114
115 public static void checkTablePerms(Configuration conf, TableName table, byte[] family, byte[] column,
116 Permission.Action... actions) throws IOException {
117 Permission[] perms = new Permission[actions.length];
118 for (int i = 0; i < actions.length; i++) {
119 perms[i] = new TablePermission(table, family, column, actions[i]);
120 }
121
122 checkTablePerms(conf, table, perms);
123 }
124
125 public static void checkTablePerms(Configuration conf, TableName table, Permission... perms)
126 throws IOException {
127 CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
128 for (Permission p : perms) {
129 request.addPermission(ProtobufUtil.toPermission(p));
130 }
131 try (Connection connection = ConnectionFactory.createConnection(conf)) {
132 try (Table acl = connection.getTable(table)) {
133 AccessControlService.BlockingInterface protocol =
134 AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
135 try {
136 protocol.checkPermissions(null, request.build());
137 } catch (ServiceException se) {
138 ProtobufUtil.toIOException(se);
139 }
140 }
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154 static interface AccessTestAction extends PrivilegedExceptionAction<Object> { }
155
156
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
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
196 public static void verifyDenied(AccessTestAction action, User... users) throws Exception {
197 for (User user : users) {
198 verifyDenied(user, action);
199 }
200 }
201
202
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("Unexpected action results: " + results + " for user '"
211 + 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
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() + "'");
229 }
230 } catch (AccessDeniedException ade) {
231 fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
232 }
233 }
234 }
235
236
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
246
247
248 for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) {
249 if (ex instanceof AccessDeniedException) {
250 isAccessDeniedException = true;
251 break;
252 }
253 }
254 }
255 else {
256
257
258 Throwable ex = e;
259 do {
260 if (ex instanceof AccessDeniedException) {
261 isAccessDeniedException = true;
262 break;
263 }
264 } while((ex = ex.getCause()) != null);
265 }
266 if (!isAccessDeniedException) {
267 fail("Expected exception was not thrown for user '" + user.getShortName() + "'");
268 }
269 } catch (UndeclaredThrowableException ute) {
270
271 Throwable ex = ute.getUndeclaredThrowable();
272 if (ex instanceof PrivilegedActionException) {
273 ex = ((PrivilegedActionException) ex).getException();
274 }
275 if (ex instanceof ServiceException) {
276 ServiceException se = (ServiceException)ex;
277 if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) {
278
279 return;
280 }
281 }
282 fail("Expected exception was not thrown for user '" + user.getShortName() + "'");
283 }
284 }
285 }
286
287 private static List<AccessController> getAccessControllers(MiniHBaseCluster cluster) {
288 List<AccessController> result = Lists.newArrayList();
289 for (RegionServerThread t: cluster.getLiveRegionServerThreads()) {
290 for (HRegion region: t.getRegionServer().getOnlineRegionsLocalContext()) {
291 Coprocessor cp = region.getCoprocessorHost()
292 .findCoprocessor(AccessController.class.getName());
293 if (cp != null) {
294 result.add((AccessController)cp);
295 }
296 }
297 }
298 return result;
299 }
300
301 private static Map<AccessController,Long> getAuthManagerMTimes(MiniHBaseCluster cluster) {
302 Map<AccessController,Long> result = Maps.newHashMap();
303 for (AccessController ac: getAccessControllers(cluster)) {
304 result.put(ac, ac.getAuthManager().getMTime());
305 }
306 return result;
307 }
308
309 @SuppressWarnings("rawtypes")
310 private static void updateACLs(final HBaseTestingUtility util, Callable c) throws Exception {
311
312 final Map<AccessController,Long> oldMTimes = getAuthManagerMTimes(util.getHBaseCluster());
313
314
315 c.call();
316
317
318 util.waitFor(WAIT_TIME, 100, new Predicate<IOException>() {
319 @Override
320 public boolean evaluate() throws IOException {
321 Map<AccessController,Long> mtimes = getAuthManagerMTimes(util.getHBaseCluster());
322 for (Map.Entry<AccessController,Long> e: mtimes.entrySet()) {
323 if (!oldMTimes.containsKey(e.getKey())) {
324 LOG.error("Snapshot of AccessController state does not include instance on region " +
325 e.getKey().getRegion().getRegionNameAsString());
326
327 return false;
328 }
329 long old = oldMTimes.get(e.getKey());
330 long now = e.getValue();
331 if (now <= old) {
332 LOG.info("AccessController on region " +
333 e.getKey().getRegion().getRegionNameAsString() + " has not updated: mtime=" +
334 now);
335 return false;
336 }
337 }
338 return true;
339 }
340 });
341 }
342
343
344
345
346
347
348 public static void grantGlobal(final HBaseTestingUtility util, final String user,
349 final Permission.Action... actions) throws Exception {
350 SecureTestUtil.updateACLs(util, new Callable<Void>() {
351 @Override
352 public Void call() throws Exception {
353 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
354 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
355 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
356 AccessControlService.BlockingInterface protocol =
357 AccessControlService.newBlockingStub(service);
358 ProtobufUtil.grant(protocol, user, actions);
359 }
360 }
361 return null;
362 }
363 });
364 }
365
366
367
368
369
370
371 public static void revokeGlobal(final HBaseTestingUtility util, final String user,
372 final Permission.Action... actions) throws Exception {
373 SecureTestUtil.updateACLs(util, new Callable<Void>() {
374 @Override
375 public Void call() throws Exception {
376 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
377 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
378 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
379 AccessControlService.BlockingInterface protocol =
380 AccessControlService.newBlockingStub(service);
381 ProtobufUtil.revoke(protocol, user, actions);
382 }
383 }
384 return null;
385 }
386 });
387 }
388
389
390
391
392
393
394 public static void grantOnNamespace(final HBaseTestingUtility util, final String user,
395 final String namespace, final Permission.Action... actions) throws Exception {
396 SecureTestUtil.updateACLs(util, new Callable<Void>() {
397 @Override
398 public Void call() throws Exception {
399 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
400 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
401 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
402 AccessControlService.BlockingInterface protocol =
403 AccessControlService.newBlockingStub(service);
404 ProtobufUtil.grant(protocol, user, namespace, actions);
405 }
406 }
407 return null;
408 }
409 });
410 }
411
412
413
414
415
416
417 public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtility util,
418 final Configuration conf, final String user, final String namespace,
419 final Permission.Action... actions) throws Exception {
420 SecureTestUtil.updateACLs(util, new Callable<Void>() {
421 @Override
422 public Void call() throws Exception {
423 try {
424 AccessControlClient.grant(conf, namespace, user, actions);
425 } catch (Throwable t) {
426 t.printStackTrace();
427 }
428 return null;
429 }
430 });
431 }
432
433
434
435
436
437
438 public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtility util,
439 final Connection connection, final String user, final String namespace,
440 final Permission.Action... actions) throws Exception {
441 SecureTestUtil.updateACLs(util, new Callable<Void>() {
442 @Override
443 public Void call() throws Exception {
444 try {
445 AccessControlClient.grant(connection, namespace, user, actions);
446 } catch (Throwable t) {
447 t.printStackTrace();
448 }
449 return null;
450 }
451 });
452 }
453
454
455
456
457
458
459 public static void revokeFromNamespaceUsingAccessControlClient(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.revoke(connection, namespace, user, actions);
467 } catch (Throwable t) {
468 t.printStackTrace();
469 }
470 return null;
471 }
472 });
473 }
474
475
476
477
478
479
480 public static void revokeFromNamespace(final HBaseTestingUtility util, final String user,
481 final String namespace, final Permission.Action... actions) throws Exception {
482 SecureTestUtil.updateACLs(util, new Callable<Void>() {
483 @Override
484 public Void call() throws Exception {
485 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
486 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
487 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
488 AccessControlService.BlockingInterface protocol =
489 AccessControlService.newBlockingStub(service);
490 ProtobufUtil.revoke(protocol, user, namespace, actions);
491 }
492 }
493 return null;
494 }
495 });
496 }
497
498
499
500
501
502
503 public static void grantOnTable(final HBaseTestingUtility util, final String user,
504 final TableName table, final byte[] family, final byte[] qualifier,
505 final Permission.Action... actions) throws Exception {
506 SecureTestUtil.updateACLs(util, new Callable<Void>() {
507 @Override
508 public Void call() throws Exception {
509 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
510 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
511 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
512 AccessControlService.BlockingInterface protocol =
513 AccessControlService.newBlockingStub(service);
514 ProtobufUtil.grant(protocol, user, table, family, qualifier, actions);
515 }
516 }
517 return null;
518 }
519 });
520 }
521
522
523
524
525
526
527 public static void grantOnTableUsingAccessControlClient(final HBaseTestingUtility util,
528 final Connection connection, final String user, final TableName table, final byte[] family,
529 final byte[] qualifier, final Permission.Action... actions) throws Exception {
530 SecureTestUtil.updateACLs(util, new Callable<Void>() {
531 @Override
532 public Void call() throws Exception {
533 try {
534 AccessControlClient.grant(connection, table, user, family, qualifier, actions);
535 } catch (Throwable t) {
536 t.printStackTrace();
537 }
538 return null;
539 }
540 });
541 }
542
543
544
545
546
547
548 public static void grantGlobalUsingAccessControlClient(final HBaseTestingUtility util,
549 final Connection connection, final String user, final Permission.Action... actions)
550 throws Exception {
551 SecureTestUtil.updateACLs(util, new Callable<Void>() {
552 @Override
553 public Void call() throws Exception {
554 try {
555 AccessControlClient.grant(connection, user, actions);
556 } catch (Throwable t) {
557 t.printStackTrace();
558 }
559 return null;
560 }
561 });
562 }
563
564
565
566
567
568
569 public static void revokeFromTable(final HBaseTestingUtility util, final String user,
570 final TableName table, final byte[] family, final byte[] qualifier,
571 final Permission.Action... actions) throws Exception {
572 SecureTestUtil.updateACLs(util, new Callable<Void>() {
573 @Override
574 public Void call() throws Exception {
575 try (Connection connection = ConnectionFactory.createConnection(util.getConfiguration())) {
576 try (Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME)) {
577 BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
578 AccessControlService.BlockingInterface protocol =
579 AccessControlService.newBlockingStub(service);
580 ProtobufUtil.revoke(protocol, user, table, family, qualifier, actions);
581 }
582 }
583 return null;
584 }
585 });
586 }
587
588
589
590
591
592
593 public static void revokeFromTableUsingAccessControlClient(final HBaseTestingUtility 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.revoke(connection, table, user, family, qualifier, actions);
601 } catch (Throwable t) {
602 t.printStackTrace();
603 }
604 return null;
605 }
606 });
607 }
608
609
610
611
612
613
614 public static void revokeGlobalUsingAccessControlClient(final HBaseTestingUtility 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.revoke(connection, user, actions);
622 } catch (Throwable t) {
623 t.printStackTrace();
624 }
625 return null;
626 }
627 });
628 }
629
630 public static void createNamespace(HBaseTestingUtility testUtil, NamespaceDescriptor nsDesc)
631 throws Exception {
632 testUtil.getHBaseAdmin().createNamespace(nsDesc);
633 }
634
635 public static void deleteNamespace(HBaseTestingUtility testUtil, String namespace)
636 throws Exception {
637 testUtil.getHBaseAdmin().deleteNamespace(namespace);
638 }
639
640 public static String convertToNamespace(String namespace) {
641 return AccessControlLists.NAMESPACE_PREFIX + namespace;
642 }
643
644 public static String convertToGroup(String group) {
645 return AccessControlLists.GROUP_PREFIX + group;
646 }
647
648 public void checkGlobalPerms(HBaseTestingUtility testUtil, Permission.Action... actions)
649 throws IOException {
650 Permission[] perms = new Permission[actions.length];
651 for (int i = 0; i < actions.length; i++) {
652 perms[i] = new Permission(actions[i]);
653 }
654 CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
655 for (Action a : actions) {
656 request.addPermission(AccessControlProtos.Permission.newBuilder()
657 .setType(AccessControlProtos.Permission.Type.Global)
658 .setGlobalPermission(
659 AccessControlProtos.GlobalPermission.newBuilder()
660 .addAction(ProtobufUtil.toPermissionAction(a)).build()));
661 }
662 HTable acl = new HTable(testUtil.getConfiguration(), AccessControlLists.ACL_TABLE_NAME);
663 try {
664 BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
665 AccessControlService.BlockingInterface protocol =
666 AccessControlService.newBlockingStub(channel);
667 try {
668 protocol.checkPermissions(null, request.build());
669 } catch (ServiceException se) {
670 ProtobufUtil.toIOException(se);
671 }
672 } finally {
673 acl.close();
674 }
675 }
676
677 public void checkTablePerms(HBaseTestingUtility testUtil, TableName table, byte[] family,
678 byte[] column, Permission.Action... actions) throws IOException {
679 Permission[] perms = new Permission[actions.length];
680 for (int i = 0; i < actions.length; i++) {
681 perms[i] = new TablePermission(table, family, column, actions[i]);
682 }
683 checkTablePerms(testUtil, table, perms);
684 }
685
686 public void checkTablePerms(HBaseTestingUtility testUtil, TableName table, Permission... perms)
687 throws IOException {
688 CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
689 for (Permission p : perms) {
690 request.addPermission(ProtobufUtil.toPermission(p));
691 }
692 HTable acl = new HTable(testUtil.getConfiguration(), table);
693 try {
694 AccessControlService.BlockingInterface protocol =
695 AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
696 try {
697 protocol.checkPermissions(null, request.build());
698 } catch (ServiceException se) {
699 ProtobufUtil.toIOException(se);
700 }
701 } finally {
702 acl.close();
703 }
704 }
705
706 }