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.visibility;
19  
20  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
21  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
22  import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertNull;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  
30  import java.io.IOException;
31  import java.security.PrivilegedExceptionAction;
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.hbase.Cell;
37  import org.apache.hadoop.hbase.CellScanner;
38  import org.apache.hadoop.hbase.HBaseTestingUtility;
39  import org.apache.hadoop.hbase.HColumnDescriptor;
40  import org.apache.hadoop.hbase.HConstants;
41  import org.apache.hadoop.hbase.HTableDescriptor;
42  import org.apache.hadoop.hbase.MediumTests;
43  import org.apache.hadoop.hbase.TableName;
44  import org.apache.hadoop.hbase.client.Append;
45  import org.apache.hadoop.hbase.client.Get;
46  import org.apache.hadoop.hbase.client.HBaseAdmin;
47  import org.apache.hadoop.hbase.client.HTable;
48  import org.apache.hadoop.hbase.client.Increment;
49  import org.apache.hadoop.hbase.client.Put;
50  import org.apache.hadoop.hbase.client.Result;
51  import org.apache.hadoop.hbase.client.ResultScanner;
52  import org.apache.hadoop.hbase.client.RowMutations;
53  import org.apache.hadoop.hbase.client.Scan;
54  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
55  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
56  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
57  import org.apache.hadoop.hbase.regionserver.BloomType;
58  import org.apache.hadoop.hbase.regionserver.HRegion;
59  import org.apache.hadoop.hbase.regionserver.HRegionServer;
60  import org.apache.hadoop.hbase.security.User;
61  import org.apache.hadoop.hbase.util.Bytes;
62  import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
63  import org.junit.After;
64  import org.junit.AfterClass;
65  import org.junit.Assert;
66  import org.junit.BeforeClass;
67  import org.junit.Rule;
68  import org.junit.Test;
69  import org.junit.experimental.categories.Category;
70  import org.junit.rules.TestName;
71  
72  import com.google.protobuf.ByteString;
73  
74  /**
75   * Test class that tests the visibility labels
76   */
77  @Category(MediumTests.class)
78  public class TestVisibilityLabels {
79  
80    private static final String TOPSECRET = "topsecret";
81    private static final String PUBLIC = "public";
82    private static final String PRIVATE = "private";
83    private static final String CONFIDENTIAL = "confidential";
84    private static final String SECRET = "secret";
85    public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
86    private static final byte[] row1 = Bytes.toBytes("row1");
87    private static final byte[] row2 = Bytes.toBytes("row2");
88    private static final byte[] row3 = Bytes.toBytes("row3");
89    private static final byte[] row4 = Bytes.toBytes("row4");
90    private final static byte[] fam = Bytes.toBytes("info");
91    private final static byte[] qual = Bytes.toBytes("qual");
92    private final static byte[] value = Bytes.toBytes("value");
93    public static Configuration conf;
94  
95    private volatile boolean killedRS = false;
96    @Rule 
97    public final TestName TEST_NAME = new TestName();
98    public static User SUPERUSER;
99  
100   @BeforeClass
101   public static void setupBeforeClass() throws Exception {
102     // setup configuration
103     conf = TEST_UTIL.getConfiguration();
104     conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false);
105     conf.setBoolean("hbase.online.schema.update.enable", true);
106     conf.setInt("hfile.format.version", 3);
107     conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName());
108     conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName());
109     conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
110         ScanLabelGenerator.class);
111     conf.set("hbase.superuser", "admin");
112     TEST_UTIL.startMiniCluster(2);
113     SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
114 
115     // Wait for the labels table to become available
116     TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
117     addLabels();
118   }
119 
120   @AfterClass
121   public static void tearDownAfterClass() throws Exception {
122     TEST_UTIL.shutdownMiniCluster();
123   }
124 
125   @After
126   public void tearDown() throws Exception {
127     killedRS = false;
128   }
129 
130   @Test
131   public void testSimpleVisibilityLabels() throws Exception {
132     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
133     HTable table = createTableAndWriteDataWithLabels(tableName, SECRET + "|" + CONFIDENTIAL,
134         PRIVATE + "|" + CONFIDENTIAL);
135     try {
136       Scan s = new Scan();
137       s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE));
138       ResultScanner scanner = table.getScanner(s);
139       Result[] next = scanner.next(3);
140 
141       assertTrue(next.length == 2);
142       CellScanner cellScanner = next[0].cellScanner();
143       cellScanner.advance();
144       Cell current = cellScanner.current();
145       assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
146           current.getRowLength(), row1, 0, row1.length));
147       cellScanner = next[1].cellScanner();
148       cellScanner.advance();
149       current = cellScanner.current();
150       assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
151           current.getRowLength(), row2, 0, row2.length));
152     } finally {
153       if (table != null) {
154         table.close();
155       }
156     }
157   }
158 
159   @Test
160   public void testVisibilityLabelsWithComplexLabels() throws Exception {
161     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
162     HTable table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
163         + ")" + "&" + "!" + TOPSECRET, "(" + PRIVATE + "&" + CONFIDENTIAL + "&" + SECRET + ")", "("
164         + PRIVATE + "&" + CONFIDENTIAL + "&" + SECRET + ")", "(" + PRIVATE + "&" + CONFIDENTIAL
165         + "&" + SECRET + ")");
166     try {
167       Scan s = new Scan();
168       s.setAuthorizations(new Authorizations(TOPSECRET, CONFIDENTIAL, PRIVATE, PUBLIC, SECRET));
169       ResultScanner scanner = table.getScanner(s);
170       Result[] next = scanner.next(4);
171       assertEquals(3, next.length);
172       CellScanner cellScanner = next[0].cellScanner();
173       cellScanner.advance();
174       Cell current = cellScanner.current();
175       assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
176           current.getRowLength(), row2, 0, row2.length));
177       cellScanner = next[1].cellScanner();
178       cellScanner.advance();
179       current = cellScanner.current();
180       assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
181           current.getRowLength(), row3, 0, row3.length));
182       cellScanner = next[2].cellScanner();
183       cellScanner.advance();
184       current = cellScanner.current();
185       assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
186           current.getRowLength(), row4, 0, row4.length));
187     } finally {
188       if (table != null) {
189         table.close();
190       }
191     }
192   }
193 
194   @Test
195   public void testVisibilityLabelsThatDoesNotPassTheCriteria() throws Exception {
196     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
197     HTable table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
198         + ")", PRIVATE);
199     try {
200       Scan s = new Scan();
201       s.setAuthorizations(new Authorizations(PUBLIC));
202       ResultScanner scanner = table.getScanner(s);
203       Result[] next = scanner.next(3);
204       assertTrue(next.length == 0);
205     } finally {
206       if (table != null) {
207         table.close();
208       }
209     }
210   }
211 
212   @Test
213   public void testVisibilityLabelsInPutsThatDoesNotMatchAnyDefinedLabels() throws Exception {
214     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
215     try {
216       createTableAndWriteDataWithLabels(tableName, "SAMPLE_LABEL", "TEST");
217       fail("Should have failed with failed sanity check exception");
218     } catch (Exception e) {
219     }
220   }
221 
222   @Test
223   public void testVisibilityLabelsInScanThatDoesNotMatchAnyDefinedLabels() throws Exception {
224     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
225     HTable table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
226         + ")", PRIVATE);
227     try {
228       Scan s = new Scan();
229       s.setAuthorizations(new Authorizations("SAMPLE"));
230       ResultScanner scanner = table.getScanner(s);
231       Result[] next = scanner.next(3);
232       assertTrue(next.length == 0);
233     } finally {
234       if (table != null) {
235         table.close();
236       }
237     }
238   }
239 
240   @Test
241   public void testVisibilityLabelsWithGet() throws Exception {
242     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
243     HTable table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL + "&!"
244         + PRIVATE, SECRET + "&" + CONFIDENTIAL + "&" + PRIVATE);
245     try {
246       Get get = new Get(row1);
247       get.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
248       Result result = table.get(get);
249       assertTrue(!result.isEmpty());
250       Cell cell = result.getColumnLatestCell(fam, qual);
251       assertTrue(Bytes.equals(value, 0, value.length, cell.getValueArray(), cell.getValueOffset(),
252           cell.getValueLength()));
253     } finally {
254       if (table != null) {
255         table.close();
256       }
257     }
258   }
259 
260   @Test
261   public void testVisibilityLabelsOnKillingOfRSContainingLabelsTable() throws Exception {
262     List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
263         .getRegionServerThreads();
264     int liveRS = 0;
265     for (RegionServerThread rsThreads : regionServerThreads) {
266       if (!rsThreads.getRegionServer().isAborted()) {
267         liveRS++;
268       }
269     }
270     if (liveRS == 1) {
271       TEST_UTIL.getHBaseCluster().startRegionServer();
272     }
273     Thread t1 = new Thread() {
274       public void run() {
275         List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
276             .getRegionServerThreads();
277         for (RegionServerThread rsThread : regionServerThreads) {
278           List<HRegion> onlineRegions = rsThread.getRegionServer().getOnlineRegions(
279               LABELS_TABLE_NAME);
280           if (onlineRegions.size() > 0) {
281             rsThread.getRegionServer().abort("Aborting ");
282             killedRS = true;
283             break;
284           }
285         }
286       }
287 
288     };
289     t1.start();
290     final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
291     Thread t = new Thread() {
292       public void run() {
293         try {
294           while (!killedRS) {
295             Thread.sleep(1);
296           }
297           createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL + ")",
298               PRIVATE);
299         } catch (Exception e) {
300         }
301       }
302     };
303     t.start();
304     regionServerThreads = TEST_UTIL.getHBaseCluster().getRegionServerThreads();
305     while (!killedRS) {
306       Thread.sleep(10);
307     }
308     regionServerThreads = TEST_UTIL.getHBaseCluster().getRegionServerThreads();
309     for (RegionServerThread rsThread : regionServerThreads) {
310       while (true) {
311         if (!rsThread.getRegionServer().isAborted()) {
312           List<HRegion> onlineRegions = rsThread.getRegionServer().getOnlineRegions(
313               LABELS_TABLE_NAME);
314           if (onlineRegions.size() > 0) {
315             break;
316           } else {
317             Thread.sleep(10);
318           }
319         } else {
320           break;
321         }
322       }
323     }
324     TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
325     t.join();
326     HTable table = null;
327     try {
328       table = new HTable(TEST_UTIL.getConfiguration(), tableName);
329       Scan s = new Scan();
330       s.setAuthorizations(new Authorizations(SECRET));
331       ResultScanner scanner = table.getScanner(s);
332       Result[] next = scanner.next(3);
333       assertTrue(next.length == 1);
334     } finally {
335       if (table != null) {
336         table.close();
337       }
338     }
339   }
340 
341   @Test(timeout = 60 * 1000)
342   public void testVisibilityLabelsOnRSRestart() throws Exception {
343     final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
344     HTable table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
345         + ")", PRIVATE);
346     List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
347         .getRegionServerThreads();
348     for (RegionServerThread rsThread : regionServerThreads) {
349       rsThread.getRegionServer().abort("Aborting ");
350     }
351     // Start one new RS
352     RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer();
353     waitForLabelsRegionAvailability(rs.getRegionServer());
354     try {
355       Scan s = new Scan();
356       s.setAuthorizations(new Authorizations(SECRET));
357       ResultScanner scanner = table.getScanner(s);
358       Result[] next = scanner.next(3);
359       assertTrue(next.length == 1);
360     } finally {
361       if (table != null) {
362         table.close();
363       }
364     }
365   }
366 
367   @Test(timeout = 60 * 1000)
368   public void testAddVisibilityLabelsOnRSRestart() throws Exception {
369     List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
370         .getRegionServerThreads();
371     for (RegionServerThread rsThread : regionServerThreads) {
372       rsThread.getRegionServer().abort("Aborting ");
373     }
374     // Start one new RS
375     RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer();
376     waitForLabelsRegionAvailability(rs.getRegionServer());
377     PrivilegedExceptionAction<VisibilityLabelsResponse> action =
378         new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
379       public VisibilityLabelsResponse run() throws Exception {
380         String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, "ABC", "XYZ" };
381         try {
382           VisibilityClient.addLabels(conf, labels);
383         } catch (Throwable t) {
384           throw new IOException(t);
385         }
386         return null;
387       }
388     };
389     SUPERUSER.runAs(action);
390     // Scan the visibility label
391     Scan s = new Scan();
392     s.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
393     HTable ht = new HTable(conf, LABELS_TABLE_NAME.getName());
394     int i = 0;
395     try {
396       ResultScanner scanner = ht.getScanner(s);
397       while (true) {
398         Result next = scanner.next();
399         if (next == null) {
400           break;
401         }
402         i++;
403       }
404     } finally {
405       if (ht != null) {
406         ht.close();
407       }
408     }
409     // One label is the "system" label.
410     Assert.assertEquals("The count should be 8", 8, i);
411   }
412 
413   private void waitForLabelsRegionAvailability(HRegionServer regionServer) {
414     while (!regionServer.isOnline()) {
415       try {
416         Thread.sleep(10);
417       } catch (InterruptedException e) {
418       }
419     }
420     while (regionServer.getOnlineRegions(LABELS_TABLE_NAME).isEmpty()) {
421       try {
422         Thread.sleep(10);
423       } catch (InterruptedException e) {
424       }
425     }
426     HRegion labelsTableRegion = regionServer.getOnlineRegions(LABELS_TABLE_NAME).get(0);
427     while (labelsTableRegion.isRecovering()) {
428       try {
429         Thread.sleep(10);
430       } catch (InterruptedException e) {
431       }
432     }
433   }
434 
435   @Test
436   public void testVisibilityLabelsInGetThatDoesNotMatchAnyDefinedLabels() throws Exception {
437     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
438     HTable table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
439         + ")", PRIVATE);
440     try {
441       Get get = new Get(row1);
442       get.setAuthorizations(new Authorizations("SAMPLE"));
443       Result result = table.get(get);
444       assertTrue(result.isEmpty());
445     } finally {
446       if (table != null) {
447         table.close();
448       }
449     }
450   }
451 
452   @Test
453   public void testAddLabels() throws Throwable {
454     PrivilegedExceptionAction<VisibilityLabelsResponse> action = 
455         new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
456       public VisibilityLabelsResponse run() throws Exception {
457         String[] labels = { "L1", SECRET, "L2", "invalid~", "L3" };
458         VisibilityLabelsResponse response = null;
459         try {
460           response = VisibilityClient.addLabels(conf, labels);
461         } catch (Throwable e) {
462           fail("Should not have thrown exception");
463         }
464         List<RegionActionResult> resultList = response.getResultList();
465         assertEquals(5, resultList.size());
466         assertTrue(resultList.get(0).getException().getValue().isEmpty());
467         assertEquals("org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException",
468             resultList.get(1).getException().getName());
469         assertTrue(resultList.get(2).getException().getValue().isEmpty());
470         assertEquals("org.apache.hadoop.hbase.security.visibility.InvalidLabelException",
471             resultList.get(3).getException().getName());
472         assertTrue(resultList.get(4).getException().getValue().isEmpty());
473         return null;
474       }
475     };
476     SUPERUSER.runAs(action);
477   }
478 
479   @Test
480   public void testSetAndGetUserAuths() throws Throwable {
481     final String user = "user1";
482     PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
483       public Void run() throws Exception {
484         String[] auths = { SECRET, CONFIDENTIAL };
485         try {
486           VisibilityClient.setAuths(conf, auths, user);
487         } catch (Throwable e) {
488         }
489         return null;
490       }
491     };
492     SUPERUSER.runAs(action);
493     HTable ht = null;
494     try {
495       ht = new HTable(conf, LABELS_TABLE_NAME);
496       Scan scan = new Scan();
497       scan.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
498       ResultScanner scanner = ht.getScanner(scan);
499       Result result = null;
500       while ((result = scanner.next()) != null) {
501         Cell label = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
502         Cell userAuth = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
503         if (Bytes.equals(SECRET.getBytes(), 0, SECRET.getBytes().length, label.getValueArray(),
504             label.getValueOffset(), label.getValueLength())
505             || Bytes.equals(CONFIDENTIAL.getBytes(), 0, CONFIDENTIAL.getBytes().length,
506                 label.getValueArray(), label.getValueOffset(), label.getValueLength())) {
507           assertNotNull(userAuth);
508         } else {
509           assertNull(userAuth);
510         }
511       }
512     } finally {
513       if (ht != null) {
514         ht.close();
515       }
516     }
517 
518     action = new PrivilegedExceptionAction<Void>() {
519       public Void run() throws Exception {
520         GetAuthsResponse authsResponse = null;
521         try {
522           authsResponse = VisibilityClient.getAuths(conf, user);
523         } catch (Throwable e) {
524           fail("Should not have failed");
525         }
526         List<String> authsList = new ArrayList<String>();
527         for (ByteString authBS : authsResponse.getAuthList()) {
528           authsList.add(Bytes.toString(authBS.toByteArray()));
529         }
530         assertEquals(2, authsList.size());
531         assertTrue(authsList.contains(SECRET));
532         assertTrue(authsList.contains(CONFIDENTIAL));
533         return null;
534       }
535     };
536     SUPERUSER.runAs(action);
537 
538     // Try doing setAuths once again and there should not be any duplicates
539     action = new PrivilegedExceptionAction<Void>() {
540       public Void run() throws Exception {
541         String[] auths1 = { SECRET, CONFIDENTIAL };
542         GetAuthsResponse authsResponse = null;
543         try {
544           VisibilityClient.setAuths(conf, auths1, user);
545           try {
546             authsResponse = VisibilityClient.getAuths(conf, user);
547           } catch (Throwable e) {
548             fail("Should not have failed");
549           }
550         } catch (Throwable e) {
551         }
552         List<String> authsList = new ArrayList<String>();
553         for (ByteString authBS : authsResponse.getAuthList()) {
554           authsList.add(Bytes.toString(authBS.toByteArray()));
555         }
556         assertEquals(2, authsList.size());
557         assertTrue(authsList.contains(SECRET));
558         assertTrue(authsList.contains(CONFIDENTIAL));
559         return null;
560       }
561     };
562     SUPERUSER.runAs(action);
563   }
564 
565   @Test
566   public void testClearUserAuths() throws Throwable {
567     PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
568       public Void run() throws Exception {
569         String[] auths = { SECRET, CONFIDENTIAL, PRIVATE };
570         String user = "testUser";
571         try {
572           VisibilityClient.setAuths(conf, auths, user);
573         } catch (Throwable e) {
574           fail("Should not have failed");
575         }
576         // Removing the auths for SECRET and CONFIDENTIAL for the user.
577         // Passing a non existing auth also.
578         auths = new String[] { SECRET, PUBLIC, CONFIDENTIAL };
579         VisibilityLabelsResponse response = null;
580         try {
581           response = VisibilityClient.clearAuths(conf, auths, user);
582         } catch (Throwable e) {
583           fail("Should not have failed");
584         }
585         List<RegionActionResult> resultList = response.getResultList();
586         assertEquals(3, resultList.size());
587         assertTrue(resultList.get(0).getException().getValue().isEmpty());
588         assertEquals("org.apache.hadoop.hbase.security.visibility.InvalidLabelException",
589             resultList.get(1).getException().getName());
590         assertTrue(resultList.get(2).getException().getValue().isEmpty());
591         HTable ht = null;
592         try {
593           ht = new HTable(conf, LABELS_TABLE_NAME);
594           ResultScanner scanner = ht.getScanner(new Scan());
595           Result result = null;
596           while ((result = scanner.next()) != null) {
597             Cell label = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
598             Cell userAuth = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
599             if (Bytes.equals(PRIVATE.getBytes(), 0, PRIVATE.getBytes().length,
600                 label.getValueArray(), label.getValueOffset(), label.getValueLength())) {
601               assertNotNull(userAuth);
602             } else {
603               assertNull(userAuth);
604             }
605           }
606         } finally {
607           if (ht != null) {
608             ht.close();
609           }
610         }
611 
612         GetAuthsResponse authsResponse = null;
613         try {
614           authsResponse = VisibilityClient.getAuths(conf, user);
615         } catch (Throwable e) {
616           fail("Should not have failed");
617         }
618         List<String> authsList = new ArrayList<String>();
619         for (ByteString authBS : authsResponse.getAuthList()) {
620           authsList.add(Bytes.toString(authBS.toByteArray()));
621         }
622         assertEquals(1, authsList.size());
623         assertTrue(authsList.contains(PRIVATE));
624         return null;
625       }
626     };
627     SUPERUSER.runAs(action);
628   }
629 
630   @Test
631   public void testLabelsWithCheckAndPut() throws Throwable {
632     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
633     HTable table = null;
634     try {
635       table = TEST_UTIL.createTable(tableName, fam);
636       byte[] row1 = Bytes.toBytes("row1");
637       Put put = new Put(row1);
638       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
639       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
640       table.checkAndPut(row1, fam, qual, null, put);
641       byte[] row2 = Bytes.toBytes("row2");
642       put = new Put(row2);
643       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
644       put.setCellVisibility(new CellVisibility(SECRET));
645       table.checkAndPut(row2, fam, qual, null, put);
646       
647       Scan scan = new Scan();
648       scan.setAuthorizations(new Authorizations(SECRET));
649       ResultScanner scanner = table.getScanner(scan);
650       Result result = scanner.next();
651       assertTrue(!result.isEmpty());
652       assertTrue(Bytes.equals(row2, result.getRow()));
653       result = scanner.next();
654       assertNull(result);
655     } finally {
656       if (table != null) {
657         table.close();
658       }
659     }
660   }
661 
662   @Test
663   public void testLabelsWithIncrement() throws Throwable {
664     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
665     HTable table = null;
666     try {
667       table = TEST_UTIL.createTable(tableName, fam);
668       byte[] row1 = Bytes.toBytes("row1");
669       byte[] val = Bytes.toBytes(1L);
670       Put put = new Put(row1);
671       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, val);
672       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
673       table.put(put);
674       Get get = new Get(row1);
675       get.setAuthorizations(new Authorizations(SECRET));
676       Result result = table.get(get);
677       assertTrue(result.isEmpty());
678       table.incrementColumnValue(row1, fam, qual, 2L);
679       result = table.get(get);
680       assertTrue(result.isEmpty());
681       Increment increment = new Increment(row1);
682       increment.addColumn(fam, qual, 2L);
683       increment.setCellVisibility(new CellVisibility(SECRET));
684       table.increment(increment);
685       result = table.get(get);
686       assertTrue(!result.isEmpty());
687     } finally {
688       if (table != null) {
689         table.close();
690       }
691     }
692   }
693 
694   @Test
695   public void testLabelsWithAppend() throws Throwable {
696     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
697     HTable table = null;
698     try {
699       table = TEST_UTIL.createTable(tableName, fam);
700       byte[] row1 = Bytes.toBytes("row1");
701       byte[] val = Bytes.toBytes("a");
702       Put put = new Put(row1);
703       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, val);
704       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
705       table.put(put);
706       Get get = new Get(row1);
707       get.setAuthorizations(new Authorizations(SECRET));
708       Result result = table.get(get);
709       assertTrue(result.isEmpty());
710       Append append = new Append(row1);
711       append.add(fam, qual, Bytes.toBytes("b"));
712       table.append(append);
713       result = table.get(get);
714       assertTrue(result.isEmpty());
715       append = new Append(row1);
716       append.add(fam, qual, Bytes.toBytes("c"));
717       append.setCellVisibility(new CellVisibility(SECRET));
718       table.append(append);
719       result = table.get(get);
720       assertTrue(!result.isEmpty());
721     } finally {
722       if (table != null) {
723         table.close();
724       }
725     }
726   }
727 
728   @Test
729   public void testUserShouldNotDoDDLOpOnLabelsTable() throws Exception {
730     HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
731     try {
732       admin.disableTable(LABELS_TABLE_NAME);
733       fail("Lables table should not get disabled by user.");
734     } catch (Exception e) {
735     }
736     try {
737       admin.deleteTable(LABELS_TABLE_NAME);
738       fail("Lables table should not get disabled by user.");
739     } catch (Exception e) {
740     }
741     try {
742       HColumnDescriptor hcd = new HColumnDescriptor("testFamily");
743       admin.addColumn(LABELS_TABLE_NAME, hcd);
744       fail("Lables table should not get altered by user.");
745     } catch (Exception e) {
746     }
747     try {
748       admin.deleteColumn(LABELS_TABLE_NAME, VisibilityConstants.LABELS_TABLE_FAMILY);
749       fail("Lables table should not get altered by user.");
750     } catch (Exception e) {
751     }
752     try {
753       HColumnDescriptor hcd = new HColumnDescriptor(VisibilityConstants.LABELS_TABLE_FAMILY);
754       hcd.setBloomFilterType(BloomType.ROWCOL);
755       admin.modifyColumn(LABELS_TABLE_NAME, hcd);
756       fail("Lables table should not get altered by user.");
757     } catch (Exception e) {
758     }
759     try {
760       HTableDescriptor htd = new HTableDescriptor(LABELS_TABLE_NAME);
761       htd.addFamily(new HColumnDescriptor("f1"));
762       htd.addFamily(new HColumnDescriptor("f2"));
763       admin.modifyTable(LABELS_TABLE_NAME, htd);
764       fail("Lables table should not get altered by user.");
765     } catch (Exception e) {
766     }
767   }
768 
769   @Test
770   public void testMultipleVersions() throws Exception {
771     final byte[] r1 = Bytes.toBytes("row1");
772     final byte[] r2 = Bytes.toBytes("row2");
773     final byte[] v1 = Bytes.toBytes("100");
774     final byte[] v2 = Bytes.toBytes("101");
775     final byte[] fam2 = Bytes.toBytes("info2");
776     final byte[] qual2 = Bytes.toBytes("qual2");
777     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
778     HTableDescriptor desc = new HTableDescriptor(tableName);
779     HColumnDescriptor col = new HColumnDescriptor(fam);// Default max versions is 1.
780     desc.addFamily(col);
781     col = new HColumnDescriptor(fam2);
782     col.setMaxVersions(5);
783     desc.addFamily(col);
784     TEST_UTIL.getHBaseAdmin().createTable(desc);
785     HTable table = null;
786     try {
787       table = new HTable(TEST_UTIL.getConfiguration(), tableName);
788       Put put = new Put(r1);
789       put.add(fam, qual, 3l, v1);
790       put.add(fam, qual2, 3l, v1);
791       put.add(fam2, qual, 3l, v1);
792       put.add(fam2, qual2, 3l, v1);
793       put.setCellVisibility(new CellVisibility(SECRET));
794       table.put(put);
795       put = new Put(r1);
796       put.add(fam, qual, 4l, v2);
797       put.add(fam, qual2, 4l, v2);
798       put.add(fam2, qual, 4l, v2);
799       put.add(fam2, qual2, 4l, v2);
800       put.setCellVisibility(new CellVisibility(PRIVATE));
801       table.put(put);
802 
803       put = new Put(r2);
804       put.add(fam, qual, 3l, v1);
805       put.add(fam, qual2, 3l, v1);
806       put.add(fam2, qual, 3l, v1);
807       put.add(fam2, qual2, 3l, v1);
808       put.setCellVisibility(new CellVisibility(SECRET));
809       table.put(put);
810       put = new Put(r2);
811       put.add(fam, qual, 4l, v2);
812       put.add(fam, qual2, 4l, v2);
813       put.add(fam2, qual, 4l, v2);
814       put.add(fam2, qual2, 4l, v2);
815       put.setCellVisibility(new CellVisibility(SECRET));
816       table.put(put);
817 
818       Scan s = new Scan();
819       s.setMaxVersions(1);
820       s.setAuthorizations(new Authorizations(SECRET));
821       ResultScanner scanner = table.getScanner(s);
822       Result result = scanner.next();
823       assertTrue(Bytes.equals(r1, result.getRow()));
824       // for cf 'fam' max versions in HCD is 1. So the old version cells, which are having matching
825       // CellVisibility with Authorizations, should not get considered in the label evaluation at
826       // all.
827       assertNull(result.getColumnLatestCell(fam, qual));
828       assertNull(result.getColumnLatestCell(fam, qual2));
829       // for cf 'fam2' max versions in HCD is > 1. So we can consider the old version cells, which
830       // are having matching CellVisibility with Authorizations, in the label evaluation. It can
831       // just skip those recent versions for which visibility is not there as per the new version's
832       // CellVisibility. The old versions which are having visibility can be send back
833       Cell cell = result.getColumnLatestCell(fam2, qual);
834       assertNotNull(cell);
835       assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
836           cell.getValueLength()));
837       cell = result.getColumnLatestCell(fam2, qual2);
838       assertNotNull(cell);
839       assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
840           cell.getValueLength()));
841 
842       result = scanner.next();
843       assertTrue(Bytes.equals(r2, result.getRow()));
844       cell = result.getColumnLatestCell(fam, qual);
845       assertNotNull(cell);
846       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
847           cell.getValueLength()));
848       cell = result.getColumnLatestCell(fam, qual2);
849       assertNotNull(cell);
850       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
851           cell.getValueLength()));
852       cell = result.getColumnLatestCell(fam2, qual);
853       assertNotNull(cell);
854       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
855           cell.getValueLength()));
856       cell = result.getColumnLatestCell(fam2, qual2);
857       assertNotNull(cell);
858       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
859           cell.getValueLength()));
860     } finally {
861       if (table != null) {
862         table.close();
863       }
864     }
865   }
866 
867   @Test
868   public void testMutateRow() throws Exception {
869     final byte[] qual2 = Bytes.toBytes("qual2");
870     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
871     HTableDescriptor desc = new HTableDescriptor(tableName);
872     HColumnDescriptor col = new HColumnDescriptor(fam);
873     desc.addFamily(col);
874     TEST_UTIL.getHBaseAdmin().createTable(desc);
875     HTable table = new HTable(TEST_UTIL.getConfiguration(), tableName);
876     try {
877       Put p1 = new Put(row1);
878       p1.add(fam, qual, value);
879       p1.setCellVisibility(new CellVisibility(CONFIDENTIAL));
880 
881       Put p2 = new Put(row1);
882       p2.add(fam, qual2, value);
883       p2.setCellVisibility(new CellVisibility(SECRET));
884 
885       RowMutations rm = new RowMutations(row1);
886       rm.add(p1);
887       rm.add(p2);
888 
889       table.mutateRow(rm);
890 
891       Get get = new Get(row1);
892       get.setAuthorizations(new Authorizations(CONFIDENTIAL));
893       Result result = table.get(get);
894       assertTrue(result.containsColumn(fam, qual));
895       assertFalse(result.containsColumn(fam, qual2));
896 
897       get.setAuthorizations(new Authorizations(SECRET));
898       result = table.get(get);
899       assertFalse(result.containsColumn(fam, qual));
900       assertTrue(result.containsColumn(fam, qual2));
901     } finally {
902       table.close();
903     }
904   }
905 
906   private static HTable createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
907       throws Exception {
908     HTable table = null;
909     try {
910       table = TEST_UTIL.createTable(tableName, fam);
911       int i = 1;
912       List<Put> puts = new ArrayList<Put>();
913       for (String labelExp : labelExps) {
914         Put put = new Put(Bytes.toBytes("row" + i));
915         put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
916         put.setCellVisibility(new CellVisibility(labelExp));
917         puts.add(put);
918         i++;
919       }
920       table.put(puts);
921     } finally {
922       if (table != null) {
923         table.close();
924       }
925     }
926     return table;
927   }
928 
929   public static void addLabels() throws Exception {
930     PrivilegedExceptionAction<VisibilityLabelsResponse> action =
931         new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
932       public VisibilityLabelsResponse run() throws Exception {
933         String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE };
934         try {
935           VisibilityClient.addLabels(conf, labels);
936         } catch (Throwable t) {
937           throw new IOException(t);
938         }
939         return null;
940       }
941     };
942     SUPERUSER.runAs(action);
943   }
944 }