Repository: hbase Updated Branches: refs/heads/branch-1 a731ea630 -> 09617cc2a refs/heads/master b4371252f -> 8a2c84156
http://git-wip-us.apache.org/repos/asf/hbase/blob/8a2c8415/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java index ae4af26..4ed81dc 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java @@ -223,7 +223,7 @@ public class TableAuthManager { * Updates the internal permissions cache for a single table, splitting * the permissions listed into separate caches for users and groups to optimize * group lookups. - * + * * @param table * @param tablePerms */ @@ -349,6 +349,20 @@ public class TableAuthManager { return false; } + private boolean hasAccess(List<TablePermission> perms, + TableName table, Permission.Action action) { + if (perms != null) { + for (TablePermission p : perms) { + if (p.implies(action)) { + return true; + } + } + } else if (LOG.isDebugEnabled()) { + LOG.debug("No permissions found for table="+table); + } + return false; + } + /** * Authorize a user for a given KV. This is called from AccessControlFilter. */ @@ -442,7 +456,7 @@ public class TableAuthManager { byte[] qualifier, Permission.Action action) { if (table == null) table = AccessControlLists.ACL_TABLE_NAME; // Global and namespace authorizations supercede table level - if (authorize(user, table.getNamespaceAsString(), action)) { + if (authorize(user, table.getNamespaceAsString(), action)) { return true; } // Check table permissions @@ -451,6 +465,25 @@ public class TableAuthManager { } /** + * Checks if the user has access to the full table or at least a family/qualifier + * for the specified action. + * + * @param user + * @param table + * @param action + * @return true if the user has access to the table, false otherwise + */ + public boolean userHasAccess(User user, TableName table, Permission.Action action) { + if (table == null) table = AccessControlLists.ACL_TABLE_NAME; + // Global and namespace authorizations supercede table level + if (authorize(user, table.getNamespaceAsString(), action)) { + return true; + } + // Check table permissions + return hasAccess(getTablePermissions(table).getUser(user.getShortName()), table, action); + } + + /** * Checks global authorization for a given action for a group, based on the stored * permissions. */ @@ -460,7 +493,7 @@ public class TableAuthManager { /** * Checks authorization to a given table and column family for a group, based - * on the stored permissions. + * on the stored permissions. * @param groupName * @param table * @param family @@ -483,6 +516,29 @@ public class TableAuthManager { return authorize(getTablePermissions(table).getGroup(groupName), table, family, action); } + /** + * Checks if the user has access to the full table or at least a family/qualifier + * for the specified action. + * @param groupName + * @param table + * @param action + * @return true if the group has access to the table, false otherwise + */ + public boolean groupHasAccess(String groupName, TableName table, Permission.Action action) { + // Global authorization supercedes table level + if (authorizeGroup(groupName, action)) { + return true; + } + if (table == null) table = AccessControlLists.ACL_TABLE_NAME; + // Namespace authorization supercedes table level + if (hasAccess(getNamespacePermissions(table.getNamespaceAsString()).getGroup(groupName), + table, action)) { + return true; + } + // Check table level + return hasAccess(getTablePermissions(table).getGroup(groupName), table, action); + } + public boolean authorize(User user, TableName table, byte[] family, byte[] qualifier, Permission.Action action) { if (authorizeUser(user, table, family, qualifier, action)) { @@ -500,6 +556,22 @@ public class TableAuthManager { return false; } + public boolean hasAccess(User user, TableName table, Permission.Action action) { + if (userHasAccess(user, table, action)) { + return true; + } + + String[] groups = user.getGroupNames(); + if (groups != null) { + for (String group : groups) { + if (groupHasAccess(group, table, action)) { + return true; + } + } + } + return false; + } + public boolean authorize(User user, TableName table, byte[] family, Permission.Action action) { return authorize(user, table, family, null, action); http://git-wip-us.apache.org/repos/asf/hbase/blob/8a2c8415/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java index 8fc4471..d3593cd 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java @@ -54,6 +54,7 @@ import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.protobuf.RequestConverter; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription; import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest; +import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableNamesRequest; import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas; import org.apache.hadoop.hbase.regionserver.HRegionServer; import org.apache.hadoop.hbase.testclassification.CoprocessorTests; @@ -150,6 +151,8 @@ public class TestMasterObserver { private boolean postModifyTableHandlerCalled; private boolean preGetTableDescriptorsCalled; private boolean postGetTableDescriptorsCalled; + private boolean postGetTableNamesCalled; + private boolean preGetTableNamesCalled; public void enableBypass(boolean bypass) { this.bypass = bypass; @@ -224,6 +227,8 @@ public class TestMasterObserver { postModifyTableHandlerCalled = false; preGetTableDescriptorsCalled = false; postGetTableDescriptorsCalled = false; + postGetTableNamesCalled = false; + preGetTableNamesCalled = false; } @Override @@ -773,11 +778,6 @@ public class TestMasterObserver { postDeleteSnapshotCalled = true; } - @Override - public void preGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx, - List<TableName> tableNamesList, List<HTableDescriptor> descriptors) throws IOException { - } - public boolean wasDeleteSnapshotCalled() { return preDeleteSnapshotCalled && postDeleteSnapshotCalled; } @@ -1018,17 +1018,29 @@ public class TestMasterObserver { @Override public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx, - List<HTableDescriptor> descriptors) throws IOException { + List<TableName> tableNamesList, List<HTableDescriptor> descriptors, + String regex) throws IOException { + postGetTableDescriptorsCalled = true; + } + + public boolean wasGetTableDescriptorsCalled() { + return preGetTableDescriptorsCalled && postGetTableDescriptorsCalled; } @Override - public void postGetTableDescriptors(ObserverContext<MasterCoprocessorEnvironment> ctx, + public void preGetTableNames(ObserverContext<MasterCoprocessorEnvironment> ctx, List<HTableDescriptor> descriptors, String regex) throws IOException { - postGetTableDescriptorsCalled = true; + preGetTableNamesCalled = true; } - public boolean wasGetTableDescriptorsCalled() { - return preGetTableDescriptorsCalled && postGetTableDescriptorsCalled; + @Override + public void postGetTableNames(ObserverContext<MasterCoprocessorEnvironment> ctx, + List<HTableDescriptor> descriptors, String regex) throws IOException { + postGetTableNamesCalled = true; + } + + public boolean wasGetTableNamesCalled() { + return preGetTableNamesCalled && postGetTableNamesCalled; } @Override @@ -1552,4 +1564,19 @@ public class TestMasterObserver { cp.wasGetTableDescriptorsCalled()); } + @Test + public void testTableNamesEnumeration() throws Exception { + MiniHBaseCluster cluster = UTIL.getHBaseCluster(); + + HMaster master = cluster.getMaster(); + MasterCoprocessorHost host = master.getMasterCoprocessorHost(); + CPMasterObserver cp = (CPMasterObserver)host.findCoprocessor( + CPMasterObserver.class.getName()); + cp.resetStates(); + + master.getMasterRpcServices().getTableNames(null, + GetTableNamesRequest.newBuilder().build()); + assertTrue("Coprocessor should be called on table names request", + cp.wasGetTableNamesCalled()); + } } http://git-wip-us.apache.org/repos/asf/hbase/blob/8a2c8415/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index 9f268c9..fb7af84 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -26,6 +26,7 @@ import static org.junit.Assert.fail; import java.io.IOException; import java.security.PrivilegedAction; +import java.util.Arrays; import java.util.List; import org.apache.commons.logging.Log; @@ -264,7 +265,7 @@ public class TestAccessController extends SecureTestUtil { try { assertEquals(4, AccessControlClient.getUserPermissions(conf, TEST_TABLE.toString()).size()); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.getUserPermissions. " + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.getUserPermissions. ", e); } } @@ -2059,11 +2060,14 @@ public class TestAccessController extends SecureTestUtil { AccessTestAction listTablesAction = new AccessTestAction() { @Override public Object run() throws Exception { - Admin admin = TEST_UTIL.getHBaseAdmin(); + Connection unmanagedConnection = + ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); + Admin admin = unmanagedConnection.getAdmin(); try { admin.listTables(); } finally { admin.close(); + unmanagedConnection.close(); } return null; } @@ -2072,24 +2076,48 @@ public class TestAccessController extends SecureTestUtil { AccessTestAction getTableDescAction = new AccessTestAction() { @Override public Object run() throws Exception { - Admin admin = TEST_UTIL.getHBaseAdmin(); + Connection unmanagedConnection = + ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); + Admin admin = unmanagedConnection.getAdmin(); try { admin.getTableDescriptor(TEST_TABLE.getTableName()); } finally { admin.close(); + unmanagedConnection.close(); } return null; } }; - verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN); - verifyDenied(listTablesAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, TABLE_ADMIN); + verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN, USER_CREATE, TABLE_ADMIN); + verifyDenied(listTablesAction, USER_RW, USER_RO, USER_NONE); verifyAllowed(getTableDescAction, SUPERUSER, USER_ADMIN, USER_CREATE, TABLE_ADMIN); verifyDenied(getTableDescAction, USER_RW, USER_RO, USER_NONE); } @Test + public void testTableNameEnumeration() throws Exception { + AccessTestAction listTablesAction = new AccessTestAction() { + @Override + public Object run() throws Exception { + Connection unmanagedConnection = + ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); + Admin admin = unmanagedConnection.getAdmin(); + try { + return Arrays.asList(admin.listTableNames()); + } finally { + admin.close(); + unmanagedConnection.close(); + } + } + }; + + verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN, USER_CREATE, USER_RW, USER_RO); + verifyDenied(listTablesAction, USER_NONE); + } + + @Test public void testTableDeletion() throws Exception { User TABLE_ADMIN = User.createUserForTesting(conf, "TestUser", new String[0]); @@ -2166,7 +2194,7 @@ public class TestAccessController extends SecureTestUtil { grantOnTableUsingAccessControlClient(TEST_UTIL, conf, testGrantRevoke.getShortName(), TEST_TABLE.getTableName(), null, null, Permission.Action.READ); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.grant. " + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.grant. ", e); } // Now testGrantRevoke should be able to read also @@ -2177,7 +2205,7 @@ public class TestAccessController extends SecureTestUtil { revokeFromTableUsingAccessControlClient(TEST_UTIL, conf, testGrantRevoke.getShortName(), TEST_TABLE.getTableName(), null, null, Permission.Action.READ); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.revoke " + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.revoke ", e); } // Now testGrantRevoke shouldn't be able read @@ -2207,7 +2235,7 @@ public class TestAccessController extends SecureTestUtil { grantOnNamespaceUsingAccessControlClient(TEST_UTIL, conf, testNS.getShortName(), TEST_TABLE.getTableName().getNamespaceAsString(), Permission.Action.READ); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.grant. " + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.grant. ", e); } // Now testNS should be able to read also @@ -2218,7 +2246,7 @@ public class TestAccessController extends SecureTestUtil { revokeFromNamespaceUsingAccessControlClient(TEST_UTIL, conf, testNS.getShortName(), TEST_TABLE.getTableName().getNamespaceAsString(), Permission.Action.READ); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.revoke " + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.revoke ", e); } // Now testNS shouldn't be able read @@ -2451,8 +2479,7 @@ public class TestAccessController extends SecureTestUtil { try { return AccessControlClient.getUserPermissions(conf, regex); } catch (Throwable e) { - LOG.error("error during call of AccessControlClient.getUserPermissions. " - + e.getStackTrace()); + LOG.error("error during call of AccessControlClient.getUserPermissions.", e); return null; } } @@ -2462,10 +2489,12 @@ public class TestAccessController extends SecureTestUtil { @Test public void testAccessControlClientUserPerms() throws Exception { // adding default prefix explicitly as it is not included in the table name. - final String regex = TEST_TABLE.getTableName().getNamespaceAsString() + ":" - + TEST_TABLE.getTableName().getNameAsString(); + assertEquals(NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR, + TEST_TABLE.getTableName().getNamespaceAsString()); + final String regex = NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR + + TableName.NAMESPACE_DELIM + TEST_TABLE.getTableName().getNameAsString(); User testUserPerms = User.createUserForTesting(conf, "testUserPerms", new String[0]); - assertNull(testUserPerms.runAs(getPrivilegedAction(regex))); + assertEquals(0, testUserPerms.runAs(getPrivilegedAction(regex)).size()); // Grant TABLE ADMIN privs to testUserPerms grantOnTable(TEST_UTIL, testUserPerms.getShortName(), TEST_TABLE.getTableName(), null, null, Action.ADMIN); @@ -2475,12 +2504,12 @@ public class TestAccessController extends SecureTestUtil { assertEquals(5, perms.size()); } - - @Test - public void testAccessControllerRegexHandling() throws Exception { + public void testAccessControllerUserPermsRegexHandling() throws Exception { User testRegexHandler = User.createUserForTesting(conf, "testRegexHandling", new String[0]); - String tableName = "testRegex"; + + final String REGEX_ALL_TABLES = ".*"; + final String tableName = "testRegex"; final TableName table1 = TableName.valueOf(tableName); final byte[] family = Bytes.toBytes("f1"); @@ -2494,20 +2523,32 @@ public class TestAccessController extends SecureTestUtil { // creating the ns and table in it String ns = "testNamespace"; NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build(); - final TableName table2 = TableName.valueOf(ns + ":" + tableName); + final TableName table2 = TableName.valueOf(ns, tableName); TEST_UTIL.getMiniHBaseCluster().getMaster().createNamespace(desc); htd = new HTableDescriptor(table2); htd.addFamily(new HColumnDescriptor(family)); admin.createTable(htd); + TEST_UTIL.waitUntilAllRegionsAssigned(table2); + + // Verify that we can read sys-tables + String aclTableName = AccessControlLists.ACL_TABLE_NAME.getNameAsString(); + assertEquals(1, SUPERUSER.runAs(getPrivilegedAction(aclTableName)).size()); + assertEquals(0, testRegexHandler.runAs(getPrivilegedAction(aclTableName)).size()); // Grant TABLE ADMIN privs to testUserPerms + assertEquals(0, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); grantOnTable(TEST_UTIL, testRegexHandler.getShortName(), table1, null, null, Action.ADMIN); + assertEquals(2, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); grantOnTable(TEST_UTIL, testRegexHandler.getShortName(), table2, null, null, Action.ADMIN); + assertEquals(4, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); // USER_ADMIN, testUserPerms must have a row each. assertEquals(2, testRegexHandler.runAs(getPrivilegedAction(tableName)).size()); - assertEquals(2, testRegexHandler.runAs(getPrivilegedAction("default:" + tableName)).size()); - assertEquals(2, testRegexHandler.runAs(getPrivilegedAction(ns + ":" + tableName)).size()); + assertEquals(2, testRegexHandler.runAs(getPrivilegedAction( + NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR + TableName.NAMESPACE_DELIM + tableName) + ).size()); + assertEquals(2, testRegexHandler.runAs(getPrivilegedAction( + ns + TableName.NAMESPACE_DELIM + tableName)).size()); assertEquals(0, testRegexHandler.runAs(getPrivilegedAction("notMatchingAny")).size()); TEST_UTIL.deleteTable(table1); http://git-wip-us.apache.org/repos/asf/hbase/blob/8a2c8415/hbase-shell/src/main/ruby/hbase/admin.rb ---------------------------------------------------------------------- diff --git a/hbase-shell/src/main/ruby/hbase/admin.rb b/hbase-shell/src/main/ruby/hbase/admin.rb index 2c8828e..748a41c 100644 --- a/hbase-shell/src/main/ruby/hbase/admin.rb +++ b/hbase-shell/src/main/ruby/hbase/admin.rb @@ -43,7 +43,7 @@ module Hbase #---------------------------------------------------------------------------------------------- # Returns a list of tables in hbase def list(regex = ".*") - @admin.getTableNames(regex).to_a + @admin.listTableNames(regex).map { |t| t.getNameAsString } end #----------------------------------------------------------------------------------------------