1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.security.access;
19
20 import static org.apache.hadoop.hbase.AuthUtil.toGroupEntry;
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25
26 import java.util.Arrays;
27 import java.util.List;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.Coprocessor;
33 import org.apache.hadoop.hbase.HBaseTestingUtility;
34 import org.apache.hadoop.hbase.HColumnDescriptor;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.HTableDescriptor;
37 import org.apache.hadoop.hbase.NamespaceDescriptor;
38 import org.apache.hadoop.hbase.TableName;
39 import org.apache.hadoop.hbase.TableNotFoundException;
40 import org.apache.hadoop.hbase.client.Admin;
41 import org.apache.hadoop.hbase.client.Connection;
42 import org.apache.hadoop.hbase.client.ConnectionFactory;
43 import org.apache.hadoop.hbase.client.Put;
44 import org.apache.hadoop.hbase.client.Result;
45 import org.apache.hadoop.hbase.client.ResultScanner;
46 import org.apache.hadoop.hbase.client.Scan;
47 import org.apache.hadoop.hbase.client.Table;
48 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
49 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
50 import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
51 import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
52 import org.apache.hadoop.hbase.security.User;
53 import org.apache.hadoop.hbase.security.access.Permission.Action;
54 import org.apache.hadoop.hbase.testclassification.LargeTests;
55 import org.apache.hadoop.hbase.util.Bytes;
56 import org.apache.hadoop.hbase.util.TestTableName;
57 import org.apache.hadoop.hbase.zookeeper.ZKUtil;
58 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
59 import org.junit.After;
60 import org.junit.AfterClass;
61 import org.junit.Before;
62 import org.junit.BeforeClass;
63 import org.junit.Rule;
64 import org.junit.Test;
65 import org.junit.experimental.categories.Category;
66
67 @Category(LargeTests.class)
68 public class TestAccessController2 extends SecureTestUtil {
69 private static final Log LOG = LogFactory.getLog(TestAccessController2.class);
70
71 private static final byte[] TEST_ROW = Bytes.toBytes("test");
72 private static final byte[] TEST_FAMILY = Bytes.toBytes("f");
73 private static final byte[] TEST_QUALIFIER = Bytes.toBytes("q");
74 private static final byte[] TEST_VALUE = Bytes.toBytes("value");
75
76 private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
77 private static Configuration conf;
78
79
80
81
82 private static Connection systemUserConnection;
83
84 private final static byte[] Q1 = Bytes.toBytes("q1");
85 private final static byte[] value1 = Bytes.toBytes("value1");
86
87 private static byte[] TEST_FAMILY_2 = Bytes.toBytes("f2");
88 private static byte[] TEST_ROW_2 = Bytes.toBytes("r2");
89 private final static byte[] Q2 = Bytes.toBytes("q2");
90 private final static byte[] value2 = Bytes.toBytes("value2");
91
92 private static byte[] TEST_ROW_3 = Bytes.toBytes("r3");
93
94 private static final String TESTGROUP_1 = "testgroup_1";
95 private static final String TESTGROUP_2 = "testgroup_2";
96
97 private static User TESTGROUP1_USER1;
98 private static User TESTGROUP2_USER1;
99
100 @Rule
101 public TestTableName TEST_TABLE = new TestTableName();
102 private String namespace = "testNamespace";
103 private String tname = namespace + ":testtable1";
104 private TableName tableName = TableName.valueOf(tname);
105 private static String TESTGROUP_1_NAME;
106
107 @BeforeClass
108 public static void setupBeforeClass() throws Exception {
109 conf = TEST_UTIL.getConfiguration();
110
111 conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10);
112
113 enableSecurity(conf);
114
115 verifyConfiguration(conf);
116 TEST_UTIL.startMiniCluster();
117
118 TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME);
119
120 TESTGROUP_1_NAME = toGroupEntry(TESTGROUP_1);
121 TESTGROUP1_USER1 =
122 User.createUserForTesting(conf, "testgroup1_user1", new String[] { TESTGROUP_1 });
123 TESTGROUP2_USER1 =
124 User.createUserForTesting(conf, "testgroup2_user2", new String[] { TESTGROUP_2 });
125
126 systemUserConnection = ConnectionFactory.createConnection(conf);
127 }
128
129 @Before
130 public void setUp() throws Exception {
131 createNamespace(TEST_UTIL, NamespaceDescriptor.create(namespace).build());
132 try (Table table = createTable(TEST_UTIL, tableName,
133 new byte[][] { TEST_FAMILY, TEST_FAMILY_2 })) {
134 TEST_UTIL.waitTableEnabled(tableName);
135
136
137 table.put(Arrays.asList(new Put(TEST_ROW).addColumn(TEST_FAMILY, Q1, value1),
138 new Put(TEST_ROW_2).addColumn(TEST_FAMILY, Q2, value2),
139 new Put(TEST_ROW_3).addColumn(TEST_FAMILY_2, Q1, value1)));
140 }
141
142 assertEquals(1, AccessControlLists.getTablePermissions(conf, tableName).size());
143 try {
144 assertEquals(1, AccessControlClient.getUserPermissions(systemUserConnection, tableName.toString())
145 .size());
146 } catch (Throwable e) {
147 LOG.error("Error during call of AccessControlClient.getUserPermissions. ", e);
148 }
149 }
150
151 @AfterClass
152 public static void tearDownAfterClass() throws Exception {
153 systemUserConnection.close();
154 TEST_UTIL.shutdownMiniCluster();
155 }
156
157 @After
158 public void tearDown() throws Exception {
159
160 try {
161 deleteTable(TEST_UTIL, tableName);
162 } catch (TableNotFoundException ex) {
163
164 LOG.info("Test deleted table " + tableName);
165 }
166 deleteNamespace(TEST_UTIL, namespace);
167
168 assertEquals(0, AccessControlLists.getTablePermissions(conf, tableName).size());
169 assertEquals(0, AccessControlLists.getNamespacePermissions(conf, namespace).size());
170 }
171
172 @Test (timeout=180000)
173 public void testCreateWithCorrectOwner() throws Exception {
174
175 final User testUser = User.createUserForTesting(TEST_UTIL.getConfiguration(), "TestUser",
176 new String[0]);
177
178 SecureTestUtil.grantGlobal(TEST_UTIL, testUser.getShortName(), Action.CREATE);
179 verifyAllowed(new AccessTestAction() {
180 @Override
181 public Object run() throws Exception {
182 HTableDescriptor desc = new HTableDescriptor(TEST_TABLE.getTableName());
183 desc.addFamily(new HColumnDescriptor(TEST_FAMILY));
184 try (Connection connection =
185 ConnectionFactory.createConnection(TEST_UTIL.getConfiguration(), testUser)) {
186 try (Admin admin = connection.getAdmin()) {
187 createTable(TEST_UTIL, admin, desc);
188 }
189 }
190 return null;
191 }
192 }, testUser);
193 TEST_UTIL.waitTableAvailable(TEST_TABLE.getTableName());
194
195
196 List<TablePermission> perms =
197 AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName())
198 .get(testUser.getShortName());
199 assertNotNull(perms);
200 assertFalse(perms.isEmpty());
201
202 assertTrue(perms.get(0).implies(Permission.Action.READ));
203 assertTrue(perms.get(0).implies(Permission.Action.WRITE));
204 assertTrue(perms.get(0).implies(Permission.Action.EXEC));
205 assertTrue(perms.get(0).implies(Permission.Action.CREATE));
206 assertTrue(perms.get(0).implies(Permission.Action.ADMIN));
207 }
208
209 @Test (timeout=180000)
210 public void testCreateTableWithGroupPermissions() throws Exception {
211 grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
212 try {
213 AccessTestAction createAction = new AccessTestAction() {
214 @Override
215 public Object run() throws Exception {
216 HTableDescriptor desc = new HTableDescriptor(TEST_TABLE.getTableName());
217 desc.addFamily(new HColumnDescriptor(TEST_FAMILY));
218 try (Connection connection =
219 ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
220 try (Admin admin = connection.getAdmin()) {
221 admin.createTable(desc);
222 }
223 }
224 return null;
225 }
226 };
227 verifyAllowed(createAction, TESTGROUP1_USER1);
228 verifyDenied(createAction, TESTGROUP2_USER1);
229 } finally {
230 revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.CREATE);
231 }
232 }
233
234 @Test (timeout=180000)
235 public void testACLTableAccess() throws Exception {
236 final Configuration conf = TEST_UTIL.getConfiguration();
237
238
239 User superUser = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
240
241
242 User globalRead = User.createUserForTesting(conf, "globalRead", new String[0]);
243 User globalWrite = User.createUserForTesting(conf, "globalWrite", new String[0]);
244 User globalCreate = User.createUserForTesting(conf, "globalCreate", new String[0]);
245 User globalAdmin = User.createUserForTesting(conf, "globalAdmin", new String[0]);
246 SecureTestUtil.grantGlobal(TEST_UTIL, globalRead.getShortName(), Action.READ);
247 SecureTestUtil.grantGlobal(TEST_UTIL, globalWrite.getShortName(), Action.WRITE);
248 SecureTestUtil.grantGlobal(TEST_UTIL, globalCreate.getShortName(), Action.CREATE);
249 SecureTestUtil.grantGlobal(TEST_UTIL, globalAdmin.getShortName(), Action.ADMIN);
250
251
252 User nsRead = User.createUserForTesting(conf, "nsRead", new String[0]);
253 User nsWrite = User.createUserForTesting(conf, "nsWrite", new String[0]);
254 User nsCreate = User.createUserForTesting(conf, "nsCreate", new String[0]);
255 User nsAdmin = User.createUserForTesting(conf, "nsAdmin", new String[0]);
256 SecureTestUtil.grantOnNamespace(TEST_UTIL, nsRead.getShortName(),
257 TEST_TABLE.getTableName().getNamespaceAsString(), Action.READ);
258 SecureTestUtil.grantOnNamespace(TEST_UTIL, nsWrite.getShortName(),
259 TEST_TABLE.getTableName().getNamespaceAsString(), Action.WRITE);
260 SecureTestUtil.grantOnNamespace(TEST_UTIL, nsCreate.getShortName(),
261 TEST_TABLE.getTableName().getNamespaceAsString(), Action.CREATE);
262 SecureTestUtil.grantOnNamespace(TEST_UTIL, nsAdmin.getShortName(),
263 TEST_TABLE.getTableName().getNamespaceAsString(), Action.ADMIN);
264
265
266 User tableRead = User.createUserForTesting(conf, "tableRead", new String[0]);
267 User tableWrite = User.createUserForTesting(conf, "tableWrite", new String[0]);
268 User tableCreate = User.createUserForTesting(conf, "tableCreate", new String[0]);
269 User tableAdmin = User.createUserForTesting(conf, "tableAdmin", new String[0]);
270 SecureTestUtil.grantOnTable(TEST_UTIL, tableRead.getShortName(),
271 TEST_TABLE.getTableName(), null, null, Action.READ);
272 SecureTestUtil.grantOnTable(TEST_UTIL, tableWrite.getShortName(),
273 TEST_TABLE.getTableName(), null, null, Action.WRITE);
274 SecureTestUtil.grantOnTable(TEST_UTIL, tableCreate.getShortName(),
275 TEST_TABLE.getTableName(), null, null, Action.CREATE);
276 SecureTestUtil.grantOnTable(TEST_UTIL, tableAdmin.getShortName(),
277 TEST_TABLE.getTableName(), null, null, Action.ADMIN);
278
279 grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
280 try {
281
282
283 AccessTestAction writeAction = new AccessTestAction() {
284 @Override
285 public Object run() throws Exception {
286 try (Connection conn = ConnectionFactory.createConnection(conf);
287 Table t = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) {
288 t.put(new Put(TEST_ROW).addColumn(AccessControlLists.ACL_LIST_FAMILY, TEST_QUALIFIER,
289 TEST_VALUE));
290 return null;
291 } finally {
292 }
293 }
294 };
295
296
297
298 verifyDenied(writeAction, globalAdmin, globalCreate, globalRead, TESTGROUP2_USER1);
299 verifyDenied(writeAction, nsAdmin, nsCreate, nsRead, nsWrite);
300 verifyDenied(writeAction, tableAdmin, tableCreate, tableRead, tableWrite);
301 verifyAllowed(writeAction, superUser, globalWrite, TESTGROUP1_USER1);
302 } finally {
303 revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.WRITE);
304 }
305
306 grantGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
307 try {
308
309
310 AccessTestAction scanAction = new AccessTestAction() {
311 @Override
312 public Object run() throws Exception {
313 try (Connection conn = ConnectionFactory.createConnection(conf);
314 Table t = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) {
315 ResultScanner s = t.getScanner(new Scan());
316 try {
317 for (Result r = s.next(); r != null; r = s.next()) {
318
319 }
320 } finally {
321 s.close();
322 }
323 return null;
324 }
325 }
326 };
327
328
329
330 verifyDenied(scanAction, globalAdmin, globalCreate, globalWrite, TESTGROUP2_USER1);
331 verifyDenied(scanAction, nsCreate, nsAdmin, nsRead, nsWrite);
332 verifyDenied(scanAction, tableCreate, tableAdmin, tableRead, tableWrite);
333 verifyAllowed(scanAction, superUser, globalRead, TESTGROUP1_USER1);
334 } finally {
335 revokeGlobal(TEST_UTIL, TESTGROUP_1_NAME, Action.READ);
336 }
337 }
338
339
340
341
342 @Test(timeout = 300000)
343 public void testPostGrantAndRevokeScanAction() throws Exception {
344 AccessTestAction scanTableActionForGroupWithTableLevelAccess = new AccessTestAction() {
345 @Override
346 public Void run() throws Exception {
347 try (Connection connection = ConnectionFactory.createConnection(conf);
348 Table table = connection.getTable(tableName);) {
349 Scan s1 = new Scan();
350 try (ResultScanner scanner1 = table.getScanner(s1);) {
351 Result[] next1 = scanner1.next(5);
352 assertTrue("User having table level access should be able to scan all "
353 + "the data in the table.", next1.length == 3);
354 }
355 }
356 return null;
357 }
358 };
359
360 AccessTestAction scanTableActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
361 @Override
362 public Void run() throws Exception {
363 try (Connection connection = ConnectionFactory.createConnection(conf);
364 Table table = connection.getTable(tableName);) {
365 Scan s1 = new Scan();
366 try (ResultScanner scanner1 = table.getScanner(s1);) {
367 Result[] next1 = scanner1.next(5);
368 assertTrue("User having column family level access should be able to scan all "
369 + "the data belonging to that family.", next1.length == 2);
370 }
371 }
372 return null;
373 }
374 };
375
376 AccessTestAction scanFamilyActionForGroupWithFamilyLevelAccess = new AccessTestAction() {
377 @Override
378 public Void run() throws Exception {
379 try (Connection connection = ConnectionFactory.createConnection(conf);
380 Table table = connection.getTable(tableName);) {
381 Scan s1 = new Scan();
382 s1.addFamily(TEST_FAMILY_2);
383 try (ResultScanner scanner1 = table.getScanner(s1);) {
384 }
385 }
386 return null;
387 }
388 };
389
390 AccessTestAction scanTableActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
391 @Override
392 public Void run() throws Exception {
393 try (Connection connection = ConnectionFactory.createConnection(conf);
394 Table table = connection.getTable(tableName);) {
395 Scan s1 = new Scan();
396 try (ResultScanner scanner1 = table.getScanner(s1);) {
397 Result[] next1 = scanner1.next(5);
398 assertTrue("User having column qualifier level access should be able to scan "
399 + "that column family qualifier data.", next1.length == 1);
400 }
401 }
402 return null;
403 }
404 };
405
406 AccessTestAction scanFamilyActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
407 @Override
408 public Void run() throws Exception {
409 try (Connection connection = ConnectionFactory.createConnection(conf);
410 Table table = connection.getTable(tableName);) {
411 Scan s1 = new Scan();
412 s1.addFamily(TEST_FAMILY_2);
413 try (ResultScanner scanner1 = table.getScanner(s1);) {
414 }
415 }
416 return null;
417 }
418 };
419
420 AccessTestAction scanQualifierActionForGroupWithQualifierLevelAccess = new AccessTestAction() {
421 @Override
422 public Void run() throws Exception {
423 try (Connection connection = ConnectionFactory.createConnection(conf);
424 Table table = connection.getTable(tableName);) {
425 Scan s1 = new Scan();
426 s1.addColumn(TEST_FAMILY, Q2);
427 try (ResultScanner scanner1 = table.getScanner(s1);) {
428 }
429 }
430 return null;
431 }
432 };
433
434
435
436 grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, null, null, Action.READ);
437 verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
438 verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithTableLevelAccess);
439
440
441 revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, null, null);
442 verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess);
443
444
445
446 grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, null,
447 Permission.Action.READ);
448 verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
449 verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
450 verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithFamilyLevelAccess);
451 verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithFamilyLevelAccess);
452
453
454
455 revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, null);
456 verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess);
457
458
459
460 grantOnTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, Q1, Action.READ);
461 verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
462 verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
463 verifyDenied(TESTGROUP1_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
464 verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithQualifierLevelAccess);
465 verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithQualifierLevelAccess);
466 verifyDenied(TESTGROUP2_USER1, scanQualifierActionForGroupWithQualifierLevelAccess);
467
468
469
470 revokeFromTable(TEST_UTIL, TESTGROUP_1_NAME, tableName, TEST_FAMILY, Q1);
471 verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess);
472 }
473
474 public static class MyAccessController extends AccessController {
475 }
476
477 @Test (timeout=180000)
478 public void testCoprocessorLoading() throws Exception {
479 MasterCoprocessorHost cpHost =
480 TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost();
481 cpHost.load(MyAccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
482 AccessController ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(
483 MyAccessController.class.getName());
484 MasterCoprocessorEnvironment CP_ENV = cpHost.createEnvironment(
485 MyAccessController.class, ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf);
486 RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
487 .getRegionServerCoprocessorHost();
488 RegionServerCoprocessorEnvironment RSCP_ENV = rsHost.createEnvironment(
489 MyAccessController.class, ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf);
490 }
491
492 @Test (timeout=180000)
493 public void testACLZNodeDeletion() throws Exception {
494 String baseAclZNode = "/hbase/acl/";
495 String ns = "testACLZNodeDeletionNamespace";
496 NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build();
497 createNamespace(TEST_UTIL, desc);
498
499 final TableName table = TableName.valueOf(ns, "testACLZNodeDeletionTable");
500 final byte[] family = Bytes.toBytes("f1");
501 HTableDescriptor htd = new HTableDescriptor(table);
502 htd.addFamily(new HColumnDescriptor(family));
503 createTable(TEST_UTIL, htd);
504
505
506 grantOnNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
507 ZooKeeperWatcher zkw = TEST_UTIL.getMiniHBaseCluster().getMaster().getZooKeeper();
508 assertTrue("The acl znode for table should exist", ZKUtil.checkExists(zkw, baseAclZNode +
509 table.getNameAsString()) != -1);
510 assertTrue("The acl znode for namespace should exist", ZKUtil.checkExists(zkw, baseAclZNode +
511 convertToNamespace(ns)) != -1);
512
513 revokeFromNamespace(TEST_UTIL, TESTGROUP1_USER1.getShortName(), ns, Action.ADMIN);
514 deleteTable(TEST_UTIL, table);
515 deleteNamespace(TEST_UTIL, ns);
516
517 assertTrue("The acl znode for table should have been deleted",
518 ZKUtil.checkExists(zkw, baseAclZNode + table.getNameAsString()) == -1);
519 assertTrue( "The acl znode for namespace should have been deleted",
520 ZKUtil.checkExists(zkw, baseAclZNode + convertToNamespace(ns)) == -1);
521 }
522 }