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         assertNotNull(authsResponse);
487         List<String> authsList = new ArrayList<String>();
488         for (ByteString authBS : authsResponse.getAuthList()) {
489           authsList.add(Bytes.toString(authBS.toByteArray()));
490         }
491         assertEquals(2, authsList.size());
492         assertTrue(authsList.contains(SECRET));
493         assertTrue(authsList.contains(CONFIDENTIAL));
494         return null;
495       }
496     };
497     SUPERUSER.runAs(action);
498   }
499 
500   protected List<String> extractAuths(String user, List<Result> results) {
501     List<String> auths = new ArrayList<String>();
502     for (Result result : results) {
503       Cell labelCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
504       Cell userAuthCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
505       if (userAuthCell != null) {
506         auths.add(Bytes.toString(labelCell.getValueArray(), labelCell.getValueOffset(),
507             labelCell.getValueLength()));
508       }
509     }
510     return auths;
511   }
512 
513   @Test
514   public void testClearUserAuths() throws Throwable {
515     PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
516       public Void run() throws Exception {
517         String[] auths = { SECRET, CONFIDENTIAL, PRIVATE };
518         String user = "testUser";
519         try (Connection conn = ConnectionFactory.createConnection(conf)) {
520           VisibilityClient.setAuths(conn, auths, user);
521         } catch (Throwable e) {
522           fail("Should not have failed");
523         }
524         // Removing the auths for SECRET and CONFIDENTIAL for the user.
525         // Passing a non existing auth also.
526         auths = new String[] { SECRET, PUBLIC, CONFIDENTIAL };
527         VisibilityLabelsResponse response = null;
528         try (Connection conn = ConnectionFactory.createConnection(conf)) {
529           response = VisibilityClient.clearAuths(conn, auths, user);
530         } catch (Throwable e) {
531           fail("Should not have failed");
532         }
533         List<RegionActionResult> resultList = response.getResultList();
534         assertEquals(3, resultList.size());
535         assertTrue(resultList.get(0).getException().getValue().isEmpty());
536         assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException",
537             resultList.get(1).getException().getName());
538         assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray())
539             .contains(
540                 "org.apache.hadoop.hbase.security.visibility.InvalidLabelException: "
541                     + "Label 'public' is not set for the user testUser"));
542         assertTrue(resultList.get(2).getException().getValue().isEmpty());
543         try (Connection connection = ConnectionFactory.createConnection(conf);
544              Table ht = connection.getTable(LABELS_TABLE_NAME)) {
545           ResultScanner scanner = ht.getScanner(new Scan());
546           Result result = null;
547           List<Result> results = new ArrayList<Result>();
548           while ((result = scanner.next()) != null) {
549             results.add(result);
550           }
551           List<String> curAuths = extractAuths(user, results);
552           assertTrue(curAuths.contains(PRIVATE));
553           assertEquals(1, curAuths.size());
554         }
555 
556         GetAuthsResponse authsResponse = null;
557         try (Connection conn = ConnectionFactory.createConnection(conf)) {
558           authsResponse = VisibilityClient.getAuths(conn, user);
559         } catch (Throwable e) {
560           fail("Should not have failed");
561         }
562         List<String> authsList = new ArrayList<String>();
563         for (ByteString authBS : authsResponse.getAuthList()) {
564           authsList.add(Bytes.toString(authBS.toByteArray()));
565         }
566         assertEquals(1, authsList.size());
567         assertTrue(authsList.contains(PRIVATE));
568         return null;
569       }
570     };
571     SUPERUSER.runAs(action);
572   }
573 
574   @Test
575   public void testLabelsWithCheckAndPut() throws Throwable {
576     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
577     try (Table table = TEST_UTIL.createTable(tableName, fam)) {
578       byte[] row1 = Bytes.toBytes("row1");
579       Put put = new Put(row1);
580       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
581       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
582       table.checkAndPut(row1, fam, qual, null, put);
583       byte[] row2 = Bytes.toBytes("row2");
584       put = new Put(row2);
585       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
586       put.setCellVisibility(new CellVisibility(SECRET));
587       table.checkAndPut(row2, fam, qual, null, put);
588       
589       Scan scan = new Scan();
590       scan.setAuthorizations(new Authorizations(SECRET));
591       ResultScanner scanner = table.getScanner(scan);
592       Result result = scanner.next();
593       assertTrue(!result.isEmpty());
594       assertTrue(Bytes.equals(row2, result.getRow()));
595       result = scanner.next();
596       assertNull(result);
597     }
598   }
599 
600   @Test
601   public void testLabelsWithIncrement() throws Throwable {
602     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
603     try (Table table = TEST_UTIL.createTable(tableName, fam)) {
604       byte[] row1 = Bytes.toBytes("row1");
605       byte[] val = Bytes.toBytes(1L);
606       Put put = new Put(row1);
607       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, val);
608       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
609       table.put(put);
610       Get get = new Get(row1);
611       get.setAuthorizations(new Authorizations(SECRET));
612       Result result = table.get(get);
613       assertTrue(result.isEmpty());
614       table.incrementColumnValue(row1, fam, qual, 2L);
615       result = table.get(get);
616       assertTrue(result.isEmpty());
617       Increment increment = new Increment(row1);
618       increment.addColumn(fam, qual, 2L);
619       increment.setCellVisibility(new CellVisibility(SECRET));
620       table.increment(increment);
621       result = table.get(get);
622       assertTrue(!result.isEmpty());
623     }
624   }
625 
626   @Test
627   public void testLabelsWithAppend() throws Throwable {
628     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
629     try (Table table = TEST_UTIL.createTable(tableName, fam);) {
630       byte[] row1 = Bytes.toBytes("row1");
631       byte[] val = Bytes.toBytes("a");
632       Put put = new Put(row1);
633       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, val);
634       put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
635       table.put(put);
636       Get get = new Get(row1);
637       get.setAuthorizations(new Authorizations(SECRET));
638       Result result = table.get(get);
639       assertTrue(result.isEmpty());
640       Append append = new Append(row1);
641       append.add(fam, qual, Bytes.toBytes("b"));
642       table.append(append);
643       result = table.get(get);
644       assertTrue(result.isEmpty());
645       append = new Append(row1);
646       append.add(fam, qual, Bytes.toBytes("c"));
647       append.setCellVisibility(new CellVisibility(SECRET));
648       table.append(append);
649       result = table.get(get);
650       assertTrue(!result.isEmpty());
651     }
652   }
653 
654   @Test
655   public void testUserShouldNotDoDDLOpOnLabelsTable() throws Exception {
656     Admin admin = TEST_UTIL.getHBaseAdmin();
657     try {
658       admin.disableTable(LABELS_TABLE_NAME);
659       fail("Lables table should not get disabled by user.");
660     } catch (Exception e) {
661     }
662     try {
663       admin.deleteTable(LABELS_TABLE_NAME);
664       fail("Lables table should not get disabled by user.");
665     } catch (Exception e) {
666     }
667     try {
668       HColumnDescriptor hcd = new HColumnDescriptor("testFamily");
669       admin.addColumn(LABELS_TABLE_NAME, hcd);
670       fail("Lables table should not get altered by user.");
671     } catch (Exception e) {
672     }
673     try {
674       admin.deleteColumn(LABELS_TABLE_NAME, VisibilityConstants.LABELS_TABLE_FAMILY);
675       fail("Lables table should not get altered by user.");
676     } catch (Exception e) {
677     }
678     try {
679       HColumnDescriptor hcd = new HColumnDescriptor(VisibilityConstants.LABELS_TABLE_FAMILY);
680       hcd.setBloomFilterType(BloomType.ROWCOL);
681       admin.modifyColumn(LABELS_TABLE_NAME, hcd);
682       fail("Lables table should not get altered by user.");
683     } catch (Exception e) {
684     }
685     try {
686       HTableDescriptor htd = new HTableDescriptor(LABELS_TABLE_NAME);
687       htd.addFamily(new HColumnDescriptor("f1"));
688       htd.addFamily(new HColumnDescriptor("f2"));
689       admin.modifyTable(LABELS_TABLE_NAME, htd);
690       fail("Lables table should not get altered by user.");
691     } catch (Exception e) {
692     }
693   }
694 
695   @Test
696   public void testMultipleVersions() throws Exception {
697     final byte[] r1 = Bytes.toBytes("row1");
698     final byte[] r2 = Bytes.toBytes("row2");
699     final byte[] v1 = Bytes.toBytes("100");
700     final byte[] v2 = Bytes.toBytes("101");
701     final byte[] fam2 = Bytes.toBytes("info2");
702     final byte[] qual2 = Bytes.toBytes("qual2");
703     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
704     HTableDescriptor desc = new HTableDescriptor(tableName);
705     HColumnDescriptor col = new HColumnDescriptor(fam);// Default max versions is 1.
706     desc.addFamily(col);
707     col = new HColumnDescriptor(fam2);
708     col.setMaxVersions(5);
709     desc.addFamily(col);
710     TEST_UTIL.getHBaseAdmin().createTable(desc);
711     try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
712       Put put = new Put(r1);
713       put.add(fam, qual, 3l, v1);
714       put.add(fam, qual2, 3l, v1);
715       put.add(fam2, qual, 3l, v1);
716       put.add(fam2, qual2, 3l, v1);
717       put.setCellVisibility(new CellVisibility(SECRET));
718       table.put(put);
719       put = new Put(r1);
720       put.add(fam, qual, 4l, v2);
721       put.add(fam, qual2, 4l, v2);
722       put.add(fam2, qual, 4l, v2);
723       put.add(fam2, qual2, 4l, v2);
724       put.setCellVisibility(new CellVisibility(PRIVATE));
725       table.put(put);
726 
727       put = new Put(r2);
728       put.add(fam, qual, 3l, v1);
729       put.add(fam, qual2, 3l, v1);
730       put.add(fam2, qual, 3l, v1);
731       put.add(fam2, qual2, 3l, v1);
732       put.setCellVisibility(new CellVisibility(SECRET));
733       table.put(put);
734       put = new Put(r2);
735       put.add(fam, qual, 4l, v2);
736       put.add(fam, qual2, 4l, v2);
737       put.add(fam2, qual, 4l, v2);
738       put.add(fam2, qual2, 4l, v2);
739       put.setCellVisibility(new CellVisibility(SECRET));
740       table.put(put);
741 
742       Scan s = new Scan();
743       s.setMaxVersions(1);
744       s.setAuthorizations(new Authorizations(SECRET));
745       ResultScanner scanner = table.getScanner(s);
746       Result result = scanner.next();
747       assertTrue(Bytes.equals(r1, result.getRow()));
748       // for cf 'fam' max versions in HCD is 1. So the old version cells, which are having matching
749       // CellVisibility with Authorizations, should not get considered in the label evaluation at
750       // all.
751       assertNull(result.getColumnLatestCell(fam, qual));
752       assertNull(result.getColumnLatestCell(fam, qual2));
753       // for cf 'fam2' max versions in HCD is > 1. So we can consider the old version cells, which
754       // are having matching CellVisibility with Authorizations, in the label evaluation. It can
755       // just skip those recent versions for which visibility is not there as per the new version's
756       // CellVisibility. The old versions which are having visibility can be send back
757       Cell cell = result.getColumnLatestCell(fam2, qual);
758       assertNotNull(cell);
759       assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
760           cell.getValueLength()));
761       cell = result.getColumnLatestCell(fam2, qual2);
762       assertNotNull(cell);
763       assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
764           cell.getValueLength()));
765 
766       result = scanner.next();
767       assertTrue(Bytes.equals(r2, result.getRow()));
768       cell = result.getColumnLatestCell(fam, qual);
769       assertNotNull(cell);
770       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
771           cell.getValueLength()));
772       cell = result.getColumnLatestCell(fam, qual2);
773       assertNotNull(cell);
774       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
775           cell.getValueLength()));
776       cell = result.getColumnLatestCell(fam2, qual);
777       assertNotNull(cell);
778       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
779           cell.getValueLength()));
780       cell = result.getColumnLatestCell(fam2, qual2);
781       assertNotNull(cell);
782       assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
783           cell.getValueLength()));
784     }
785   }
786 
787   @Test
788   public void testMutateRow() throws Exception {
789     final byte[] qual2 = Bytes.toBytes("qual2");
790     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
791     HTableDescriptor desc = new HTableDescriptor(tableName);
792     HColumnDescriptor col = new HColumnDescriptor(fam);
793     desc.addFamily(col);
794     TEST_UTIL.getHBaseAdmin().createTable(desc);
795     try (Table table = TEST_UTIL.getConnection().getTable(tableName)){
796       Put p1 = new Put(row1);
797       p1.add(fam, qual, value);
798       p1.setCellVisibility(new CellVisibility(CONFIDENTIAL));
799 
800       Put p2 = new Put(row1);
801       p2.add(fam, qual2, value);
802       p2.setCellVisibility(new CellVisibility(SECRET));
803 
804       RowMutations rm = new RowMutations(row1);
805       rm.add(p1);
806       rm.add(p2);
807 
808       table.mutateRow(rm);
809 
810       Get get = new Get(row1);
811       get.setAuthorizations(new Authorizations(CONFIDENTIAL));
812       Result result = table.get(get);
813       assertTrue(result.containsColumn(fam, qual));
814       assertFalse(result.containsColumn(fam, qual2));
815 
816       get.setAuthorizations(new Authorizations(SECRET));
817       result = table.get(get);
818       assertFalse(result.containsColumn(fam, qual));
819       assertTrue(result.containsColumn(fam, qual2));
820     }
821   }
822 
823   @Test
824   public void testFlushedFileWithVisibilityTags() throws Exception {
825     final byte[] qual2 = Bytes.toBytes("qual2");
826     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
827     HTableDescriptor desc = new HTableDescriptor(tableName);
828     HColumnDescriptor col = new HColumnDescriptor(fam);
829     desc.addFamily(col);
830     TEST_UTIL.getHBaseAdmin().createTable(desc);
831     try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
832       Put p1 = new Put(row1);
833       p1.add(fam, qual, value);
834       p1.setCellVisibility(new CellVisibility(CONFIDENTIAL));
835 
836       Put p2 = new Put(row1);
837       p2.add(fam, qual2, value);
838       p2.setCellVisibility(new CellVisibility(SECRET));
839 
840       RowMutations rm = new RowMutations(row1);
841       rm.add(p1);
842       rm.add(p2);
843 
844       table.mutateRow(rm);
845     }
846     TEST_UTIL.getHBaseAdmin().flush(tableName);
847     List<HRegion> regions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
848     Store store = regions.get(0).getStore(fam);
849     Collection<StoreFile> storefiles = store.getStorefiles();
850     assertTrue(storefiles.size() > 0);
851     for (StoreFile storeFile : storefiles) {
852       assertTrue(storeFile.getReader().getHFileReader().getFileContext().isIncludesTags());
853     }
854   }
855 
856   static Table createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
857       throws Exception {
858     List<Put> puts = new ArrayList<Put>();
859     for (int i = 0; i < labelExps.length; i++) {
860       Put put = new Put(Bytes.toBytes("row" + (i+1)));
861       put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
862       put.setCellVisibility(new CellVisibility(labelExps[i]));
863       puts.add(put);
864     }
865     Table table = TEST_UTIL.createTable(tableName, fam);
866     table.put(puts);
867     return table;
868   }
869 
870   public static void addLabels() throws Exception {
871     PrivilegedExceptionAction<VisibilityLabelsResponse> action =
872         new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
873       public VisibilityLabelsResponse run() throws Exception {
874         String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE, COPYRIGHT, ACCENT,
875             UNICODE_VIS_TAG, UC1, UC2 };
876         try (Connection conn = ConnectionFactory.createConnection(conf)) {
877           VisibilityClient.addLabels(conn, labels);
878         } catch (Throwable t) {
879           throw new IOException(t);
880         }
881         return null;
882       }
883     };
884     SUPERUSER.runAs(action);
885   }
886 }