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.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertFalse; 023import static org.junit.jupiter.api.Assertions.assertTrue; 024import static org.junit.jupiter.api.Assertions.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.HBaseTestingUtil; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.TableName; 036import org.apache.hadoop.hbase.client.Connection; 037import org.apache.hadoop.hbase.client.ConnectionFactory; 038import org.apache.hadoop.hbase.client.Put; 039import org.apache.hadoop.hbase.client.Result; 040import org.apache.hadoop.hbase.client.ResultScanner; 041import org.apache.hadoop.hbase.client.Scan; 042import org.apache.hadoop.hbase.client.Table; 043import org.apache.hadoop.hbase.security.User; 044import org.apache.hadoop.hbase.testclassification.MediumTests; 045import org.apache.hadoop.hbase.testclassification.SecurityTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.junit.jupiter.api.AfterAll; 048import org.junit.jupiter.api.BeforeAll; 049import org.junit.jupiter.api.Tag; 050import org.junit.jupiter.api.Test; 051import org.junit.jupiter.api.TestInfo; 052 053import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 054 055import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; 056import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 057 058@Tag(SecurityTests.TAG) 059@Tag(MediumTests.TAG) 060public class TestVisibilityLablesWithGroups { 061 062 public static final String CONFIDENTIAL = "confidential"; 063 private static final String SECRET = "secret"; 064 public static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 065 private static final byte[] ROW_1 = Bytes.toBytes("row1"); 066 private final static byte[] CF = Bytes.toBytes("f"); 067 private final static byte[] Q1 = Bytes.toBytes("q1"); 068 private final static byte[] Q2 = Bytes.toBytes("q2"); 069 private final static byte[] Q3 = Bytes.toBytes("q3"); 070 private final static byte[] value1 = Bytes.toBytes("value1"); 071 private final static byte[] value2 = Bytes.toBytes("value2"); 072 private final static byte[] value3 = Bytes.toBytes("value3"); 073 public static Configuration conf; 074 075 public static User SUPERUSER; 076 public static User TESTUSER; 077 078 @BeforeAll 079 public static void setupBeforeClass() throws Exception { 080 // setup configuration 081 conf = TEST_UTIL.getConfiguration(); 082 VisibilityTestUtil.enableVisiblityLabels(conf); 083 // Not setting any SLG class. This means to use the default behavior. 084 // Use a group as the super user. 085 conf.set("hbase.superuser", "@supergroup"); 086 TEST_UTIL.startMiniCluster(1); 087 // 'admin' has super user permission because it is part of the 'supergroup' 088 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 089 // 'test' user will inherit 'testgroup' visibility labels 090 TESTUSER = User.createUserForTesting(conf, "test", new String[] { "testgroup" }); 091 092 // Wait for the labels table to become available 093 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 094 095 // Set up for the test 096 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 097 @Override 098 public Void run() throws Exception { 099 try (Connection conn = ConnectionFactory.createConnection(conf)) { 100 VisibilityClient.addLabels(conn, new String[] { SECRET, CONFIDENTIAL }); 101 // set auth for @testgroup 102 VisibilityClient.setAuths(conn, 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(TestInfo testInfo) throws Exception { 113 final TableName tableName = TableName.valueOf(testInfo.getTestMethod().get().getName()); 114 // create the table 115 TEST_UTIL.createTable(tableName, CF); 116 // put the data. 117 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 118 @Override 119 public Void run() throws Exception { 120 try (Connection connection = ConnectionFactory.createConnection(conf); 121 Table table = connection.getTable(tableName)) { 122 Put put = new Put(ROW_1); 123 put.addColumn(CF, Q1, HConstants.LATEST_TIMESTAMP, value1); 124 put.setCellVisibility(new CellVisibility(SECRET)); 125 table.put(put); 126 put = new Put(ROW_1); 127 put.addColumn(CF, Q2, HConstants.LATEST_TIMESTAMP, value2); 128 put.setCellVisibility(new CellVisibility(CONFIDENTIAL)); 129 table.put(put); 130 put = new Put(ROW_1); 131 put.addColumn(CF, Q3, HConstants.LATEST_TIMESTAMP, value3); 132 table.put(put); 133 } 134 return null; 135 } 136 }); 137 138 // 'admin' user is part of 'supergroup', thus can see all the cells. 139 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 140 @Override 141 public Void run() throws Exception { 142 try (Connection connection = ConnectionFactory.createConnection(conf); 143 Table table = connection.getTable(tableName)) { 144 Scan s = new Scan(); 145 ResultScanner scanner = table.getScanner(s); 146 Result[] next = scanner.next(1); 147 148 // Test that super user can see all the cells. 149 assertTrue(next.length == 1); 150 CellScanner cellScanner = next[0].cellScanner(); 151 cellScanner.advance(); 152 Cell current = cellScanner.current(); 153 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 154 current.getRowLength(), ROW_1, 0, ROW_1.length)); 155 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 156 current.getQualifierLength(), Q1, 0, Q1.length)); 157 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 158 current.getValueLength(), value1, 0, value1.length)); 159 cellScanner.advance(); 160 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(), Q2, 0, Q2.length)); 165 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 166 current.getValueLength(), value2, 0, value2.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(), Q3, 0, Q3.length)); 173 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 174 current.getValueLength(), value3, 0, value3.length)); 175 } 176 return null; 177 } 178 }); 179 180 // Get testgroup's labels. 181 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 182 @Override 183 public Void run() throws Exception { 184 GetAuthsResponse authsResponse = null; 185 try (Connection conn = ConnectionFactory.createConnection(conf)) { 186 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 187 } catch (Throwable e) { 188 fail("Should not have failed"); 189 } 190 List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size()); 191 for (ByteString authBS : authsResponse.getAuthList()) { 192 authsList.add(Bytes.toString(authBS.toByteArray())); 193 } 194 assertEquals(1, authsList.size()); 195 assertTrue(authsList.contains(CONFIDENTIAL)); 196 return null; 197 } 198 }); 199 200 // Test that test user can see what 'testgroup' has been authorized to. 201 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 202 @Override 203 public Void run() throws Exception { 204 try (Connection connection = ConnectionFactory.createConnection(conf); 205 Table table = connection.getTable(tableName)) { 206 // Test scan with no auth attribute 207 Scan s = new Scan(); 208 ResultScanner scanner = table.getScanner(s); 209 Result[] next = scanner.next(1); 210 211 assertTrue(next.length == 1); 212 CellScanner cellScanner = next[0].cellScanner(); 213 cellScanner.advance(); 214 Cell current = cellScanner.current(); 215 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 216 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 217 current.getRowLength(), ROW_1, 0, ROW_1.length)); 218 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 219 current.getQualifierLength(), Q2, 0, Q2.length)); 220 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 221 current.getValueLength(), value2, 0, value2.length)); 222 cellScanner.advance(); 223 current = cellScanner.current(); 224 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 225 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 226 current.getRowLength(), ROW_1, 0, ROW_1.length)); 227 assertTrue(Bytes.equals(current.getQualifierArray(), current.getQualifierOffset(), 228 current.getQualifierLength(), Q3, 0, Q3.length)); 229 assertTrue(Bytes.equals(current.getValueArray(), current.getValueOffset(), 230 current.getValueLength(), value3, 0, value3.length)); 231 232 // Test scan with correct auth attribute for test user 233 Scan s1 = new Scan(); 234 // test user is entitled to 'CONFIDENTIAL'. 235 // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs. 236 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 237 ResultScanner scanner1 = table.getScanner(s1); 238 Result[] next1 = scanner1.next(1); 239 240 assertTrue(next1.length == 1); 241 CellScanner cellScanner1 = next1[0].cellScanner(); 242 cellScanner1.advance(); 243 Cell current1 = cellScanner1.current(); 244 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 245 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 246 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 247 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 248 current1.getQualifierLength(), Q2, 0, Q2.length)); 249 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 250 current1.getValueLength(), value2, 0, value2.length)); 251 cellScanner1.advance(); 252 current1 = cellScanner1.current(); 253 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 254 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 255 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 256 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 257 current1.getQualifierLength(), Q3, 0, Q3.length)); 258 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 259 current1.getValueLength(), value3, 0, value3.length)); 260 261 // Test scan with incorrect auth attribute for test user 262 Scan s2 = new Scan(); 263 // test user is entitled to 'CONFIDENTIAL'. 264 // If we set 'SECRET', it will be dropped by the SLGs. 265 s2.setAuthorizations(new Authorizations(new String[] { SECRET })); 266 ResultScanner scanner2 = table.getScanner(s2); 267 Result next2 = scanner2.next(); 268 CellScanner cellScanner2 = next2.cellScanner(); 269 cellScanner2.advance(); 270 Cell current2 = cellScanner2.current(); 271 // This scan will only see value3 (no label) 272 assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(), 273 current2.getRowLength(), ROW_1, 0, ROW_1.length)); 274 assertTrue(Bytes.equals(current2.getQualifierArray(), current2.getQualifierOffset(), 275 current2.getQualifierLength(), Q3, 0, Q3.length)); 276 assertTrue(Bytes.equals(current2.getValueArray(), current2.getValueOffset(), 277 current2.getValueLength(), value3, 0, value3.length)); 278 279 assertFalse(cellScanner2.advance()); 280 } 281 return null; 282 } 283 }); 284 285 // Clear 'testgroup' of CONFIDENTIAL label. 286 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 287 @Override 288 public Void run() throws Exception { 289 VisibilityLabelsResponse response = null; 290 try (Connection conn = ConnectionFactory.createConnection(conf)) { 291 response = VisibilityClient.clearAuths(conn, new String[] { CONFIDENTIAL }, "@testgroup"); 292 } catch (Throwable e) { 293 fail("Should not have failed"); 294 } 295 return null; 296 } 297 }); 298 299 // Get testgroup's labels. No label is returned. 300 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 301 @Override 302 public Void run() throws Exception { 303 GetAuthsResponse authsResponse = null; 304 try (Connection conn = ConnectionFactory.createConnection(conf)) { 305 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 306 } catch (Throwable e) { 307 fail("Should not have failed"); 308 } 309 List<String> authsList = new ArrayList<>(authsResponse.getAuthList().size()); 310 for (ByteString authBS : authsResponse.getAuthList()) { 311 authsList.add(Bytes.toString(authBS.toByteArray())); 312 } 313 assertEquals(0, authsList.size()); 314 return null; 315 } 316 }); 317 318 // Test that test user cannot see the cells with the labels anymore. 319 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 320 @Override 321 public Void run() throws Exception { 322 try (Connection connection = ConnectionFactory.createConnection(conf); 323 Table table = connection.getTable(tableName)) { 324 Scan s1 = new Scan(); 325 // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped 326 // testgroup's label. test user has no auth labels now. 327 // scan's labels will be dropped on the server side. 328 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 329 ResultScanner scanner1 = table.getScanner(s1); 330 Result[] next1 = scanner1.next(1); 331 332 assertTrue(next1.length == 1); 333 CellScanner cellScanner1 = next1[0].cellScanner(); 334 cellScanner1.advance(); 335 Cell current1 = cellScanner1.current(); 336 // test user can only see value3 (no label) 337 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 338 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 339 assertTrue(Bytes.equals(current1.getQualifierArray(), current1.getQualifierOffset(), 340 current1.getQualifierLength(), Q3, 0, Q3.length)); 341 assertTrue(Bytes.equals(current1.getValueArray(), current1.getValueOffset(), 342 current1.getValueLength(), value3, 0, value3.length)); 343 344 assertFalse(cellScanner1.advance()); 345 } 346 return null; 347 } 348 }); 349 350 } 351 352 @AfterAll 353 public static void tearDownAfterClass() throws Exception { 354 TEST_UTIL.shutdownMiniCluster(); 355 } 356}