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_NAME;
021import static org.junit.jupiter.api.Assertions.assertFalse;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import java.security.PrivilegedExceptionAction;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseTestingUtil;
028import org.apache.hadoop.hbase.HConstants;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.TableNameTestExtension;
031import org.apache.hadoop.hbase.client.Connection;
032import org.apache.hadoop.hbase.client.ConnectionFactory;
033import org.apache.hadoop.hbase.client.Get;
034import org.apache.hadoop.hbase.client.Put;
035import org.apache.hadoop.hbase.client.Result;
036import org.apache.hadoop.hbase.client.Table;
037import org.apache.hadoop.hbase.security.User;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.apache.hadoop.hbase.testclassification.SecurityTests;
040import org.apache.hadoop.hbase.util.Bytes;
041import org.junit.jupiter.api.AfterAll;
042import org.junit.jupiter.api.BeforeAll;
043import org.junit.jupiter.api.Tag;
044import org.junit.jupiter.api.Test;
045import org.junit.jupiter.api.TestInfo;
046
047@Tag(SecurityTests.TAG)
048@Tag(MediumTests.TAG)
049public class TestEnforcingScanLabelGenerator {
050
051  public static final String CONFIDENTIAL = "confidential";
052  private static final String SECRET = "secret";
053  public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
054  private static final byte[] ROW_1 = Bytes.toBytes("row1");
055  private final static byte[] CF = Bytes.toBytes("f");
056  private final static byte[] Q1 = Bytes.toBytes("q1");
057  private final static byte[] Q2 = Bytes.toBytes("q2");
058  private final static byte[] Q3 = Bytes.toBytes("q3");
059  private final static byte[] value = Bytes.toBytes("value");
060  public static Configuration conf;
061
062  public static User SUPERUSER;
063  public static User TESTUSER;
064
065  @BeforeAll
066  public static void setupBeforeClass() throws Exception {
067    // setup configuration
068    conf = TEST_UTIL.getConfiguration();
069    VisibilityTestUtil.enableVisiblityLabels(conf);
070    String classes = DefinedSetFilterScanLabelGenerator.class.getCanonicalName() + " , "
071      + EnforcingScanLabelGenerator.class.getCanonicalName();
072    conf.setStrings(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, classes);
073    conf.set("hbase.superuser", "admin");
074    TEST_UTIL.startMiniCluster(1);
075    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
076    TESTUSER = User.createUserForTesting(conf, "test", new String[] {});
077
078    // Wait for the labels table to become available
079    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
080
081    // Set up for the test
082    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
083      @Override
084      public Void run() throws Exception {
085        try (Connection conn = ConnectionFactory.createConnection(conf)) {
086          VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL });
087          VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL, }, TESTUSER.getShortName());
088        } catch (Throwable t) {
089          throw new IOException(t);
090        }
091        return null;
092      }
093    });
094  }
095
096  @Test
097  public void testEnforcingScanLabelGenerator(TestInfo testInfo) throws Exception {
098    final TableName tableName = TableName
099      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
100
101    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
102      @Override
103      public Void run() throws Exception {
104        try (Connection connection = ConnectionFactory.createConnection(conf);
105          Table table = TEST_UTIL.createTable(tableName, CF)) {
106          Put put = new Put(ROW_1);
107          put.addColumn(CF, Q1, HConstants.LATEST_TIMESTAMP, value);
108          put.setCellVisibility(new CellVisibility(SECRET));
109          table.put(put);
110          put = new Put(ROW_1);
111          put.addColumn(CF, Q2, HConstants.LATEST_TIMESTAMP, value);
112          put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
113          table.put(put);
114          put = new Put(ROW_1);
115          put.addColumn(CF, Q3, HConstants.LATEST_TIMESTAMP, value);
116          table.put(put);
117          return null;
118        }
119      }
120    });
121
122    // Test that super user can see all the cells.
123    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
124      @Override
125      public Void run() throws Exception {
126        try (Connection connection = ConnectionFactory.createConnection(conf);
127          Table table = connection.getTable(tableName)) {
128          // Test that super user can see all the cells.
129          Get get = new Get(ROW_1);
130          Result result = table.get(get);
131          assertTrue(result.containsColumn(CF, Q1), "Missing authorization");
132          assertTrue(result.containsColumn(CF, Q2), "Missing authorization");
133          assertTrue(result.containsColumn(CF, Q3), "Missing authorization");
134          return null;
135        }
136      }
137    });
138
139    TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
140      @Override
141      public Void run() throws Exception {
142        try (Connection connection = ConnectionFactory.createConnection(conf);
143          Table table = connection.getTable(tableName)) {
144          // Test that we enforce the defined set
145          Get get = new Get(ROW_1);
146          get.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
147          Result result = table.get(get);
148          assertFalse(result.containsColumn(CF, Q1), "Inappropriate authorization");
149          assertTrue(result.containsColumn(CF, Q2), "Missing authorization");
150          assertTrue(result.containsColumn(CF, Q3), "Inappropriate filtering");
151          // Test that we also enforce the defined set for the user if no auths are provided
152          get = new Get(ROW_1);
153          result = table.get(get);
154          assertFalse(result.containsColumn(CF, Q1), "Inappropriate authorization");
155          assertTrue(result.containsColumn(CF, Q2), "Missing authorization");
156          assertTrue(result.containsColumn(CF, Q3), "Inappropriate filtering");
157          return null;
158        }
159      }
160    });
161
162  }
163
164  @AfterAll
165  public static void tearDownAfterClass() throws Exception {
166    TEST_UTIL.shutdownMiniCluster();
167  }
168}