HBASE-17472: Correct the semantic of permission grant

Signed-off-by: zhangduo <zhangduo@apache.org>
This commit is contained in:
huzheng 2017-02-16 18:20:41 +08:00 committed by zhangduo
parent 964e17f7b0
commit 45357c078d
12 changed files with 466 additions and 111 deletions

View File

@ -2354,15 +2354,15 @@ public final class ProtobufUtil {
*/
public static void grant(RpcController controller,
AccessControlService.BlockingInterface protocol, String userShortName,
Permission.Action... actions) throws ServiceException {
boolean mergeExistingPermissions, Permission.Action... actions) throws ServiceException {
List<AccessControlProtos.Permission.Action> permActions =
Lists.newArrayListWithCapacity(actions.length);
for (Permission.Action a : actions) {
permActions.add(ProtobufUtil.toPermissionAction(a));
}
AccessControlProtos.GrantRequest request = RequestConverter.
buildGrantRequest(userShortName, permActions.toArray(
new AccessControlProtos.Permission.Action[actions.length]));
AccessControlProtos.GrantRequest request =
RequestConverter.buildGrantRequest(userShortName, mergeExistingPermissions,
permActions.toArray(new AccessControlProtos.Permission.Action[actions.length]));
protocol.grant(controller, request);
}
@ -2382,15 +2382,16 @@ public final class ProtobufUtil {
*/
public static void grant(RpcController controller,
AccessControlService.BlockingInterface protocol, String userShortName, TableName tableName,
byte[] f, byte[] q, Permission.Action... actions) throws ServiceException {
byte[] f, byte[] q, boolean mergeExistingPermissions, Permission.Action... actions)
throws ServiceException {
List<AccessControlProtos.Permission.Action> permActions =
Lists.newArrayListWithCapacity(actions.length);
for (Permission.Action a : actions) {
permActions.add(ProtobufUtil.toPermissionAction(a));
}
AccessControlProtos.GrantRequest request = RequestConverter.
buildGrantRequest(userShortName, tableName, f, q, permActions.toArray(
new AccessControlProtos.Permission.Action[actions.length]));
AccessControlProtos.GrantRequest request =
RequestConverter.buildGrantRequest(userShortName, tableName, f, q, mergeExistingPermissions,
permActions.toArray(new AccessControlProtos.Permission.Action[actions.length]));
protocol.grant(controller, request);
}
@ -2406,15 +2407,15 @@ public final class ProtobufUtil {
*/
public static void grant(RpcController controller,
AccessControlService.BlockingInterface protocol, String userShortName, String namespace,
Permission.Action... actions) throws ServiceException {
boolean mergeExistingPermissions, Permission.Action... actions) throws ServiceException {
List<AccessControlProtos.Permission.Action> permActions =
Lists.newArrayListWithCapacity(actions.length);
for (Permission.Action a : actions) {
permActions.add(ProtobufUtil.toPermissionAction(a));
}
AccessControlProtos.GrantRequest request = RequestConverter.
buildGrantRequest(userShortName, namespace, permActions.toArray(
new AccessControlProtos.Permission.Action[actions.length]));
AccessControlProtos.GrantRequest request =
RequestConverter.buildGrantRequest(userShortName, namespace, mergeExistingPermissions,
permActions.toArray(new AccessControlProtos.Permission.Action[actions.length]));
protocol.grant(controller, request);
}

View File

@ -1542,8 +1542,8 @@ public final class RequestConverter {
* @param actions the permissions to be granted
* @return A {@link AccessControlProtos} GrantRequest
*/
public static AccessControlProtos.GrantRequest buildGrantRequest(
String username, AccessControlProtos.Permission.Action... actions) {
public static AccessControlProtos.GrantRequest buildGrantRequest(String username,
boolean mergeExistingPermissions, AccessControlProtos.Permission.Action... actions) {
AccessControlProtos.Permission.Builder ret =
AccessControlProtos.Permission.newBuilder();
AccessControlProtos.GlobalPermission.Builder permissionBuilder =
@ -1554,11 +1554,9 @@ public final class RequestConverter {
ret.setType(AccessControlProtos.Permission.Type.Global)
.setGlobalPermission(permissionBuilder);
return AccessControlProtos.GrantRequest.newBuilder()
.setUserPermission(
AccessControlProtos.UserPermission.newBuilder()
.setUser(ByteString.copyFromUtf8(username))
.setPermission(ret)
).build();
.setUserPermission(AccessControlProtos.UserPermission.newBuilder()
.setUser(ByteString.copyFromUtf8(username)).setPermission(ret))
.setMergeExistingPermissions(mergeExistingPermissions).build();
}
/**
@ -1572,7 +1570,7 @@ public final class RequestConverter {
* @return A {@link AccessControlProtos} GrantRequest
*/
public static AccessControlProtos.GrantRequest buildGrantRequest(
String username, TableName tableName, byte[] family, byte[] qualifier,
String username, TableName tableName, byte[] family, byte[] qualifier, boolean mergeExistingPermissions,
AccessControlProtos.Permission.Action... actions) {
AccessControlProtos.Permission.Builder ret =
AccessControlProtos.Permission.newBuilder();
@ -1599,7 +1597,7 @@ public final class RequestConverter {
AccessControlProtos.UserPermission.newBuilder()
.setUser(ByteString.copyFromUtf8(username))
.setPermission(ret)
).build();
).setMergeExistingPermissions(mergeExistingPermissions).build();
}
/**
@ -1610,8 +1608,8 @@ public final class RequestConverter {
* @param actions the permissions to be granted
* @return A {@link AccessControlProtos} GrantRequest
*/
public static AccessControlProtos.GrantRequest buildGrantRequest(
String username, String namespace,
public static AccessControlProtos.GrantRequest buildGrantRequest(String username,
String namespace, boolean mergeExistingPermissions,
AccessControlProtos.Permission.Action... actions) {
AccessControlProtos.Permission.Builder ret =
AccessControlProtos.Permission.newBuilder();
@ -1626,11 +1624,9 @@ public final class RequestConverter {
ret.setType(AccessControlProtos.Permission.Type.Namespace)
.setNamespacePermission(permissionBuilder);
return AccessControlProtos.GrantRequest.newBuilder()
.setUserPermission(
AccessControlProtos.UserPermission.newBuilder()
.setUser(ByteString.copyFromUtf8(username))
.setPermission(ret)
).build();
.setUserPermission(AccessControlProtos.UserPermission.newBuilder()
.setUser(ByteString.copyFromUtf8(username)).setPermission(ret))
.setMergeExistingPermissions(mergeExistingPermissions).build();
}
/**

View File

@ -90,23 +90,67 @@ public class AccessControlClient {
* @param userName
* @param family
* @param qual
* @param mergeExistingPermissions If set to false, later granted permissions will override
* previous granted permissions. otherwise, it'll merge with previous granted
* permissions.
* @param actions
* @throws Throwable
*/
public static void grant(Connection connection, final TableName tableName, final String userName,
final byte[] family, final byte[] qual, boolean mergeExistingPermissions,
final Permission.Action... actions) throws Throwable {
PayloadCarryingRpcController controller =
((ClusterConnection) connection).getRpcControllerFactory().newController();
controller.setPriority(tableName);
try (Table table = connection.getTable(ACL_TABLE_NAME)) {
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, tableName,
family, qual, mergeExistingPermissions, actions);
}
}
/**
* Grants permission on the specified table for the specified user. If permissions for a specified
* user exists, later granted permissions will override previous granted permissions.
* @param connection
* @param tableName
* @param userName
* @param family
* @param qual
* @param actions
* @throws Throwable
*/
public static void grant(final Connection connection, final TableName tableName,
final String userName, final byte[] family, final byte[] qual,
final Permission.Action... actions) throws Throwable {
PayloadCarryingRpcController controller
= ((ClusterConnection) connection).getRpcControllerFactory().newController();
controller.setPriority(tableName);
try (Table table = connection.getTable(ACL_TABLE_NAME)) {
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, tableName,
family, qual, actions);
}
grant(connection, tableName, userName, family, qual, false, actions);
}
/**
* Grants permission on the specified namespace for the specified user.
* @param connection
* @param namespace
* @param userName
* @param mergeExistingPermissions If set to false, later granted permissions will override
* previous granted permissions. otherwise, it'll merge with previous granted
* permissions.
* @param actions
* @throws Throwable
*/
public static void grant(final Connection connection, final String namespace,
final String userName, boolean mergeExistingPermissions, final Permission.Action... actions)
throws Throwable {
PayloadCarryingRpcController controller =
((ClusterConnection) connection).getRpcControllerFactory().newController();
try (Table table = connection.getTable(ACL_TABLE_NAME)) {
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, namespace,
mergeExistingPermissions, actions);
}
}
/**
* Grants permission on the specified namespace for the specified user. If permissions for a
* specified user exists, later granted permissions will override previous granted permissions.
* @param connection The Connection instance to use
* @param namespace
* @param userName
@ -115,26 +159,39 @@ public class AccessControlClient {
*/
public static void grant(final Connection connection, final String namespace,
final String userName, final Permission.Action... actions) throws Throwable {
PayloadCarryingRpcController controller
= ((ClusterConnection) connection).getRpcControllerFactory().newController();
grant(connection, namespace, userName, false, actions);
}
/**
* Grant global permissions for the specified user.
* @param connection The Connection instance to use
* @param userName
* @param mergeExistingPermissions If set to false, later granted permissions will override
* previous granted permissions. otherwise, it'll merge with previous granted
* permissions.
* @param actions
* @throws Throwable
*/
public static void grant(final Connection connection, final String userName,
boolean mergeExistingPermissions, final Permission.Action... actions) throws Throwable {
PayloadCarryingRpcController controller =
((ClusterConnection) connection).getRpcControllerFactory().newController();
try (Table table = connection.getTable(ACL_TABLE_NAME)) {
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, namespace,
actions);
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName,
mergeExistingPermissions, actions);
}
}
/**
* @param connection The Connection instance to use
* Grant global permissions for the specified user.
* @param connection The Connection instance to use
* @param userName
* @param actions
* @throws Throwable
*/
public static void grant(final Connection connection, final String userName,
final Permission.Action... actions) throws Throwable {
PayloadCarryingRpcController controller
= ((ClusterConnection) connection).getRpcControllerFactory().newController();
try (Table table = connection.getTable(ACL_TABLE_NAME)) {
ProtobufUtil.grant(controller, getAccessControlServiceStub(table), userName, actions);
}
final Permission.Action... actions) throws Throwable {
grant(connection, userName, false, actions);
}
public static boolean isAccessControllerRunning(final Connection connection)

View File

@ -311,6 +311,21 @@ public class TablePermission extends Permission {
return super.implies(action);
}
public boolean tableFieldsEqual(TablePermission other) {
if (!(((table == null && other.getTableName() == null)
|| (table != null && table.equals(other.getTableName())))
&& ((family == null && other.getFamily() == null)
|| Bytes.equals(family, other.getFamily()))
&& ((qualifier == null && other.getQualifier() == null)
|| Bytes.equals(qualifier, other.getQualifier()))
&& ((namespace == null && other.getNamespace() == null)
|| (namespace != null && namespace.equals(other.getNamespace()))))) {
return false;
} else {
return true;
}
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH",
justification="Passed on construction except on constructor not to be used")
@ -320,15 +335,7 @@ public class TablePermission extends Permission {
}
TablePermission other = (TablePermission)obj;
if (!(((table == null && other.getTableName() == null) ||
(table != null && table.equals(other.getTableName()))) &&
((family == null && other.getFamily() == null) ||
Bytes.equals(family, other.getFamily())) &&
((qualifier == null && other.getQualifier() == null) ||
Bytes.equals(qualifier, other.getQualifier())) &&
((namespace == null && other.getNamespace() == null) ||
(namespace != null && namespace.equals(other.getNamespace())))
)) {
if(!this.tableFieldsEqual(other)){
return false;
}

View File

@ -5569,6 +5569,16 @@ public final class AccessControlProtos {
* <code>required .hbase.pb.UserPermission user_permission = 1;</code>
*/
org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.UserPermissionOrBuilder getUserPermissionOrBuilder();
// optional bool merge_existing_permissions = 2 [default = false];
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
boolean hasMergeExistingPermissions();
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
boolean getMergeExistingPermissions();
}
/**
* Protobuf type {@code hbase.pb.GrantRequest}
@ -5634,6 +5644,11 @@ public final class AccessControlProtos {
bitField0_ |= 0x00000001;
break;
}
case 16: {
bitField0_ |= 0x00000002;
mergeExistingPermissions_ = input.readBool();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@ -5696,8 +5711,25 @@ public final class AccessControlProtos {
return userPermission_;
}
// optional bool merge_existing_permissions = 2 [default = false];
public static final int MERGE_EXISTING_PERMISSIONS_FIELD_NUMBER = 2;
private boolean mergeExistingPermissions_;
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public boolean hasMergeExistingPermissions() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public boolean getMergeExistingPermissions() {
return mergeExistingPermissions_;
}
private void initFields() {
userPermission_ = org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.UserPermission.getDefaultInstance();
mergeExistingPermissions_ = false;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@ -5722,6 +5754,9 @@ public final class AccessControlProtos {
if (((bitField0_ & 0x00000001) == 0x00000001)) {
output.writeMessage(1, userPermission_);
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeBool(2, mergeExistingPermissions_);
}
getUnknownFields().writeTo(output);
}
@ -5735,6 +5770,10 @@ public final class AccessControlProtos {
size += com.google.protobuf.CodedOutputStream
.computeMessageSize(1, userPermission_);
}
if (((bitField0_ & 0x00000002) == 0x00000002)) {
size += com.google.protobuf.CodedOutputStream
.computeBoolSize(2, mergeExistingPermissions_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@ -5763,6 +5802,11 @@ public final class AccessControlProtos {
result = result && getUserPermission()
.equals(other.getUserPermission());
}
result = result && (hasMergeExistingPermissions() == other.hasMergeExistingPermissions());
if (hasMergeExistingPermissions()) {
result = result && (getMergeExistingPermissions()
== other.getMergeExistingPermissions());
}
result = result &&
getUnknownFields().equals(other.getUnknownFields());
return result;
@ -5780,6 +5824,10 @@ public final class AccessControlProtos {
hash = (37 * hash) + USER_PERMISSION_FIELD_NUMBER;
hash = (53 * hash) + getUserPermission().hashCode();
}
if (hasMergeExistingPermissions()) {
hash = (37 * hash) + MERGE_EXISTING_PERMISSIONS_FIELD_NUMBER;
hash = (53 * hash) + hashBoolean(getMergeExistingPermissions());
}
hash = (29 * hash) + getUnknownFields().hashCode();
memoizedHashCode = hash;
return hash;
@ -5896,6 +5944,8 @@ public final class AccessControlProtos {
userPermissionBuilder_.clear();
}
bitField0_ = (bitField0_ & ~0x00000001);
mergeExistingPermissions_ = false;
bitField0_ = (bitField0_ & ~0x00000002);
return this;
}
@ -5932,6 +5982,10 @@ public final class AccessControlProtos {
} else {
result.userPermission_ = userPermissionBuilder_.build();
}
if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
to_bitField0_ |= 0x00000002;
}
result.mergeExistingPermissions_ = mergeExistingPermissions_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@ -5951,6 +6005,9 @@ public final class AccessControlProtos {
if (other.hasUserPermission()) {
mergeUserPermission(other.getUserPermission());
}
if (other.hasMergeExistingPermissions()) {
setMergeExistingPermissions(other.getMergeExistingPermissions());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@ -6103,6 +6160,39 @@ public final class AccessControlProtos {
return userPermissionBuilder_;
}
// optional bool merge_existing_permissions = 2 [default = false];
private boolean mergeExistingPermissions_ ;
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public boolean hasMergeExistingPermissions() {
return ((bitField0_ & 0x00000002) == 0x00000002);
}
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public boolean getMergeExistingPermissions() {
return mergeExistingPermissions_;
}
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public Builder setMergeExistingPermissions(boolean value) {
bitField0_ |= 0x00000002;
mergeExistingPermissions_ = value;
onChanged();
return this;
}
/**
* <code>optional bool merge_existing_permissions = 2 [default = false];</code>
*/
public Builder clearMergeExistingPermissions() {
bitField0_ = (bitField0_ & ~0x00000002);
mergeExistingPermissions_ = false;
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:hbase.pb.GrantRequest)
}
@ -10432,29 +10522,30 @@ public final class AccessControlProtos {
"\0132-.hbase.pb.UsersAndPermissions.UserPer" +
"missions\032J\n\017UserPermissions\022\014\n\004user\030\001 \002(" +
"\014\022)\n\013permissions\030\002 \003(\0132\024.hbase.pb.Permis" +
"sion\"A\n\014GrantRequest\0221\n\017user_permission\030" +
"\001 \002(\0132\030.hbase.pb.UserPermission\"\017\n\rGrant" +
"Response\"B\n\rRevokeRequest\0221\n\017user_permis" +
"sion\030\001 \002(\0132\030.hbase.pb.UserPermission\"\020\n\016" +
"RevokeResponse\"\205\001\n\031GetUserPermissionsReq" +
"uest\022\'\n\004type\030\001 \001(\0162\031.hbase.pb.Permission" +
".Type\022\'\n\ntable_name\030\002 \001(\0132\023.hbase.pb.Tab",
"leName\022\026\n\016namespace_name\030\003 \001(\014\"O\n\032GetUse" +
"rPermissionsResponse\0221\n\017user_permission\030" +
"\001 \003(\0132\030.hbase.pb.UserPermission\"C\n\027Check" +
"PermissionsRequest\022(\n\npermission\030\001 \003(\0132\024" +
".hbase.pb.Permission\"\032\n\030CheckPermissions" +
"Response2\311\002\n\024AccessControlService\0228\n\005Gra" +
"nt\022\026.hbase.pb.GrantRequest\032\027.hbase.pb.Gr" +
"antResponse\022;\n\006Revoke\022\027.hbase.pb.RevokeR" +
"equest\032\030.hbase.pb.RevokeResponse\022_\n\022GetU" +
"serPermissions\022#.hbase.pb.GetUserPermiss",
"ionsRequest\032$.hbase.pb.GetUserPermission" +
"sResponse\022Y\n\020CheckPermissions\022!.hbase.pb" +
".CheckPermissionsRequest\032\".hbase.pb.Chec" +
"kPermissionsResponseBI\n*org.apache.hadoo" +
"p.hbase.protobuf.generatedB\023AccessContro" +
"lProtosH\001\210\001\001\240\001\001"
"sion\"l\n\014GrantRequest\0221\n\017user_permission\030" +
"\001 \002(\0132\030.hbase.pb.UserPermission\022)\n\032merge" +
"_existing_permissions\030\002 \001(\010:\005false\"\017\n\rGr" +
"antResponse\"B\n\rRevokeRequest\0221\n\017user_per" +
"mission\030\001 \002(\0132\030.hbase.pb.UserPermission\"" +
"\020\n\016RevokeResponse\"\205\001\n\031GetUserPermissions" +
"Request\022\'\n\004type\030\001 \001(\0162\031.hbase.pb.Permiss",
"ion.Type\022\'\n\ntable_name\030\002 \001(\0132\023.hbase.pb." +
"TableName\022\026\n\016namespace_name\030\003 \001(\014\"O\n\032Get" +
"UserPermissionsResponse\0221\n\017user_permissi" +
"on\030\001 \003(\0132\030.hbase.pb.UserPermission\"C\n\027Ch" +
"eckPermissionsRequest\022(\n\npermission\030\001 \003(" +
"\0132\024.hbase.pb.Permission\"\032\n\030CheckPermissi" +
"onsResponse2\311\002\n\024AccessControlService\0228\n\005" +
"Grant\022\026.hbase.pb.GrantRequest\032\027.hbase.pb" +
".GrantResponse\022;\n\006Revoke\022\027.hbase.pb.Revo" +
"keRequest\032\030.hbase.pb.RevokeResponse\022_\n\022G",
"etUserPermissions\022#.hbase.pb.GetUserPerm" +
"issionsRequest\032$.hbase.pb.GetUserPermiss" +
"ionsResponse\022Y\n\020CheckPermissions\022!.hbase" +
".pb.CheckPermissionsRequest\032\".hbase.pb.C" +
"heckPermissionsResponseBI\n*org.apache.ha" +
"doop.hbase.protobuf.generatedB\023AccessCon" +
"trolProtosH\001\210\001\001\240\001\001"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -10508,7 +10599,7 @@ public final class AccessControlProtos {
internal_static_hbase_pb_GrantRequest_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_hbase_pb_GrantRequest_descriptor,
new java.lang.String[] { "UserPermission", });
new java.lang.String[] { "UserPermission", "MergeExistingPermissions", });
internal_static_hbase_pb_GrantResponse_descriptor =
getDescriptor().getMessageTypes().get(7);
internal_static_hbase_pb_GrantResponse_fieldAccessorTable = new

View File

@ -79,6 +79,7 @@ message UsersAndPermissions {
message GrantRequest {
required UserPermission user_permission = 1;
optional bool merge_existing_permissions = 2 [default = false];
}
message GrantResponse {

View File

@ -139,15 +139,8 @@ public class AccessControlLists {
master.createSystemTable(ACL_TABLEDESC);
}
/**
* Stores a new user permission grant in the access control lists table.
* @param conf the configuration
* @param userPerm the details of the permission to be granted
* @param t acl table instance. It is closed upon method return
* @throws IOException in the case of an error accessing the metadata table
*/
static void addUserPermission(Configuration conf, UserPermission userPerm, Table t)
throws IOException {
static void addUserPermission(Configuration conf, UserPermission userPerm, Table t,
boolean mergeExistingPermissions) throws IOException {
Permission.Action[] actions = userPerm.getActions();
byte[] rowKey = userPermissionRowKey(userPerm);
Put p = new Put(rowKey);
@ -159,16 +152,38 @@ public class AccessControlLists {
throw new IOException(msg);
}
byte[] value = new byte[actions.length];
for (int i = 0; i < actions.length; i++) {
value[i] = actions[i].code();
Set<Permission.Action> actionSet = new TreeSet<Permission.Action>();
if (mergeExistingPermissions) {
List<UserPermission> perms = getUserPermissions(conf, rowKey);
UserPermission currentPerm = null;
for (UserPermission perm : perms) {
if (Bytes.equals(perm.getUser(), userPerm.getUser())
&& ((userPerm.isGlobal() && ACL_TABLE_NAME.equals(perm.getTableName()))
|| perm.tableFieldsEqual(userPerm))) {
currentPerm = perm;
break;
}
}
if (currentPerm != null && currentPerm.getActions() != null) {
actionSet.addAll(Arrays.asList(currentPerm.getActions()));
}
}
// merge current action with new action.
actionSet.addAll(Arrays.asList(actions));
// serialize to byte array.
byte[] value = new byte[actionSet.size()];
int index = 0;
for (Permission.Action action : actionSet) {
value[index++] = action.code();
}
p.addImmutable(ACL_LIST_FAMILY, key, value);
if (LOG.isDebugEnabled()) {
LOG.debug("Writing permission with rowKey "+
Bytes.toString(rowKey)+" "+
Bytes.toString(key)+": "+Bytes.toStringBinary(value)
);
LOG.debug("Writing permission with rowKey " + Bytes.toString(rowKey) + " "
+ Bytes.toString(key) + ": " + Bytes.toStringBinary(value));
}
try {
t.put(p);
@ -177,6 +192,18 @@ public class AccessControlLists {
}
}
/**
* Stores a new user permission grant in the access control lists table.
* @param conf the configuration
* @param userPerm the details of the permission to be granted
* @param t acl table instance. It is closed upon method return
* @throws IOException in the case of an error accessing the metadata table
*/
static void addUserPermission(Configuration conf, UserPermission userPerm, Table t)
throws IOException {
addUserPermission(conf, userPerm, t, false);
}
/**
* Removes a previously granted permission from the stored access control
* lists. The {@link TablePermission} being removed must exactly match what

View File

@ -2237,7 +2237,7 @@ public class AccessController extends BaseMasterAndRegionObserver
@Override
public void grant(RpcController controller,
AccessControlProtos.GrantRequest request,
final AccessControlProtos.GrantRequest request,
RpcCallback<AccessControlProtos.GrantResponse> done) {
final UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
AccessControlProtos.GrantResponse response = null;
@ -2266,7 +2266,8 @@ public class AccessController extends BaseMasterAndRegionObserver
@Override
public Void run() throws Exception {
AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm,
regionEnv.getTable(AccessControlLists.ACL_TABLE_NAME));
regionEnv.getTable(AccessControlLists.ACL_TABLE_NAME),
request.getMergeExistingPermissions());
return null;
}
});

View File

@ -372,7 +372,7 @@ public class SecureTestUtil {
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, user, actions);
ProtobufUtil.grant(null, protocol, user, false, actions);
}
}
return null;
@ -418,7 +418,7 @@ public class SecureTestUtil {
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, user, namespace, actions);
ProtobufUtil.grant(null, protocol, user, namespace, false, actions);
}
}
return null;
@ -440,7 +440,23 @@ public class SecureTestUtil {
try {
AccessControlClient.grant(connection, namespace, user, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("grant failed: ", t);
}
return null;
}
});
}
public static void grantOnNamespaceUsingAccessControlClient(final HBaseTestingUtility util,
final Connection connection, final String user, final String namespace,
final boolean mergeExistingPermissions, final Permission.Action... actions) throws Exception {
SecureTestUtil.updateACLs(util, new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
AccessControlClient.grant(connection, namespace, user, mergeExistingPermissions, actions);
} catch (Throwable t) {
LOG.error("grant failed: ", t);
}
return null;
}
@ -461,7 +477,7 @@ public class SecureTestUtil {
try {
AccessControlClient.revoke(connection, namespace, user, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("revoke failed: ", t);
}
return null;
}
@ -507,7 +523,7 @@ public class SecureTestUtil {
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, user, table, family, qualifier, actions);
ProtobufUtil.grant(null, protocol, user, table, family, qualifier, false, actions);
}
}
return null;
@ -529,13 +545,33 @@ public class SecureTestUtil {
try {
AccessControlClient.grant(connection, table, user, family, qualifier, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("grant failed: ", t);
}
return null;
}
});
}
public static void grantOnTableUsingAccessControlClient(final HBaseTestingUtility util,
final Connection connection, final String user, final TableName table, final byte[] family,
final byte[] qualifier, final boolean mergeExistingPermissions,
final Permission.Action... actions) throws Exception {
SecureTestUtil.updateACLs(util, new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
AccessControlClient.grant(connection, table, user, family, qualifier,
mergeExistingPermissions, actions);
} catch (Throwable t) {
LOG.error("grant failed: ", t);
}
return null;
}
});
}
/**
* Grant global permissions to the given user using AccessControlClient. Will wait until all
* active AccessController instances have updated their permissions caches or will
@ -550,7 +586,23 @@ public class SecureTestUtil {
try {
AccessControlClient.grant(connection, user, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("grant failed: ", t);
}
return null;
}
});
}
public static void grantGlobalUsingAccessControlClient(final HBaseTestingUtility util,
final Connection connection, final String user, final boolean mergeExistingPermissions,
final Permission.Action... actions) throws Exception {
SecureTestUtil.updateACLs(util, new Callable<Void>() {
@Override
public Void call() throws Exception {
try {
AccessControlClient.grant(connection, user, mergeExistingPermissions, actions);
} catch (Throwable t) {
LOG.error("grant failed: ", t);
}
return null;
}
@ -595,7 +647,7 @@ public class SecureTestUtil {
try {
AccessControlClient.revoke(connection, table, user, family, qualifier, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("revoke failed: ", t);
}
return null;
}
@ -616,7 +668,7 @@ public class SecureTestUtil {
try {
AccessControlClient.revoke(connection, user, actions);
} catch (Throwable t) {
t.printStackTrace();
LOG.error("revoke failed: ", t);
}
return null;
}

View File

@ -58,7 +58,6 @@ import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.Tag;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Append;
import org.apache.hadoop.hbase.client.Connection;
@ -1192,7 +1191,7 @@ public class TestAccessController extends SecureTestUtil {
BlockingRpcChannel service = acl.coprocessorService(TEST_TABLE.getName());
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null,
ProtobufUtil.grant(null, protocol, USER_RO.getShortName(), TEST_TABLE, TEST_FAMILY, null, false,
Action.READ);
}
return null;
@ -2405,6 +2404,128 @@ public class TestAccessController extends SecureTestUtil {
}
}
@Test(timeout = 180000)
public void testAccessControlClientGrantRevokeWithMultiActions() throws Exception {
User testGrantRevoke = User.createUserForTesting(conf, "testGrantRevoke", new String[0]);
AccessTestAction getAction = new AccessTestAction() {
@Override
public Object run() throws Exception {
try (Connection conn = ConnectionFactory.createConnection(conf);
Table t = conn.getTable(TEST_TABLE)) {
return t.get(new Get(TEST_ROW));
}
}
};
AccessTestAction putAction = new AccessTestAction() {
@Override
public Object run() throws Exception {
Put p = new Put(TEST_ROW);
p.addColumn(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(1));
try (Connection conn = ConnectionFactory.createConnection(conf);
Table t = conn.getTable(TEST_TABLE)) {
t.put(p);
return null;
}
}
};
verifyDenied(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant global READ permissions to testGrantRevoke.
String userName = testGrantRevoke.getShortName();
try {
grantGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, true,
Permission.Action.READ);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant global READ permissions to testGrantRevoke.
try {
grantGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, true,
Permission.Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyAllowed(putAction, testGrantRevoke);
// Revoke global READ permission to testGrantRevoke.
try {
revokeGlobalUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName,
Permission.Action.READ, Permission.Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.revoke ", e);
}
verifyDenied(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant table READ & WRITE permissions to testGrantRevoke
try {
grantOnTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE,
null, null, true, Permission.Action.READ);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant table WRITE permissions to testGrantRevoke
try {
grantOnTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE,
null, null, true, Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyAllowed(putAction, testGrantRevoke);
// Revoke table READ & WRITE permission to testGrantRevoke.
try {
revokeFromTableUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName, TEST_TABLE,
null, null, Permission.Action.READ, Permission.Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.revoke ", e);
}
verifyDenied(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant Namespace READ permissions to testGrantRevoke
String namespace = TEST_TABLE.getNamespaceAsString();
try {
grantOnNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName,
namespace, true, Permission.Action.READ);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
// Grant Namespace WRITE permissions to testGrantRevoke
try {
grantOnNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName,
namespace, true, Permission.Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.grant. ", e);
}
verifyAllowed(getAction, testGrantRevoke);
verifyAllowed(putAction, testGrantRevoke);
// Revoke table READ & WRITE permission to testGrantRevoke.
try {
revokeFromNamespaceUsingAccessControlClient(TEST_UTIL, systemUserConnection, userName,
TEST_TABLE.getNamespaceAsString(), Permission.Action.READ, Permission.Action.WRITE);
} catch (Throwable e) {
LOG.error("error during call of AccessControlClient.revoke ", e);
}
verifyDenied(getAction, testGrantRevoke);
verifyDenied(putAction, testGrantRevoke);
}
@Test (timeout=180000)
public void testAccessControlClientGrantRevokeOnNamespace() throws Exception {
// Create user for testing, who has no READ privileges by default.

View File

@ -357,7 +357,7 @@ public class TestNamespaceCommands extends SecureTestUtil {
acl.coprocessorService(HConstants.EMPTY_START_ROW);
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, testUser, TEST_NAMESPACE, Action.WRITE);
ProtobufUtil.grant(null, protocol, testUser, TEST_NAMESPACE, false, Action.WRITE);
}
return null;
}
@ -372,7 +372,7 @@ public class TestNamespaceCommands extends SecureTestUtil {
AccessControlService.BlockingInterface protocol =
AccessControlService.newBlockingStub(service);
ProtobufUtil.grant(null, protocol, USER_GROUP_NS_ADMIN.getShortName(),
TEST_NAMESPACE, Action.READ);
TEST_NAMESPACE, false, Action.READ);
}
return null;
}

View File

@ -45,6 +45,7 @@ import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.security.access.Permission.Action;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
@ -449,7 +450,7 @@ public class TestTablePermissions {
assertEquals("Should have 1 permission for user3", 1, user3Perms.size());
assertEquals("user3 should have ADMIN, READ, CREATE permission",
new Permission.Action[] {
Permission.Action.ADMIN, Permission.Action.READ, Permission.Action.CREATE
Permission.Action.READ, Permission.Action.CREATE, Permission.Action.ADMIN,
},
user3Perms.get(0).getActions());
}