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