From 9b4defb85f40bfc920206696d0706e056fc587b4 Mon Sep 17 00:00:00 2001 From: Ashish Singhi Date: Wed, 18 Mar 2015 11:35:42 +0530 Subject: [PATCH] HBASE-13241 Add tests for group level grants --- .../security/access/TestAccessController2.java | 378 ++++++++++++++++++++- 1 file changed, 375 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 80c4fac..2f5ba4b 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,39 +17,55 @@ */ 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 static org.junit.Assert.fail; +import java.security.PrivilegedExceptionAction; +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; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService; +import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.Permission.Action; 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; import org.junit.experimental.categories.Category; -@Category({SecurityTests.class, LargeTests.class}) +import com.google.protobuf.BlockingRpcChannel; + +@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 +75,33 @@ 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 SUPERGROUP = "supergroup"; + private static final String TESTGROUP_TABLE = "testgroup_table"; + private static final String TESTGROUP_FAMILY = "testgroup_family"; + private static final String TESTGROUP_QUAIFIER = "testgroup_qualifier"; + + private static User SUPERUSER; + private static User USER1_TESTGROUP_QUALIFIER; + private static User USER1_TESTGROUP_TABLE; + private static User USER1_TESTGROUP_FAMILY; + + @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 { @@ -71,13 +113,81 @@ public class TestAccessController2 extends SecureTestUtil { TEST_UTIL.startMiniCluster(); // Wait for the ACL table to become available TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME); + + // 'admin' has super user permission because it is part of the 'supergroup' + SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { SUPERGROUP }); + + USER1_TESTGROUP_TABLE = + User.createUserForTesting(conf, "test_table1", new String[] { TESTGROUP_TABLE }); + + USER1_TESTGROUP_FAMILY = + User.createUserForTesting(conf, "test_family1", new String[] { TESTGROUP_FAMILY }); + + USER1_TESTGROUP_QUALIFIER = + User.createUserForTesting(conf, "test_qualifier1", new String[] { TESTGROUP_QUAIFIER }); + + 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); + } + // Set up initial grants + grantGlobal(TEST_UTIL, '@' + SUPERGROUP, Permission.Action.ADMIN, Permission.Action.CREATE, + Permission.Action.READ, Permission.Action.WRITE); + + 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 +323,266 @@ public class TestAccessController2 extends SecureTestUtil { verifyAllowed(scanAction, superUser, globalRead); } + /* + * Test grant and revoke access to a group at table level. + */ + @Test(timeout = 300000) + public void testGrantAndRevokeGroupAccessAtTableLevel() throws Exception { + AccessTestAction grantActionAtTableLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.grant(protocol, '@' + TESTGROUP_TABLE, tableName, null, null, Action.ADMIN, + Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(grantActionAtTableLevel, SUPERUSER, USER1_TESTGROUP_TABLE); + verifyDenied(grantActionAtTableLevel, USER1_TESTGROUP_FAMILY, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_TABLE.runAs(new PrivilegedExceptionAction() { + 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(next1.length == 3); + } + } + return null; + } + }); + + AccessTestAction revokeActionAtTableLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.revoke(protocol, '@' + TESTGROUP_TABLE, tableName, null, null, Action.ADMIN, + Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(revokeActionAtTableLevel, SUPERUSER, USER1_TESTGROUP_TABLE); + verifyDenied(revokeActionAtTableLevel, USER1_TESTGROUP_FAMILY, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_TABLE.runAs(new PrivilegedExceptionAction() { + 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);) { + fail("Access should be denied as the user " + USER1_TESTGROUP_FAMILY + + " read privilege has been revoked on column family " + + Bytes.toString(TEST_FAMILY_2)); + } catch (AccessDeniedException ignore) { + } + } + return null; + } + }); + + } + + /* + * Test grant and revoke access to a group at family level. + */ + @Test(timeout = 300000) + public void testGrantAndRevokeGroupAccessAtFamilyLevel() throws Exception { + AccessTestAction grantActionAtFamilyLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.grant(protocol, '@' + TESTGROUP_FAMILY, tableName, TEST_FAMILY, null, + Action.ADMIN, Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(grantActionAtFamilyLevel, SUPERUSER, USER1_TESTGROUP_TABLE, + USER1_TESTGROUP_FAMILY); + verifyDenied(grantActionAtFamilyLevel, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_FAMILY.runAs(new PrivilegedExceptionAction() { + 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(next1.length == 2); + } + + try { + s1.addFamily(TEST_FAMILY_2); + table.getScanner(s1); + fail("Access should be denied as the user " + USER1_TESTGROUP_FAMILY + + " does not have read privilege on column family " + + Bytes.toString(TEST_FAMILY_2)); + } catch (AccessDeniedException ignore) { + } + } + return null; + } + }); + + AccessTestAction revokeActionAtFamilyLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.revoke(protocol, '@' + TESTGROUP_FAMILY, tableName, TEST_FAMILY, null, + Action.ADMIN, Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(revokeActionAtFamilyLevel, SUPERUSER, USER1_TESTGROUP_TABLE, + USER1_TESTGROUP_FAMILY); + verifyDenied(revokeActionAtFamilyLevel, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_FAMILY.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + + s1.addFamily(TEST_FAMILY); + try (ResultScanner scanner1 = table.getScanner(s1);) { + fail("Access should be denied as the user " + USER1_TESTGROUP_FAMILY + + " read privilege has been revoked on column family " + + Bytes.toString(TEST_FAMILY)); + } catch (AccessDeniedException ignore) { + } + } + return null; + } + }); + } + + /* + * Test grant and revoke access to a group at column qualifier level. + */ + @Test(timeout = 300000) + public void testGrantAndRevokeGroupAccessAtQualifierLevel() throws Exception { + + AccessTestAction grantActionAtQualifierLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.grant(protocol, '@' + TESTGROUP_QUAIFIER, tableName, TEST_FAMILY, Q1, + Action.ADMIN, Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(grantActionAtQualifierLevel, SUPERUSER, USER1_TESTGROUP_TABLE, + USER1_TESTGROUP_FAMILY, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_QUALIFIER.runAs(new PrivilegedExceptionAction() { + 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(next1.length == 1); + } + + try { + s1.addColumn(TEST_FAMILY, Q2); + table.getScanner(s1); + fail("Access should be denied as the user " + USER1_TESTGROUP_QUALIFIER + + " does not have read privilege on column family qualifier " + + Bytes.toString(TEST_FAMILY) + ':' + Bytes.toString(Q2)); + } catch (AccessDeniedException ignore) { + } + + try { + s1.addFamily(TEST_FAMILY_2); + table.getScanner(s1); + fail("Access should be denied as the user " + USER1_TESTGROUP_QUALIFIER + + " does not have read privilege on column family " + + Bytes.toString(TEST_FAMILY_2)); + } catch (AccessDeniedException ignore) { + } + } + return null; + } + }); + + AccessTestAction revokeActionAtQualifierLevel = new AccessTestAction() { + @Override + public Object run() throws Exception { + Table acl = connection.getTable(AccessControlLists.ACL_TABLE_NAME); + try { + BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); + AccessControlService.BlockingInterface protocol = + AccessControlService.newBlockingStub(service); + ProtobufUtil.revoke(protocol, '@' + TESTGROUP_QUAIFIER, tableName, TEST_FAMILY, Q1, + Action.ADMIN, Action.READ); + } finally { + acl.close(); + } + return null; + } + }; + + verifyAllowed(revokeActionAtQualifierLevel, SUPERUSER, USER1_TESTGROUP_TABLE, + USER1_TESTGROUP_FAMILY, USER1_TESTGROUP_QUALIFIER); + + USER1_TESTGROUP_QUALIFIER.runAs(new PrivilegedExceptionAction() { + public Void run() throws Exception { + try (Connection connection = ConnectionFactory.createConnection(conf); + Table table = connection.getTable(tableName);) { + Scan s1 = new Scan(); + + s1.addColumn(TEST_FAMILY, Q1); + try (ResultScanner scanner1 = table.getScanner(s1);) { + fail("Access should be denied as the user " + USER1_TESTGROUP_QUALIFIER + + " read privilege has been revoked on column family qualifier " + + Bytes.toString(TEST_FAMILY) + ':' + Bytes.toString(Q1)); + } catch (AccessDeniedException ignore) { + } + } + return null; + } + }); + } } -- 1.9.5.msysgit.0