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