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.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.security.PrivilegedExceptionAction;
028import java.util.ArrayList;
029import java.util.List;
030import org.apache.hadoop.conf.Configuration;
031import org.apache.hadoop.hbase.Cell;
032import org.apache.hadoop.hbase.CellScanner;
033import org.apache.hadoop.hbase.HBaseClassTestRule;
034import org.apache.hadoop.hbase.HBaseTestingUtil;
035import org.apache.hadoop.hbase.HConstants;
036import org.apache.hadoop.hbase.TableName;
037import org.apache.hadoop.hbase.client.Connection;
038import org.apache.hadoop.hbase.client.ConnectionFactory;
039import org.apache.hadoop.hbase.client.Put;
040import org.apache.hadoop.hbase.client.Result;
041import org.apache.hadoop.hbase.client.ResultScanner;
042import org.apache.hadoop.hbase.client.Scan;
043import org.apache.hadoop.hbase.client.Table;
044import org.apache.hadoop.hbase.security.User;
045import org.apache.hadoop.hbase.testclassification.MediumTests;
046import org.apache.hadoop.hbase.testclassification.SecurityTests;
047import org.apache.hadoop.hbase.util.Bytes;
048import org.junit.AfterClass;
049import org.junit.BeforeClass;
050import org.junit.ClassRule;
051import org.junit.Rule;
052import org.junit.Test;
053import org.junit.experimental.categories.Category;
054import org.junit.rules.TestName;
055
056import org.apache.hbase.thirdparty.com.google.protobuf.ByteString;
057
058import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
059import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
060
061@Category({ SecurityTests.class, MediumTests.class })
062public class TestVisibilityLablesWithGroups {
063
064  @ClassRule
065  public static final HBaseClassTestRule CLASS_RULE =
066    HBaseClassTestRule.forClass(TestVisibilityLablesWithGroups.class);
067
068  public static final String CONFIDENTIAL = "confidential";
069  private static final String SECRET = "secret";
070  public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
071  private static final byte[] ROW_1 = Bytes.toBytes("row1");
072  private final static byte[] CF = Bytes.toBytes("f");
073  private final static byte[] Q1 = Bytes.toBytes("q1");
074  private final static byte[] Q2 = Bytes.toBytes("q2");
075  private final static byte[] Q3 = Bytes.toBytes("q3");
076  private final static byte[] value1 = Bytes.toBytes("value1");
077  private final static byte[] value2 = Bytes.toBytes("value2");
078  private final static byte[] value3 = Bytes.toBytes("value3");
079  public static Configuration conf;
080
081  @Rule
082  public final TestName TEST_NAME = new TestName();
083  public static User SUPERUSER;
084  public static User TESTUSER;
085
086  @BeforeClass
087  public static void setupBeforeClass() throws Exception {
088    // setup configuration
089    conf = TEST_UTIL.getConfiguration();
090    VisibilityTestUtil.enableVisiblityLabels(conf);
091    // Not setting any SLG class. This means to use the default behavior.
092    // Use a group as the super user.
093    conf.set("hbase.superuser", "@supergroup");
094    TEST_UTIL.startMiniCluster(1);
095    // 'admin' has super user permission because it is part of the 'supergroup'
096    SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
097    // 'test' user will inherit 'testgroup' visibility labels
098    TESTUSER = User.createUserForTesting(conf, "test", new String[] { "testgroup" });
099
100    // Wait for the labels table to become available
101    TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
102
103    // Set up for the test
104    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
105      @Override
106      public Void run() throws Exception {
107        try (Connection conn = ConnectionFactory.createConnection(conf)) {
108          VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL });
109          // set auth for @testgroup
110          VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup");
111        } catch (Throwable t) {
112          throw new IOException(t);
113        }
114        return null;
115      }
116    });
117  }
118
119  @Test
120  public void testGroupAuths() throws Exception {
121    final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
122    // create the table
123    TEST_UTIL.createTable(tableName, CF);
124    // put the data.
125    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
126      @Override
127      public Void run() throws Exception {
128        try (Connection connection = ConnectionFactory.createConnection(conf);
129          Table table = connection.getTable(tableName)) {
130          Put put = new Put(ROW_1);
131          put.addColumn(CF, Q1, HConstants.LATEST_TIMESTAMP, value1);
132          put.setCellVisibility(new CellVisibility(SECRET));
133          table.put(put);
134          put = new Put(ROW_1);
135          put.addColumn(CF, Q2, HConstants.LATEST_TIMESTAMP, value2);
136          put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
137          table.put(put);
138          put = new Put(ROW_1);
139          put.addColumn(CF, Q3, HConstants.LATEST_TIMESTAMP, value3);
140          table.put(put);
141        }
142        return null;
143      }
144    });
145
146    // 'admin' user is part of 'supergroup', thus can see all the cells.
147    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
148      @Override
149      public Void run() throws Exception {
150        try (Connection connection = ConnectionFactory.createConnection(conf);
151          Table table = connection.getTable(tableName)) {
152          Scan s = new Scan();
153          ResultScanner scanner = table.getScanner(s);
154          Result[] next = scanner.next(1);
155
156          // Test that super user can see all the cells.
157          assertTrue(next.length == 1);
158          CellScanner cellScanner = next[0].cellScanner();
159          cellScanner.advance();
160          Cell current = cellScanner.current();
161          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
162            current.getRowLength(), ROW_1, 0, ROW_1.length));
163          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
164            current.getQualifierLength(), Q1, 0, Q1.length));
165          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
166            current.getValueLength(), value1, 0, value1.length));
167          cellScanner.advance();
168          current = cellScanner.current();
169          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
170            current.getRowLength(), ROW_1, 0, ROW_1.length));
171          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
172            current.getQualifierLength(), Q2, 0, Q2.length));
173          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
174            current.getValueLength(), value2, 0, value2.length));
175          cellScanner.advance();
176          current = cellScanner.current();
177          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
178            current.getRowLength(), ROW_1, 0, ROW_1.length));
179          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
180            current.getQualifierLength(), Q3, 0, Q3.length));
181          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
182            current.getValueLength(), value3, 0, value3.length));
183        }
184        return null;
185      }
186    });
187
188    // Get testgroup's labels.
189    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
190      @Override
191      public Void run() throws Exception {
192        GetAuthsResponse authsResponse = null;
193        try (Connection conn = ConnectionFactory.createConnection(conf)) {
194          authsResponse = VisibilityClient.getAuths(conn, "@testgroup");
195        } catch (Throwable e) {
196          fail("Should not have failed");
197        }
198        List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
199        for (ByteString authBS : authsResponse.getAuthList()) {
200          authsList.add(Bytes.toString(authBS.toByteArray()));
201        }
202        assertEquals(1, authsList.size());
203        assertTrue(authsList.contains(CONFIDENTIAL));
204        return null;
205      }
206    });
207
208    // Test that test user can see what 'testgroup' has been authorized to.
209    TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
210      @Override
211      public Void run() throws Exception {
212        try (Connection connection = ConnectionFactory.createConnection(conf);
213          Table table = connection.getTable(tableName)) {
214          // Test scan with no auth attribute
215          Scan s = new Scan();
216          ResultScanner scanner = table.getScanner(s);
217          Result[] next = scanner.next(1);
218
219          assertTrue(next.length == 1);
220          CellScanner cellScanner = next[0].cellScanner();
221          cellScanner.advance();
222          Cell current = cellScanner.current();
223          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
224          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
225            current.getRowLength(), ROW_1, 0, ROW_1.length));
226          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
227            current.getQualifierLength(), Q2, 0, Q2.length));
228          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
229            current.getValueLength(), value2, 0, value2.length));
230          cellScanner.advance();
231          current = cellScanner.current();
232          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
233          assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
234            current.getRowLength(), ROW_1, 0, ROW_1.length));
235          assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(),
236            current.getQualifierLength(), Q3, 0, Q3.length));
237          assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(),
238            current.getValueLength(), value3, 0, value3.length));
239
240          // Test scan with correct auth attribute for test user
241          Scan s1 = new Scan();
242          // test user is entitled to 'CONFIDENTIAL'.
243          // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs.
244          s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
245          ResultScanner scanner1 = table.getScanner(s1);
246          Result[] next1 = scanner1.next(1);
247
248          assertTrue(next1.length == 1);
249          CellScanner cellScanner1 = next1[0].cellScanner();
250          cellScanner1.advance();
251          Cell current1 = cellScanner1.current();
252          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
253          assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
254            current1.getRowLength(), ROW_1, 0, ROW_1.length));
255          assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(),
256            current1.getQualifierLength(), Q2, 0, Q2.length));
257          assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(),
258            current1.getValueLength(), value2, 0, value2.length));
259          cellScanner1.advance();
260          current1 = cellScanner1.current();
261          // test user can see value2 (CONFIDENTIAL) and value3 (no label)
262          assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
263            current1.getRowLength(), ROW_1, 0, ROW_1.length));
264          assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(),
265            current1.getQualifierLength(), Q3, 0, Q3.length));
266          assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(),
267            current1.getValueLength(), value3, 0, value3.length));
268
269          // Test scan with incorrect auth attribute for test user
270          Scan s2 = new Scan();
271          // test user is entitled to 'CONFIDENTIAL'.
272          // If we set 'SECRET', it will be dropped by the SLGs.
273          s2.setAuthorizations(new Authorizations(new String[] { SECRET }));
274          ResultScanner scanner2 = table.getScanner(s2);
275          Result next2 = scanner2.next();
276          CellScanner cellScanner2 = next2.cellScanner();
277          cellScanner2.advance();
278          Cell current2 = cellScanner2.current();
279          // This scan will only see value3 (no label)
280          assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(),
281            current2.getRowLength(), ROW_1, 0, ROW_1.length));
282          assertTrue(Bytes.equals(current2.getQualifierArray(), current2.getQualifierOffset(),
283            current2.getQualifierLength(), Q3, 0, Q3.length));
284          assertTrue(Bytes.equals(current2.getValueArray(), current2.getValueOffset(),
285            current2.getValueLength(), value3, 0, value3.length));
286
287          assertFalse(cellScanner2.advance());
288        }
289        return null;
290      }
291    });
292
293    // Clear 'testgroup' of CONFIDENTIAL label.
294    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
295      @Override
296      public Void run() throws Exception {
297        VisibilityLabelsResponse response = null;
298        try (Connection conn = ConnectionFactory.createConnection(conf)) {
299          response = VisibilityClient.clearAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup");
300        } catch (Throwable e) {
301          fail("Should not have failed");
302        }
303        return null;
304      }
305    });
306
307    // Get testgroup's labels. No label is returned.
308    SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
309      @Override
310      public Void run() throws Exception {
311        GetAuthsResponse authsResponse = null;
312        try (Connection conn = ConnectionFactory.createConnection(conf)) {
313          authsResponse = VisibilityClient.getAuths(conn, "@testgroup");
314        } catch (Throwable e) {
315          fail("Should not have failed");
316        }
317        List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size());
318        for (ByteString authBS : authsResponse.getAuthList()) {
319          authsList.add(Bytes.toString(authBS.toByteArray()));
320        }
321        assertEquals(0, authsList.size());
322        return null;
323      }
324    });
325
326    // Test that test user cannot see the cells with the labels anymore.
327    TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
328      @Override
329      public Void run() throws Exception {
330        try (Connection connection = ConnectionFactory.createConnection(conf);
331          Table table = connection.getTable(tableName)) {
332          Scan s1 = new Scan();
333          // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped
334          // testgroup's label. test user has no auth labels now.
335          // scan's labels will be dropped on the server side.
336          s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
337          ResultScanner scanner1 = table.getScanner(s1);
338          Result[] next1 = scanner1.next(1);
339
340          assertTrue(next1.length == 1);
341          CellScanner cellScanner1 = next1[0].cellScanner();
342          cellScanner1.advance();
343          Cell current1 = cellScanner1.current();
344          // test user can only see value3 (no label)
345          assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
346            current1.getRowLength(), ROW_1, 0, ROW_1.length));
347          assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(),
348            current1.getQualifierLength(), Q3, 0, Q3.length));
349          assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(),
350            current1.getValueLength(), value3, 0, value3.length));
351
352          assertFalse(cellScanner1.advance());
353        }
354        return null;
355      }
356    });
357
358  }
359
360  @AfterClass
361  public static void tearDownAfterClass() throws Exception {
362    TEST_UTIL.shutdownMiniCluster();
363  }
364}