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