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 java.util.UUID;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.HBaseTestingUtility;
031import org.apache.hadoop.hbase.HConstants;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.client.Connection;
034import org.apache.hadoop.hbase.client.ConnectionFactory;
035import org.apache.hadoop.hbase.client.Put;
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.security.User;
041import org.apache.hadoop.hbase.testclassification.LargeTests;
042import org.apache.hadoop.hbase.testclassification.SecurityTests;
043import org.apache.hadoop.hbase.util.Bytes;
044import org.junit.AfterClass;
045import org.junit.Before;
046import org.junit.BeforeClass;
047import org.junit.ClassRule;
048import org.junit.Rule;
049import org.junit.Test;
050import org.junit.experimental.categories.Category;
051import org.junit.rules.TestName;
052
053@Category({SecurityTests.class, LargeTests.class})
054public class TestAccessControlFilter extends SecureTestUtil {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058      HBaseClassTestRule.forClass(TestAccessControlFilter.class);
059
060  @Rule public TestName name = new TestName();
061  private static HBaseTestingUtility 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 HBaseTestingUtility();
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(AccessControlLists.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", UUID.randomUUID().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", UUID.randomUUID().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", UUID.randomUUID().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}