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.access; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.security.PrivilegedExceptionAction; 025import java.util.ArrayList; 026import java.util.List; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtility; 030import org.apache.hadoop.hbase.HConstants; 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.Put; 035import org.apache.hadoop.hbase.client.Result; 036import org.apache.hadoop.hbase.client.ResultScanner; 037import org.apache.hadoop.hbase.client.Scan; 038import org.apache.hadoop.hbase.client.Table; 039import org.apache.hadoop.hbase.security.User; 040import org.apache.hadoop.hbase.testclassification.MediumTests; 041import org.apache.hadoop.hbase.testclassification.SecurityTests; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.junit.AfterClass; 044import org.junit.Before; 045import org.junit.BeforeClass; 046import org.junit.ClassRule; 047import org.junit.Rule; 048import org.junit.Test; 049import org.junit.experimental.categories.Category; 050import org.junit.rules.TestName; 051 052@Category({SecurityTests.class, MediumTests.class}) 053public class TestAccessControlFilter extends SecureTestUtil { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestAccessControlFilter.class); 058 059 @Rule public TestName name = new TestName(); 060 private static HBaseTestingUtility TEST_UTIL; 061 062 private static User READER; 063 private static User LIMITED; 064 private static User DENIED; 065 066 private static TableName TABLE; 067 private static byte[] FAMILY = Bytes.toBytes("f1"); 068 private static byte[] PRIVATE_COL = Bytes.toBytes("private"); 069 private static byte[] PUBLIC_COL = Bytes.toBytes("public"); 070 071 @Before 072 public void setup () { 073 TABLE = TableName.valueOf(name.getMethodName()); 074 } 075 076 @BeforeClass 077 public static void setupBeforeClass() throws Exception { 078 TEST_UTIL = new HBaseTestingUtility(); 079 Configuration conf = TEST_UTIL.getConfiguration(); 080 // Up the handlers; this test needs more than usual. 081 conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10); 082 enableSecurity(conf); 083 verifyConfiguration(conf); 084 085 // We expect 0.98 scanning semantics 086 conf.setBoolean(AccessControlConstants.CF_ATTRIBUTE_EARLY_OUT, false); 087 088 TEST_UTIL.startMiniCluster(); 089 TEST_UTIL.waitTableEnabled(PermissionStorage.ACL_TABLE_NAME.getName(), 50000); 090 091 READER = User.createUserForTesting(conf, "reader", new String[0]); 092 LIMITED = User.createUserForTesting(conf, "limited", new String[0]); 093 DENIED = User.createUserForTesting(conf, "denied", new String[0]); 094 } 095 096 @AfterClass 097 public static void tearDownAfterClass() throws Exception { 098 TEST_UTIL.shutdownMiniCluster(); 099 } 100 101 @Test 102 public void testQualifierAccess() throws Exception { 103 final Table table = createTable(TEST_UTIL, TABLE, new byte[][] { FAMILY }); 104 try { 105 doQualifierAccess(table); 106 } finally { 107 table.close(); 108 } 109 } 110 111 private void doQualifierAccess(final Table table) throws Exception { 112 // set permissions 113 SecureTestUtil.grantOnTable(TEST_UTIL, READER.getShortName(), TABLE, null, null, 114 Permission.Action.READ); 115 SecureTestUtil.grantOnTable(TEST_UTIL, LIMITED.getShortName(), TABLE, FAMILY, PUBLIC_COL, 116 Permission.Action.READ); 117 118 // put some test data 119 List<Put> puts = new ArrayList<>(100); 120 for (int i=0; i<100; i++) { 121 Put p = new Put(Bytes.toBytes(i)); 122 p.addColumn(FAMILY, PRIVATE_COL, Bytes.toBytes("secret " + i)); 123 p.addColumn(FAMILY, PUBLIC_COL, Bytes.toBytes("info " + i)); 124 puts.add(p); 125 } 126 table.put(puts); 127 128 // test read 129 READER.runAs(new PrivilegedExceptionAction<Object>() { 130 @Override 131 public Object run() throws Exception { 132 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 133 // force a new RS connection 134 conf.set("testkey", TEST_UTIL.getRandomUUID().toString()); 135 Connection connection = ConnectionFactory.createConnection(conf); 136 Table t = connection.getTable(TABLE); 137 try { 138 ResultScanner rs = t.getScanner(new Scan()); 139 int rowcnt = 0; 140 for (Result r : rs) { 141 rowcnt++; 142 int rownum = Bytes.toInt(r.getRow()); 143 assertTrue(r.containsColumn(FAMILY, PRIVATE_COL)); 144 assertEquals("secret "+rownum, Bytes.toString(r.getValue(FAMILY, PRIVATE_COL))); 145 assertTrue(r.containsColumn(FAMILY, PUBLIC_COL)); 146 assertEquals("info "+rownum, Bytes.toString(r.getValue(FAMILY, PUBLIC_COL))); 147 } 148 assertEquals("Expected 100 rows returned", 100, rowcnt); 149 return null; 150 } finally { 151 t.close(); 152 connection.close(); 153 } 154 } 155 }); 156 157 // test read with qualifier filter 158 LIMITED.runAs(new PrivilegedExceptionAction<Object>() { 159 @Override 160 public Object run() throws Exception { 161 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 162 // force a new RS connection 163 conf.set("testkey", TEST_UTIL.getRandomUUID().toString()); 164 Connection connection = ConnectionFactory.createConnection(conf); 165 Table t = connection.getTable(TABLE); 166 try { 167 ResultScanner rs = t.getScanner(new Scan()); 168 int rowcnt = 0; 169 for (Result r : rs) { 170 rowcnt++; 171 int rownum = Bytes.toInt(r.getRow()); 172 assertFalse(r.containsColumn(FAMILY, PRIVATE_COL)); 173 assertTrue(r.containsColumn(FAMILY, PUBLIC_COL)); 174 assertEquals("info " + rownum, Bytes.toString(r.getValue(FAMILY, PUBLIC_COL))); 175 } 176 assertEquals("Expected 100 rows returned", 100, rowcnt); 177 return null; 178 } finally { 179 t.close(); 180 connection.close(); 181 } 182 } 183 }); 184 185 // test as user with no permission 186 DENIED.runAs(new PrivilegedExceptionAction<Object>(){ 187 @Override 188 public Object run() throws Exception { 189 Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); 190 // force a new RS connection 191 conf.set("testkey", TEST_UTIL.getRandomUUID().toString()); 192 Connection connection = ConnectionFactory.createConnection(conf); 193 Table t = connection.getTable(TABLE); 194 try { 195 ResultScanner rs = t.getScanner(new Scan()); 196 int rowcnt = 0; 197 for (Result r : rs) { 198 rowcnt++; 199 int rownum = Bytes.toInt(r.getRow()); 200 assertFalse(r.containsColumn(FAMILY, PRIVATE_COL)); 201 assertTrue(r.containsColumn(FAMILY, PUBLIC_COL)); 202 assertEquals("info " + rownum, Bytes.toString(r.getValue(FAMILY, PUBLIC_COL))); 203 } 204 assertEquals("Expected 0 rows returned", 0, rowcnt); 205 return null; 206 } finally { 207 t.close(); 208 connection.close(); 209 } 210 } 211 }); 212 } 213}