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