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