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