Repository: hbase Updated Branches: refs/heads/0.98 a837d0a04 -> 9115f303c
HBASE-12925 Use acl cache for doing access control checks in prepare and clean phases of Bulkloading (Srikanth Srungarapu) Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/9115f303 Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/9115f303 Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/9115f303 Branch: refs/heads/0.98 Commit: 9115f303c19f01264272b4472108ff3e4b44907f Parents: a837d0a Author: Matteo Bertozzi <matteo.berto...@cloudera.com> Authored: Tue Jan 27 13:33:56 2015 +0000 Committer: Matteo Bertozzi <matteo.berto...@cloudera.com> Committed: Tue Jan 27 13:33:56 2015 +0000 ---------------------------------------------------------------------- .../hbase/security/access/AccessController.java | 83 +++++++++----------- .../hbase/security/access/TableAuthManager.java | 78 +++++++++++++++++- .../security/access/TestAccessController.java | 27 ++++++- 3 files changed, 137 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/9115f303/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index b564f64..cffcf10 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -18,7 +18,6 @@ import java.io.IOException; import java.net.InetAddress; import java.security.PrivilegedExceptionAction; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -30,7 +29,6 @@ import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellScanner; @@ -51,6 +49,7 @@ import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Tag; import org.apache.hadoop.hbase.catalog.MetaReader; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.Append; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Durability; @@ -431,6 +430,36 @@ public class AccessController extends BaseMasterAndRegionObserver } /** + * Authorizes that the current user has any of the given permissions to access the table. + * + * @param tableName Table requested + * @param permissions Actions being requested + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if user has no authorization + */ + private void requireAccess(String request, TableName tableName, + Action... permissions) throws IOException { + User user = getActiveUser(); + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.hasAccess(user, tableName, permission)) { + result = AuthResult.allow(request, "Table permission granted", user, + permission, tableName, null, null); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, tableName, null, null); + } + } + logResult(result); + if (!result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); + } + } + + /** * Authorizes that the current user has global privileges for the given action. * @param perm The action being requested * @throws IOException if obtaining the current user fails @@ -1000,7 +1029,7 @@ public class AccessController extends BaseMasterAndRegionObserver TableName tableName, final HTableDescriptor htd) throws IOException { final Configuration conf = c.getEnvironment().getConfiguration(); // default the table owner to current user, if not specified. - final String owner = (htd.getOwnerString() != null) ? htd.getOwnerString() : + final String owner = (htd.getOwnerString() != null) ? htd.getOwnerString() : getActiveUser().getShortName(); User.runAsLoginUser(new PrivilegedExceptionAction<Void>() { @Override @@ -1903,34 +1932,6 @@ public class AccessController extends BaseMasterAndRegionObserver } } - private AuthResult hasSomeAccess(RegionCoprocessorEnvironment e, String method, Action action) - throws IOException { - User requestUser = getActiveUser(); - final TableName tableName = e.getRegion().getTableDesc().getTableName(); - AuthResult authResult = permissionGranted(method, requestUser, action, e, - Collections.EMPTY_MAP); - if (!authResult.isAllowed()) { - final Configuration conf = e.getConfiguration(); - // hasSomeAccess is called from bulkload pre hooks - List<UserPermission> perms = - User.runAsLoginUser(new PrivilegedExceptionAction<List<UserPermission>>() { - @Override - public List<UserPermission> run() throws Exception { - return AccessControlLists.getUserTablePermissions(conf, tableName); - } - }); - for (UserPermission userPerm: perms) { - for (Action userAction: userPerm.getActions()) { - if (userAction.equals(action)) { - return AuthResult.allow(method, "Access allowed", requestUser, - action, tableName, null, null); - } - } - } - } - return authResult; - } - /** * Authorization check for * SecureBulkLoadProtocol.prepareBulkLoad() @@ -1941,14 +1942,8 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void prePrepareBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx, PrepareBulkLoadRequest request) throws IOException { - RegionCoprocessorEnvironment e = ctx.getEnvironment(); - - AuthResult authResult = hasSomeAccess(e, "prePrepareBulkLoad", Action.CREATE); - logResult(authResult); - if (!authResult.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions (table=" + - e.getRegion().getTableDesc().getTableName() + ", action=CREATE)"); - } + requireAccess("prePareBulkLoad", + ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE); } /** @@ -1961,14 +1956,8 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preCleanupBulkLoad(ObserverContext<RegionCoprocessorEnvironment> ctx, CleanupBulkLoadRequest request) throws IOException { - RegionCoprocessorEnvironment e = ctx.getEnvironment(); - - AuthResult authResult = hasSomeAccess(e, "preCleanupBulkLoad", Action.CREATE); - logResult(authResult); - if (!authResult.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions (table=" + - e.getRegion().getTableDesc().getTableName() + ", action=CREATE)"); - } + requireAccess("preCleanupBulkLoad", + ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE); } /* ---- EndpointObserver implementation ---- */ http://git-wip-us.apache.org/repos/asf/hbase/blob/9115f303/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 f2983ad..6ca40e6 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/9115f303/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 e10e0f1..ed885e2 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 @@ -257,7 +257,7 @@ public class TestAccessController extends SecureTestUtil { grantOnTable(TEST_UTIL, USER_ADMIN_CF.getShortName(), TEST_TABLE.getTableName(), TEST_FAMILY, - null, Permission.Action.ADMIN); + null, Permission.Action.ADMIN, Permission.Action.CREATE); assertEquals(5, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size()); try { @@ -2370,4 +2370,29 @@ public class TestAccessController extends SecureTestUtil { throw new HBaseIOException(e); } } + + private void verifyAnyCreate(AccessTestAction action) throws Exception { + verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_ADMIN_CF); + verifyDenied(action, USER_NONE, USER_RO, USER_RW); + } + + @Test + public void testPrepareAndCleanBulkLoad() throws Exception { + AccessTestAction prepareBulkLoadAction = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.prePrepareBulkLoad(ObserverContext.createAndPrepare(RCP_ENV, null), null); + return null; + } + }; + AccessTestAction cleanupBulkLoadAction = new AccessTestAction() { + @Override + public Object run() throws Exception { + ACCESS_CONTROLLER.preCleanupBulkLoad(ObserverContext.createAndPrepare(RCP_ENV, null), null); + return null; + } + }; + verifyAnyCreate(prepareBulkLoadAction); + verifyAnyCreate(cleanupBulkLoadAction); + } }