HBASE-6209. ACL Corrections for AccessControllerProtocol (Laxman)

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1352354 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andrew Kyle Purtell 2012-06-20 22:24:39 +00:00
parent 9aca0a9450
commit 5487a32813
2 changed files with 137 additions and 58 deletions

View File

@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.client.Append;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Increment;
@ -142,8 +143,12 @@ public class AccessController extends BaseRegionObserver
.append(toContextString()).toString();
}
public static AuthResult allow(String reason, User user,
Permission.Action action, byte[] table) {
public static AuthResult allow(String reason, User user, Permission.Action action,
byte[] table, byte[] family, byte[] qualifier) {
return new AuthResult(true, reason, user, action, table, family, qualifier);
}
public static AuthResult allow(String reason, User user, Permission.Action action, byte[] table) {
return new AuthResult(true, reason, user, action, table, null, null);
}
@ -356,22 +361,26 @@ public class AccessController extends BaseRegionObserver
}
/**
* Authorizes that the current user has any of the given permissions for the given table.
* Authorizes that the current user has any of the given permissions for the
* given table, column family and column qualifier.
* @param tableName Table requested
* @param family Column family requested
* @param qualifier Column qualifier requested
* @throws IOException if obtaining the current user fails
* @throws AccessDeniedException if user has no authorization
*/
private void requireTablePermission(byte[] tableName, Action... permissions) throws IOException {
private void requirePermission(byte[] tableName, byte[] family, byte[] qualifier,
Action... permissions) throws IOException {
User user = getActiveUser();
AuthResult result = null;
for (Action permission : permissions) {
if (authManager.authorize(user, tableName, (byte[]) null, permission)) {
result = AuthResult.allow("Table permission granted", user, permission, tableName);
if (authManager.authorize(user, tableName, null, null, permission)) {
result = AuthResult.allow("Table permission granted", user, permission, tableName, family, qualifier);
break;
} else {
// rest of the world
result = AuthResult.deny("Insufficient permissions", user, permission, tableName);
result = AuthResult.deny("Insufficient permissions", user, permission, tableName, family, qualifier);
}
}
logResult(result);
@ -540,10 +549,11 @@ public class AccessController extends BaseRegionObserver
HTableDescriptor desc, HRegionInfo[] regions) throws IOException {}
@Override
public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preDeleteTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preDeleteTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {}
@ -557,10 +567,11 @@ public class AccessController extends BaseRegionObserver
byte[] tableName) throws IOException {}
@Override
public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HTableDescriptor htd) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preModifyTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
HTableDescriptor htd) throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preModifyTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HTableDescriptor htd) throws IOException {}
@ -582,10 +593,11 @@ public class AccessController extends BaseRegionObserver
@Override
public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HColumnDescriptor column) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preAddColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
HColumnDescriptor column) throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preAddColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HColumnDescriptor column) throws IOException {}
@ -597,10 +609,11 @@ public class AccessController extends BaseRegionObserver
byte[] tableName, HColumnDescriptor column) throws IOException {}
@Override
public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HColumnDescriptor descriptor) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preModifyColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
HColumnDescriptor descriptor) throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preModifyColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, HColumnDescriptor descriptor) throws IOException {}
@ -613,10 +626,11 @@ public class AccessController extends BaseRegionObserver
@Override
public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, byte[] col) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preDeleteColumn(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName,
byte[] col) throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preDeleteColumnHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName, byte[] col) throws IOException {}
@ -631,10 +645,11 @@ public class AccessController extends BaseRegionObserver
byte[] tableName, byte[] col) throws IOException {}
@Override
public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preEnableTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preEnableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {}
@ -646,10 +661,11 @@ public class AccessController extends BaseRegionObserver
byte[] tableName) throws IOException {}
@Override
public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {
requireTablePermission(tableName, Action.ADMIN, Action.CREATE);
public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> c, byte[] tableName)
throws IOException {
requirePermission(tableName, null, null, Action.ADMIN, Action.CREATE);
}
@Override
public void preDisableTableHandler(ObserverContext<MasterCoprocessorEnvironment> c,
byte[] tableName) throws IOException {}
@ -762,18 +778,18 @@ public class AccessController extends BaseRegionObserver
@Override
public void preFlush(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
requireTablePermission(getTableName(e.getEnvironment()), Action.ADMIN);
requirePermission(getTableName(e.getEnvironment()), null, null, Action.ADMIN);
}
@Override
public void preSplit(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
requireTablePermission(getTableName(e.getEnvironment()), Action.ADMIN);
requirePermission(getTableName(e.getEnvironment()), null, null, Action.ADMIN);
}
@Override
public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
final Store store, final InternalScanner scanner) throws IOException {
requireTablePermission(getTableName(e.getEnvironment()), Action.ADMIN);
requirePermission(getTableName(e.getEnvironment()), null, null, Action.ADMIN);
return scanner;
}
@ -899,6 +915,13 @@ public class AccessController extends BaseRegionObserver
return -1;
}
@Override
public Result preAppend(ObserverContext<RegionCoprocessorEnvironment> c, Append append)
throws IOException {
requirePermission(TablePermission.Action.WRITE, c.getEnvironment(), append.getFamilyMap());
return null;
}
@Override
public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
final Increment increment)
@ -1003,25 +1026,23 @@ public class AccessController extends BaseRegionObserver
* This will be restricted by both client side and endpoint implementations.
*/
@Override
public void grant(UserPermission userPermission)
throws IOException {
public void grant(UserPermission perm) throws IOException {
// verify it's only running at .acl.
if (aclRegion) {
if (LOG.isDebugEnabled()) {
LOG.debug("Received request to grant access permission " + userPermission.toString());
LOG.debug("Received request to grant access permission " + perm.toString());
}
requirePermission(Permission.Action.ADMIN);
requirePermission(perm.getTable(), perm.getFamily(), perm.getQualifier(), Action.ADMIN);
AccessControlLists.addUserPermission(regionEnv.getConfiguration(), userPermission);
AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
if (AUDITLOG.isTraceEnabled()) {
// audit log should store permission changes in addition to auth results
AUDITLOG.trace("Granted permission " + userPermission.toString());
AUDITLOG.trace("Granted permission " + perm.toString());
}
} else {
throw new CoprocessorException(AccessController.class, "This method " +
"can only execute at " +
Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
throw new CoprocessorException(AccessController.class, "This method "
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
}
}
@ -1035,25 +1056,23 @@ public class AccessController extends BaseRegionObserver
}
@Override
public void revoke(UserPermission userPermission)
throws IOException{
public void revoke(UserPermission perm) throws IOException {
// only allowed to be called on _acl_ region
if (aclRegion) {
if (LOG.isDebugEnabled()) {
LOG.debug("Received request to revoke access permission " + userPermission.toString());
LOG.debug("Received request to revoke access permission " + perm.toString());
}
requirePermission(Permission.Action.ADMIN);
requirePermission(perm.getTable(), perm.getFamily(), perm.getQualifier(), Action.ADMIN);
AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), userPermission);
AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
if (AUDITLOG.isTraceEnabled()) {
// audit log should record all permission changes
AUDITLOG.trace("Revoked permission " + userPermission.toString());
AUDITLOG.trace("Revoked permission " + perm.toString());
}
} else {
throw new CoprocessorException(AccessController.class, "This method " +
"can only execute at " +
Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
throw new CoprocessorException(AccessController.class, "This method "
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
}
}
@ -1067,19 +1086,17 @@ public class AccessController extends BaseRegionObserver
}
@Override
public List<UserPermission> getUserPermissions(final byte[] tableName)
throws IOException {
public List<UserPermission> getUserPermissions(final byte[] tableName) throws IOException {
// only allowed to be called on _acl_ region
if (aclRegion) {
requirePermission(Permission.Action.ADMIN);
requirePermission(tableName, null, null, Action.ADMIN);
List<UserPermission> perms = AccessControlLists.getUserPermissions
(regionEnv.getConfiguration(), tableName);
List<UserPermission> perms = AccessControlLists.getUserPermissions(
regionEnv.getConfiguration(), tableName);
return perms;
} else {
throw new CoprocessorException(AccessController.class, "This method " +
"can only execute at " +
Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
throw new CoprocessorException(AccessController.class, "This method "
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
}
}

View File

@ -36,6 +36,7 @@ import org.apache.hadoop.hbase.HServerAddress;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.LargeTests;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.client.Append;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
@ -590,8 +591,69 @@ public class TestAccessController {
verifyReadWrite(checkAndPut);
}
@Test
public void testAppend() throws Exception {
PrivilegedExceptionAction appendAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Append append = new Append(TEST_TABLE);
append.add(TEST_FAMILY, Bytes.toBytes("qualifier"), Bytes.toBytes("value"));
ACCESS_CONTROLLER.preAppend(ObserverContext.createAndPrepare(RCP_ENV, null), append);
return null;
}
};
verifyAllowed(appendAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_RW);
verifyDenied(appendAction, USER_CREATE, USER_RO, USER_NONE);
}
@Test
public void testGrantRevoke() throws Exception {
PrivilegedExceptionAction grantAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
AccessControllerProtocol protocol = acl.coprocessorProxy(AccessControllerProtocol.class,
TEST_TABLE);
protocol.grant(new UserPermission(Bytes.toBytes(USER_RO.getShortName()), TEST_TABLE,
TEST_FAMILY, (byte[]) null, Action.READ));
return null;
}
};
PrivilegedExceptionAction revokeAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
AccessControllerProtocol protocol = acl.coprocessorProxy(AccessControllerProtocol.class,
TEST_TABLE);
protocol.revoke(new UserPermission(Bytes.toBytes(USER_RO.getShortName()), TEST_TABLE,
TEST_FAMILY, (byte[]) null, Action.READ));
return null;
}
};
PrivilegedExceptionAction getPermissionsAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
AccessControllerProtocol protocol = acl.coprocessorProxy(AccessControllerProtocol.class,
TEST_TABLE);
protocol.getUserPermissions(TEST_TABLE);
return null;
}
};
verifyAllowed(grantAction, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(grantAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
verifyAllowed(revokeAction, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(revokeAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
verifyAllowed(getPermissionsAction, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(getPermissionsAction, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testPostGrantRevoke() throws Exception {
final byte[] tableName = Bytes.toBytes("TempTable");
final byte[] family1 = Bytes.toBytes("f1");
final byte[] family2 = Bytes.toBytes("f2");
@ -823,7 +885,7 @@ public class TestAccessController {
}
@Test
public void testGrantRevokeAtQualifierLevel() throws Exception {
public void testPostGrantRevokeAtQualifierLevel() throws Exception {
final byte[] tableName = Bytes.toBytes("testGrantRevokeAtQualifierLevel");
final byte[] family1 = Bytes.toBytes("f1");
final byte[] family2 = Bytes.toBytes("f2");