001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.security.visibility;
019
020import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
021import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
022import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER;
023import static org.junit.Assert.assertEquals;
024import static org.junit.Assert.assertFalse;
025import static org.junit.Assert.assertNotNull;
026import static org.junit.Assert.assertNull;
027import static org.junit.Assert.assertTrue;
028import static org.junit.Assert.fail;
029
030import com.google.protobuf.ByteString;
031
032import java.io.IOException;
033import java.security.PrivilegedExceptionAction;
034import java.util.ArrayList;
035import java.util.Collection;
036import java.util.List;
037
038import org.apache.hadoop.conf.Configuration;
039import org.apache.hadoop.hbase.Cell;
040import org.apache.hadoop.hbase.CellScanner;
041import org.apache.hadoop.hbase.HBaseTestingUtility;
042import org.apache.hadoop.hbase.HColumnDescriptor;
043import org.apache.hadoop.hbase.HConstants;
044import org.apache.hadoop.hbase.HTableDescriptor;
045import org.apache.hadoop.hbase.TableName;
046import org.apache.hadoop.hbase.client.Admin;
047import org.apache.hadoop.hbase.client.Append;
048import org.apache.hadoop.hbase.client.Connection;
049import org.apache.hadoop.hbase.client.ConnectionFactory;
050import org.apache.hadoop.hbase.client.Get;
051import org.apache.hadoop.hbase.client.Increment;
052import org.apache.hadoop.hbase.client.Put;
053import org.apache.hadoop.hbase.client.Result;
054import org.apache.hadoop.hbase.client.ResultScanner;
055import org.apache.hadoop.hbase.client.RowMutations;
056import org.apache.hadoop.hbase.client.Scan;
057import org.apache.hadoop.hbase.client.Table;
058import org.apache.hadoop.hbase.client.security.SecurityCapability;
059import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult;
060import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
061import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
062import org.apache.hadoop.hbase.regionserver.BloomType;
063import org.apache.hadoop.hbase.regionserver.HRegion;
064import org.apache.hadoop.hbase.regionserver.HRegionServer;
065import org.apache.hadoop.hbase.regionserver.HStore;
066import org.apache.hadoop.hbase.regionserver.HStoreFile;
067import org.apache.hadoop.hbase.security.User;
068import org.apache.hadoop.hbase.util.Bytes;
069import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
070import org.junit.After;
071import org.junit.AfterClass;
072import org.junit.Rule;
073import org.junit.Test;
074import org.junit.rules.TestName;
075
076/**
077 * Base test class for visibility labels basic features
078 */
079public abstract class TestVisibilityLabels {
080
081  public static final String TOPSECRET = "topsecret";
082  public static final String PUBLIC = "public";
083  public static final String PRIVATE = "private";
084  public static final String CONFIDENTIAL = "confidential";
085  public static final String SECRET = "secret";
086  public static final String COPYRIGHT = "\u00A9ABC";
087  public static final String ACCENT = "\u0941";
088  public static final String UNICODE_VIS_TAG = COPYRIGHT + "\"" + ACCENT + "\\" + SECRET + "\""
089      + "\u0027&\\";
090  public static final String UC1 = "\u0027\"\u002b";
091  public static final String UC2 = "\u002d\u003f";
092  public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
093  public static final byte[] row1 = Bytes.toBytes("row1");
094  public static final byte[] row2 = Bytes.toBytes("row2");
095  public static final byte[] row3 = Bytes.toBytes("row3");
096  public static final byte[] row4 = Bytes.toBytes("row4");
097  public final static byte[] fam = Bytes.toBytes("info");
098  public final static byte[] qual = Bytes.toBytes("qual");
099  public final static byte[] value = Bytes.toBytes("value");
100  public static Configuration conf;
101
102  private volatile boolean killedRS = false;
103  @Rule
104  public final TestName TEST_NAME = new TestName();
105  public static User SUPERUSER, USER1;
106
107  @AfterClass
108  public static void tearDownAfterClass() throws Exception {
109    TEST_UTIL.shutdownMiniCluster();
110  }
111
112  @After
113  public void tearDown() throws Exception {
114    killedRS = false;
115  }
116
117  @Test
118  public void testSecurityCapabilities() throws Exception {
119    List<SecurityCapability> capabilities = TEST_UTIL.getConnection().getAdmin()
120      .getSecurityCapabilities();
121    assertTrue("CELL_VISIBILITY capability is missing",
122      capabilities.contains(SecurityCapability.CELL_VISIBILITY));
123  }
124
125  @Test
126  public void testSimpleVisibilityLabels() throws Exception {
127    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
128    try (Table table = createTableAndWriteDataWithLabels(tableName, SECRET + "|" + CONFIDENTIAL,
129        PRIVATE + "|" + CONFIDENTIAL)) {
130      Scan s = new Scan();
131      s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE));
132      ResultScanner scanner = table.getScanner(s);
133      Result[] next = scanner.next(3);
134
135      assertTrue(next.length == 2);
136      CellScanner cellScanner = next[0].cellScanner();
137      cellScanner.advance();
138      Cell current = cellScanner.current();
139      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
140          current.getRowLength(), row1, 0, row1.length));
141      cellScanner = next[1].cellScanner();
142      cellScanner.advance();
143      current = cellScanner.current();
144      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
145          current.getRowLength(), row2, 0, row2.length));
146    }
147  }
148
149  @Test
150  public void testSimpleVisibilityLabelsWithUniCodeCharacters() throws Exception {
151    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
152    try (Table table = createTableAndWriteDataWithLabels(tableName,
153        SECRET + "|" + CellVisibility.quote(COPYRIGHT), "(" + CellVisibility.quote(COPYRIGHT)
154            + "&"  + CellVisibility.quote(ACCENT) + ")|" + CONFIDENTIAL,
155        CellVisibility.quote(UNICODE_VIS_TAG) + "&" + SECRET)) {
156      Scan s = new Scan();
157      s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE, COPYRIGHT, ACCENT,
158          UNICODE_VIS_TAG));
159      ResultScanner scanner = table.getScanner(s);
160      Result[] next = scanner.next(3);
161      assertTrue(next.length == 3);
162      CellScanner cellScanner = next[0].cellScanner();
163      cellScanner.advance();
164      Cell current = cellScanner.current();
165      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
166          current.getRowLength(), row1, 0, row1.length));
167      cellScanner = next[1].cellScanner();
168      cellScanner.advance();
169      current = cellScanner.current();
170      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
171          current.getRowLength(), row2, 0, row2.length));
172      cellScanner = next[2].cellScanner();
173      cellScanner.advance();
174      current = cellScanner.current();
175      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
176          current.getRowLength(), row3, 0, row3.length));
177    }
178  }
179
180  @Test
181  public void testAuthorizationsWithSpecialUnicodeCharacters() throws Exception {
182    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
183    try (Table table = createTableAndWriteDataWithLabels(tableName,
184        CellVisibility.quote(UC1) + "|" + CellVisibility.quote(UC2), CellVisibility.quote(UC1),
185        CellVisibility.quote(UNICODE_VIS_TAG))) {
186      Scan s = new Scan();
187      s.setAuthorizations(new Authorizations(UC1, UC2, ACCENT,
188          UNICODE_VIS_TAG));
189      ResultScanner scanner = table.getScanner(s);
190      Result[] next = scanner.next(3);
191      assertTrue(next.length == 3);
192      CellScanner cellScanner = next[0].cellScanner();
193      cellScanner.advance();
194      Cell current = cellScanner.current();
195      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
196          current.getRowLength(), row1, 0, row1.length));
197      cellScanner = next[1].cellScanner();
198      cellScanner.advance();
199      current = cellScanner.current();
200      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
201          current.getRowLength(), row2, 0, row2.length));
202      cellScanner = next[2].cellScanner();
203      cellScanner.advance();
204      current = cellScanner.current();
205      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
206          current.getRowLength(), row3, 0, row3.length));
207    }
208  }
209
210  @Test
211  public void testVisibilityLabelsWithComplexLabels() throws Exception {
212    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
213    try (Table table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|"
214        + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET, "(" + PRIVATE + "&" + CONFIDENTIAL + "&"
215        + SECRET + ")", "(" + PRIVATE + "&" + CONFIDENTIAL + "&" + SECRET + ")", "(" + PRIVATE
216        + "&" + CONFIDENTIAL + "&" + SECRET + ")")) {
217      Scan s = new Scan();
218      s.setAuthorizations(new Authorizations(TOPSECRET, CONFIDENTIAL, PRIVATE, PUBLIC, SECRET));
219      ResultScanner scanner = table.getScanner(s);
220      Result[] next = scanner.next(4);
221      assertEquals(3, next.length);
222      CellScanner cellScanner = next[0].cellScanner();
223      cellScanner.advance();
224      Cell current = cellScanner.current();
225      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
226          current.getRowLength(), row2, 0, row2.length));
227      cellScanner = next[1].cellScanner();
228      cellScanner.advance();
229      current = cellScanner.current();
230      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
231          current.getRowLength(), row3, 0, row3.length));
232      cellScanner = next[2].cellScanner();
233      cellScanner.advance();
234      current = cellScanner.current();
235      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
236          current.getRowLength(), row4, 0, row4.length));
237    }
238  }
239
240  @Test
241  public void testVisibilityLabelsThatDoesNotPassTheCriteria() throws Exception {
242    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
243    try (Table table = createTableAndWriteDataWithLabels(tableName,
244        "(" + SECRET + "|" + CONFIDENTIAL + ")", PRIVATE)){
245      Scan s = new Scan();
246      s.setAuthorizations(new Authorizations(PUBLIC));
247      ResultScanner scanner = table.getScanner(s);
248      Result[] next = scanner.next(3);
249      assertTrue(next.length == 0);
250    }
251  }
252
253  @Test
254  public void testVisibilityLabelsInPutsThatDoesNotMatchAnyDefinedLabels() throws Exception {
255    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
256    try {
257      createTableAndWriteDataWithLabels(tableName, "SAMPLE_LABEL", "TEST");
258      fail("Should have failed with failed sanity check exception");
259    } catch (Exception e) {
260    }
261  }
262
263  @Test
264  public void testVisibilityLabelsInScanThatDoesNotMatchAnyDefinedLabels() throws Exception {
265    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
266    try ( Table table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|"
267        + CONFIDENTIAL + ")", PRIVATE)){
268      Scan s = new Scan();
269      s.setAuthorizations(new Authorizations("SAMPLE"));
270      ResultScanner scanner = table.getScanner(s);
271      Result[] next = scanner.next(3);
272      assertTrue(next.length == 0);
273    }
274  }
275
276  @Test
277  public void testVisibilityLabelsWithGet() throws Exception {
278    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
279    try (Table table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL
280        + "&!" + PRIVATE, SECRET + "&" + CONFIDENTIAL + "&" + PRIVATE)) {
281      Get get = new Get(row1);
282      get.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
283      Result result = table.get(get);
284      assertTrue(!result.isEmpty());
285      Cell cell = result.getColumnLatestCell(fam, qual);
286      assertTrue(Bytes.equals(value, 0, value.length, cell.getValueArray(), cell.getValueOffset(),
287          cell.getValueLength()));
288    }
289  }
290
291  @Test
292  public void testVisibilityLabelsOnKillingOfRSContainingLabelsTable() throws Exception {
293    List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
294        .getRegionServerThreads();
295    int liveRS = 0;
296    for (RegionServerThread rsThreads : regionServerThreads) {
297      if (!rsThreads.getRegionServer().isAborted()) {
298        liveRS++;
299      }
300    }
301    if (liveRS == 1) {
302      TEST_UTIL.getHBaseCluster().startRegionServer();
303    }
304    Thread t1 = new Thread() {
305      @Override
306      public void run() {
307        List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
308            .getRegionServerThreads();
309        for (RegionServerThread rsThread : regionServerThreads) {
310          List<HRegion> onlineRegions = rsThread.getRegionServer().getRegions(
311              LABELS_TABLE_NAME);
312          if (onlineRegions.size() > 0) {
313            rsThread.getRegionServer().abort("Aborting ");
314            killedRS = true;
315            break;
316          }
317        }
318      }
319
320    };
321    t1.start();
322    final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
323    Thread t = new Thread() {
324      @Override
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<HRegion> onlineRegions = rsThread.getRegionServer().getRegions(
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
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.getRegions(LABELS_TABLE_NAME).isEmpty()) {
397      try {
398        Thread.sleep(10);
399      } catch (InterruptedException e) {
400      }
401    }
402  }
403
404  @Test
405  public void testVisibilityLabelsInGetThatDoesNotMatchAnyDefinedLabels() throws Exception {
406    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
407    try (Table table = createTableAndWriteDataWithLabels(tableName, "(" + SECRET + "|" + CONFIDENTIAL
408        + ")", PRIVATE)) {
409      Get get = new Get(row1);
410      get.setAuthorizations(new Authorizations("SAMPLE"));
411      Result result = table.get(get);
412      assertTrue(result.isEmpty());
413    }
414  }
415
416  @Test
417  public void testSetAndGetUserAuths() throws Throwable {
418    final String user = "user1";
419    PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
420      @Override
421      public Void run() throws Exception {
422        String[] auths = { SECRET, CONFIDENTIAL };
423        try (Connection conn = ConnectionFactory.createConnection(conf)) {
424          VisibilityClient.setAuths(conn, auths, user);
425        } catch (Throwable e) {
426        }
427        return null;
428      }
429    };
430    SUPERUSER.runAs(action);
431    try (Table ht = TEST_UTIL.getConnection().getTable(LABELS_TABLE_NAME);) {
432      Scan scan = new Scan();
433      scan.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
434      ResultScanner scanner = ht.getScanner(scan);
435      Result result = null;
436      List<Result> results = new ArrayList<>();
437      while ((result = scanner.next()) != null) {
438        results.add(result);
439      }
440      List<String> auths = extractAuths(user, results);
441      assertTrue(auths.contains(SECRET));
442      assertTrue(auths.contains(CONFIDENTIAL));
443      assertEquals(2, auths.size());
444    }
445
446    action = new PrivilegedExceptionAction<Void>() {
447      @Override
448      public Void run() throws Exception {
449        GetAuthsResponse authsResponse = null;
450        try (Connection conn = ConnectionFactory.createConnection(conf)) {
451          authsResponse = VisibilityClient.getAuths(conn, user);
452        } catch (Throwable e) {
453          fail("Should not have failed");
454        }
455        List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
456        for (ByteString authBS : authsResponse.getAuthList()) {
457          authsList.add(Bytes.toString(authBS.toByteArray()));
458        }
459        assertEquals(2, authsList.size());
460        assertTrue(authsList.contains(SECRET));
461        assertTrue(authsList.contains(CONFIDENTIAL));
462        return null;
463      }
464    };
465    SUPERUSER.runAs(action);
466
467    // Try doing setAuths once again and there should not be any duplicates
468    action = new PrivilegedExceptionAction<Void>() {
469      @Override
470      public Void run() throws Exception {
471        String[] auths1 = { SECRET, CONFIDENTIAL };
472        GetAuthsResponse authsResponse = null;
473        try (Connection conn = ConnectionFactory.createConnection(conf)) {
474          VisibilityClient.setAuths(conn, auths1, user);
475          try {
476            authsResponse = VisibilityClient.getAuths(conn, user);
477          } catch (Throwable e) {
478            fail("Should not have failed");
479          }
480        } catch (Throwable e) {
481        }
482        List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
483        for (ByteString authBS : authsResponse.getAuthList()) {
484          authsList.add(Bytes.toString(authBS.toByteArray()));
485        }
486        assertEquals(2, authsList.size());
487        assertTrue(authsList.contains(SECRET));
488        assertTrue(authsList.contains(CONFIDENTIAL));
489        return null;
490      }
491    };
492    SUPERUSER.runAs(action);
493  }
494
495  protected List<String> extractAuths(String user, List<Result> results) {
496    List<String> auths = new ArrayList<>();
497    for (Result result : results) {
498      Cell labelCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
499      Cell userAuthCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, Bytes.toBytes(user));
500      if (userAuthCell != null) {
501        auths.add(Bytes.toString(labelCell.getValueArray(), labelCell.getValueOffset(),
502            labelCell.getValueLength()));
503      }
504    }
505    return auths;
506  }
507
508  @Test
509  public void testClearUserAuths() throws Throwable {
510    PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
511      @Override
512      public Void run() throws Exception {
513        String[] auths = { SECRET, CONFIDENTIAL, PRIVATE };
514        String user = "testUser";
515        try (Connection conn = ConnectionFactory.createConnection(conf)) {
516          VisibilityClient.setAuths(conn, auths, user);
517        } catch (Throwable e) {
518          fail("Should not have failed");
519        }
520        // Removing the auths for SECRET and CONFIDENTIAL for the user.
521        // Passing a non existing auth also.
522        auths = new String[] { SECRET, PUBLIC, CONFIDENTIAL };
523        VisibilityLabelsResponse response = null;
524        try (Connection conn = ConnectionFactory.createConnection(conf)) {
525          response = VisibilityClient.clearAuths(conn, auths, user);
526        } catch (Throwable e) {
527          fail("Should not have failed");
528        }
529        List<RegionActionResult> resultList = response.getResultList();
530        assertEquals(3, resultList.size());
531        assertTrue(resultList.get(0).getException().getValue().isEmpty());
532        assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException",
533            resultList.get(1).getException().getName());
534        assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray())
535            .contains(
536                "org.apache.hadoop.hbase.security.visibility.InvalidLabelException: "
537                    + "Label 'public' is not set for the user testUser"));
538        assertTrue(resultList.get(2).getException().getValue().isEmpty());
539        try (Connection connection = ConnectionFactory.createConnection(conf);
540             Table ht = connection.getTable(LABELS_TABLE_NAME)) {
541          ResultScanner scanner = ht.getScanner(new Scan());
542          Result result = null;
543          List<Result> results = new ArrayList<>();
544          while ((result = scanner.next()) != null) {
545            results.add(result);
546          }
547          List<String> curAuths = extractAuths(user, results);
548          assertTrue(curAuths.contains(PRIVATE));
549          assertEquals(1, curAuths.size());
550        }
551
552        GetAuthsResponse authsResponse = null;
553        try (Connection conn = ConnectionFactory.createConnection(conf)) {
554          authsResponse = VisibilityClient.getAuths(conn, user);
555        } catch (Throwable e) {
556          fail("Should not have failed");
557        }
558        List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
559        for (ByteString authBS : authsResponse.getAuthList()) {
560          authsList.add(Bytes.toString(authBS.toByteArray()));
561        }
562        assertEquals(1, authsList.size());
563        assertTrue(authsList.contains(PRIVATE));
564        return null;
565      }
566    };
567    SUPERUSER.runAs(action);
568  }
569
570  @Test
571  public void testLabelsWithCheckAndPut() throws Throwable {
572    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
573    try (Table table = TEST_UTIL.createTable(tableName, fam)) {
574      byte[] row1 = Bytes.toBytes("row1");
575      Put put = new Put(row1);
576      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, value);
577      put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
578      table.checkAndMutate(row1, fam).qualifier(qual).ifNotExists().thenPut(put);
579      byte[] row2 = Bytes.toBytes("row2");
580      put = new Put(row2);
581      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, value);
582      put.setCellVisibility(new CellVisibility(SECRET));
583      table.checkAndMutate(row2, fam).qualifier(qual).ifNotExists().thenPut(put);
584
585      Scan scan = new Scan();
586      scan.setAuthorizations(new Authorizations(SECRET));
587      ResultScanner scanner = table.getScanner(scan);
588      Result result = scanner.next();
589      assertTrue(!result.isEmpty());
590      assertTrue(Bytes.equals(row2, result.getRow()));
591      result = scanner.next();
592      assertNull(result);
593    }
594  }
595
596  @Test
597  public void testLabelsWithIncrement() throws Throwable {
598    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
599    try (Table table = TEST_UTIL.createTable(tableName, fam)) {
600      byte[] row1 = Bytes.toBytes("row1");
601      byte[] val = Bytes.toBytes(1L);
602      Put put = new Put(row1);
603      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, val);
604      put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
605      table.put(put);
606      Get get = new Get(row1);
607      get.setAuthorizations(new Authorizations(SECRET));
608      Result result = table.get(get);
609      assertTrue(result.isEmpty());
610      table.incrementColumnValue(row1, fam, qual, 2L);
611      result = table.get(get);
612      assertTrue(result.isEmpty());
613      Increment increment = new Increment(row1);
614      increment.addColumn(fam, qual, 2L);
615      increment.setCellVisibility(new CellVisibility(SECRET));
616      table.increment(increment);
617      result = table.get(get);
618      assertTrue(!result.isEmpty());
619    }
620  }
621
622  @Test
623  public void testLabelsWithAppend() throws Throwable {
624    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
625    try (Table table = TEST_UTIL.createTable(tableName, fam);) {
626      byte[] row1 = Bytes.toBytes("row1");
627      byte[] val = Bytes.toBytes("a");
628      Put put = new Put(row1);
629      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, val);
630      put.setCellVisibility(new CellVisibility(SECRET + " & " + CONFIDENTIAL));
631      table.put(put);
632      Get get = new Get(row1);
633      get.setAuthorizations(new Authorizations(SECRET));
634      Result result = table.get(get);
635      assertTrue(result.isEmpty());
636      Append append = new Append(row1);
637      append.addColumn(fam, qual, Bytes.toBytes("b"));
638      table.append(append);
639      result = table.get(get);
640      assertTrue(result.isEmpty());
641      append = new Append(row1);
642      append.addColumn(fam, qual, Bytes.toBytes("c"));
643      append.setCellVisibility(new CellVisibility(SECRET));
644      table.append(append);
645      result = table.get(get);
646      assertTrue(!result.isEmpty());
647    }
648  }
649
650  @Test
651  public void testUserShouldNotDoDDLOpOnLabelsTable() throws Exception {
652    Admin admin = TEST_UTIL.getAdmin();
653    try {
654      admin.disableTable(LABELS_TABLE_NAME);
655      fail("Lables table should not get disabled by user.");
656    } catch (Exception e) {
657    }
658    try {
659      admin.deleteTable(LABELS_TABLE_NAME);
660      fail("Lables table should not get disabled by user.");
661    } catch (Exception e) {
662    }
663    try {
664      HColumnDescriptor hcd = new HColumnDescriptor("testFamily");
665      admin.addColumnFamily(LABELS_TABLE_NAME, hcd);
666      fail("Lables table should not get altered by user.");
667    } catch (Exception e) {
668    }
669    try {
670      admin.deleteColumnFamily(LABELS_TABLE_NAME, VisibilityConstants.LABELS_TABLE_FAMILY);
671      fail("Lables table should not get altered by user.");
672    } catch (Exception e) {
673    }
674    try {
675      HColumnDescriptor hcd = new HColumnDescriptor(VisibilityConstants.LABELS_TABLE_FAMILY);
676      hcd.setBloomFilterType(BloomType.ROWCOL);
677      admin.modifyColumnFamily(LABELS_TABLE_NAME, hcd);
678      fail("Lables table should not get altered by user.");
679    } catch (Exception e) {
680    }
681    try {
682      HTableDescriptor htd = new HTableDescriptor(LABELS_TABLE_NAME);
683      htd.addFamily(new HColumnDescriptor("f1"));
684      htd.addFamily(new HColumnDescriptor("f2"));
685      admin.modifyTable(LABELS_TABLE_NAME, htd);
686      fail("Lables table should not get altered by user.");
687    } catch (Exception e) {
688    }
689  }
690
691  @Test
692  public void testMultipleVersions() throws Exception {
693    final byte[] r1 = Bytes.toBytes("row1");
694    final byte[] r2 = Bytes.toBytes("row2");
695    final byte[] v1 = Bytes.toBytes("100");
696    final byte[] v2 = Bytes.toBytes("101");
697    final byte[] fam2 = Bytes.toBytes("info2");
698    final byte[] qual2 = Bytes.toBytes("qual2");
699    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
700    HTableDescriptor desc = new HTableDescriptor(tableName);
701    HColumnDescriptor col = new HColumnDescriptor(fam);// Default max versions is 1.
702    desc.addFamily(col);
703    col = new HColumnDescriptor(fam2);
704    col.setMaxVersions(5);
705    desc.addFamily(col);
706    TEST_UTIL.getAdmin().createTable(desc);
707    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
708      Put put = new Put(r1);
709      put.addColumn(fam, qual, 3L, v1);
710      put.addColumn(fam, qual2, 3L, v1);
711      put.addColumn(fam2, qual, 3L, v1);
712      put.addColumn(fam2, qual2, 3L, v1);
713      put.setCellVisibility(new CellVisibility(SECRET));
714      table.put(put);
715      put = new Put(r1);
716      put.addColumn(fam, qual, 4L, v2);
717      put.addColumn(fam, qual2, 4L, v2);
718      put.addColumn(fam2, qual, 4L, v2);
719      put.addColumn(fam2, qual2, 4L, v2);
720      put.setCellVisibility(new CellVisibility(PRIVATE));
721      table.put(put);
722
723      put = new Put(r2);
724      put.addColumn(fam, qual, 3L, v1);
725      put.addColumn(fam, qual2, 3L, v1);
726      put.addColumn(fam2, qual, 3L, v1);
727      put.addColumn(fam2, qual2, 3L, v1);
728      put.setCellVisibility(new CellVisibility(SECRET));
729      table.put(put);
730      put = new Put(r2);
731      put.addColumn(fam, qual, 4L, v2);
732      put.addColumn(fam, qual2, 4L, v2);
733      put.addColumn(fam2, qual, 4L, v2);
734      put.addColumn(fam2, qual2, 4L, v2);
735      put.setCellVisibility(new CellVisibility(SECRET));
736      table.put(put);
737
738      Scan s = new Scan();
739      s.setMaxVersions(1);
740      s.setAuthorizations(new Authorizations(SECRET));
741      ResultScanner scanner = table.getScanner(s);
742      Result result = scanner.next();
743      assertTrue(Bytes.equals(r1, result.getRow()));
744      // for cf 'fam' max versions in HCD is 1. So the old version cells, which are having matching
745      // CellVisibility with Authorizations, should not get considered in the label evaluation at
746      // all.
747      assertNull(result.getColumnLatestCell(fam, qual));
748      assertNull(result.getColumnLatestCell(fam, qual2));
749      // for cf 'fam2' max versions in HCD is > 1. So we can consider the old version cells, which
750      // are having matching CellVisibility with Authorizations, in the label evaluation. It can
751      // just skip those recent versions for which visibility is not there as per the new version's
752      // CellVisibility. The old versions which are having visibility can be send back
753      Cell cell = result.getColumnLatestCell(fam2, qual);
754      assertNotNull(cell);
755      assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
756          cell.getValueLength()));
757      cell = result.getColumnLatestCell(fam2, qual2);
758      assertNotNull(cell);
759      assertTrue(Bytes.equals(v1, 0, v1.length, cell.getValueArray(), cell.getValueOffset(),
760          cell.getValueLength()));
761
762      result = scanner.next();
763      assertTrue(Bytes.equals(r2, result.getRow()));
764      cell = result.getColumnLatestCell(fam, qual);
765      assertNotNull(cell);
766      assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
767          cell.getValueLength()));
768      cell = result.getColumnLatestCell(fam, qual2);
769      assertNotNull(cell);
770      assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
771          cell.getValueLength()));
772      cell = result.getColumnLatestCell(fam2, qual);
773      assertNotNull(cell);
774      assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
775          cell.getValueLength()));
776      cell = result.getColumnLatestCell(fam2, qual2);
777      assertNotNull(cell);
778      assertTrue(Bytes.equals(v2, 0, v2.length, cell.getValueArray(), cell.getValueOffset(),
779          cell.getValueLength()));
780    }
781  }
782
783  @Test
784  public void testMutateRow() throws Exception {
785    final byte[] qual2 = Bytes.toBytes("qual2");
786    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
787    HTableDescriptor desc = new HTableDescriptor(tableName);
788    HColumnDescriptor col = new HColumnDescriptor(fam);
789    desc.addFamily(col);
790    TEST_UTIL.getAdmin().createTable(desc);
791    try (Table table = TEST_UTIL.getConnection().getTable(tableName)){
792      Put p1 = new Put(row1);
793      p1.addColumn(fam, qual, value);
794      p1.setCellVisibility(new CellVisibility(CONFIDENTIAL));
795
796      Put p2 = new Put(row1);
797      p2.addColumn(fam, qual2, value);
798      p2.setCellVisibility(new CellVisibility(SECRET));
799
800      RowMutations rm = new RowMutations(row1);
801      rm.add(p1);
802      rm.add(p2);
803
804      table.mutateRow(rm);
805
806      Get get = new Get(row1);
807      get.setAuthorizations(new Authorizations(CONFIDENTIAL));
808      Result result = table.get(get);
809      assertTrue(result.containsColumn(fam, qual));
810      assertFalse(result.containsColumn(fam, qual2));
811
812      get.setAuthorizations(new Authorizations(SECRET));
813      result = table.get(get);
814      assertFalse(result.containsColumn(fam, qual));
815      assertTrue(result.containsColumn(fam, qual2));
816    }
817  }
818
819  @Test
820  public void testFlushedFileWithVisibilityTags() throws Exception {
821    final byte[] qual2 = Bytes.toBytes("qual2");
822    TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
823    HTableDescriptor desc = new HTableDescriptor(tableName);
824    HColumnDescriptor col = new HColumnDescriptor(fam);
825    desc.addFamily(col);
826    TEST_UTIL.getAdmin().createTable(desc);
827    try (Table table = TEST_UTIL.getConnection().getTable(tableName)) {
828      Put p1 = new Put(row1);
829      p1.addColumn(fam, qual, value);
830      p1.setCellVisibility(new CellVisibility(CONFIDENTIAL));
831
832      Put p2 = new Put(row1);
833      p2.addColumn(fam, qual2, value);
834      p2.setCellVisibility(new CellVisibility(SECRET));
835
836      RowMutations rm = new RowMutations(row1);
837      rm.add(p1);
838      rm.add(p2);
839
840      table.mutateRow(rm);
841    }
842    TEST_UTIL.getAdmin().flush(tableName);
843    List<HRegion> regions = TEST_UTIL.getHBaseCluster().getRegions(tableName);
844    HStore store = regions.get(0).getStore(fam);
845    Collection<HStoreFile> storefiles = store.getStorefiles();
846    assertTrue(storefiles.size() > 0);
847    for (HStoreFile storeFile : storefiles) {
848      assertTrue(storeFile.getReader().getHFileReader().getFileContext().isIncludesTags());
849    }
850  }
851
852  static Table createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
853      throws Exception {
854    List<Put> puts = new ArrayList<>(labelExps.length);
855    for (int i = 0; i < labelExps.length; i++) {
856      Put put = new Put(Bytes.toBytes("row" + (i+1)));
857      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, value);
858      put.setCellVisibility(new CellVisibility(labelExps[i]));
859      puts.add(put);
860    }
861    Table table = TEST_UTIL.createTable(tableName, fam);
862    table.put(puts);
863    return table;
864  }
865
866  public static void addLabels() throws Exception {
867    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
868        new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
869      @Override
870      public VisibilityLabelsResponse run() throws Exception {
871        String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE, COPYRIGHT, ACCENT,
872            UNICODE_VIS_TAG, UC1, UC2 };
873        try (Connection conn = ConnectionFactory.createConnection(conf)) {
874          VisibilityClient.addLabels(conn, labels);
875        } catch (Throwable t) {
876          throw new IOException(t);
877        }
878        return null;
879      }
880    };
881    SUPERUSER.runAs(action);
882  }
883}