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.assertTrue;
022
023import java.io.IOException;
024import java.security.PrivilegedExceptionAction;
025import java.util.ArrayList;
026import java.util.List;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.Cell;
029import org.apache.hadoop.hbase.CellScanner;
030import org.apache.hadoop.hbase.HBaseTestingUtil;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.TableNameTestExtension;
034import org.apache.hadoop.hbase.client.Connection;
035import org.apache.hadoop.hbase.client.ConnectionFactory;
036import org.apache.hadoop.hbase.client.Delete;
037import org.apache.hadoop.hbase.client.Put;
038import org.apache.hadoop.hbase.client.Result;
039import org.apache.hadoop.hbase.client.ResultScanner;
040import org.apache.hadoop.hbase.client.Scan;
041import org.apache.hadoop.hbase.client.Table;
042import org.apache.hadoop.hbase.security.User;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.jupiter.api.AfterAll;
045import org.junit.jupiter.api.BeforeAll;
046import org.junit.jupiter.api.Test;
047import org.junit.jupiter.api.TestInfo;
048
049import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
050
051/**
052 * Tests visibility labels with deletes
053 */
054public abstract class VisibilityLabelsWithDeletesTestBase {
055
056  protected static final String TOPSECRET = "TOPSECRET";
057  protected static final String PUBLIC = "PUBLIC";
058  protected static final String PRIVATE = "PRIVATE";
059  protected static final String CONFIDENTIAL = "CONFIDENTIAL";
060  protected static final String SECRET = "SECRET";
061  protected static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
062  protected static final byte[] row1 = Bytes.toBytes("row1");
063  protected static final byte[] row2 = Bytes.toBytes("row2");
064  protected final static byte[] fam = Bytes.toBytes("info");
065  protected final static byte[] qual = Bytes.toBytes("qual");
066  protected final static byte[] qual1 = Bytes.toBytes("qual1");
067  protected final static byte[] qual2 = Bytes.toBytes("qual2");
068  protected final static byte[] value = Bytes.toBytes("value");
069  protected final static byte[] value1 = Bytes.toBytes("value1");
070  protected static Configuration conf;
071
072  protected static User SUPERUSER;
073
074  @BeforeAll
075  public static void setupBeforeClass() throws Exception {
076    // setup configuration
077    conf = TEST_UTIL.getConfiguration();
078    VisibilityTestUtil.enableVisiblityLabels(conf);
079    conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
080      ScanLabelGenerator.class);
081    conf.set("hbase.superuser", "admin");
082    TEST_UTIL.startMiniCluster(2);
083    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
084
085    // Wait for the labels table to become available
086    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
087    addLabels();
088  }
089
090  @AfterAll
091  public static void tearDownAfterClass() throws Exception {
092    TEST_UTIL.shutdownMiniCluster();
093  }
094
095  public static void addLabels() throws Exception {
096    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
097      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
098        @Override
099        public VisibilityLabelsResponse run() throws Exception {
100          String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE };
101          try (Connection conn = ConnectionFactory.createConnection(conf)) {
102            VisibilityClient.addLabels(conn, labels);
103          } catch (Throwable t) {
104            throw new IOException(t);
105          }
106          return null;
107        }
108      };
109    SUPERUSER.runAs(action);
110  }
111
112  protected abstract Table createTable(byte[] fam, TestInfo testInfo) throws IOException;
113
114  protected final void setAuths() throws IOException, InterruptedException {
115    PrivilegedExceptionAction<VisibilityLabelsResponse> action =
116      new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
117        @Override
118        public VisibilityLabelsResponse run() throws Exception {
119          try (Connection conn = ConnectionFactory.createConnection(conf)) {
120            return VisibilityClient.setAuths(conn,
121              new String[] { CONFIDENTIAL, PRIVATE, SECRET, TOPSECRET }, SUPERUSER.getShortName());
122          } catch (Throwable e) {
123          }
124          return null;
125        }
126      };
127    SUPERUSER.runAs(action);
128  }
129
130  private Table createTableAndWriteDataWithLabels(TestInfo testInfo, String... labelExps)
131    throws Exception {
132    Table table = createTable(fam, testInfo);
133    int i = 1;
134    List<Put> puts = new ArrayList<>(labelExps.length);
135    for (String labelExp : labelExps) {
136      Put put = new Put(Bytes.toBytes("row" + i));
137      put.addColumn(fam, qual, HConstants.LATEST_TIMESTAMP, value);
138      put.setCellVisibility(new CellVisibility(labelExp));
139      puts.add(put);
140      table.put(put);
141      i++;
142    }
143    // table.put(puts);
144    return table;
145  }
146
147  private Table createTableAndWriteDataWithLabels(long[] timestamp, TestInfo testInfo,
148    String... labelExps) throws Exception {
149    Table table = createTable(fam, testInfo);
150    int i = 1;
151    List<Put> puts = new ArrayList<>(labelExps.length);
152    for (String labelExp : labelExps) {
153      Put put = new Put(Bytes.toBytes("row" + i));
154      put.addColumn(fam, qual, timestamp[i - 1], value);
155      put.setCellVisibility(new CellVisibility(labelExp));
156      puts.add(put);
157      table.put(put);
158      TEST_UTIL.getAdmin().flush(table.getName());
159      i++;
160    }
161    return table;
162  }
163
164  @Test
165  public void testVisibilityLabelsWithDeleteColumns(TestInfo testInfo) throws Throwable {
166    setAuths();
167    final TableName tableName = TableName
168      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
169
170    try (
171      Table table = createTableAndWriteDataWithLabels(testInfo, SECRET + "&" + TOPSECRET, SECRET)) {
172      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
173        @Override
174        public Void run() throws Exception {
175          try (Connection connection = ConnectionFactory.createConnection(conf);
176            Table table = connection.getTable(tableName)) {
177            Delete d = new Delete(row1);
178            d.setCellVisibility(new CellVisibility(TOPSECRET + "&" + SECRET));
179            d.addColumns(fam, qual);
180            table.delete(d);
181          } catch (Throwable t) {
182            throw new IOException(t);
183          }
184          return null;
185        }
186      };
187      SUPERUSER.runAs(actiona);
188
189      TEST_UTIL.getAdmin().flush(tableName);
190      Scan s = new Scan();
191      s.setAuthorizations(new Authorizations(SECRET, PRIVATE, CONFIDENTIAL));
192      ResultScanner scanner = table.getScanner(s);
193      Result[] next = scanner.next(3);
194      assertTrue(next.length == 1);
195      CellScanner cellScanner = next[0].cellScanner();
196      cellScanner.advance();
197      Cell current = cellScanner.current();
198      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), current.getRowLength(),
199        row2, 0, row2.length));
200
201    }
202  }
203
204  @Test
205  public void testVisibilityLabelsWithDeleteFamily(TestInfo testInfo) throws Exception {
206    setAuths();
207    final TableName tableName = TableName
208      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
209    try (Table table =
210      createTableAndWriteDataWithLabels(testInfo, SECRET, CONFIDENTIAL + "|" + TOPSECRET)) {
211      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
212        @Override
213        public Void run() throws Exception {
214          try (Connection connection = ConnectionFactory.createConnection(conf);
215            Table table = connection.getTable(tableName)) {
216            Delete d = new Delete(row2);
217            d.setCellVisibility(new CellVisibility(TOPSECRET + "|" + CONFIDENTIAL));
218            d.addFamily(fam);
219            table.delete(d);
220          } catch (Throwable t) {
221            throw new IOException(t);
222          }
223          return null;
224        }
225      };
226      SUPERUSER.runAs(actiona);
227
228      TEST_UTIL.getAdmin().flush(tableName);
229      Scan s = new Scan();
230      s.setAuthorizations(new Authorizations(SECRET, PRIVATE, CONFIDENTIAL));
231      ResultScanner scanner = table.getScanner(s);
232      Result[] next = scanner.next(3);
233      assertTrue(next.length == 1);
234      CellScanner cellScanner = next[0].cellScanner();
235      cellScanner.advance();
236      Cell current = cellScanner.current();
237      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), current.getRowLength(),
238        row1, 0, row1.length));
239    }
240  }
241
242  @Test
243  public void testVisibilityLabelsWithDeleteFamilyVersion(TestInfo testInfo) throws Exception {
244    setAuths();
245    final TableName tableName = TableName
246      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
247    long[] ts = new long[] { 123L, 125L };
248    try (Table table =
249      createTableAndWriteDataWithLabels(ts, testInfo, CONFIDENTIAL + "|" + TOPSECRET, SECRET)) {
250      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
251        @Override
252        public Void run() throws Exception {
253          try (Connection connection = ConnectionFactory.createConnection(conf);
254            Table table = connection.getTable(tableName)) {
255            Delete d = new Delete(row1);
256            d.setCellVisibility(new CellVisibility(TOPSECRET + "|" + CONFIDENTIAL));
257            d.addFamilyVersion(fam, 123L);
258            table.delete(d);
259          } catch (Throwable t) {
260            throw new IOException(t);
261          }
262          return null;
263        }
264      };
265      SUPERUSER.runAs(actiona);
266
267      TEST_UTIL.getAdmin().flush(tableName);
268      Scan s = new Scan();
269      s.setAuthorizations(new Authorizations(SECRET, PRIVATE, CONFIDENTIAL));
270      ResultScanner scanner = table.getScanner(s);
271      Result[] next = scanner.next(3);
272      assertTrue(next.length == 1);
273      CellScanner cellScanner = next[0].cellScanner();
274      cellScanner.advance();
275      Cell current = cellScanner.current();
276      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), current.getRowLength(),
277        row2, 0, row2.length));
278    }
279  }
280
281  @Test
282  public void testVisibilityLabelsWithDeleteColumnExactVersion(TestInfo testInfo) throws Exception {
283    setAuths();
284    final TableName tableName = TableName
285      .valueOf(TableNameTestExtension.cleanUpTestName(testInfo.getTestMethod().get().getName()));
286    long[] ts = new long[] { 123L, 125L };
287    try (Table table =
288      createTableAndWriteDataWithLabels(ts, testInfo, CONFIDENTIAL + "|" + TOPSECRET, SECRET)) {
289      PrivilegedExceptionAction<Void> actiona = new PrivilegedExceptionAction<Void>() {
290        @Override
291        public Void run() throws Exception {
292          try (Connection connection = ConnectionFactory.createConnection(conf);
293            Table table = connection.getTable(tableName)) {
294            Delete d = new Delete(row1);
295            d.setCellVisibility(new CellVisibility(TOPSECRET + "|" + CONFIDENTIAL));
296            d.addColumn(fam, qual, 123L);
297            table.delete(d);
298          } catch (Throwable t) {
299            throw new IOException(t);
300          }
301          return null;
302        }
303      };
304      SUPERUSER.runAs(actiona);
305
306      TEST_UTIL.getAdmin().flush(tableName);
307      Scan s = new Scan();
308      s.setAuthorizations(new Authorizations(SECRET, PRIVATE, CONFIDENTIAL));
309      ResultScanner scanner = table.getScanner(s);
310      Result[] next = scanner.next(3);
311      assertTrue(next.length == 1);
312      CellScanner cellScanner = next[0].cellScanner();
313      cellScanner.advance();
314      Cell current = cellScanner.current();
315      assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), current.getRowLength(),
316        row2, 0, row2.length));
317    }
318  }
319}