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