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.junit.Assert.assertEquals;
21
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.hbase.AuthUtil;
30 import org.apache.hadoop.hbase.Cell;
31 import org.apache.hadoop.hbase.Coprocessor;
32 import org.apache.hadoop.hbase.HBaseTestingUtility;
33 import org.apache.hadoop.hbase.HColumnDescriptor;
34 import org.apache.hadoop.hbase.HTableDescriptor;
35 import org.apache.hadoop.hbase.testclassification.MediumTests;
36 import org.apache.hadoop.hbase.TableNotFoundException;
37 import org.apache.hadoop.hbase.client.Admin;
38 import org.apache.hadoop.hbase.client.Connection;
39 import org.apache.hadoop.hbase.client.ConnectionFactory;
40 import org.apache.hadoop.hbase.client.Delete;
41 import org.apache.hadoop.hbase.client.Get;
42 import org.apache.hadoop.hbase.client.HTable;
43 import org.apache.hadoop.hbase.client.Increment;
44 import org.apache.hadoop.hbase.client.Put;
45 import org.apache.hadoop.hbase.client.Result;
46 import org.apache.hadoop.hbase.client.ResultScanner;
47 import org.apache.hadoop.hbase.client.Scan;
48 import org.apache.hadoop.hbase.client.Table;
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.util.Bytes;
54 import org.apache.hadoop.hbase.util.TestTableName;
55 import org.apache.hadoop.hbase.util.Threads;
56 import org.apache.log4j.Level;
57 import org.apache.log4j.Logger;
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 import com.google.common.collect.Lists;
67
68 @Category(MediumTests.class)
69 public class TestCellACLs extends SecureTestUtil {
70 private static final Log LOG = LogFactory.getLog(TestCellACLs.class);
71
72 static {
73 Logger.getLogger(AccessController.class).setLevel(Level.TRACE);
74 Logger.getLogger(AccessControlFilter.class).setLevel(Level.TRACE);
75 Logger.getLogger(TableAuthManager.class).setLevel(Level.TRACE);
76 }
77
78 @Rule
79 public TestTableName TEST_TABLE = new TestTableName();
80 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
81 private static final byte[] TEST_FAMILY = Bytes.toBytes("f1");
82 private static final byte[] TEST_ROW = Bytes.toBytes("cellpermtest");
83 private static final byte[] TEST_Q1 = Bytes.toBytes("q1");
84 private static final byte[] TEST_Q2 = Bytes.toBytes("q2");
85 private static final byte[] TEST_Q3 = Bytes.toBytes("q3");
86 private static final byte[] TEST_Q4 = Bytes.toBytes("q4");
87 private static final byte[] ZERO = Bytes.toBytes(0L);
88 private static final byte[] ONE = Bytes.toBytes(1L);
89
90 private static Configuration conf;
91
92 private static final String GROUP = "group";
93 private static User GROUP_USER;
94 private static User USER_OWNER;
95 private static User USER_OTHER;
96 private static String[] usersAndGroups;
97
98 @BeforeClass
99 public static void setupBeforeClass() throws Exception {
100
101 conf = TEST_UTIL.getConfiguration();
102
103 enableSecurity(conf);
104
105 verifyConfiguration(conf);
106
107
108 conf.setBoolean(AccessControlConstants.CF_ATTRIBUTE_EARLY_OUT, false);
109
110 TEST_UTIL.startMiniCluster();
111 MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster()
112 .getMasterCoprocessorHost();
113 cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
114 AccessController ac = (AccessController)
115 cpHost.findCoprocessor(AccessController.class.getName());
116 cpHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
117 RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
118 .getRegionServerCoprocessorHost();
119 rsHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
120
121
122 TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
123
124
125 USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
126 USER_OTHER = User.createUserForTesting(conf, "other", new String[0]);
127 GROUP_USER = User.createUserForTesting(conf, "group_user", new String[] { GROUP });
128
129 usersAndGroups = new String[] { USER_OTHER.getShortName(), AuthUtil.toGroupEntry(GROUP) };
130 }
131
132 @AfterClass
133 public static void tearDownAfterClass() throws Exception {
134 TEST_UTIL.shutdownMiniCluster();
135 }
136
137 @Before
138 public void setUp() throws Exception {
139
140 Admin admin = TEST_UTIL.getHBaseAdmin();
141 HTableDescriptor htd = new HTableDescriptor(TEST_TABLE.getTableName());
142 HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY);
143 hcd.setMaxVersions(4);
144 htd.setOwner(USER_OWNER);
145 htd.addFamily(hcd);
146 admin.createTable(htd, new byte[][] { Bytes.toBytes("s") });
147 TEST_UTIL.waitTableEnabled(TEST_TABLE.getTableName());
148 LOG.info("Sleeping a second because of HBASE-12581");
149 Threads.sleep(1000);
150 }
151
152 @Test
153 public void testCellPermissions() throws Exception {
154
155 verifyAllowed(new AccessTestAction() {
156 @Override
157 public Object run() throws Exception {
158 Table t = new HTable(conf, TEST_TABLE.getTableName());
159 try {
160 Put p;
161
162 p = new Put(TEST_ROW).add(TEST_FAMILY, TEST_Q1, ZERO);
163 p.setACL(prepareCellPermissions(usersAndGroups, Action.READ));
164 t.put(p);
165
166 p = new Put(TEST_ROW).add(TEST_FAMILY, TEST_Q2, ZERO);
167 p.setACL(prepareCellPermissions(usersAndGroups, Action.READ, Action.WRITE));
168 t.put(p);
169
170 p = new Put(TEST_ROW)
171 .add(TEST_FAMILY, TEST_Q3, ZERO)
172 .add(TEST_FAMILY, TEST_Q4, ZERO);
173 t.put(p);
174 } finally {
175 t.close();
176 }
177 return null;
178 }
179 }, USER_OWNER);
180
181
182
183 AccessTestAction getQ1 = new AccessTestAction() {
184 @Override
185 public Object run() throws Exception {
186 Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q1);
187 Table t = new HTable(conf, TEST_TABLE.getTableName());
188 try {
189 return t.get(get).listCells();
190 } finally {
191 t.close();
192 }
193 }
194 };
195
196 AccessTestAction getQ2 = new AccessTestAction() {
197 @Override
198 public Object run() throws Exception {
199 Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q2);
200 Table t = new HTable(conf, TEST_TABLE.getTableName());
201 try {
202 return t.get(get).listCells();
203 } finally {
204 t.close();
205 }
206 }
207 };
208
209 AccessTestAction getQ3 = new AccessTestAction() {
210 @Override
211 public Object run() throws Exception {
212 Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q3);
213 Table t = new HTable(conf, TEST_TABLE.getTableName());
214 try {
215 return t.get(get).listCells();
216 } finally {
217 t.close();
218 }
219 }
220 };
221
222 AccessTestAction getQ4 = new AccessTestAction() {
223 @Override
224 public Object run() throws Exception {
225 Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q4);
226 Table t = new HTable(conf, TEST_TABLE.getTableName());
227 try {
228 return t.get(get).listCells();
229 } finally {
230 t.close();
231 }
232 }
233 };
234
235
236
237 verifyAllowed(getQ1, USER_OTHER, GROUP_USER);
238 verifyAllowed(getQ2, USER_OTHER, GROUP_USER);
239
240
241
242 verifyIfNull(getQ3, USER_OTHER, GROUP_USER);
243 verifyIfNull(getQ4, USER_OTHER, GROUP_USER);
244
245
246
247
248
249 final List<Cell> scanResults = Lists.newArrayList();
250
251 AccessTestAction scanAction = new AccessTestAction() {
252 @Override
253 public List<Cell> run() throws Exception {
254 Scan scan = new Scan();
255 scan.setStartRow(TEST_ROW);
256 scan.setStopRow(Bytes.add(TEST_ROW, new byte[]{ 0 } ));
257 scan.addFamily(TEST_FAMILY);
258 Table t = new HTable(conf, TEST_TABLE.getTableName());
259 try {
260 ResultScanner scanner = t.getScanner(scan);
261 Result result = null;
262 do {
263 result = scanner.next();
264 if (result != null) {
265 scanResults.addAll(result.listCells());
266 }
267 } while (result != null);
268 } finally {
269 t.close();
270 }
271 return scanResults;
272 }
273 };
274
275
276 scanResults.clear();
277 verifyAllowed(scanAction, USER_OWNER);
278 assertEquals(4, scanResults.size());
279
280
281 scanResults.clear();
282 verifyAllowed(scanAction, USER_OTHER);
283 assertEquals(2, scanResults.size());
284
285 scanResults.clear();
286 verifyAllowed(scanAction, GROUP_USER);
287 assertEquals(2, scanResults.size());
288
289
290
291 AccessTestAction incrementQ1 = new AccessTestAction() {
292 @Override
293 public Object run() throws Exception {
294 Increment i = new Increment(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q1, 1L);
295 Table t = new HTable(conf, TEST_TABLE.getTableName());
296 try {
297 t.increment(i);
298 } finally {
299 t.close();
300 }
301 return null;
302 }
303 };
304
305 AccessTestAction incrementQ2 = new AccessTestAction() {
306 @Override
307 public Object run() throws Exception {
308 Increment i = new Increment(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q2, 1L);
309 Table t = new HTable(conf, TEST_TABLE.getTableName());
310 try {
311 t.increment(i);
312 } finally {
313 t.close();
314 }
315 return null;
316 }
317 };
318
319 AccessTestAction incrementQ2newDenyACL = new AccessTestAction() {
320 @Override
321 public Object run() throws Exception {
322 Increment i = new Increment(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q2, 1L);
323
324 i.setACL(USER_OTHER.getShortName(), new Permission(Action.READ));
325 Table t = new HTable(conf, TEST_TABLE.getTableName());
326 try {
327 t.increment(i);
328 } finally {
329 t.close();
330 }
331 return null;
332 }
333 };
334
335 AccessTestAction incrementQ3 = new AccessTestAction() {
336 @Override
337 public Object run() throws Exception {
338 Increment i = new Increment(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q3, 1L);
339 Table t = new HTable(conf, TEST_TABLE.getTableName());
340 try {
341 t.increment(i);
342 } finally {
343 t.close();
344 }
345 return null;
346 }
347 };
348
349 verifyDenied(incrementQ1, USER_OTHER, GROUP_USER);
350 verifyDenied(incrementQ3, USER_OTHER, GROUP_USER);
351
352
353
354 verifyAllowed(incrementQ2, USER_OTHER, GROUP_USER);
355 verifyAllowed(incrementQ2newDenyACL, USER_OTHER);
356
357
358 verifyDenied(incrementQ2, USER_OTHER, GROUP_USER);
359
360
361
362 AccessTestAction deleteFamily = new AccessTestAction() {
363 @Override
364 public Object run() throws Exception {
365 Delete delete = new Delete(TEST_ROW).deleteFamily(TEST_FAMILY);
366 Table t = new HTable(conf, TEST_TABLE.getTableName());
367 try {
368 t.delete(delete);
369 } finally {
370 t.close();
371 }
372 return null;
373 }
374 };
375
376 AccessTestAction deleteQ1 = new AccessTestAction() {
377 @Override
378 public Object run() throws Exception {
379 Delete delete = new Delete(TEST_ROW).deleteColumn(TEST_FAMILY, TEST_Q1);
380 Table t = new HTable(conf, TEST_TABLE.getTableName());
381 try {
382 t.delete(delete);
383 } finally {
384 t.close();
385 }
386 return null;
387 }
388 };
389
390 verifyDenied(deleteFamily, USER_OTHER, GROUP_USER);
391 verifyDenied(deleteQ1, USER_OTHER, GROUP_USER);
392 verifyAllowed(deleteQ1, USER_OWNER);
393 }
394
395
396
397
398
399 @Test
400 public void testCoveringCheck() throws Exception {
401
402 grantOnTable(TEST_UTIL, USER_OTHER.getShortName(), TEST_TABLE.getTableName(), TEST_FAMILY,
403 null, Action.READ);
404
405 grantOnTable(TEST_UTIL, AuthUtil.toGroupEntry(GROUP), TEST_TABLE.getTableName(), TEST_FAMILY,
406 null, Action.READ);
407
408
409
410
411 verfifyUserDeniedForWrite(USER_OTHER, ZERO);
412
413 verfifyUserDeniedForWrite(GROUP_USER, ZERO);
414
415
416 verifyAllowed(new AccessTestAction() {
417 @Override
418 public Object run() throws Exception {
419 Table t = new HTable(conf, TEST_TABLE.getTableName());
420 try {
421 Put p;
422 p = new Put(TEST_ROW).add(TEST_FAMILY, TEST_Q1, ZERO);
423 t.put(p);
424 } finally {
425 t.close();
426 }
427 return null;
428 }
429 }, USER_OWNER);
430
431
432 verfifyUserDeniedForWrite(USER_OTHER, ONE);
433
434 verfifyUserDeniedForWrite(GROUP_USER, ONE);
435
436
437 verifyUserAllowedForRead(USER_OTHER);
438
439 verifyUserAllowedForRead(GROUP_USER);
440 }
441
442 private void verfifyUserDeniedForWrite(final User user, final byte[] value) throws Exception {
443 verifyDenied(new AccessTestAction() {
444 @Override
445 public Object run() throws Exception {
446 try (Connection connection = ConnectionFactory.createConnection(conf);
447 Table t = connection.getTable(TEST_TABLE.getTableName())) {
448 Put p;
449 p = new Put(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q1, value);
450 t.put(p);
451 }
452 return null;
453 }
454 }, user);
455 }
456
457 private void verifyUserAllowedForRead(final User user) throws Exception {
458 verifyAllowed(new AccessTestAction() {
459 @Override
460 public Object run() throws Exception {
461 try (Connection connection = ConnectionFactory.createConnection(conf);
462 Table t = connection.getTable(TEST_TABLE.getTableName())) {
463 return t.get(new Get(TEST_ROW).addColumn(TEST_FAMILY, TEST_Q1));
464 }
465 }
466 }, user);
467 }
468
469 private Map<String, Permission> prepareCellPermissions(String[] users, Action... action) {
470 Map<String, Permission> perms = new HashMap<String, Permission>(2);
471 for (String user : users) {
472 perms.put(user, new Permission(action));
473 }
474 return perms;
475 }
476
477
478 @After
479 public void tearDown() throws Exception {
480
481 try {
482 TEST_UTIL.deleteTable(TEST_TABLE.getTableName());
483 } catch (TableNotFoundException ex) {
484
485 LOG.info("Test deleted table " + TEST_TABLE.getTableName());
486 }
487 assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
488 }
489 }