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