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