From 3fa3dcd9f92e67e74c9ad197d87460c4eb5118ba Mon Sep 17 00:00:00 2001 From: Guangxu Cheng Date: Tue, 9 Jan 2018 17:56:32 +0800 Subject: [PATCH] HBASE-19483 Add proper privilege check for rsgroup commands Signed-off-by: tedyu --- ...grationTestIngestWithVisibilityLabels.java | 7 +- ...rationTestBigLinkedListWithVisibility.java | 7 +- ...onTestWithCellVisibilityLoadAndVerify.java | 7 +- .../TestImportTSVWithVisibilityLabels.java | 6 +- .../hbase/rest/TestScannersWithLabels.java | 6 +- .../hbase/rsgroup/RSGroupAdminEndpoint.java | 134 +++- .../hbase/rsgroup/TestRSGroupsWithACL.java | 329 ++++++++ .../hbase/master/MasterRpcServices.java | 3 +- .../hbase/security/access/AccessChecker.java | 332 ++++++++ .../security/access/AccessControlLists.java | 4 +- .../security/access/AccessController.java | 705 ++++++----------- .../security/access/TableAuthManager.java | 2 +- .../visibility/VisibilityController.java | 13 +- .../hbase/security/access/SecureTestUtil.java | 6 + .../security/access/TestAccessController.java | 75 -- .../visibility/VisibilityTestUtil.java | 3 +- ...stThriftHBaseServiceHandlerWithLabels.java | 728 +++++++++--------- .../_chapters/appendix_acl_matrix.adoc | 11 + src/main/asciidoc/_chapters/ops_mgt.adoc | 10 + src/main/asciidoc/_chapters/security.adoc | 36 + 20 files changed, 1441 insertions(+), 983 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 b7d8dad4bb2..2928b6d02e5 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 @@ -23,11 +23,10 @@ import java.util.Arrays; import java.util.List; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.io.hfile.HFile; 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 +75,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 d0e6e52bdcf..9349f7b350f 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 @@ -47,7 +47,6 @@ import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.log.HBaseMarkers; import org.apache.hadoop.hbase.mapreduce.Import; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; @@ -57,7 +56,7 @@ import org.apache.hadoop.hbase.security.access.Permission; import org.apache.hadoop.hbase.security.visibility.Authorizations; import org.apache.hadoop.hbase.security.visibility.CellVisibility; 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.AbstractHBaseTool; import org.apache.hadoop.hbase.util.Bytes; @@ -373,9 +372,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 3cafe9d433d..a9c24e928df 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 @@ -40,7 +40,6 @@ import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.ScannerCallable; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil; import org.apache.hadoop.hbase.mapreduce.TableMapper; import org.apache.hadoop.hbase.mapreduce.TableRecordReaderImpl; @@ -48,7 +47,7 @@ import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.visibility.Authorizations; import org.apache.hadoop.hbase.security.visibility.CellVisibility; 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.AbstractHBaseTool; import org.apache.hadoop.hbase.util.Bytes; @@ -116,9 +115,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-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java index 8d3f3df1064..6d6b72916b9 100644 --- a/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java +++ b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestImportTSVWithVisibilityLabels.java @@ -58,7 +58,7 @@ import org.apache.hadoop.hbase.security.visibility.ScanLabelGenerator; 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.testclassification.MapReduceTests; @@ -121,9 +121,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.startMiniCluster(); 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 6ac8e87cb92..8d738edbe12 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 @@ -43,7 +43,7 @@ import org.apache.hadoop.hbase.security.visibility.ScanLabelGenerator; 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.MediumTests; import org.apache.hadoop.hbase.testclassification.RestTests; @@ -130,10 +130,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 2af6c03c77d..fd632b15f5b 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 @@ -18,6 +18,11 @@ package org.apache.hadoop.hbase.rsgroup; + +import com.google.protobuf.RpcCallback; +import com.google.protobuf.RpcController; +import com.google.protobuf.Service; + import java.io.IOException; import java.util.Collections; import java.util.HashSet; @@ -26,10 +31,6 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import com.google.protobuf.RpcCallback; -import com.google.protobuf.RpcController; -import com.google.protobuf.Service; - import org.apache.hadoop.hbase.CoprocessorEnvironment; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.NamespaceDescriptor; @@ -46,6 +47,7 @@ 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.CoprocessorRpcUtils; +import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; @@ -75,10 +77,16 @@ 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.hbase.thirdparty.com.google.common.collect.Sets; +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.Action; +import org.apache.hadoop.hbase.security.access.TableAuthManager; +import org.apache.hadoop.hbase.zookeeper.ZKWatcher; import org.apache.yetus.audience.InterfaceAudience; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.hbase.thirdparty.com.google.common.collect.Sets; // TODO: Encapsulate MasterObserver functions into separate subclass. @CoreCoprocessor @@ -92,12 +100,17 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { private RSGroupInfoManager groupInfoManager; private RSGroupAdminServer groupAdminServer; private final RSGroupAdminService groupAdminService = new RSGroupAdminServiceImpl(); + private AccessChecker accessChecker; + + /** Provider for mapping principal names to Users */ + private UserProvider userProvider; @Override public void start(CoprocessorEnvironment env) throws IOException { if (!(env instanceof HasMasterServices)) { throw new IOException("Does not implement HMasterServices"); } + master = ((HasMasterServices)env).getMasterServices(); groupInfoManager = RSGroupInfoManagerImpl.getInstance(master); groupAdminServer = new RSGroupAdminServer(master, groupInfoManager); @@ -106,6 +119,16 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { if (!RSGroupableBalancer.class.isAssignableFrom(clazz)) { throw new IOException("Configured balancer does not support RegionServer groups."); } + ZKWatcher zk = ((HasMasterServices)env).getMasterServices().getZooKeeper(); + accessChecker = new AccessChecker(env.getConfiguration(), zk); + + // set the user-provider. + this.userProvider = UserProvider.instantiate(env.getConfiguration()); + } + + @Override + public void stop(CoprocessorEnvironment env) { + TableAuthManager.release(accessChecker.getAuthManager()); } @Override @@ -137,6 +160,7 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, group=" + groupName); try { + checkPermission("getRSGroupInfo"); RSGroupInfo rsGroupInfo = groupAdminServer.getRSGroupInfo(groupName); if (rsGroupInfo != null) { builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(rsGroupInfo)); @@ -151,10 +175,11 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void getRSGroupInfoOfTable(RpcController controller, GetRSGroupInfoOfTableRequest request, RpcCallback done) { GetRSGroupInfoOfTableResponse.Builder builder = GetRSGroupInfoOfTableResponse.newBuilder(); + TableName tableName = ProtobufUtil.toTableName(request.getTableName()); + LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, table=" + + tableName); try { - TableName tableName = ProtobufUtil.toTableName(request.getTableName()); - LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, table=" - + tableName); + checkPermission("getRSGroupInfoOfTable"); RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupInfoOfTable(tableName); if (RSGroupInfo != null) { builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); @@ -169,13 +194,14 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void moveServers(RpcController controller, MoveServersRequest request, RpcCallback done) { MoveServersResponse.Builder builder = MoveServersResponse.newBuilder(); + Set
hostPorts = Sets.newHashSet(); + for (HBaseProtos.ServerName el : request.getServersList()) { + hostPorts.add(Address.fromParts(el.getHostName(), el.getPort())); + } + LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts +" to rsgroup " + + request.getTargetGroup()); try { - Set
hostPorts = Sets.newHashSet(); - for (HBaseProtos.ServerName el : request.getServersList()) { - hostPorts.add(Address.fromParts(el.getHostName(), el.getPort())); - } - LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts +" to rsgroup " - + request.getTargetGroup()); + checkPermission("moveServers"); groupAdminServer.moveServers(hostPorts, request.getTargetGroup()); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -187,13 +213,14 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void moveTables(RpcController controller, MoveTablesRequest request, RpcCallback done) { MoveTablesResponse.Builder builder = MoveTablesResponse.newBuilder(); + Set tables = new HashSet<>(request.getTableNameList().size()); + for (TableProtos.TableName tableName : request.getTableNameList()) { + tables.add(ProtobufUtil.toTableName(tableName)); + } + LOG.info(master.getClientIdAuditPrefix() + " move tables " + tables +" to rsgroup " + + request.getTargetGroup()); try { - Set tables = new HashSet<>(request.getTableNameList().size()); - for (TableProtos.TableName tableName : request.getTableNameList()) { - tables.add(ProtobufUtil.toTableName(tableName)); - } - LOG.info(master.getClientIdAuditPrefix() + " move tables " + tables +" to rsgroup " - + request.getTargetGroup()); + checkPermission("moveTables"); groupAdminServer.moveTables(tables, request.getTargetGroup()); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -207,6 +234,7 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { AddRSGroupResponse.Builder builder = AddRSGroupResponse.newBuilder(); LOG.info(master.getClientIdAuditPrefix() + " add rsgroup " + request.getRSGroupName()); try { + checkPermission("addRSGroup"); groupAdminServer.addRSGroup(request.getRSGroupName()); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -221,6 +249,7 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { RemoveRSGroupResponse.newBuilder(); LOG.info(master.getClientIdAuditPrefix() + " remove rsgroup " + request.getRSGroupName()); try { + checkPermission("removeRSGroup"); groupAdminServer.removeRSGroup(request.getRSGroupName()); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -232,8 +261,10 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void balanceRSGroup(RpcController controller, BalanceRSGroupRequest request, RpcCallback done) { BalanceRSGroupResponse.Builder builder = BalanceRSGroupResponse.newBuilder(); - LOG.info(master.getClientIdAuditPrefix() + " balance rsgroup, group=" + request.getRSGroupName()); + LOG.info(master.getClientIdAuditPrefix() + " balance rsgroup, group=" + + request.getRSGroupName()); try { + checkPermission("balanceRSGroup"); builder.setBalanceRan(groupAdminServer.balanceRSGroup(request.getRSGroupName())); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -248,6 +279,7 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { ListRSGroupInfosResponse.Builder builder = ListRSGroupInfosResponse.newBuilder(); LOG.info(master.getClientIdAuditPrefix() + " list rsgroup"); try { + checkPermission("listRSGroup"); for (RSGroupInfo RSGroupInfo : groupAdminServer.listRSGroups()) { builder.addRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); } @@ -261,10 +293,12 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void getRSGroupInfoOfServer(RpcController controller, GetRSGroupInfoOfServerRequest request, RpcCallback done) { GetRSGroupInfoOfServerResponse.Builder builder = GetRSGroupInfoOfServerResponse.newBuilder(); + Address hp = Address.fromParts(request.getServer().getHostName(), + request.getServer().getPort()); + LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, server=" + + hp); try { - Address hp = Address.fromParts(request.getServer().getHostName(), - request.getServer().getPort()); - LOG.info(master.getClientIdAuditPrefix() + " initiates rsgroup info retrieval, server=" + hp); + checkPermission("getRSGroupInfoOfServer"); RSGroupInfo RSGroupInfo = groupAdminServer.getRSGroupOfServer(hp); if (RSGroupInfo != null) { builder.setRSGroupInfo(RSGroupProtobufUtil.toProtoGroupInfo(RSGroupInfo)); @@ -279,17 +313,18 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { public void moveServersAndTables(RpcController controller, MoveServersAndTablesRequest request, RpcCallback done) { MoveServersAndTablesResponse.Builder builder = MoveServersAndTablesResponse.newBuilder(); + Set
hostPorts = Sets.newHashSet(); + for (HBaseProtos.ServerName el : request.getServersList()) { + hostPorts.add(Address.fromParts(el.getHostName(), el.getPort())); + } + Set tables = new HashSet<>(request.getTableNameList().size()); + for (TableProtos.TableName tableName : request.getTableNameList()) { + tables.add(ProtobufUtil.toTableName(tableName)); + } + LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts + + " and tables " + tables + " to rsgroup" + request.getTargetGroup()); try { - Set
hostPorts = Sets.newHashSet(); - for (HBaseProtos.ServerName el : request.getServersList()) { - hostPorts.add(Address.fromParts(el.getHostName(), el.getPort())); - } - Set tables = new HashSet<>(request.getTableNameList().size()); - for (TableProtos.TableName tableName : request.getTableNameList()) { - tables.add(ProtobufUtil.toTableName(tableName)); - } - LOG.info(master.getClientIdAuditPrefix() + " move servers " + hostPorts - + " and tables " + tables + " to rsgroup" + request.getTargetGroup()); + checkPermission("moveServersAndTables"); groupAdminServer.moveServersAndTables(hostPorts, tables, request.getTargetGroup()); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -303,13 +338,14 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { RpcCallback done) { RemoveServersResponse.Builder builder = RemoveServersResponse.newBuilder(); + Set
servers = Sets.newHashSet(); + for (HBaseProtos.ServerName el : request.getServersList()) { + servers.add(Address.fromParts(el.getHostName(), el.getPort())); + } + LOG.info(master.getClientIdAuditPrefix() + + " remove decommissioned servers from rsgroup: " + servers); try { - Set
servers = Sets.newHashSet(); - for (HBaseProtos.ServerName el : request.getServersList()) { - servers.add(Address.fromParts(el.getHostName(), el.getPort())); - } - LOG.info(master.getClientIdAuditPrefix() - + " remove decommissioned servers from rsgroup: " + servers); + checkPermission("removeServers"); groupAdminServer.removeServers(servers); } catch (IOException e) { CoprocessorRpcUtils.setControllerException(controller, e); @@ -395,5 +431,21 @@ public class RSGroupAdminEndpoint implements MasterCoprocessor, MasterObserver { groupAdminServer.removeServers(clearedServer); } - ///////////////////////////////////////////////////////////////////////////// + public void checkPermission(String request) throws IOException { + accessChecker.requirePermission(getActiveUser(), request, 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 { + // for non-rpc handling, fallback to system user + Optional optionalUser = RpcServer.getRequestUser(); + if (optionalUser.isPresent()) { + return optionalUser.get(); + } + return userProvider.getCurrent(); + } } 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 00000000000..1018dfafd2b --- /dev/null +++ b/hbase-rsgroup/src/test/java/org/apache/hadoop/hbase/rsgroup/TestRSGroupsWithACL.java @@ -0,0 +1,329 @@ +/* + * 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.HConstants; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.TableNotFoundException; +import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.TableDescriptorBuilder; +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 { + TableDescriptorBuilder tableBuilder = TableDescriptorBuilder.newBuilder(TEST_TABLE); + ColumnFamilyDescriptorBuilder cfd = ColumnFamilyDescriptorBuilder.newBuilder(TEST_FAMILY); + cfd.setMaxVersions(100); + tableBuilder.addColumnFamily(cfd.build()); + tableBuilder.setValue(TableDescriptorBuilder.OWNER, USER_OWNER.getShortName()); + createTable(TEST_UTIL, tableBuilder.build(), + 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(5, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); + try { + assertEquals(5, AccessControlClient.getUserPermissions(systemUserConnection, + TEST_TABLE.toString()).size()); + } catch (Throwable e) { + LOG.error("error during call of AccessControlClient.getUserPermissions. ", e); + } + } + + private static void cleanUp() throws Exception { + // Clean the _acl_ table + try { + deleteTable(TEST_UTIL, TEST_TABLE); + } catch (TableNotFoundException ex) { + // Test deleted the table, no problem + LOG.info("Test deleted table " + TEST_TABLE); + } + // Verify all table/namespace permissions are erased + assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE).size()); + assertEquals(0, AccessControlLists.getNamespacePermissions(conf, + TEST_TABLE.getNamespaceAsString()).size()); + } + + @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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 = () -> { + 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 8f41e4f1c60..907ca9b41de 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 @@ -83,6 +83,7 @@ import org.apache.hadoop.hbase.replication.ReplicationException; import org.apache.hadoop.hbase.replication.ReplicationPeerConfig; import org.apache.hadoop.hbase.replication.ReplicationPeerDescription; 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; @@ -1810,7 +1811,7 @@ public class MasterRpcServices extends RSRpcServices // A coprocessor that implements AccessControlService can provide AUTHORIZATION and // CELL_AUTHORIZATION if (master.cpHost != null && hasAccessControlServiceCoprocessor(master.cpHost)) { - if (AccessController.isAuthorizationSupported(master.getConfiguration())) { + if (AccessChecker.isAuthorizationSupported(master.getConfiguration())) { capabilities.add(SecurityCapabilitiesResponse.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 00000000000..d88e5229aff --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessChecker.java @@ -0,0 +1,332 @@ +/* + * 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.DoNotRetryIOException; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.RegionInfo; +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.ZKWatcher; +import org.apache.yetus.audience.InterfaceAudience; +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 ZKWatcher} + */ + public AccessChecker(final Configuration conf, final ZKWatcher 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; + } + + /** + * 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; + 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; + 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()); + } + } + + /** + * 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()); + } + } + + public void checkLockPermissions(User user, String namespace, + TableName tableName, RegionInfo[] regionInfos, String reason) + throws IOException { + if (namespace != null && !namespace.isEmpty()) { + requireNamespacePermission(user, reason, namespace, Action.ADMIN, Action.CREATE); + } else if (tableName != null || (regionInfos != null && regionInfos.length > 0)) { + // So, either a table or regions op. If latter, check perms ons table. + TableName tn = tableName != null? tableName: regionInfos[0].getTable(); + requireTablePermission(user, reason, tn, null, null, + Action.ADMIN, Action.CREATE); + } else { + throw new DoNotRetryIOException("Invalid lock level when requesting permissions."); + } + } + + public static void logResult(AuthResult result) { + if (AUDITLOG.isTraceEnabled()) { + AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + " for user " + ( + result.getUser() != null ? + result.getUser().getShortName() : + "UNKNOWN") + "; reason: " + result.getReason() + "; remote address: " + + RpcServer.getRemoteAddress().map(InetAddress::toString).orElse("") + + "; request: " + result.getRequest() + "; context: " + 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 eb20c387eff..b0f33bdb461 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 @@ -63,6 +63,7 @@ import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.Region; import org.apache.hadoop.hbase.security.User; +import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; import org.apache.hbase.thirdparty.com.google.common.collect.ArrayListMultimap; import org.apache.hbase.thirdparty.com.google.common.collect.ListMultimap; import org.apache.hbase.thirdparty.com.google.common.collect.Lists; @@ -490,7 +491,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 602af91e5a1..5a3c883388a 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 @@ -23,7 +23,6 @@ import com.google.protobuf.RpcCallback; import com.google.protobuf.RpcController; import com.google.protobuf.Service; import java.io.IOException; -import java.net.InetAddress; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; @@ -96,7 +95,6 @@ import org.apache.hadoop.hbase.filter.FilterList; import org.apache.hadoop.hbase.io.hfile.HFile; import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils; import org.apache.hadoop.hbase.ipc.RpcServer; -import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService; @@ -186,10 +184,10 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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 */ @@ -204,19 +202,19 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, /** 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; @@ -224,12 +222,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, /** 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); } @@ -238,10 +232,10 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } 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 = @@ -253,7 +247,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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; } @@ -263,7 +257,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, * 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); for (Map.Entry> f : familyMap.entrySet()) { @@ -274,7 +268,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } } } - ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher(); + ZKPermissionWatcher zkw = getAuthManager().getZKPermissionWatcher(); Configuration conf = regionEnv.getConfiguration(); byte [] currentEntry = null; // TODO: Here we are already on the ACL region. (And it is single @@ -312,7 +306,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, * the request * @return an authorization result */ - AuthResult permissionGranted(String request, User user, Action permRequest, + private AuthResult permissionGranted(String request, User user, Action permRequest, RegionCoprocessorEnvironment e, Map> families) { RegionInfo hri = e.getRegion().getRegionInfo(); @@ -333,7 +327,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } // 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); } @@ -343,7 +337,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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 } @@ -354,7 +348,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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)); @@ -363,7 +357,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } else if (family.getValue() instanceof List) { // List List cellList = (List)family.getValue(); for (Cell cell : cellList) { - if (!authManager.authorize(user, tableName, family.getKey(), + if (!getAuthManager().authorize(user, tableName, family.getKey(), CellUtil.cloneQualifier(cell), permRequest)) { return AuthResult.deny(request, "Failed qualifier check", user, permRequest, tableName, makeFamilyMap(family.getKey(), CellUtil.cloneQualifier(cell))); @@ -398,7 +392,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, * @param actions the desired actions * @return an authorization result */ - AuthResult permissionGranted(OpType opType, User user, RegionCoprocessorEnvironment e, + private AuthResult permissionGranted(OpType opType, User user, RegionCoprocessorEnvironment e, Map> families, Action... actions) { AuthResult result = null; for (Action action: actions) { @@ -410,241 +404,61 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, return result; } - private void logResult(AuthResult result) { - if (AUDITLOG.isTraceEnabled()) { - AUDITLOG.trace("Access " + (result.isAllowed() ? "allowed" : "denied") + " for user " + - (result.getUser() != null ? result.getUser().getShortName() : "UNKNOWN") + "; reason: " + - result.getReason() + "; remote address: " + - RpcServer.getRemoteAddress().map(InetAddress::toString).orElse("") + "; 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, - * otherwise the currently logged in user is used. - */ - private User getActiveUser(ObserverContext ctx) throws IOException { - // for non-rpc handling, fallback to system user - Optional optionalUser = ctx.getCaller(); - User user; - if (optionalUser.isPresent()) { - return optionalUser.get(); - } - return userProvider.getCurrent(); - } - - /** - * 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(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 - */ - private 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 - */ - private void requireAccess(User user, String request, TableName tableName, + public void requireAccess(ObserverContext ctx, 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()); - } + accessChecker.requireAccess(getActiveUser(ctx), request, tableName, permissions); } - /** - * 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(User user, String request, Action perm) throws IOException { - requireGlobalPermission(user, request, perm, null, null); + public void requirePermission(ObserverContext ctx, String request, + Action perm) throws IOException { + accessChecker.requirePermission(getActiveUser(ctx), request, perm); } - /** - * 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(User user, String request, Action perm, TableName tableName, + public void requireGlobalPermission(ObserverContext ctx, 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() + ")"); - } - } + accessChecker.requireGlobalPermission(getActiveUser(ctx), + request, perm,tableName, familyMap); } - /** - * 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(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() + ")"); - } - } + public void requireGlobalPermission(ObserverContext ctx, String request, + Action perm, String namespace) throws IOException { + accessChecker.requireGlobalPermission(getActiveUser(ctx), + request, perm, namespace); } - /** - * Checks that the user has the given global or namespace permission. - * @param namespace - * @param permissions Actions being requested - */ - public void requireNamespacePermission(User user, String request, String namespace, + public void requireNamespacePermission(ObserverContext ctx, 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()); - } + accessChecker.requireNamespacePermission(getActiveUser(ctx), + request, namespace, permissions); } - /** - * Checks that the user has the given global or namespace permission. - * @param namespace - * @param permissions Actions being requested - */ - public void requireNamespacePermission(User user, String request, String namespace, + public void requireNamespacePermission(ObserverContext ctx, String request, String namespace, TableName tableName, Map> familyMap, - Action... permissions) - throws IOException { - AuthResult result = null; + Action... permissions) throws IOException { + accessChecker.requireNamespacePermission(getActiveUser(ctx), + request, namespace, tableName, familyMap, + permissions); + } - 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()); - } + public void requirePermission(ObserverContext ctx, String request, TableName tableName, + byte[] family, byte[] qualifier, Action... permissions) throws IOException { + accessChecker.requirePermission(getActiveUser(ctx), request, + tableName, family, qualifier, permissions); + } + + public void requireTablePermission(ObserverContext ctx, String request, + TableName tableName,byte[] family, byte[] qualifier, + Action... permissions) throws IOException { + accessChecker.requireTablePermission(getActiveUser(ctx), + request, tableName, family, qualifier, permissions); + } + + public void checkLockPermissions(ObserverContext ctx, String namespace, + TableName tableName, RegionInfo[] regionInfos, String reason) + throws IOException { + accessChecker.checkLockPermissions(getActiveUser(ctx), + namespace, tableName, regionInfos, reason); } /** @@ -669,13 +483,13 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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; } @@ -865,7 +679,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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; } @@ -940,7 +754,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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."); } @@ -980,27 +794,13 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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); - } + TableAuthManager.release(getAuthManager()); } /*********************************** Observer/Service Getters ***********************************/ @@ -1045,7 +845,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, for (byte[] family: families) { familyMap.put(family, null); } - requireNamespacePermission(getActiveUser(c), "createTable", + requireNamespacePermission(c, "createTable", desc.getTableName().getNamespaceAsString(), desc.getTableName(), familyMap, Action.CREATE); } @@ -1102,8 +902,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preDeleteTable(ObserverContext c, TableName tableName) throws IOException { - requirePermission(getActiveUser(c), "deleteTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "deleteTable", + tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1120,14 +920,14 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, return null; } }); - this.authManager.getZKPermissionWatcher().deleteTableACLNode(tableName); + getAuthManager().getZKPermissionWatcher().deleteTableACLNode(tableName); } @Override public void preTruncateTable(ObserverContext c, final TableName tableName) throws IOException { - requirePermission(getActiveUser(c), "truncateTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "truncateTable", + tableName, null, null, Action.ADMIN, Action.CREATE); final Configuration conf = c.getEnvironment().getConfiguration(); User.runAsLoginUser(new PrivilegedExceptionAction() { @@ -1168,8 +968,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, public void preModifyTable(ObserverContext c, TableName tableName, TableDescriptor htd) throws IOException { // TODO: potentially check if this is a add/modify/delete column operation - requirePermission(getActiveUser(c), "modifyTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "modifyTable", + tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1196,8 +996,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preEnableTable(ObserverContext c, TableName tableName) throws IOException { - requirePermission(getActiveUser(c), "enableTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "enableTable", + tableName, null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1211,14 +1011,14 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, throw new AccessDeniedException("Not allowed to disable " + AccessControlLists.ACL_TABLE_NAME + " table with AccessController installed"); } - requirePermission(getActiveUser(c), "disableTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "disableTable", + tableName, null, null, Action.ADMIN, Action.CREATE); } @Override public void preAbortProcedure(ObserverContext ctx, final long procId) throws IOException { - requirePermission(getActiveUser(ctx), "abortProcedure", Action.ADMIN); + requirePermission(ctx, "abortProcedure", Action.ADMIN); } @Override @@ -1230,74 +1030,73 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preGetProcedures(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "getProcedure", Action.ADMIN); + requirePermission(ctx, "getProcedure", Action.ADMIN); } @Override public void preGetLocks(ObserverContext ctx) throws IOException { User user = getActiveUser(ctx); - requirePermission(user, "getLocks", Action.ADMIN); + accessChecker.requirePermission(user, "getLocks", Action.ADMIN); } @Override public void preMove(ObserverContext c, RegionInfo region, ServerName srcServer, ServerName destServer) throws IOException { - requirePermission(getActiveUser(c), "move", region.getTable(), null, null, Action.ADMIN); + requirePermission(c, "move", + region.getTable(), null, null, Action.ADMIN); } @Override public void preAssign(ObserverContext c, RegionInfo regionInfo) throws IOException { - requirePermission(getActiveUser(c), "assign", regionInfo.getTable(), null, null, Action.ADMIN); + requirePermission(c, "assign", + regionInfo.getTable(), null, null, Action.ADMIN); } @Override public void preUnassign(ObserverContext c, RegionInfo regionInfo, boolean force) throws IOException { - requirePermission(getActiveUser(c), "unassign", regionInfo.getTable(), null, null, Action.ADMIN); + requirePermission(c, "unassign", + regionInfo.getTable(), null, null, Action.ADMIN); } @Override public void preRegionOffline(ObserverContext c, RegionInfo regionInfo) throws IOException { - requirePermission(getActiveUser(c), "regionOffline", regionInfo.getTable(), null, null, - Action.ADMIN); + requirePermission(c, "regionOffline", + regionInfo.getTable(), null, null, Action.ADMIN); } @Override public void preSetSplitOrMergeEnabled(final ObserverContext ctx, final boolean newValue, final MasterSwitchType switchType) throws IOException { - requirePermission(getActiveUser(ctx), "setSplitOrMergeEnabled", Action.ADMIN); - } - - @Override - public void postSetSplitOrMergeEnabled(final ObserverContext ctx, - final boolean newValue, final MasterSwitchType switchType) throws IOException { + requirePermission(ctx, "setSplitOrMergeEnabled", + Action.ADMIN); } @Override public void preBalance(ObserverContext c) throws IOException { - requirePermission(getActiveUser(c), "balance", Action.ADMIN); + requirePermission(c, "balance", Action.ADMIN); } @Override public void preBalanceSwitch(ObserverContext c, boolean newValue) throws IOException { - requirePermission(getActiveUser(c), "balanceSwitch", Action.ADMIN); + requirePermission(c, "balanceSwitch", Action.ADMIN); } @Override public void preShutdown(ObserverContext c) throws IOException { - requirePermission(getActiveUser(c), "shutdown", Action.ADMIN); + requirePermission(c, "shutdown", Action.ADMIN); } @Override public void preStopMaster(ObserverContext c) throws IOException { - requirePermission(getActiveUser(c), "stopMaster", Action.ADMIN); + requirePermission(c, "stopMaster", Action.ADMIN); } @Override @@ -1335,8 +1134,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, public void preSnapshot(final ObserverContext ctx, final SnapshotDescription snapshot, final TableDescriptor hTableDescriptor) throws IOException { - requirePermission(getActiveUser(ctx), "snapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, - Permission.Action.ADMIN); + requirePermission(ctx, "snapshot " + snapshot.getName(), + hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN); } @Override @@ -1347,9 +1146,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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(user, "listSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(user, "listSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1363,9 +1162,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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(user, "cloneSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(user, "cloneSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1375,10 +1174,10 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, throws IOException { User user = getActiveUser(ctx); if (SnapshotDescriptionUtils.isSnapshotOwner(snapshot, user)) { - requirePermission(user, "restoreSnapshot " + snapshot.getName(), hTableDescriptor.getTableName(), null, null, - Permission.Action.ADMIN); + accessChecker.requirePermission(user, "restoreSnapshot " + snapshot.getName(), + hTableDescriptor.getTableName(), null, null, Permission.Action.ADMIN); } else { - requirePermission(user, "restoreSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(user, "restoreSnapshot " + snapshot.getName(), Action.ADMIN); } } @@ -1390,22 +1189,24 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // 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(user, "deleteSnapshot " + snapshot.getName(), Action.ADMIN); + accessChecker.requirePermission(user, "deleteSnapshot " + snapshot.getName(), Action.ADMIN); } } @Override public void preCreateNamespace(ObserverContext ctx, NamespaceDescriptor ns) throws IOException { - requireGlobalPermission(getActiveUser(ctx), "createNamespace", Action.ADMIN, ns.getName()); + requireGlobalPermission(ctx, "createNamespace", + Action.ADMIN, ns.getName()); } @Override public void preDeleteNamespace(ObserverContext ctx, String namespace) throws IOException { - requireGlobalPermission(getActiveUser(ctx), "deleteNamespace", Action.ADMIN, namespace); + requireGlobalPermission(ctx, "deleteNamespace", + Action.ADMIN, namespace); } @Override @@ -1422,7 +1223,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, return null; } }); - this.authManager.getZKPermissionWatcher().deleteNamespaceACLNode(namespace); + getAuthManager().getZKPermissionWatcher().deleteNamespaceACLNode(namespace); LOG.info(namespace + " entry deleted in " + AccessControlLists.ACL_TABLE_NAME + " table."); } @@ -1431,13 +1232,15 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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(getActiveUser(ctx), "modifyNamespace", Action.ADMIN, ns.getName()); + requireGlobalPermission(ctx, "modifyNamespace", + Action.ADMIN, ns.getName()); } @Override public void preGetNamespaceDescriptor(ObserverContext ctx, String namespace) throws IOException { - requireNamespacePermission(getActiveUser(ctx), "getNamespaceDescriptor", namespace, Action.ADMIN); + requireNamespacePermission(ctx, "getNamespaceDescriptor", + namespace, Action.ADMIN); } @Override @@ -1450,7 +1253,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, while (itr.hasNext()) { NamespaceDescriptor desc = itr.next(); try { - requireNamespacePermission(user, "listNamespaces", desc.getName(), Action.ADMIN); + accessChecker.requireNamespacePermission(user, "listNamespaces", + desc.getName(), Action.ADMIN); } catch (AccessDeniedException e) { itr.remove(); } @@ -1460,8 +1264,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preTableFlush(final ObserverContext ctx, final TableName tableName) throws IOException { - requirePermission(getActiveUser(ctx), "flushTable", tableName, null, null, - Action.ADMIN, Action.CREATE); + requirePermission(ctx, "flushTable", tableName, + null, null, Action.ADMIN, Action.CREATE); } @Override @@ -1469,29 +1273,33 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, final ObserverContext ctx, final TableName tableName, final byte[] splitRow) throws IOException { - requirePermission(getActiveUser(ctx), "split", tableName, null, null, Action.ADMIN); + requirePermission(ctx, "split", tableName, + null, null, Action.ADMIN); } @Override - public void preClearDeadServers(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "clearDeadServers", Action.ADMIN); + public void preClearDeadServers(ObserverContext ctx) + throws IOException { + requirePermission(ctx, "clearDeadServers", Action.ADMIN); } @Override public void preDecommissionRegionServers(ObserverContext ctx, List servers, boolean offload) throws IOException { - requirePermission(getActiveUser(ctx), "decommissionRegionServers", Action.ADMIN); + requirePermission(ctx, "decommissionRegionServers", Action.ADMIN); } @Override - public void preListDecommissionedRegionServers(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "listDecommissionedRegionServers", Action.ADMIN); + public void preListDecommissionedRegionServers(ObserverContext ctx) + throws IOException { + requirePermission(ctx, "listDecommissionedRegionServers", + Action.ADMIN); } @Override public void preRecommissionRegionServer(ObserverContext ctx, ServerName server, List encodedRegionNames) throws IOException { - requirePermission(getActiveUser(ctx), "recommissionRegionServers", Action.ADMIN); + requirePermission(ctx, "recommissionRegionServers", Action.ADMIN); } /* ---- RegionObserver implementation ---- */ @@ -1508,7 +1316,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, if (regionInfo.getTable().isSystemTable()) { checkSystemOrSuperUser(getActiveUser(c)); } else { - requirePermission(getActiveUser(c), "preOpen", Action.ADMIN); + requirePermission(c, "preOpen", Action.ADMIN); } } } @@ -1538,16 +1346,16 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preFlush(ObserverContext c, FlushLifeCycleTracker tracker) throws IOException { - requirePermission(getActiveUser(c), "flush", getTableName(c.getEnvironment()), null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "flush", getTableName(c.getEnvironment()), + null, null, Action.ADMIN, Action.CREATE); } @Override public InternalScanner preCompact(ObserverContext c, Store store, InternalScanner scanner, ScanType scanType, CompactionLifeCycleTracker tracker, CompactionRequest request) throws IOException { - requirePermission(getActiveUser(c), "compact", getTableName(c.getEnvironment()), null, null, - Action.ADMIN, Action.CREATE); + requirePermission(c, "compact", getTableName(c.getEnvironment()), + null, null, Action.ADMIN, Action.CREATE); return scanner; } @@ -1594,7 +1402,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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 +1432,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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 +1454,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions for user '" + (user != null ? user.getShortName() : "null") @@ -1682,8 +1490,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // security policy over time without requiring expensive updates. RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = put.getFamilyCellMap(); - AuthResult authResult = permissionGranted(OpType.PUT, user, env, families, Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted(OpType.PUT, + user, env, families, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { put.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1727,8 +1536,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = delete.getFamilyCellMap(); User user = getActiveUser(c); - AuthResult authResult = permissionGranted(OpType.DELETE, user, env, families, Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted(OpType.DELETE, + user, env, families, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { delete.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1766,7 +1576,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, authResult = AuthResult.deny(opType.toString(), "Covering cell set", user, Action.WRITE, table, m.getFamilyCellMap()); } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -1797,9 +1607,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // Require READ and WRITE permissions on the table, CF, and KV to update RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = makeFamilyMap(family, qualifier); - AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, user, env, families, - Action.READ, Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted(OpType.CHECK_AND_PUT, + user, env, families, Action.READ, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { put.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1822,10 +1632,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public boolean preCheckAndPutAfterRowLock(final ObserverContext c, - final byte[] row, final byte[] family, final byte[] qualifier, - final CompareOperator opp, final ByteArrayComparable comparator, final Put put, - final boolean result) - throws IOException { + final byte[] row, final byte[] family, final byte[] qualifier, + final CompareOperator opp, final ByteArrayComparable comparator, final Put put, + final boolean result) throws IOException { if (put.getAttribute(CHECK_COVERING_PERM) != null) { // We had failure with table, cf and q perm checks and now giving a chance for cell // perm check @@ -1835,13 +1644,13 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, User user = getActiveUser(c); if (checkCoveringPermission(user, OpType.CHECK_AND_PUT, c.getEnvironment(), row, families, HConstants.LATEST_TIMESTAMP, Action.READ)) { - authResult = AuthResult.allow(OpType.CHECK_AND_PUT.toString(), "Covering cell set", - user, Action.READ, table, families); + authResult = AuthResult.allow(OpType.CHECK_AND_PUT.toString(), + "Covering cell set", user, Action.READ, table, families); } else { - authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), "Covering cell set", - user, Action.READ, table, families); + authResult = AuthResult.deny(OpType.CHECK_AND_PUT.toString(), + "Covering cell set", user, Action.READ, table, families); } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } @@ -1865,9 +1674,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = makeFamilyMap(family, qualifier); User user = getActiveUser(c); - AuthResult authResult = permissionGranted(OpType.CHECK_AND_DELETE, user, env, families, - Action.READ, Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted( + OpType.CHECK_AND_DELETE, user, env, families, Action.READ, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { delete.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1881,8 +1690,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public boolean preCheckAndDeleteAfterRowLock( - final ObserverContext c, final byte[] row, final byte[] family, - final byte[] qualifier, final CompareOperator op, + final ObserverContext c, final byte[] row, + final byte[] family, final byte[] qualifier, final CompareOperator op, final ByteArrayComparable comparator, final Delete delete, final boolean result) throws IOException { if (delete.getAttribute(CHECK_COVERING_PERM) != null) { @@ -1892,15 +1701,15 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, Map> families = makeFamilyMap(family, qualifier); AuthResult authResult = null; User user = getActiveUser(c); - if (checkCoveringPermission(user, OpType.CHECK_AND_DELETE, c.getEnvironment(), row, families, - HConstants.LATEST_TIMESTAMP, Action.READ)) { - authResult = AuthResult.allow(OpType.CHECK_AND_DELETE.toString(), "Covering cell set", - user, Action.READ, table, families); + if (checkCoveringPermission(user, OpType.CHECK_AND_DELETE, c.getEnvironment(), + row, families, HConstants.LATEST_TIMESTAMP, Action.READ)) { + authResult = AuthResult.allow(OpType.CHECK_AND_DELETE.toString(), + "Covering cell set", user, Action.READ, table, families); } else { - authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), "Covering cell set", - user, Action.READ, table, families); + authResult = AuthResult.deny(OpType.CHECK_AND_DELETE.toString(), + "Covering cell set", user, Action.READ, table, families); } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); } @@ -1917,8 +1726,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // Require WRITE permission to the table, CF, and the KV to be appended RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = append.getFamilyCellMap(); - AuthResult authResult = permissionGranted(OpType.APPEND, user, env, families, Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted(OpType.APPEND, user, + env, families, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { append.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -1951,13 +1761,13 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, User user = getActiveUser(c); if (checkCoveringPermission(user, OpType.APPEND, c.getEnvironment(), append.getRow(), append.getFamilyCellMap(), append.getTimeRange().getMax(), Action.WRITE)) { - authResult = AuthResult.allow(OpType.APPEND.toString(), "Covering cell set", - user, Action.WRITE, table, append.getFamilyCellMap()); + authResult = AuthResult.allow(OpType.APPEND.toString(), + "Covering cell set", user, Action.WRITE, table, append.getFamilyCellMap()); } else { - authResult = AuthResult.deny(OpType.APPEND.toString(), "Covering cell set", - user, Action.WRITE, table, append.getFamilyCellMap()); + authResult = AuthResult.deny(OpType.APPEND.toString(), + "Covering cell set", user, Action.WRITE, table, append.getFamilyCellMap()); } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -1977,9 +1787,9 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // the incremented value RegionCoprocessorEnvironment env = c.getEnvironment(); Map> families = increment.getFamilyCellMap(); - AuthResult authResult = permissionGranted(OpType.INCREMENT, user, env, families, - Action.WRITE); - logResult(authResult); + AuthResult authResult = permissionGranted(OpType.INCREMENT, + user, env, families, Action.WRITE); + AccessChecker.logResult(authResult); if (!authResult.isAllowed()) { if (cellFeaturesEnabled && !compatibleEarlyTermination) { increment.setAttribute(CHECK_COVERING_PERM, TRUE); @@ -2018,7 +1828,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, authResult = AuthResult.deny(OpType.INCREMENT.toString(), "Covering cell set", user, Action.WRITE, table, increment.getFamilyCellMap()); } - logResult(authResult); + AccessChecker.logResult(authResult); if (authorizationEnabled && !authResult.isAllowed()) { throw new AccessDeniedException("Insufficient permissions " + authResult.toContextString()); @@ -2156,7 +1966,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, List> familyPaths) throws IOException { User user = getActiveUser(ctx); for(Pair el : familyPaths) { - requirePermission(user, "preBulkLoadHFile", + accessChecker.requirePermission(user, "preBulkLoadHFile", ctx.getEnvironment().getRegion().getTableDescriptor().getTableName(), el.getFirst(), null, @@ -2173,7 +1983,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void prePrepareBulkLoad(ObserverContext ctx) throws IOException { - requireAccess(getActiveUser(ctx), "prePrepareBulkLoad", + requireAccess(ctx, "prePrepareBulkLoad", ctx.getEnvironment().getRegion().getTableDescriptor().getTableName(), Action.CREATE); } @@ -2186,7 +1996,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preCleanupBulkLoad(ObserverContext ctx) throws IOException { - requireAccess(getActiveUser(ctx), "preCleanupBulkLoad", + requireAccess(ctx, "preCleanupBulkLoad", ctx.getEnvironment().getRegion().getTableDescriptor().getTableName(), Action.CREATE); } @@ -2198,7 +2008,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // Don't intercept calls to our own AccessControlService, we check for // appropriate permissions in the service handlers if (shouldCheckExecPermission && !(service instanceof AccessControlService)) { - requirePermission(getActiveUser(ctx), + requirePermission(ctx, "invoke(" + service.getDescriptorForType().getName() + "." + methodName + ")", getTableName(ctx.getEnvironment()), null, null, Action.EXEC); @@ -2215,8 +2025,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void grant(RpcController controller, - AccessControlProtos.GrantRequest request, - RpcCallback done) { + AccessControlProtos.GrantRequest request, + RpcCallback done) { final UserPermission perm = AccessControlUtil.toUserPermission(request.getUserPermission()); AccessControlProtos.GrantResponse response = null; try { @@ -2233,11 +2043,12 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, switch(request.getUserPermission().getPermission().getType()) { case Global : case Table : - requirePermission(caller, "grant", perm.getTableName(), + accessChecker.requirePermission(caller, "grant", perm.getTableName(), perm.getFamily(), perm.getQualifier(), Action.ADMIN); break; case Namespace : - requireNamespacePermission(caller, "grant", perm.getNamespace(), Action.ADMIN); + accessChecker.requireNamespacePermission(caller, "grant", perm.getNamespace(), + Action.ADMIN); break; } @@ -2272,8 +2083,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void revoke(RpcController controller, - AccessControlProtos.RevokeRequest request, - RpcCallback done) { + AccessControlProtos.RevokeRequest request, + RpcCallback done) { final UserPermission perm = AccessControlUtil.toUserPermission(request.getUserPermission()); AccessControlProtos.RevokeResponse response = null; try { @@ -2290,11 +2101,12 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, switch(request.getUserPermission().getPermission().getType()) { case Global : case Table : - requirePermission(caller, "revoke", perm.getTableName(), perm.getFamily(), + accessChecker.requirePermission(caller, "revoke", perm.getTableName(), perm.getFamily(), perm.getQualifier(), Action.ADMIN); break; case Namespace : - requireNamespacePermission(caller, "revoke", perm.getNamespace(), Action.ADMIN); + accessChecker.requireNamespacePermission(caller, "revoke", perm.getNamespace(), + Action.ADMIN); break; } @@ -2328,8 +2140,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void getUserPermissions(RpcController controller, - AccessControlProtos.GetUserPermissionsRequest request, - RpcCallback done) { + AccessControlProtos.GetUserPermissionsRequest request, + RpcCallback done) { AccessControlProtos.GetUserPermissionsResponse response = null; try { // only allowed to be called on _acl_ region @@ -2343,7 +2155,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, if (request.getType() == AccessControlProtos.Permission.Type.Table) { final TableName table = request.hasTableName() ? ProtobufUtil.toTableName(request.getTableName()) : null; - requirePermission(caller, "userPermissions", table, null, null, Action.ADMIN); + accessChecker.requirePermission(caller, "userPermissions", + table, null, null, Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2352,7 +2165,8 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, }); } else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) { final String namespace = request.getNamespaceName().toStringUtf8(); - requireNamespacePermission(caller, "userPermissions", namespace, Action.ADMIN); + accessChecker.requireNamespacePermission(caller, "userPermissions", + namespace, Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2361,7 +2175,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, } }); } else { - requirePermission(caller, "userPermissions", Action.ADMIN); + accessChecker.requirePermission(caller, "userPermissions", Action.ADMIN); perms = User.runAsLoginUser(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -2426,7 +2240,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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 @@ -2441,14 +2255,14 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, 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 @@ -2488,7 +2302,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preClose(ObserverContext c, boolean abortRequested) throws IOException { - requirePermission(getActiveUser(c), "preClose", Action.ADMIN); + requirePermission(c, "preClose", Action.ADMIN); } private void checkSystemOrSuperUser(User activeUser) throws IOException { @@ -2506,7 +2320,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, public void preStopRegionServer( ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "preStopRegionServer", Action.ADMIN); + requirePermission(ctx, "preStopRegionServer", Action.ADMIN); } private Map> makeFamilyMap(byte[] family, @@ -2536,7 +2350,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, for (TableName tableName: tableNamesList) { // Skip checks for a table that does not exist if (!admin.tableExists(tableName)) continue; - requirePermission(getActiveUser(ctx), "getTableDescriptors", tableName, null, null, + requirePermission(ctx, "getTableDescriptors", tableName, null, null, Action.ADMIN, Action.CREATE); } } @@ -2558,7 +2372,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, while (itr.hasNext()) { TableDescriptor htd = itr.next(); try { - requirePermission(getActiveUser(ctx), "getTableDescriptors", htd.getTableName(), null, null, + requirePermission(ctx, "getTableDescriptors", htd.getTableName(), null, null, Action.ADMIN, Action.CREATE); } catch (AccessDeniedException e) { itr.remove(); @@ -2574,7 +2388,7 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, while (itr.hasNext()) { TableDescriptor htd = itr.next(); try { - requireAccess(getActiveUser(ctx), "getTableNames", htd.getTableName(), Action.values()); + requireAccess(ctx, "getTableNames", htd.getTableName(), Action.values()); } catch (AccessDeniedException e) { itr.remove(); } @@ -2584,14 +2398,14 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preMergeRegions(final ObserverContext ctx, final RegionInfo[] regionsToMerge) throws IOException { - requirePermission(getActiveUser(ctx), "mergeRegions", regionsToMerge[0].getTable(), null, null, + requirePermission(ctx, "mergeRegions", regionsToMerge[0].getTable(), null, null, Action.ADMIN); } @Override public void preRollWALWriterRequest(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "preRollLogWriterRequest", Permission.Action.ADMIN); + requirePermission(ctx, "preRollLogWriterRequest", Permission.Action.ADMIN); } @Override @@ -2601,33 +2415,33 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final GlobalQuotaSettings quotas) throws IOException { - requirePermission(getActiveUser(ctx), "setUserQuota", Action.ADMIN); + requirePermission(ctx, "setUserQuota", Action.ADMIN); } @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final TableName tableName, final GlobalQuotaSettings quotas) throws IOException { - requirePermission(getActiveUser(ctx), "setUserTableQuota", tableName, null, null, Action.ADMIN); + requirePermission(ctx, "setUserTableQuota", tableName, null, null, Action.ADMIN); } @Override public void preSetUserQuota(final ObserverContext ctx, final String userName, final String namespace, final GlobalQuotaSettings quotas) throws IOException { - requirePermission(getActiveUser(ctx), "setUserNamespaceQuota", Action.ADMIN); + requirePermission(ctx, "setUserNamespaceQuota", Action.ADMIN); } @Override public void preSetTableQuota(final ObserverContext ctx, final TableName tableName, final GlobalQuotaSettings quotas) throws IOException { - requirePermission(getActiveUser(ctx), "setTableQuota", tableName, null, null, Action.ADMIN); + requirePermission(ctx, "setTableQuota", tableName, null, null, Action.ADMIN); } @Override public void preSetNamespaceQuota(final ObserverContext ctx, final String namespace, final GlobalQuotaSettings quotas) throws IOException { - requirePermission(getActiveUser(ctx), "setNamespaceQuota", Action.ADMIN); + requirePermission(ctx, "setNamespaceQuota", Action.ADMIN); } @Override @@ -2639,98 +2453,56 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, @Override public void preReplicateLogEntries(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "replicateLogEntries", Action.WRITE); + requirePermission(ctx, "replicateLogEntries", Action.WRITE); } @Override public void preClearCompactionQueues(ObserverContext ctx) throws IOException { - requirePermission(getActiveUser(ctx), "preClearCompactionQueues", Permission.Action.ADMIN); - } - - @Override - public void preMoveServersAndTables(ObserverContext ctx, - Set
servers, Set tables, String targetGroup) throws IOException { - requirePermission(getActiveUser(ctx), "moveServersAndTables", Action.ADMIN); - } - - @Override - public void preMoveServers(ObserverContext ctx, - Set
servers, String targetGroup) throws IOException { - requirePermission(getActiveUser(ctx), "moveServers", Action.ADMIN); - } - - @Override - public void preMoveTables(ObserverContext ctx, - Set tables, String targetGroup) throws IOException { - requirePermission(getActiveUser(ctx), "moveTables", Action.ADMIN); - } - - @Override - public void preAddRSGroup(ObserverContext ctx, - String name) throws IOException { - requirePermission(getActiveUser(ctx), "addRSGroup", Action.ADMIN); - } - - @Override - public void preRemoveRSGroup(ObserverContext ctx, - String name) throws IOException { - requirePermission(getActiveUser(ctx), "removeRSGroup", Action.ADMIN); - } - - @Override - public void preBalanceRSGroup(ObserverContext ctx, - String groupName) throws IOException { - requirePermission(getActiveUser(ctx), "balanceRSGroup", Action.ADMIN); - } - - @Override - public void preRemoveServers(ObserverContext ctx, - Set
servers) throws IOException { - requirePermission(getActiveUser(ctx), "removeServers", Action.ADMIN); + requirePermission(ctx, "preClearCompactionQueues", Permission.Action.ADMIN); } @Override public void preAddReplicationPeer(final ObserverContext ctx, String peerId, ReplicationPeerConfig peerConfig) throws IOException { - requirePermission(getActiveUser(ctx), "addReplicationPeer", Action.ADMIN); + requirePermission(ctx, "addReplicationPeer", Action.ADMIN); } @Override public void preRemoveReplicationPeer(final ObserverContext ctx, String peerId) throws IOException { - requirePermission(getActiveUser(ctx), "removeReplicationPeer", Action.ADMIN); + requirePermission(ctx, "removeReplicationPeer", Action.ADMIN); } @Override public void preEnableReplicationPeer(final ObserverContext ctx, String peerId) throws IOException { - requirePermission(getActiveUser(ctx), "enableReplicationPeer", Action.ADMIN); + requirePermission(ctx, "enableReplicationPeer", Action.ADMIN); } @Override public void preDisableReplicationPeer(final ObserverContext ctx, String peerId) throws IOException { - requirePermission(getActiveUser(ctx), "disableReplicationPeer", Action.ADMIN); + requirePermission(ctx, "disableReplicationPeer", Action.ADMIN); } @Override public void preGetReplicationPeerConfig(final ObserverContext ctx, String peerId) throws IOException { - requirePermission(getActiveUser(ctx), "getReplicationPeerConfig", Action.ADMIN); + requirePermission(ctx, "getReplicationPeerConfig", Action.ADMIN); } @Override public void preUpdateReplicationPeerConfig( final ObserverContext ctx, String peerId, ReplicationPeerConfig peerConfig) throws IOException { - requirePermission(getActiveUser(ctx), "updateReplicationPeerConfig", Action.ADMIN); + requirePermission(ctx, "updateReplicationPeerConfig", Action.ADMIN); } @Override public void preListReplicationPeers(final ObserverContext ctx, String regex) throws IOException { - requirePermission(getActiveUser(ctx), "listReplicationPeers", Action.ADMIN); + requirePermission(ctx, "listReplicationPeers", Action.ADMIN); } @Override @@ -2740,27 +2512,26 @@ public class AccessController implements MasterCoprocessor, RegionCoprocessor, // There are operations in the CREATE and ADMIN domain which may require lock, READ // or WRITE. So for any lock request, we check for these two perms irrespective of lock type. String reason = String.format("Description=%s", description); - checkLockPermissions(getActiveUser(ctx), namespace, tableName, regionInfos, reason); + checkLockPermissions(ctx, namespace, tableName, regionInfos, reason); } @Override public void preLockHeartbeat(ObserverContext ctx, TableName tableName, String description) throws IOException { - checkLockPermissions(getActiveUser(ctx), null, tableName, null, description); + checkLockPermissions(ctx, null, tableName, null, description); } - private void checkLockPermissions(User user, String namespace, - TableName tableName, RegionInfo[] regionInfos, String reason) - throws IOException { - if (namespace != null && !namespace.isEmpty()) { - requireNamespacePermission(user, reason, namespace, Action.ADMIN, Action.CREATE); - } else if (tableName != null || (regionInfos != null && regionInfos.length > 0)) { - // So, either a table or regions op. If latter, check perms ons table. - TableName tn = tableName != null? tableName: regionInfos[0].getTable(); - requireTablePermission(user, reason, tn, null, null, - Action.ADMIN, Action.CREATE); - } else { - throw new DoNotRetryIOException("Invalid lock level when requesting permissions."); + /** + * 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. + */ + public User getActiveUser(ObserverContext ctx) throws IOException { + // for non-rpc handling, fallback to system user + Optional optionalUser = ctx.getCaller(); + if (optionalUser.isPresent()) { + return optionalUser.get(); } + return userProvider.getCurrent(); } } 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 41a4dd3261b..76feff4c0b6 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 @@ -754,7 +754,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 d4b8e58cbd7..1ba60291621 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 @@ -105,6 +105,7 @@ import org.apache.hadoop.hbase.regionserver.querymatcher.DeleteTracker; 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.hbase.thirdparty.com.google.common.collect.Lists; import org.apache.hbase.thirdparty.com.google.common.collect.MapMaker; @@ -141,8 +142,8 @@ public class VisibilityController implements MasterCoprocessor, RegionCoprocesso private VisibilityLabelService visibilityLabelService; - /** 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 @@ -153,19 +154,15 @@ public class VisibilityController implements MasterCoprocessor, RegionCoprocesso 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/security/access/SecureTestUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/access/SecureTestUtil.java index 8bf792ba2e2..407ebf9be53 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 @@ -106,6 +106,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.set(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, "true"); configureSuperuser(conf); } @@ -129,6 +130,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 2.0.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 24355321e3d..be1b0e43324 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 @@ -2838,81 +2838,6 @@ public class TestAccessController extends SecureTestUtil { USER_GROUP_READ, USER_GROUP_ADMIN, USER_GROUP_CREATE); } - @Test - public void testMoveServers() throws Exception { - AccessTestAction action1 = new AccessTestAction() { - @Override - public Object run() throws Exception { - ACCESS_CONTROLLER.preMoveServers(ObserverContextImpl.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 testMoveTables() throws Exception { - AccessTestAction action1 = new AccessTestAction() { - @Override - public Object run() throws Exception { - ACCESS_CONTROLLER.preMoveTables(ObserverContextImpl.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 testAddGroup() throws Exception { - AccessTestAction action1 = new AccessTestAction() { - @Override - public Object run() throws Exception { - ACCESS_CONTROLLER.preAddRSGroup(ObserverContextImpl.createAndPrepare(CP_ENV), - 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(ObserverContextImpl.createAndPrepare(CP_ENV), - 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(ObserverContextImpl.createAndPrepare(CP_ENV), - null); - return null; - } - }; - - verifyAllowed(action1, SUPERUSER, USER_ADMIN); - verifyDenied(action1, USER_CREATE, USER_RW, USER_RO, USER_NONE, USER_OWNER); - } - @Test public void testAddReplicationPeer() throws Exception { AccessTestAction action = new AccessTestAction() { 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 7dbe2561469..4e2c4b7c303 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 89a48bd4006..18a028c6c4b 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 @@ -47,7 +47,7 @@ import org.apache.hadoop.hbase.security.visibility.ScanLabelGenerator; 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.ClientTests; import org.apache.hadoop.hbase.testclassification.MediumTests; @@ -78,389 +78,387 @@ public class TestThriftHBaseServiceHandlerWithLabels { private static final Logger LOG = LoggerFactory .getLogger(TestThriftHBaseServiceHandlerWithLabels.class); -private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); + 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) }; + // 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 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; + private static Configuration conf; -public void assertTColumnValuesEqual(List columnValuesA, + 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()); - } -} - -@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 = UTIL.getAdmin(); - HTableDescriptor tableDescriptor = new HTableDescriptor( - TableName.valueOf(tableAname)); - for (HColumnDescriptor family : families) { - tableDescriptor.addFamily(family); - } - 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); + 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())); } - return null; - } - }; - SUPERUSER.runAs(action); -} + }; + Collections.sort(columnValuesA, comparator); + Collections.sort(columnValuesB, comparator); -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); + 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()); + } } -} -@AfterClass -public static void afterClass() throws Exception { - UTIL.shutdownMiniCluster(); -} + @BeforeClass + public static void beforeClass() throws Exception { -@Before -public void setup() throws Exception { - -} - -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<>(1); - 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)); + 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 = UTIL.getAdmin(); + HTableDescriptor tableDescriptor = new HTableDescriptor( + TableName.valueOf(tableAname)); + for (HColumnDescriptor family : families) { + tableDescriptor.addFamily(family); } + 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); + } + 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); + } + } + + @AfterClass + public static void afterClass() throws Exception { + UTIL.shutdownMiniCluster(); + } + + @Before + public void setup() throws Exception { + + } + + 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<>(1); + 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); + } + + // create scan instance + TScan scan = new TScan(); + List columns = new ArrayList<>(1); + 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<>(2); + 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) { + } + } + + @Test + 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<>(1); + 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); + } + + // create scan instance + TScan scan = new TScan(); + List columns = new ArrayList<>(1); + 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<>(2); + 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 testGetsWithLabels() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testPutGet".getBytes(); + ByteBuffer table = wrap(tableAname); + + List columnValues = new ArrayList<>(2); + 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<>(2); + 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<>(1); - 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()); + @Test + public void testIncrementWithTags() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testIncrementWithTags".getBytes(); + ByteBuffer table = wrap(tableAname); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList<>(2); - 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) { - } -} - -@Test -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<>(1); - 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)); - } + List columnValues = new ArrayList<>(1); + 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<>(1); + 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<>(1); + 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()); } - // create scan instance - TScan scan = new TScan(); - List columns = new ArrayList<>(1); - TColumn column = new TColumn(); - column.setFamily(familyAname); - column.setQualifier(qualifierAname); - columns.add(column); - scan.setColumns(columns); - scan.setStartRow("testGetScannerResults".getBytes()); + @Test + public void testIncrementWithTagsWithNotMatchLabels() throws Exception { + ThriftHBaseServiceHandler handler = createHandler(); + byte[] rowName = "testIncrementWithTagsWithNotMatchLabels".getBytes(); + ByteBuffer table = wrap(tableAname); - // get 5 rows and check the returned results - scan.setStopRow("testGetScannerResults05".getBytes()); - TAuthorization tauth = new TAuthorization(); - List labels = new ArrayList<>(2); - 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()); + List columnValues = new ArrayList<>(1); + 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<>(1); + 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<>(1); + 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<>(1); + 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<>(1); + 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<>(1); + 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; } } - -@Test -public void testGetsWithLabels() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testPutGet".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList<>(2); - 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<>(2); - 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 testIncrementWithTags() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testIncrementWithTags".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList<>(1); - 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<>(1); - 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<>(1); - 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 testIncrementWithTagsWithNotMatchLabels() throws Exception { - ThriftHBaseServiceHandler handler = createHandler(); - byte[] rowName = "testIncrementWithTagsWithNotMatchLabels".getBytes(); - ByteBuffer table = wrap(tableAname); - - List columnValues = new ArrayList<>(1); - 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<>(1); - 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<>(1); - 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<>(1); - 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<>(1); - 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<>(1); - 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; -} -} diff --git a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc index 83043f71ce6..d5ea0765ba8 100644 --- a/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc +++ b/src/main/asciidoc/_chapters/appendix_acl_matrix.adoc @@ -164,6 +164,17 @@ 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/ops_mgt.adoc b/src/main/asciidoc/_chapters/ops_mgt.adoc index 7b0f89b47d8..bdd9c60565c 100644 --- a/src/main/asciidoc/_chapters/ops_mgt.adoc +++ b/src/main/asciidoc/_chapters/ops_mgt.adoc @@ -2703,5 +2703,15 @@ Viewing the Master log will give you insight on rsgroup operation. If it appears stuck, restart the Master process. +=== ACL +To enable ACL, add the following to your hbase-site.xml and restart your Master: + +[source,xml] +---- + + hbase.security.authorization + true + +---- diff --git a/src/main/asciidoc/_chapters/security.adoc b/src/main/asciidoc/_chapters/security.adoc index cca9364a608..ef7d6c46b5c 100644 --- a/src/main/asciidoc/_chapters/security.adoc +++ b/src/main/asciidoc/_chapters/security.adoc @@ -806,6 +806,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 @@ -1186,6 +1190,10 @@ NOTE: Visibility labels are not currently applied for superusers. + [source,xml] ---- + + hbase.security.authorization + true + hbase.coprocessor.region.classes org.apache.hadoop.hbase.security.visibility.VisibilityController @@ -1453,6 +1461,10 @@ As mentioned in the above sections, the interface `VisibilityLabelService` could [source,xml] ---- + + hbase.security.authorization + true + hbase.coprocessor.regionserver.classes org.apache.hadoop.hbase.security.visibility.VisibilityController$VisibilityReplication @@ -1671,6 +1683,10 @@ To enable secure bulk load, add the following properties to _hbase-site.xml_. [source,xml] ---- + + hbase.security.authorization + true + hbase.bulkload.staging.dir /tmp/hbase-staging @@ -1682,6 +1698,22 @@ To enable secure bulk load, add the following properties to _hbase-site.xml_. ---- +[[hbase.secure.enable]] +=== Secure Enable +After hbase-2.x, the default 'hbase.security.authorization' changed. +Before hbase-2.x, it defaulted to true, in later HBase versions, the +default became false. +So to enable hbase authorization, the following propertie must be configured in _hbase-site.xml_. +See link:https://issues.apache.org/jira/browse/HBASE-19483[HBASE-19483]; + +[source,xml] +---- + + hbase.security.authorization + true + +---- + [[security.example.config]] == Security Configuration Example @@ -1703,6 +1735,10 @@ All options have been discussed separately in the sections above. hbase, admin + + hbase.security.authorization + true + hbase.coprocessor.region.classes org.apache.hadoop.hbase.security.access.AccessController,