1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hbase.security.visibility;
19 
20 import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import java.io.IOException;
27 import java.security.PrivilegedExceptionAction;
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.CellScanner;
34 import org.apache.hadoop.hbase.HBaseTestingUtility;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.client.Connection;
38 import org.apache.hadoop.hbase.client.ConnectionFactory;
39 import org.apache.hadoop.hbase.client.Put;
40 import org.apache.hadoop.hbase.client.Result;
41 import org.apache.hadoop.hbase.client.ResultScanner;
42 import org.apache.hadoop.hbase.client.Scan;
43 import org.apache.hadoop.hbase.client.Table;
44 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
45 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
46 import org.apache.hadoop.hbase.security.User;
47 import org.apache.hadoop.hbase.testclassification.MediumTests;
48 import org.apache.hadoop.hbase.util.Bytes;
49 import org.junit.AfterClass;
50 import org.junit.BeforeClass;
51 import org.junit.Rule;
52 import org.junit.Test;
53 import org.junit.experimental.categories.Category;
54 import org.junit.rules.TestName;
55 
56 import com.google.protobuf.ByteString;
57 
58 @Category(MediumTests.class)
59 public class TestVisibilityLablesWithGroups {
60 
61   public static final String CONFIDENTIAL = "confidential";
62   private static final String SECRET = "secret";
63   public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
64   private static final byte[] ROW_1 = Bytes.toBytes("row1");
65   private final static byte[] CF = Bytes.toBytes("f");
66   private final static byte[] Q1 = Bytes.toBytes("q1");
67   private final static byte[] Q2 = Bytes.toBytes("q2");
68   private final static byte[] Q3 = Bytes.toBytes("q3");
69   private final static byte[] value1 = Bytes.toBytes("value1");
70   private final static byte[] value2 = Bytes.toBytes("value2");
71   private final static byte[] value3 = Bytes.toBytes("value3");
72   public static Configuration conf;
73 
74   @Rule
75   public final TestName TEST_NAME = new TestName();
76   public static User SUPERUSER;
77   public static User TESTUSER;
78 
79   @BeforeClass
setupBeforeClass()80   public static void setupBeforeClass() throws Exception {
81     // setup configuration
82     conf = TEST_UTIL.getConfiguration();
83     VisibilityTestUtil.enableVisiblityLabels(conf);
84     // Not setting any SLG class. This means to use the default behavior.
85     // Use a group as the super user.
86     conf.set("hbase.superuser", "@supergroup");
87     TEST_UTIL.startMiniCluster(1);
88     // 'admin' has super user permission because it is part of the 'supergroup'
89     SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
90     // 'test' user will inherit 'testgroup' visibility labels
91     TESTUSER = User.createUserForTesting(conf, "test", new String[] {"testgroup" });
92 
93     // Wait for the labels table to become available
94     TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
95 
96     // Set up for the test
97     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
98       public Void run() throws Exception {
99         try (Connection conn = ConnectionFactory.createConnection(conf)) {
100           VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL });
101           // set auth for @testgroup
102           VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup");
103         } catch (Throwable t) {
104           throw new IOException(t);
105         }
106         return null;
107       }
108     });
109   }
110 
111   @Test
testGroupAuths()112   public void testGroupAuths() throws Exception {
113     final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
114     // create the table
115     TEST_UTIL.createTable(tableName, CF);
116     // put the data.
117     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
118       public Void run() throws Exception {
119         try (Connection connection = ConnectionFactory.createConnection(conf);
120              Table table = connection.getTable(tableName)) {
121           Put put = new Put(ROW_1);
122           put.add(CF, Q1, HConstants.LATEST_TIMESTAMP, value1);
123           put.setCellVisibility(new CellVisibility(SECRET));
124           table.put(put);
125           put = new Put(ROW_1);
126           put.add(CF, Q2, HConstants.LATEST_TIMESTAMP, value2);
127           put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
128           table.put(put);
129           put = new Put(ROW_1);
130           put.add(CF, Q3, HConstants.LATEST_TIMESTAMP, value3);
131           table.put(put);
132         }
133         return null;
134       }
135     });
136 
137     // 'admin' user is part of 'supergroup', thus can see all the cells.
138     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
139       public Void run() throws Exception {
140         try (Connection connection = ConnectionFactory.createConnection(conf);
141              Table table = connection.getTable(tableName)) {
142           Scan s = new Scan();
143           ResultScanner scanner = table.getScanner(s);
144           Result[] next = scanner.next(1);
145 
146           // Test that super user can see all the cells.
147           assertTrue(next.length == 1);
148           CellScanner cellScanner = next[0].cellScanner();
149           cellScanner.advance();
150           Cell current = cellScanner.current();
151           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
152               current.getRowLength(), ROW_1, 0, ROW_1.length));
153           assertTrue(Bytes.equals(current.getQualifier(), Q1));
154           assertTrue(Bytes.equals(current.getValue(), value1));
155           cellScanner.advance();
156           current = cellScanner.current();
157           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
158               current.getRowLength(), ROW_1, 0, ROW_1.length));
159           assertTrue(Bytes.equals(current.getQualifier(), Q2));
160           assertTrue(Bytes.equals(current.getValue(), value2));
161           cellScanner.advance();
162           current = cellScanner.current();
163           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
164               current.getRowLength(), ROW_1, 0, ROW_1.length));
165           assertTrue(Bytes.equals(current.getQualifier(), Q3));
166           assertTrue(Bytes.equals(current.getValue(), value3));
167         }
168         return null;
169       }
170     });
171 
172     // Get testgroup's labels.
173     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
174       public Void run() throws Exception {
175         GetAuthsResponse authsResponse = null;
176         try (Connection conn = ConnectionFactory.createConnection(conf)) {
177           authsResponse = VisibilityClient.getAuths(conn, "@testgroup");
178         } catch (Throwable e) {
179           fail("Should not have failed");
180         }
181         List<String> authsList = new ArrayList<String>();
182         for (ByteString authBS : authsResponse.getAuthList()) {
183           authsList.add(Bytes.toString(authBS.toByteArray()));
184         }
185         assertEquals(1, authsList.size());
186         assertTrue(authsList.contains(CONFIDENTIAL));
187         return null;
188       }
189     });
190 
191     // Test that test user can see what 'testgroup' has been authorized to.
192     TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
193       public Void run() throws Exception {
194         try (Connection connection = ConnectionFactory.createConnection(conf);
195              Table table = connection.getTable(tableName)) {
196           // Test scan with no auth attribute
197           Scan s = new Scan();
198           ResultScanner scanner = table.getScanner(s);
199           Result[] next = scanner.next(1);
200 
201           assertTrue(next.length == 1);
202           CellScanner cellScanner = next[0].cellScanner();
203           cellScanner.advance();
204           Cell current = cellScanner.current();
205           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
206           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
207               current.getRowLength(), ROW_1, 0, ROW_1.length));
208           assertTrue(Bytes.equals(current.getQualifier(), Q2));
209           assertTrue(Bytes.equals(current.getValue(), value2));
210           cellScanner.advance();
211           current = cellScanner.current();
212           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
213           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
214               current.getRowLength(), ROW_1, 0, ROW_1.length));
215           assertTrue(Bytes.equals(current.getQualifier(), Q3));
216           assertTrue(Bytes.equals(current.getValue(), value3));
217 
218           // Test scan with correct auth attribute for test user
219           Scan s1 = new Scan();
220           // test user is entitled to 'CONFIDENTIAL'.
221           // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs.
222           s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
223           ResultScanner scanner1 = table.getScanner(s1);
224           Result[] next1 = scanner1.next(1);
225 
226           assertTrue(next1.length == 1);
227           CellScanner cellScanner1 = next1[0].cellScanner();
228           cellScanner1.advance();
229           Cell current1 = cellScanner1.current();
230           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
231           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
232             current1.getRowLength(), ROW_1, 0, ROW_1.length));
233           assertTrue(Bytes.equals(current1.getQualifier(), Q2));
234           assertTrue(Bytes.equals(current1.getValue(), value2));
235           cellScanner1.advance();
236           current1 = cellScanner1.current();
237           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
238           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
239             current1.getRowLength(), ROW_1, 0, ROW_1.length));
240           assertTrue(Bytes.equals(current1.getQualifier(), Q3));
241           assertTrue(Bytes.equals(current1.getValue(), value3));
242 
243           // Test scan with incorrect auth attribute for test user
244           Scan s2 = new Scan();
245           // test user is entitled to 'CONFIDENTIAL'.
246           // If we set 'SECRET', it will be dropped by the SLGs.
247           s2.setAuthorizations(new Authorizations(new String[] { SECRET }));
248           ResultScanner scanner2 = table.getScanner(s2);
249           Result next2 = scanner2.next();
250           CellScanner cellScanner2 = next2.cellScanner();
251           cellScanner2.advance();
252           Cell current2 = cellScanner2.current();
253           // This scan will only see value3 (no label)
254           assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(),
255             current2.getRowLength(), ROW_1, 0, ROW_1.length));
256           assertTrue(Bytes.equals(current2.getQualifier(), Q3));
257           assertTrue(Bytes.equals(current2.getValue(), value3));
258 
259           assertFalse(cellScanner2.advance());
260         }
261         return null;
262       }
263     });
264 
265     // Clear 'testgroup' of CONFIDENTIAL label.
266     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
267       public Void run() throws Exception {
268         VisibilityLabelsResponse response = null;
269         try (Connection conn = ConnectionFactory.createConnection(conf)) {
270           response = VisibilityClient.clearAuths(conn, new String[] {
271             CONFIDENTIAL }, "@testgroup");
272         } catch (Throwable e) {
273           fail("Should not have failed");
274         }
275         return null;
276       }
277     });
278 
279     // Get testgroup's labels.  No label is returned.
280     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
281       public Void run() throws Exception {
282         GetAuthsResponse authsResponse = null;
283         try (Connection conn = ConnectionFactory.createConnection(conf)) {
284           authsResponse = VisibilityClient.getAuths(conn, "@testgroup");
285         } catch (Throwable e) {
286           fail("Should not have failed");
287         }
288         List<String> authsList = new ArrayList<String>();
289         for (ByteString authBS : authsResponse.getAuthList()) {
290           authsList.add(Bytes.toString(authBS.toByteArray()));
291         }
292         assertEquals(0, authsList.size());
293         return null;
294       }
295     });
296 
297     // Test that test user cannot see the cells with the labels anymore.
298     TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
299       public Void run() throws Exception {
300         try (Connection connection = ConnectionFactory.createConnection(conf);
301              Table table = connection.getTable(tableName)) {
302           Scan s1 = new Scan();
303           // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped
304           // testgroup's label.  test user has no auth labels now.
305           // scan's labels will be dropped on the server side.
306           s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
307           ResultScanner scanner1 = table.getScanner(s1);
308           Result[] next1 = scanner1.next(1);
309 
310           assertTrue(next1.length == 1);
311           CellScanner cellScanner1 = next1[0].cellScanner();
312           cellScanner1.advance();
313           Cell current1 = cellScanner1.current();
314           // test user can only see value3 (no label)
315           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
316             current1.getRowLength(), ROW_1, 0, ROW_1.length));
317           assertTrue(Bytes.equals(current1.getQualifier(), Q3));
318           assertTrue(Bytes.equals(current1.getValue(), value3));
319 
320           assertFalse(cellScanner1.advance());
321         }
322         return null;
323       }
324     });
325 
326   }
327 
328   @AfterClass
tearDownAfterClass()329   public static void tearDownAfterClass() throws Exception {
330     TEST_UTIL.shutdownMiniCluster();
331   }
332 }
333