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.assertNull;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.security.PrivilegedExceptionAction;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.HBaseTestingUtility;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.MediumTests;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.client.Get;
36  import org.apache.hadoop.hbase.client.HTable;
37  import org.apache.hadoop.hbase.client.Put;
38  import org.apache.hadoop.hbase.client.Result;
39  import org.apache.hadoop.hbase.client.ResultScanner;
40  import org.apache.hadoop.hbase.client.Scan;
41  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
42  import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
43  import org.apache.hadoop.hbase.security.User;
44  import org.apache.hadoop.hbase.security.access.AccessControlLists;
45  import org.apache.hadoop.hbase.security.access.AccessController;
46  import org.apache.hadoop.hbase.security.access.Permission;
47  import org.apache.hadoop.hbase.security.access.SecureTestUtil;
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 TestVisibilityLabelsWithACL {
60  
61    private static final String PRIVATE = "private";
62    private static final String CONFIDENTIAL = "confidential";
63    private static final String SECRET = "secret";
64    private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
65    private static final byte[] row1 = Bytes.toBytes("row1");
66    private final static byte[] fam = Bytes.toBytes("info");
67    private final static byte[] qual = Bytes.toBytes("qual");
68    private final static byte[] value = Bytes.toBytes("value");
69    private static Configuration conf;
70  
71    @Rule
72    public final TestName TEST_NAME = new TestName();
73    private static User SUPERUSER;
74    private static User NORMAL_USER1;
75    private static User NORMAL_USER2;
76  
77    @BeforeClass
78    public static void setupBeforeClass() throws Exception {
79      // setup configuration
80      conf = TEST_UTIL.getConfiguration();
81      SecureTestUtil.enableSecurity(conf);
82      conf.set("hbase.coprocessor.master.classes", AccessController.class.getName() + ","
83          + VisibilityController.class.getName());
84      conf.set("hbase.coprocessor.region.classes", AccessController.class.getName() + ","
85          + VisibilityController.class.getName());
86      TEST_UTIL.startMiniCluster(2);
87  
88      TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName(), 50000);
89      // Wait for the labels table to become available
90      TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
91      addLabels();
92  
93      // Create users for testing
94      SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
95      NORMAL_USER1 = User.createUserForTesting(conf, "user1", new String[] {});
96      NORMAL_USER2 = User.createUserForTesting(conf, "user2", new String[] {});
97      // Grant users EXEC privilege on the labels table. For the purposes of this
98      // test, we want to insure that access is denied even with the ability to access
99      // the endpoint.
100     SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER1.getShortName(), LABELS_TABLE_NAME,
101       null, null, Permission.Action.EXEC);
102     SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER2.getShortName(), LABELS_TABLE_NAME,
103       null, null, Permission.Action.EXEC);
104   }
105 
106   @AfterClass
107   public static void tearDownAfterClass() throws Exception {
108     TEST_UTIL.shutdownMiniCluster();
109   }
110 
111   @Test
112   public void testScanForUserWithFewerLabelAuthsThanLabelsInScanAuthorizations() throws Throwable {
113     String[] auths = { SECRET };
114     String user = "user2";
115     VisibilityClient.setAuths(conf, auths, user);
116     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
117     final HTable table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL
118         + "&!" + PRIVATE, SECRET + "&!" + PRIVATE);
119     SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER2.getShortName(), tableName,
120       null, null, Permission.Action.READ);
121     PrivilegedExceptionAction<Void> scanAction = new PrivilegedExceptionAction<Void>() {
122       public Void run() throws Exception {
123         Scan s = new Scan();
124         s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
125         HTable t = new HTable(conf, table.getTableName());
126         try {
127           ResultScanner scanner = t.getScanner(s);
128           Result result = scanner.next();
129           assertTrue(!result.isEmpty());
130           assertTrue(Bytes.equals(Bytes.toBytes("row2"), result.getRow()));
131           result = scanner.next();
132           assertNull(result);
133         } finally {
134           t.close();
135         }
136         return null;
137       }
138     };
139     NORMAL_USER2.runAs(scanAction);
140   }
141 
142   @Test
143   public void testScanForSuperUserWithFewerLabelAuths() throws Throwable {
144     String[] auths = { SECRET };
145     String user = "admin";
146     VisibilityClient.setAuths(conf, auths, user);
147     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
148     final HTable table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL
149         + "&!" + PRIVATE, SECRET + "&!" + PRIVATE);
150     PrivilegedExceptionAction<Void> scanAction = new PrivilegedExceptionAction<Void>() {
151       public Void run() throws Exception {
152         Scan s = new Scan();
153         s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
154         HTable t = new HTable(conf, table.getTableName());
155         try {
156           ResultScanner scanner = t.getScanner(s);
157           Result[] result = scanner.next(5);
158           assertTrue(result.length == 2);
159         } finally {
160           t.close();
161         }
162         return null;
163       }
164     };
165     SUPERUSER.runAs(scanAction);
166   }
167 
168   @Test
169   public void testGetForSuperUserWithFewerLabelAuths() throws Throwable {
170     String[] auths = { SECRET };
171     String user = "admin";
172     VisibilityClient.setAuths(conf, auths, user);
173     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
174     final HTable table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL
175         + "&!" + PRIVATE, SECRET + "&!" + PRIVATE);
176     PrivilegedExceptionAction<Void> scanAction = new PrivilegedExceptionAction<Void>() {
177       public Void run() throws Exception {
178         Get g = new Get(row1);
179         g.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
180         HTable t = new HTable(conf, table.getTableName());
181         try {
182           Result result = t.get(g);
183           assertTrue(!result.isEmpty());
184         } finally {
185           t.close();
186         }
187         return null;
188       }
189     };
190     SUPERUSER.runAs(scanAction);
191   }
192 
193   @Test
194   public void testVisibilityLabelsForUserWithNoAuths() throws Throwable {
195     String user = "admin";
196     String[] auths = { SECRET };
197     VisibilityClient.clearAuths(conf, auths, user); // Removing all auths if any.
198     VisibilityClient.setAuths(conf, auths, "user1");
199     TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
200     final HTable table = createTableAndWriteDataWithLabels(tableName, SECRET);
201     SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER1.getShortName(), tableName,
202       null, null, Permission.Action.READ);
203     SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER2.getShortName(), tableName,
204       null, null, Permission.Action.READ);
205     PrivilegedExceptionAction<Void> getAction = new PrivilegedExceptionAction<Void>() {
206       public Void run() throws Exception {
207         Get g = new Get(row1);
208         g.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL));
209         HTable t = new HTable(conf, table.getTableName());
210         try {
211           Result result = t.get(g);
212           assertTrue(result.isEmpty());
213         } finally {
214           t.close();
215         }
216         return null;
217       }
218     };
219     NORMAL_USER2.runAs(getAction);
220   }
221 
222   @Test
223   public void testLabelsTableOpsWithDifferentUsers() throws Throwable {
224     PrivilegedExceptionAction<VisibilityLabelsResponse> action = 
225         new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
226       public VisibilityLabelsResponse run() throws Exception {
227         try {
228           return VisibilityClient.addLabels(conf, new String[] { "l1", "l2" });
229         } catch (Throwable e) {
230         }
231         return null;
232       }
233     };
234     VisibilityLabelsResponse response = NORMAL_USER1.runAs(action);
235     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response
236         .getResult(0).getException().getName());
237     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response
238         .getResult(1).getException().getName());
239 
240     action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
241       public VisibilityLabelsResponse run() throws Exception {
242         try {
243           return VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL, PRIVATE }, "user1");
244         } catch (Throwable e) {
245         }
246         return null;
247       }
248     };
249     response = NORMAL_USER1.runAs(action);
250     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response
251         .getResult(0).getException().getName());
252     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response
253         .getResult(1).getException().getName());
254 
255     action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
256       public VisibilityLabelsResponse run() throws Exception {
257         try {
258           return VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL, PRIVATE }, "user1");
259         } catch (Throwable e) {
260         }
261         return null;
262       }
263     };
264     response = SUPERUSER.runAs(action);
265     assertTrue(response.getResult(0).getException().getValue().isEmpty());
266     assertTrue(response.getResult(1).getException().getValue().isEmpty());
267 
268     action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
269       public VisibilityLabelsResponse run() throws Exception {
270         try {
271           return VisibilityClient.clearAuths(conf, new String[] { CONFIDENTIAL, PRIVATE }, "user1");
272         } catch (Throwable e) {
273         }
274         return null;
275       }
276     };
277     response = NORMAL_USER1.runAs(action);
278     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response.getResult(0)
279         .getException().getName());
280     assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response.getResult(1)
281         .getException().getName());
282 
283     response = VisibilityClient.clearAuths(conf, new String[] { CONFIDENTIAL, PRIVATE }, "user1");
284     assertTrue(response.getResult(0).getException().getValue().isEmpty());
285     assertTrue(response.getResult(1).getException().getValue().isEmpty());
286 
287     VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL, PRIVATE }, "user3");
288     PrivilegedExceptionAction<GetAuthsResponse> action1 = 
289         new PrivilegedExceptionAction<GetAuthsResponse>() {
290       public GetAuthsResponse run() throws Exception {
291         try {
292           return VisibilityClient.getAuths(conf, "user3");
293         } catch (Throwable e) {
294         }
295         return null;
296       }
297     };
298     GetAuthsResponse authsResponse = NORMAL_USER1.runAs(action1);
299     assertNull(authsResponse);
300     authsResponse = SUPERUSER.runAs(action1);
301     List<String> authsList = new ArrayList<String>();
302     for (ByteString authBS : authsResponse.getAuthList()) {
303       authsList.add(Bytes.toString(authBS.toByteArray()));
304     }
305     assertEquals(2, authsList.size());
306     assertTrue(authsList.contains(CONFIDENTIAL));
307     assertTrue(authsList.contains(PRIVATE));
308   }
309 
310   private static HTable createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
311       throws Exception {
312     HTable table = null;
313     try {
314       table = TEST_UTIL.createTable(tableName, fam);
315       int i = 1;
316       List<Put> puts = new ArrayList<Put>();
317       for (String labelExp : labelExps) {
318         Put put = new Put(Bytes.toBytes("row" + i));
319         put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value);
320         put.setCellVisibility(new CellVisibility(labelExp));
321         puts.add(put);
322         i++;
323       }
324       table.put(puts);
325     } finally {
326       if (table != null) {
327         table.close();
328       }
329     }
330     return table;
331   }
332 
333   private static void addLabels() throws IOException {
334     String[] labels = { SECRET, CONFIDENTIAL, PRIVATE };
335     try {
336       VisibilityClient.addLabels(conf, labels);
337     } catch (Throwable t) {
338       throw new IOException(t);
339     }
340   }
341 }