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     // 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 {
177           authsResponse = VisibilityClient.getAuths(conf, "@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 {
270           response = VisibilityClient.clearAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup");
271         } catch (Throwable e) {
272           fail("Should not have failed");
273         }
274         return null;
275       }
276     });
277 
278     // Get testgroup's labels.  No label is returned.
279     SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
280       public Void run() throws Exception {
281         GetAuthsResponse authsResponse = null;
282         try {
283           authsResponse = VisibilityClient.getAuths(conf, "@testgroup");
284         } catch (Throwable e) {
285           fail("Should not have failed");
286         }
287         List<String> authsList = new ArrayList<String>();
288         for (ByteString authBS : authsResponse.getAuthList()) {
289           authsList.add(Bytes.toString(authBS.toByteArray()));
290         }
291         assertEquals(0, authsList.size());
292         return null;
293       }
294     });
295 
296     // Test that test user cannot see the cells with the labels anymore.
297     TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
298       public Void run() throws Exception {
299         try (Connection connection = ConnectionFactory.createConnection(conf);
300              Table table = connection.getTable(tableName)) {
301           Scan s1 = new Scan();
302           // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped
303           // testgroup's label.  test user has no auth labels now.
304           // scan's labels will be dropped on the server side.
305           s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
306           ResultScanner scanner1 = table.getScanner(s1);
307           Result[] next1 = scanner1.next(1);
308 
309           assertTrue(next1.length == 1);
310           CellScanner cellScanner1 = next1[0].cellScanner();
311           cellScanner1.advance();
312           Cell current1 = cellScanner1.current();
313           // test user can only see value3 (no label)
314           assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
315             current1.getRowLength(), ROW_1, 0, ROW_1.length));
316           assertTrue(Bytes.equals(current1.getQualifier(), Q3));
317           assertTrue(Bytes.equals(current1.getValue(), value3));
318 
319           assertFalse(cellScanner1.advance());
320         }
321         return null;
322       }
323     });
324 
325   }
326 
327   @AfterClass
328   public static void tearDownAfterClass() throws Exception {
329     TEST_UTIL.shutdownMiniCluster();
330   }
331 }