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; 025 026import java.io.IOException; 027import java.security.PrivilegedExceptionAction; 028import java.util.List; 029import java.util.concurrent.atomic.AtomicBoolean; 030import org.apache.hadoop.hbase.HBaseClassTestRule; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Connection; 033import org.apache.hadoop.hbase.client.ConnectionFactory; 034import org.apache.hadoop.hbase.client.Result; 035import org.apache.hadoop.hbase.client.ResultScanner; 036import org.apache.hadoop.hbase.client.Scan; 037import org.apache.hadoop.hbase.client.Table; 038import org.apache.hadoop.hbase.security.User; 039import org.apache.hadoop.hbase.testclassification.MediumTests; 040import org.apache.hadoop.hbase.testclassification.SecurityTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 043import org.apache.hadoop.hbase.util.Threads; 044import org.junit.Assert; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Test; 048import org.junit.experimental.categories.Category; 049import org.slf4j.Logger; 050import org.slf4j.LoggerFactory; 051 052import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 053 054import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 055import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.RegionActionResult; 056import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.NameBytesPair; 057import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.ListLabelsResponse; 058import org.apache.hadoop.hbase.shaded.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; 059 060@Category({ SecurityTests.class, MediumTests.class }) 061public class TestVisibilityLabelsWithDefaultVisLabelService extends TestVisibilityLabels { 062 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestVisibilityLabelsWithDefaultVisLabelService.class); 066 067 private static final Logger LOG = 068 LoggerFactory.getLogger(TestVisibilityLabelsWithDefaultVisLabelService.class); 069 070 @BeforeClass 071 public static void setupBeforeClass() throws Exception { 072 // setup configuration 073 conf = TEST_UTIL.getConfiguration(); 074 VisibilityTestUtil.enableVisiblityLabels(conf); 075 conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, 076 ScanLabelGenerator.class); 077 conf.set("hbase.superuser", "admin"); 078 TEST_UTIL.startMiniCluster(2); 079 SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); 080 USER1 = User.createUserForTesting(conf, "user1", new String[] {}); 081 082 // Wait for the labels table to become available 083 TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); 084 addLabels(); 085 } 086 087 @Test 088 public void testAddLabels() throws Throwable { 089 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 090 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 091 @Override 092 public VisibilityLabelsResponse run() throws Exception { 093 String[] labels = { "L1", SECRET, "L2", "invalid~", "L3" }; 094 VisibilityLabelsResponse response = null; 095 try (Connection conn = ConnectionFactory.createConnection(conf)) { 096 response = VisibilityClient.addLabels(conn, labels); 097 } catch (Throwable e) { 098 throw new IOException(e); 099 } 100 List<RegionActionResult> resultList = response.getResultList(); 101 assertEquals(5, resultList.size()); 102 assertTrue(resultList.get(0).getException().getValue().isEmpty()); 103 assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException", 104 resultList.get(1).getException().getName()); 105 assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray()) 106 .contains("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 = 120 TEST_UTIL.getHBaseCluster().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 throw new IOException(e); 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(Bytes.toBytes(SECRET)))); 196 assertTrue(labels.contains(ByteString.copyFrom(Bytes.toBytes(TOPSECRET)))); 197 assertTrue(labels.contains(ByteString.copyFrom(Bytes.toBytes(CONFIDENTIAL)))); 198 assertTrue(labels.contains(ByteString.copyFrom(Bytes.toBytes("ABC")))); 199 assertTrue(labels.contains(ByteString.copyFrom(Bytes.toBytes("XYZ")))); 200 assertFalse(labels.contains(ByteString.copyFrom(Bytes.toBytes(SYSTEM_LABEL)))); 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 throw new IOException(e); 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(Bytes.toBytes(SECRET)))); 223 assertTrue(labels.contains(ByteString.copyFrom(Bytes.toBytes(TOPSECRET)))); 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 = 236 TEST_UTIL.getHBaseCluster().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}