/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.lang.reflect.UndeclaredThrowableException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hbase.Coprocessor;
import org.apache.hadoop.hbase.HBaseTestingUtility;
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.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;
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.MasterCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
import org.apache.hadoop.hbase.io.hfile.CacheConfig;
import org.apache.hadoop.hbase.io.hfile.HFile;
import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.protobuf.RequestConverter;
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos;
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
import org.apache.hadoop.hbase.regionserver.HRegion;
import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
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.util.Bytes;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import com.google.protobuf.BlockingRpcChannel;
import com.google.protobuf.ByteString;
import com.google.protobuf.ServiceException;
/**
* Performs authorization checks for common operations, according to different
* levels of authorized users.
*/
@Category(LargeTests.class)
@SuppressWarnings("rawtypes")
public class TestAccessController {
private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
private static Configuration conf;
// user with all permissions
private static User SUPERUSER;
// user granted with all global permission
private static User USER_ADMIN;
// user with rw permissions on column family.
private static User USER_RW;
// user with read-only permissions
private static User USER_RO;
// user is table owner. will have all permissions on table
private static User USER_OWNER;
// user with create table permissions alone
private static User USER_CREATE;
// user with no permissions
private static User USER_NONE;
private static byte[] TEST_TABLE = Bytes.toBytes("testtable");
private static byte[] TEST_FAMILY = Bytes.toBytes("f1");
private static MasterCoprocessorEnvironment CP_ENV;
private static RegionCoprocessorEnvironment RCP_ENV;
private static RegionServerCoprocessorEnvironment RSCP_ENV;
private static AccessController ACCESS_CONTROLLER;
@BeforeClass
public static void setupBeforeClass() throws Exception {
// setup configuration
conf = TEST_UTIL.getConfiguration();
SecureTestUtil.enableSecurity(conf);
TEST_UTIL.startMiniCluster();
MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster().getCoprocessorHost();
cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(AccessController.class.getName());
CP_ENV = cpHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
Coprocessor.PRIORITY_HIGHEST, 1, conf);
RegionServerCoprocessorHost rsHost = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0)
.getCoprocessorHost();
RSCP_ENV = rsHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
Coprocessor.PRIORITY_HIGHEST, 1, conf);
// Wait for the ACL table to become available
TEST_UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME, 5000);
// create a set of test users
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
USER_ADMIN = User.createUserForTesting(conf, "admin2", new String[0]);
USER_RW = User.createUserForTesting(conf, "rwuser", new String[0]);
USER_RO = User.createUserForTesting(conf, "rouser", new String[0]);
USER_OWNER = User.createUserForTesting(conf, "owner", new String[0]);
USER_CREATE = User.createUserForTesting(conf, "tbl_create", new String[0]);
USER_NONE = User.createUserForTesting(conf, "nouser", new String[0]);
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
htd.setOwner(USER_OWNER);
admin.createTable(htd);
HRegion region = TEST_UTIL.getHBaseCluster().getRegions(TEST_TABLE).get(0);
RegionCoprocessorHost rcpHost = region.getCoprocessorHost();
RCP_ENV = rcpHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER,
Coprocessor.PRIORITY_HIGHEST, 1, conf);
// initilize access control
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
protocol.grant(null, RequestConverter.buildGrantRequest(USER_ADMIN.getShortName(),
null, null, null,
AccessControlProtos.Permission.Action.ADMIN,
AccessControlProtos.Permission.Action.CREATE,
AccessControlProtos.Permission.Action.READ,
AccessControlProtos.Permission.Action.WRITE));
protocol.grant(null, RequestConverter.buildGrantRequest(USER_RW.getShortName(),
TEST_TABLE, TEST_FAMILY, null,
AccessControlProtos.Permission.Action.READ,
AccessControlProtos.Permission.Action.WRITE));
protocol.grant(null, RequestConverter.buildGrantRequest(USER_RO.getShortName(), TEST_TABLE,
TEST_FAMILY, null, AccessControlProtos.Permission.Action.READ));
protocol.grant(null, RequestConverter.buildGrantRequest(USER_CREATE.getShortName(),
TEST_TABLE, null, null, AccessControlProtos.Permission.Action.CREATE));
} finally {
acl.close();
}
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
TEST_UTIL.shutdownMiniCluster();
}
public void verifyAllowed(User user, PrivilegedExceptionAction... actions) throws Exception {
for (PrivilegedExceptionAction action : actions) {
try {
user.runAs(action);
} catch (AccessDeniedException ade) {
fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
}
}
}
public void verifyAllowed(PrivilegedExceptionAction action, User... users) throws Exception {
for (User user : users) {
verifyAllowed(user, action);
}
}
public void verifyDenied(User user, PrivilegedExceptionAction... actions) throws Exception {
for (PrivilegedExceptionAction action : actions) {
try {
user.runAs(action);
fail("Expected AccessDeniedException for user '" + user.getShortName() + "'");
} catch (IOException e) {
boolean isAccessDeniedException = false;
if(e instanceof RetriesExhaustedWithDetailsException) {
// in case of batch operations, and put, the client assembles a
// RetriesExhaustedWithDetailsException instead of throwing an
// AccessDeniedException
for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) {
if (ex instanceof AccessDeniedException) {
isAccessDeniedException = true;
break;
}
}
}
else {
// For doBulkLoad calls AccessDeniedException
// is buried in the stack trace
Throwable ex = e;
do {
if (ex instanceof AccessDeniedException) {
isAccessDeniedException = true;
break;
}
} while((ex = ex.getCause()) != null);
}
if (!isAccessDeniedException) {
fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
}
} catch (UndeclaredThrowableException ute) {
// TODO why we get a PrivilegedActionException, which is unexpected?
Throwable ex = ute.getUndeclaredThrowable();
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException();
}
if (ex instanceof ServiceException) {
ServiceException se = (ServiceException)ex;
if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) {
// expected result
return;
}
}
fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
}
}
}
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() {
public Object run() throws Exception {
HTableDescriptor htd = new HTableDescriptor("testnewtable");
htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
ACCESS_CONTROLLER.preCreateTable(ObserverContext.createAndPrepare(CP_ENV, null), htd, null);
return null;
}
};
// verify that superuser can create tables
verifyAllowed(createTable, SUPERUSER, USER_ADMIN);
// all others should be denied
verifyDenied(createTable, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testTableModify() throws Exception {
PrivilegedExceptionAction modifyTable = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTableDescriptor htd = new HTableDescriptor(TEST_TABLE);
htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
htd.addFamily(new HColumnDescriptor("fam_" + User.getCurrent().getShortName()));
ACCESS_CONTROLLER.preModifyTable(ObserverContext.createAndPrepare(CP_ENV, null),
TEST_TABLE, htd);
return null;
}
};
verifyAllowed(modifyTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(modifyTable, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testTableDelete() throws Exception {
PrivilegedExceptionAction deleteTable = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER
.preDeleteTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE);
return null;
}
};
verifyAllowed(deleteTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(deleteTable, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testAddColumn() throws Exception {
final HColumnDescriptor hcd = new HColumnDescriptor("fam_new");
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preAddColumn(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE,
hcd);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(action, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testModifyColumn() throws Exception {
final HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY);
hcd.setMaxVersions(10);
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preModifyColumn(ObserverContext.createAndPrepare(CP_ENV, null),
TEST_TABLE, hcd);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(action, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testDeleteColumn() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preDeleteColumn(ObserverContext.createAndPrepare(CP_ENV, null),
TEST_TABLE, TEST_FAMILY);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(action, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testTableDisable() throws Exception {
PrivilegedExceptionAction disableTable = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null),
TEST_TABLE);
return null;
}
};
PrivilegedExceptionAction disableAclTable = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null),
AccessControlLists.ACL_TABLE_NAME);
return null;
}
};
verifyAllowed(disableTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(disableTable, USER_RW, USER_RO, USER_NONE);
// No user should be allowed to disable _acl_ table
verifyDenied(disableAclTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO);
}
@Test
public void testTableEnable() throws Exception {
PrivilegedExceptionAction enableTable = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER
.preEnableTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE);
return null;
}
};
verifyAllowed(enableTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER);
verifyDenied(enableTable, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testMove() throws Exception {
Map<HRegionInfo, ServerName> regions;
HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE);
try {
regions = table.getRegionLocations();
} finally {
table.close();
}
final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
final ServerName server = TEST_UTIL.getHBaseCluster().getRegionServer(0).getServerName();
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preMove(ObserverContext.createAndPrepare(CP_ENV, null),
firstRegion.getKey(), server, server);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testAssign() throws Exception {
Map<HRegionInfo, ServerName> regions;
HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE);
try {
regions = table.getRegionLocations();
} finally {
table.close();
}
final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preAssign(ObserverContext.createAndPrepare(CP_ENV, null),
firstRegion.getKey());
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testUnassign() throws Exception {
Map<HRegionInfo, ServerName> regions;
HTable table = new HTable(TEST_UTIL.getConfiguration(), TEST_TABLE);
try {
regions = table.getRegionLocations();
} finally {
table.close();
}
final Map.Entry<HRegionInfo, ServerName> firstRegion = regions.entrySet().iterator().next();
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preUnassign(ObserverContext.createAndPrepare(CP_ENV, null),
firstRegion.getKey(), false);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testBalance() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preBalance(ObserverContext.createAndPrepare(CP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testBalanceSwitch() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preBalanceSwitch(ObserverContext.createAndPrepare(CP_ENV, null), true);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testShutdown() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preShutdown(ObserverContext.createAndPrepare(CP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testStopMaster() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preStopMaster(ObserverContext.createAndPrepare(CP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
}
private void verifyWrite(PrivilegedExceptionAction action) throws Exception {
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_RW);
verifyDenied(action, USER_NONE, USER_CREATE, USER_RO);
}
@Test
public void testSplit() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preSplit(ObserverContext.createAndPrepare(RCP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testSplitWithSplitRow() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preSplit(
ObserverContext.createAndPrepare(RCP_ENV, null),
Bytes.toBytes("row2"));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testFlush() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preFlush(ObserverContext.createAndPrepare(RCP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testCompact() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preCompact(ObserverContext.createAndPrepare(RCP_ENV, null), null, null);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testPreCompactSelection() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preCompactSelection(ObserverContext.createAndPrepare(RCP_ENV, null), null, null);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE);
}
private void verifyRead(PrivilegedExceptionAction action) throws Exception {
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_RW, USER_RO);
verifyDenied(action, USER_NONE, USER_CREATE);
}
private void verifyReadWrite(PrivilegedExceptionAction action) throws Exception {
verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_RW);
verifyDenied(action, USER_NONE, USER_CREATE, USER_RO);
}
@Test
public void testRead() throws Exception {
// get action
PrivilegedExceptionAction getAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Get g = new Get(Bytes.toBytes("random_row"));
g.addFamily(TEST_FAMILY);
HTable t = new HTable(conf, TEST_TABLE);
try {
t.get(g);
} finally {
t.close();
}
return null;
}
};
verifyRead(getAction);
// action for scanning
PrivilegedExceptionAction scanAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Scan s = new Scan();
s.addFamily(TEST_FAMILY);
HTable table = new HTable(conf, TEST_TABLE);
try {
ResultScanner scanner = table.getScanner(s);
try {
for (Result r = scanner.next(); r != null; r = scanner.next()) {
// do nothing
}
} catch (IOException e) {
} finally {
scanner.close();
}
} finally {
table.close();
}
return null;
}
};
verifyRead(scanAction);
}
@Test
// test put, delete, increment
public void testWrite() throws Exception {
// put action
PrivilegedExceptionAction putAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("random_row"));
p.add(TEST_FAMILY, Bytes.toBytes("Qualifier"), Bytes.toBytes(1));
HTable t = new HTable(conf, TEST_TABLE);
try {
t.put(p);
} finally {
t.close();
}
return null;
}
};
verifyWrite(putAction);
// delete action
PrivilegedExceptionAction deleteAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteFamily(TEST_FAMILY);
HTable t = new HTable(conf, TEST_TABLE);
try {
t.delete(d);
} finally {
t.close();
}
return null;
}
};
verifyWrite(deleteAction);
// increment action
PrivilegedExceptionAction incrementAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Increment inc = new Increment(Bytes.toBytes("random_row"));
inc.addColumn(TEST_FAMILY, Bytes.toBytes("Qualifier"), 1);
HTable t = new HTable(conf, TEST_TABLE);
try {
t.increment(inc);
} finally {
t.close();
}
return null;
}
};
verifyWrite(incrementAction);
}
@Test
public void testReadWrite() throws Exception {
// action for checkAndDelete
PrivilegedExceptionAction checkAndDeleteAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteFamily(TEST_FAMILY);
HTable t = new HTable(conf, TEST_TABLE);
try {
t.checkAndDelete(Bytes.toBytes("random_row"), TEST_FAMILY, Bytes.toBytes("q"),
Bytes.toBytes("test_value"), d);
} finally {
t.close();
}
return null;
}
};
verifyReadWrite(checkAndDeleteAction);
// action for checkAndPut()
PrivilegedExceptionAction checkAndPut = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("random_row"));
p.add(TEST_FAMILY, Bytes.toBytes("Qualifier"), Bytes.toBytes(1));
HTable t = new HTable(conf, TEST_TABLE);
try {
t.checkAndPut(Bytes.toBytes("random_row"), TEST_FAMILY, Bytes.toBytes("q"),
Bytes.toBytes("test_value"), p);
} finally {
t.close();
}
return null;
}
};
verifyReadWrite(checkAndPut);
}
@Test
public void testBulkLoad() throws Exception {
FileSystem fs = TEST_UTIL.getTestFileSystem();
final Path dir = TEST_UTIL.getDataTestDir("testBulkLoad");
fs.mkdirs(dir);
//need to make it globally writable
//so users creating HFiles have write permissions
fs.setPermission(dir, FsPermission.valueOf("-rwxrwxrwx"));
PrivilegedExceptionAction bulkLoadAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
int numRows = 3;
//Making the assumption that the test table won't split between the range
byte[][][] hfileRanges = {{{(byte)0}, {(byte)9}}};
Path bulkLoadBasePath = new Path(dir, new Path(User.getCurrent().getName()));
new BulkLoadHelper(bulkLoadBasePath)
.bulkLoadHFile(TEST_TABLE, TEST_FAMILY, Bytes.toBytes("q"), hfileRanges, numRows);
return null;
}
};
verifyWrite(bulkLoadAction);
// Reinit after the bulk upload
TEST_UTIL.getHBaseAdmin().disableTable(TEST_TABLE);
TEST_UTIL.getHBaseAdmin().enableTable(TEST_TABLE);
}
public class BulkLoadHelper {
private final FileSystem fs;
private final Path loadPath;
private final Configuration conf;
public BulkLoadHelper(Path loadPath) throws IOException {
fs = TEST_UTIL.getTestFileSystem();
conf = TEST_UTIL.getConfiguration();
loadPath = loadPath.makeQualified(fs);
this.loadPath = loadPath;
}
private void createHFile(Path path,
byte[] family, byte[] qualifier,
byte[] startKey, byte[] endKey, int numRows) throws IOException {
HFile.Writer writer = null;
long now = System.currentTimeMillis();
try {
writer = HFile.getWriterFactory(conf, new CacheConfig(conf))
.withPath(fs, path)
.withComparator(KeyValue.KEY_COMPARATOR)
.create();
// subtract 2 since numRows doesn't include boundary keys
for (byte[] key : Bytes.iterateOnSplits(startKey, endKey, true, numRows-2)) {
KeyValue kv = new KeyValue(key, family, qualifier, now, key);
writer.append(kv);
}
} finally {
if(writer != null)
writer.close();
}
}
private void bulkLoadHFile(
byte[] tableName,
byte[] family,
byte[] qualifier,
byte[][][] hfileRanges,
int numRowsPerRange) throws Exception {
Path familyDir = new Path(loadPath, Bytes.toString(family));
fs.mkdirs(familyDir);
int hfileIdx = 0;
for (byte[][] range : hfileRanges) {
byte[] from = range[0];
byte[] to = range[1];
createHFile(new Path(familyDir, "hfile_"+(hfileIdx++)),
family, qualifier, from, to, numRowsPerRange);
}
//set global read so RegionServer can move it
setPermission(loadPath, FsPermission.valueOf("-rwxrwxrwx"));
HTable table = new HTable(conf, tableName);
try {
TEST_UTIL.waitTableAvailable(tableName, 30000);
LoadIncrementalHFiles loader = new LoadIncrementalHFiles(conf);
loader.doBulkLoad(loadPath, table);
} finally {
table.close();
}
}
public void setPermission(Path dir, FsPermission perm) throws IOException {
if(!fs.getFileStatus(dir).isDir()) {
fs.setPermission(dir,perm);
}
else {
for(FileStatus el : fs.listStatus(dir)) {
fs.setPermission(el.getPath(), perm);
setPermission(el.getPath() , perm);
}
}
}
}
@Test
public void testAppend() throws Exception {
PrivilegedExceptionAction appendAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
byte[] row = Bytes.toBytes("random_row");
byte[] qualifier = Bytes.toBytes("q");
Put put = new Put(row);
put.add(TEST_FAMILY, qualifier, Bytes.toBytes(1));
Append append = new Append(row);
append.add(TEST_FAMILY, qualifier, Bytes.toBytes(2));
HTable t = new HTable(conf, TEST_TABLE);
try {
t.put(put);
t.append(append);
} finally {
t.close();
}
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);
try {
BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, USER_RO.getShortName(), TEST_TABLE,
TEST_FAMILY, null, Action.READ);
} finally {
acl.close();
}
return null;
}
};
PrivilegedExceptionAction revokeAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.revoke(protocol, USER_RO.getShortName(), TEST_TABLE,
TEST_FAMILY, null, Action.READ);
} finally {
acl.close();
}
return null;
}
};
PrivilegedExceptionAction getPermissionsAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.getUserPermissions(protocol, TEST_TABLE);
} finally {
acl.close();
}
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");
final byte[] qualifier = Bytes.toBytes("q");
// create table
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
if (admin.tableExists(tableName)) {
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
HTableDescriptor htd = new HTableDescriptor(tableName);
htd.addFamily(new HColumnDescriptor(family1));
htd.addFamily(new HColumnDescriptor(family2));
admin.createTable(htd);
// create temp users
User tblUser = User
.createUserForTesting(TEST_UTIL.getConfiguration(), "tbluser", new String[0]);
User gblUser = User
.createUserForTesting(TEST_UTIL.getConfiguration(), "gbluser", new String[0]);
// prepare actions:
PrivilegedExceptionAction putActionAll = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("a"));
p.add(family1, qualifier, Bytes.toBytes("v1"));
p.add(family2, qualifier, Bytes.toBytes("v2"));
HTable t = new HTable(conf, tableName);
try {
t.put(p);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction putAction1 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("a"));
p.add(family1, qualifier, Bytes.toBytes("v1"));
HTable t = new HTable(conf, tableName);
try {
t.put(p);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction putAction2 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("a"));
p.add(family2, qualifier, Bytes.toBytes("v2"));
HTable t = new HTable(conf, tableName);
try {
t.put(p);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction getActionAll = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Get g = new Get(Bytes.toBytes("random_row"));
g.addFamily(family1);
g.addFamily(family2);
HTable t = new HTable(conf, tableName);
try {
t.get(g);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction getAction1 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Get g = new Get(Bytes.toBytes("random_row"));
g.addFamily(family1);
HTable t = new HTable(conf, tableName);
try {
t.get(g);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction getAction2 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Get g = new Get(Bytes.toBytes("random_row"));
g.addFamily(family2);
HTable t = new HTable(conf, tableName);
try {
t.get(g);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction deleteActionAll = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteFamily(family1);
d.deleteFamily(family2);
HTable t = new HTable(conf, tableName);
try {
t.delete(d);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction deleteAction1 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteFamily(family1);
HTable t = new HTable(conf, tableName);
try {
t.delete(d);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction deleteAction2 = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteFamily(family2);
HTable t = new HTable(conf, tableName);
try {
t.delete(d);
} finally {
t.close();
}
return null;
}
};
// initial check:
verifyDenied(tblUser, getActionAll, getAction1, getAction2);
verifyDenied(tblUser, putActionAll, putAction1, putAction2);
verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
verifyDenied(gblUser, getActionAll, getAction1, getAction2);
verifyDenied(gblUser, putActionAll, putAction1, putAction2);
verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// grant table read permission
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, tblUser.getShortName(),
tableName, null, null, Permission.Action.READ);
ProtobufUtil.grant(protocol, gblUser.getShortName(),
null, null, null, Permission.Action.READ);
} finally {
acl.close();
}
Thread.sleep(100);
// check
verifyAllowed(tblUser, getActionAll, getAction1, getAction2);
verifyDenied(tblUser, putActionAll, putAction1, putAction2);
verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
verifyAllowed(gblUser, getActionAll, getAction1, getAction2);
verifyDenied(gblUser, putActionAll, putAction1, putAction2);
verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// grant table write permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, tblUser.getShortName(),
tableName, null, null, Permission.Action.WRITE);
ProtobufUtil.grant(protocol, gblUser.getShortName(),
null, null, null, Permission.Action.WRITE);
} finally {
acl.close();
}
Thread.sleep(100);
verifyDenied(tblUser, getActionAll, getAction1, getAction2);
verifyAllowed(tblUser, putActionAll, putAction1, putAction2);
verifyAllowed(tblUser, deleteActionAll, deleteAction1, deleteAction2);
verifyDenied(gblUser, getActionAll, getAction1, getAction2);
verifyAllowed(gblUser, putActionAll, putAction1, putAction2);
verifyAllowed(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// revoke table permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, tblUser.getShortName(), tableName, null, null,
Permission.Action.READ, Permission.Action.WRITE);
ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, null, null);
ProtobufUtil.revoke(protocol, gblUser.getShortName(), null, null, null);
} finally {
acl.close();
}
Thread.sleep(100);
verifyDenied(tblUser, getActionAll, getAction1, getAction2);
verifyDenied(tblUser, putActionAll, putAction1, putAction2);
verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
verifyDenied(gblUser, getActionAll, getAction1, getAction2);
verifyDenied(gblUser, putActionAll, putAction1, putAction2);
verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// grant column family read permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, tblUser.getShortName(),
tableName, family1, null, Permission.Action.READ);
ProtobufUtil.grant(protocol, gblUser.getShortName(),
null, null, null, Permission.Action.READ);
} finally {
acl.close();
}
Thread.sleep(100);
// Access should be denied for family2
verifyAllowed(tblUser, getActionAll, getAction1);
verifyDenied(tblUser, getAction2);
verifyDenied(tblUser, putActionAll, putAction1, putAction2);
verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
verifyAllowed(gblUser, getActionAll, getAction1, getAction2);
verifyDenied(gblUser, putActionAll, putAction1, putAction2);
verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// grant column family write permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, tblUser.getShortName(),
tableName, family2, null, Permission.Action.WRITE);
ProtobufUtil.grant(protocol, gblUser.getShortName(),
null, null, null, Permission.Action.WRITE);
} finally {
acl.close();
}
Thread.sleep(100);
// READ from family1, WRITE to family2 are allowed
verifyAllowed(tblUser, getActionAll, getAction1);
verifyAllowed(tblUser, putAction2, deleteAction2);
verifyDenied(tblUser, getAction2);
verifyDenied(tblUser, putActionAll, putAction1);
verifyDenied(tblUser, deleteActionAll, deleteAction1);
verifyDenied(gblUser, getActionAll, getAction1, getAction2);
verifyAllowed(gblUser, putActionAll, putAction1, putAction2);
verifyAllowed(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// revoke column family permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, family2, null);
ProtobufUtil.revoke(protocol, gblUser.getShortName(), null, null, null);
} finally {
acl.close();
}
Thread.sleep(100);
// Revoke on family2 should not have impact on family1 permissions
verifyAllowed(tblUser, getActionAll, getAction1);
verifyDenied(tblUser, getAction2);
verifyDenied(tblUser, putActionAll, putAction1, putAction2);
verifyDenied(tblUser, deleteActionAll, deleteAction1, deleteAction2);
// Should not have access as global permissions are completely revoked
verifyDenied(gblUser, getActionAll, getAction1, getAction2);
verifyDenied(gblUser, putActionAll, putAction1, putAction2);
verifyDenied(gblUser, deleteActionAll, deleteAction1, deleteAction2);
// delete table
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
private boolean hasFoundUserPermission(UserPermission userPermission, List<UserPermission> perms) {
return perms.contains(userPermission);
}
@Test
public void testPostGrantRevokeAtQualifierLevel() throws Exception {
final byte[] tableName = Bytes.toBytes("testGrantRevokeAtQualifierLevel");
final byte[] family1 = Bytes.toBytes("f1");
final byte[] family2 = Bytes.toBytes("f2");
final byte[] qualifier = Bytes.toBytes("q");
// create table
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
if (admin.tableExists(tableName)) {
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
HTableDescriptor htd = new HTableDescriptor(tableName);
htd.addFamily(new HColumnDescriptor(family1));
htd.addFamily(new HColumnDescriptor(family2));
admin.createTable(htd);
// create temp users
User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]);
PrivilegedExceptionAction getQualifierAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Get g = new Get(Bytes.toBytes("random_row"));
g.addColumn(family1, qualifier);
HTable t = new HTable(conf, tableName);
try {
t.get(g);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction putQualifierAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Put p = new Put(Bytes.toBytes("random_row"));
p.add(family1, qualifier, Bytes.toBytes("v1"));
HTable t = new HTable(conf, tableName);
try {
t.put(p);
} finally {
t.close();
}
return null;
}
};
PrivilegedExceptionAction deleteQualifierAction = new PrivilegedExceptionAction() {
public Object run() throws Exception {
Delete d = new Delete(Bytes.toBytes("random_row"));
d.deleteColumn(family1, qualifier);
// d.deleteFamily(family1);
HTable t = new HTable(conf, tableName);
try {
t.delete(d);
} finally {
t.close();
}
return null;
}
};
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.revoke(protocol, user.getShortName(), tableName, family1, null);
} finally {
acl.close();
}
Thread.sleep(100);
verifyDenied(user, getQualifierAction);
verifyDenied(user, putQualifierAction);
verifyDenied(user, deleteQualifierAction);
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, user.getShortName(),
tableName, family1, qualifier, Permission.Action.READ);
} finally {
acl.close();
}
Thread.sleep(100);
verifyAllowed(user, getQualifierAction);
verifyDenied(user, putQualifierAction);
verifyDenied(user, deleteQualifierAction);
// only grant write permission
// TODO: comment this portion after HBASE-3583
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, user.getShortName(),
tableName, family1, qualifier, Permission.Action.WRITE);
} finally {
acl.close();
}
Thread.sleep(100);
verifyDenied(user, getQualifierAction);
verifyAllowed(user, putQualifierAction);
verifyAllowed(user, deleteQualifierAction);
// grant both read and write permission.
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, user.getShortName(),
tableName, family1, qualifier,
Permission.Action.READ, Permission.Action.WRITE);
} finally {
acl.close();
}
Thread.sleep(100);
verifyAllowed(user, getQualifierAction);
verifyAllowed(user, putQualifierAction);
verifyAllowed(user, deleteQualifierAction);
// revoke family level permission won't impact column level.
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.revoke(protocol, user.getShortName(),
tableName, family1, qualifier);
} finally {
acl.close();
}
Thread.sleep(100);
verifyDenied(user, getQualifierAction);
verifyDenied(user, putQualifierAction);
verifyDenied(user, deleteQualifierAction);
// delete table
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
@Test
public void testPermissionList() throws Exception {
final byte[] tableName = Bytes.toBytes("testPermissionList");
final byte[] family1 = Bytes.toBytes("f1");
final byte[] family2 = Bytes.toBytes("f2");
final byte[] qualifier = Bytes.toBytes("q");
// create table
HBaseAdmin admin = TEST_UTIL.getHBaseAdmin();
if (admin.tableExists(tableName)) {
admin.disableTable(tableName);
admin.deleteTable(tableName);
}
HTableDescriptor htd = new HTableDescriptor(tableName);
htd.addFamily(new HColumnDescriptor(family1));
htd.addFamily(new HColumnDescriptor(family2));
htd.setOwner(USER_OWNER);
admin.createTable(htd);
List<UserPermission> perms;
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
perms = ProtobufUtil.getUserPermissions(protocol, tableName);
} finally {
acl.close();
}
UserPermission ownerperm = new UserPermission(
Bytes.toBytes(USER_OWNER.getName()), tableName, null, Action.values());
assertTrue("Owner should have all permissions on table",
hasFoundUserPermission(ownerperm, perms));
User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]);
byte[] userName = Bytes.toBytes(user.getShortName());
UserPermission up = new UserPermission(userName,
tableName, family1, qualifier, Permission.Action.READ);
assertFalse("User should not be granted permission: " + up.toString(),
hasFoundUserPermission(up, perms));
// grant read permission
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, user.getShortName(),
tableName, family1, qualifier, Permission.Action.READ);
perms = ProtobufUtil.getUserPermissions(protocol, tableName);
} finally {
acl.close();
}
UserPermission upToVerify = new UserPermission(
userName, tableName, family1, qualifier, Permission.Action.READ);
assertTrue("User should be granted permission: " + upToVerify.toString(),
hasFoundUserPermission(upToVerify, perms));
upToVerify = new UserPermission(
userName, tableName, family1, qualifier, Permission.Action.WRITE);
assertFalse("User should not be granted permission: " + upToVerify.toString(),
hasFoundUserPermission(upToVerify, perms));
// grant read+write
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(protocol, user.getShortName(),
tableName, family1, qualifier,
Permission.Action.WRITE, Permission.Action.READ);
perms = ProtobufUtil.getUserPermissions(protocol, tableName);
} finally {
acl.close();
}
upToVerify = new UserPermission(userName, tableName, family1,
qualifier, Permission.Action.WRITE, Permission.Action.READ);
assertTrue("User should be granted permission: " + upToVerify.toString(),
hasFoundUserPermission(upToVerify, perms));
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.revoke(protocol, user.getShortName(), tableName, family1, qualifier,
Permission.Action.WRITE, Permission.Action.READ);
perms = ProtobufUtil.getUserPermissions(protocol, tableName);
} finally {
acl.close();
}
assertFalse("User should not be granted permission: " + upToVerify.toString(),
hasFoundUserPermission(upToVerify, perms));
// disable table before modification
admin.disableTable(tableName);
User newOwner = User.createUserForTesting(conf, "new_owner", new String[] {});
htd.setOwner(newOwner);
admin.modifyTable(tableName, htd);
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(tableName);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
perms = ProtobufUtil.getUserPermissions(protocol, tableName);
} finally {
acl.close();
}
UserPermission newOwnerperm = new UserPermission(
Bytes.toBytes(newOwner.getName()), tableName, null, Action.values());
assertTrue("New owner should have all permissions on table",
hasFoundUserPermission(newOwnerperm, perms));
// delete table
admin.deleteTable(tableName);
}
@Test
public void testGlobalPermissionList() throws Exception {
List<UserPermission> perms;
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
perms = ProtobufUtil.getUserPermissions(protocol, null);
} finally {
acl.close();
}
UserPermission adminPerm = new UserPermission(Bytes.toBytes(USER_ADMIN.getShortName()),
AccessControlLists.ACL_TABLE_NAME, null, null, Bytes.toBytes("ACRW"));
assertTrue("Only user admin has permission on table _acl_ per setup",
perms.size() == 1 && hasFoundUserPermission(adminPerm, perms));
}
/** global operations */
private void verifyGlobal(PrivilegedExceptionAction<?> action) throws Exception {
verifyAllowed(action, SUPERUSER);
verifyDenied(action, USER_CREATE, USER_RW, USER_NONE, USER_RO);
}
public void checkGlobalPerms(Permission.Action... actions) throws IOException {
Permission[] perms = new Permission[actions.length];
for (int i = 0; i < actions.length; i++) {
perms[i] = new Permission(actions[i]);
}
CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
for (Action a : actions) {
request.addPermission(AccessControlProtos.Permission.newBuilder()
.addAction(ProtobufUtil.toPermissionAction(a)).build());
}
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(channel);
try {
protocol.checkPermissions(null, request.build());
} catch (ServiceException se) {
ProtobufUtil.toIOException(se);
}
} finally {
acl.close();
}
}
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 {
CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
for (Permission p : perms) {
request.addPermission(ProtobufUtil.toPermission(p));
}
HTable acl = new HTable(conf, table);
try {
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
try {
protocol.checkPermissions(null, request.build());
} catch (ServiceException se) {
ProtobufUtil.toIOException(se);
}
} finally {
acl.close();
}
}
@Test
public void testCheckPermissions() throws Exception {
// --------------------------------------
// 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]);
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(channel);
ProtobufUtil.grant(protocol, userTable.getShortName(),
TEST_TABLE, null, null, Permission.Action.READ);
ProtobufUtil.grant(protocol, userColumn.getShortName(),
TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ);
ProtobufUtil.grant(protocol, userQualifier.getShortName(),
TEST_TABLE, TEST_FAMILY, TEST_Q1, Permission.Action.READ);
} finally {
acl.close();
}
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;
}
};
verifyAllowed(familyReadWrite, SUPERUSER, USER_OWNER, USER_RW);
verifyDenied(familyReadWrite, USER_NONE, USER_CREATE, USER_RO);
// --------------------------------------
// check for wrong table region
CheckPermissionsRequest checkRequest = CheckPermissionsRequest.newBuilder()
.addPermission(AccessControlProtos.Permission.newBuilder()
.setTable(ByteString.copyFrom(TEST_TABLE))
.addAction(AccessControlProtos.Permission.Action.CREATE)
).build();
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
try {
BlockingRpcChannel channel = acl.coprocessorService(new byte[0]);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(channel);
try {
// but ask for TablePermissions for TEST_TABLE
protocol.checkPermissions(null, checkRequest);
fail("this should have thrown CoprocessorException");
} catch (ServiceException ex) {
// expected
}
} finally {
acl.close();
}
}
@Test
public void testStopRegionServer() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preStopRegionServer(ObserverContext.createAndPrepare(RSCP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE);
}
@Test
public void testOpenRegion() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preOpen(ObserverContext.createAndPrepare(RCP_ENV, null));
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
}
@Test
public void testCloseRegion() throws Exception {
PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
public Object run() throws Exception {
ACCESS_CONTROLLER.preClose(ObserverContext.createAndPrepare(RCP_ENV, null), false);
return null;
}
};
verifyAllowed(action, SUPERUSER, USER_ADMIN);
verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER);
}
}