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