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.assertFalse; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import java.io.IOException; 27 import java.security.PrivilegedExceptionAction; 28 import java.util.ArrayList; 29 import java.util.List; 30 31 import org.apache.hadoop.conf.Configuration; 32 import org.apache.hadoop.hbase.Cell; 33 import org.apache.hadoop.hbase.CellScanner; 34 import org.apache.hadoop.hbase.HBaseTestingUtility; 35 import org.apache.hadoop.hbase.HConstants; 36 import org.apache.hadoop.hbase.TableName; 37 import org.apache.hadoop.hbase.client.Connection; 38 import org.apache.hadoop.hbase.client.ConnectionFactory; 39 import org.apache.hadoop.hbase.client.Put; 40 import org.apache.hadoop.hbase.client.Result; 41 import org.apache.hadoop.hbase.client.ResultScanner; 42 import org.apache.hadoop.hbase.client.Scan; 43 import org.apache.hadoop.hbase.client.Table; 44 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse; 45 import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 46 import org.apache.hadoop.hbase.security.User; 47 import org.apache.hadoop.hbase.testclassification.MediumTests; 48 import org.apache.hadoop.hbase.util.Bytes; 49 import org.junit.AfterClass; 50 import org.junit.BeforeClass; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.experimental.categories.Category; 54 import org.junit.rules.TestName; 55 56 import com.google.protobuf.ByteString; 57 58 @Category(MediumTests.class) 59 public class TestVisibilityLablesWithGroups { 60 61 public static final String CONFIDENTIAL = "confidential"; 62 private static final String SECRET = "secret"; 63 public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 64 private static final byte[] ROW_1 = Bytes.toBytes("row1"); 65 private final static byte[] CF = Bytes.toBytes("f"); 66 private final static byte[] Q1 = Bytes.toBytes("q1"); 67 private final static byte[] Q2 = Bytes.toBytes("q2"); 68 private final static byte[] Q3 = Bytes.toBytes("q3"); 69 private final static byte[] value1 = Bytes.toBytes("value1"); 70 private final static byte[] value2 = Bytes.toBytes("value2"); 71 private final static byte[] value3 = Bytes.toBytes("value3"); 72 public static Configuration conf; 73 74 @Rule 75 public final TestName TEST_NAME = new TestName(); 76 public static User SUPERUSER; 77 public static User TESTUSER; 78 79 @BeforeClass setupBeforeClass()80 public static void setupBeforeClass() throws Exception { 81 // setup configuration 82 conf = TEST_UTIL.getConfiguration(); 83 VisibilityTestUtil.enableVisiblityLabels(conf); 84 // Not setting any SLG class. This means to use the default behavior. 85 // Use a group as the super user. 86 conf.set("hbase.superuser", "@supergroup"); 87 TEST_UTIL.startMiniCluster(1); 88 // 'admin' has super user permission because it is part of the 'supergroup' 89 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 90 // 'test' user will inherit 'testgroup' visibility labels 91 TESTUSER = User.createUserForTesting(conf, "test", new String[] {"testgroup" }); 92 93 // Wait for the labels table to become available 94 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 95 96 // Set up for the test 97 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 98 public Void run() throws Exception { 99 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 testGroupAuths()112 public void testGroupAuths() throws Exception { 113 final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 114 // create the table 115 TEST_UTIL.createTable(tableName, CF); 116 // put the data. 117 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 118 public Void run() throws Exception { 119 try (Connection connection = ConnectionFactory.createConnection(conf); 120 Table table = connection.getTable(tableName)) { 121 Put put = new Put(ROW_1); 122 put.add(CF, Q1, HConstants.LATEST_TIMESTAMP, value1); 123 put.setCellVisibility(new CellVisibility(SECRET)); 124 table.put(put); 125 put = new Put(ROW_1); 126 put.add(CF, Q2, HConstants.LATEST_TIMESTAMP, value2); 127 put.setCellVisibility(new CellVisibility(CONFIDENTIAL)); 128 table.put(put); 129 put = new Put(ROW_1); 130 put.add(CF, Q3, HConstants.LATEST_TIMESTAMP, value3); 131 table.put(put); 132 } 133 return null; 134 } 135 }); 136 137 // 'admin' user is part of 'supergroup', thus can see all the cells. 138 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 139 public Void run() throws Exception { 140 try (Connection connection = ConnectionFactory.createConnection(conf); 141 Table table = connection.getTable(tableName)) { 142 Scan s = new Scan(); 143 ResultScanner scanner = table.getScanner(s); 144 Result[] next = scanner.next(1); 145 146 // Test that super user can see all the cells. 147 assertTrue(next.length == 1); 148 CellScanner cellScanner = next[0].cellScanner(); 149 cellScanner.advance(); 150 Cell current = cellScanner.current(); 151 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 152 current.getRowLength(), ROW_1, 0, ROW_1.length)); 153 assertTrue(Bytes.equals(current.getQualifier(), Q1)); 154 assertTrue(Bytes.equals(current.getValue(), value1)); 155 cellScanner.advance(); 156 current = cellScanner.current(); 157 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 158 current.getRowLength(), ROW_1, 0, ROW_1.length)); 159 assertTrue(Bytes.equals(current.getQualifier(), Q2)); 160 assertTrue(Bytes.equals(current.getValue(), value2)); 161 cellScanner.advance(); 162 current = cellScanner.current(); 163 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 164 current.getRowLength(), ROW_1, 0, ROW_1.length)); 165 assertTrue(Bytes.equals(current.getQualifier(), Q3)); 166 assertTrue(Bytes.equals(current.getValue(), value3)); 167 } 168 return null; 169 } 170 }); 171 172 // Get testgroup's labels. 173 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 174 public Void run() throws Exception { 175 GetAuthsResponse authsResponse = null; 176 try (Connection conn = ConnectionFactory.createConnection(conf)) { 177 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 178 } catch (Throwable e) { 179 fail("Should not have failed"); 180 } 181 List<String> authsList = new ArrayList<String>(); 182 for (ByteString authBS : authsResponse.getAuthList()) { 183 authsList.add(Bytes.toString(authBS.toByteArray())); 184 } 185 assertEquals(1, authsList.size()); 186 assertTrue(authsList.contains(CONFIDENTIAL)); 187 return null; 188 } 189 }); 190 191 // Test that test user can see what 'testgroup' has been authorized to. 192 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 193 public Void run() throws Exception { 194 try (Connection connection = ConnectionFactory.createConnection(conf); 195 Table table = connection.getTable(tableName)) { 196 // Test scan with no auth attribute 197 Scan s = new Scan(); 198 ResultScanner scanner = table.getScanner(s); 199 Result[] next = scanner.next(1); 200 201 assertTrue(next.length == 1); 202 CellScanner cellScanner = next[0].cellScanner(); 203 cellScanner.advance(); 204 Cell current = cellScanner.current(); 205 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 206 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 207 current.getRowLength(), ROW_1, 0, ROW_1.length)); 208 assertTrue(Bytes.equals(current.getQualifier(), Q2)); 209 assertTrue(Bytes.equals(current.getValue(), value2)); 210 cellScanner.advance(); 211 current = cellScanner.current(); 212 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 213 assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), 214 current.getRowLength(), ROW_1, 0, ROW_1.length)); 215 assertTrue(Bytes.equals(current.getQualifier(), Q3)); 216 assertTrue(Bytes.equals(current.getValue(), value3)); 217 218 // Test scan with correct auth attribute for test user 219 Scan s1 = new Scan(); 220 // test user is entitled to 'CONFIDENTIAL'. 221 // If we set both labels in the scan, 'SECRET' will be dropped by the SLGs. 222 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 223 ResultScanner scanner1 = table.getScanner(s1); 224 Result[] next1 = scanner1.next(1); 225 226 assertTrue(next1.length == 1); 227 CellScanner cellScanner1 = next1[0].cellScanner(); 228 cellScanner1.advance(); 229 Cell current1 = cellScanner1.current(); 230 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 231 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 232 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 233 assertTrue(Bytes.equals(current1.getQualifier(), Q2)); 234 assertTrue(Bytes.equals(current1.getValue(), value2)); 235 cellScanner1.advance(); 236 current1 = cellScanner1.current(); 237 // test user can see value2 (CONFIDENTIAL) and value3 (no label) 238 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 239 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 240 assertTrue(Bytes.equals(current1.getQualifier(), Q3)); 241 assertTrue(Bytes.equals(current1.getValue(), value3)); 242 243 // Test scan with incorrect auth attribute for test user 244 Scan s2 = new Scan(); 245 // test user is entitled to 'CONFIDENTIAL'. 246 // If we set 'SECRET', it will be dropped by the SLGs. 247 s2.setAuthorizations(new Authorizations(new String[] { SECRET })); 248 ResultScanner scanner2 = table.getScanner(s2); 249 Result next2 = scanner2.next(); 250 CellScanner cellScanner2 = next2.cellScanner(); 251 cellScanner2.advance(); 252 Cell current2 = cellScanner2.current(); 253 // This scan will only see value3 (no label) 254 assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(), 255 current2.getRowLength(), ROW_1, 0, ROW_1.length)); 256 assertTrue(Bytes.equals(current2.getQualifier(), Q3)); 257 assertTrue(Bytes.equals(current2.getValue(), value3)); 258 259 assertFalse(cellScanner2.advance()); 260 } 261 return null; 262 } 263 }); 264 265 // Clear 'testgroup' of CONFIDENTIAL label. 266 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 267 public Void run() throws Exception { 268 VisibilityLabelsResponse response = null; 269 try (Connection conn = ConnectionFactory.createConnection(conf)) { 270 response = VisibilityClient.clearAuths(conn, new String[] { 271 CONFIDENTIAL }, "@testgroup"); 272 } catch (Throwable e) { 273 fail("Should not have failed"); 274 } 275 return null; 276 } 277 }); 278 279 // Get testgroup's labels. No label is returned. 280 SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() { 281 public Void run() throws Exception { 282 GetAuthsResponse authsResponse = null; 283 try (Connection conn = ConnectionFactory.createConnection(conf)) { 284 authsResponse = VisibilityClient.getAuths(conn, "@testgroup"); 285 } catch (Throwable e) { 286 fail("Should not have failed"); 287 } 288 List<String> authsList = new ArrayList<String>(); 289 for (ByteString authBS : authsResponse.getAuthList()) { 290 authsList.add(Bytes.toString(authBS.toByteArray())); 291 } 292 assertEquals(0, authsList.size()); 293 return null; 294 } 295 }); 296 297 // Test that test user cannot see the cells with the labels anymore. 298 TESTUSER.runAs(new PrivilegedExceptionAction<Void>() { 299 public Void run() throws Exception { 300 try (Connection connection = ConnectionFactory.createConnection(conf); 301 Table table = connection.getTable(tableName)) { 302 Scan s1 = new Scan(); 303 // test user is not entitled to 'CONFIDENTIAL' anymore since we dropped 304 // testgroup's label. test user has no auth labels now. 305 // scan's labels will be dropped on the server side. 306 s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL })); 307 ResultScanner scanner1 = table.getScanner(s1); 308 Result[] next1 = scanner1.next(1); 309 310 assertTrue(next1.length == 1); 311 CellScanner cellScanner1 = next1[0].cellScanner(); 312 cellScanner1.advance(); 313 Cell current1 = cellScanner1.current(); 314 // test user can only see value3 (no label) 315 assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(), 316 current1.getRowLength(), ROW_1, 0, ROW_1.length)); 317 assertTrue(Bytes.equals(current1.getQualifier(), Q3)); 318 assertTrue(Bytes.equals(current1.getValue(), value3)); 319 320 assertFalse(cellScanner1.advance()); 321 } 322 return null; 323 } 324 }); 325 326 } 327 328 @AfterClass tearDownAfterClass()329 public static void tearDownAfterClass() throws Exception { 330 TEST_UTIL.shutdownMiniCluster(); 331 } 332 } 333