You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2014/05/01 03:04:36 UTC
svn commit: r1591524 [2/2] - in /hbase/trunk:
hbase-client/src/main/java/org/apache/hadoop/hbase/security/access/
hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/
hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/
Added: hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestScanEarlyTermination.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestScanEarlyTermination.java?rev=1591524&view=auto
==============================================================================
--- hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestScanEarlyTermination.java (added)
+++ hbase/trunk/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestScanEarlyTermination.java Thu May 1 01:04:36 2014
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.security.access;
+
+import static org.junit.Assert.*;
+
+import java.util.UUID;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Coprocessor;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.MediumTests;
+import org.apache.hadoop.hbase.TableNotFoundException;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
+import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
+import org.apache.hadoop.hbase.security.User;
+import org.apache.hadoop.hbase.security.access.Permission.Action;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.TestTableName;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(MediumTests.class)
+public class TestScanEarlyTermination extends SecureTestUtil {
+ private static final Log LOG = LogFactory.getLog(TestScanEarlyTermination.class);
+
+ static {
+ Logger.getLogger(AccessController.class).setLevel(Level.TRACE);
+ Logger.getLogger(AccessControlFilter.class).setLevel(Level.TRACE);
+ Logger.getLogger(TableAuthManager.class).setLevel(Level.TRACE);
+ }
+
+ @Rule
+ public TestTableName TEST_TABLE = new TestTableName();
+ private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
+ private static final byte[] TEST_FAMILY1 = Bytes.toBytes("f1");
+ private static final byte[] TEST_FAMILY2 = Bytes.toBytes("f2");
+ private static final byte[] TEST_ROW = Bytes.toBytes("testrow");
+ private static final byte[] TEST_Q1 = Bytes.toBytes("q1");
+ private static final byte[] TEST_Q2 = Bytes.toBytes("q2");
+ private static final byte[] ZERO = Bytes.toBytes(0L);
+
+ private static Configuration conf;
+
+ private static User USER_OWNER;
+ private static User USER_OTHER;
+
+ @BeforeClass
+ public static void setupBeforeClass() throws Exception {
+ // setup configuration
+ conf = TEST_UTIL.getConfiguration();
+ // Enable security
+ enableSecurity(conf);
+ // Verify enableSecurity sets up what we require
+ verifyConfiguration(conf);
+
+ TEST_UTIL.startMiniCluster();
+ MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster()
+ .getMasterCoprocessorHost();
+ cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
+ AccessController ac = (AccessController)
+ cpHost.findCoprocessor(AccessController.class.getName());
+ cpHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
+ RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
+ .getRegionServerCoprocessorHost();
+ rsHost.createEnvironment(AccessController.class, ac, Coprocessor.PRIORITY_HIGHEST, 1, conf);
+
+ // Wait for the ACL table to become available
+ TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
+
+ // create a set of test users
+ USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
+ USER_OTHER = User.createUserForTesting(conf, "other", new String[0]);
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ TEST_UTIL.shutdownMiniCluster();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
+ HTableDescriptor htd = new HTableDescriptor(TEST_TABLE.getTableName());
+ htd.setOwner(USER_OWNER);
+ HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY1);
+ hcd.setMaxVersions(10);
+ htd.addFamily(hcd);
+ hcd = new HColumnDescriptor(TEST_FAMILY2);
+ hcd.setMaxVersions(10);
+ htd.addFamily(hcd);
+
+ // Enable backwards compatible early termination behavior in the HTD. We
+ // want to confirm that the per-table configuration is properly picked up.
+ htd.setConfiguration(AccessControlConstants.CF_ATTRIBUTE_EARLY_OUT, "true");
+
+ admin.createTable(htd);
+
+ TEST_UTIL.waitTableEnabled(TEST_TABLE.getTableName().getName());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // Clean the _acl_ table
+ try {
+ TEST_UTIL.deleteTable(TEST_TABLE.getTableName());
+ } catch (TableNotFoundException ex) {
+ // Test deleted the table, no problem
+ LOG.info("Test deleted table " + TEST_TABLE.getTableName());
+ }
+ assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
+ }
+
+ @Test
+ public void testEarlyScanTermination() throws Exception {
+ // Grant USER_OTHER access to TEST_FAMILY1 only
+ grantOnTable(TEST_UTIL, USER_OTHER.getShortName(), TEST_TABLE.getTableName(), TEST_FAMILY1,
+ null, Action.READ);
+
+ // Set up test data
+ verifyAllowed(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Put put = new Put(TEST_ROW).add(TEST_FAMILY1, TEST_Q1, ZERO);
+ t.put(put);
+ // Set a READ cell ACL for USER_OTHER on this value in FAMILY2
+ put = new Put(TEST_ROW).add(TEST_FAMILY2, TEST_Q1, ZERO);
+ put.setACL(USER_OTHER.getShortName(), new Permission(Action.READ));
+ t.put(put);
+ // Set an empty cell ACL for USER_OTHER on this other value in FAMILY2
+ put = new Put(TEST_ROW).add(TEST_FAMILY2, TEST_Q2, ZERO);
+ put.setACL(USER_OTHER.getShortName(), new Permission());
+ t.put(put);
+ } finally {
+ t.close();
+ }
+ return null;
+ }
+ }, USER_OWNER);
+
+ // A scan of FAMILY1 will be allowed
+ verifyAllowed(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Scan scan = new Scan().addFamily(TEST_FAMILY1);
+ Result result = t.getScanner(scan).next();
+ if (result != null) {
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY1, TEST_Q1));
+ assertFalse("Improper inclusion", result.containsColumn(TEST_FAMILY2, TEST_Q1));
+ return result.listCells();
+ }
+ return null;
+ } finally {
+ t.close();
+ }
+ }
+ }, USER_OTHER);
+
+ // A scan of FAMILY1 and FAMILY2 will produce results for FAMILY1 without
+ // throwing an exception, however no cells from FAMILY2 will be returned
+ // because we early out checks at the CF level.
+ verifyAllowed(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Scan scan = new Scan();
+ Result result = t.getScanner(scan).next();
+ if (result != null) {
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY1, TEST_Q1));
+ assertFalse("Improper inclusion", result.containsColumn(TEST_FAMILY2, TEST_Q1));
+ return result.listCells();
+ }
+ return null;
+ } finally {
+ t.close();
+ }
+ }
+ }, USER_OTHER);
+
+ // A scan of FAMILY2 will throw an AccessDeniedException
+ verifyDeniedWithException(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Scan scan = new Scan().addFamily(TEST_FAMILY2);
+ Result result = t.getScanner(scan).next();
+ if (result != null) {
+ return result.listCells();
+ }
+ return null;
+ } finally {
+ t.close();
+ }
+ }
+ }, USER_OTHER);
+
+ // Now grant USER_OTHER access to TEST_FAMILY2:TEST_Q2
+ grantOnTable(TEST_UTIL, USER_OTHER.getShortName(), TEST_TABLE.getTableName(), TEST_FAMILY2,
+ TEST_Q2, Action.READ);
+
+ // A scan of FAMILY1 and FAMILY2 will produce combined results. In FAMILY2
+ // we have access granted to Q2 at the CF level. Because we early out
+ // checks at the CF level the cell ACL on Q1 also granting access is ignored.
+ verifyAllowed(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Scan scan = new Scan();
+ Result result = t.getScanner(scan).next();
+ if (result != null) {
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY1, TEST_Q1));
+ assertFalse("Improper inclusion", result.containsColumn(TEST_FAMILY2, TEST_Q1));
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY2, TEST_Q2));
+ return result.listCells();
+ }
+ return null;
+ } finally {
+ t.close();
+ }
+ }
+ }, USER_OTHER);
+
+ // A scan of FAMILY1 and FAMILY2 will produce combined results. If we use
+ // a cell first strategy then cell ACLs come into effect. In FAMILY2, that
+ // cell ACL on Q1 now grants access and the empty permission set on Q2 now
+ // denies access.
+ verifyAllowed(new AccessTestAction() {
+ @Override
+ public Object run() throws Exception {
+ // force a new RS connection
+ conf.set("testkey", UUID.randomUUID().toString());
+ HTable t = new HTable(conf, TEST_TABLE.getTableName());
+ try {
+ Scan scan = new Scan();
+ scan.setACLStrategy(true);
+ Result result = t.getScanner(scan).next();
+ if (result != null) {
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY1, TEST_Q1));
+ assertTrue("Improper exclusion", result.containsColumn(TEST_FAMILY2, TEST_Q1));
+ assertFalse("Improper inclusion", result.containsColumn(TEST_FAMILY2, TEST_Q2));
+ return result.listCells();
+ }
+ return null;
+ } finally {
+ t.close();
+ }
+ }
+ }, USER_OTHER);
+
+ }
+}