View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.security.access;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.fail;
22  
23  import java.security.PrivilegedExceptionAction;
24  import java.util.HashMap;
25  import java.util.Map;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.Coprocessor;
31  import org.apache.hadoop.hbase.HBaseTestingUtility;
32  import org.apache.hadoop.hbase.HColumnDescriptor;
33  import org.apache.hadoop.hbase.HTableDescriptor;
34  import org.apache.hadoop.hbase.testclassification.MediumTests;
35  import org.apache.hadoop.hbase.TableNotFoundException;
36  import org.apache.hadoop.hbase.client.Admin;
37  import org.apache.hadoop.hbase.client.Connection;
38  import org.apache.hadoop.hbase.client.ConnectionFactory;
39  import org.apache.hadoop.hbase.client.Delete;
40  import org.apache.hadoop.hbase.client.Get;
41  import org.apache.hadoop.hbase.client.Increment;
42  import org.apache.hadoop.hbase.client.Put;
43  import org.apache.hadoop.hbase.client.Table;
44  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
45  import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
46  import org.apache.hadoop.hbase.security.User;
47  import org.apache.hadoop.hbase.util.Bytes;
48  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
49  import org.apache.hadoop.hbase.util.TestTableName;
50  import org.apache.hadoop.hbase.util.Threads;
51  import org.apache.log4j.Level;
52  import org.apache.log4j.Logger;
53  import org.junit.After;
54  import org.junit.AfterClass;
55  import org.junit.Before;
56  import org.junit.BeforeClass;
57  import org.junit.Rule;
58  import org.junit.Test;
59  import org.junit.experimental.categories.Category;
60  
61  @Category(MediumTests.class)
62  public class TestCellACLWithMultipleVersions extends SecureTestUtil {
63    private static final Log LOG = LogFactory.getLog(TestCellACLWithMultipleVersions.class);
64  
65    static {
66      Logger.getLogger(AccessController.class).setLevel(Level.TRACE);
67      Logger.getLogger(AccessControlFilter.class).setLevel(Level.TRACE);
68      Logger.getLogger(TableAuthManager.class).setLevel(Level.TRACE);
69    }
70  
71    @Rule
72    public TestTableName TEST_TABLE = new TestTableName();
73    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
74    private static final byte[] TEST_FAMILY1 = Bytes.toBytes("f1");
75    private static final byte[] TEST_FAMILY2 = Bytes.toBytes("f2");
76    private static final byte[] TEST_ROW = Bytes.toBytes("cellpermtest");
77    private static final byte[] TEST_Q1 = Bytes.toBytes("q1");
78    private static final byte[] TEST_Q2 = Bytes.toBytes("q2");
79    private static final byte[] ZERO = Bytes.toBytes(0L);
80    private static final byte[] ONE = Bytes.toBytes(1L);
81    private static final byte[] TWO = Bytes.toBytes(2L);
82  
83    private static Configuration conf;
84  
85    private static User USER_OWNER;
86    private static User USER_OTHER;
87    private static User USER_OTHER2;
88  
89    @BeforeClass
90    public static void setupBeforeClass() throws Exception {
91      // setup configuration
92      conf = TEST_UTIL.getConfiguration();
93      // Enable security
94      enableSecurity(conf);
95      // Verify enableSecurity sets up what we require
96      verifyConfiguration(conf);
97  
98      // We expect 0.98 cell ACL semantics
99      conf.setBoolean(AccessControlConstants.CF_ATTRIBUTE_EARLY_OUT, false);
100 
101     TEST_UTIL.startMiniCluster();
102     MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster()
103         .getMasterCoprocessorHost();
104     cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
105     AccessController ac = (AccessController)
106       cpHost.findCoprocessor(AccessController.class.getName());
107     cpHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
108     RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
109         .getRegionServerCoprocessorHost();
110     rsHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
111 
112     // Wait for the ACL table to become available
113     TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
114 
115     // create a set of test users
116     USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
117     USER_OTHER = User.createUserForTesting(conf, "other", new String[0]);
118     USER_OTHER2 = User.createUserForTesting(conf, "other2", new String[0]);
119   }
120 
121   @AfterClass
122   public static void tearDownAfterClass() throws Exception {
123     TEST_UTIL.shutdownMiniCluster();
124   }
125 
126   @Before
127   public void setUp() throws Exception {
128     HTableDescriptor htd = new HTableDescriptor(TEST_TABLE.getTableName());
129     HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY1);
130     hcd.setMaxVersions(4);
131     htd.setOwner(USER_OWNER);
132     htd.addFamily(hcd);
133     hcd = new HColumnDescriptor(TEST_FAMILY2);
134     hcd.setMaxVersions(4);
135     htd.setOwner(USER_OWNER);
136     htd.addFamily(hcd);
137     // Create the test table (owner added to the _acl_ table)
138     try (Connection connection = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
139       try (Admin admin = connection.getAdmin()) {
140         admin.createTable(htd, new byte[][] { Bytes.toBytes("s") });
141       }
142     }
143     TEST_UTIL.waitTableEnabled(TEST_TABLE.getTableName());
144     LOG.info("Sleeping a second because of HBASE-12581");
145     Threads.sleep(1000);
146   }
147 
148   @Test
149   public void testCellPermissionwithVersions() throws Exception {
150     // store two sets of values, one store with a cell level ACL, and one
151     // without
152     verifyAllowed(new AccessTestAction() {
153       @Override
154       public Object run() throws Exception {
155         try(Connection connection = ConnectionFactory.createConnection(conf);
156             Table t = connection.getTable(TEST_TABLE.getTableName())) {
157           Put p;
158           // with ro ACL
159           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
160           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.WRITE));
161           t.put(p);
162           // with ro ACL
163           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
164           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.READ));
165           t.put(p);
166           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
167           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.WRITE));
168           t.put(p);
169           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
170           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.READ));
171           t.put(p);
172           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
173           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.WRITE));
174           t.put(p);
175         }
176         return null;
177       }
178     }, USER_OWNER);
179 
180     /* ---- Gets ---- */
181 
182     AccessTestAction getQ1 = new AccessTestAction() {
183       @Override
184       public Object run() throws Exception {
185         Get get = new Get(TEST_ROW);
186         get.setMaxVersions(10);
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 get2 = new AccessTestAction() {
195       @Override
196       public Object run() throws Exception {
197         Get get = new Get(TEST_ROW);
198         get.setMaxVersions(10);
199         try(Connection connection = ConnectionFactory.createConnection(conf);
200             Table t = connection.getTable(TEST_TABLE.getTableName())) {
201           return t.get(get).listCells();
202         }
203       }
204     };
205     // Confirm special read access set at cell level
206 
207     verifyAllowed(USER_OTHER, getQ1, 2);
208 
209     // store two sets of values, one store with a cell level ACL, and one
210     // without
211     verifyAllowed(new AccessTestAction() {
212       @Override
213       public Object run() throws Exception {
214         try(Connection connection = ConnectionFactory.createConnection(conf);
215             Table t = connection.getTable(TEST_TABLE.getTableName())) {
216           Put p;
217           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
218           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.WRITE));
219           t.put(p);
220           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
221           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.READ));
222           t.put(p);
223           p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
224           p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.WRITE));
225           t.put(p);
226         }
227         return null;
228       }
229     }, USER_OWNER);
230     // Confirm special read access set at cell level
231 
232     verifyAllowed(USER_OTHER, get2, 1);
233   }
234 
235   @Test
236   public void testCellPermissionsWithDeleteMutipleVersions() throws Exception {
237     // table/column/qualifier level permissions
238     final byte[] TEST_ROW1 = Bytes.toBytes("r1");
239     final byte[] TEST_ROW2 = Bytes.toBytes("r2");
240     final byte[] TEST_Q1 = Bytes.toBytes("q1");
241     final byte[] TEST_Q2 = Bytes.toBytes("q2");
242     final byte[] ZERO = Bytes.toBytes(0L);
243 
244     // additional test user
245     final User user1 = User.createUserForTesting(conf, "user1", new String[0]);
246     final User user2 = User.createUserForTesting(conf, "user2", new String[0]);
247 
248     verifyAllowed(new AccessTestAction() {
249       @Override
250       public Object run() throws Exception {
251         try (Connection connection = ConnectionFactory.createConnection(conf)) {
252           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
253             // with rw ACL for "user1"
254             Put p = new Put(TEST_ROW1);
255             p.add(TEST_FAMILY1, TEST_Q1, ZERO);
256             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
257             p.setACL(user1.getShortName(), new Permission(Permission.Action.READ,
258                 Permission.Action.WRITE));
259             t.put(p);
260             // with rw ACL for "user1"
261             p = new Put(TEST_ROW2);
262             p.add(TEST_FAMILY1, TEST_Q1, ZERO);
263             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
264             p.setACL(user1.getShortName(), new Permission(Permission.Action.READ,
265                 Permission.Action.WRITE));
266             t.put(p);
267           }
268         }
269         return null;
270       }
271     }, USER_OWNER);
272 
273     verifyAllowed(new AccessTestAction() {
274       @Override
275       public Object run() throws Exception {
276         try (Connection connection = ConnectionFactory.createConnection(conf)) {
277           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
278             // with rw ACL for "user1" and "user2"
279             Put p = new Put(TEST_ROW1);
280             p.add(TEST_FAMILY1, TEST_Q1, ZERO);
281             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
282             Map<String, Permission> perms = new HashMap<String, Permission>();
283             perms.put(user1.getShortName(), new Permission(Permission.Action.READ,
284                 Permission.Action.WRITE));
285             perms.put(user2.getShortName(), new Permission(Permission.Action.READ,
286                 Permission.Action.WRITE));
287             p.setACL(perms);
288             t.put(p);
289             // with rw ACL for "user1" and "user2"
290             p = new Put(TEST_ROW2);
291             p.add(TEST_FAMILY1, TEST_Q1, ZERO);
292             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
293             p.setACL(perms);
294             t.put(p);
295           }
296         }
297         return null;
298       }
299     }, user1);
300 
301     // user1 should be allowed to delete TEST_ROW1 as he is having write permission on both
302     // versions of the cells
303     user1.runAs(new PrivilegedExceptionAction<Void>() {
304       @Override
305       public Void run() throws Exception {
306         try (Connection connection = ConnectionFactory.createConnection(conf)) {
307           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
308             Delete d = new Delete(TEST_ROW1);
309             d.deleteColumns(TEST_FAMILY1, TEST_Q1);
310             d.deleteColumns(TEST_FAMILY1, TEST_Q2);
311             t.delete(d);
312           }
313         }
314         return null;
315       }
316     });
317     // user2 should not be allowed to delete TEST_ROW2 as he is having write permission only on one
318     // version of the cells.
319     user2.runAs(new PrivilegedExceptionAction<Void>() {
320       @Override
321       public Void run() throws Exception {
322         try (Connection connection = ConnectionFactory.createConnection(conf)) {
323           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
324             Delete d = new Delete(TEST_ROW2);
325             d.deleteColumns(TEST_FAMILY1, TEST_Q1);
326             d.deleteColumns(TEST_FAMILY1, TEST_Q2);
327             t.delete(d);
328             fail("user2 should not be allowed to delete the row");
329           } catch (Exception e) {
330 
331           }
332         }
333         return null;
334       }
335     });
336     // user1 should be allowed to delete the cf. (All data under cf for a row)
337     user1.runAs(new PrivilegedExceptionAction<Void>() {
338       @Override
339       public Void run() throws Exception {
340         try (Connection connection = ConnectionFactory.createConnection(conf)) {
341           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
342             Delete d = new Delete(TEST_ROW2);
343             d.deleteFamily(TEST_FAMILY1);
344             t.delete(d);
345           }
346         }
347         return null;
348       }
349     });
350   }
351 
352 
353   @Test
354   public void testDeleteWithFutureTimestamp() throws Exception {
355     // Store two values, one in the future
356 
357     verifyAllowed(new AccessTestAction() {
358       @Override
359       public Object run() throws Exception {
360         try (Connection connection = ConnectionFactory.createConnection(conf)) {
361           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
362             // Store a read write ACL without a timestamp, server will use current time
363             Put p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q2, ONE);
364             p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.READ,
365                 Permission.Action.WRITE));
366             t.put(p);
367             LOG.info("Stored at current time");
368             // Store read only ACL at a future time
369             p = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1,
370                 EnvironmentEdgeManager.currentTime() + 1000000, ZERO);
371             p.setACL(USER_OTHER.getShortName(), new Permission(Permission.Action.READ));
372             t.put(p);
373           }
374         }
375         return null;
376       }
377     }, USER_OWNER);
378 
379     // Confirm stores are visible
380 
381     AccessTestAction getQ1 = new AccessTestAction() {
382       @Override
383       public Object run() throws Exception {
384         Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY1, TEST_Q1);
385         try (Connection connection = ConnectionFactory.createConnection(conf)) {
386           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
387             return t.get(get).listCells();
388           }
389         }
390       }
391     };
392 
393     AccessTestAction getQ2 = new AccessTestAction() {
394       @Override
395       public Object run() throws Exception {
396         Get get = new Get(TEST_ROW).addColumn(TEST_FAMILY1, TEST_Q2);
397         try (Connection connection = ConnectionFactory.createConnection(conf)) {
398           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
399             return t.get(get).listCells();
400           }
401         }
402       }
403     };
404 
405     verifyAllowed(getQ1, USER_OWNER, USER_OTHER);
406     verifyAllowed(getQ2, USER_OWNER, USER_OTHER);
407 
408 
409     // Issue a DELETE for the family, should succeed because the future ACL is
410     // not considered
411 
412     AccessTestAction deleteFamily = new AccessTestAction() {
413       @Override
414       public Object run() throws Exception {
415         Delete delete = new Delete(TEST_ROW).deleteFamily(TEST_FAMILY1);
416         try (Connection connection = ConnectionFactory.createConnection(conf)) {
417           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
418             t.delete(delete);
419           }
420         }
421         return null;
422       }
423     };
424 
425     verifyAllowed(deleteFamily, USER_OTHER);
426 
427     // The future put should still exist
428     
429     verifyAllowed(getQ1, USER_OWNER, USER_OTHER);
430     
431     // The other put should be covered by the tombstone
432 
433     verifyIfNull(getQ2, USER_OTHER);
434   }
435 
436   @Test
437   public void testCellPermissionsWithDeleteWithUserTs() throws Exception {
438     USER_OWNER.runAs(new AccessTestAction() {
439       @Override
440       public Object run() throws Exception {
441         try (Connection connection = ConnectionFactory.createConnection(conf)) {
442           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
443             // This version (TS = 123) with rw ACL for USER_OTHER and USER_OTHER2
444             Put p = new Put(TEST_ROW);
445             p.add(TEST_FAMILY1, TEST_Q1, 123L, ZERO);
446             p.add(TEST_FAMILY1, TEST_Q2, 123L, ZERO);
447             Map<String, Permission> perms = new HashMap<String, Permission>();
448             perms.put(USER_OTHER.getShortName(), new Permission(Permission.Action.READ,
449                 Permission.Action.WRITE));
450             perms.put(USER_OTHER2.getShortName(), new Permission(Permission.Action.READ,
451                 Permission.Action.WRITE));
452             p.setACL(perms);
453             t.put(p);
454 
455             // This version (TS = 125) with rw ACL for USER_OTHER
456             p = new Put(TEST_ROW);
457             p.add(TEST_FAMILY1, TEST_Q1, 125L, ONE);
458             p.add(TEST_FAMILY1, TEST_Q2, 125L, ONE);
459             perms = new HashMap<String, Permission>();
460             perms.put(USER_OTHER.getShortName(), new Permission(Permission.Action.READ,
461                 Permission.Action.WRITE));
462             p.setACL(perms);
463             t.put(p);
464 
465             // This version (TS = 127) with rw ACL for USER_OTHER
466             p = new Put(TEST_ROW);
467             p.add(TEST_FAMILY1, TEST_Q1, 127L, TWO);
468             p.add(TEST_FAMILY1, TEST_Q2, 127L, TWO);
469             perms = new HashMap<String, Permission>();
470             perms.put(USER_OTHER.getShortName(), new Permission(Permission.Action.READ,
471                 Permission.Action.WRITE));
472             p.setACL(perms);
473             t.put(p);
474 
475             return null;
476           }
477         }
478       }
479     });
480 
481     // USER_OTHER2 should be allowed to delete the column f1:q1 versions older than TS 124L
482     USER_OTHER2.runAs(new AccessTestAction() {
483       @Override
484       public Object run() throws Exception {
485         try (Connection connection = ConnectionFactory.createConnection(conf)) {
486           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
487             Delete d = new Delete(TEST_ROW, 124L);
488             d.deleteColumns(TEST_FAMILY1, TEST_Q1);
489             t.delete(d);
490           }
491         }
492         return null;
493       }
494     });
495 
496     // USER_OTHER2 should be allowed to delete the column f1:q2 versions older than TS 124L
497     USER_OTHER2.runAs(new AccessTestAction() {
498       @Override
499       public Object run() throws Exception {
500         try (Connection connection = ConnectionFactory.createConnection(conf)) {
501           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
502             Delete d = new Delete(TEST_ROW);
503             d.deleteColumns(TEST_FAMILY1, TEST_Q2, 124L);
504             t.delete(d);
505           }
506         }
507         return null;
508       }
509     });
510   }
511 
512   @Test
513   public void testCellPermissionsWithDeleteExactVersion() throws Exception {
514     final byte[] TEST_ROW1 = Bytes.toBytes("r1");
515     final byte[] TEST_Q1 = Bytes.toBytes("q1");
516     final byte[] TEST_Q2 = Bytes.toBytes("q2");
517     final byte[] ZERO = Bytes.toBytes(0L);
518 
519     final User user1 = User.createUserForTesting(conf, "user1", new String[0]);
520     final User user2 = User.createUserForTesting(conf, "user2", new String[0]);
521 
522     verifyAllowed(new AccessTestAction() {
523       @Override
524       public Object run() throws Exception {
525         try (Connection connection = ConnectionFactory.createConnection(conf)) {
526           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
527             Map<String, Permission> permsU1andOwner = new HashMap<String, Permission>();
528             permsU1andOwner.put(user1.getShortName(), new Permission(Permission.Action.READ,
529                 Permission.Action.WRITE));
530             permsU1andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
531                 Permission.Action.WRITE));
532             Map<String, Permission> permsU2andOwner = new HashMap<String, Permission>();
533             permsU2andOwner.put(user2.getShortName(), new Permission(Permission.Action.READ,
534                 Permission.Action.WRITE));
535             permsU2andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
536                 Permission.Action.WRITE));
537             Put p = new Put(TEST_ROW1);
538             p.add(TEST_FAMILY1, TEST_Q1, 123, ZERO);
539             p.setACL(permsU1andOwner);
540             t.put(p);
541             p = new Put(TEST_ROW1);
542             p.add(TEST_FAMILY1, TEST_Q2, 123, ZERO);
543             p.setACL(permsU2andOwner);
544             t.put(p);
545             p = new Put(TEST_ROW1);
546             p.add(TEST_FAMILY2, TEST_Q1, 123, ZERO);
547             p.add(TEST_FAMILY2, TEST_Q2, 123, ZERO);
548             p.setACL(permsU2andOwner);
549             t.put(p);
550 
551             p = new Put(TEST_ROW1);
552             p.add(TEST_FAMILY2, TEST_Q1, 125, ZERO);
553             p.add(TEST_FAMILY2, TEST_Q2, 125, ZERO);
554             p.setACL(permsU1andOwner);
555             t.put(p);
556 
557             p = new Put(TEST_ROW1);
558             p.add(TEST_FAMILY1, TEST_Q1, 127, ZERO);
559             p.setACL(permsU2andOwner);
560             t.put(p);
561             p = new Put(TEST_ROW1);
562             p.add(TEST_FAMILY1, TEST_Q2, 127, ZERO);
563             p.setACL(permsU1andOwner);
564             t.put(p);
565             p = new Put(TEST_ROW1);
566             p.add(TEST_FAMILY2, TEST_Q1, 129, ZERO);
567             p.add(TEST_FAMILY2, TEST_Q2, 129, ZERO);
568             p.setACL(permsU1andOwner);
569             t.put(p);
570           }
571         }
572         return null;
573       }
574     }, USER_OWNER);
575 
576     // user1 should be allowed to delete TEST_ROW1 as he is having write permission on both
577     // versions of the cells
578     user1.runAs(new PrivilegedExceptionAction<Void>() {
579       @Override
580       public Void run() throws Exception {
581         try (Connection connection = ConnectionFactory.createConnection(conf)) {
582           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
583             Delete d = new Delete(TEST_ROW1);
584             d.deleteColumn(TEST_FAMILY1, TEST_Q1, 123);
585             d.deleteColumn(TEST_FAMILY1, TEST_Q2);
586             d.deleteFamilyVersion(TEST_FAMILY2, 125);
587             t.delete(d);
588           }
589         }
590         return null;
591       }
592     });
593 
594     user2.runAs(new PrivilegedExceptionAction<Void>() {
595       @Override
596       public Void run() throws Exception {
597         try (Connection connection = ConnectionFactory.createConnection(conf)) {
598           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
599             Delete d = new Delete(TEST_ROW1, 127);
600             d.deleteColumns(TEST_FAMILY1, TEST_Q1);
601             d.deleteColumns(TEST_FAMILY1, TEST_Q2);
602             d.deleteFamily(TEST_FAMILY2, 129);
603             t.delete(d);
604             fail("user2 can not do the delete");
605           } catch (Exception e) {
606 
607           }
608         }
609         return null;
610       }
611     });
612   }
613 
614   @Test
615   public void testCellPermissionsForIncrementWithMultipleVersions() throws Exception {
616     final byte[] TEST_ROW1 = Bytes.toBytes("r1");
617     final byte[] TEST_Q1 = Bytes.toBytes("q1");
618     final byte[] TEST_Q2 = Bytes.toBytes("q2");
619     final byte[] ZERO = Bytes.toBytes(0L);
620 
621     final User user1 = User.createUserForTesting(conf, "user1", new String[0]);
622     final User user2 = User.createUserForTesting(conf, "user2", new String[0]);
623 
624     verifyAllowed(new AccessTestAction() {
625       @Override
626       public Object run() throws Exception {
627         try (Connection connection = ConnectionFactory.createConnection(conf)) {
628           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
629             Map<String, Permission> permsU1andOwner = new HashMap<String, Permission>();
630             permsU1andOwner.put(user1.getShortName(), new Permission(Permission.Action.READ,
631                 Permission.Action.WRITE));
632             permsU1andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
633                 Permission.Action.WRITE));
634             Map<String, Permission> permsU2andOwner = new HashMap<String, Permission>();
635             permsU2andOwner.put(user2.getShortName(), new Permission(Permission.Action.READ,
636                 Permission.Action.WRITE));
637             permsU2andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
638                 Permission.Action.WRITE));
639             Put p = new Put(TEST_ROW1);
640             p.add(TEST_FAMILY1, TEST_Q1, 123, ZERO);
641             p.setACL(permsU1andOwner);
642             t.put(p);
643             p = new Put(TEST_ROW1);
644             p.add(TEST_FAMILY1, TEST_Q2, 123, ZERO);
645             p.setACL(permsU2andOwner);
646             t.put(p);
647 
648             p = new Put(TEST_ROW1);
649             p.add(TEST_FAMILY1, TEST_Q1, 127, ZERO);
650             p.setACL(permsU2andOwner);
651             t.put(p);
652             p = new Put(TEST_ROW1);
653             p.add(TEST_FAMILY1, TEST_Q2, 127, ZERO);
654             p.setACL(permsU1andOwner);
655             t.put(p);
656           }
657         }
658         return null;
659       }
660     }, USER_OWNER);
661 
662     // Increment considers the TimeRange set on it.
663     user1.runAs(new PrivilegedExceptionAction<Void>() {
664       @Override
665       public Void run() throws Exception {
666         try (Connection connection = ConnectionFactory.createConnection(conf)) {
667           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
668             Increment inc = new Increment(TEST_ROW1);
669             inc.setTimeRange(0, 123);
670             inc.addColumn(TEST_FAMILY1, TEST_Q1, 2L);
671             t.increment(inc);
672             t.incrementColumnValue(TEST_ROW1, TEST_FAMILY1, TEST_Q2, 1L);
673           }
674         }
675         return null;
676       }
677     });
678 
679     user2.runAs(new PrivilegedExceptionAction<Void>() {
680       @Override
681       public Void run() throws Exception {
682         try (Connection connection = ConnectionFactory.createConnection(conf)) {
683           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
684             Increment inc = new Increment(TEST_ROW1);
685             inc.setTimeRange(0, 127);
686             inc.addColumn(TEST_FAMILY1, TEST_Q2, 2L);
687             t.increment(inc);
688             fail();
689           } catch (Exception e) {
690 
691           }
692         }
693         return null;
694       }
695     });
696   }
697 
698   @Test
699   public void testCellPermissionsForPutWithMultipleVersions() throws Exception {
700     final byte[] TEST_ROW1 = Bytes.toBytes("r1");
701     final byte[] TEST_Q1 = Bytes.toBytes("q1");
702     final byte[] TEST_Q2 = Bytes.toBytes("q2");
703     final byte[] ZERO = Bytes.toBytes(0L);
704 
705     final User user1 = User.createUserForTesting(conf, "user1", new String[0]);
706     final User user2 = User.createUserForTesting(conf, "user2", new String[0]);
707 
708     verifyAllowed(new AccessTestAction() {
709       @Override
710       public Object run() throws Exception {
711         try (Connection connection = ConnectionFactory.createConnection(conf)) {
712           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
713             Map<String, Permission> permsU1andOwner = new HashMap<String, Permission>();
714             permsU1andOwner.put(user1.getShortName(), new Permission(Permission.Action.READ,
715                 Permission.Action.WRITE));
716             permsU1andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
717                 Permission.Action.WRITE));
718             Map<String, Permission> permsU2andOwner = new HashMap<String, Permission>();
719             permsU2andOwner.put(user2.getShortName(), new Permission(Permission.Action.READ,
720                 Permission.Action.WRITE));
721             permsU2andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
722                 Permission.Action.WRITE));
723             Put p = new Put(TEST_ROW1);
724             p.add(TEST_FAMILY1, TEST_Q1, 123, ZERO);
725             p.setACL(permsU1andOwner);
726             t.put(p);
727             p = new Put(TEST_ROW1);
728             p.add(TEST_FAMILY1, TEST_Q2, 123, ZERO);
729             p.setACL(permsU2andOwner);
730             t.put(p);
731 
732             p = new Put(TEST_ROW1);
733             p.add(TEST_FAMILY1, TEST_Q1, 127, ZERO);
734             p.setACL(permsU2andOwner);
735             t.put(p);
736             p = new Put(TEST_ROW1);
737             p.add(TEST_FAMILY1, TEST_Q2, 127, ZERO);
738             p.setACL(permsU1andOwner);
739             t.put(p);
740           }
741         }
742         return null;
743       }
744     }, USER_OWNER);
745 
746     // new Put with TEST_Q1 column having TS=125. This covers old cell with TS 123 and user1 is
747     // having RW permission. While TEST_Q2 is with latest TS and so it covers old cell with TS 127.
748     // User1 is having RW permission on that too.
749     user1.runAs(new PrivilegedExceptionAction<Void>() {
750       @Override
751       public Void run() throws Exception {
752         try (Connection connection = ConnectionFactory.createConnection(conf)) {
753           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
754             Put p = new Put(TEST_ROW1);
755             p.add(TEST_FAMILY1, TEST_Q1, 125, ZERO);
756             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
757             p.setACL(user2.getShortName(), new Permission(Permission.Action.READ,
758                 Permission.Action.WRITE));
759             t.put(p);
760           }
761         }
762         return null;
763       }
764     });
765 
766     // Should be denied.
767     user2.runAs(new PrivilegedExceptionAction<Void>() {
768       @Override
769       public Void run() throws Exception {
770         try (Connection connection = ConnectionFactory.createConnection(conf)) {
771           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
772             Put p = new Put(TEST_ROW1);
773             // column Q1 covers version at 123 fr which user2 do not have permission
774             p.add(TEST_FAMILY1, TEST_Q1, 124, ZERO);
775             p.add(TEST_FAMILY1, TEST_Q2, ZERO);
776             t.put(p);
777             fail();
778           } catch (Exception e) {
779 
780           }
781         }
782         return null;
783       }
784     });
785   }
786 
787   @Test
788   public void testCellPermissionsForCheckAndDelete() throws Exception {
789     final byte[] TEST_ROW1 = Bytes.toBytes("r1");
790     final byte[] ZERO = Bytes.toBytes(0L);
791 
792     final User user1 = User.createUserForTesting(conf, "user1", new String[0]);
793     final User user2 = User.createUserForTesting(conf, "user2", new String[0]);
794     
795     verifyAllowed(new AccessTestAction() {
796       @Override
797       public Object run() throws Exception {
798         try (Connection connection = ConnectionFactory.createConnection(conf)) {
799           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
800             Map<String, Permission> permsU1andOwner = new HashMap<String, Permission>();
801             permsU1andOwner.put(user1.getShortName(), new Permission(Permission.Action.READ,
802                 Permission.Action.WRITE));
803             permsU1andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
804                 Permission.Action.WRITE));
805             Map<String, Permission> permsU1andU2andOwner = new HashMap<String, Permission>();
806             permsU1andU2andOwner.put(user1.getShortName(), new Permission(Permission.Action.READ,
807                 Permission.Action.WRITE));
808             permsU1andU2andOwner.put(user2.getShortName(), new Permission(Permission.Action.READ,
809                 Permission.Action.WRITE));
810             permsU1andU2andOwner.put(USER_OWNER.getShortName(), new Permission(Permission.Action.READ,
811                 Permission.Action.WRITE));
812             Map<String, Permission> permsU1andU2 = new HashMap<String, Permission>();
813             permsU1andU2.put(user1.getShortName(), new Permission(Permission.Action.READ,
814                 Permission.Action.WRITE));
815             permsU1andU2.put(user2.getShortName(), new Permission(Permission.Action.READ,
816                 Permission.Action.WRITE));
817 
818             Put p = new Put(TEST_ROW1);
819             p.add(TEST_FAMILY1, TEST_Q1, 120, ZERO);
820             p.add(TEST_FAMILY1, TEST_Q2, 120, ZERO);
821             p.setACL(permsU1andU2andOwner);
822             t.put(p);
823 
824             p = new Put(TEST_ROW1);
825             p.add(TEST_FAMILY1, TEST_Q1, 123, ZERO);
826             p.add(TEST_FAMILY1, TEST_Q2, 123, ZERO);
827             p.setACL(permsU1andOwner);
828             t.put(p);
829 
830             p = new Put(TEST_ROW1);
831             p.add(TEST_FAMILY1, TEST_Q1, 127, ZERO);
832             p.setACL(permsU1andU2);
833             t.put(p);
834 
835             p = new Put(TEST_ROW1);
836             p.add(TEST_FAMILY1, TEST_Q2, 127, ZERO);
837             p.setACL(user2.getShortName(), new Permission(Permission.Action.READ));
838             t.put(p);
839           }
840         }
841         return null;
842       }
843     }, USER_OWNER);
844 
845     // user1 should be allowed to do the checkAndDelete. user1 having read permission on the latest
846     // version cell and write permission on all versions
847     user1.runAs(new PrivilegedExceptionAction<Void>() {
848       @Override
849       public Void run() throws Exception {
850         try (Connection connection = ConnectionFactory.createConnection(conf)) {
851           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
852             Delete d = new Delete(TEST_ROW1);
853             d.deleteColumns(TEST_FAMILY1, TEST_Q1, 120);
854             t.checkAndDelete(TEST_ROW1, TEST_FAMILY1, TEST_Q1, ZERO, d);
855           }
856         }
857         return null;
858       }
859     });
860     // user2 shouldn't be allowed to do the checkAndDelete. user2 having RW permission on the latest
861     // version cell but not on cell version TS=123
862     user2.runAs(new PrivilegedExceptionAction<Void>() {
863       @Override
864       public Void run() throws Exception {
865         try (Connection connection = ConnectionFactory.createConnection(conf)) {
866           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
867             Delete d = new Delete(TEST_ROW1);
868             d.deleteColumns(TEST_FAMILY1, TEST_Q1);
869             t.checkAndDelete(TEST_ROW1, TEST_FAMILY1, TEST_Q1, ZERO, d);
870             fail("user2 should not be allowed to do checkAndDelete");
871           } catch (Exception e) {
872           }
873         }
874         return null;
875       }
876     });
877     // user2 should be allowed to do the checkAndDelete when delete tries to delete the old version
878     // TS=120. user2 having R permission on the latest version(no W permission) cell
879     // and W permission on cell version TS=120.
880     user2.runAs(new PrivilegedExceptionAction<Void>() {
881       @Override
882       public Void run() throws Exception {
883         try (Connection connection = ConnectionFactory.createConnection(conf)) {
884           try (Table t = connection.getTable(TEST_TABLE.getTableName())) {
885             Delete d = new Delete(TEST_ROW1);
886             d.deleteColumn(TEST_FAMILY1, TEST_Q2, 120);
887             t.checkAndDelete(TEST_ROW1, TEST_FAMILY1, TEST_Q2, ZERO, d);
888           }
889         }
890         return null;
891       }
892     });
893   }
894 
895   @After
896   public void tearDown() throws Exception {
897     // Clean the _acl_ table
898     try {
899       TEST_UTIL.deleteTable(TEST_TABLE.getTableName());
900     } catch (TableNotFoundException ex) {
901       // Test deleted the table, no problem
902       LOG.info("Test deleted table " + TEST_TABLE.getTableName());
903     }
904     assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
905   }
906 }