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