From 6f102689d57a197225b8c2601c0ad7c32f6c8a39 Mon Sep 17 00:00:00 2001 From: Guangxu Cheng Date: Wed, 10 Jan 2018 14:14:11 +0800 Subject: [PATCH] HBASE-19483 Add proper privilege check for rsgroup commands --- .../IntegrationTestIngestWithVisibilityLabels.java | 5 +- ...IntegrationTestBigLinkedListWithVisibility.java | 5 +- ...grationTestWithCellVisibilityLoadAndVerify.java | 5 +- .../hadoop/hbase/rest/TestScannersWithLabels.java | 5 +- .../hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java | 50 +- .../hadoop/hbase/rsgroup/TestRSGroupsWithACL.java | 358 +++++++++++ .../hadoop/hbase/master/MasterRpcServices.java | 3 +- .../hbase/security/access/AccessChecker.java | 300 +++++++++ .../hbase/security/access/AccessControlLists.java | 4 +- .../hbase/security/access/AccessController.java | 477 ++++---------- .../hbase/security/access/TableAuthManager.java | 2 +- .../security/visibility/VisibilityController.java | 13 +- .../TestImportTSVWithVisibilityLabels.java | 5 +- .../hbase/security/access/SecureTestUtil.java | 6 + .../security/access/TestAccessController.java | 75 --- .../security/visibility/VisibilityTestUtil.java | 3 +- .../TestThriftHBaseServiceHandlerWithLabels.java | 707 ++++++++++----------- .../asciidoc/_chapters/appendix_acl_matrix.adoc | 12 + src/main/asciidoc/_chapters/security.adoc | 16 + 19 files changed, 1228 insertions(+), 823 deletions(-) create mode 100644 hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java create mode 100644 hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestIngestWithVisibilityLabels.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestIngestWithVisibilityLabels.java index b9429184a2..6d2f783a33 100644 --- a/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestIngestWithVisibilityLabels.java +++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/IntegrationTestIngestWithVisibilityLabels.java @@ -28,6 +28,7 @@ import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.visibility.LoadTestDataGeneratorWithVisibilityLabels; import org.apache.hadoop.hbase.security.visibility.VisibilityClient; import org.apache.hadoop.hbase.security.visibility.VisibilityController; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.testclassification.IntegrationTests; import org.apache.hadoop.hbase.util.LoadTestTool; import org.junit.experimental.categories.Category; @@ -76,9 +77,7 @@ public class IntegrationTestIngestWithVisibilityLabels extends IntegrationTestIn public void setUpCluster() throws Exception { util = getTestingUtil(null); Configuration conf = util.getConfiguration(); - conf.setInt(HFile.FORMAT_VERSION_KEY, 3); - conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); conf.set("hbase.superuser", "admin," + User.getCurrent().getName()); super.setUpCluster(); addLabels(); diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestBigLinkedListWithVisibility.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestBigLinkedListWithVisibility.java index 8e93eaa2b6..5f1f5b97c4 100644 --- a/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestBigLinkedListWithVisibility.java +++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestBigLinkedListWithVisibility.java @@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.IntegrationTestingUtility; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.testclassification.IntegrationTests; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.chaos.factories.MonkeyFactory; @@ -369,9 +370,7 @@ public class IntegrationTestBigLinkedListWithVisibility extends IntegrationTestB public void setUpCluster() throws Exception { util = getTestingUtil(null); Configuration conf = util.getConfiguration(); - conf.setInt(HFile.FORMAT_VERSION_KEY, 3); - conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); conf.set("hbase.superuser", User.getCurrent().getName()); conf.setBoolean("dfs.permissions", false); USER = User.createUserForTesting(conf, userName, new String[] {}); diff --git a/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestWithCellVisibilityLoadAndVerify.java b/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestWithCellVisibilityLoadAndVerify.java index 9db5a70b3b..f119541977 100644 --- a/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestWithCellVisibilityLoadAndVerify.java +++ b/hbase-it/src/test/java/org/apache/hadoop/hbase/test/IntegrationTestWithCellVisibilityLoadAndVerify.java @@ -32,6 +32,7 @@ import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.IntegrationTestingUtility; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.testclassification.IntegrationTests; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.HBaseAdmin; @@ -115,9 +116,7 @@ public class IntegrationTestWithCellVisibilityLoadAndVerify extends IntegrationT public void setUpCluster() throws Exception { util = getTestingUtil(null); Configuration conf = util.getConfiguration(); - conf.setInt(HFile.FORMAT_VERSION_KEY, 3); - conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); conf.set("hbase.superuser", User.getCurrent().getName()); conf.setBoolean("dfs.permissions", false); super.setUpCluster(); diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithLabels.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithLabels.java index c72517c949..356889a7fb 100644 --- a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithLabels.java +++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithLabels.java @@ -37,6 +37,7 @@ import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Admin; @@ -130,10 +131,8 @@ public class TestScannersWithLabels { conf = TEST_UTIL.getConfiguration(); conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, ScanLabelGenerator.class); - conf.setInt("hfile.format.version", 3); conf.set("hbase.superuser", SUPERUSER.getShortName()); - conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); TEST_UTIL.startMiniCluster(1); // Wait for the labels table to become available TEST_UTIL.waitTableEnabled(VisibilityConstants.LABELS_TABLE_NAME.getName(), 50000); diff --git a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java index a09010e27e..fb9c6a6e96 100644 --- a/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java +++ b/hbase-rsgroup/src/main/java/org/apache/hadoop/hbase/rsgroup/RSGroupAdminEndpoint.java @@ -48,6 +48,7 @@ import org.apache.hadoop.hbase.coprocessor.CoprocessorService; import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.MasterObserver; import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; @@ -83,6 +84,12 @@ import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveRSGro import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersRequest; import org.apache.hadoop.hbase.protobuf.generated.RSGroupAdminProtos.RemoveServersResponse; import org.apache.hadoop.hbase.protobuf.generated.TableProtos; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.UserProvider; +import org.apache.hadoop.hbase.security.access.AccessChecker; +import org.apache.hadoop.hbase.security.access.Permission; +import org.apache.hadoop.hbase.security.access.TableAuthManager; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; public class RSGroupAdminEndpoint extends RSGroupAdminService implements CoprocessorService, Coprocessor, MasterObserver { @@ -91,6 +98,10 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService private static RSGroupInfoManagerImpl groupInfoManager; private RSGroupAdminServer groupAdminServer; + private AccessChecker accessChecker; + + /** Provider for mapping principal names to Users */ + private UserProvider userProvider; @Override public void start(CoprocessorEnvironment env) throws IOException { @@ -103,10 +114,18 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService if (!RSGroupableBalancer.class.isAssignableFrom(clazz)) { throw new IOException("Configured balancer is not a GroupableBalancer"); } + ZooKeeperWatcher zk = menv.getMasterServices().getZooKeeper(); + accessChecker = new AccessChecker(env.getConfiguration(), zk); + + // set the user-provider. + this.userProvider = UserProvider.instantiate(env.getConfiguration()); } @Override - public void stop(CoprocessorEnvironment env) throws IOException { + public void stop(CoprocessorEnvironment env) { + if (accessChecker.getAuthManager() != null) { + TableAuthManager.release(accessChecker.getAuthManager()); + } } @Override @@ -141,6 +160,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService GetRSGroupInfoResponse.Builder builder = GetRSGroupInfoResponse.newBuilder(); RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfo(request.getRSGroupName()); + checkPermission("getRSGroupInfo"); if(RSGroupInfo != null) { builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); } @@ -160,6 +180,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService GetRSGroupInfoOfTableResponse.Builder builder = GetRSGroupInfoOfTableResponse.newBuilder(); TableName tableName = ProtobufUtil.toTableName(request.getTableName()); + checkPermission("getRSGroupInfoOfTable"); RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfoOfTable(tableName); if (RSGroupInfo == null) { response = builder.build(); @@ -184,6 +205,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService for(HBaseProtos.ServerName el: request.getServersList()) { servers.add(Address.fromParts(el.getHostName(), el.getPort())); } + checkPermission("moveServers"); groupAdminServer.moveServers(servers, request.getTargetGroup()); response = builder.build(); } catch (IOException e) { @@ -204,6 +226,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService for(TableProtos.TableName tableName: request.getTableNameList()) { tables.add(ProtobufUtil.toTableName(tableName)); } + checkPermission("moveTables"); groupAdminServer.moveTables(tables, request.getTargetGroup()); response = builder.build(); } catch (IOException e) { @@ -225,6 +248,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService for (TableProtos.TableName tableName : request.getTableNameList()) { tables.add(ProtobufUtil.toTableName(tableName)); } + checkPermission("moveServersAndTables"); groupAdminServer.moveServersAndTables(servers, tables, request.getTargetGroup()); } catch (IOException e) { ResponseConverter.setControllerException(controller, e); @@ -240,6 +264,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService try { AddRSGroupResponse.Builder builder = AddRSGroupResponse.newBuilder(); + checkPermission("addRSGroup"); groupAdminServer.addRSGroup(request.getRSGroupName()); response = builder.build(); } catch (IOException e) { @@ -256,6 +281,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService try { RemoveRSGroupResponse.Builder builder = RemoveRSGroupResponse.newBuilder(); + checkPermission("removeRSGroup"); groupAdminServer.removeRSGroup(request.getRSGroupName()); response = builder.build(); } catch (IOException e) { @@ -270,6 +296,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService RpcCallback done) { BalanceRSGroupResponse.Builder builder = BalanceRSGroupResponse.newBuilder(); try { + checkPermission("balanceRSGroup"); builder.setBalanceRan(groupAdminServer.balanceRSGroup(request.getRSGroupName())); } catch (IOException e) { ResponseConverter.setControllerException(controller, e); @@ -286,6 +313,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService try { ListRSGroupInfosResponse.Builder builder = ListRSGroupInfosResponse.newBuilder(); + checkPermission("listRSGroupInfos"); for(RSGroupInfo RSGroupInfo : groupAdminServer.listRSGroups()) { builder.addRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); } @@ -304,6 +332,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService try { Address server = Address.fromParts(request.getServer().getHostName(), request.getServer().getPort()); + checkPermission("getRSGroupInfoOfServer"); RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupOfServer(server); if (RSGroupInfo != null) { builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); @@ -325,6 +354,7 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService for (HBaseProtos.ServerName el : request.getServersList()) { servers.add(Address.fromParts(el.getHostName(), el.getPort())); } + checkPermission("removeServers"); groupAdminServer.removeServers(servers); } catch (IOException e) { ResponseConverter.setControllerException(controller, e); @@ -1080,4 +1110,22 @@ public class RSGroupAdminEndpoint extends RSGroupAdminService String groupName, boolean balancerRan) throws IOException { } + + public void checkPermission(String request) throws IOException { + accessChecker.requirePermission(getActiveUser(), request, Permission.Action.ADMIN); + } + + /** + * Returns the active user to which authorization checks should be applied. + * If we are in the context of an RPC call, the remote user is used, + * otherwise the currently logged in user is used. + */ + private User getActiveUser() throws IOException { + User user = RpcServer.getRequestUser(); + if (user == null) { + // for non-rpc handling, fallback to system user + user = userProvider.getCurrent(); + } + return user; + } } diff --git a/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java new file mode 100644 index 0000000000..3b15f1b888 --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java @@ -0,0 +1,358 @@ +/* + * 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.rsgroup; + +import static org.apache.hadoop.hbase.AuthUtil.toGroupEntry; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.TableNotFoundException; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessControlClient; +import org.apache.hadoop.hbase.security.access.AccessControlLists; +import org.apache.hadoop.hbase.security.access.Permission; +import org.apache.hadoop.hbase.security.access.SecureTestUtil; +import org.apache.hadoop.hbase.security.access.TableAuthManager; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Performs authorization checks for rsgroup operations, according to different + * levels of authorized users. + */ +@Category({SecurityTests.class}) +public class TestRSGroupsWithACL extends SecureTestUtil{ + private static final Logger LOG = LoggerFactory.getLogger(TestRSGroupsWithACL.class); + private static TableName TEST_TABLE = TableName.valueOf("testtable1"); + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static Configuration conf; + + 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; + + 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; + + private static byte[] TEST_FAMILY = Bytes.toBytes("f1"); + + private static RSGroupAdminEndpoint rsGroupAdminEndpoint; + + @BeforeClass + public static void setupBeforeClass() throws Exception { + // setup configuration + conf = TEST_UTIL.getConfiguration(); + conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, + RSGroupBasedLoadBalancer.class.getName()); + // Enable security + enableSecurity(conf); + // Verify enableSecurity sets up what we require + verifyConfiguration(conf); + // Enable rsgroup + configureRSGroupAdminEndpoint(conf); + + TEST_UTIL.startMiniCluster(); + rsGroupAdminEndpoint = (RSGroupAdminEndpoint) TEST_UTIL.getMiniHBaseCluster().getMaster(). + getMasterCoprocessorHost().findCoprocessor(RSGroupAdminEndpoint.class.getName()); + // 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_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(); + } + + 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") }); + + // 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); + + 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(4, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); + try { + assertEquals(4, 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()); + } + + @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 configureRSGroupAdminEndpoint(Configuration conf) { + String currentCoprocessors = conf.get(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY); + String coprocessors = RSGroupAdminEndpoint.class.getName(); + if (currentCoprocessors != null) { + coprocessors += "," + currentCoprocessors; + } + conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, coprocessors); + conf.set(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, + RSGroupBasedLoadBalancer.class.getName()); + } + + @Test + public void testGetRSGroupInfo() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("getRSGroupInfo"); + 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 + public void testGetRSGroupInfoOfTable() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("getRSGroupInfoOfTable"); + 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 + public void testMoveServers() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("moveServers"); + 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 + public void testMoveTables() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("moveTables"); + 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 + public void testAddRSGroup() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("addRSGroup"); + 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 + public void testRemoveRSGroup() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("removeRSGroup"); + 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 + public void testBalanceRSGroup() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("balanceRSGroup"); + 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 + public void testListRSGroup() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("listRSGroup"); + 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 + public void testGetRSGroupInfoOfServer() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("getRSGroupInfoOfServer"); + 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 + public void testMoveServersAndTables() throws Exception { + AccessTestAction action = new AccessTestAction() { + @Override + public Object run() throws Exception { + rsGroupAdminEndpoint.checkPermission("moveServersAndTables"); + 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); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java index b86a9f5c6b..08a20339f7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterRpcServices.java @@ -190,6 +190,7 @@ import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.Repor import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.ReportRegionStateTransitionResponse; import org.apache.hadoop.hbase.regionserver.RSRpcServices; import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessChecker; import org.apache.hadoop.hbase.security.access.AccessController; import org.apache.hadoop.hbase.security.visibility.VisibilityController; import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils; @@ -1669,7 +1670,7 @@ public class MasterRpcServices extends RSRpcServices // The AccessController can provide AUTHORIZATION and CELL_AUTHORIZATION if (master.cpHost != null && master.cpHost.findCoprocessor(AccessController.class.getName()) != null) { - if (AccessController.isAuthorizationSupported(master.getConfiguration())) { + if (AccessChecker.isAuthorizationSupported(master.getConfiguration())) { capabilities.add(Capability.AUTHORIZATION); } if (AccessController.isCellAuthorizationSupported(master.getConfiguration())) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java new file mode 100644 index 0000000000..6afc96d868 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java @@ -0,0 +1,300 @@ +/* + * 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 java.io.IOException; +import java.net.InetAddress; +import java.util.Collection; +import java.util.Map; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.ipc.RpcServer; +import org.apache.hadoop.hbase.security.AccessDeniedException; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission.Action; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@InterfaceAudience.Private +public final class AccessChecker { + private static final Logger AUDITLOG = + LoggerFactory.getLogger("SecurityLogger." + AccessChecker.class.getName()); + private TableAuthManager authManager; + /** + * if we are active, usually false, only true if "hbase.security.authorization" + * has been set to true in site configuration.see HBASE-19483. + */ + private boolean authorizationEnabled; + + public static boolean isAuthorizationSupported(Configuration conf) { + return conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, false); + } + + /** + * Constructor with existing configuration + * + * @param conf Existing configuration to use + * @param zkw reference to the {@link ZooKeeperWatcher} + */ + public AccessChecker(final Configuration conf, final ZooKeeperWatcher zkw) + throws RuntimeException { + // If zk is null or IOException while obtaining auth manager, + // throw RuntimeException so that the coprocessor is unloaded. + if (zkw != null) { + try { + this.authManager = TableAuthManager.getOrCreate(zkw, conf); + } catch (IOException ioe) { + throw new RuntimeException("Error obtaining AccessChecker", ioe); + } + } else { + throw new NullPointerException("Error obtaining AccessChecker, zk found null."); + } + authorizationEnabled = isAuthorizationSupported(conf); + } + + public TableAuthManager getAuthManager() { + return authManager; + } + + public void logResult(AuthResult result) { + if (AUDITLOG.isTraceEnabled()) { + InetAddress remoteAddr = RpcServer.getRemoteAddress(); + AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + + " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") + + "; reason: " + result.getReason() + + "; remote address: " + (remoteAddr != null ? remoteAddr : "") + + "; request: " + result.getRequest() + + "; context: " + result.toContextString()); + } + } + + /** + * Authorizes that the current user has any of the given permissions for the + * given table, column family and column qualifier. + * @param tableName Table requested + * @param family Column family requested + * @param qualifier Column qualifier requested + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if user has no authorization + */ + public void requirePermission(User user, String request, TableName tableName, byte[] family, + byte[] qualifier, Action... permissions) throws IOException { + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.authorize(user, tableName, family, qualifier, permission)) { + result = AuthResult.allow(request, "Table permission granted", user, + permission, tableName, family, qualifier); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, tableName, family, qualifier); + } + } + logResult(result); + if (authorizationEnabled && !result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); + } + } + + /** + * Authorizes that the current user has any of the given permissions for the + * given table, column family and column qualifier. + * @param tableName Table requested + * @param family Column family param + * @param qualifier Column qualifier param + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if user has no authorization + */ + public void requireTablePermission(User user, String request, TableName tableName, byte[] family, + byte[] qualifier, Action... permissions) throws IOException { + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.authorize(user, tableName, null, null, permission)) { + result = AuthResult.allow(request, "Table permission granted", user, + permission, tableName, null, null); + result.getParams().setFamily(family).setQualifier(qualifier); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, tableName, family, qualifier); + result.getParams().setFamily(family).setQualifier(qualifier); + } + } + logResult(result); + if (authorizationEnabled && !result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); + } + } + + /** + * Authorizes that the current user has any of the given permissions to access the table. + * + * @param tableName Table requested + * @param permissions Actions being requested + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if user has no authorization + */ + public void requireAccess(User user, String request, TableName tableName, + Action... permissions) throws IOException { + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.hasAccess(user, tableName, permission)) { + result = AuthResult.allow(request, "Table permission granted", user, + permission, tableName, null, null); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, tableName, null, null); + } + } + logResult(result); + if (authorizationEnabled && !result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); + } + } + + /** + * Authorizes that the current user has global privileges for the given action. + * @param perm The action being requested + * @throws IOException if obtaining the current user fails + * @throws AccessDeniedException if authorization is denied + */ + public void requirePermission(User user, String request, Action perm) throws IOException { + requireGlobalPermission(user, request, perm, null, null); + } + + /** + * Checks that the user has the given global permission. The generated + * audit log message will contain context information for the operation + * being authorized, based on the given parameters. + * @param perm Action being requested + * @param tableName Affected table name. + * @param familyMap Affected column families. + */ + public void requireGlobalPermission(User user, String request, Action perm, TableName tableName, + Map> familyMap) throws IOException { + AuthResult result = null; + if (authManager.authorize(user, perm)) { + result = AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap); + result.getParams().setTableName(tableName).setFamilies(familyMap); + logResult(result); + } else { + result = AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap); + result.getParams().setTableName(tableName).setFamilies(familyMap); + logResult(result); + if (authorizationEnabled) { + throw new AccessDeniedException("Insufficient permissions for user '" + + (user != null ? user.getShortName() : "null") +"' (global, action=" + + perm.toString() + ")"); + } + } + } + + /** + * Checks that the user has the given global permission. The generated + * audit log message will contain context information for the operation + * being authorized, based on the given parameters. + * @param perm Action being requested + * @param namespace The given namespace + */ + public void requireGlobalPermission(User user, String request, Action perm, + String namespace) throws IOException { + AuthResult authResult = null; + if (authManager.authorize(user, perm)) { + authResult = AuthResult.allow(request, "Global check allowed", user, perm, null); + authResult.getParams().setNamespace(namespace); + logResult(authResult); + } else { + authResult = AuthResult.deny(request, "Global check failed", user, perm, null); + authResult.getParams().setNamespace(namespace); + logResult(authResult); + if (authorizationEnabled) { + throw new AccessDeniedException("Insufficient permissions for user '" + + (user != null ? user.getShortName() : "null") +"' (global, action=" + + perm.toString() + ")"); + } + } + } + + /** + * Checks that the user has the given global or namespace permission. + * @param namespace The given namespace + * @param permissions Actions being requested + */ + public void requireNamespacePermission(User user, String request, String namespace, + Action... permissions) throws IOException { + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.authorize(user, namespace, permission)) { + result = AuthResult.allow(request, "Namespace permission granted", + user, permission, namespace); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, namespace); + } + } + logResult(result); + if (authorizationEnabled && !result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + + result.toContextString()); + } + } + + /** + * Checks that the user has the given global or namespace permission. + * @param namespace The given namespace + * @param permissions Actions being requested + */ + public void requireNamespacePermission(User user, String request, String namespace, TableName tableName, + Map> familyMap, Action... permissions) + throws IOException { + AuthResult result = null; + + for (Action permission : permissions) { + if (authManager.authorize(user, namespace, permission)) { + result = AuthResult.allow(request, "Namespace permission granted", + user, permission, namespace); + result.getParams().setTableName(tableName).setFamilies(familyMap); + break; + } else { + // rest of the world + result = AuthResult.deny(request, "Insufficient permissions", user, + permission, namespace); + result.getParams().setTableName(tableName).setFamilies(familyMap); + } + } + logResult(result); + if (authorizationEnabled && !result.isAllowed()) { + throw new AccessDeniedException("Insufficient permissions " + + result.toContextString()); + } + } +} \ No newline at end of file diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java index a2ac927e0f..57c0f7b40c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessControlLists.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; @@ -510,7 +511,8 @@ public class AccessControlLists { return getPermissions(conf, tableName != null ? tableName.getName() : null, null); } - static ListMultimap getNamespacePermissions(Configuration conf, + @VisibleForTesting + public static ListMultimap getNamespacePermissions(Configuration conf, String namespace) throws IOException { return getPermissions(conf, Bytes.toBytes(toNamespaceEntry(namespace)), null); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java index b06b2bf5aa..97b3456766 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java @@ -177,10 +177,10 @@ public class AccessController extends BaseMasterAndRegionObserver private static final String TAG_CHECK_PASSED = "tag_check_passed"; private static final byte[] TRUE = Bytes.toBytes(true); - TableAuthManager authManager = null; + private AccessChecker accessChecker; /** flags if we are running on a region of the _acl_ table */ - boolean aclRegion = false; + private boolean aclRegion = false; /** defined only for Endpoint implementation, so it can have way to access region services */ @@ -195,19 +195,19 @@ public class AccessController extends BaseMasterAndRegionObserver /** Provider for mapping principal names to Users */ private UserProvider userProvider; - /** if we are active, usually true, only not true if "hbase.security.authorization" - has been set to false in site configuration */ - boolean authorizationEnabled; + /** if we are active, usually false, only true if "hbase.security.authorization" + has been set to true in site configuration */ + private boolean authorizationEnabled; /** if we are able to support cell ACLs */ - boolean cellFeaturesEnabled; + private boolean cellFeaturesEnabled; /** if we should check EXEC permissions */ - boolean shouldCheckExecPermission; + private boolean shouldCheckExecPermission; /** if we should terminate access checks early as soon as table or CF grants allow access; pre-0.98 compatible behavior */ - boolean compatibleEarlyTermination; + private boolean compatibleEarlyTermination; /** if we have been successfully initialized */ private volatile boolean initialized = false; @@ -215,12 +215,8 @@ public class AccessController extends BaseMasterAndRegionObserver /** if the ACL table is available, only relevant in the master */ private volatile boolean aclTabAvailable = false; - public static boolean isAuthorizationSupported(Configuration conf) { - return conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); - } - public static boolean isCellAuthorizationSupported(Configuration conf) { - return isAuthorizationSupported(conf) && + return AccessChecker.isAuthorizationSupported(conf) && (HFile.getFormatVersion(conf) >= HFile.MIN_FORMAT_VERSION_WITH_TAGS); } @@ -229,10 +225,10 @@ public class AccessController extends BaseMasterAndRegionObserver } public TableAuthManager getAuthManager() { - return authManager; + return accessChecker.getAuthManager(); } - void initialize(RegionCoprocessorEnvironment e) throws IOException { + private void initialize(RegionCoprocessorEnvironment e) throws IOException { final Region region = e.getRegion(); Configuration conf = e.getConfiguration(); Map> tables = @@ -244,7 +240,7 @@ public class AccessController extends BaseMasterAndRegionObserver byte[] entry = t.getKey(); ListMultimap perms = t.getValue(); byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf); - this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized); + getAuthManager().getZKPermissionWatcher().writeToZookeeper(entry, serialized); } initialized = true; } @@ -254,7 +250,7 @@ public class AccessController extends BaseMasterAndRegionObserver * znodes. This is called to synchronize ACL changes following {@code _acl_} * table updates. */ - void updateACL(RegionCoprocessorEnvironment e, + private void updateACL(RegionCoprocessorEnvironment e, final Map> familyMap) { Set entries = new TreeSet(Bytes.BYTES_RAWCOMPARATOR); @@ -268,7 +264,7 @@ public class AccessController extends BaseMasterAndRegionObserver } } } - ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher(); + ZKPermissionWatcher zkw = getAuthManager().getZKPermissionWatcher(); Configuration conf = regionEnv.getConfiguration(); for (byte[] entry: entries) { try { @@ -320,7 +316,7 @@ public class AccessController extends BaseMasterAndRegionObserver } // 2. check for the table-level, if successful we can short-circuit - if (authManager.authorize(user, tableName, (byte[])null, permRequest)) { + if (getAuthManager().authorize(user, tableName, (byte[])null, permRequest)) { return AuthResult.allow(request, "Table permission granted", user, permRequest, tableName, families); } @@ -330,7 +326,7 @@ public class AccessController extends BaseMasterAndRegionObserver // all families must pass for (Map.Entry> family : families.entrySet()) { // a) check for family level access - if (authManager.authorize(user, tableName, family.getKey(), + if (getAuthManager().authorize(user, tableName, family.getKey(), permRequest)) { continue; // family-level permission overrides per-qualifier } @@ -341,7 +337,7 @@ public class AccessController extends BaseMasterAndRegionObserver // for each qualifier of the family Set familySet = (Set)family.getValue(); for (byte[] qualifier : familySet) { - if (!authManager.authorize(user, tableName, family.getKey(), + if (!getAuthManager().authorize(user, tableName, family.getKey(), qualifier, permRequest)) { return AuthResult.deny(request, "Failed qualifier check", user, permRequest, tableName, makeFamilyMap(family.getKey(), qualifier)); @@ -350,7 +346,7 @@ public class AccessController extends BaseMasterAndRegionObserver } else if (family.getValue() instanceof List) { // List List kvList = (List)family.getValue(); for (KeyValue kv : kvList) { - if (!authManager.authorize(user, tableName, family.getKey(), + if (!getAuthManager().authorize(user, tableName, family.getKey(), kv.getQualifier(), permRequest)) { return AuthResult.deny(request, "Failed qualifier check", user, permRequest, tableName, makeFamilyMap(family.getKey(), kv.getQualifier())); @@ -397,18 +393,6 @@ public class AccessController extends BaseMasterAndRegionObserver return result; } - private void logResult(AuthResult result) { - if (AUDITLOG.isTraceEnabled()) { - InetAddress remoteAddr = RpcServer.getRemoteAddress(); - AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + - " for user " + (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") + - "; reason: " + result.getReason() + - "; remote address: " + (remoteAddr != null ? remoteAddr : "") + - "; request: " + result.getRequest() + - "; context: " + result.toContextString()); - } - } - /** * Returns the active user to which authorization checks should be applied. * If we are in the context of an RPC call, the remote user is used, @@ -424,224 +408,6 @@ public class AccessController extends BaseMasterAndRegionObserver } /** - * Authorizes that the current user has any of the given permissions for the - * given table, column family and column qualifier. - * @param tableName Table requested - * @param family Column family requested - * @param qualifier Column qualifier requested - * @throws IOException if obtaining the current user fails - * @throws AccessDeniedException if user has no authorization - */ - private void requirePermission(String request, TableName tableName, byte[] family, - byte[] qualifier, Action... permissions) throws IOException { - User user = getActiveUser(); - AuthResult result = null; - - for (Action permission : permissions) { - if (authManager.authorize(user, tableName, family, qualifier, permission)) { - result = AuthResult.allow(request, "Table permission granted", user, - permission, tableName, family, qualifier); - break; - } else { - // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", user, - permission, tableName, family, qualifier); - } - } - logResult(result); - if (authorizationEnabled && !result.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); - } - } - - /** - * Authorizes that the current user has any of the given permissions for the - * given table, column family and column qualifier. - * @param tableName Table requested - * @param family Column family param - * @param qualifier Column qualifier param - * @throws IOException if obtaining the current user fails - * @throws AccessDeniedException if user has no authorization - */ - private void requireTablePermission(String request, TableName tableName, byte[] family, - byte[] qualifier, Action... permissions) throws IOException { - User user = getActiveUser(); - AuthResult result = null; - - for (Action permission : permissions) { - if (authManager.authorize(user, tableName, null, null, permission)) { - result = AuthResult.allow(request, "Table permission granted", user, - permission, tableName, null, null); - result.getParams().setFamily(family).setQualifier(qualifier); - break; - } else { - // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", user, - permission, tableName, family, qualifier); - result.getParams().setFamily(family).setQualifier(qualifier); - } - } - logResult(result); - if (authorizationEnabled && !result.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); - } - } - - /** - * Authorizes that the current user has any of the given permissions to access the table. - * - * @param tableName Table requested - * @param permissions Actions being requested - * @throws IOException if obtaining the current user fails - * @throws AccessDeniedException if user has no authorization - */ - private void requireAccess(String request, TableName tableName, - Action... permissions) throws IOException { - User user = getActiveUser(); - AuthResult result = null; - - for (Action permission : permissions) { - if (authManager.hasAccess(user, tableName, permission)) { - result = AuthResult.allow(request, "Table permission granted", user, - permission, tableName, null, null); - break; - } else { - // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", user, - permission, tableName, null, null); - } - } - logResult(result); - if (authorizationEnabled && !result.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions " + result.toContextString()); - } - } - - /** - * Authorizes that the current user has global privileges for the given action. - * @param perm The action being requested - * @throws IOException if obtaining the current user fails - * @throws AccessDeniedException if authorization is denied - */ - private void requirePermission(String request, Action perm) throws IOException { - requireGlobalPermission(request, perm, null, null); - } - - /** - * Checks that the user has the given global permission. The generated - * audit log message will contain context information for the operation - * being authorized, based on the given parameters. - * @param perm Action being requested - * @param tableName Affected table name. - * @param familyMap Affected column families. - */ - private void requireGlobalPermission(String request, Action perm, TableName tableName, - Map> familyMap) throws IOException { - User user = getActiveUser(); - AuthResult result = null; - if (authManager.authorize(user, perm)) { - result = AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap); - result.getParams().setTableName(tableName).setFamilies(familyMap); - logResult(result); - } else { - result = AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap); - result.getParams().setTableName(tableName).setFamilies(familyMap); - logResult(result); - if (authorizationEnabled) { - throw new AccessDeniedException("Insufficient permissions for user '" + - (user != null ? user.getShortName() : "null") +"' (global, action=" + - perm.toString() + ")"); - } - } - } - - /** - * Checks that the user has the given global permission. The generated - * audit log message will contain context information for the operation - * being authorized, based on the given parameters. - * @param perm Action being requested - * @param namespace - */ - private void requireGlobalPermission(String request, Action perm, - String namespace) throws IOException { - User user = getActiveUser(); - AuthResult authResult = null; - if (authManager.authorize(user, perm)) { - authResult = AuthResult.allow(request, "Global check allowed", user, perm, null); - authResult.getParams().setNamespace(namespace); - logResult(authResult); - } else { - authResult = AuthResult.deny(request, "Global check failed", user, perm, null); - authResult.getParams().setNamespace(namespace); - logResult(authResult); - if (authorizationEnabled) { - throw new AccessDeniedException("Insufficient permissions for user '" + - (user != null ? user.getShortName() : "null") +"' (global, action=" + - perm.toString() + ")"); - } - } - } - - /** - * Checks that the user has the given global or namespace permission. - * @param namespace - * @param permissions Actions being requested - */ - public void requireNamespacePermission(String request, String namespace, - Action... permissions) throws IOException { - User user = getActiveUser(); - AuthResult result = null; - - for (Action permission : permissions) { - if (authManager.authorize(user, namespace, permission)) { - result = AuthResult.allow(request, "Namespace permission granted", - user, permission, namespace); - break; - } else { - // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", user, - permission, namespace); - } - } - logResult(result); - if (authorizationEnabled && !result.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions " - + result.toContextString()); - } - } - - /** - * Checks that the user has the given global or namespace permission. - * @param namespace - * @param permissions Actions being requested - */ - public void requireNamespacePermission(String request, String namespace, TableName tableName, - Map> familyMap, Action... permissions) - throws IOException { - User user = getActiveUser(); - AuthResult result = null; - - for (Action permission : permissions) { - if (authManager.authorize(user, namespace, permission)) { - result = AuthResult.allow(request, "Namespace permission granted", - user, permission, namespace); - result.getParams().setTableName(tableName).setFamilies(familyMap); - break; - } else { - // rest of the world - result = AuthResult.deny(request, "Insufficient permissions", user, - permission, namespace); - result.getParams().setTableName(tableName).setFamilies(familyMap); - } - } - logResult(result); - if (authorizationEnabled && !result.isAllowed()) { - throw new AccessDeniedException("Insufficient permissions " - + result.toContextString()); - } - } - - /** * Returns true if the current user is allowed the given action * over at least one of the column qualifiers in the given column families. */ @@ -663,13 +429,13 @@ public class AccessController extends BaseMasterAndRegionObserver familyMap.entrySet()) { if (family.getValue() != null && !family.getValue().isEmpty()) { for (byte[] qualifier : family.getValue()) { - if (authManager.matchPermission(user, tableName, + if (getAuthManager().matchPermission(user, tableName, family.getKey(), qualifier, perm)) { return true; } } } else { - if (authManager.matchPermission(user, tableName, family.getKey(), + if (getAuthManager().matchPermission(user, tableName, family.getKey(), perm)) { return true; } @@ -861,7 +627,7 @@ public class AccessController extends BaseMasterAndRegionObserver foundColumn = true; for (Action action: actions) { // Are there permissions for this user for the cell? - if (!authManager.authorize(user, getTableName(e), cell, action)) { + if (!getAuthManager().authorize(user, getTableName(e), cell, action)) { // We can stop if the cell ACL denies access return false; } @@ -942,7 +708,7 @@ public class AccessController extends BaseMasterAndRegionObserver CompoundConfiguration conf = new CompoundConfiguration(); conf.add(env.getConfiguration()); - authorizationEnabled = isAuthorizationSupported(conf); + authorizationEnabled = AccessChecker.isAuthorizationSupported(conf); if (!authorizationEnabled) { LOG.warn("The AccessController has been loaded with authorization checks disabled."); } @@ -976,26 +742,14 @@ public class AccessController extends BaseMasterAndRegionObserver // set the user-provider. this.userProvider = UserProvider.instantiate(env.getConfiguration()); - - // If zk is null or IOException while obtaining auth manager, - // throw RuntimeException so that the coprocessor is unloaded. - if (zk != null) { - try { - this.authManager = TableAuthManager.getOrCreate(zk, env.getConfiguration()); - } catch (IOException ioe) { - throw new RuntimeException("Error obtaining TableAuthManager", ioe); - } - } else { - throw new RuntimeException("Error obtaining TableAuthManager, zk found null."); - } - + accessChecker = new AccessChecker(env.getConfiguration(), zk); tableAcls = new MapMaker().weakValues().makeMap(); } @Override public void stop(CoprocessorEnvironment env) { - if (this.authManager != null) { - TableAuthManager.release(authManager); + if (getAuthManager()!= null) { + TableAuthManager.release(getAuthManager()); } } @@ -1007,7 +761,7 @@ public class AccessController extends BaseMasterAndRegionObserver for (byte[] family: families) { familyMap.put(family, null); } - requireNamespacePermission("createTable", desc.getTableName().getNamespaceAsString(), + accessChecker.requireNamespacePermission(getActiveUser(),"createTable", desc.getTableName().getNamespaceAsString(), desc.getTableName(), familyMap, Action.CREATE); } @@ -1059,7 +813,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preDeleteTable(ObserverContext c, TableName tableName) throws IOException { - requirePermission("deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"deleteTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1074,13 +828,13 @@ public class AccessController extends BaseMasterAndRegionObserver return null; } }); - this.authManager.getZKPermissionWatcher().deleteTableACLNode(tableName); + getAuthManager().getZKPermissionWatcher().deleteTableACLNode(tableName); } @Override public void preTruncateTable(ObserverContext c, final TableName tableName) throws IOException { - requirePermission("truncateTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"truncateTable", tableName, null, null, Action.ADMIN, Action.CREATE); final Configuration conf = c.getEnvironment().getConfiguration(); User.runAsLoginUser(new PrivilegedExceptionAction() { @@ -1118,7 +872,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preModifyTable(ObserverContext c, TableName tableName, HTableDescriptor htd) throws IOException { - requirePermission("modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"modifyTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1143,21 +897,21 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preAddColumn(ObserverContext c, TableName tableName, HColumnDescriptor column) throws IOException { - requireTablePermission("addColumn", tableName, column.getName(), null, Action.ADMIN, + accessChecker.requireTablePermission(getActiveUser(),"addColumn", tableName, column.getName(), null, Action.ADMIN, Action.CREATE); } @Override public void preModifyColumn(ObserverContext c, TableName tableName, HColumnDescriptor descriptor) throws IOException { - requirePermission("modifyColumn", tableName, descriptor.getName(), null, Action.ADMIN, + accessChecker.requirePermission(getActiveUser(),"modifyColumn", tableName, descriptor.getName(), null, Action.ADMIN, Action.CREATE); } @Override public void preDeleteColumn(ObserverContext c, TableName tableName, byte[] col) throws IOException { - requirePermission("deleteColumn", tableName, col, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"deleteColumn", tableName, col, null, Action.ADMIN, Action.CREATE); } @Override @@ -1177,7 +931,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preEnableTable(ObserverContext c, TableName tableName) throws IOException { - requirePermission("enableTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"enableTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1191,7 +945,7 @@ public class AccessController extends BaseMasterAndRegionObserver throw new AccessDeniedException("Not allowed to disable " + AccessControlLists.ACL_TABLE_NAME + " table with AccessController installed"); } - requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"disableTable", tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1202,7 +956,7 @@ public class AccessController extends BaseMasterAndRegionObserver if (!procEnv.isProcedureOwner(procId, getActiveUser())) { // If the user is not the procedure owner, then we should further probe whether // he can abort the procedure. - requirePermission("abortProcedure", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"abortProcedure", Action.ADMIN); } } @@ -1237,7 +991,7 @@ public class AccessController extends BaseMasterAndRegionObserver if (!ProcedureInfo.isProcedureOwner(procInfo, user)) { // If the user is not the procedure owner, then we should further probe whether // he can see the procedure. - requirePermission("listProcedures", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"listProcedures", Action.ADMIN); } } catch (AccessDeniedException e) { itr.remove(); @@ -1248,31 +1002,31 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preMove(ObserverContext c, HRegionInfo region, ServerName srcServer, ServerName destServer) throws IOException { - requirePermission("move", region.getTable(), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"move", region.getTable(), null, null, Action.ADMIN); } @Override public void preAssign(ObserverContext c, HRegionInfo regionInfo) throws IOException { - requirePermission("assign", regionInfo.getTable(), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"assign", regionInfo.getTable(), null, null, Action.ADMIN); } @Override public void preUnassign(ObserverContext c, HRegionInfo regionInfo, boolean force) throws IOException { - requirePermission("unassign", regionInfo.getTable(), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"unassign", regionInfo.getTable(), null, null, Action.ADMIN); } @Override public void preRegionOffline(ObserverContext c, HRegionInfo regionInfo) throws IOException { - requirePermission("regionOffline", regionInfo.getTable(), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"regionOffline", regionInfo.getTable(), null, null, Action.ADMIN); } @Override public boolean preSetSplitOrMergeEnabled(final ObserverContext ctx, final boolean newValue, final Admin.MasterSwitchType switchType) throws IOException { - requirePermission("setSplitOrMergeEnabled", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setSplitOrMergeEnabled", Action.ADMIN); return false; } @@ -1284,26 +1038,26 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preBalance(ObserverContext c) throws IOException { - requirePermission("balance", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"balance", Action.ADMIN); } @Override public boolean preBalanceSwitch(ObserverContext c, boolean newValue) throws IOException { - requirePermission("balanceSwitch", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"balanceSwitch", Action.ADMIN); return newValue; } @Override public void preShutdown(ObserverContext c) throws IOException { - requirePermission("shutdown", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"shutdown", Action.ADMIN); } @Override public void preStopMaster(ObserverContext c) throws IOException { - requirePermission("stopMaster", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"stopMaster", Action.ADMIN); } @Override @@ -1322,7 +1076,7 @@ public class AccessController extends BaseMasterAndRegionObserver public void preSnapshot(final ObserverContext ctx, final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor) throws IOException { - requirePermission("snapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, + accessChecker.requirePermission(getActiveUser(),"snapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN); } @@ -1334,9 +1088,9 @@ public class AccessController extends BaseMasterAndRegionObserver // list it, if user is the owner of snapshot AuthResult result = AuthResult.allow("listSnapshot " + snapshot.getName(), "Snapshot owner check allowed", user, null, null, null); - logResult(result); + accessChecker.logResult(result); } else { - requirePermission("listSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"listSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1350,9 +1104,9 @@ public class AccessController extends BaseMasterAndRegionObserver // Snapshot owner is allowed to create a table with the same name as the snapshot he took AuthResult result = AuthResult.allow("cloneSnapshot " + snapshot.getName(), "Snapshot owner check allowed", user, null, hTableDescriptor.getTableName(), null); - logResult(result); + accessChecker.logResult(result); } else { - requirePermission("cloneSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"cloneSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1361,10 +1115,10 @@ public class AccessController extends BaseMasterAndRegionObserver final SnapshotDescription snapshot, final HTableDescriptor hTableDescriptor) throws IOException { if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, getActiveUser())) { - requirePermission("restoreSnapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, + accessChecker.requirePermission(getActiveUser(),"restoreSnapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN); } else { - requirePermission("restoreSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"restoreSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1376,22 +1130,22 @@ public class AccessController extends BaseMasterAndRegionObserver // Snapshot owner is allowed to delete the snapshot AuthResult result = AuthResult.allow("deleteSnapshot " + snapshot.getName(), "Snapshot owner check allowed", user, null, null, null); - logResult(result); + accessChecker.logResult(result); } else { - requirePermission("deleteSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"deleteSnapshot " + snapshot.getName(), Action.ADMIN); } } @Override public void preCreateNamespace(ObserverContext ctx, NamespaceDescriptor ns) throws IOException { - requireGlobalPermission("createNamespace", Action.ADMIN, ns.getName()); + accessChecker.requireGlobalPermission(getActiveUser(),"createNamespace", Action.ADMIN, ns.getName()); } @Override public void preDeleteNamespace(ObserverContext ctx, String namespace) throws IOException { - requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace); + accessChecker.requireGlobalPermission(getActiveUser(),"deleteNamespace", Action.ADMIN, namespace); } @Override @@ -1406,7 +1160,7 @@ public class AccessController extends BaseMasterAndRegionObserver return null; } }); - this.authManager.getZKPermissionWatcher().deleteNamespaceACLNode(namespace); + getAuthManager().getZKPermissionWatcher().deleteNamespaceACLNode(namespace); LOG.info(namespace + " entry deleted in " + AccessControlLists.ACL_TABLE_NAME + " table."); } @@ -1415,13 +1169,13 @@ public class AccessController extends BaseMasterAndRegionObserver NamespaceDescriptor ns) throws IOException { // We require only global permission so that // a user with NS admin cannot altering namespace configurations. i.e. namespace quota - requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName()); + accessChecker.requireGlobalPermission(getActiveUser(),"modifyNamespace", Action.ADMIN, ns.getName()); } @Override public void preGetNamespaceDescriptor(ObserverContext ctx, String namespace) throws IOException { - requireNamespacePermission("getNamespaceDescriptor", namespace, Action.ADMIN); + accessChecker.requireNamespacePermission(getActiveUser(),"getNamespaceDescriptor", namespace, Action.ADMIN); } @Override @@ -1433,7 +1187,7 @@ public class AccessController extends BaseMasterAndRegionObserver while (itr.hasNext()) { NamespaceDescriptor desc = itr.next(); try { - requireNamespacePermission("listNamespaces", desc.getName(), Action.ADMIN); + accessChecker.requireNamespacePermission(getActiveUser(),"listNamespaces", desc.getName(), Action.ADMIN); } catch (AccessDeniedException e) { itr.remove(); } @@ -1443,7 +1197,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preTableFlush(final ObserverContext ctx, final TableName tableName) throws IOException { - requirePermission("flushTable", tableName, null, null, Action.ADMIN, Action.CREATE); + accessChecker.requirePermission(getActiveUser(),"flushTable", tableName, null, null, Action.ADMIN, Action.CREATE); } /* ---- RegionObserver implementation ---- */ @@ -1460,7 +1214,7 @@ public class AccessController extends BaseMasterAndRegionObserver if (regionInfo.getTable().isSystemTable()) { checkSystemOrSuperUser(); } else { - requirePermission("preOpen", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"preOpen", Action.ADMIN); } } } @@ -1505,26 +1259,26 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preFlush(ObserverContext e) throws IOException { - requirePermission("flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN, + accessChecker.requirePermission(getActiveUser(),"flush", getTableName(e.getEnvironment()), null, null, Action.ADMIN, Action.CREATE); } @Override public void preSplit(ObserverContext e) throws IOException { - requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"split", getTableName(e.getEnvironment()), null, null, Action.ADMIN); } @Override public void preSplit(ObserverContext e, byte[] splitRow) throws IOException { - requirePermission("split", getTableName(e.getEnvironment()), null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"split", getTableName(e.getEnvironment()), null, null, Action.ADMIN); } @Override public InternalScanner preCompact(ObserverContext e, final Store store, final InternalScanner scanner, final ScanType scanType) throws IOException { - requirePermission("compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN, + accessChecker.requirePermission(getActiveUser(),"compact", getTableName(e.getEnvironment()), null, null, Action.ADMIN, Action.CREATE); return scanner; } @@ -1544,7 +1298,7 @@ public class AccessController extends BaseMasterAndRegionObserver families, HConstants.LATEST_TIMESTAMP, Action.READ)); authResult.setReason("Covering cell set"); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -1594,7 +1348,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult.setReason("Access allowed with filter"); // Only wrap the filter if we are enforcing authorizations if (authorizationEnabled) { - Filter ourFilter = new AccessControlFilter(authManager, user, table, + Filter ourFilter = new AccessControlFilter(getAuthManager(), user, table, AccessControlFilter.Strategy.CHECK_TABLE_AND_CF_ONLY, cfVsMaxVersions); // wrap any existing filter @@ -1624,7 +1378,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult.setReason("Access allowed with filter"); // Only wrap the filter if we are enforcing authorizations if (authorizationEnabled) { - Filter ourFilter = new AccessControlFilter(authManager, user, table, + Filter ourFilter = new AccessControlFilter(getAuthManager(), user, table, AccessControlFilter.Strategy.CHECK_CELL_DEFAULT, cfVsMaxVersions); // wrap any existing filter if (filter != null) { @@ -1646,7 +1400,7 @@ public class AccessController extends BaseMasterAndRegionObserver } } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") @@ -1683,7 +1437,7 @@ public class AccessController extends BaseMasterAndRegionObserver RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = put.getFamilyCellMap(); AuthResult authResult = permissionGranted(OpType.PUT, user, env, families, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { put.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1728,7 +1482,7 @@ public class AccessController extends BaseMasterAndRegionObserver Map> families = delete.getFamilyCellMap(); User user = getActiveUser(); AuthResult authResult = permissionGranted(OpType.DELETE, user, env, families, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { delete.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1765,7 +1519,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult = AuthResult.deny(opType.toString(), "Covering cell set", getActiveUser(), Action.WRITE, table, m.getFamilyCellMap()); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -1798,7 +1552,7 @@ public class AccessController extends BaseMasterAndRegionObserver Map> families = makeFamilyMap(family, qualifier); AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families, Action.READ, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { put.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1838,7 +1592,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), "Covering cell set", getActiveUser(), Action.READ, table, families); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } @@ -1864,7 +1618,7 @@ public class AccessController extends BaseMasterAndRegionObserver User user = getActiveUser(); AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families, Action.READ, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { delete.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1896,7 +1650,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), "Covering cell set", getActiveUser(), Action.READ, table, families); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } @@ -1921,7 +1675,7 @@ public class AccessController extends BaseMasterAndRegionObserver families, HConstants.LATEST_TIMESTAMP, Action.WRITE)); authResult.setReason("Covering cell set"); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } @@ -1938,7 +1692,7 @@ public class AccessController extends BaseMasterAndRegionObserver RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = append.getFamilyCellMap(); AuthResult authResult = permissionGranted(OpType.APPEND, user, env, families, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { append.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1976,7 +1730,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult = AuthResult.deny(OpType.APPEND.toString(), "Covering cell set", getActiveUser(), Action.WRITE, table, append.getFamilyCellMap()); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -1998,7 +1752,7 @@ public class AccessController extends BaseMasterAndRegionObserver Map> families = increment.getFamilyCellMap(); AuthResult authResult = permissionGranted(OpType.INCREMENT, user, env, families, Action.WRITE); - logResult(authResult); + accessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { increment.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -2036,7 +1790,7 @@ public class AccessController extends BaseMasterAndRegionObserver authResult = AuthResult.deny(OpType.INCREMENT.toString(), "Covering cell set", getActiveUser(), Action.WRITE, table, increment.getFamilyCellMap()); } - logResult(authResult); + accessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -2178,7 +1932,7 @@ public class AccessController extends BaseMasterAndRegionObserver public void preBulkLoadHFile(ObserverContext ctx, List> familyPaths) throws IOException { for(Pair el : familyPaths) { - requirePermission("preBulkLoadHFile", + accessChecker.requirePermission(getActiveUser(),"preBulkLoadHFile", ctx.getEnvironment().getRegion().getTableDesc().getTableName(), el.getFirst(), null, @@ -2196,7 +1950,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void prePrepareBulkLoad(ObserverContext ctx, PrepareBulkLoadRequest request) throws IOException { - requireAccess("prePareBulkLoad", + accessChecker.requireAccess(getActiveUser(),"prePareBulkLoad", ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE); } @@ -2210,7 +1964,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preCleanupBulkLoad(ObserverContext ctx, CleanupBulkLoadRequest request) throws IOException { - requireAccess("preCleanupBulkLoad", + accessChecker.requireAccess(getActiveUser(),"preCleanupBulkLoad", ctx.getEnvironment().getRegion().getTableDesc().getTableName(), Action.CREATE); } @@ -2222,7 +1976,7 @@ public class AccessController extends BaseMasterAndRegionObserver // Don't intercept calls to our own AccessControlService, we check for // appropriate permissions in the service handlers if (shouldCheckExecPermission && !(service instanceof AccessControlService)) { - requirePermission("invoke(" + service.getDescriptorForType().getName() + "." + + accessChecker.requirePermission(getActiveUser(),"invoke(" + service.getDescriptorForType().getName() + "." + methodName + ")", getTableName(ctx.getEnvironment()), null, null, Action.EXEC); @@ -2256,11 +2010,11 @@ public class AccessController extends BaseMasterAndRegionObserver switch(request.getUserPermission().getPermission().getType()) { case Global : case Table : - requirePermission("grant", perm.getTableName(), perm.getFamily(), + accessChecker.requirePermission(getActiveUser(),"grant", perm.getTableName(), perm.getFamily(), perm.getQualifier(), Action.ADMIN); break; case Namespace : - requireNamespacePermission("grant", perm.getNamespace(), Action.ADMIN); + accessChecker.requireNamespacePermission(getActiveUser(),"grant", perm.getNamespace(), Action.ADMIN); break; } @@ -2309,11 +2063,11 @@ public class AccessController extends BaseMasterAndRegionObserver switch(request.getUserPermission().getPermission().getType()) { case Global : case Table : - requirePermission("revoke", perm.getTableName(), perm.getFamily(), + accessChecker.requirePermission(getActiveUser(),"revoke", perm.getTableName(), perm.getFamily(), perm.getQualifier(), Action.ADMIN); break; case Namespace : - requireNamespacePermission("revoke", perm.getNamespace(), Action.ADMIN); + accessChecker.requireNamespacePermission(getActiveUser(),"revoke", perm.getNamespace(), Action.ADMIN); break; } @@ -2356,7 +2110,7 @@ public class AccessController extends BaseMasterAndRegionObserver if (request.getType() == AccessControlProtos.Permission.Type.Table) { final TableName table = request.hasTableName() ? ProtobufUtil.toTableName(request.getTableName()) : null; - requirePermission("userPermissions", table, null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"userPermissions", table, null, null, Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2365,7 +2119,7 @@ public class AccessController extends BaseMasterAndRegionObserver }); } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) { final String namespace = request.getNamespaceName().toStringUtf8(); - requireNamespacePermission("userPermissions", namespace, Action.ADMIN); + accessChecker.requireNamespacePermission(getActiveUser(),"userPermissions", namespace, Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2374,7 +2128,7 @@ public class AccessController extends BaseMasterAndRegionObserver } }); } else { - requirePermission("userPermissions", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"userPermissions", Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2440,7 +2194,7 @@ public class AccessController extends BaseMasterAndRegionObserver AuthResult result = permissionGranted("checkPermissions", user, action, regionEnv, familyMap); - logResult(result); + accessChecker.logResult(result); if (!result.isAllowed()) { // Even if passive we need to throw an exception here, we support checking // effective permissions, so throw unconditionally @@ -2455,14 +2209,14 @@ public class AccessController extends BaseMasterAndRegionObserver for (Action action : permission.getActions()) { AuthResult result; - if (authManager.authorize(user, action)) { + if (getAuthManager().authorize(user, action)) { result = AuthResult.allow("checkPermissions", "Global action allowed", user, action, null, null); } else { result = AuthResult.deny("checkPermissions", "Global action denied", user, action, null, null); } - logResult(result); + accessChecker.logResult(result); if (!result.isAllowed()) { // Even if passive we need to throw an exception here, we support checking // effective permissions, so throw unconditionally @@ -2507,7 +2261,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preClose(ObserverContext e, boolean abortRequested) throws IOException { - requirePermission("preClose", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"preClose", Action.ADMIN); } private void checkSystemOrSuperUser() throws IOException { @@ -2526,7 +2280,7 @@ public class AccessController extends BaseMasterAndRegionObserver public void preStopRegionServer( ObserverContext env) throws IOException { - requirePermission("preStopRegionServer", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"preStopRegionServer", Action.ADMIN); } private Map> makeFamilyMap(byte[] family, @@ -2555,7 +2309,7 @@ public class AccessController extends BaseMasterAndRegionObserver if (masterServices.getTableDescriptors().get(tableName) == null) { continue; } - requirePermission("getTableDescriptors", tableName, null, null, + accessChecker.requirePermission(getActiveUser(),"getTableDescriptors", tableName, null, null, Action.ADMIN, Action.CREATE); } } @@ -2576,7 +2330,7 @@ public class AccessController extends BaseMasterAndRegionObserver while (itr.hasNext()) { HTableDescriptor htd = itr.next(); try { - requirePermission("getTableDescriptors", htd.getTableName(), null, null, + accessChecker.requirePermission(getActiveUser(),"getTableDescriptors", htd.getTableName(), null, null, Action.ADMIN, Action.CREATE); } catch (AccessDeniedException e) { itr.remove(); @@ -2592,7 +2346,7 @@ public class AccessController extends BaseMasterAndRegionObserver while (itr.hasNext()) { HTableDescriptor htd = itr.next(); try { - requireAccess("getTableNames", htd.getTableName(), Action.values()); + accessChecker.requireAccess(getActiveUser(),"getTableNames", htd.getTableName(), Action.values()); } catch (AccessDeniedException e) { itr.remove(); } @@ -2602,14 +2356,14 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preDispatchMerge(final ObserverContext ctx, HRegionInfo regionA, HRegionInfo regionB) throws IOException { - requirePermission("mergeRegions", regionA.getTable(), null, null, + accessChecker.requirePermission(getActiveUser(),"mergeRegions", regionA.getTable(), null, null, Action.ADMIN); } @Override public void preClearDeadServers(ObserverContext ctx) throws IOException { - requirePermission("clearDeadServers", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"clearDeadServers", Action.ADMIN); } @Override @@ -2619,7 +2373,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preMerge(ObserverContext ctx, Region regionA, Region regionB) throws IOException { - requirePermission("mergeRegions", regionA.getTableDesc().getTableName(), null, null, + accessChecker.requirePermission(getActiveUser(),"mergeRegions", regionA.getTableDesc().getTableName(), null, null, Action.ADMIN); } @@ -2646,7 +2400,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preRollWALWriterRequest(ObserverContext ctx) throws IOException { - requirePermission("preRollLogWriterRequest", Permission.Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"preRollLogWriterRequest", Permission.Action.ADMIN); } @Override @@ -2662,7 +2416,7 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preReplicateLogEntries(ObserverContext ctx, List entries, CellScanner cells) throws IOException { - requirePermission("replicateLogEntries", Action.WRITE); + accessChecker.requirePermission(getActiveUser(),"replicateLogEntries", Action.WRITE); } @Override @@ -2673,72 +2427,65 @@ public class AccessController extends BaseMasterAndRegionObserver @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final Quotas quotas) throws IOException { - requirePermission("setUserQuota", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setUserQuota", Action.ADMIN); } @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final TableName tableName, final Quotas quotas) throws IOException { - requirePermission("setUserTableQuota", tableName, null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setUserTableQuota", tableName, null, null, Action.ADMIN); } @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final String namespace, final Quotas quotas) throws IOException { - requirePermission("setUserNamespaceQuota", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setUserNamespaceQuota", Action.ADMIN); } @Override public void preSetTableQuota(final ObserverContext ctx, final TableName tableName, final Quotas quotas) throws IOException { - requirePermission("setTableQuota", tableName, null, null, Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setTableQuota", tableName, null, null, Action.ADMIN); } @Override public void preSetNamespaceQuota(final ObserverContext ctx, final String namespace, final Quotas quotas) throws IOException { - requirePermission("setNamespaceQuota", Action.ADMIN); + accessChecker.requirePermission(getActiveUser(),"setNamespaceQuota", Action.ADMIN); } @Override public void preMoveServersAndTables(ObserverContext ctx, Set
servers, Set tables, String targetGroup) throws IOException { - requirePermission("moveServersAndTables", Action.ADMIN); } @Override public void preMoveServers(ObserverContext ctx, Set
servers, String targetGroup) throws IOException { - requirePermission("moveServers", Action.ADMIN); } @Override public void preMoveTables(ObserverContext ctx, Set tables, String targetGroup) throws IOException { - requirePermission("moveTables", Action.ADMIN); } @Override public void preRemoveServers(ObserverContext ctx, Set
servers) throws IOException { - requirePermission("removeServers", Action.ADMIN); } @Override public void preAddRSGroup(ObserverContext ctx, String name) throws IOException { - requirePermission("addRSGroup", Action.ADMIN); } @Override public void preRemoveRSGroup(ObserverContext ctx, String name) throws IOException { - requirePermission("removeRSGroup", Action.ADMIN); } @Override public void preBalanceRSGroup(ObserverContext ctx, String groupName) throws IOException { - requirePermission("balanceRSGroup", Action.ADMIN); } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java index dd1059ccfb..a12757d1b8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/TableAuthManager.java @@ -758,7 +758,7 @@ public class TableAuthManager implements Closeable { } @VisibleForTesting - static int getTotalRefCount() { + public static int getTotalRefCount() { int total = 0; for (int count : refCount.values()) { total += count; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java index 0fa320777e..9692ecde25 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java @@ -101,6 +101,7 @@ import org.apache.hadoop.hbase.replication.ReplicationEndpoint; import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.Superusers; import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessChecker; import org.apache.hadoop.hbase.security.access.AccessController; import org.apache.hadoop.hbase.util.ByteStringer; import org.apache.hadoop.hbase.util.Bytes; @@ -139,8 +140,8 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements private VisibilityLabelService visibilityLabelService; // FindBugs: MT_CORRECTNESS FIX!!! - /** if we are active, usually true, only not true if "hbase.security.authorization" - has been set to false in site configuration */ + /** if we are active, usually false, only true if "hbase.security.authorization" + has been set to true in site configuration */ boolean authorizationEnabled; // Add to this list if there are any reserved tag types @@ -151,19 +152,15 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements RESERVED_VIS_TAG_TYPES.add(TagType.STRING_VIS_TAG_TYPE); } - public static boolean isAuthorizationSupported(Configuration conf) { - return conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); - } - public static boolean isCellAuthorizationSupported(Configuration conf) { - return isAuthorizationSupported(conf); + return AccessChecker.isAuthorizationSupported(conf); } @Override public void start(CoprocessorEnvironment env) throws IOException { this.conf = env.getConfiguration(); - authorizationEnabled = isAuthorizationSupported(conf); + authorizationEnabled = AccessChecker.isAuthorizationSupported(conf); if (!authorizationEnabled) { LOG.warn("The VisibilityController has been loaded with authorization checks disabled."); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java index 6426ec9afd..e00a31ec67 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java @@ -64,6 +64,7 @@ import org.apache.hadoop.hbase.security.visibility.SimpleScanLabelGenerator; import org.apache.hadoop.hbase.security.visibility.VisibilityClient; import org.apache.hadoop.hbase.security.visibility.VisibilityConstants; import org.apache.hadoop.hbase.security.visibility.VisibilityController; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.security.visibility.VisibilityUtils; import org.apache.hadoop.hbase.testclassification.LargeTests; import org.apache.hadoop.hbase.util.Bytes; @@ -117,9 +118,7 @@ public class TestImportTSVWithVisibilityLabels implements Configurable { conf = util.getConfiguration(); SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" }); conf.set("hbase.superuser", "admin,"+User.getCurrent().getName()); - conf.setInt("hfile.format.version", 3); - conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, ScanLabelGenerator.class); util.setJobWithoutMRCluster(); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java index 2c18a824dc..7a2671776b 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java @@ -104,6 +104,7 @@ public class SecureTestUtil { conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, AccessController.class.getName()); // Need HFile V3 for tags for security features conf.setInt(HFile.FORMAT_VERSION_KEY, 3); + conf.setBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); configureSuperuser(conf); } @@ -127,6 +128,11 @@ public class SecureTestUtil { if (conf.getInt(HFile.FORMAT_VERSION_KEY, 2) < HFile.MIN_FORMAT_VERSION_WITH_TAGS) { throw new RuntimeException("Post 0.96 security features require HFile version >= 3"); } + + if (!conf.getBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, false)) { + throw new RuntimeException("Post 1.5.0 security features require set " + + User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY + " to true"); + } } public static void checkTablePerms(Configuration conf, TableName table, byte[] family, byte[] column, diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java index 5071ca0e38..c1d6e8faa9 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/TestAccessController.java @@ -2967,81 +2967,6 @@ public class TestAccessController extends SecureTestUtil { } @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 testGetClusterStatus() throws Exception { AccessTestAction action = new AccessTestAction() { @Override diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/VisibilityTestUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/VisibilityTestUtil.java index 7dbe256146..4e2c4b7c30 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/VisibilityTestUtil.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/VisibilityTestUtil.java @@ -14,7 +14,7 @@ import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; - +import org.apache.hadoop.hbase.security.User; /** * Utility methods for testing visibility labels. @@ -23,6 +23,7 @@ public class VisibilityTestUtil { public static void enableVisiblityLabels(Configuration conf) throws IOException { conf.setInt("hfile.format.version", 3); + conf.setBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); appendCoprocessor(conf, CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, VisibilityController.class.getName()); appendCoprocessor(conf, CoprocessorHost.REGION_COPROCESSOR_CONF_KEY, diff --git a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift2/TestThriftHBaseServiceHandlerWithLabels.java b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift2/TestThriftHBaseServiceHandlerWithLabels.java index 999a8df163..9f2d70d700 100644 --- a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift2/TestThriftHBaseServiceHandlerWithLabels.java +++ b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift2/TestThriftHBaseServiceHandlerWithLabels.java @@ -35,6 +35,7 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Admin; @@ -73,390 +74,386 @@ import org.junit.experimental.categories.Category; @Category(MediumTests.class) public class TestThriftHBaseServiceHandlerWithLabels { -private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); - -// Static names for tables, columns, rows, and values -private static byte[] tableAname = Bytes.toBytes("tableA"); -private static byte[] familyAname = Bytes.toBytes("familyA"); -private static byte[] familyBname = Bytes.toBytes("familyB"); -private static byte[] qualifierAname = Bytes.toBytes("qualifierA"); -private static byte[] qualifierBname = Bytes.toBytes("qualifierB"); -private static byte[] valueAname = Bytes.toBytes("valueA"); -private static byte[] valueBname = Bytes.toBytes("valueB"); -private static HColumnDescriptor[] families = new HColumnDescriptor[] { - new HColumnDescriptor(familyAname).setMaxVersions(3), - new HColumnDescriptor(familyBname).setMaxVersions(2) }; - -private final static String TOPSECRET = "topsecret"; -private final static String PUBLIC = "public"; -private final static String PRIVATE = "private"; -private final static String CONFIDENTIAL = "confidential"; -private final static String SECRET = "secret"; -private static User SUPERUSER; - -private static Configuration conf; - -public void assertTColumnValuesEqual(List columnValuesA, - List columnValuesB) { - assertEquals(columnValuesA.size(), columnValuesB.size()); - Comparator comparator = new Comparator() { - @Override - public int compare(TColumnValue o1, TColumnValue o2) { - return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()), - Bytes.add(o2.getFamily(), o2.getQualifier())); + private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); + + // Static names for tables, columns, rows, and values + private static byte[] tableAname = Bytes.toBytes("tableA"); + private static byte[] familyAname = Bytes.toBytes("familyA"); + private static byte[] familyBname = Bytes.toBytes("familyB"); + private static byte[] qualifierAname = Bytes.toBytes("qualifierA"); + private static byte[] qualifierBname = Bytes.toBytes("qualifierB"); + private static byte[] valueAname = Bytes.toBytes("valueA"); + private static byte[] valueBname = Bytes.toBytes("valueB"); + private static HColumnDescriptor[] families = new HColumnDescriptor[] { + new HColumnDescriptor(familyAname).setMaxVersions(3), + new HColumnDescriptor(familyBname).setMaxVersions(2) }; + + private final static String TOPSECRET = "topsecret"; + private final static String PUBLIC = "public"; + private final static String PRIVATE = "private"; + private final static String CONFIDENTIAL = "confidential"; + private final static String SECRET = "secret"; + private static User SUPERUSER; + + private static Configuration conf; + + public void assertTColumnValuesEqual(List columnValuesA, + List columnValuesB) { + assertEquals(columnValuesA.size(), columnValuesB.size()); + Comparator comparator = new Comparator() { + @Override + public int compare(TColumnValue o1, TColumnValue o2) { + return Bytes.compareTo(Bytes.add(o1.getFamily(), o1.getQualifier()), + Bytes.add(o2.getFamily(), o2.getQualifier())); + } + }; + Collections.sort(columnValuesA, comparator); + Collections.sort(columnValuesB, comparator); + + for (int i = 0; i < columnValuesA.size(); i++) { + TColumnValue a = columnValuesA.get(i); + TColumnValue b = columnValuesB.get(i); + assertArrayEquals(a.getFamily(), b.getFamily()); + assertArrayEquals(a.getQualifier(), b.getQualifier()); + assertArrayEquals(a.getValue(), b.getValue()); } - }; - Collections.sort(columnValuesA, comparator); - Collections.sort(columnValuesB, comparator); - - for (int i = 0; i < columnValuesA.size(); i++) { - TColumnValue a = columnValuesA.get(i); - TColumnValue b = columnValuesB.get(i); - assertArrayEquals(a.getFamily(), b.getFamily()); - assertArrayEquals(a.getQualifier(), b.getQualifier()); - assertArrayEquals(a.getValue(), b.getValue()); } -} -@BeforeClass -public static void beforeClass() throws Exception { - SUPERUSER = User.createUserForTesting(conf, "admin", - new String[] { "supergroup" }); - conf = UTIL.getConfiguration(); - conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, - SimpleScanLabelGenerator.class, ScanLabelGenerator.class); - conf.set("hbase.superuser", SUPERUSER.getShortName()); - conf.set("hbase.coprocessor.master.classes", - VisibilityController.class.getName()); - conf.set("hbase.coprocessor.region.classes", - VisibilityController.class.getName()); - conf.setInt("hfile.format.version", 3); - UTIL.startMiniCluster(1); - // Wait for the labels table to become available - UTIL.waitTableEnabled(VisibilityConstants.LABELS_TABLE_NAME.getName(), 50000); - createLabels(); - Admin admin = new HBaseAdmin(UTIL.getConfiguration()); - HTableDescriptor tableDescriptor = new HTableDescriptor( - TableName.valueOf(tableAname)); - for (HColumnDescriptor family : families) { - tableDescriptor.addFamily(family); + @BeforeClass + public static void beforeClass() throws Exception { + SUPERUSER = User.createUserForTesting(conf, "admin", + new String[] { "supergroup" }); + conf = UTIL.getConfiguration(); + conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, + SimpleScanLabelGenerator.class, ScanLabelGenerator.class); + conf.set("hbase.superuser", SUPERUSER.getShortName()); + VisibilityTestUtil.enableVisiblityLabels(conf); + UTIL.startMiniCluster(1); + // Wait for the labels table to become available + UTIL.waitTableEnabled(VisibilityConstants.LABELS_TABLE_NAME.getName(), 50000); + createLabels(); + Admin admin = new HBaseAdmin(UTIL.getConfiguration()); + HTableDescriptor tableDescriptor = new HTableDescriptor( + TableName.valueOf(tableAname)); + for (HColumnDescriptor family : families) { + tableDescriptor.addFamily(family); + } + admin.createTable(tableDescriptor); + admin.close(); + setAuths(); } - admin.createTable(tableDescriptor); - admin.close(); - setAuths(); -} -private static void createLabels() throws IOException, InterruptedException { - PrivilegedExceptionAction action = new PrivilegedExceptionAction() { - public VisibilityLabelsResponse run() throws Exception { - String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; - try (Connection conn = ConnectionFactory.createConnection(conf)) { - VisibilityClient.addLabels(conn, labels); - } catch (Throwable t) { - throw new IOException(t); + private static void createLabels() throws IOException, InterruptedException { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public VisibilityLabelsResponse run() throws Exception { + String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; + try (Connection conn = ConnectionFactory.createConnection(conf)) { + VisibilityClient.addLabels(conn, labels); + } catch (Throwable t) { + throw new IOException(t); + } + return null; } - return null; + }; + SUPERUSER.runAs(action); + } + + private static void setAuths() throws IOException { + String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; + try { + VisibilityClient.setAuths(UTIL.getConnection(), labels, User.getCurrent().getShortName()); + } catch (Throwable t) { + throw new IOException(t); } - }; - SUPERUSER.runAs(action); -} + } -private static void setAuths() throws IOException { - String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, PUBLIC, TOPSECRET }; - try { - VisibilityClient.setAuths(UTIL.getConnection(), labels, User.getCurrent().getShortName()); - } catch (Throwable t) { - throw new IOException(t); + @AfterClass + public static void afterClass() throws Exception { + UTIL.shutdownMiniCluster(); } -} -@AfterClass -public static void afterClass() throws Exception { - UTIL.shutdownMiniCluster(); -} + @Before + public void setup() throws Exception { -@Before -public void setup() throws Exception { + } -} + private ThriftHBaseServiceHandler createHandler() throws IOException { + return new ThriftHBaseServiceHandler(conf, UserProvider.instantiate(conf)); + } -private ThriftHBaseServiceHandler createHandler() throws IOException { - return new ThriftHBaseServiceHandler(conf, UserProvider.instantiate(conf)); -} + @Test + public void testScanWithVisibilityLabels() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + ByteBuffer table = wrap(tableAname); + + // insert data + TColumnValue columnValue = new TColumnValue(wrap(familyAname), + wrap(qualifierAname), wrap(valueAname)); + List columnValues = new ArrayList(); + columnValues.add(columnValue); + for (int i = 0; i < 10; i++) { + TPut put = new TPut(wrap(("testScan" + i).getBytes()), columnValues); + if (i == 5) { + put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); + } else { + put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET + + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); + } + handler.put(table, put); + } -@Test -public void testScanWithVisibilityLabels() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - ByteBuffer table = wrap(tableAname); - - // insert data - TColumnValue columnValue = new TColumnValue(wrap(familyAname), - wrap(qualifierAname), wrap(valueAname)); - List columnValues = new ArrayList(); - columnValues.add(columnValue); - for (int i = 0; i < 10; i++) { - TPut put = new TPut(wrap(("testScan" + i).getBytes()), columnValues); - if (i == 5) { - put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); - } else { - put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET - + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); + // create scan instance + TScan scan = new TScan(); + List columns = new ArrayList(); + TColumn column = new TColumn(); + column.setFamily(familyAname); + column.setQualifier(qualifierAname); + columns.add(column); + scan.setColumns(columns); + scan.setStartRow("testScan".getBytes()); + scan.setStopRow("testScan\uffff".getBytes()); + + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(SECRET); + labels.add(PRIVATE); + tauth.setLabels(labels); + scan.setAuthorizations(tauth); + // get scanner and rows + int scanId = handler.openScanner(table, scan); + List results = handler.getScannerRows(scanId, 10); + assertEquals(9, results.size()); + Assert.assertFalse(Bytes.equals(results.get(5).getRow(), + ("testScan" + 5).getBytes())); + for (int i = 0; i < 9; i++) { + if (i < 5) { + assertArrayEquals(("testScan" + i).getBytes(), results.get(i).getRow()); + } else if (i == 5) { + continue; + } else { + assertArrayEquals(("testScan" + (i + 1)).getBytes(), results.get(i) + .getRow()); + } } - handler.put(table, put); - } - // create scan instance - TScan scan = new TScan(); - List columns = new ArrayList(); - TColumn column = new TColumn(); - column.setFamily(familyAname); - column.setQualifier(qualifierAname); - columns.add(column); - scan.setColumns(columns); - scan.setStartRow("testScan".getBytes()); - scan.setStopRow("testScan\uffff".getBytes()); - - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(SECRET); - labels.add(PRIVATE); - tauth.setLabels(labels); - scan.setAuthorizations(tauth); - // get scanner and rows - int scanId = handler.openScanner(table, scan); - List results = handler.getScannerRows(scanId, 10); - assertEquals(9, results.size()); - Assert.assertFalse(Bytes.equals(results.get(5).getRow(), - ("testScan" + 5).getBytes())); - for (int i = 0; i < 9; i++) { - if (i < 5) { - assertArrayEquals(("testScan" + i).getBytes(), results.get(i).getRow()); - } else if (i == 5) { - continue; - } else { - assertArrayEquals(("testScan" + (i + 1)).getBytes(), results.get(i) - .getRow()); + // check that we are at the end of the scan + results = handler.getScannerRows(scanId, 9); + assertEquals(0, results.size()); + + // close scanner and check that it was indeed closed + handler.closeScanner(scanId); + try { + handler.getScannerRows(scanId, 9); + fail("Scanner id should be invalid"); + } catch (TIllegalArgument e) { } } - // check that we are at the end of the scan - results = handler.getScannerRows(scanId, 9); - assertEquals(0, results.size()); + @Test + @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UC_USELESS_CONDITION", + justification="Intended") + public void testGetScannerResultsWithAuthorizations() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + ByteBuffer table = wrap(tableAname); + + // insert data + TColumnValue columnValue = new TColumnValue(wrap(familyAname), + wrap(qualifierAname), wrap(valueAname)); + List columnValues = new ArrayList(); + columnValues.add(columnValue); + for (int i = 0; i < 20; i++) { + TPut put = new TPut( + wrap(("testGetScannerResults" + pad(i, (byte) 2)).getBytes()), + columnValues); + if (i == 3) { + put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); + } else { + put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET + + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); + } + handler.put(table, put); + } - // close scanner and check that it was indeed closed - handler.closeScanner(scanId); - try { - handler.getScannerRows(scanId, 9); - fail("Scanner id should be invalid"); - } catch (TIllegalArgument e) { + // create scan instance + TScan scan = new TScan(); + List columns = new ArrayList(); + TColumn column = new TColumn(); + column.setFamily(familyAname); + column.setQualifier(qualifierAname); + columns.add(column); + scan.setColumns(columns); + scan.setStartRow("testGetScannerResults".getBytes()); + + // get 5 rows and check the returned results + scan.setStopRow("testGetScannerResults05".getBytes()); + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(SECRET); + labels.add(PRIVATE); + tauth.setLabels(labels); + scan.setAuthorizations(tauth); + List results = handler.getScannerResults(table, scan, 5); + assertEquals(4, results.size()); + for (int i = 0; i < 4; i++) { + if (i < 3) { + assertArrayEquals( + ("testGetScannerResults" + pad(i, (byte) 2)).getBytes(), + results.get(i).getRow()); + } else if (i == 3) { + continue; + } else { + assertArrayEquals( + ("testGetScannerResults" + pad(i + 1, (byte) 2)).getBytes(), results + .get(i).getRow()); + } + } } -} -@Test -@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UC_USELESS_CONDITION", - justification="Intended") -public void testGetScannerResultsWithAuthorizations() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - ByteBuffer table = wrap(tableAname); - - // insert data - TColumnValue columnValue = new TColumnValue(wrap(familyAname), - wrap(qualifierAname), wrap(valueAname)); - List columnValues = new ArrayList(); - columnValues.add(columnValue); - for (int i = 0; i < 20; i++) { - TPut put = new TPut( - wrap(("testGetScannerResults" + pad(i, (byte) 2)).getBytes()), - columnValues); - if (i == 3) { - put.setCellVisibility(new TCellVisibility().setExpression(PUBLIC)); - } else { - put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET - + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); - } + @Test + public void testGetsWithLabels() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testPutGet".getBytes(); + ByteBuffer table = wrap(tableAname); + + List columnValues = new ArrayList(); + columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), + wrap(valueAname))); + columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), + wrap(valueBname))); + TPut put = new TPut(wrap(rowName), columnValues); + + put.setColumnValues(columnValues); + put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET + "|" + + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); handler.put(table, put); + TGet get = new TGet(wrap(rowName)); + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(SECRET); + labels.add(PRIVATE); + tauth.setLabels(labels); + get.setAuthorizations(tauth); + TResult result = handler.get(table, get); + assertArrayEquals(rowName, result.getRow()); + List returnedColumnValues = result.getColumnValues(); + assertTColumnValuesEqual(columnValues, returnedColumnValues); } - // create scan instance - TScan scan = new TScan(); - List columns = new ArrayList(); - TColumn column = new TColumn(); - column.setFamily(familyAname); - column.setQualifier(qualifierAname); - columns.add(column); - scan.setColumns(columns); - scan.setStartRow("testGetScannerResults".getBytes()); - - // get 5 rows and check the returned results - scan.setStopRow("testGetScannerResults05".getBytes()); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(SECRET); - labels.add(PRIVATE); - tauth.setLabels(labels); - scan.setAuthorizations(tauth); - List results = handler.getScannerResults(table, scan, 5); - assertEquals(4, results.size()); - for (int i = 0; i < 4; i++) { - if (i < 3) { - assertArrayEquals( - ("testGetScannerResults" + pad(i, (byte) 2)).getBytes(), - results.get(i).getRow()); - } else if (i == 3) { - continue; - } else { - assertArrayEquals( - ("testGetScannerResults" + pad(i + 1, (byte) 2)).getBytes(), results - .get(i).getRow()); - } + @Test + public void testIncrementWithTags() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testIncrementWithTags".getBytes(); + ByteBuffer table = wrap(tableAname); + + List columnValues = new ArrayList(); + columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), + wrap(Bytes.toBytes(1L)))); + TPut put = new TPut(wrap(rowName), columnValues); + put.setColumnValues(columnValues); + put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); + handler.put(table, put); + + List incrementColumns = new ArrayList(); + incrementColumns.add(new TColumnIncrement(wrap(familyAname), + wrap(qualifierAname))); + TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); + increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); + handler.increment(table, increment); + + TGet get = new TGet(wrap(rowName)); + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(SECRET); + tauth.setLabels(labels); + get.setAuthorizations(tauth); + TResult result = handler.get(table, get); + + assertArrayEquals(rowName, result.getRow()); + assertEquals(1, result.getColumnValuesSize()); + TColumnValue columnValue = result.getColumnValues().get(0); + assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue()); } -} -@Test -public void testGetsWithLabels() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testPutGet".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList(); - columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), - wrap(valueAname))); - columnValues.add(new TColumnValue(wrap(familyBname), wrap(qualifierBname), - wrap(valueBname))); - TPut put = new TPut(wrap(rowName), columnValues); - - put.setColumnValues(columnValues); - put.setCellVisibility(new TCellVisibility().setExpression("(" + SECRET + "|" - + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET)); - handler.put(table, put); - TGet get = new TGet(wrap(rowName)); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(SECRET); - labels.add(PRIVATE); - tauth.setLabels(labels); - get.setAuthorizations(tauth); - TResult result = handler.get(table, get); - assertArrayEquals(rowName, result.getRow()); - List returnedColumnValues = result.getColumnValues(); - assertTColumnValuesEqual(columnValues, returnedColumnValues); -} + @Test + public void testIncrementWithTagsWithNotMatchLabels() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testIncrementWithTagsWithNotMatchLabels".getBytes(); + ByteBuffer table = wrap(tableAname); + + List columnValues = new ArrayList(); + columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), + wrap(Bytes.toBytes(1L)))); + TPut put = new TPut(wrap(rowName), columnValues); + put.setColumnValues(columnValues); + put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); + handler.put(table, put); -@Test -public void testIncrementWithTags() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testIncrementWithTags".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList(); - columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), - wrap(Bytes.toBytes(1L)))); - TPut put = new TPut(wrap(rowName), columnValues); - put.setColumnValues(columnValues); - put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); - handler.put(table, put); - - List incrementColumns = new ArrayList(); - incrementColumns.add(new TColumnIncrement(wrap(familyAname), - wrap(qualifierAname))); - TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); - increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); - handler.increment(table, increment); - - TGet get = new TGet(wrap(rowName)); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(SECRET); - tauth.setLabels(labels); - get.setAuthorizations(tauth); - TResult result = handler.get(table, get); - - assertArrayEquals(rowName, result.getRow()); - assertEquals(1, result.getColumnValuesSize()); - TColumnValue columnValue = result.getColumnValues().get(0); - assertArrayEquals(Bytes.toBytes(2L), columnValue.getValue()); -} + List incrementColumns = new ArrayList(); + incrementColumns.add(new TColumnIncrement(wrap(familyAname), + wrap(qualifierAname))); + TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); + increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); + handler.increment(table, increment); + + TGet get = new TGet(wrap(rowName)); + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(PUBLIC); + tauth.setLabels(labels); + get.setAuthorizations(tauth); + TResult result = handler.get(table, get); + assertNull(result.getRow()); + } -@Test -public void testIncrementWithTagsWithNotMatchLabels() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testIncrementWithTagsWithNotMatchLabels".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList(); - columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), - wrap(Bytes.toBytes(1L)))); - TPut put = new TPut(wrap(rowName), columnValues); - put.setColumnValues(columnValues); - put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); - handler.put(table, put); - - List incrementColumns = new ArrayList(); - incrementColumns.add(new TColumnIncrement(wrap(familyAname), - wrap(qualifierAname))); - TIncrement increment = new TIncrement(wrap(rowName), incrementColumns); - increment.setCellVisibility(new TCellVisibility().setExpression(SECRET)); - handler.increment(table, increment); - - TGet get = new TGet(wrap(rowName)); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(PUBLIC); - tauth.setLabels(labels); - get.setAuthorizations(tauth); - TResult result = handler.get(table, get); - assertNull(result.getRow()); -} + @Test + public void testAppend() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testAppend".getBytes(); + ByteBuffer table = wrap(tableAname); + byte[] v1 = Bytes.toBytes(1L); + byte[] v2 = Bytes.toBytes(5L); + List columnValues = new ArrayList(); + columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), + wrap(Bytes.toBytes(1L)))); + TPut put = new TPut(wrap(rowName), columnValues); + put.setColumnValues(columnValues); + put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); + handler.put(table, put); -@Test -public void testAppend() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testAppend".getBytes(); - ByteBuffer table = wrap(tableAname); - byte[] v1 = Bytes.toBytes(1L); - byte[] v2 = Bytes.toBytes(5L); - List columnValues = new ArrayList(); - columnValues.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), - wrap(Bytes.toBytes(1L)))); - TPut put = new TPut(wrap(rowName), columnValues); - put.setColumnValues(columnValues); - put.setCellVisibility(new TCellVisibility().setExpression(PRIVATE)); - handler.put(table, put); - - List appendColumns = new ArrayList(); - appendColumns.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), - wrap(v2))); - TAppend append = new TAppend(wrap(rowName), appendColumns); - append.setCellVisibility(new TCellVisibility().setExpression(SECRET)); - handler.append(table, append); - - TGet get = new TGet(wrap(rowName)); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList(); - labels.add(SECRET); - tauth.setLabels(labels); - get.setAuthorizations(tauth); - TResult result = handler.get(table, get); - - assertArrayEquals(rowName, result.getRow()); - assertEquals(1, result.getColumnValuesSize()); - TColumnValue columnValue = result.getColumnValues().get(0); - assertArrayEquals(Bytes.add(v1, v2), columnValue.getValue()); -} + List appendColumns = new ArrayList(); + appendColumns.add(new TColumnValue(wrap(familyAname), wrap(qualifierAname), + wrap(v2))); + TAppend append = new TAppend(wrap(rowName), appendColumns); + append.setCellVisibility(new TCellVisibility().setExpression(SECRET)); + handler.append(table, append); + + TGet get = new TGet(wrap(rowName)); + TAuthorization tauth = new TAuthorization(); + List labels = new ArrayList(); + labels.add(SECRET); + tauth.setLabels(labels); + get.setAuthorizations(tauth); + TResult result = handler.get(table, get); + + assertArrayEquals(rowName, result.getRow()); + assertEquals(1, result.getColumnValuesSize()); + TColumnValue columnValue = result.getColumnValues().get(0); + assertArrayEquals(Bytes.add(v1, v2), columnValue.getValue()); + } -/** - * Padding numbers to make comparison of sort order easier in a for loop - * - * @param n - * The number to pad. - * @param pad - * The length to pad up to. - * @return The padded number as a string. - */ -private String pad(int n, byte pad) { - String res = Integer.toString(n); - while (res.length() < pad) - res = "0" + res; - return res; -} + /** + * Padding numbers to make comparison of sort order easier in a for loop + * + * @param n + * The number to pad. + * @param pad + * The length to pad up to. + * @return The padded number as a string. + */ + private String pad(int n, byte pad) { + String res = Integer.toString(n); + while (res.length() < pad) + res = "0" + res; + return res; + } } diff --git a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc index adc2b1f4de..197583599a 100644 --- a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc +++ b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc @@ -156,6 +156,18 @@ In case the table goes out of date, the unit tests which check for accuracy of p | | mergeRegions | superuser\|global(A) | | rollWALWriterRequest | superuser\|global(A) | | replicateLogEntries | superuser\|global(W) +|RSGroup |addRSGroup |superuser\|global(A) +| |balanceRSGroup |superuser\|global(A) +| |getRSGroupInfo |superuser\|global(A) +| |getRSGroupInfoOfTable|superuser\|global(A) +| |getRSGroupOfServer |superuser\|global(A) +| |listRSGroups |superuser\|global(A) +| |moveServers |superuser\|global(A) +| |moveServersAndTables |superuser\|global(A) +| |moveTables |superuser\|global(A) +| |removeRSGroup |superuser\|global(A) +| |removeServers |superuser\|global(A) + |=== :numbered: diff --git a/src/main/asciidoc/_chapters/security.adoc b/src/main/asciidoc/_chapters/security.adoc index 9ed6682425..d6e457f7e7 100644 --- a/src/main/asciidoc/_chapters/security.adoc +++ b/src/main/asciidoc/_chapters/security.adoc @@ -782,6 +782,10 @@ For an example of using both together, see <>. + [source,xml] ---- + + hbase.security.authorization + true + hbase.coprocessor.region.classes org.apache.hadoop.hbase.security.access.AccessController, org.apache.hadoop.hbase.security.token.TokenProvider @@ -1178,6 +1182,10 @@ NOTE: Visibility labels are not currently applied for superusers. hbase.coprocessor.master.classes org.apache.hadoop.hbase.security.visibility.VisibilityController + + hbase.security.authorization + true + ---- + NOTE: If you use the AccessController and VisibilityController coprocessors together, the AccessController must come first in the list, because with both components active, the VisibilityController will delegate access control on its system tables to the AccessController. @@ -1651,6 +1659,10 @@ To enable secure bulk load, add the following properties to _hbase-site.xml_. org.apache.hadoop.hbase.security.token.TokenProvider, org.apache.hadoop.hbase.security.access.AccessController,org.apache.hadoop.hbase.security.access.SecureBulkLoadEndpoint + + hbase.security.authorization + true + ---- [[security.example.config]] @@ -1746,6 +1758,10 @@ All options have been discussed separately in the sections above. org.apache.hadoop.hbase.security.token.TokenProvider, org.apache.hadoop.hbase.security.access.AccessController,org.apache.hadoop.hbase.security.access.SecureBulkLoadEndpoint + + hbase.security.authorization + true + ---- ==== -- 2.13