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.Cell;
028import org.apache.hadoop.hbase.CellScanner;
029import org.apache.hadoop.hbase.HBaseTestingUtil;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.TableNameTestExtension;
033import org.apache.hadoop.hbase.client.Connection;
034import org.apache.hadoop.hbase.client.ConnectionFactory;
035import org.apache.hadoop.hbase.client.Put;
036import org.apache.hadoop.hbase.client.Result;
037import org.apache.hadoop.hbase.client.ResultScanner;
038import org.apache.hadoop.hbase.client.Scan;
039import org.apache.hadoop.hbase.client.Table;
040import org.apache.hadoop.hbase.security.User;
041import org.apache.hadoop.hbase.testclassification.MediumTests;
042import org.apache.hadoop.hbase.testclassification.SecurityTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.jupiter.api.AfterAll;
045import org.junit.jupiter.api.BeforeAll;
046import org.junit.jupiter.api.Tag;
047import org.junit.jupiter.api.Test;
048import org.junit.jupiter.api.TestInfo;
049
050@Tag(SecurityTests.TAG)
051@Tag(MediumTests.TAG)
052public class TestDefaultScanLabelGeneratorStack {
053
054  public static final String CONFIDENTIAL = "confidential";
055  private static final String SECRET = "secret";
056  public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
057  private static final byte[] ROW_1 = Bytes.toBytes("row1");
058  private final static byte[] CF = Bytes.toBytes("f");
059  private final static byte[] Q1 = Bytes.toBytes("q1");
060  private final static byte[] Q2 = Bytes.toBytes("q2");
061  private final static byte[] Q3 = Bytes.toBytes("q3");
062  private final static byte[] value1 = Bytes.toBytes("value1");
063  private final static byte[] value2 = Bytes.toBytes("value2");
064  private final static byte[] value3 = Bytes.toBytes("value3");
065  public static Configuration conf;
066
067  public static User SUPERUSER;
068  public static User TESTUSER;
069
070  @BeforeAll
071  public static void setupBeforeClass() throws Exception {
072    // setup configuration
073    conf = TEST_UTIL.getConfiguration();
074    VisibilityTestUtil.enableVisiblityLabels(conf);
075    // Not setting any SLG class. This means to use the default behavior.
076    conf.set("hbase.superuser", "admin");
077    TEST_UTIL.startMiniCluster(1);
078    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
079    TESTUSER = User.createUserForTesting(conf, "test", new String[] {});
080
081    // Wait for the labels table to become available
082    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
083
084    // Set up for the test
085    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
086      @Override
087      public Void run() throws Exception {
088        try (Connection conn = ConnectionFactory.createConnection(conf)) {
089          VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL });
090          VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL }, TESTUSER.getShortName());
091        } catch (Throwable t) {
092          throw new IOException(t);
093        }
094        return null;
095      }
096    });
097  }
098
099  @Test
100  public void testDefaultScanLabelGeneratorStack(TestInfo testInfo) throws Exception {
101    final TableName tableName = TableName
102      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
103
104    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
105      @Override
106      public Void run() throws Exception {
107        try (Connection connection = ConnectionFactory.createConnection(conf);
108          Table table = TEST_UTIL.createTable(tableName, CF)) {
109          Put put = new Put(ROW_1);
110          put.addColumn(CF, Q1, HConstants.LATEST_TIMESTAMP, value1);
111          put.setCellVisibility(new CellVisibility(SECRET));
112          table.put(put);
113          put = new Put(ROW_1);
114          put.addColumn(CF, Q2, HConstants.LATEST_TIMESTAMP, value2);
115          put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
116          table.put(put);
117          put = new Put(ROW_1);
118          put.addColumn(CF, Q3, HConstants.LATEST_TIMESTAMP, value3);
119          table.put(put);
120          return null;
121        }
122      }
123    });
124
125    // Test that super user can see all the cells.
126    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
127      @Override
128      public Void run() throws Exception {
129        try (Connection connection = ConnectionFactory.createConnection(conf);
130          Table table = connection.getTable(tableName)) {
131          Result[] next = getResult(table, new Scan());
132
133          // Test that super user can see all the cells.
134          CellScanner cellScanner = next[0].cellScanner();
135          cellScanner.advance();
136          Cell current = cellScanner.current();
137          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
138            current.getRowLength(), ROW_1, 0, ROW_1.length));
139          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
140            current.getQualifierLength(), Q1, 0, Q1.length));
141          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
142            current.getValueLength(), value1, 0, value1.length));
143          cellScanner.advance();
144          current = cellScanner.current();
145          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
146            current.getRowLength(), ROW_1, 0, ROW_1.length));
147          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
148            current.getQualifierLength(), Q2, 0, Q2.length));
149          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
150            current.getValueLength(), value2, 0, value2.length));
151          cellScanner.advance();
152          current = cellScanner.current();
153          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
154            current.getRowLength(), ROW_1, 0, ROW_1.length));
155          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
156            current.getQualifierLength(), Q3, 0, Q3.length));
157          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
158            current.getValueLength(), value3, 0, value3.length));
159
160          return null;
161        }
162      }
163    });
164
165    TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
166      @Override
167      public Void run() throws Exception {
168        try (Connection connection = ConnectionFactory.createConnection(conf);
169          Table table = connection.getTable(tableName)) {
170          // Test scan with no auth attribute
171          Result[] next = getResult(table, new Scan());
172          CellScanner cellScanner = next[0].cellScanner();
173          cellScanner.advance();
174          Cell current = cellScanner.current();
175          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
176          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
177            current.getRowLength(), ROW_1, 0, ROW_1.length));
178          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
179            current.getQualifierLength(), Q2, 0, Q2.length));
180          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
181            current.getValueLength(), value2, 0, value2.length));
182          cellScanner.advance();
183          current = cellScanner.current();
184          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
185          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
186            current.getRowLength(), ROW_1, 0, ROW_1.length));
187          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
188            current.getQualifierLength(), Q3, 0, Q3.length));
189          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
190            current.getValueLength(), value3, 0, value3.length));
191
192          // Test scan with correct auth attribute for test user
193          Scan s1 = new Scan();
194          // test user is entitled to 'CONFIDENTIAL'.
195          // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs.
196          s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
197          ResultScanner scanner1 = table.getScanner(s1);
198          Result[] next1 = scanner1.next(1);
199
200          assertTrue(next1.length == 1);
201          CellScanner cellScanner1 = next1[0].cellScanner();
202          cellScanner1.advance();
203          Cell current1 = cellScanner1.current();
204          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
205          assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
206            current1.getRowLength(), ROW_1, 0, ROW_1.length));
207          assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(),
208            current1.getQualifierLength(), Q2, 0, Q2.length));
209          assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(),
210            current1.getValueLength(), value2, 0, value2.length));
211          cellScanner1.advance();
212          current1 = cellScanner1.current();
213          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
214          assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
215            current1.getRowLength(), ROW_1, 0, ROW_1.length));
216          assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(),
217            current1.getQualifierLength(), Q3, 0, Q3.length));
218          assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(),
219            current1.getValueLength(), value3, 0, value3.length));
220
221          // Test scan with incorrect auth attribute for test user
222          Scan s2 = new Scan();
223          // test user is entitled to 'CONFIDENTIAL'.
224          // If we set 'SECRET', it will be dropped by the SLGs.
225          s2.setAuthorizations(new Authorizations(new String[] { SECRET }));
226          ResultScanner scanner2 = table.getScanner(s2);
227          Result next2 = scanner2.next();
228          CellScanner cellScanner2 = next2.cellScanner();
229          cellScanner2.advance();
230          Cell current2 = cellScanner2.current();
231          // This scan will only see value3 (no label)
232          assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(),
233            current2.getRowLength(), ROW_1, 0, ROW_1.length));
234          assertTrue(Bytes.equals(current2.getQualifierArray(), current2.getQualifierOffset(),
235            current2.getQualifierLength(), Q3, 0, Q3.length));
236          assertTrue(Bytes.equals(current2.getValueArray(), current2.getValueOffset(),
237            current2.getValueLength(), value3, 0, value3.length));
238
239          assertFalse(cellScanner2.advance());
240
241          return null;
242        }
243      }
244    });
245
246  }
247
248  private static Result[] getResult(Table table, Scan scan) throws IOException {
249    ResultScanner scanner = table.getScanner(scan);
250    Result[] next = scanner.next(1);
251    assertTrue(next.length == 1);
252    return next;
253  }
254
255  @AfterAll
256  public static void tearDownAfterClass() throws Exception {
257    TEST_UTIL.shutdownMiniCluster();
258  }
259}