View Javadoc

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
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 {
100           VisibilityClient.addLabels(conf, new String[] { SECRET, CONFIDENTIAL });
101           // set auth for @testgroup
102           VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup");
103         } catch (Throwable t) {
104           throw new IOException(t);
105         }
106         return null;
107       }
108     });
109   }
110 
111   @Test
112   public void testGroupAuths() throws Exception {
113     final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
114 
115     // create the table and put data.
116     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
117       public Void run() throws Exception {
118         Table table = TEST_UTIL.createTable(tableName, CF);
119         try {
120           Put put = new Put(ROW_1);
121           put.add(CF, Q1, HConstants.LATEST_TIMESTAMP, value1);
122           put.setCellVisibility(new CellVisibility(SECRET));
123           table.put(put);
124           put = new Put(ROW_1);
125           put.add(CF, Q2, HConstants.LATEST_TIMESTAMP, value2);
126           put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
127           table.put(put);
128           put = new Put(ROW_1);
129           put.add(CF, Q3, HConstants.LATEST_TIMESTAMP, value3);
130           table.put(put);
131         } finally {
132           table.close();
133         }
134         return null;
135       }
136     });
137 
138     // 'admin' user is part of 'supergroup', thus can see all the cells.
139     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
140       public Void run() throws Exception {
141         Connection connection = ConnectionFactory.createConnection(conf);
142         Table table = connection.getTable(tableName);
143         try {
144           Scan s = new Scan();
145           ResultScanner scanner = table.getScanner(s);
146           Result[] next = scanner.next(1);
147 
148           // Test that super user can see all the cells.
149           assertTrue(next.length == 1);
150           CellScanner cellScanner = next[0].cellScanner();
151           cellScanner.advance();
152           Cell current = cellScanner.current();
153           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
154               current.getRowLength(), ROW_1, 0, ROW_1.length));
155           assertTrue(Bytes.equals(current.getQualifier(), Q1));
156           assertTrue(Bytes.equals(current.getValue(), value1));
157           cellScanner.advance();
158           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.getQualifier(), Q2));
162           assertTrue(Bytes.equals(current.getValue(), value2));
163           cellScanner.advance();
164           current = cellScanner.current();
165           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
166               current.getRowLength(), ROW_1, 0, ROW_1.length));
167           assertTrue(Bytes.equals(current.getQualifier(), Q3));
168           assertTrue(Bytes.equals(current.getValue(), value3));
169 
170         } finally {
171           table.close();
172           connection.close();
173         }
174         return null;
175       }
176     });
177 
178     // Get testgroup's labels.
179     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
180       public Void run() throws Exception {
181         GetAuthsResponse authsResponse = null;
182         try {
183           authsResponse = VisibilityClient.getAuths(conf, "@testgroup");
184         } catch (Throwable e) {
185           fail("Should not have failed");
186         }
187         List<String> authsList = new ArrayList<String>();
188         for (ByteString authBS : authsResponse.getAuthList()) {
189           authsList.add(Bytes.toString(authBS.toByteArray()));
190         }
191         assertEquals(1, authsList.size());
192         assertTrue(authsList.contains(CONFIDENTIAL));
193         return null;
194       }
195     });
196 
197     // Test that test user can see what 'testgroup' has been authorized to.
198     TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
199       public Void run() throws Exception {
200         Connection connection = ConnectionFactory.createConnection(conf);
201         Table table = connection.getTable(tableName);
202         try {
203           // Test scan with no auth attribute
204           Scan s = new Scan();
205           ResultScanner scanner = table.getScanner(s);
206           Result[] next = scanner.next(1);
207 
208           assertTrue(next.length == 1);
209           CellScanner cellScanner = next[0].cellScanner();
210           cellScanner.advance();
211           Cell 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(), Q2));
216           assertTrue(Bytes.equals(current.getValue(), value2));
217           cellScanner.advance();
218           current = cellScanner.current();
219           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
220           assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
221               current.getRowLength(), ROW_1, 0, ROW_1.length));
222           assertTrue(Bytes.equals(current.getQualifier(), Q3));
223           assertTrue(Bytes.equals(current.getValue(), value3));
224 
225           // Test scan with correct auth attribute for test user
226           Scan s1 = new Scan();
227           // test user is entitled to 'CONFIDENTIAL'.
228           // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs.
229           s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
230           ResultScanner scanner1 = table.getScanner(s1);
231           Result[] next1 = scanner1.next(1);
232 
233           assertTrue(next1.length == 1);
234           CellScanner cellScanner1 = next1[0].cellScanner();
235           cellScanner1.advance();
236           Cell 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(), Q2));
241           assertTrue(Bytes.equals(current1.getValue(), value2));
242           cellScanner1.advance();
243           current1 = cellScanner1.current();
244           // test user can see value2 (CONFIDENTIAL) and value3 (no label)
245           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
246             current1.getRowLength(), ROW_1, 0, ROW_1.length));
247           assertTrue(Bytes.equals(current1.getQualifier(), Q3));
248           assertTrue(Bytes.equals(current1.getValue(), value3));
249 
250           // Test scan with incorrect auth attribute for test user
251           Scan s2 = new Scan();
252           // test user is entitled to 'CONFIDENTIAL'.
253           // If we set 'SECRET', it will be dropped by the SLGs.
254           s2.setAuthorizations(new Authorizations(new String[] { SECRET }));
255           ResultScanner scanner2 = table.getScanner(s2);
256           Result next2 = scanner2.next();
257           CellScanner cellScanner2 = next2.cellScanner();
258           cellScanner2.advance();
259           Cell current2 = cellScanner2.current();
260           // This scan will only see value3 (no label)
261           assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(),
262             current2.getRowLength(), ROW_1, 0, ROW_1.length));
263           assertTrue(Bytes.equals(current2.getQualifier(), Q3));
264           assertTrue(Bytes.equals(current2.getValue(), value3));
265 
266           assertFalse(cellScanner2.advance());
267         } finally {
268           table.close();
269           connection.close();
270         }
271         return null;
272       }
273     });
274 
275     // Clear 'testgroup' of CONFIDENTIAL label.
276     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
277       public Void run() throws Exception {
278         VisibilityLabelsResponse response = null;
279         try {
280           response = VisibilityClient.clearAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup");
281         } catch (Throwable e) {
282           fail("Should not have failed");
283         }
284         return null;
285       }
286     });
287 
288     // Get testgroup's labels.  No label is returned.
289     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
290       public Void run() throws Exception {
291         GetAuthsResponse authsResponse = null;
292         try {
293           authsResponse = VisibilityClient.getAuths(conf, "@testgroup");
294         } catch (Throwable e) {
295           fail("Should not have failed");
296         }
297         List<String> authsList = new ArrayList<String>();
298         for (ByteString authBS : authsResponse.getAuthList()) {
299           authsList.add(Bytes.toString(authBS.toByteArray()));
300         }
301         assertEquals(0, authsList.size());
302         return null;
303       }
304     });
305 
306     // Test that test user cannot see the cells with the labels anymore.
307     TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
308       public Void run() throws Exception {
309         Connection connection = ConnectionFactory.createConnection(conf);
310         Table table = connection.getTable(tableName);
311         try {
312           Scan s1 = new Scan();
313           // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped
314           // testgroup's label.  test user has no auth labels now.
315           // scan's labels will be dropped on the server side.
316           s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
317           ResultScanner scanner1 = table.getScanner(s1);
318           Result[] next1 = scanner1.next(1);
319 
320           assertTrue(next1.length == 1);
321           CellScanner cellScanner1 = next1[0].cellScanner();
322           cellScanner1.advance();
323           Cell current1 = cellScanner1.current();
324           // test user can only see value3 (no label)
325           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
326             current1.getRowLength(), ROW_1, 0, ROW_1.length));
327           assertTrue(Bytes.equals(current1.getQualifier(), Q3));
328           assertTrue(Bytes.equals(current1.getValue(), value3));
329 
330           assertFalse(cellScanner1.advance());
331         } finally {
332           table.close();
333           connection.close();
334         }
335         return null;
336       }
337     });
338 
339   }
340 
341   @AfterClass
342   public static void tearDownAfterClass() throws Exception {
343     TEST_UTIL.shutdownMiniCluster();
344   }
345 }