From 2bf904f216ab8315c37a65e4860f51c526a3b654 Mon Sep 17 00:00:00 2001 From: Ashish Singhi Date: Thu, 19 Mar 2015 15:48:12 +0530 Subject: [PATCH] HBASE-13241 Add tests for group level grants --- .../access/TestAccessController2.java | 229 +++++++++++++++++- 1 file changed, 226 insertions(+), 3 deletions(-) diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController2.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController2.java index 80c4fac019c..9e101048727 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController2.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController2.java @@ -17,20 +17,26 @@ */ package org.apache.hadoop.hbase.security.access; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.util.ArrayList; import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; -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.ResultScanner; @@ -42,7 +48,9 @@ import org.apache.hadoop.hbase.testclassification.LargeTests; import org.apache.hadoop.hbase.testclassification.SecurityTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.TestTableName; +import org.junit.After; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; @@ -50,6 +58,7 @@ import org.junit.experimental.categories.Category; @Category({SecurityTests.class, LargeTests.class}) public class TestAccessController2 extends SecureTestUtil { + private static final Log LOG = LogFactory.getLog(TestAccessController2.class); private static final byte[] TEST_ROW = Bytes.toBytes("test"); private static final byte[] TEST_FAMILY = Bytes.toBytes("f"); @@ -59,7 +68,29 @@ public class TestAccessController2 extends SecureTestUtil { private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static Configuration conf; - @Rule public TestTableName TEST_TABLE = new TestTableName(); + private static Connection connection; + + private final static byte[] Q1 = Bytes.toBytes("q1"); + private final static byte[] value1 = Bytes.toBytes("value1"); + + private static byte[] TEST_FAMILY_2 = Bytes.toBytes("f2"); + private static byte[] TEST_ROW_2 = Bytes.toBytes("r2"); + private final static byte[] Q2 = Bytes.toBytes("q2"); + private final static byte[] value2 = Bytes.toBytes("value2"); + + private static byte[] TEST_ROW_3 = Bytes.toBytes("r3"); + + private static final String TESTGROUP_1 = "testgroup_1"; + private static final String TESTGROUP_2 = "testgroup_2"; + + private static User TESTGROUP1_USER1; + private static User TESTGROUP2_USER1; + + @Rule + public TestTableName TEST_TABLE = new TestTableName(); + private String namespace = "testNamespace"; + private String tname = namespace + ":testtable1"; + private TableName tableName = TableName.valueOf(tname); @BeforeClass public static void setupBeforeClass() throws Exception { @@ -70,14 +101,72 @@ public class TestAccessController2 extends SecureTestUtil { verifyConfiguration(conf); TEST_UTIL.startMiniCluster(); // Wait for the ACL table to become available - TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); + TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME); + + TESTGROUP1_USER1 = + User.createUserForTesting(conf, "testgroup1_user1", new String[] { TESTGROUP_1 }); + TESTGROUP2_USER1 = + User.createUserForTesting(conf, "testgroup2_user2", new String[] { TESTGROUP_2 }); + + connection = ConnectionFactory.createConnection(conf); + } + + @Before + public void setUp() throws Exception { + TEST_UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(namespace).build()); + try (Table table = + TEST_UTIL.createTable(tableName, + new String[] { Bytes.toString(TEST_FAMILY), Bytes.toString(TEST_FAMILY_2) })) { + TEST_UTIL.waitTableEnabled(tableName); + + List puts = new ArrayList(5); + Put put_1 = new Put(TEST_ROW); + put_1.addColumn(TEST_FAMILY, Q1, value1); + + Put put_2 = new Put(TEST_ROW_2); + put_2.addColumn(TEST_FAMILY, Q2, value2); + + Put put_3 = new Put(TEST_ROW_3); + put_3.addColumn(TEST_FAMILY_2, Q1, value1); + + puts.add(put_1); + puts.add(put_2); + puts.add(put_3); + + table.put(puts); + } + + assertEquals(1, AccessControlLists.getTablePermissions(conf, tableName).size()); + try { + assertEquals(1, AccessControlClient.getUserPermissions(connection, tableName.toString()) + .size()); + } catch (Throwable e) { + LOG.error("Error during call of AccessControlClient.getUserPermissions. ", e); + } + // setupOperations(); } @AfterClass public static void tearDownAfterClass() throws Exception { + connection.close(); TEST_UTIL.shutdownMiniCluster(); } + @After + public void tearDown() throws Exception { + // Clean the _acl_ table + try { + deleteTable(TEST_UTIL, tableName); + } catch (TableNotFoundException ex) { + // Test deleted the table, no problem + LOG.info("Test deleted table " + tableName); + } + TEST_UTIL.getHBaseAdmin().deleteNamespace(namespace); + // Verify all table/namespace permissions are erased + assertEquals(0, AccessControlLists.getTablePermissions(conf, tableName).size()); + assertEquals(0, AccessControlLists.getNamespacePermissions(conf, namespace).size()); + } + @Test public void testCreateWithCorrectOwner() throws Exception { // Create a test user @@ -213,4 +302,138 @@ public class TestAccessController2 extends SecureTestUtil { verifyAllowed(scanAction, superUser, globalRead); } + /* + * Test table scan operation at table, column family and column qualifier level. + */ + @Test(timeout = 300000) + public void testPostGrantAndRevokeScanAction() throws Exception { + AccessTestAction scanTableActionForGroupWithTableLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + try (ResultScanner scanner1 = table.getScanner(s1);) { + Result[] next1 = scanner1.next(5); + assertTrue("User having table level access should be able to scan all " + + "the data in the table.", next1.length == 3); + } + } + return null; + } + }; + + AccessTestAction scanTableActionForGroupWithFamilyLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + try (ResultScanner scanner1 = table.getScanner(s1);) { + Result[] next1 = scanner1.next(5); + assertTrue("User having column family level access should be able to scan all " + + "the data belonging to that family.", next1.length == 2); + } + } + return null; + } + }; + + AccessTestAction scanFamilyActionForGroupWithFamilyLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + s1.addFamily(TEST_FAMILY_2); + try (ResultScanner scanner1 = table.getScanner(s1);) { + } + } + return null; + } + }; + + AccessTestAction scanTableActionForGroupWithQualifierLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + try (ResultScanner scanner1 = table.getScanner(s1);) { + Result[] next1 = scanner1.next(5); + assertTrue("User having column qualifier level access should be able to scan " + + "that column family qualifier data.", next1.length == 1); + } + } + return null; + } + }; + + AccessTestAction scanFamilyActionForGroupWithQualifierLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + s1.addFamily(TEST_FAMILY_2); + try (ResultScanner scanner1 = table.getScanner(s1);) { + } + } + return null; + } + }; + + AccessTestAction scanQualifierActionForGroupWithQualifierLevelAccess = new AccessTestAction() { + @Override + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + s1.addColumn(TEST_FAMILY, Q2); + try (ResultScanner scanner1 = table.getScanner(s1);) { + } + } + return null; + } + }; + + // Verify user from a group which has table level access can read all the data and group which + // has no access can't read any data. + grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, null, null, Permission.Action.READ); + verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithTableLevelAccess); + + // Verify user from a group whose table level access has been revoked can't read any data. + revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, null, null); + verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithTableLevelAccess); + + // Verify user from a group which has column family level access can read all the data + // belonging to that family and group which has no access can't read any data. + grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, TEST_FAMILY, null, + Permission.Action.READ); + verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess); + verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithFamilyLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithFamilyLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithFamilyLevelAccess); + + // Verify user from a group whose column family level access has been revoked can't read any + // data from that family. + revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, TEST_FAMILY, null); + verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithFamilyLevelAccess); + + // Verify user from a group which has column qualifier level access can read data that has this + // family and qualifier, and group which has no access can't read any data. + grantOnTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, TEST_FAMILY, Q1, Permission.Action.READ); + verifyAllowed(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess); + verifyDenied(TESTGROUP1_USER1, scanFamilyActionForGroupWithQualifierLevelAccess); + verifyDenied(TESTGROUP1_USER1, scanQualifierActionForGroupWithQualifierLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanTableActionForGroupWithQualifierLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanFamilyActionForGroupWithQualifierLevelAccess); + verifyDenied(TESTGROUP2_USER1, scanQualifierActionForGroupWithQualifierLevelAccess); + + // Verify user from a group whose column qualifier level access has been revoked can't read the + // data having this column family and qualifier. + revokeFromTable(TEST_UTIL, '@' + TESTGROUP_1, tableName, TEST_FAMILY, Q1); + verifyDenied(TESTGROUP1_USER1, scanTableActionForGroupWithQualifierLevelAccess); + } }