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.thrift2; 019 020import static java.nio.ByteBuffer.wrap; 021import static org.junit.Assert.assertArrayEquals; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertNull; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import java.nio.ByteBuffer; 028import java.security.PrivilegedExceptionAction; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.Collections; 032import java.util.Comparator; 033import java.util.List; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.HBaseClassTestRule; 036import org.apache.hadoop.hbase.HBaseTestingUtil; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.client.Admin; 039import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 040import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 041import org.apache.hadoop.hbase.client.Connection; 042import org.apache.hadoop.hbase.client.ConnectionFactory; 043import org.apache.hadoop.hbase.client.TableDescriptor; 044import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 045import org.apache.hadoop.hbase.security.User; 046import org.apache.hadoop.hbase.security.UserProvider; 047import org.apache.hadoop.hbase.security.visibility.ScanLabelGenerator; 048import org.apache.hadoop.hbase.security.visibility.SimpleScanLabelGenerator; 049import org.apache.hadoop.hbase.security.visibility.VisibilityClient; 050import org.apache.hadoop.hbase.security.visibility.VisibilityConstants; 051import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; 052import org.apache.hadoop.hbase.security.visibility.VisibilityUtils; 053import org.apache.hadoop.hbase.testclassification.ClientTests; 054import org.apache.hadoop.hbase.testclassification.MediumTests; 055import org.apache.hadoop.hbase.thrift2.generated.TAppend; 056import org.apache.hadoop.hbase.thrift2.generated.TAuthorization; 057import org.apache.hadoop.hbase.thrift2.generated.TCellVisibility; 058import org.apache.hadoop.hbase.thrift2.generated.TColumn; 059import org.apache.hadoop.hbase.thrift2.generated.TColumnIncrement; 060import org.apache.hadoop.hbase.thrift2.generated.TColumnValue; 061import org.apache.hadoop.hbase.thrift2.generated.TGet; 062import org.apache.hadoop.hbase.thrift2.generated.TIllegalArgument; 063import org.apache.hadoop.hbase.thrift2.generated.TIncrement; 064import org.apache.hadoop.hbase.thrift2.generated.TPut; 065import org.apache.hadoop.hbase.thrift2.generated.TResult; 066import org.apache.hadoop.hbase.thrift2.generated.TScan; 067import org.apache.hadoop.hbase.util.Bytes; 068import org.junit.AfterClass; 069import org.junit.Assert; 070import org.junit.Before; 071import org.junit.BeforeClass; 072import org.junit.ClassRule; 073import org.junit.Test; 074import org.junit.experimental.categories.Category; 075import org.slf4j.Logger; 076import org.slf4j.LoggerFactory; 077 078import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 079 080@Category({ ClientTests.class, MediumTests.class }) 081public class TestThriftHBaseServiceHandlerWithLabels { 082 083 @ClassRule 084 public static final HBaseClassTestRule CLASS_RULE = 085 HBaseClassTestRule.forClass(TestThriftHBaseServiceHandlerWithLabels.class); 086 087 private static final Logger LOG = 088 LoggerFactory.getLogger(TestThriftHBaseServiceHandlerWithLabels.class); 089 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 090 091 // Static names for tables, columns, rows, and values 092 private static byte[] tableAname = Bytes.toBytes("tableA"); 093 private static byte[] familyAname = Bytes.toBytes("familyA"); 094 private static byte[] familyBname = Bytes.toBytes("familyB"); 095 private static byte[] qualifierAname = Bytes.toBytes("qualifierA"); 096 private static byte[] qualifierBname = Bytes.toBytes("qualifierB"); 097 private static byte[] valueAname = Bytes.toBytes("valueA"); 098 private static byte[] valueBname = Bytes.toBytes("valueB"); 099 private static ColumnFamilyDescriptor[] families = new ColumnFamilyDescriptor[] { 100 ColumnFamilyDescriptorBuilder.newBuilder(familyAname).setMaxVersions(3).build(), 101 ColumnFamilyDescriptorBuilder.newBuilder(familyBname).setMaxVersions(2).build() }; 102 103 private final static String TOPSECRET = "topsecret"; 104 private final static String PUBLIC = "public"; 105 private final static String PRIVATE = "private"; 106 private final static String CONFIDENTIAL = "confidential"; 107 private final static String SECRET = "secret"; 108 private static User SUPERUSER; 109 110 private static Configuration conf; 111 112 public void assertTColumnValuesEqual(List<TColumnValue> columnValuesA, 113 List<TColumnValue> columnValuesB) { 114 assertEquals(columnValuesA.size(), columnValuesB.size()); 115 Comparator<TColumnValue> comparator = new Comparator<TColumnValue>() { 116 @Override 117 public int compare(TColumnValue o1, TColumnValue o2) { 118 return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()), 119 Bytes.add(o2.getFamily(), o2.getQualifier())); 120 } 121 }; 122 Collections.sort(columnValuesA, comparator); 123 Collections.sort(columnValuesB, comparator); 124 125 for (int i = 0; i < columnValuesA.size(); i++) { 126 TColumnValue a = columnValuesA.get(i); 127 TColumnValue b = columnValuesB.get(i); 128 assertArrayEquals(a.getFamily(), b.getFamily()); 129 assertArrayEquals(a.getQualifier(), b.getQualifier()); 130 assertArrayEquals(a.getValue(), b.getValue()); 131 } 132 } 133 134 @BeforeClass 135 public static void beforeClass() throws Exception { 136 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 137 conf = UTIL.getConfiguration(); 138 conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, 139 ScanLabelGenerator.class); 140 conf.set("hbase.superuser", SUPERUSER.getShortName()); 141 VisibilityTestUtil.enableVisiblityLabels(conf); 142 UTIL.startMiniCluster(1); 143 // Wait for the labels table to become available 144 UTIL.waitTableEnabled(VisibilityConstants.LABELS_TABLE_NAME.getName(), 50000); 145 createLabels(); 146 Admin admin = UTIL.getAdmin(); 147 TableDescriptor tableDescriptor = TableDescriptorBuilder 148 .newBuilder(TableName.valueOf(tableAname)).setColumnFamilies(Arrays.asList(families)).build(); 149 admin.createTable(tableDescriptor); 150 admin.close(); 151 setAuths(); 152 } 153 154 private static void createLabels() throws IOException, InterruptedException { 155 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 156 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 157 @Override 158 public VisibilityLabelsResponse run() throws Exception { 159 String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; 160 try (Connection conn = ConnectionFactory.createConnection(conf)) { 161 VisibilityClient.addLabels(conn, labels); 162 } catch (Throwable t) { 163 throw new IOException(t); 164 } 165 return null; 166 } 167 }; 168 SUPERUSER.runAs(action); 169 } 170 171 private static void setAuths() throws IOException { 172 String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; 173 try { 174 VisibilityClient.setAuths(UTIL.getConnection(), labels, User.getCurrent().getShortName()); 175 } catch (Throwable t) { 176 throw new IOException(t); 177 } 178 } 179 180 @AfterClass 181 public static void afterClass() throws Exception { 182 UTIL.shutdownMiniCluster(); 183 } 184 185 @Before 186 public void setup() throws Exception { 187 188 } 189 190 private ThriftHBaseServiceHandler createHandler() throws IOException { 191 return new ThriftHBaseServiceHandler(conf, UserProvider.instantiate(conf)); 192 } 193 194 @Test 195 public void testScanWithVisibilityLabels() throws Exception { 196 ThriftHBaseServiceHandler handler = createHandler(); 197 ByteBuffer table = wrap(tableAname); 198 199 // insert data 200 TColumnValue columnValue = 201 new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)); 202 List<TColumnValue> columnValues = new ArrayList<>(1); 203 columnValues.add(columnValue); 204 for (int i = 0; i < 10; i++) { 205 TPut put = new TPut(wrap(Bytes.toBytes("testScan" + i)), columnValues); 206 if (i == 5) { 207 put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); 208 } else { 209 put.setCellVisibility(new TCellVisibility() 210 .setExpression("(" + SECRET + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); 211 } 212 handler.put(table, put); 213 } 214 215 // create scan instance 216 TScan scan = new TScan(); 217 List<TColumn> columns = new ArrayList<>(1); 218 TColumn column = new TColumn(); 219 column.setFamily(familyAname); 220 column.setQualifier(qualifierAname); 221 columns.add(column); 222 scan.setColumns(columns); 223 scan.setStartRow(Bytes.toBytes("testScan")); 224 scan.setStopRow(Bytes.toBytes("testScan\uffff")); 225 226 TAuthorization tauth = new TAuthorization(); 227 List<String> labels = new ArrayList<>(2); 228 labels.add(SECRET); 229 labels.add(PRIVATE); 230 tauth.setLabels(labels); 231 scan.setAuthorizations(tauth); 232 // get scanner and rows 233 int scanId = handler.openScanner(table, scan); 234 List<TResult> results = handler.getScannerRows(scanId, 10); 235 assertEquals(9, results.size()); 236 Assert.assertFalse(Bytes.equals(results.get(5).getRow(), Bytes.toBytes("testScan" + 5))); 237 for (int i = 0; i < 9; i++) { 238 if (i < 5) { 239 assertArrayEquals(Bytes.toBytes("testScan" + i), results.get(i).getRow()); 240 } else if (i == 5) { 241 continue; 242 } else { 243 assertArrayEquals(Bytes.toBytes("testScan" + (i + 1)), results.get(i).getRow()); 244 } 245 } 246 247 // check that we are at the end of the scan 248 results = handler.getScannerRows(scanId, 9); 249 assertEquals(0, results.size()); 250 251 // close scanner and check that it was indeed closed 252 handler.closeScanner(scanId); 253 try { 254 handler.getScannerRows(scanId, 9); 255 fail("Scanner id should be invalid"); 256 } catch (TIllegalArgument e) { 257 } 258 } 259 260 @Test 261 public void testGetScannerResultsWithAuthorizations() throws Exception { 262 ThriftHBaseServiceHandler handler = createHandler(); 263 ByteBuffer table = wrap(tableAname); 264 265 // insert data 266 TColumnValue columnValue = 267 new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname)); 268 List<TColumnValue> columnValues = new ArrayList<>(1); 269 columnValues.add(columnValue); 270 for (int i = 0; i < 20; i++) { 271 TPut put = 272 new TPut(wrap(Bytes.toBytes("testGetScannerResults" + pad(i, (byte) 2))), columnValues); 273 if (i == 3) { 274 put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); 275 } else { 276 put.setCellVisibility(new TCellVisibility() 277 .setExpression("(" + SECRET + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); 278 } 279 handler.put(table, put); 280 } 281 282 // create scan instance 283 TScan scan = new TScan(); 284 List<TColumn> columns = new ArrayList<>(1); 285 TColumn column = new TColumn(); 286 column.setFamily(familyAname); 287 column.setQualifier(qualifierAname); 288 columns.add(column); 289 scan.setColumns(columns); 290 scan.setStartRow(Bytes.toBytes("testGetScannerResults")); 291 292 // get 5 rows and check the returned results 293 scan.setStopRow(Bytes.toBytes("testGetScannerResults05")); 294 TAuthorization tauth = new TAuthorization(); 295 List<String> labels = new ArrayList<>(2); 296 labels.add(SECRET); 297 labels.add(PRIVATE); 298 tauth.setLabels(labels); 299 scan.setAuthorizations(tauth); 300 List<TResult> results = handler.getScannerResults(table, scan, 5); 301 assertEquals(4, results.size()); 302 for (int i = 0; i < 4; i++) { 303 if (i < 3) { 304 assertArrayEquals(Bytes.toBytes("testGetScannerResults" + pad(i, (byte) 2)), 305 results.get(i).getRow()); 306 } else if (i == 3) { 307 continue; 308 } else { 309 assertArrayEquals(Bytes.toBytes("testGetScannerResults" + pad(i + 1, (byte) 2)), 310 results.get(i).getRow()); 311 } 312 } 313 } 314 315 @Test 316 public void testGetsWithLabels() throws Exception { 317 ThriftHBaseServiceHandler handler = createHandler(); 318 byte[] rowName = Bytes.toBytes("testPutGet"); 319 ByteBuffer table = wrap(tableAname); 320 321 List<TColumnValue> columnValues = new ArrayList<>(2); 322 columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(valueAname))); 323 columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), wrap(valueBname))); 324 TPut put = new TPut(wrap(rowName), columnValues); 325 326 put.setColumnValues(columnValues); 327 put.setCellVisibility(new TCellVisibility() 328 .setExpression("(" + SECRET + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); 329 handler.put(table, put); 330 TGet get = new TGet(wrap(rowName)); 331 TAuthorization tauth = new TAuthorization(); 332 List<String> labels = new ArrayList<>(2); 333 labels.add(SECRET); 334 labels.add(PRIVATE); 335 tauth.setLabels(labels); 336 get.setAuthorizations(tauth); 337 TResult result = handler.get(table, get); 338 assertArrayEquals(rowName, result.getRow()); 339 List<TColumnValue> returnedColumnValues = result.getColumnValues(); 340 assertTColumnValuesEqual(columnValues, returnedColumnValues); 341 } 342 343 @Test 344 public void testIncrementWithTags() throws Exception { 345 ThriftHBaseServiceHandler handler = createHandler(); 346 byte[] rowName = Bytes.toBytes("testIncrementWithTags"); 347 ByteBuffer table = wrap(tableAname); 348 349 List<TColumnValue> columnValues = new ArrayList<>(1); 350 columnValues 351 .add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(Bytes.toBytes(1L)))); 352 TPut put = new TPut(wrap(rowName), columnValues); 353 put.setColumnValues(columnValues); 354 put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); 355 handler.put(table, put); 356 357 List<TColumnIncrement> incrementColumns = new ArrayList<>(1); 358 incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname))); 359 TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); 360 increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); 361 handler.increment(table, increment); 362 363 TGet get = new TGet(wrap(rowName)); 364 TAuthorization tauth = new TAuthorization(); 365 List<String> labels = new ArrayList<>(1); 366 labels.add(SECRET); 367 tauth.setLabels(labels); 368 get.setAuthorizations(tauth); 369 TResult result = handler.get(table, get); 370 371 assertArrayEquals(rowName, result.getRow()); 372 assertEquals(1, result.getColumnValuesSize()); 373 TColumnValue columnValue = result.getColumnValues().get(0); 374 assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue()); 375 } 376 377 @Test 378 public void testIncrementWithTagsWithNotMatchLabels() throws Exception { 379 ThriftHBaseServiceHandler handler = createHandler(); 380 byte[] rowName = Bytes.toBytes("testIncrementWithTagsWithNotMatchLabels"); 381 ByteBuffer table = wrap(tableAname); 382 383 List<TColumnValue> columnValues = new ArrayList<>(1); 384 columnValues 385 .add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(Bytes.toBytes(1L)))); 386 TPut put = new TPut(wrap(rowName), columnValues); 387 put.setColumnValues(columnValues); 388 put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); 389 handler.put(table, put); 390 391 List<TColumnIncrement> incrementColumns = new ArrayList<>(1); 392 incrementColumns.add(new TColumnIncrement(wrap(familyAname), wrap(qualifierAname))); 393 TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); 394 increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); 395 handler.increment(table, increment); 396 397 TGet get = new TGet(wrap(rowName)); 398 TAuthorization tauth = new TAuthorization(); 399 List<String> labels = new ArrayList<>(1); 400 labels.add(PUBLIC); 401 tauth.setLabels(labels); 402 get.setAuthorizations(tauth); 403 TResult result = handler.get(table, get); 404 assertNull(result.getRow()); 405 } 406 407 @Test 408 public void testAppend() throws Exception { 409 ThriftHBaseServiceHandler handler = createHandler(); 410 byte[] rowName = Bytes.toBytes("testAppend"); 411 ByteBuffer table = wrap(tableAname); 412 byte[] v1 = Bytes.toBytes(1L); 413 byte[] v2 = Bytes.toBytes(5L); 414 List<TColumnValue> columnValues = new ArrayList<>(1); 415 columnValues 416 .add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(Bytes.toBytes(1L)))); 417 TPut put = new TPut(wrap(rowName), columnValues); 418 put.setColumnValues(columnValues); 419 put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); 420 handler.put(table, put); 421 422 List<TColumnValue> appendColumns = new ArrayList<>(1); 423 appendColumns.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), wrap(v2))); 424 TAppend append = new TAppend(wrap(rowName), appendColumns); 425 append.setCellVisibility(new TCellVisibility().setExpression(SECRET)); 426 handler.append(table, append); 427 428 TGet get = new TGet(wrap(rowName)); 429 TAuthorization tauth = new TAuthorization(); 430 List<String> labels = new ArrayList<>(1); 431 labels.add(SECRET); 432 tauth.setLabels(labels); 433 get.setAuthorizations(tauth); 434 TResult result = handler.get(table, get); 435 436 assertArrayEquals(rowName, result.getRow()); 437 assertEquals(1, result.getColumnValuesSize()); 438 TColumnValue columnValue = result.getColumnValues().get(0); 439 assertArrayEquals(Bytes.add(v1, v2), columnValue.getValue()); 440 } 441 442 /** 443 * Padding numbers to make comparison of sort order easier in a for loop The number to pad. 444 * @param n The number to pad. 445 * @param pad The length to pad up to. 446 * @return The padded number as a string. 447 */ 448 private String pad(int n, byte pad) { 449 String res = Integer.toString(n); 450 451 while (res.length() < pad) { 452 res = "0" + res; 453 } 454 455 return res; 456 } 457}