001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security.visibility; 019 020import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.security.PrivilegedExceptionAction; 028import java.util.ArrayList; 029import java.util.List; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.Cell; 032import org.apache.hadoop.hbase.CellScanner; 033import org.apache.hadoop.hbase.HBaseClassTestRule; 034import org.apache.hadoop.hbase.HBaseTestingUtil; 035import org.apache.hadoop.hbase.HConstants; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.Connection; 038import org.apache.hadoop.hbase.client.ConnectionFactory; 039import org.apache.hadoop.hbase.client.Put; 040import org.apache.hadoop.hbase.client.Result; 041import org.apache.hadoop.hbase.client.ResultScanner; 042import org.apache.hadoop.hbase.client.Scan; 043import org.apache.hadoop.hbase.client.Table; 044import org.apache.hadoop.hbase.security.User; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.testclassification.SecurityTests; 047import org.apache.hadoop.hbase.util.Bytes; 048import org.junit.AfterClass; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Rule; 052import org.junit.Test; 053import org.junit.experimental.categories.Category; 054import org.junit.rules.TestName; 055 056import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 057 058import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; 059import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 060 061@Category({ SecurityTests.class, MediumTests.class }) 062public class TestVisibilityLablesWithGroups { 063 064 @ClassRule 065 public static final HBaseClassTestRule CLASS_RULE = 066 HBaseClassTestRule.forClass(TestVisibilityLablesWithGroups.class); 067 068 public static final String CONFIDENTIAL = "confidential"; 069 private static final String SECRET = "secret"; 070 public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 071 private static final byte[] ROW_1 = Bytes.toBytes("row1"); 072 private final static byte[] CF = Bytes.toBytes("f"); 073 private final static byte[] Q1 = Bytes.toBytes("q1"); 074 private final static byte[] Q2 = Bytes.toBytes("q2"); 075 private final static byte[] Q3 = Bytes.toBytes("q3"); 076 private final static byte[] value1 = Bytes.toBytes("value1"); 077 private final static byte[] value2 = Bytes.toBytes("value2"); 078 private final static byte[] value3 = Bytes.toBytes("value3"); 079 public static Configuration conf; 080 081 @Rule 082 public final TestName TEST_NAME = new TestName(); 083 public static User SUPERUSER; 084 public static User TESTUSER; 085 086 @BeforeClass 087 public static void setupBeforeClass() throws Exception { 088 // setup configuration 089 conf = TEST_UTIL.getConfiguration(); 090 VisibilityTestUtil.enableVisiblityLabels(conf); 091 // Not setting any SLG class. This means to use the default behavior. 092 // Use a group as the super user. 093 conf.set("hbase.superuser", "@supergroup"); 094 TEST_UTIL.startMiniCluster(1); 095 // 'admin' has super user permission because it is part of the 'supergroup' 096 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 097 // 'test' user will inherit 'testgroup' visibility labels 098 TESTUSER = User.createUserForTesting(conf, "test", new String[] { "testgroup" }); 099 100 // Wait for the labels table to become available 101 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 102 103 // Set up for the test 104 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 105 @Override 106 public Void run() throws Exception { 107 try (Connection conn = ConnectionFactory.createConnection(conf)) { 108 VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL }); 109 // set auth for @testgroup 110 VisibilityClient.setAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup"); 111 } catch (Throwable t) { 112 throw new IOException(t); 113 } 114 return null; 115 } 116 }); 117 } 118 119 @Test 120 public void testGroupAuths() throws Exception { 121 final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 122 // create the table 123 TEST_UTIL.createTable(tableName, CF); 124 // put the data. 125 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 126 @Override 127 public Void run() throws Exception { 128 try (Connection connection = ConnectionFactory.createConnection(conf); 129 Table table = connection.getTable(tableName)) { 130 Put put = new Put(ROW_1); 131 put.addColumn(CF, Q1, HConstants.LATEST_TIMESTAMP, value1); 132 put.setCellVisibility(new CellVisibility(SECRET)); 133 table.put(put); 134 put = new Put(ROW_1); 135 put.addColumn(CF, Q2, HConstants.LATEST_TIMESTAMP, value2); 136 put.setCellVisibility(new CellVisibility(CONFIDENTIAL)); 137 table.put(put); 138 put = new Put(ROW_1); 139 put.addColumn(CF, Q3, HConstants.LATEST_TIMESTAMP, value3); 140 table.put(put); 141 } 142 return null; 143 } 144 }); 145 146 // 'admin' user is part of 'supergroup', thus can see all the cells. 147 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 148 @Override 149 public Void run() throws Exception { 150 try (Connection connection = ConnectionFactory.createConnection(conf); 151 Table table = connection.getTable(tableName)) { 152 Scan s = new Scan(); 153 ResultScanner scanner = table.getScanner(s); 154 Result[] next = scanner.next(1); 155 156 // Test that super user can see all the cells. 157 assertTrue(next.length == 1); 158 CellScanner cellScanner = next[0].cellScanner(); 159 cellScanner.advance(); 160 Cell current = cellScanner.current(); 161 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 162 current.getRowLength(), ROW_1, 0, ROW_1.length)); 163 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 164 current.getQualifierLength(), Q1, 0, Q1.length)); 165 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 166 current.getValueLength(), value1, 0, value1.length)); 167 cellScanner.advance(); 168 current = cellScanner.current(); 169 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 170 current.getRowLength(), ROW_1, 0, ROW_1.length)); 171 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 172 current.getQualifierLength(), Q2, 0, Q2.length)); 173 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 174 current.getValueLength(), value2, 0, value2.length)); 175 cellScanner.advance(); 176 current = cellScanner.current(); 177 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 178 current.getRowLength(), ROW_1, 0, ROW_1.length)); 179 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 180 current.getQualifierLength(), Q3, 0, Q3.length)); 181 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 182 current.getValueLength(), value3, 0, value3.length)); 183 } 184 return null; 185 } 186 }); 187 188 // Get testgroup's labels. 189 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 190 @Override 191 public Void run() throws Exception { 192 GetAuthsResponse authsResponse = null; 193 try (Connection conn = ConnectionFactory.createConnection(conf)) { 194 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 195 } catch (Throwable e) { 196 fail("Should not have failed"); 197 } 198 List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size()); 199 for (ByteString authBS : authsResponse.getAuthList()) { 200 authsList.add(Bytes.toString(authBS.toByteArray())); 201 } 202 assertEquals(1, authsList.size()); 203 assertTrue(authsList.contains(CONFIDENTIAL)); 204 return null; 205 } 206 }); 207 208 // Test that test user can see what 'testgroup' has been authorized to. 209 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 210 @Override 211 public Void run() throws Exception { 212 try (Connection connection = ConnectionFactory.createConnection(conf); 213 Table table = connection.getTable(tableName)) { 214 // Test scan with no auth attribute 215 Scan s = new Scan(); 216 ResultScanner scanner = table.getScanner(s); 217 Result[] next = scanner.next(1); 218 219 assertTrue(next.length == 1); 220 CellScanner cellScanner = next[0].cellScanner(); 221 cellScanner.advance(); 222 Cell current = cellScanner.current(); 223 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 224 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 225 current.getRowLength(), ROW_1, 0, ROW_1.length)); 226 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 227 current.getQualifierLength(), Q2, 0, Q2.length)); 228 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 229 current.getValueLength(), value2, 0, value2.length)); 230 cellScanner.advance(); 231 current = cellScanner.current(); 232 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 233 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 234 current.getRowLength(), ROW_1, 0, ROW_1.length)); 235 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 236 current.getQualifierLength(), Q3, 0, Q3.length)); 237 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 238 current.getValueLength(), value3, 0, value3.length)); 239 240 // Test scan with correct auth attribute for test user 241 Scan s1 = new Scan(); 242 // test user is entitled to 'CONFIDENTIAL'. 243 // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs. 244 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 245 ResultScanner scanner1 = table.getScanner(s1); 246 Result[] next1 = scanner1.next(1); 247 248 assertTrue(next1.length == 1); 249 CellScanner cellScanner1 = next1[0].cellScanner(); 250 cellScanner1.advance(); 251 Cell current1 = cellScanner1.current(); 252 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 253 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 254 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 255 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 256 current1.getQualifierLength(), Q2, 0, Q2.length)); 257 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 258 current1.getValueLength(), value2, 0, value2.length)); 259 cellScanner1.advance(); 260 current1 = cellScanner1.current(); 261 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 262 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 263 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 264 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 265 current1.getQualifierLength(), Q3, 0, Q3.length)); 266 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 267 current1.getValueLength(), value3, 0, value3.length)); 268 269 // Test scan with incorrect auth attribute for test user 270 Scan s2 = new Scan(); 271 // test user is entitled to 'CONFIDENTIAL'. 272 // If we set 'SECRET', it will be dropped by the SLGs. 273 s2.setAuthorizations(new Authorizations(new String[] { SECRET })); 274 ResultScanner scanner2 = table.getScanner(s2); 275 Result next2 = scanner2.next(); 276 CellScanner cellScanner2 = next2.cellScanner(); 277 cellScanner2.advance(); 278 Cell current2 = cellScanner2.current(); 279 // This scan will only see value3 (no label) 280 assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(), 281 current2.getRowLength(), ROW_1, 0, ROW_1.length)); 282 assertTrue(Bytes.equals(current2.getQualifierArray(), current2.getQualifierOffset(), 283 current2.getQualifierLength(), Q3, 0, Q3.length)); 284 assertTrue(Bytes.equals(current2.getValueArray(), current2.getValueOffset(), 285 current2.getValueLength(), value3, 0, value3.length)); 286 287 assertFalse(cellScanner2.advance()); 288 } 289 return null; 290 } 291 }); 292 293 // Clear 'testgroup' of CONFIDENTIAL label. 294 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 295 @Override 296 public Void run() throws Exception { 297 VisibilityLabelsResponse response = null; 298 try (Connection conn = ConnectionFactory.createConnection(conf)) { 299 response = VisibilityClient.clearAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup"); 300 } catch (Throwable e) { 301 fail("Should not have failed"); 302 } 303 return null; 304 } 305 }); 306 307 // Get testgroup's labels. No label is returned. 308 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 309 @Override 310 public Void run() throws Exception { 311 GetAuthsResponse authsResponse = null; 312 try (Connection conn = ConnectionFactory.createConnection(conf)) { 313 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 314 } catch (Throwable e) { 315 fail("Should not have failed"); 316 } 317 List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size()); 318 for (ByteString authBS : authsResponse.getAuthList()) { 319 authsList.add(Bytes.toString(authBS.toByteArray())); 320 } 321 assertEquals(0, authsList.size()); 322 return null; 323 } 324 }); 325 326 // Test that test user cannot see the cells with the labels anymore. 327 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 328 @Override 329 public Void run() throws Exception { 330 try (Connection connection = ConnectionFactory.createConnection(conf); 331 Table table = connection.getTable(tableName)) { 332 Scan s1 = new Scan(); 333 // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped 334 // testgroup's label. test user has no auth labels now. 335 // scan's labels will be dropped on the server side. 336 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 337 ResultScanner scanner1 = table.getScanner(s1); 338 Result[] next1 = scanner1.next(1); 339 340 assertTrue(next1.length == 1); 341 CellScanner cellScanner1 = next1[0].cellScanner(); 342 cellScanner1.advance(); 343 Cell current1 = cellScanner1.current(); 344 // test user can only see value3 (no label) 345 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 346 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 347 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 348 current1.getQualifierLength(), Q3, 0, Q3.length)); 349 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 350 current1.getValueLength(), value3, 0, value3.length)); 351 352 assertFalse(cellScanner1.advance()); 353 } 354 return null; 355 } 356 }); 357 358 } 359 360 @AfterClass 361 public static void tearDownAfterClass() throws Exception { 362 TEST_UTIL.shutdownMiniCluster(); 363 } 364}