/* * 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.apache.hadoop.hbase.AuthUtil.toGroupEntry; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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.CoprocessorEnvironment; import org.apache.hadoop.hbase.HBaseIOException; 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.HRegionLocation; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.ProcedureInfo; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.Append; import org.apache.hadoop.hbase.client.Connection; import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Increment; import org.apache.hadoop.hbase.client.MasterSwitchType; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.RegionLocator; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.client.security.SecurityCapability; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.coprocessor.CoprocessorService; 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.coprocessor.protobuf.generated.PingProtos.CountRequest; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.CountResponse; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.HelloRequest; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.HelloResponse; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.IncrementCountRequest; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.IncrementCountResponse; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.NoopRequest; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.NoopResponse; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.PingRequest; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.PingResponse; import org.apache.hadoop.hbase.coprocessor.protobuf.generated.PingProtos.PingService; import org.apache.hadoop.hbase.exceptions.HBaseException; import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.io.hfile.HFileContext; import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; import org.apache.hadoop.hbase.master.locking.LockProcedure; import org.apache.hadoop.hbase.master.locking.LockProcedure.LockType; import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; import org.apache.hadoop.hbase.master.procedure.TableProcedureInterface; import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.procedure2.ProcedureYieldException; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 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.HRegionServer; import org.apache.hadoop.hbase.regionserver.Region; import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost; import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost; import org.apache.hadoop.hbase.regionserver.ScanType; import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; import org.apache.hadoop.hbase.security.Superusers; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.access.Permission.Action; import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProcedureProtos; import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos.ProcedureState; import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription; import org.apache.hadoop.hbase.testclassification.LargeTests; import org.apache.hadoop.hbase.testclassification.SecurityTests; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.JVMClusterUtil; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TestName; import com.google.protobuf.BlockingRpcChannel; import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import com.google.protobuf.Service; import com.google.protobuf.ServiceException; /** * Performs authorization checks for common operations, according to different * levels of authorized users. */ @Category({SecurityTests.class, LargeTests.class}) public class TestAccessController extends SecureTestUtil { private static final Log LOG = LogFactory.getLog(TestAccessController.class); static { Logger.getLogger(AccessController.class).setLevel(Level.TRACE); Logger.getLogger(AccessControlFilter.class).setLevel(Level.TRACE); Logger.getLogger(TableAuthManager.class).setLevel(Level.TRACE); } private static TableName TEST_TABLE = TableName.valueOf("testtable1"); private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); private static Configuration conf; /** The systemUserConnection created here is tied to the system user. In case, you are planning * to create AccessTestAction, DON'T use this systemUserConnection as the 'doAs' user * gets eclipsed by the system user. */ private static Connection systemUserConnection; // 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; // user with admin rights on the column family private static User USER_ADMIN_CF; private static final String GROUP_ADMIN = "group_admin"; private static final String GROUP_CREATE = "group_create"; private static final String GROUP_READ = "group_read"; private static final String GROUP_WRITE = "group_write"; private static User USER_GROUP_ADMIN; private static User USER_GROUP_CREATE; private static User USER_GROUP_READ; private static User USER_GROUP_WRITE; // TODO: convert this test to cover the full matrix in // https://hbase.apache.org/book/appendix_acl_matrix.html // creating all Scope x Permission combinations private static TableName TEST_TABLE2 = TableName.valueOf("testtable2"); private static byte[] TEST_FAMILY = Bytes.toBytes("f1"); private static byte[] TEST_QUALIFIER = Bytes.toBytes("q1"); private static byte[] TEST_ROW = Bytes.toBytes("r1"); private static MasterCoprocessorEnvironment CP_ENV; private static AccessController ACCESS_CONTROLLER; private static RegionServerCoprocessorEnvironment RSCP_ENV; private static RegionCoprocessorEnvironment RCP_ENV; @Rule public TestName name = new TestName(); @BeforeClass public static void setupBeforeClass() throws Exception { // setup configuration conf = TEST_UTIL.getConfiguration(); // Up the handlers; this test needs more than usual. conf.setInt(HConstants.REGION_SERVER_HIGH_PRIORITY_HANDLER_COUNT, 10); // Enable security enableSecurity(conf); // In this particular test case, we can't use SecureBulkLoadEndpoint because its doAs will fail // to move a file for a random user conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, AccessController.class.getName()); // Verify enableSecurity sets up what we require verifyConfiguration(conf); // Enable EXEC permission checking conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true); TEST_UTIL.startMiniCluster(); MasterCoprocessorHost cpHost = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterCoprocessorHost(); 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) .getRegionServerCoprocessorHost(); RSCP_ENV = rsHost.createEnvironment(AccessController.class, ACCESS_CONTROLLER, Coprocessor.PRIORITY_HIGHEST, 1, conf); // Wait for the ACL table to become available TEST_UTIL.waitUntilAllRegionsAssigned(AccessControlLists.ACL_TABLE_NAME); // 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]); USER_ADMIN_CF = User.createUserForTesting(conf, "col_family_admin", new String[0]); USER_GROUP_ADMIN = User.createUserForTesting(conf, "user_group_admin", new String[] { GROUP_ADMIN }); USER_GROUP_CREATE = User.createUserForTesting(conf, "user_group_create", new String[] { GROUP_CREATE }); USER_GROUP_READ = User.createUserForTesting(conf, "user_group_read", new String[] { GROUP_READ }); USER_GROUP_WRITE = User.createUserForTesting(conf, "user_group_write", new String[] { GROUP_WRITE }); systemUserConnection = TEST_UTIL.getConnection(); setUpTableAndUserPermissions(); } @AfterClass public static void tearDownAfterClass() throws Exception { cleanUp(); TEST_UTIL.shutdownMiniCluster(); int total = TableAuthManager.getTotalRefCount(); assertTrue("Unexpected reference count: " + total, total == 0); } private static void setUpTableAndUserPermissions() throws Exception { HTableDescriptor htd = new HTableDescriptor(TEST_TABLE); HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY); hcd.setMaxVersions(100); htd.addFamily(hcd); htd.setOwner(USER_OWNER); createTable(TEST_UTIL, htd, new byte[][] { Bytes.toBytes("s") }); Region 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); // Set up initial grants grantGlobal(TEST_UTIL, USER_ADMIN.getShortName(), Permission.Action.ADMIN, Permission.Action.CREATE, Permission.Action.READ, Permission.Action.WRITE); grantOnTable(TEST_UTIL, USER_RW.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ, Permission.Action.WRITE); // USER_CREATE is USER_RW plus CREATE permissions grantOnTable(TEST_UTIL, USER_CREATE.getShortName(), TEST_TABLE, null, null, Permission.Action.CREATE, Permission.Action.READ, Permission.Action.WRITE); grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ); grantOnTable(TEST_UTIL, USER_ADMIN_CF.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.ADMIN, Permission.Action.CREATE); grantGlobal(TEST_UTIL, toGroupEntry(GROUP_ADMIN), Permission.Action.ADMIN); grantGlobal(TEST_UTIL, toGroupEntry(GROUP_CREATE), Permission.Action.CREATE); grantGlobal(TEST_UTIL, toGroupEntry(GROUP_READ), Permission.Action.READ); grantGlobal(TEST_UTIL, toGroupEntry(GROUP_WRITE), Permission.Action.WRITE); assertEquals(5, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); try { assertEquals(5, AccessControlClient.getUserPermissions(systemUserConnection, TEST_TABLE.toString()).size()); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.getUserPermissions. ", e); } } private static void cleanUp() throws Exception { // Clean the _acl_ table try { deleteTable(TEST_UTIL, TEST_TABLE); } catch (TableNotFoundException ex) { // Test deleted the table, no problem LOG.info("Test deleted table " + TEST_TABLE); } // Verify all table/namespace permissions are erased assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); assertEquals( 0, AccessControlLists.getNamespacePermissions(conf, TEST_TABLE.getNamespaceAsString()).size()); } @Test (timeout=180000) public void testUnauthorizedShutdown() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); master.shutdown(); return null; } }; verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testUnauthorizedStopMaster() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { HMaster master = TEST_UTIL.getHBaseCluster().getMaster(); master.stopMaster(); return null; } }; verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testSecurityCapabilities() throws Exception { List<SecurityCapability> capabilities = TEST_UTIL.getConnection().getAdmin() .getSecurityCapabilities(); assertTrue("AUTHORIZATION capability is missing", capabilities.contains(SecurityCapability.AUTHORIZATION)); assertTrue("CELL_AUTHORIZATION capability is missing", capabilities.contains(SecurityCapability.CELL_AUTHORIZATION)); } @Test (timeout=180000) public void testTableCreate() throws Exception { AccessTestAction createTable = new AccessTestAction() { @Override public Object run() throws Exception { HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name.getMethodName())); 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, USER_GROUP_CREATE); // all others should be denied verifyDenied(createTable, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testTableModify() throws Exception { AccessTestAction modifyTable = new AccessTestAction() { @Override 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, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(modifyTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testTableDelete() throws Exception { AccessTestAction deleteTable = new AccessTestAction() { @Override 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, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(deleteTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testTableTruncate() throws Exception { AccessTestAction truncateTable = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER .preTruncateTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE); return null; } }; verifyAllowed(truncateTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(truncateTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testAddColumn() throws Exception { final HColumnDescriptor hcd = new HColumnDescriptor("fam_new"); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preAddColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE, hcd); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testModifyColumn() throws Exception { final HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY); hcd.setMaxVersions(10); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preModifyColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE, hcd); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_ADMIN_CF, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testDeleteColumn() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDeleteColumnFamily(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE, TEST_FAMILY); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_ADMIN_CF, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testTableDisable() throws Exception { AccessTestAction disableTable = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE); return null; } }; AccessTestAction disableAclTable = new AccessTestAction() { @Override 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, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(disableTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); // No user should be allowed to disable _acl_ table verifyDenied(disableAclTable, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testTableEnable() throws Exception { AccessTestAction enableTable = new AccessTestAction() { @Override 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, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(enableTable, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } public static class TestTableDDLProcedure extends Procedure<MasterProcedureEnv> implements TableProcedureInterface { private TableName tableName; public TestTableDDLProcedure() { } public TestTableDDLProcedure(final MasterProcedureEnv env, final TableName tableName) throws IOException { this.tableName = tableName; this.setTimeout(180000); // Timeout in 3 minutes this.setOwner(env.getRequestUser()); } @Override public TableName getTableName() { return tableName; } @Override public TableOperationType getTableOperationType() { return TableOperationType.EDIT; } @Override protected boolean abort(MasterProcedureEnv env) { return true; } @Override protected void serializeStateData(OutputStream stream) throws IOException { TestProcedureProtos.TestTableDDLStateData.Builder testTableDDLMsg = TestProcedureProtos.TestTableDDLStateData.newBuilder() .setTableName(tableName.getNameAsString()); testTableDDLMsg.build().writeDelimitedTo(stream); } @Override protected void deserializeStateData(InputStream stream) throws IOException { TestProcedureProtos.TestTableDDLStateData testTableDDLMsg = TestProcedureProtos.TestTableDDLStateData.parseDelimitedFrom(stream); tableName = TableName.valueOf(testTableDDLMsg.getTableName()); } @Override protected Procedure[] execute(MasterProcedureEnv env) throws ProcedureYieldException, InterruptedException { // Not letting the procedure to complete until timed out setState(ProcedureState.WAITING_TIMEOUT); return null; } @Override protected void rollback(MasterProcedureEnv env) throws IOException, InterruptedException { } } @Test public void testAbortProcedure() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final ProcedureExecutor<MasterProcedureEnv> procExec = TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); Procedure proc = new TestTableDDLProcedure(procExec.getEnvironment(), tableName); proc.setOwner(USER_OWNER); final long procId = procExec.submitProcedure(proc); AccessTestAction abortProcedureAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER .preAbortProcedure(ObserverContext.createAndPrepare(CP_ENV, null), procExec, procId); return null; } }; verifyAllowed(abortProcedureAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyAllowed(abortProcedureAction, USER_OWNER); verifyDenied( abortProcedureAction, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test public void testListProcedures() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final ProcedureExecutor<MasterProcedureEnv> procExec = TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); Procedure proc = new TestTableDDLProcedure(procExec.getEnvironment(), tableName); proc.setOwner(USER_OWNER); final long procId = procExec.submitProcedure(proc); final List<ProcedureInfo> procInfoList = procExec.listProcedures(); AccessTestAction listProceduresAction = new AccessTestAction() { @Override public Object run() throws Exception { List<ProcedureInfo> procInfoListClone = new ArrayList<>(procInfoList.size()); for(ProcedureInfo pi : procInfoList) { procInfoListClone.add(pi.clone()); } ACCESS_CONTROLLER .postListProcedures(ObserverContext.createAndPrepare(CP_ENV, null), procInfoListClone); return null; } }; verifyAllowed(listProceduresAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyAllowed(listProceduresAction, USER_OWNER); verifyIfNull( listProceduresAction, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testMove() throws Exception { List<HRegionLocation> regions; try (RegionLocator locator = systemUserConnection.getRegionLocator(TEST_TABLE)) { regions = locator.getAllRegionLocations(); } HRegionLocation location = regions.get(0); final HRegionInfo hri = location.getRegionInfo(); final ServerName server = location.getServerName(); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preMove(ObserverContext.createAndPrepare(CP_ENV, null), hri, server, server); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testAssign() throws Exception { List<HRegionLocation> regions; try (RegionLocator locator = systemUserConnection.getRegionLocator(TEST_TABLE)) { regions = locator.getAllRegionLocations(); } HRegionLocation location = regions.get(0); final HRegionInfo hri = location.getRegionInfo(); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preAssign(ObserverContext.createAndPrepare(CP_ENV, null), hri); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testUnassign() throws Exception { List<HRegionLocation> regions; try (RegionLocator locator = systemUserConnection.getRegionLocator(TEST_TABLE)) { regions = locator.getAllRegionLocations(); } HRegionLocation location = regions.get(0); final HRegionInfo hri = location.getRegionInfo(); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preUnassign(ObserverContext.createAndPrepare(CP_ENV, null), hri, false); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testRegionOffline() throws Exception { List<HRegionLocation> regions; try (RegionLocator locator = systemUserConnection.getRegionLocator(TEST_TABLE)) { regions = locator.getAllRegionLocations(); } HRegionLocation location = regions.get(0); final HRegionInfo hri = location.getRegionInfo(); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRegionOffline(ObserverContext.createAndPrepare(CP_ENV, null), hri); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testSetSplitOrMergeEnabled() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetSplitOrMergeEnabled(ObserverContext.createAndPrepare(CP_ENV, null), true, MasterSwitchType.MERGE); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testBalance() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preBalance(ObserverContext.createAndPrepare(CP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testBalanceSwitch() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preBalanceSwitch(ObserverContext.createAndPrepare(CP_ENV, null), true); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testShutdown() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preShutdown(ObserverContext.createAndPrepare(CP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testStopMaster() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preStopMaster(ObserverContext.createAndPrepare(CP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } private void verifyWrite(AccessTestAction action) throws Exception { verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW, USER_GROUP_WRITE); verifyDenied(action, USER_NONE, USER_RO, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_CREATE); } @Test (timeout=180000) public void testSplitWithSplitRow() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); createTestTable(tableName); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSplitRegion( ObserverContext.createAndPrepare(CP_ENV, null), tableName, TEST_ROW); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testMergeRegions() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); createTestTable(tableName); try { final List<HRegion> regions = TEST_UTIL.getHBaseCluster().findRegionsForTable(tableName); assertTrue("not enough regions: " + regions.size(), regions.size() >= 2); AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preMerge(ObserverContext.createAndPrepare(RSCP_ENV, null), regions.get(0), regions.get(1)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } finally { deleteTable(TEST_UTIL, tableName); } } @Test (timeout=180000) public void testFlush() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preFlush(ObserverContext.createAndPrepare(RCP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testCompact() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preCompact(ObserverContext.createAndPrepare(RCP_ENV, null), null, null, ScanType.COMPACT_RETAIN_DELETES); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(action, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } private void verifyRead(AccessTestAction action) throws Exception { verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW, USER_RO, USER_GROUP_READ); verifyDenied(action, USER_NONE, USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_WRITE); } private void verifyReadWrite(AccessTestAction action) throws Exception { verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW); verifyDenied(action, USER_NONE, USER_RO, USER_GROUP_ADMIN, USER_GROUP_CREATE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testRead() throws Exception { // get action AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { Get g = new Get(TEST_ROW); g.addFamily(TEST_FAMILY); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { t.get(g); } return null; } }; verifyRead(getAction); // action for scanning AccessTestAction scanAction = new AccessTestAction() { @Override public Object run() throws Exception { Scan s = new Scan(); s.addFamily(TEST_FAMILY); try(Connection conn = ConnectionFactory.createConnection(conf); Table table = conn.getTable(TEST_TABLE)) { ResultScanner scanner = table.getScanner(s); try { for (Result r = scanner.next(); r != null; r = scanner.next()) { // do nothing } } finally { scanner.close(); } } return null; } }; verifyRead(scanAction); } @Test (timeout=180000) // test put, delete, increment public void testWrite() throws Exception { // put action AccessTestAction putAction = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(TEST_ROW); p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(1)); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { t.put(p); } return null; } }; verifyWrite(putAction); // delete action AccessTestAction deleteAction = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addFamily(TEST_FAMILY); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { t.delete(d); } return null; } }; verifyWrite(deleteAction); // increment action AccessTestAction incrementAction = new AccessTestAction() { @Override public Object run() throws Exception { Increment inc = new Increment(TEST_ROW); inc.addColumn(TEST_FAMILY, TEST_QUALIFIER, 1); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { t.increment(inc); } return null; } }; verifyWrite(incrementAction); } @Test (timeout=180000) public void testReadWrite() throws Exception { // action for checkAndDelete AccessTestAction checkAndDeleteAction = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addFamily(TEST_FAMILY); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { t.checkAndDelete(TEST_ROW, TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("test_value"), d); } return null; } }; verifyReadWrite(checkAndDeleteAction); // action for checkAndPut() AccessTestAction checkAndPut = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(TEST_ROW); p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(1)); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { t.checkAndPut(TEST_ROW, TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes("test_value"), p); } return null; } }; verifyReadWrite(checkAndPut); } @Test (timeout=180000) public void testBulkLoad() throws Exception { try { FileSystem fs = TEST_UTIL.getTestFileSystem(); final Path dir = TEST_UTIL.getDataTestDirOnTestFS("testBulkLoad"); fs.mkdirs(dir); // need to make it globally writable // so users creating HFiles have write permissions fs.setPermission(dir, FsPermission.valueOf("-rwxrwxrwx")); AccessTestAction bulkLoadAction = new AccessTestAction() { @Override 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, TEST_QUALIFIER, hfileRanges, numRows); return null; } }; // User performing bulk loads must have privilege to read table metadata // (ADMIN or CREATE) verifyAllowed(bulkLoadAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_GROUP_CREATE); verifyDenied(bulkLoadAction, USER_RW, USER_NONE, USER_RO, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_ADMIN); } finally { // Reinit after the bulk upload TEST_UTIL.getAdmin().disableTable(TEST_TABLE); TEST_UTIL.getAdmin().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 { HFileContext context = new HFileContextBuilder().build(); writer = HFile.getWriterFactory(conf, new CacheConfig(conf)) .withPath(fs, path) .withFileContext(context) .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( TableName 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")); try (Connection conn = ConnectionFactory.createConnection(conf); Admin admin = conn.getAdmin(); RegionLocator locator = conn.getRegionLocator(tableName); Table table = conn.getTable(tableName)) { TEST_UTIL.waitUntilAllRegionsAssigned(tableName); LoadIncrementalHFiles loader = new LoadIncrementalHFiles(conf); loader.doBulkLoad(loadPath, admin, table, locator); } } public void setPermission(Path dir, FsPermission perm) throws IOException { if(!fs.getFileStatus(dir).isDirectory()) { fs.setPermission(dir,perm); } else { for(FileStatus el : fs.listStatus(dir)) { fs.setPermission(el.getPath(), perm); setPermission(el.getPath() , perm); } } } } @Test (timeout=180000) public void testAppend() throws Exception { AccessTestAction appendAction = new AccessTestAction() { @Override public Object run() throws Exception { byte[] row = TEST_ROW; byte[] qualifier = TEST_QUALIFIER; Put put = new Put(row); put.addColumn(TEST_FAMILY, qualifier, Bytes.toBytes(1)); Append append = new Append(row); append.add(TEST_FAMILY, qualifier, Bytes.toBytes(2)); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { t.put(put); t.append(append); } return null; } }; verifyAllowed(appendAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_RW, USER_GROUP_WRITE); verifyDenied(appendAction, USER_RO, USER_NONE, USER_GROUP_CREATE, USER_GROUP_READ, USER_GROUP_ADMIN); } @Test (timeout=180000) public void testGrantRevoke() throws Exception { AccessTestAction grantAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) { BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); AccessControlUtil.grant(null, protocol, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null, false, Action.READ); } return null; } }; AccessTestAction revokeAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME)) { BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); AccessControlUtil.revoke(null, protocol, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null, Action.READ); } return null; } }; AccessTestAction getTablePermissionsAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME)){ BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); AccessControlUtil.getUserPermissions(null, protocol, TEST_TABLE); } return null; } }; AccessTestAction getGlobalPermissionsAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table acl = conn.getTable(AccessControlLists.ACL_TABLE_NAME);) { BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); AccessControlUtil.getUserPermissions(null, protocol); } return null; } }; verifyAllowed(grantAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(grantAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); try { verifyAllowed(revokeAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(revokeAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(getTablePermissionsAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(getTablePermissionsAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(getGlobalPermissionsAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(getGlobalPermissionsAction, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } finally { // Cleanup, Grant the revoked permission back to the user grantOnTable(TEST_UTIL, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ); } } @Test (timeout=180000) public void testPostGrantRevoke() throws Exception { final TableName tableName = TableName.valueOf("TempTable"); final byte[] family1 = Bytes.toBytes("f1"); final byte[] family2 = Bytes.toBytes("f2"); final byte[] qualifier = Bytes.toBytes("q"); // create table Admin admin = TEST_UTIL.getAdmin(); if (admin.tableExists(tableName)) { deleteTable(TEST_UTIL, tableName); } HTableDescriptor htd = new HTableDescriptor(tableName); htd.addFamily(new HColumnDescriptor(family1)); htd.addFamily(new HColumnDescriptor(family2)); createTable(TEST_UTIL, htd); try { // 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: AccessTestAction putActionAll = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(Bytes.toBytes("a")); p.addColumn(family1, qualifier, Bytes.toBytes("v1")); p.addColumn(family2, qualifier, Bytes.toBytes("v2")); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName);) { t.put(p); } return null; } }; AccessTestAction putAction1 = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(Bytes.toBytes("a")); p.addColumn(family1, qualifier, Bytes.toBytes("v1")); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.put(p); } return null; } }; AccessTestAction putAction2 = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(Bytes.toBytes("a")); p.addColumn(family2, qualifier, Bytes.toBytes("v2")); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName);) { t.put(p); } return null; } }; AccessTestAction getActionAll = new AccessTestAction() { @Override public Object run() throws Exception { Get g = new Get(TEST_ROW); g.addFamily(family1); g.addFamily(family2); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName);) { t.get(g); } return null; } }; AccessTestAction getAction1 = new AccessTestAction() { @Override public Object run() throws Exception { Get g = new Get(TEST_ROW); g.addFamily(family1); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.get(g); } return null; } }; AccessTestAction getAction2 = new AccessTestAction() { @Override public Object run() throws Exception { Get g = new Get(TEST_ROW); g.addFamily(family2); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.get(g); } return null; } }; AccessTestAction deleteActionAll = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addFamily(family1); d.addFamily(family2); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.delete(d); } return null; } }; AccessTestAction deleteAction1 = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addFamily(family1); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.delete(d); } return null; } }; AccessTestAction deleteAction2 = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addFamily(family2); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.delete(d); } 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 grantGlobal(TEST_UTIL, gblUser.getShortName(), Permission.Action.READ); grantOnTable(TEST_UTIL, tblUser.getShortName(), tableName, null, null, Permission.Action.READ); // 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 while revoking read permissions grantGlobal(TEST_UTIL, gblUser.getShortName(), Permission.Action.WRITE); grantOnTable(TEST_UTIL, tblUser.getShortName(), tableName, null, null, Permission.Action.WRITE); 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 permissions revokeGlobal(TEST_UTIL, gblUser.getShortName()); revokeFromTable(TEST_UTIL, tblUser.getShortName(), tableName, null, null); 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 grantGlobal(TEST_UTIL, gblUser.getShortName(), Permission.Action.READ); grantOnTable(TEST_UTIL, tblUser.getShortName(), tableName, family1, null, Permission.Action.READ); // 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 grantGlobal(TEST_UTIL, gblUser.getShortName(), Permission.Action.WRITE); grantOnTable(TEST_UTIL, tblUser.getShortName(), tableName, family2, null, Permission.Action.WRITE); // 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 revokeGlobal(TEST_UTIL, gblUser.getShortName()); revokeFromTable(TEST_UTIL, tblUser.getShortName(), tableName, family2, null); // 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); } finally { // delete table deleteTable(TEST_UTIL, tableName); } } private boolean hasFoundUserPermission(List<UserPermission> userPermissions, List<UserPermission> perms) { return perms.containsAll(userPermissions); } private boolean hasFoundUserPermission(UserPermission userPermission, List<UserPermission> perms) { return perms.contains(userPermission); } @Test (timeout=180000) public void testPostGrantRevokeAtQualifierLevel() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final byte[] family1 = Bytes.toBytes("f1"); final byte[] family2 = Bytes.toBytes("f2"); final byte[] qualifier = Bytes.toBytes("q"); // create table Admin admin = TEST_UTIL.getAdmin(); if (admin.tableExists(tableName)) { deleteTable(TEST_UTIL, tableName); } HTableDescriptor htd = new HTableDescriptor(tableName); htd.addFamily(new HColumnDescriptor(family1)); htd.addFamily(new HColumnDescriptor(family2)); createTable(TEST_UTIL, htd); try { // create temp users User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "user", new String[0]); AccessTestAction getQualifierAction = new AccessTestAction() { @Override public Object run() throws Exception { Get g = new Get(TEST_ROW); g.addColumn(family1, qualifier); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.get(g); } return null; } }; AccessTestAction putQualifierAction = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(TEST_ROW); p.addColumn(family1, qualifier, Bytes.toBytes("v1")); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.put(p); } return null; } }; AccessTestAction deleteQualifierAction = new AccessTestAction() { @Override public Object run() throws Exception { Delete d = new Delete(TEST_ROW); d.addColumn(family1, qualifier); // d.deleteFamily(family1); try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(tableName)) { t.delete(d); } return null; } }; revokeFromTable(TEST_UTIL, user.getShortName(), tableName, family1, null); verifyDenied(user, getQualifierAction); verifyDenied(user, putQualifierAction); verifyDenied(user, deleteQualifierAction); grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.READ); verifyAllowed(user, getQualifierAction); verifyDenied(user, putQualifierAction); verifyDenied(user, deleteQualifierAction); // only grant write permission // TODO: comment this portion after HBASE-3583 grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.WRITE); verifyDenied(user, getQualifierAction); verifyAllowed(user, putQualifierAction); verifyAllowed(user, deleteQualifierAction); // grant both read and write permission grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.READ, Permission.Action.WRITE); verifyAllowed(user, getQualifierAction); verifyAllowed(user, putQualifierAction); verifyAllowed(user, deleteQualifierAction); // revoke family level permission won't impact column level revokeFromTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier); verifyDenied(user, getQualifierAction); verifyDenied(user, putQualifierAction); verifyDenied(user, deleteQualifierAction); } finally { // delete table deleteTable(TEST_UTIL, tableName); } } @Test (timeout=180000) public void testPermissionList() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); final byte[] family1 = Bytes.toBytes("f1"); final byte[] family2 = Bytes.toBytes("f2"); final byte[] qualifier = Bytes.toBytes("q"); // create table Admin admin = TEST_UTIL.getAdmin(); if (admin.tableExists(tableName)) { deleteTable(TEST_UTIL, tableName); } HTableDescriptor htd = new HTableDescriptor(tableName); htd.addFamily(new HColumnDescriptor(family1)); htd.addFamily(new HColumnDescriptor(family2)); htd.setOwner(USER_OWNER); createTable(TEST_UTIL, htd); try { List<UserPermission> perms; Table acl = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, 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 grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.READ); acl = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, 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 grantOnTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.WRITE, Permission.Action.READ); acl = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, 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)); // revoke revokeFromTable(TEST_UTIL, user.getShortName(), tableName, family1, qualifier, Permission.Action.WRITE, Permission.Action.READ); acl = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, 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 = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(tableName.getName()); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, 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)); } finally { // delete table deleteTable(TEST_UTIL, tableName); } } @Test (timeout=180000) public void testGlobalPermissionList() throws Exception { List<UserPermission> perms; Table acl = systemUserConnection.getTable(AccessControlLists.ACL_TABLE_NAME); try { BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW); AccessControlService.BlockingInterface protocol = AccessControlService.newBlockingStub(service); perms = AccessControlUtil.getUserPermissions(null, protocol); } finally { acl.close(); } List<String> superUsers = Superusers.getSuperUsers(); List<UserPermission> adminPerms = new ArrayList<>(superUsers.size() + 1); adminPerms.add(new UserPermission(Bytes.toBytes(USER_ADMIN.getShortName()), AccessControlLists.ACL_TABLE_NAME, null, null, Bytes.toBytes("ACRW"))); for(String user: superUsers) { adminPerms.add(new UserPermission(Bytes.toBytes(user), AccessControlLists.ACL_TABLE_NAME, null, null, Action.values())); } assertTrue("Only super users, global users and user admin has permission on table hbase:acl " + "per setup", perms.size() == 5 + superUsers.size() && hasFoundUserPermission(adminPerms, perms)); } /** global operations */ private void verifyGlobal(AccessTestAction action) throws Exception { verifyAllowed(action, SUPERUSER); verifyDenied(action, USER_CREATE, USER_RW, USER_NONE, USER_RO); } @Test (timeout=180000) public void testCheckPermissions() throws Exception { // -------------------------------------- // test global permissions AccessTestAction globalAdmin = new AccessTestAction() { @Override public Void run() throws Exception { checkGlobalPerms(TEST_UTIL, Permission.Action.ADMIN); return null; } }; // verify that only superuser can admin verifyGlobal(globalAdmin); // -------------------------------------- // test multiple permissions AccessTestAction globalReadWrite = new AccessTestAction() { @Override public Void run() throws Exception { checkGlobalPerms(TEST_UTIL, 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]); grantOnTable(TEST_UTIL, userTable.getShortName(), TEST_TABLE, null, null, Permission.Action.READ); grantOnTable(TEST_UTIL, userColumn.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ); grantOnTable(TEST_UTIL, userQualifier.getShortName(), TEST_TABLE, TEST_FAMILY, TEST_Q1, Permission.Action.READ); try { AccessTestAction tableRead = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, TEST_TABLE, null, null, Permission.Action.READ); return null; } }; AccessTestAction columnRead = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ); return null; } }; AccessTestAction qualifierRead = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, TEST_TABLE, TEST_FAMILY, TEST_Q1, Permission.Action.READ); return null; } }; AccessTestAction multiQualifierRead = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, 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; } }; AccessTestAction globalAndTableRead = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, TEST_TABLE, new Permission[] { new Permission(Permission.Action.READ), new TablePermission(TEST_TABLE, null, (byte[]) null, Permission.Action.READ), }); return null; } }; AccessTestAction noCheck = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, 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 AccessTestAction familyReadWrite = new AccessTestAction() { @Override public Void run() throws Exception { checkTablePerms(TEST_UTIL, TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ, Permission.Action.WRITE); return null; } }; verifyAllowed(familyReadWrite, SUPERUSER, USER_OWNER, USER_CREATE, USER_RW); verifyDenied(familyReadWrite, USER_NONE, USER_RO); // -------------------------------------- // check for wrong table region CheckPermissionsRequest checkRequest = CheckPermissionsRequest .newBuilder() .addPermission( AccessControlProtos.Permission .newBuilder() .setType(AccessControlProtos.Permission.Type.Table) .setTablePermission( AccessControlProtos.TablePermission.newBuilder() .setTableName(ProtobufUtil.toProtoTableName(TEST_TABLE)) .addAction(AccessControlProtos.Permission.Action.CREATE))).build(); Table acl = systemUserConnection.getTable(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(); } } finally { revokeFromTable(TEST_UTIL, userTable.getShortName(), TEST_TABLE, null, null, Permission.Action.READ); revokeFromTable(TEST_UTIL, userColumn.getShortName(), TEST_TABLE, TEST_FAMILY, null, Permission.Action.READ); revokeFromTable(TEST_UTIL, userQualifier.getShortName(), TEST_TABLE, TEST_FAMILY, TEST_Q1, Permission.Action.READ); } } @Test (timeout=180000) public void testStopRegionServer() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preStopRegionServer(ObserverContext.createAndPrepare(RSCP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testRollWALWriterRequest() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRollWALWriterRequest(ObserverContext.createAndPrepare(RSCP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testOpenRegion() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preOpen(ObserverContext.createAndPrepare(RCP_ENV, null)); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_CREATE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testCloseRegion() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preClose(ObserverContext.createAndPrepare(RCP_ENV, null), false); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_CREATE, USER_GROUP_READ, USER_GROUP_WRITE); } @Test (timeout=180000) public void testSnapshot() throws Exception { Admin admin = TEST_UTIL.getAdmin(); final HTableDescriptor htd = admin.getTableDescriptor(TEST_TABLE); SnapshotDescription.Builder builder = SnapshotDescription.newBuilder(); builder.setName(TEST_TABLE.getNameAsString() + "-snapshot"); builder.setTable(TEST_TABLE.getNameAsString()); final SnapshotDescription snapshot = builder.build(); AccessTestAction snapshotAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, htd); return null; } }; AccessTestAction deleteAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDeleteSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot); return null; } }; AccessTestAction restoreAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRestoreSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, htd); return null; } }; AccessTestAction cloneAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preCloneSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, null); return null; } }; verifyAllowed(snapshotAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(snapshotAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(cloneAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(deleteAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(restoreAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(restoreAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(deleteAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(cloneAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testSnapshotWithOwner() throws Exception { Admin admin = TEST_UTIL.getAdmin(); final HTableDescriptor htd = admin.getTableDescriptor(TEST_TABLE); SnapshotDescription.Builder builder = SnapshotDescription.newBuilder(); builder.setName(TEST_TABLE.getNameAsString() + "-snapshot"); builder.setTable(TEST_TABLE.getNameAsString()); builder.setOwner(USER_OWNER.getName()); final SnapshotDescription snapshot = builder.build(); AccessTestAction snapshotAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, htd); return null; } }; verifyAllowed(snapshotAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(snapshotAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); AccessTestAction deleteAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDeleteSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot); return null; } }; verifyAllowed(deleteAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(deleteAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); AccessTestAction restoreAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRestoreSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, htd); return null; } }; verifyAllowed(restoreAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(restoreAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); AccessTestAction cloneAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preCloneSnapshot(ObserverContext.createAndPrepare(CP_ENV, null), snapshot, htd); return null; } }; verifyAllowed(cloneAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN, USER_OWNER); verifyDenied(cloneAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testGlobalAuthorizationForNewRegisteredRS() throws Exception { LOG.debug("Test for global authorization for a new registered RegionServer."); MiniHBaseCluster hbaseCluster = TEST_UTIL.getHBaseCluster(); final Admin admin = TEST_UTIL.getAdmin(); HTableDescriptor htd = new HTableDescriptor(TEST_TABLE2); htd.addFamily(new HColumnDescriptor(TEST_FAMILY)); createTable(TEST_UTIL, htd); // Starting a new RegionServer. JVMClusterUtil.RegionServerThread newRsThread = hbaseCluster .startRegionServer(); final HRegionServer newRs = newRsThread.getRegionServer(); // Move region to the new RegionServer. List<HRegionLocation> regions; try (RegionLocator locator = systemUserConnection.getRegionLocator(TEST_TABLE2)) { regions = locator.getAllRegionLocations(); } HRegionLocation location = regions.get(0); final HRegionInfo hri = location.getRegionInfo(); final ServerName server = location.getServerName(); try (Table table = systemUserConnection.getTable(TEST_TABLE2)) { AccessTestAction moveAction = new AccessTestAction() { @Override public Object run() throws Exception { admin.move(hri.getEncodedNameAsBytes(), Bytes.toBytes(newRs.getServerName().getServerName())); return null; } }; SUPERUSER.runAs(moveAction); final int RETRIES_LIMIT = 10; int retries = 0; while (newRs.getOnlineRegions(TEST_TABLE2).size() < 1 && retries < RETRIES_LIMIT) { LOG.debug("Waiting for region to be opened. Already retried " + retries + " times."); try { Thread.sleep(1000); } catch (InterruptedException e) { } retries++; if (retries == RETRIES_LIMIT - 1) { fail("Retry exhaust for waiting region to be opened."); } } // Verify write permission for user "admin2" who has the global // permissions. AccessTestAction putAction = new AccessTestAction() { @Override public Object run() throws Exception { Put put = new Put(Bytes.toBytes("test")); put.addColumn(TEST_FAMILY, Bytes.toBytes("qual"), Bytes.toBytes("value")); table.put(put); return null; } }; USER_ADMIN.runAs(putAction); } } @Test (timeout=180000) public void testTableDescriptorsEnumeration() throws Exception { User TABLE_ADMIN = User.createUserForTesting(conf, "UserA", new String[0]); // Grant TABLE ADMIN privs grantOnTable(TEST_UTIL, TABLE_ADMIN.getShortName(), TEST_TABLE, null, null, Permission.Action.ADMIN); try { AccessTestAction listTablesAction = new AccessTestAction() { @Override public Object run() throws Exception { try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); Admin admin = conn.getAdmin()) { return Arrays.asList(admin.listTables()); } } }; AccessTestAction getTableDescAction = new AccessTestAction() { @Override public Object run() throws Exception { try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); Admin admin = conn.getAdmin();) { return admin.getTableDescriptor(TEST_TABLE); } } }; verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, TABLE_ADMIN, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyIfEmptyList(listTablesAction, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); verifyAllowed(getTableDescAction, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, TABLE_ADMIN, USER_GROUP_CREATE, USER_GROUP_ADMIN); verifyDenied(getTableDescAction, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); } finally { // Cleanup, revoke TABLE ADMIN privs revokeFromTable(TEST_UTIL, TABLE_ADMIN.getShortName(), TEST_TABLE, null, null, Permission.Action.ADMIN); } } @Test (timeout=180000) public void testTableNameEnumeration() throws Exception { AccessTestAction listTablesAction = new AccessTestAction() { @Override public Object run() throws Exception { Connection unmanagedConnection = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); Admin admin = unmanagedConnection.getAdmin(); try { return Arrays.asList(admin.listTableNames()); } finally { admin.close(); unmanagedConnection.close(); } } }; verifyAllowed(listTablesAction, SUPERUSER, USER_ADMIN, USER_CREATE, USER_OWNER, USER_RW, USER_RO, USER_GROUP_CREATE, USER_GROUP_ADMIN, USER_GROUP_READ, USER_GROUP_WRITE); verifyIfEmptyList(listTablesAction, USER_NONE); } @Test (timeout=180000) public void testTableDeletion() throws Exception { User TABLE_ADMIN = User.createUserForTesting(conf, "TestUser", new String[0]); final TableName tableName = TableName.valueOf(name.getMethodName()); createTestTable(tableName); // Grant TABLE ADMIN privs grantOnTable(TEST_UTIL, TABLE_ADMIN.getShortName(), tableName, null, null, Permission.Action.ADMIN); AccessTestAction deleteTableAction = new AccessTestAction() { @Override public Object run() throws Exception { Connection unmanagedConnection = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration()); Admin admin = unmanagedConnection.getAdmin(); try { deleteTable(TEST_UTIL, admin, tableName); } finally { admin.close(); unmanagedConnection.close(); } return null; } }; verifyDenied(deleteTableAction, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE); verifyAllowed(deleteTableAction, TABLE_ADMIN); } private void createTestTable(TableName tname) throws Exception { HTableDescriptor htd = new HTableDescriptor(tname); HColumnDescriptor hcd = new HColumnDescriptor(TEST_FAMILY); hcd.setMaxVersions(100); htd.addFamily(hcd); htd.setOwner(USER_OWNER); createTable(TEST_UTIL, htd, new byte[][] { Bytes.toBytes("s") }); } @Test (timeout=180000) public void testNamespaceUserGrant() throws Exception { AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { return t.get(new Get(TEST_ROW)); } } }; String namespace = TEST_TABLE.getNamespaceAsString(); // Grant namespace READ to USER_NONE, this should supersede any table permissions grantOnNamespace(TEST_UTIL, USER_NONE.getShortName(), namespace, Permission.Action.READ); // Now USER_NONE should be able to read verifyAllowed(getAction, USER_NONE); // Revoke namespace READ to USER_NONE revokeFromNamespace(TEST_UTIL, USER_NONE.getShortName(), namespace, Permission.Action.READ); verifyDenied(getAction, USER_NONE); } @Test (timeout=180000) public void testAccessControlClientGrantRevoke() throws Exception { // Create user for testing, who has no READ privileges by default. User testGrantRevoke = User.createUserForTesting(conf, "testGrantRevoke", new String[0]); AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { return t.get(new Get(TEST_ROW)); } } }; verifyDenied(getAction, testGrantRevoke); // Grant table READ permissions to testGrantRevoke. try { grantOnTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, testGrantRevoke.getShortName(), TEST_TABLE, null, null, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } // Now testGrantRevoke should be able to read also verifyAllowed(getAction, testGrantRevoke); // Revoke table READ permission to testGrantRevoke. try { revokeFromTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, testGrantRevoke.getShortName(), TEST_TABLE, null, null, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } // Now testGrantRevoke shouldn't be able read verifyDenied(getAction, testGrantRevoke); } @Test (timeout=180000) public void testAccessControlClientGlobalGrantRevoke() throws Exception { // Create user for testing, who has no READ privileges by default. User testGlobalGrantRevoke = User.createUserForTesting(conf, "testGlobalGrantRevoke", new String[0]); AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { return t.get(new Get(TEST_ROW)); } } }; verifyDenied(getAction, testGlobalGrantRevoke); // Grant table READ permissions to testGlobalGrantRevoke. String userName = testGlobalGrantRevoke.getShortName(); try { grantGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } try { // Now testGlobalGrantRevoke should be able to read also verifyAllowed(getAction, testGlobalGrantRevoke); // Revoke table READ permission to testGlobalGrantRevoke. try { revokeGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } // Now testGlobalGrantRevoke shouldn't be able read verifyDenied(getAction, testGlobalGrantRevoke); } finally { revokeGlobal(TEST_UTIL, userName, Permission.Action.READ); } } @Test(timeout = 180000) public void testAccessControlClientMultiGrantRevoke() throws Exception { User testGrantRevoke = User.createUserForTesting(conf, "testGrantRevoke", new String[0]); AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { return t.get(new Get(TEST_ROW)); } } }; AccessTestAction putAction = new AccessTestAction() { @Override public Object run() throws Exception { Put p = new Put(TEST_ROW); p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(1)); try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE)) { t.put(p); return null; } } }; verifyDenied(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant global READ permissions to testGrantRevoke. String userName = testGrantRevoke.getShortName(); try { grantGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant global READ permissions to testGrantRevoke. try { grantGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, Permission.Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyAllowed(putAction, testGrantRevoke); // Revoke global READ permission to testGrantRevoke. try { revokeGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, Permission.Action.READ, Permission.Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } verifyDenied(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant table READ & WRITE permissions to testGrantRevoke try { grantOnTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE, null, null, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant table WRITE permissions to testGrantRevoke try { grantOnTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE, null, null, Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyAllowed(putAction, testGrantRevoke); // Revoke table READ & WRITE permission to testGrantRevoke. try { revokeFromTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE, null, null, Permission.Action.READ, Permission.Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } verifyDenied(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant Namespace READ permissions to testGrantRevoke String namespace = TEST_TABLE.getNamespaceAsString(); try { grantOnNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, namespace, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); // Grant Namespace WRITE permissions to testGrantRevoke try { grantOnNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, namespace, Permission.Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } verifyAllowed(getAction, testGrantRevoke); verifyAllowed(putAction, testGrantRevoke); // Revoke table READ & WRITE permission to testGrantRevoke. try { revokeFromNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE.getNamespaceAsString(), Permission.Action.READ, Permission.Action.WRITE); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } verifyDenied(getAction, testGrantRevoke); verifyDenied(putAction, testGrantRevoke); } @Test (timeout=180000) public void testAccessControlClientGrantRevokeOnNamespace() throws Exception { // Create user for testing, who has no READ privileges by default. User testNS = User.createUserForTesting(conf, "testNS", new String[0]); AccessTestAction getAction = new AccessTestAction() { @Override public Object run() throws Exception { try(Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { return t.get(new Get(TEST_ROW)); } } }; verifyDenied(getAction, testNS); String userName = testNS.getShortName(); String namespace = TEST_TABLE.getNamespaceAsString(); // Grant namespace READ to testNS, this should supersede any table permissions try { grantOnNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, namespace, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.grant. ", e); } try { // Now testNS should be able to read also verifyAllowed(getAction, testNS); // Revoke namespace READ to testNS, this should supersede any table permissions try { revokeFromNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, namespace, Permission.Action.READ); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.revoke ", e); } // Now testNS shouldn't be able read verifyDenied(getAction, testNS); } finally { revokeFromNamespace(TEST_UTIL, userName, namespace, Permission.Action.READ); } } public static class PingCoprocessor extends PingService implements Coprocessor, CoprocessorService { @Override public void start(CoprocessorEnvironment env) throws IOException { } @Override public void stop(CoprocessorEnvironment env) throws IOException { } @Override public Service getService() { return this; } @Override public void ping(RpcController controller, PingRequest request, RpcCallback<PingResponse> callback) { callback.run(PingResponse.newBuilder().setPong("Pong!").build()); } @Override public void count(RpcController controller, CountRequest request, RpcCallback<CountResponse> callback) { callback.run(CountResponse.newBuilder().build()); } @Override public void increment(RpcController controller, IncrementCountRequest requet, RpcCallback<IncrementCountResponse> callback) { callback.run(IncrementCountResponse.newBuilder().build()); } @Override public void hello(RpcController controller, HelloRequest request, RpcCallback<HelloResponse> callback) { callback.run(HelloResponse.newBuilder().setResponse("Hello!").build()); } @Override public void noop(RpcController controller, NoopRequest request, RpcCallback<NoopResponse> callback) { callback.run(NoopResponse.newBuilder().build()); } } @Test (timeout=180000) public void testCoprocessorExec() throws Exception { // Set up our ping endpoint service on all regions of our test table for (JVMClusterUtil.RegionServerThread thread: TEST_UTIL.getMiniHBaseCluster().getRegionServerThreads()) { HRegionServer rs = thread.getRegionServer(); for (Region region: rs.getOnlineRegions(TEST_TABLE)) { region.getCoprocessorHost().load(PingCoprocessor.class, Coprocessor.PRIORITY_USER, conf); } } // Create users for testing, and grant EXEC privileges on our test table // only to user A User userA = User.createUserForTesting(conf, "UserA", new String[0]); User userB = User.createUserForTesting(conf, "UserB", new String[0]); grantOnTable(TEST_UTIL, userA.getShortName(), TEST_TABLE, null, null, Permission.Action.EXEC); try { // Create an action for invoking our test endpoint AccessTestAction execEndpointAction = new AccessTestAction() { @Override public Object run() throws Exception { try (Connection conn = ConnectionFactory.createConnection(conf); Table t = conn.getTable(TEST_TABLE);) { BlockingRpcChannel service = t.coprocessorService(HConstants.EMPTY_BYTE_ARRAY); PingCoprocessor.newBlockingStub(service).noop(null, NoopRequest.newBuilder().build()); } return null; } }; String namespace = TEST_TABLE.getNamespaceAsString(); // Now grant EXEC to the entire namespace to user B grantOnNamespace(TEST_UTIL, userB.getShortName(), namespace, Permission.Action.EXEC); // User B should now be allowed also verifyAllowed(execEndpointAction, userA, userB); revokeFromNamespace(TEST_UTIL, userB.getShortName(), namespace, Permission.Action.EXEC); // Verify that EXEC permission is checked correctly verifyDenied(execEndpointAction, userB); verifyAllowed(execEndpointAction, userA); } finally { // Cleanup, revoke the userA privileges revokeFromTable(TEST_UTIL, userA.getShortName(), TEST_TABLE, null, null, Permission.Action.EXEC); } } @Test (timeout=180000) public void testSetQuota() throws Exception { AccessTestAction setUserQuotaAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetUserQuota(ObserverContext.createAndPrepare(CP_ENV, null), null, null); return null; } }; AccessTestAction setUserTableQuotaAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetUserQuota(ObserverContext.createAndPrepare(CP_ENV, null), null, TEST_TABLE, null); return null; } }; AccessTestAction setUserNamespaceQuotaAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetUserQuota(ObserverContext.createAndPrepare(CP_ENV, null), null, (String)null, null); return null; } }; AccessTestAction setTableQuotaAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetTableQuota(ObserverContext.createAndPrepare(CP_ENV, null), TEST_TABLE, null); return null; } }; AccessTestAction setNamespaceQuotaAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preSetNamespaceQuota(ObserverContext.createAndPrepare(CP_ENV, null), null, null); return null; } }; verifyAllowed(setUserQuotaAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(setUserQuotaAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(setUserTableQuotaAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(setUserTableQuotaAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(setUserNamespaceQuotaAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(setUserNamespaceQuotaAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); verifyAllowed(setTableQuotaAction, SUPERUSER, USER_ADMIN, USER_OWNER, USER_GROUP_ADMIN); verifyDenied(setTableQuotaAction, USER_CREATE, USER_RW, USER_RO, USER_NONE); verifyAllowed(setNamespaceQuotaAction, SUPERUSER, USER_ADMIN, USER_GROUP_ADMIN); verifyDenied(setNamespaceQuotaAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_CREATE); } @Test (timeout=180000) public void testGetNamespacePermission() throws Exception { String namespace = "testGetNamespacePermission"; NamespaceDescriptor desc = NamespaceDescriptor.create(namespace).build(); createNamespace(TEST_UTIL, desc); grantOnNamespace(TEST_UTIL, USER_NONE.getShortName(), namespace, Permission.Action.READ); // Test 1: A specific namespace getNamespacePermissionsAndVerify(namespace, 1, namespace); // Test 2: '@.*' getNamespacePermissionsAndVerify(".*", 1, namespace); // Test 3: A more complex regex getNamespacePermissionsAndVerify("^test[a-zA-Z]*", 1, namespace); deleteNamespace(TEST_UTIL, namespace); } /** * List all user permissions match the given regular expression for namespace * and verify each of them. * @param namespaceRegexWithoutPrefix the regualar expression for namespace, without NAMESPACE_PREFIX * @param expectedAmount the expected amount of user permissions returned * @param expectedNamespace the expected namespace of each user permission returned * @throws HBaseException in the case of any HBase exception when accessing hbase:acl table */ private void getNamespacePermissionsAndVerify(String namespaceRegexWithoutPrefix, int expectedAmount, String expectedNamespace) throws HBaseException { try { List<UserPermission> namespacePermissions = AccessControlClient.getUserPermissions( systemUserConnection, AccessControlLists.toNamespaceEntry(namespaceRegexWithoutPrefix)); assertTrue(namespacePermissions != null); assertEquals(expectedAmount, namespacePermissions.size()); for (UserPermission namespacePermission : namespacePermissions) { assertFalse(namespacePermission.isGlobal()); // Verify it is not a global user permission assertEquals(expectedNamespace, namespacePermission.getNamespace()); // Verify namespace is set } } catch (Throwable thw) { throw new HBaseException(thw); } } @Test (timeout=180000) public void testTruncatePerms() throws Exception { try { List<UserPermission> existingPerms = AccessControlClient.getUserPermissions( systemUserConnection, TEST_TABLE.getNameAsString()); assertTrue(existingPerms != null); assertTrue(existingPerms.size() > 1); TEST_UTIL.getAdmin().disableTable(TEST_TABLE); TEST_UTIL.truncateTable(TEST_TABLE); TEST_UTIL.waitTableAvailable(TEST_TABLE); List<UserPermission> perms = AccessControlClient.getUserPermissions( systemUserConnection, TEST_TABLE.getNameAsString()); assertTrue(perms != null); assertEquals(existingPerms.size(), perms.size()); } catch (Throwable e) { throw new HBaseIOException(e); } } private PrivilegedAction<List<UserPermission>> getPrivilegedAction(final String regex) { return new PrivilegedAction<List<UserPermission>>() { @Override public List<UserPermission> run() { try(Connection conn = ConnectionFactory.createConnection(conf);) { return AccessControlClient.getUserPermissions(conn, regex); } catch (Throwable e) { LOG.error("error during call of AccessControlClient.getUserPermissions.", e); return null; } } }; } @Test (timeout=180000) public void testAccessControlClientUserPerms() throws Exception { final TableName tableName = TableName.valueOf(name.getMethodName()); createTestTable(tableName); try { final String regex = tableName.getNameWithNamespaceInclAsString(); User testUserPerms = User.createUserForTesting(conf, "testUserPerms", new String[0]); assertEquals(0, testUserPerms.runAs(getPrivilegedAction(regex)).size()); // Grant TABLE ADMIN privs to testUserPerms grantOnTable(TEST_UTIL, testUserPerms.getShortName(), tableName, null, null, Action.ADMIN); List<UserPermission> perms = testUserPerms.runAs(getPrivilegedAction(regex)); assertNotNull(perms); // Superuser, testUserPerms assertEquals(2, perms.size()); } finally { deleteTable(TEST_UTIL, tableName); } } @Test (timeout=180000) public void testAccessControllerUserPermsRegexHandling() throws Exception { User testRegexHandler = User.createUserForTesting(conf, "testRegexHandling", new String[0]); final String REGEX_ALL_TABLES = ".*"; final String tableName = name.getMethodName(); final TableName table1 = TableName.valueOf(tableName); final byte[] family = Bytes.toBytes("f1"); // create table in default ns Admin admin = TEST_UTIL.getAdmin(); HTableDescriptor htd = new HTableDescriptor(table1); htd.addFamily(new HColumnDescriptor(family)); createTable(TEST_UTIL, htd); // creating the ns and table in it String ns = "testNamespace"; NamespaceDescriptor desc = NamespaceDescriptor.create(ns).build(); final TableName table2 = TableName.valueOf(ns, tableName); createNamespace(TEST_UTIL, desc); htd = new HTableDescriptor(table2); htd.addFamily(new HColumnDescriptor(family)); createTable(TEST_UTIL, htd); // Verify that we can read sys-tables String aclTableName = AccessControlLists.ACL_TABLE_NAME.getNameAsString(); assertEquals(5, SUPERUSER.runAs(getPrivilegedAction(aclTableName)).size()); assertEquals(0, testRegexHandler.runAs(getPrivilegedAction(aclTableName)).size()); // Grant TABLE ADMIN privs to testUserPerms assertEquals(0, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); grantOnTable(TEST_UTIL, testRegexHandler.getShortName(), table1, null, null, Action.ADMIN); assertEquals(2, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); grantOnTable(TEST_UTIL, testRegexHandler.getShortName(), table2, null, null, Action.ADMIN); assertEquals(4, testRegexHandler.runAs(getPrivilegedAction(REGEX_ALL_TABLES)).size()); // USER_ADMIN, testUserPerms must have a row each. assertEquals(2, testRegexHandler.runAs(getPrivilegedAction(tableName)).size()); assertEquals(2, testRegexHandler.runAs(getPrivilegedAction( NamespaceDescriptor.DEFAULT_NAMESPACE_NAME_STR + TableName.NAMESPACE_DELIM + tableName) ).size()); assertEquals(2, testRegexHandler.runAs(getPrivilegedAction( ns + TableName.NAMESPACE_DELIM + tableName)).size()); assertEquals(0, testRegexHandler.runAs(getPrivilegedAction("notMatchingAny")).size()); deleteTable(TEST_UTIL, table1); deleteTable(TEST_UTIL, table2); deleteNamespace(TEST_UTIL, ns); } private void verifyAnyCreate(AccessTestAction action) throws Exception { verifyAllowed(action, SUPERUSER, USER_ADMIN, USER_OWNER, USER_CREATE, USER_ADMIN_CF, USER_GROUP_CREATE); verifyDenied(action, USER_NONE, USER_RO, USER_RW, USER_GROUP_READ, USER_GROUP_WRITE, USER_GROUP_ADMIN); } @Test (timeout=180000) 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); } @Test (timeout=180000) public void testReplicateLogEntries() throws Exception { AccessTestAction replicateLogEntriesAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preReplicateLogEntries(ObserverContext.createAndPrepare(RSCP_ENV, null), null, null); ACCESS_CONTROLLER.postReplicateLogEntries(ObserverContext.createAndPrepare(RSCP_ENV, null), null, null); return null; } }; verifyAllowed(replicateLogEntriesAction, SUPERUSER, USER_ADMIN, USER_GROUP_WRITE); verifyDenied(replicateLogEntriesAction, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER, USER_GROUP_READ, USER_GROUP_ADMIN, USER_GROUP_CREATE); } @Test public void testMoveServers() throws Exception { AccessTestAction action1 = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preMoveServers(ObserverContext.createAndPrepare(CP_ENV, null), null, null); return null; } }; verifyAllowed(action1, SUPERUSER, USER_ADMIN); verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testMoveTables() throws Exception { AccessTestAction action1 = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preMoveTables(ObserverContext.createAndPrepare(CP_ENV, null), null, null); return null; } }; verifyAllowed(action1, SUPERUSER, USER_ADMIN); verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testAddGroup() throws Exception { AccessTestAction action1 = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preAddRSGroup(ObserverContext.createAndPrepare(CP_ENV, null), null); return null; } }; verifyAllowed(action1, SUPERUSER, USER_ADMIN); verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testRemoveGroup() throws Exception { AccessTestAction action1 = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRemoveRSGroup(ObserverContext.createAndPrepare(CP_ENV, null), null); return null; } }; verifyAllowed(action1, SUPERUSER, USER_ADMIN); verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testBalanceGroup() throws Exception { AccessTestAction action1 = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preBalanceRSGroup(ObserverContext.createAndPrepare(CP_ENV, null), null); return null; } }; verifyAllowed(action1, SUPERUSER, USER_ADMIN); verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testAddReplicationPeer() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preAddReplicationPeer(ObserverContext.createAndPrepare(CP_ENV, null), "test", null); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testRemoveReplicationPeer() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRemoveReplicationPeer(ObserverContext.createAndPrepare(CP_ENV, null), "test"); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testEnableReplicationPeer() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preEnableReplicationPeer(ObserverContext.createAndPrepare(CP_ENV, null), "test"); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testDisableReplicationPeer() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preDisableReplicationPeer(ObserverContext.createAndPrepare(CP_ENV, null), "test"); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testGetReplicationPeerConfig() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preGetReplicationPeerConfig( ObserverContext.createAndPrepare(CP_ENV, null), "test"); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testUpdateReplicationPeerConfig() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preUpdateReplicationPeerConfig( ObserverContext.createAndPrepare(CP_ENV, null), "test", new ReplicationPeerConfig()); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testListReplicationPeers() throws Exception { AccessTestAction action = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preListReplicationPeers(ObserverContext.createAndPrepare(CP_ENV, null), "test"); return null; } }; verifyAllowed(action, SUPERUSER, USER_ADMIN); verifyDenied(action, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); } @Test public void testRemoteLocks() throws Exception { String namespace = "preQueueNs"; final TableName tableName = TableName.valueOf(namespace, name.getMethodName()); HRegionInfo[] regionInfos = new HRegionInfo[] {new HRegionInfo(tableName)}; // Setup Users // User will be granted ADMIN and CREATE on namespace. Should be denied before grant. User namespaceUser = User.createUserForTesting(conf, "qLNSUser", new String[0]); // User will be granted ADMIN and CREATE on table. Should be denied before grant. User tableACUser = User.createUserForTesting(conf, "qLTableACUser", new String[0]); // User will be granted READ, WRITE, EXECUTE on table. Should be denied. User tableRWXUser = User.createUserForTesting(conf, "qLTableRWXUser", new String[0]); grantOnTable(TEST_UTIL, tableRWXUser.getShortName(), tableName, null, null, Action.READ, Action.WRITE, Action.EXEC); // User with global READ, WRITE, EXECUTE should be denied lock access. User globalRWXUser = User.createUserForTesting(conf, "qLGlobalRWXUser", new String[0]); grantGlobal(TEST_UTIL, globalRWXUser.getShortName(), Action.READ, Action.WRITE, Action.EXEC); AccessTestAction namespaceLockAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRequestLock(ObserverContext.createAndPrepare(CP_ENV, null), namespace, null, null, LockType.EXCLUSIVE, null); return null; } }; verifyAllowed(namespaceLockAction, SUPERUSER, USER_ADMIN); verifyDenied(namespaceLockAction, globalRWXUser, tableACUser, namespaceUser, tableRWXUser); grantOnNamespace(TEST_UTIL, namespaceUser.getShortName(), namespace, Action.ADMIN); verifyAllowed(namespaceLockAction, namespaceUser); AccessTestAction tableLockAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRequestLock(ObserverContext.createAndPrepare(CP_ENV, null), null, tableName, null, LockType.EXCLUSIVE, null); return null; } }; verifyAllowed(tableLockAction, SUPERUSER, USER_ADMIN, namespaceUser); verifyDenied(tableLockAction, globalRWXUser, tableACUser, tableRWXUser); grantOnTable(TEST_UTIL, tableACUser.getShortName(), tableName, null, null, Action.ADMIN, Action.CREATE); verifyAllowed(tableLockAction, tableACUser); AccessTestAction regionsLockAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preRequestLock(ObserverContext.createAndPrepare(CP_ENV, null), null, null, regionInfos, LockType.EXCLUSIVE, null); return null; } }; verifyAllowed(regionsLockAction, SUPERUSER, USER_ADMIN, namespaceUser, tableACUser); verifyDenied(regionsLockAction, globalRWXUser, tableRWXUser); // Test heartbeats // Create a lock procedure and try sending heartbeat to it. It doesn't matter how the lock // was created, we just need namespace from the lock's tablename. LockProcedure proc = new LockProcedure(conf, tableName, LockType.EXCLUSIVE, "test", null); AccessTestAction regionLockHeartbeatAction = new AccessTestAction() { @Override public Object run() throws Exception { ACCESS_CONTROLLER.preLockHeartbeat(ObserverContext.createAndPrepare(CP_ENV, null), proc, false); return null; } }; verifyAllowed(regionLockHeartbeatAction, SUPERUSER, USER_ADMIN, namespaceUser, tableACUser); verifyDenied(regionLockHeartbeatAction, globalRWXUser, tableRWXUser); } }