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