Author: tedyu
Date: Wed Feb 15 18:13:28 2012
New Revision: 1244623
URL: http://svn.apache.org/viewvc?rev=1244623&view=rev
Log:
HBASE-5371 Introduce AccessControllerProtocol.checkPermissions(Permission[]
permissons) API (Enis Soztutar)
Modified:
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java
hbase/trunk/security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
Modified:
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java?rev=1244623&r1=1244622&r2=1244623&view=diff
==============================================================================
---
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
(original)
+++
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
Wed Feb 15 18:13:28 2012
@@ -14,17 +14,22 @@
package org.apache.hadoop.hbase.security.access;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.MapMaker;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
-import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
@@ -52,8 +57,11 @@ import org.apache.hadoop.hbase.security.
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.Bytes;
-import java.io.IOException;
-import java.util.*;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapMaker;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
/**
* Provides basic authorization checks for data access and administrative
@@ -155,7 +163,7 @@ public class AccessController extends Ba
/**
* Version number for AccessControllerProtocol
*/
- private static final long PROTOCOL_VERSION = 1L;
+ private static final long PROTOCOL_VERSION = 2L;
TableAuthManager authManager = null;
@@ -959,6 +967,40 @@ public class AccessController extends Ba
}
@Override
+ public void checkPermissions(Permission[] permissions) throws IOException {
+ byte[] tableName = regionEnv.getRegion().getTableDesc().getName();
+ for (Permission permission : permissions) {
+ if (permission instanceof TablePermission) {
+ TablePermission tperm = (TablePermission) permission;
+ for (Permission.Action action : permission.getActions()) {
+ if (!Arrays.equals(tperm.getTable(), tableName)) {
+ throw new CoprocessorException(AccessController.class,
String.format("This method "
+ + "can only execute at the table specified in TablePermission.
" +
+ "Table of the region:%s , requested table:%s",
Bytes.toString(tableName),
+ Bytes.toString(tperm.getTable())));
+ }
+
+ HashMap<byte[], Set<byte[]>> familyMap =
Maps.newHashMapWithExpectedSize(1);
+ if (tperm.getFamily() != null) {
+ if (tperm.getQualifier() != null) {
+ familyMap.put(tperm.getFamily(),
Sets.newHashSet(tperm.getQualifier()));
+ } else {
+ familyMap.put(tperm.getFamily(), null);
+ }
+ }
+
+ requirePermission(action, regionEnv, familyMap);
+ }
+
+ } else {
+ for (Permission.Action action : permission.getActions()) {
+ requirePermission(action);
+ }
+ }
+ }
+ }
+
+ @Override
public long getProtocolVersion(String protocol, long clientVersion) throws
IOException {
return PROTOCOL_VERSION;
}
Modified:
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java?rev=1244623&r1=1244622&r2=1244623&view=diff
==============================================================================
---
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java
(original)
+++
hbase/trunk/security/src/main/java/org/apache/hadoop/hbase/security/access/AccessControllerProtocol.java
Wed Feb 15 18:13:28 2012
@@ -18,15 +18,19 @@
package org.apache.hadoop.hbase.security.access;
-import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
-
import java.io.IOException;
import java.util.List;
+import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
+
/**
* A custom protocol defined for maintaining and querying access control lists.
*/
public interface AccessControllerProtocol extends CoprocessorProtocol {
+
+ /* V2: Added {@link #checkPermissions(Permission...)}) */
+ public static final long VERSION = 2L;
+
/**
* Grants the given user or group the privilege to perform the given actions
* over the specified scope contained in {@link TablePermission}
@@ -65,4 +69,18 @@ public interface AccessControllerProtoco
*/
public List<UserPermission> getUserPermissions(byte[] tableName)
throws IOException;
+
+ /**
+ * Checks whether the given Permissions will pass the access checks for the
+ * current user. Global permissions can be checked from the -acl- table
+ * or any other table, however TablePermissions can only be checked by
+ * the table's regions. If access control checks fail this method throws
+ * AccessDeniedException.
+ * @param permissions to check for. Permission subclasses can be used
+ * to do more specific checks at the table/family/column level.
+ * @throws IOException if there is an error checking the permissions
+ */
+ public void checkPermissions(Permission[] permissions)
+ throws IOException;
+
}
Modified:
hbase/trunk/security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
URL:
http://svn.apache.org/viewvc/hbase/trunk/security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java?rev=1244623&r1=1244622&r2=1244623&view=diff
==============================================================================
---
hbase/trunk/security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
(original)
+++
hbase/trunk/security/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java
Wed Feb 15 18:13:28 2012
@@ -18,9 +18,14 @@
package org.apache.hadoop.hbase.security.access;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,7 +37,17 @@ import org.apache.hadoop.hbase.HRegionIn
import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.ServerName;
-import org.apache.hadoop.hbase.client.*;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Increment;
+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.RetriesExhaustedWithDetailsException;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
@@ -43,8 +58,6 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import static org.junit.Assert.*;
-
/**
* Performs authorization checks for common operations, according to different
* levels of authorized users.
@@ -126,6 +139,13 @@ public class TestAccessController {
}
}
+ public void verifyAllowed(PrivilegedExceptionAction action, User... users)
+ throws Exception {
+ for (User user : users) {
+ verifyAllowed(user, action);
+ }
+ }
+
public void verifyDenied(User user, PrivilegedExceptionAction action)
throws Exception {
try {
@@ -151,6 +171,13 @@ public class TestAccessController {
}
}
+ public void verifyDenied(PrivilegedExceptionAction action, User... users)
+ throws Exception {
+ for (User user : users) {
+ verifyDenied(user, action);
+ }
+ }
+
@Test
public void testTableCreate() throws Exception {
PrivilegedExceptionAction createTable = new PrivilegedExceptionAction() {
@@ -977,4 +1004,193 @@ public class TestAccessController {
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
+
+ /** global operations*/
+ private void verifyGlobal(PrivilegedExceptionAction<?> action) throws
Exception {
+ // should be allowed
+ verifyAllowed(SUPERUSER, action);
+
+ // should be denied
+ verifyDenied(USER_OWNER, action);
+ verifyDenied(USER_RW, action);
+ verifyDenied(USER_NONE, action);
+ verifyDenied(USER_RO, action);
+ }
+
+ public void checkGlobalPerms(Permission.Action... actions) throws
IOException {
+ HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
+ AccessControllerProtocol protocol =
+ acl.coprocessorProxy(AccessControllerProtocol.class, new byte[0]);
+
+ Permission[] perms = new Permission[actions.length];
+ for (int i=0; i < actions.length; i++) {
+ perms[i] = new Permission(actions[i]);
+ }
+
+ protocol.checkPermissions(perms);
+ }
+
+ public void checkTablePerms(byte[] table, byte[] family, byte[] column,
+ Permission.Action... actions) throws IOException {
+ Permission[] perms = new Permission[actions.length];
+ for (int i=0; i < actions.length; i++) {
+ perms[i] = new TablePermission(table, family, column, actions[i]);
+ }
+
+ checkTablePerms(table, perms);
+ }
+
+ public void checkTablePerms(byte[] table, Permission...perms) throws
IOException {
+ HTable acl = new HTable(conf, table);
+ AccessControllerProtocol protocol =
+ acl.coprocessorProxy(AccessControllerProtocol.class, new byte[0]);
+
+ protocol.checkPermissions(perms);
+ }
+
+ public void grant(AccessControllerProtocol protocol, User user, byte[] t,
byte[] f,
+ byte[] q, Permission.Action... actions) throws IOException {
+ protocol.grant(Bytes.toBytes(user.getShortName()), new TablePermission(t,
f, q, actions));
+ }
+
+ @Test
+ public void testCheckPermissions() throws Exception {
+ final HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
+ final AccessControllerProtocol protocol =
+ acl.coprocessorProxy(AccessControllerProtocol.class, TEST_TABLE);
+
+ //--------------------------------------
+ //test global permissions
+ PrivilegedExceptionAction<Void> globalAdmin = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkGlobalPerms(Permission.Action.ADMIN);
+ return null;
+ }
+ };
+ //verify that only superuser can admin
+ verifyGlobal(globalAdmin);
+
+ //--------------------------------------
+ //test multiple permissions
+ PrivilegedExceptionAction<Void> globalReadWrite = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkGlobalPerms(Permission.Action.READ, Permission.Action.WRITE);
+ return null;
+ }
+ };
+
+ verifyGlobal(globalReadWrite);
+
+ //--------------------------------------
+ //table/column/qualifier level permissions
+ final byte[] TEST_Q1 = Bytes.toBytes("q1");
+ final byte[] TEST_Q2 = Bytes.toBytes("q2");
+
+ User userTable = User.createUserForTesting(conf, "user_check_perms_table",
new String[0]);
+ User userColumn = User.createUserForTesting(conf,
"user_check_perms_family", new String[0]);
+ User userQualifier = User.createUserForTesting(conf, "user_check_perms_q",
new String[0]);
+
+ grant(protocol, userTable, TEST_TABLE, null, null, Permission.Action.READ);
+ grant(protocol, userColumn, TEST_TABLE, TEST_FAMILY, null,
Permission.Action.READ);
+ grant(protocol, userQualifier, TEST_TABLE, TEST_FAMILY, TEST_Q1,
Permission.Action.READ);
+
+ PrivilegedExceptionAction<Void> tableRead = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, null, null, Permission.Action.READ);
+ return null;
+ }
+ };
+
+ PrivilegedExceptionAction<Void> columnRead = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ);
+ return null;
+ }
+ };
+
+ PrivilegedExceptionAction<Void> qualifierRead = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, TEST_FAMILY, TEST_Q1,
Permission.Action.READ);
+ return null;
+ }
+ };
+
+ PrivilegedExceptionAction<Void> multiQualifierRead = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, new Permission[] {
+ new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_Q1,
Permission.Action.READ),
+ new TablePermission(TEST_TABLE, TEST_FAMILY, TEST_Q2,
Permission.Action.READ),
+ });
+ return null;
+ }
+ };
+
+ PrivilegedExceptionAction<Void> globalAndTableRead = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, new Permission[] {
+ new Permission(Permission.Action.READ),
+ new TablePermission(TEST_TABLE, null, (byte[])null,
Permission.Action.READ),
+ });
+ return null;
+ }
+ };
+
+ PrivilegedExceptionAction<Void> noCheck = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, new Permission[0]);
+ return null;
+ }
+ };
+
+ verifyAllowed(tableRead, SUPERUSER, userTable);
+ verifyDenied(tableRead, userColumn, userQualifier);
+
+ verifyAllowed(columnRead, SUPERUSER, userTable, userColumn);
+ verifyDenied(columnRead, userQualifier);
+
+ verifyAllowed(qualifierRead, SUPERUSER, userTable, userColumn,
userQualifier);
+
+ verifyAllowed(multiQualifierRead, SUPERUSER, userTable, userColumn);
+ verifyDenied(multiQualifierRead, userQualifier);
+
+ verifyAllowed(globalAndTableRead, SUPERUSER);
+ verifyDenied(globalAndTableRead, userTable, userColumn, userQualifier);
+
+ verifyAllowed(noCheck, SUPERUSER, userTable, userColumn, userQualifier);
+
+ //--------------------------------------
+ //test family level multiple permissions
+ PrivilegedExceptionAction<Void> familyReadWrite = new
PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ checkTablePerms(TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ,
+ Permission.Action.WRITE);
+ return null;
+ }
+ };
+ // should be allowed
+ verifyAllowed(familyReadWrite, SUPERUSER, USER_OWNER, USER_RW);
+ // should be denied
+ verifyDenied(familyReadWrite, USER_NONE, USER_RO);
+
+ //--------------------------------------
+ //check for wrong table region
+ try {
+ //but ask for TablePermissions for TEST_TABLE
+ protocol.checkPermissions(new Permission[] {(Permission) new
TablePermission(
+ TEST_TABLE, null, (byte[])null, Permission.Action.CREATE)});
+ fail("this should have thrown CoprocessorException");
+ } catch(CoprocessorException ex) {
+ //expected
+ }
+
+ }
}