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.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertFalse; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import com.google.protobuf.ByteString; 028import java.io.IOException; 029import java.security.PrivilegedExceptionAction; 030import java.util.List; 031import java.util.concurrent.atomic.AtomicBoolean; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.TableName; 034import org.apache.hadoop.hbase.client.Connection; 035import org.apache.hadoop.hbase.client.ConnectionFactory; 036import org.apache.hadoop.hbase.client.Result; 037import org.apache.hadoop.hbase.client.ResultScanner; 038import org.apache.hadoop.hbase.client.Scan; 039import org.apache.hadoop.hbase.client.Table; 040import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 041import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionActionResult; 042import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameBytesPair; 043import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse; 044import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 045import org.apache.hadoop.hbase.security.User; 046import org.apache.hadoop.hbase.testclassification.MediumTests; 047import org.apache.hadoop.hbase.testclassification.SecurityTests; 048import org.apache.hadoop.hbase.util.Bytes; 049import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 050import org.apache.hadoop.hbase.util.Threads; 051import org.junit.Assert; 052import org.junit.BeforeClass; 053import org.junit.ClassRule; 054import org.junit.Test; 055import org.junit.experimental.categories.Category; 056import org.slf4j.Logger; 057import org.slf4j.LoggerFactory; 058 059@Category({SecurityTests.class, MediumTests.class}) 060public class TestVisibilityLabelsWithDefaultVisLabelService extends TestVisibilityLabels { 061 062 @ClassRule 063 public static final HBaseClassTestRule CLASS_RULE = 064 HBaseClassTestRule.forClass(TestVisibilityLabelsWithDefaultVisLabelService.class); 065 066 private static final Logger LOG = LoggerFactory.getLogger( 067 TestVisibilityLabelsWithDefaultVisLabelService.class); 068 069 @BeforeClass 070 public static void setupBeforeClass() throws Exception { 071 // setup configuration 072 conf = TEST_UTIL.getConfiguration(); 073 VisibilityTestUtil.enableVisiblityLabels(conf); 074 conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, 075 ScanLabelGenerator.class); 076 conf.set("hbase.superuser", "admin"); 077 TEST_UTIL.startMiniCluster(2); 078 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 079 USER1 = User.createUserForTesting(conf, "user1", new String[] {}); 080 081 // Wait for the labels table to become available 082 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 083 addLabels(); 084 } 085 086 @Test 087 public void testAddLabels() throws Throwable { 088 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 089 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 090 @Override 091 public VisibilityLabelsResponse run() throws Exception { 092 String[] labels = { "L1", SECRET, "L2", "invalid~", "L3" }; 093 VisibilityLabelsResponse response = null; 094 try (Connection conn = ConnectionFactory.createConnection(conf)) { 095 response = VisibilityClient.addLabels(conn, labels); 096 } catch (Throwable e) { 097 fail("Should not have thrown exception"); 098 } 099 List<RegionActionResult> resultList = response.getResultList(); 100 assertEquals(5, resultList.size()); 101 assertTrue(resultList.get(0).getException().getValue().isEmpty()); 102 assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException", resultList.get(1) 103 .getException().getName()); 104 assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray()) 105 .contains( 106 "org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException: " 107 + "Label 'secret' already exists")); 108 assertTrue(resultList.get(2).getException().getValue().isEmpty()); 109 assertTrue(resultList.get(3).getException().getValue().isEmpty()); 110 assertTrue(resultList.get(4).getException().getValue().isEmpty()); 111 return null; 112 } 113 }; 114 SUPERUSER.runAs(action); 115 } 116 117 @Test 118 public void testAddVisibilityLabelsOnRSRestart() throws Exception { 119 List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster() 120 .getRegionServerThreads(); 121 for (RegionServerThread rsThread : regionServerThreads) { 122 rsThread.getRegionServer().abort("Aborting "); 123 } 124 // Start one new RS 125 RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer(); 126 waitForLabelsRegionAvailability(rs.getRegionServer()); 127 final AtomicBoolean vcInitialized = new AtomicBoolean(true); 128 do { 129 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 130 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 131 @Override 132 public VisibilityLabelsResponse run() throws Exception { 133 String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, "ABC", "XYZ" }; 134 try (Connection conn = ConnectionFactory.createConnection(conf)) { 135 VisibilityLabelsResponse resp = VisibilityClient.addLabels(conn, labels); 136 List<RegionActionResult> results = resp.getResultList(); 137 if (results.get(0).hasException()) { 138 NameBytesPair pair = results.get(0).getException(); 139 Throwable t = ProtobufUtil.toException(pair); 140 LOG.debug("Got exception writing labels", t); 141 if (t instanceof VisibilityControllerNotReadyException) { 142 vcInitialized.set(false); 143 LOG.warn("VisibilityController was not yet initialized"); 144 Threads.sleep(10); 145 } else { 146 vcInitialized.set(true); 147 } 148 } else LOG.debug("new labels added: " + resp); 149 } catch (Throwable t) { 150 throw new IOException(t); 151 } 152 return null; 153 } 154 }; 155 SUPERUSER.runAs(action); 156 } while (!vcInitialized.get()); 157 // Scan the visibility label 158 Scan s = new Scan(); 159 s.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL)); 160 161 int i = 0; 162 try (Table ht = TEST_UTIL.getConnection().getTable(LABELS_TABLE_NAME); 163 ResultScanner scanner = ht.getScanner(s)) { 164 while (true) { 165 Result next = scanner.next(); 166 if (next == null) { 167 break; 168 } 169 i++; 170 } 171 } 172 // One label is the "system" label. 173 Assert.assertEquals("The count should be 13", 13, i); 174 } 175 176 @Test 177 public void testListLabels() throws Throwable { 178 PrivilegedExceptionAction<ListLabelsResponse> action = 179 new PrivilegedExceptionAction<ListLabelsResponse>() { 180 @Override 181 public ListLabelsResponse run() throws Exception { 182 ListLabelsResponse response = null; 183 try (Connection conn = ConnectionFactory.createConnection(conf)) { 184 response = VisibilityClient.listLabels(conn, null); 185 } catch (Throwable e) { 186 fail("Should not have thrown exception"); 187 } 188 // The addLabels() in setup added: 189 // { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE, COPYRIGHT, ACCENT, 190 // UNICODE_VIS_TAG, UC1, UC2 }; 191 // The previous tests added 2 more labels: ABC, XYZ 192 // The 'system' label is excluded. 193 List<ByteString> labels = response.getLabelList(); 194 assertEquals(12, labels.size()); 195 assertTrue(labels.contains(ByteString.copyFrom(SECRET.getBytes()))); 196 assertTrue(labels.contains(ByteString.copyFrom(TOPSECRET.getBytes()))); 197 assertTrue(labels.contains(ByteString.copyFrom(CONFIDENTIAL.getBytes()))); 198 assertTrue(labels.contains(ByteString.copyFrom("ABC".getBytes()))); 199 assertTrue(labels.contains(ByteString.copyFrom("XYZ".getBytes()))); 200 assertFalse(labels.contains(ByteString.copyFrom(SYSTEM_LABEL.getBytes()))); 201 return null; 202 } 203 }; 204 SUPERUSER.runAs(action); 205 } 206 207 @Test 208 public void testListLabelsWithRegEx() throws Throwable { 209 PrivilegedExceptionAction<ListLabelsResponse> action = 210 new PrivilegedExceptionAction<ListLabelsResponse>() { 211 @Override 212 public ListLabelsResponse run() throws Exception { 213 ListLabelsResponse response = null; 214 try (Connection conn = ConnectionFactory.createConnection(conf)) { 215 response = VisibilityClient.listLabels(conn, ".*secret"); 216 } catch (Throwable e) { 217 fail("Should not have thrown exception"); 218 } 219 // Only return the labels that end with 'secret' 220 List<ByteString> labels = response.getLabelList(); 221 assertEquals(2, labels.size()); 222 assertTrue(labels.contains(ByteString.copyFrom(SECRET.getBytes()))); 223 assertTrue(labels.contains(ByteString.copyFrom(TOPSECRET.getBytes()))); 224 return null; 225 } 226 }; 227 SUPERUSER.runAs(action); 228 } 229 230 @Test 231 public void testVisibilityLabelsOnWALReplay() throws Exception { 232 final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 233 try (Table table = createTableAndWriteDataWithLabels(tableName, 234 "(" + SECRET + "|" + CONFIDENTIAL + ")", PRIVATE);) { 235 List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster() 236 .getRegionServerThreads(); 237 for (RegionServerThread rsThread : regionServerThreads) { 238 rsThread.getRegionServer().abort("Aborting "); 239 } 240 // Start one new RS 241 RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer(); 242 waitForLabelsRegionAvailability(rs.getRegionServer()); 243 Scan s = new Scan(); 244 s.setAuthorizations(new Authorizations(SECRET)); 245 ResultScanner scanner = table.getScanner(s); 246 Result[] next = scanner.next(3); 247 assertTrue(next.length == 1); 248 } 249 } 250}