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