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 setupBeforeClass()80 public static void setupBeforeClass() throws Exception { 81 // setup configuration 82 conf = TEST_UTIL.getConfiguration(); 83 conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10); 84 SecureTestUtil.enableSecurity(conf); 85 conf.set("hbase.coprocessor.master.classes", AccessController.class.getName() + "," 86 + VisibilityController.class.getName()); 87 conf.set("hbase.coprocessor.region.classes", AccessController.class.getName() + "," 88 + VisibilityController.class.getName()); 89 TEST_UTIL.startMiniCluster(2); 90 91 TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName(), 50000); 92 // Wait for the labels table to become available 93 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 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 tearDownAfterClass()110 public static void tearDownAfterClass() throws Exception { 111 TEST_UTIL.shutdownMiniCluster(); 112 } 113 114 @Test testScanForUserWithFewerLabelAuthsThanLabelsInScanAuthorizations()115 public void testScanForUserWithFewerLabelAuthsThanLabelsInScanAuthorizations() throws Throwable { 116 String[] auths = { SECRET }; 117 String user = "user2"; 118 VisibilityClient.setAuths(TEST_UTIL.getConnection(), 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 testScanForSuperUserWithFewerLabelAuths()144 public void testScanForSuperUserWithFewerLabelAuths() throws Throwable { 145 String[] auths = { SECRET }; 146 String user = "admin"; 147 try (Connection conn = ConnectionFactory.createConnection(conf)) { 148 VisibilityClient.setAuths(conn, auths, user); 149 } 150 TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 151 final Table table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL 152 + "&!" + PRIVATE, SECRET + "&!" + PRIVATE); 153 PrivilegedExceptionAction<Void> scanAction = new PrivilegedExceptionAction<Void>() { 154 public Void run() throws Exception { 155 Scan s = new Scan(); 156 s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL)); 157 try (Connection connection = ConnectionFactory.createConnection(conf); 158 Table t = connection.getTable(table.getName())) { 159 ResultScanner scanner = t.getScanner(s); 160 Result[] result = scanner.next(5); 161 assertTrue(result.length == 2); 162 } 163 return null; 164 } 165 }; 166 SUPERUSER.runAs(scanAction); 167 } 168 169 @Test testGetForSuperUserWithFewerLabelAuths()170 public void testGetForSuperUserWithFewerLabelAuths() throws Throwable { 171 String[] auths = { SECRET }; 172 String user = "admin"; 173 VisibilityClient.setAuths(TEST_UTIL.getConnection(), auths, user); 174 TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 175 final Table table = createTableAndWriteDataWithLabels(tableName, SECRET + "&" + CONFIDENTIAL 176 + "&!" + PRIVATE, SECRET + "&!" + PRIVATE); 177 PrivilegedExceptionAction<Void> scanAction = new PrivilegedExceptionAction<Void>() { 178 public Void run() throws Exception { 179 Get g = new Get(row1); 180 g.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL)); 181 try (Connection connection = ConnectionFactory.createConnection(conf); 182 Table t = connection.getTable(table.getName())) { 183 Result result = t.get(g); 184 assertTrue(!result.isEmpty()); 185 } 186 return null; 187 } 188 }; 189 SUPERUSER.runAs(scanAction); 190 } 191 192 @Test testVisibilityLabelsForUserWithNoAuths()193 public void testVisibilityLabelsForUserWithNoAuths() throws Throwable { 194 String user = "admin"; 195 String[] auths = { SECRET }; 196 try (Connection conn = ConnectionFactory.createConnection(conf)) { 197 VisibilityClient.clearAuths(conn, auths, user); // Removing all auths if any. 198 VisibilityClient.setAuths(conn, auths, "user1"); 199 } 200 TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 201 final Table table = createTableAndWriteDataWithLabels(tableName, SECRET); 202 SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER1.getShortName(), tableName, 203 null, null, Permission.Action.READ); 204 SecureTestUtil.grantOnTable(TEST_UTIL, NORMAL_USER2.getShortName(), tableName, 205 null, null, Permission.Action.READ); 206 PrivilegedExceptionAction<Void> getAction = new PrivilegedExceptionAction<Void>() { 207 public Void run() throws Exception { 208 Get g = new Get(row1); 209 g.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL)); 210 try (Connection connection = ConnectionFactory.createConnection(conf); 211 Table t = connection.getTable(table.getName())) { 212 Result result = t.get(g); 213 assertTrue(result.isEmpty()); 214 } 215 return null; 216 } 217 }; 218 NORMAL_USER2.runAs(getAction); 219 } 220 221 @Test testLabelsTableOpsWithDifferentUsers()222 public void testLabelsTableOpsWithDifferentUsers() throws Throwable { 223 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 224 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 225 public VisibilityLabelsResponse run() throws Exception { 226 try (Connection conn = ConnectionFactory.createConnection(conf)) { 227 return VisibilityClient.addLabels(conn, new String[] { "l1", "l2" }); 228 } catch (Throwable e) { 229 } 230 return null; 231 } 232 }; 233 VisibilityLabelsResponse response = NORMAL_USER1.runAs(action); 234 assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response 235 .getResult(0).getException().getName()); 236 assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response 237 .getResult(1).getException().getName()); 238 239 action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 240 public VisibilityLabelsResponse run() throws Exception { 241 try (Connection conn = ConnectionFactory.createConnection(conf)) { 242 return VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL, PRIVATE }, "user1"); 243 } catch (Throwable e) { 244 } 245 return null; 246 } 247 }; 248 response = NORMAL_USER1.runAs(action); 249 assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response 250 .getResult(0).getException().getName()); 251 assertEquals("org.apache.hadoop.hbase.security.AccessDeniedException", response 252 .getResult(1).getException().getName()); 253 254 action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 255 public VisibilityLabelsResponse run() throws Exception { 256 try (Connection conn = ConnectionFactory.createConnection(conf)) { 257 return VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL, PRIVATE }, "user1"); 258 } catch (Throwable e) { 259 } 260 return null; 261 } 262 }; 263 response = SUPERUSER.runAs(action); 264 assertTrue(response.getResult(0).getException().getValue().isEmpty()); 265 assertTrue(response.getResult(1).getException().getValue().isEmpty()); 266 267 action = new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 268 public VisibilityLabelsResponse run() throws Exception { 269 try (Connection conn = ConnectionFactory.createConnection(conf)) { 270 return VisibilityClient.clearAuths(conn, new String[] { 271 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(TEST_UTIL.getConnection(), new String[] { CONFIDENTIAL, 284 PRIVATE }, "user1"); 285 assertTrue(response.getResult(0).getException().getValue().isEmpty()); 286 assertTrue(response.getResult(1).getException().getValue().isEmpty()); 287 288 VisibilityClient.setAuths(TEST_UTIL.getConnection(), new String[] { CONFIDENTIAL, PRIVATE }, 289 "user3"); 290 PrivilegedExceptionAction<GetAuthsResponse> action1 = 291 new PrivilegedExceptionAction<GetAuthsResponse>() { 292 public GetAuthsResponse run() throws Exception { 293 try (Connection conn = ConnectionFactory.createConnection(conf)) { 294 return VisibilityClient.getAuths(conn, "user3"); 295 } catch (Throwable e) { 296 } 297 return null; 298 } 299 }; 300 GetAuthsResponse authsResponse = NORMAL_USER1.runAs(action1); 301 assertNull(authsResponse); 302 authsResponse = SUPERUSER.runAs(action1); 303 List<String> authsList = new ArrayList<String>(); 304 for (ByteString authBS : authsResponse.getAuthList()) { 305 authsList.add(Bytes.toString(authBS.toByteArray())); 306 } 307 assertEquals(2, authsList.size()); 308 assertTrue(authsList.contains(CONFIDENTIAL)); 309 assertTrue(authsList.contains(PRIVATE)); 310 } 311 createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)312 private static Table createTableAndWriteDataWithLabels(TableName tableName, String... labelExps) 313 throws Exception { 314 Table table = null; 315 try { 316 table = TEST_UTIL.createTable(tableName, fam); 317 int i = 1; 318 List<Put> puts = new ArrayList<Put>(); 319 for (String labelExp : labelExps) { 320 Put put = new Put(Bytes.toBytes("row" + i)); 321 put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value); 322 put.setCellVisibility(new CellVisibility(labelExp)); 323 puts.add(put); 324 i++; 325 } 326 table.put(puts); 327 } finally { 328 if (table != null) { 329 table.close(); 330 } 331 } 332 return table; 333 } 334 addLabels()335 private static void addLabels() throws IOException { 336 String[] labels = { SECRET, CONFIDENTIAL, PRIVATE }; 337 try { 338 VisibilityClient.addLabels(TEST_UTIL.getConnection(), labels); 339 } catch (Throwable t) { 340 throw new IOException(t); 341 } 342 } 343 } 344