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 = 067 LoggerFactory.getLogger(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", 103 resultList.get(1).getException().getName()); 104 assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray()) 105 .contains("org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException: " 106 + "Label 'secret' already exists")); 107 assertTrue(resultList.get(2).getException().getValue().isEmpty()); 108 assertTrue(resultList.get(3).getException().getValue().isEmpty()); 109 assertTrue(resultList.get(4).getException().getValue().isEmpty()); 110 return null; 111 } 112 }; 113 SUPERUSER.runAs(action); 114 } 115 116 @Test 117 public void testAddVisibilityLabelsOnRSRestart() throws Exception { 118 List<RegionServerThread> regionServerThreads = 119 TEST_UTIL.getHBaseCluster().getRegionServerThreads(); 120 for (RegionServerThread rsThread : regionServerThreads) { 121 rsThread.getRegionServer().abort("Aborting "); 122 } 123 // Start one new RS 124 RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer(); 125 waitForLabelsRegionAvailability(rs.getRegionServer()); 126 final AtomicBoolean vcInitialized = new AtomicBoolean(true); 127 do { 128 PrivilegedExceptionAction<VisibilityLabelsResponse> action = 129 new PrivilegedExceptionAction<VisibilityLabelsResponse>() { 130 @Override 131 public VisibilityLabelsResponse run() throws Exception { 132 String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, "ABC", "XYZ" }; 133 try (Connection conn = ConnectionFactory.createConnection(conf)) { 134 VisibilityLabelsResponse resp = VisibilityClient.addLabels(conn, labels); 135 List<RegionActionResult> results = resp.getResultList(); 136 if (results.get(0).hasException()) { 137 NameBytesPair pair = results.get(0).getException(); 138 Throwable t = ProtobufUtil.toException(pair); 139 LOG.debug("Got exception writing labels", t); 140 if (t instanceof VisibilityControllerNotReadyException) { 141 vcInitialized.set(false); 142 LOG.warn("VisibilityController was not yet initialized"); 143 Threads.sleep(10); 144 } else { 145 vcInitialized.set(true); 146 } 147 } else LOG.debug("new labels added: " + resp); 148 } catch (Throwable t) { 149 throw new IOException(t); 150 } 151 return null; 152 } 153 }; 154 SUPERUSER.runAs(action); 155 } while (!vcInitialized.get()); 156 // Scan the visibility label 157 Scan s = new Scan(); 158 s.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL)); 159 160 int i = 0; 161 try (Table ht = TEST_UTIL.getConnection().getTable(LABELS_TABLE_NAME); 162 ResultScanner scanner = ht.getScanner(s)) { 163 while (true) { 164 Result next = scanner.next(); 165 if (next == null) { 166 break; 167 } 168 i++; 169 } 170 } 171 // One label is the "system" label. 172 Assert.assertEquals("The count should be 13", 13, i); 173 } 174 175 @Test 176 public void testListLabels() throws Throwable { 177 PrivilegedExceptionAction<ListLabelsResponse> action = 178 new PrivilegedExceptionAction<ListLabelsResponse>() { 179 @Override 180 public ListLabelsResponse run() throws Exception { 181 ListLabelsResponse response = null; 182 try (Connection conn = ConnectionFactory.createConnection(conf)) { 183 response = VisibilityClient.listLabels(conn, null); 184 } catch (Throwable e) { 185 fail("Should not have thrown exception"); 186 } 187 // The addLabels() in setup added: 188 // { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE, COPYRIGHT, ACCENT, 189 // UNICODE_VIS_TAG, UC1, UC2 }; 190 // The previous tests added 2 more labels: ABC, XYZ 191 // The 'system' label is excluded. 192 List<ByteString> labels = response.getLabelList(); 193 assertEquals(12, labels.size()); 194 assertTrue(labels.contains(ByteString.copyFrom(SECRET.getBytes()))); 195 assertTrue(labels.contains(ByteString.copyFrom(TOPSECRET.getBytes()))); 196 assertTrue(labels.contains(ByteString.copyFrom(CONFIDENTIAL.getBytes()))); 197 assertTrue(labels.contains(ByteString.copyFrom("ABC".getBytes()))); 198 assertTrue(labels.contains(ByteString.copyFrom("XYZ".getBytes()))); 199 assertFalse(labels.contains(ByteString.copyFrom(SYSTEM_LABEL.getBytes()))); 200 return null; 201 } 202 }; 203 SUPERUSER.runAs(action); 204 } 205 206 @Test 207 public void testListLabelsWithRegEx() throws Throwable { 208 PrivilegedExceptionAction<ListLabelsResponse> action = 209 new PrivilegedExceptionAction<ListLabelsResponse>() { 210 @Override 211 public ListLabelsResponse run() throws Exception { 212 ListLabelsResponse response = null; 213 try (Connection conn = ConnectionFactory.createConnection(conf)) { 214 response = VisibilityClient.listLabels(conn, ".*secret"); 215 } catch (Throwable e) { 216 fail("Should not have thrown exception"); 217 } 218 // Only return the labels that end with 'secret' 219 List<ByteString> labels = response.getLabelList(); 220 assertEquals(2, labels.size()); 221 assertTrue(labels.contains(ByteString.copyFrom(SECRET.getBytes()))); 222 assertTrue(labels.contains(ByteString.copyFrom(TOPSECRET.getBytes()))); 223 return null; 224 } 225 }; 226 SUPERUSER.runAs(action); 227 } 228 229 @Test 230 public void testVisibilityLabelsOnWALReplay() throws Exception { 231 final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName()); 232 try (Table table = createTableAndWriteDataWithLabels(tableName, 233 "(" + SECRET + "|" + CONFIDENTIAL + ")", PRIVATE);) { 234 List<RegionServerThread> regionServerThreads = 235 TEST_UTIL.getHBaseCluster().getRegionServerThreads(); 236 for (RegionServerThread rsThread : regionServerThreads) { 237 rsThread.getRegionServer().abort("Aborting "); 238 } 239 // Start one new RS 240 RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer(); 241 waitForLabelsRegionAvailability(rs.getRegionServer()); 242 Scan s = new Scan(); 243 s.setAuthorizations(new Authorizations(SECRET)); 244 ResultScanner scanner = table.getScanner(s); 245 Result[] next = scanner.next(3); 246 assertTrue(next.length == 1); 247 } 248 } 249}