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