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