HBASE-8409 Security support for namespaces
git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1513666 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
48c1e7aff3
commit
ac10b3c13d
|
@ -1565,10 +1565,10 @@ public final class ProtobufUtil {
|
|||
* @return the converted Permission
|
||||
*/
|
||||
public static Permission toPermission(AccessControlProtos.Permission proto) {
|
||||
if (proto.hasTableName()) {
|
||||
if (proto.getType() != AccessControlProtos.Permission.Type.Global) {
|
||||
return toTablePermission(proto);
|
||||
} else {
|
||||
List<Permission.Action> actions = toPermissionActions(proto.getActionList());
|
||||
List<Permission.Action> actions = toPermissionActions(proto.getGlobalPermission().getActionList());
|
||||
return new Permission(actions.toArray(new Permission.Action[actions.size()]));
|
||||
}
|
||||
}
|
||||
|
@ -1580,18 +1580,43 @@ public final class ProtobufUtil {
|
|||
* @return the converted TablePermission
|
||||
*/
|
||||
public static TablePermission toTablePermission(AccessControlProtos.Permission proto) {
|
||||
List<Permission.Action> actions = toPermissionActions(proto.getActionList());
|
||||
if(proto.getType() == AccessControlProtos.Permission.Type.Global) {
|
||||
AccessControlProtos.GlobalPermission perm = proto.getGlobalPermission();
|
||||
List<Permission.Action> actions = toPermissionActions(perm.getActionList());
|
||||
|
||||
byte[] qualifier = null;
|
||||
byte[] family = null;
|
||||
TableName table = null;
|
||||
return new TablePermission(null, null, null,
|
||||
actions.toArray(new Permission.Action[actions.size()]));
|
||||
}
|
||||
if(proto.getType() == AccessControlProtos.Permission.Type.Namespace) {
|
||||
AccessControlProtos.NamespacePermission perm = proto.getNamespacePermission();
|
||||
List<Permission.Action> actions = toPermissionActions(perm.getActionList());
|
||||
|
||||
if (proto.hasTableName()) table = ProtobufUtil.toTableName(proto.getTableName());
|
||||
if (proto.hasFamily()) family = proto.getFamily().toByteArray();
|
||||
if (proto.hasQualifier()) qualifier = proto.getQualifier().toByteArray();
|
||||
if(!proto.hasNamespacePermission()) {
|
||||
throw new IllegalStateException("Namespace must not be empty in NamespacePermission");
|
||||
}
|
||||
String namespace = perm.getNamespaceName().toStringUtf8();
|
||||
return new TablePermission(namespace, actions.toArray(new Permission.Action[actions.size()]));
|
||||
}
|
||||
if(proto.getType() == AccessControlProtos.Permission.Type.Table) {
|
||||
AccessControlProtos.TablePermission perm = proto.getTablePermission();
|
||||
List<Permission.Action> actions = toPermissionActions(perm.getActionList());
|
||||
|
||||
return new TablePermission(table, family, qualifier,
|
||||
actions.toArray(new Permission.Action[actions.size()]));
|
||||
byte[] qualifier = null;
|
||||
byte[] family = null;
|
||||
TableName table = null;
|
||||
|
||||
if (!perm.hasTableName()) {
|
||||
throw new IllegalStateException("TableName cannot be empty");
|
||||
}
|
||||
table = ProtobufUtil.toTableName(perm.getTableName());
|
||||
|
||||
if (perm.hasFamily()) family = perm.getFamily().toByteArray();
|
||||
if (perm.hasQualifier()) qualifier = perm.getQualifier().toByteArray();
|
||||
|
||||
return new TablePermission(table, family, qualifier,
|
||||
actions.toArray(new Permission.Action[actions.size()]));
|
||||
}
|
||||
throw new IllegalStateException("Unrecognize Perm Type: "+proto.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1601,23 +1626,47 @@ public final class ProtobufUtil {
|
|||
* @return the protobuf Permission
|
||||
*/
|
||||
public static AccessControlProtos.Permission toPermission(Permission perm) {
|
||||
AccessControlProtos.Permission.Builder builder = AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.Permission.Builder ret = AccessControlProtos.Permission.newBuilder();
|
||||
if (perm instanceof TablePermission) {
|
||||
TablePermission tablePerm = (TablePermission)perm;
|
||||
if (tablePerm.hasTable()) {
|
||||
if(tablePerm.hasNamespace()) {
|
||||
ret.setType(AccessControlProtos.Permission.Type.Namespace);
|
||||
|
||||
AccessControlProtos.NamespacePermission.Builder builder =
|
||||
AccessControlProtos.NamespacePermission.newBuilder();
|
||||
builder.setNamespaceName(ByteString.copyFromUtf8(tablePerm.getNamespace()));
|
||||
for (Permission.Action a : perm.getActions()) {
|
||||
builder.addAction(toPermissionAction(a));
|
||||
}
|
||||
ret.setNamespacePermission(builder);
|
||||
} else if (tablePerm.hasTable()) {
|
||||
ret.setType(AccessControlProtos.Permission.Type.Table);
|
||||
|
||||
AccessControlProtos.TablePermission.Builder builder =
|
||||
AccessControlProtos.TablePermission.newBuilder();
|
||||
builder.setTableName(ProtobufUtil.toProtoTableName(tablePerm.getTable()));
|
||||
if (tablePerm.hasFamily()) {
|
||||
builder.setFamily(ByteString.copyFrom(tablePerm.getFamily()));
|
||||
}
|
||||
if (tablePerm.hasQualifier()) {
|
||||
builder.setQualifier(ByteString.copyFrom(tablePerm.getQualifier()));
|
||||
}
|
||||
for (Permission.Action a : perm.getActions()) {
|
||||
builder.addAction(toPermissionAction(a));
|
||||
}
|
||||
ret.setTablePermission(builder);
|
||||
}
|
||||
if (tablePerm.hasFamily()) {
|
||||
builder.setFamily(ByteString.copyFrom(tablePerm.getFamily()));
|
||||
}
|
||||
if (tablePerm.hasQualifier()) {
|
||||
builder.setQualifier(ByteString.copyFrom(tablePerm.getQualifier()));
|
||||
} else {
|
||||
ret.setType(AccessControlProtos.Permission.Type.Global);
|
||||
|
||||
AccessControlProtos.GlobalPermission.Builder builder =
|
||||
AccessControlProtos.GlobalPermission.newBuilder();
|
||||
for (Permission.Action a : perm.getActions()) {
|
||||
builder.addAction(toPermissionAction(a));
|
||||
}
|
||||
ret.setGlobalPermission(builder);
|
||||
}
|
||||
for (Permission.Action a : perm.getActions()) {
|
||||
builder.addAction(toPermissionAction(a));
|
||||
}
|
||||
return builder.build();
|
||||
return ret.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1688,24 +1737,9 @@ public final class ProtobufUtil {
|
|||
* @return the protobuf UserPermission
|
||||
*/
|
||||
public static AccessControlProtos.UserPermission toUserPermission(UserPermission perm) {
|
||||
AccessControlProtos.Permission.Builder permissionBuilder =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
for (Permission.Action a : perm.getActions()) {
|
||||
permissionBuilder.addAction(toPermissionAction(a));
|
||||
}
|
||||
if (perm.hasTable()) {
|
||||
permissionBuilder.setTableName(ProtobufUtil.toProtoTableName(perm.getTable()));
|
||||
}
|
||||
if (perm.hasFamily()) {
|
||||
permissionBuilder.setFamily(ByteString.copyFrom(perm.getFamily()));
|
||||
}
|
||||
if (perm.hasQualifier()) {
|
||||
permissionBuilder.setQualifier(ByteString.copyFrom(perm.getQualifier()));
|
||||
}
|
||||
|
||||
return AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFrom(perm.getUser()))
|
||||
.setPermission(permissionBuilder)
|
||||
.setPermission(toPermission(perm))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -1716,20 +1750,8 @@ public final class ProtobufUtil {
|
|||
* @return the converted UserPermission
|
||||
*/
|
||||
public static UserPermission toUserPermission(AccessControlProtos.UserPermission proto) {
|
||||
AccessControlProtos.Permission permission = proto.getPermission();
|
||||
List<Permission.Action> actions = toPermissionActions(permission.getActionList());
|
||||
|
||||
byte[] qualifier = null;
|
||||
byte[] family = null;
|
||||
TableName table = null;
|
||||
|
||||
if (permission.hasTableName()) table = ProtobufUtil.toTableName(permission.getTableName());
|
||||
if (permission.hasFamily()) family = permission.getFamily().toByteArray();
|
||||
if (permission.hasQualifier()) qualifier = permission.getQualifier().toByteArray();
|
||||
|
||||
return new UserPermission(proto.getUser().toByteArray(),
|
||||
table, family, qualifier,
|
||||
actions.toArray(new Permission.Action[actions.size()]));
|
||||
toTablePermission(proto.getPermission()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1739,26 +1761,48 @@ public final class ProtobufUtil {
|
|||
* @param perm the list of user and table permissions
|
||||
* @return the protobuf UserTablePermissions
|
||||
*/
|
||||
public static AccessControlProtos.UserTablePermissions toUserTablePermissions(
|
||||
public static AccessControlProtos.UsersAndPermissions toUserTablePermissions(
|
||||
ListMultimap<String, TablePermission> perm) {
|
||||
AccessControlProtos.UserTablePermissions.Builder builder =
|
||||
AccessControlProtos.UserTablePermissions.newBuilder();
|
||||
AccessControlProtos.UsersAndPermissions.Builder builder =
|
||||
AccessControlProtos.UsersAndPermissions.newBuilder();
|
||||
for (Map.Entry<String, Collection<TablePermission>> entry : perm.asMap().entrySet()) {
|
||||
AccessControlProtos.UserTablePermissions.UserPermissions.Builder userPermBuilder =
|
||||
AccessControlProtos.UserTablePermissions.UserPermissions.newBuilder();
|
||||
AccessControlProtos.UsersAndPermissions.UserPermissions.Builder userPermBuilder =
|
||||
AccessControlProtos.UsersAndPermissions.UserPermissions.newBuilder();
|
||||
userPermBuilder.setUser(ByteString.copyFromUtf8(entry.getKey()));
|
||||
for (TablePermission tablePerm: entry.getValue()) {
|
||||
userPermBuilder.addPermissions(toPermission(tablePerm));
|
||||
}
|
||||
builder.addPermissions(userPermBuilder.build());
|
||||
builder.addUserPermissions(userPermBuilder.build());
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to grant a user some permissions. The permissions will
|
||||
* be global if table is not specified. Otherwise, they are for those
|
||||
* table/column family/qualifier only.
|
||||
* A utility used to grant a user global permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
* @param protocol the AccessControlService protocol proxy
|
||||
* @param userShortName the short name of the user to grant permissions
|
||||
* @param actions the permissions to be granted
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public static void grant(AccessControlService.BlockingInterface protocol,
|
||||
String userShortName, 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]));
|
||||
protocol.grant(null, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to grant a user table permissions. The permissions will
|
||||
* be for a table table/column family/qualifier.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
|
@ -1785,9 +1829,55 @@ public final class ProtobufUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* A utility used to revoke a user some permissions. The permissions will
|
||||
* be global if table is not specified. Otherwise, they are for those
|
||||
* table/column family/qualifier only.
|
||||
* A utility used to grant a user namespace permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
* @param protocol the AccessControlService protocol proxy
|
||||
* @param namespace the short name of the user to grant permissions
|
||||
* @param actions the permissions to be granted
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public static void grant(AccessControlService.BlockingInterface protocol,
|
||||
String userShortName, String namespace,
|
||||
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]));
|
||||
protocol.grant(null, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to revoke a user's global permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
* @param protocol the AccessControlService protocol proxy
|
||||
* @param userShortName the short name of the user to revoke permissions
|
||||
* @param actions the permissions to be revoked
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public static void revoke(AccessControlService.BlockingInterface protocol,
|
||||
String userShortName, 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.RevokeRequest request = RequestConverter.
|
||||
buildRevokeRequest(userShortName, permActions.toArray(
|
||||
new AccessControlProtos.Permission.Action[actions.length]));
|
||||
protocol.revoke(null, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to revoke a user's table permissions. The permissions will
|
||||
* be for a table/column family/qualifier.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
|
@ -1814,7 +1904,55 @@ public final class ProtobufUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* A utility used to get user permissions.
|
||||
* A utility used to revoke a user's namespace permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
* @param protocol the AccessControlService protocol proxy
|
||||
* @param userShortName the short name of the user to revoke permissions
|
||||
* @param namespace optional table name
|
||||
* @param actions the permissions to be revoked
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public static void revoke(AccessControlService.BlockingInterface protocol,
|
||||
String userShortName, String namespace,
|
||||
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.RevokeRequest request = RequestConverter.
|
||||
buildRevokeRequest(userShortName, namespace, permActions.toArray(
|
||||
new AccessControlProtos.Permission.Action[actions.length]));
|
||||
protocol.revoke(null, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to get user's global permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
* @param protocol the AccessControlService protocol proxy
|
||||
* @throws ServiceException
|
||||
*/
|
||||
public static List<UserPermission> getUserPermissions(
|
||||
AccessControlService.BlockingInterface protocol) throws ServiceException {
|
||||
AccessControlProtos.UserPermissionsRequest.Builder builder =
|
||||
AccessControlProtos.UserPermissionsRequest.newBuilder();
|
||||
builder.setType(AccessControlProtos.Permission.Type.Global);
|
||||
AccessControlProtos.UserPermissionsRequest request = builder.build();
|
||||
AccessControlProtos.UserPermissionsResponse response =
|
||||
protocol.getUserPermissions(null, request);
|
||||
List<UserPermission> perms = new ArrayList<UserPermission>();
|
||||
for (AccessControlProtos.UserPermission perm: response.getUserPermissionList()) {
|
||||
perms.add(ProtobufUtil.toUserPermission(perm));
|
||||
}
|
||||
return perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility used to get user table permissions.
|
||||
* <p>
|
||||
* It's also called by the shell, in case you want to find references.
|
||||
*
|
||||
|
@ -1830,11 +1968,12 @@ public final class ProtobufUtil {
|
|||
if (t != null) {
|
||||
builder.setTableName(ProtobufUtil.toProtoTableName(t));
|
||||
}
|
||||
builder.setType(AccessControlProtos.Permission.Type.Table);
|
||||
AccessControlProtos.UserPermissionsRequest request = builder.build();
|
||||
AccessControlProtos.UserPermissionsResponse response =
|
||||
protocol.getUserPermissions(null, request);
|
||||
List<UserPermission> perms = new ArrayList<UserPermission>();
|
||||
for (AccessControlProtos.UserPermission perm: response.getPermissionList()) {
|
||||
for (AccessControlProtos.UserPermission perm: response.getUserPermissionList()) {
|
||||
perms.add(ProtobufUtil.toUserPermission(perm));
|
||||
}
|
||||
return perms;
|
||||
|
@ -1848,12 +1987,12 @@ public final class ProtobufUtil {
|
|||
* @return the converted UserPermission
|
||||
*/
|
||||
public static ListMultimap<String, TablePermission> toUserTablePermissions(
|
||||
AccessControlProtos.UserTablePermissions proto) {
|
||||
AccessControlProtos.UsersAndPermissions proto) {
|
||||
ListMultimap<String, TablePermission> perms = ArrayListMultimap.create();
|
||||
AccessControlProtos.UserTablePermissions.UserPermissions userPerm;
|
||||
AccessControlProtos.UsersAndPermissions.UserPermissions userPerm;
|
||||
|
||||
for (int i = 0; i < proto.getPermissionsCount(); i++) {
|
||||
userPerm = proto.getPermissions(i);
|
||||
for (int i = 0; i < proto.getUserPermissionsCount(); i++) {
|
||||
userPerm = proto.getUserPermissions(i);
|
||||
for (int j = 0; j < userPerm.getPermissionsCount(); j++) {
|
||||
TablePermission tablePerm = toTablePermission(userPerm.getPermissions(j));
|
||||
perms.put(userPerm.getUser().toStringUtf8(), tablePerm);
|
||||
|
|
|
@ -1201,6 +1201,32 @@ public final class RequestConverter {
|
|||
ByteString.copyFrom(regionName)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request to grant user permissions.
|
||||
*
|
||||
* @param username the short user name who to grant permissions
|
||||
* @param actions the permissions to be granted
|
||||
* @return A {@link AccessControlProtos} GrantRequest
|
||||
*/
|
||||
public static AccessControlProtos.GrantRequest buildGrantRequest(
|
||||
String username, AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.GlobalPermission.Builder permissionBuilder =
|
||||
AccessControlProtos.GlobalPermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
ret.setType(AccessControlProtos.Permission.Type.Global)
|
||||
.setGlobalPermission(permissionBuilder);
|
||||
return AccessControlProtos.GrantRequest.newBuilder()
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request to grant user permissions.
|
||||
*
|
||||
|
@ -1214,26 +1240,88 @@ public final class RequestConverter {
|
|||
public static AccessControlProtos.GrantRequest buildGrantRequest(
|
||||
String username, TableName tableName, byte[] family, byte[] qualifier,
|
||||
AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder permissionBuilder =
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.TablePermission.Builder permissionBuilder =
|
||||
AccessControlProtos.TablePermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
if (tableName != null) {
|
||||
permissionBuilder.setTableName(ProtobufUtil.toProtoTableName(tableName));
|
||||
if (tableName == null) {
|
||||
throw new NullPointerException("TableName cannot be null");
|
||||
}
|
||||
permissionBuilder.setTableName(ProtobufUtil.toProtoTableName(tableName));
|
||||
|
||||
if (family != null) {
|
||||
permissionBuilder.setFamily(ByteString.copyFrom(family));
|
||||
}
|
||||
if (qualifier != null) {
|
||||
permissionBuilder.setQualifier(ByteString.copyFrom(qualifier));
|
||||
}
|
||||
|
||||
ret.setType(AccessControlProtos.Permission.Type.Table)
|
||||
.setTablePermission(permissionBuilder);
|
||||
return AccessControlProtos.GrantRequest.newBuilder()
|
||||
.setPermission(
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(permissionBuilder.build())
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request to grant user permissions.
|
||||
*
|
||||
* @param username the short user name who to grant permissions
|
||||
* @param namespace optional table name the permissions apply
|
||||
* @param actions the permissions to be granted
|
||||
* @return A {@link AccessControlProtos} GrantRequest
|
||||
*/
|
||||
public static AccessControlProtos.GrantRequest buildGrantRequest(
|
||||
String username, String namespace,
|
||||
AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.NamespacePermission.Builder permissionBuilder =
|
||||
AccessControlProtos.NamespacePermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
if (namespace != null) {
|
||||
permissionBuilder.setNamespaceName(ByteString.copyFromUtf8(namespace));
|
||||
}
|
||||
ret.setType(AccessControlProtos.Permission.Type.Namespace)
|
||||
.setNamespacePermission(permissionBuilder);
|
||||
return AccessControlProtos.GrantRequest.newBuilder()
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request to revoke user permissions.
|
||||
*
|
||||
* @param username the short user name whose permissions to be revoked
|
||||
* @param actions the permissions to be revoked
|
||||
* @return A {@link AccessControlProtos} RevokeRequest
|
||||
*/
|
||||
public static AccessControlProtos.RevokeRequest buildRevokeRequest(
|
||||
String username, AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.GlobalPermission.Builder permissionBuilder =
|
||||
AccessControlProtos.GlobalPermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
ret.setType(AccessControlProtos.Permission.Type.Global)
|
||||
.setGlobalPermission(permissionBuilder);
|
||||
return AccessControlProtos.RevokeRequest.newBuilder()
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
|
@ -1250,8 +1338,10 @@ public final class RequestConverter {
|
|||
public static AccessControlProtos.RevokeRequest buildRevokeRequest(
|
||||
String username, TableName tableName, byte[] family, byte[] qualifier,
|
||||
AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder permissionBuilder =
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.TablePermission.Builder permissionBuilder =
|
||||
AccessControlProtos.TablePermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
|
@ -1264,12 +1354,44 @@ public final class RequestConverter {
|
|||
if (qualifier != null) {
|
||||
permissionBuilder.setQualifier(ByteString.copyFrom(qualifier));
|
||||
}
|
||||
|
||||
ret.setType(AccessControlProtos.Permission.Type.Table)
|
||||
.setTablePermission(permissionBuilder);
|
||||
return AccessControlProtos.RevokeRequest.newBuilder()
|
||||
.setPermission(
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(permissionBuilder.build())
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request to revoke user permissions.
|
||||
*
|
||||
* @param username the short user name whose permissions to be revoked
|
||||
* @param namespace optional table name the permissions apply
|
||||
* @param actions the permissions to be revoked
|
||||
* @return A {@link AccessControlProtos} RevokeRequest
|
||||
*/
|
||||
public static AccessControlProtos.RevokeRequest buildRevokeRequest(
|
||||
String username, String namespace,
|
||||
AccessControlProtos.Permission.Action... actions) {
|
||||
AccessControlProtos.Permission.Builder ret =
|
||||
AccessControlProtos.Permission.newBuilder();
|
||||
AccessControlProtos.NamespacePermission.Builder permissionBuilder =
|
||||
AccessControlProtos.NamespacePermission.newBuilder();
|
||||
for (AccessControlProtos.Permission.Action a : actions) {
|
||||
permissionBuilder.addAction(a);
|
||||
}
|
||||
if (namespace != null) {
|
||||
permissionBuilder.setNamespaceName(ByteString.copyFromUtf8(namespace));
|
||||
}
|
||||
ret.setType(AccessControlProtos.Permission.Type.Namespace)
|
||||
.setNamespacePermission(permissionBuilder);
|
||||
return AccessControlProtos.RevokeRequest.newBuilder()
|
||||
.setUserPermission(
|
||||
AccessControlProtos.UserPermission.newBuilder()
|
||||
.setUser(ByteString.copyFromUtf8(username))
|
||||
.setPermission(ret)
|
||||
).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ public final class ResponseConverter {
|
|||
final List<UserPermission> permissions) {
|
||||
UserPermissionsResponse.Builder builder = UserPermissionsResponse.newBuilder();
|
||||
for (UserPermission perm : permissions) {
|
||||
builder.addPermission(ProtobufUtil.toUserPermission(perm));
|
||||
builder.addUserPermission(ProtobufUtil.toUserPermission(perm));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,10 @@ public class TablePermission extends Permission {
|
|||
private byte[] family;
|
||||
private byte[] qualifier;
|
||||
|
||||
//TODO refactor this class
|
||||
//we need to refacting this into three classes (Global, Table, Namespace)
|
||||
private String namespace;
|
||||
|
||||
/** Nullary constructor for Writable, do not use */
|
||||
public TablePermission() {
|
||||
super();
|
||||
|
@ -87,6 +91,62 @@ public class TablePermission extends Permission {
|
|||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new permission for the given namespace or table, restricted to the given
|
||||
* column family and qualifer, allowing the assigned actions to be performed.
|
||||
* @param namespace
|
||||
* @param table the table
|
||||
* @param family the family, can be null if a global permission on the table
|
||||
* @param assigned the list of allowed actions
|
||||
*/
|
||||
public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier,
|
||||
Action... assigned) {
|
||||
super(assigned);
|
||||
this.namespace = namespace;
|
||||
this.table = table;
|
||||
this.family = family;
|
||||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new permission for the given namespace or table, family and column qualifier,
|
||||
* allowing the actions matching the provided byte codes to be performed.
|
||||
* @param namespace
|
||||
* @param table the table
|
||||
* @param family the family, can be null if a global permission on the table
|
||||
* @param actionCodes the list of allowed action codes
|
||||
*/
|
||||
public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier,
|
||||
byte[] actionCodes) {
|
||||
super(actionCodes);
|
||||
this.namespace = namespace;
|
||||
this.table = table;
|
||||
this.family = family;
|
||||
this.qualifier = qualifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new permission for the given namespace,
|
||||
* allowing the actions matching the provided byte codes to be performed.
|
||||
* @param namespace
|
||||
* @param actionCodes the list of allowed action codes
|
||||
*/
|
||||
public TablePermission(String namespace, byte[] actionCodes) {
|
||||
super(actionCodes);
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new permission for the given namespace,
|
||||
* allowing the given actions.
|
||||
* @param namespace
|
||||
* @param assigned the list of allowed actions
|
||||
*/
|
||||
public TablePermission(String namespace, Action... assigned) {
|
||||
super(assigned);
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
public boolean hasTable() {
|
||||
return table != null;
|
||||
}
|
||||
|
@ -111,6 +171,32 @@ public class TablePermission extends Permission {
|
|||
return qualifier;
|
||||
}
|
||||
|
||||
public boolean hasNamespace() {
|
||||
return namespace != null;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a given table operation is authorized by this permission
|
||||
* instance.
|
||||
*
|
||||
* @param namespace the namespace where the operation is being performed
|
||||
* @param action the action being requested
|
||||
* @return <code>true</code> if the action within the given scope is allowed
|
||||
* by this permission, <code>false</code>
|
||||
*/
|
||||
public boolean implies(String namespace, Action action) {
|
||||
if (!this.namespace.equals(namespace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check actions
|
||||
return super.implies(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a given table operation is authorized by this permission
|
||||
* instance.
|
||||
|
@ -234,7 +320,9 @@ public class TablePermission extends Permission {
|
|||
((family == null && other.getFamily() == null) ||
|
||||
Bytes.equals(family, other.getFamily())) &&
|
||||
((qualifier == null && other.getQualifier() == null) ||
|
||||
Bytes.equals(qualifier, other.getQualifier()))
|
||||
Bytes.equals(qualifier, other.getQualifier())) &&
|
||||
((namespace == null && other.getNamespace() == null) ||
|
||||
namespace.equals(other.getNamespace()))
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -256,15 +344,28 @@ public class TablePermission extends Permission {
|
|||
if (qualifier != null) {
|
||||
result = prime * result + Bytes.hashCode(qualifier);
|
||||
}
|
||||
if (namespace != null) {
|
||||
result = prime * result + namespace.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder("[TablePermission: ")
|
||||
.append("table=").append(table)
|
||||
.append(", family=").append(Bytes.toString(family))
|
||||
.append(", qualifier=").append(Bytes.toString(qualifier))
|
||||
.append(", actions=");
|
||||
StringBuilder str = new StringBuilder("[TablePermission: ");
|
||||
if(namespace != null) {
|
||||
str.append("namespace=").append(namespace)
|
||||
.append(", ");
|
||||
}
|
||||
else if(table != null) {
|
||||
str.append("table=").append(table)
|
||||
.append(", family=")
|
||||
.append(family == null ? null : Bytes.toString(family))
|
||||
.append(", qualifier=")
|
||||
.append(qualifier == null ? null : Bytes.toString(qualifier))
|
||||
.append(", ");
|
||||
} else {
|
||||
str.append("actions=");
|
||||
}
|
||||
if (actions != null) {
|
||||
for (int i=0; i<actions.length; i++) {
|
||||
if (i > 0)
|
||||
|
@ -291,6 +392,9 @@ public class TablePermission extends Permission {
|
|||
if (in.readBoolean()) {
|
||||
qualifier = Bytes.readByteArray(in);
|
||||
}
|
||||
if(in.readBoolean()) {
|
||||
namespace = Bytes.toString(Bytes.readByteArray(in));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -305,5 +409,9 @@ public class TablePermission extends Permission {
|
|||
if (qualifier != null) {
|
||||
Bytes.writeByteArray(out, qualifier);
|
||||
}
|
||||
out.writeBoolean(namespace != null);
|
||||
if(namespace != null) {
|
||||
Bytes.writeByteArray(out, Bytes.toBytes(namespace));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,29 @@ public class UserPermission extends TablePermission {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for the given user.
|
||||
* @param user the user
|
||||
* @param namespace
|
||||
* @param assigned the list of allowed actions
|
||||
*/
|
||||
public UserPermission(byte[] user, String namespace, Action... assigned) {
|
||||
super(namespace, assigned);
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for the given user,
|
||||
* matching the actions with the given codes.
|
||||
* @param user the user
|
||||
* @param namespace
|
||||
* @param actionCodes the list of allowed action codes
|
||||
*/
|
||||
public UserPermission(byte[] user, String namespace, byte[] actionCodes) {
|
||||
super(namespace, actionCodes);
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for the given user, table and column family.
|
||||
* @param user the user
|
||||
|
@ -110,6 +133,18 @@ public class UserPermission extends TablePermission {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for the given user, table, column family and
|
||||
* qualifier, matching the actions with the given codes.
|
||||
* @param user the user
|
||||
* @param perm a TablePermission
|
||||
*/
|
||||
public UserPermission(byte[] user, TablePermission perm) {
|
||||
super(perm.getNamespace(), perm.getTable(), perm.getFamily(), perm.getQualifier(),
|
||||
perm.actions);
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public byte[] getUser() {
|
||||
return user;
|
||||
}
|
||||
|
@ -118,8 +153,7 @@ public class UserPermission extends TablePermission {
|
|||
* Returns true if this permission describes a global user permission.
|
||||
*/
|
||||
public boolean isGlobal() {
|
||||
TableName tableName = getTable();
|
||||
return(tableName == null);
|
||||
return(!hasTable() && !hasNamespace());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -289,6 +289,9 @@ public final class HConstants {
|
|||
/** Used by HBCK to sideline backup data */
|
||||
public static final String HBCK_SIDELINEDIR_NAME = ".hbck";
|
||||
|
||||
/** Any artifacts left from migration can be moved here */
|
||||
public static final String MIGRATION_NAME = ".migration";
|
||||
|
||||
/** Used to construct the name of the compaction directory during compaction */
|
||||
public static final String HREGION_COMPACTIONDIR_NAME = "compaction.dir";
|
||||
|
||||
|
@ -835,7 +838,7 @@ public final class HConstants {
|
|||
Collections.unmodifiableList(Arrays.asList(new String[] { HREGION_LOGDIR_NAME,
|
||||
HREGION_OLDLOGDIR_NAME, CORRUPT_DIR_NAME, SPLIT_LOGDIR_NAME,
|
||||
HBCK_SIDELINEDIR_NAME, HFILE_ARCHIVE_DIRECTORY, SNAPSHOT_DIR_NAME, HBASE_TEMP_DIRECTORY,
|
||||
OLD_SNAPSHOT_DIR_NAME, BASE_NAMESPACE_DIR}));
|
||||
OLD_SNAPSHOT_DIR_NAME, BASE_NAMESPACE_DIR, MIGRATION_NAME}));
|
||||
|
||||
/** Directories that are not HBase user table directories */
|
||||
public static final List<String> HBASE_NON_USER_TABLE_DIRS =
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -32,55 +32,76 @@ message Permission {
|
|||
CREATE = 3;
|
||||
ADMIN = 4;
|
||||
}
|
||||
repeated Action action = 1;
|
||||
optional TableName tableName = 2;
|
||||
optional bytes family = 3;
|
||||
optional bytes qualifier = 4;
|
||||
enum Type {
|
||||
Global = 1;
|
||||
Namespace = 2;
|
||||
Table = 3;
|
||||
}
|
||||
required Type type = 1;
|
||||
optional GlobalPermission global_permission = 2;
|
||||
optional NamespacePermission namespace_permission = 3;
|
||||
optional TablePermission table_permission = 4;
|
||||
}
|
||||
|
||||
message TablePermission {
|
||||
optional TableName table_name = 1;
|
||||
optional bytes family = 2;
|
||||
optional bytes qualifier = 3;
|
||||
repeated Permission.Action action = 4;
|
||||
}
|
||||
|
||||
message NamespacePermission {
|
||||
optional bytes namespace_name = 1;
|
||||
repeated Permission.Action action = 2;
|
||||
}
|
||||
|
||||
message GlobalPermission {
|
||||
repeated Permission.Action action = 1;
|
||||
}
|
||||
|
||||
message UserPermission {
|
||||
required bytes user = 1;
|
||||
required Permission permission = 2;
|
||||
required Permission permission = 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Content of the /hbase/acl/<table> znode.
|
||||
* Content of the /hbase/acl/<table or namespace> znode.
|
||||
*/
|
||||
message UserTablePermissions {
|
||||
message UsersAndPermissions {
|
||||
message UserPermissions {
|
||||
required bytes user = 1;
|
||||
repeated Permission permissions = 2;
|
||||
}
|
||||
|
||||
repeated UserPermissions permissions = 1;
|
||||
repeated UserPermissions user_permissions = 1;
|
||||
}
|
||||
|
||||
message GrantRequest {
|
||||
required UserPermission permission = 1;
|
||||
required UserPermission user_permission = 1;
|
||||
}
|
||||
|
||||
message GrantResponse {
|
||||
}
|
||||
|
||||
message RevokeRequest {
|
||||
required UserPermission permission = 1;
|
||||
|
||||
required UserPermission user_permission = 1;
|
||||
}
|
||||
|
||||
message RevokeResponse {
|
||||
}
|
||||
|
||||
|
||||
message UserPermissionsRequest {
|
||||
optional TableName tableName = 1;
|
||||
optional Permission.Type type = 1;
|
||||
optional TableName table_name = 2;
|
||||
optional bytes namespace_name = 3;
|
||||
}
|
||||
|
||||
message UserPermissionsResponse {
|
||||
repeated UserPermission permission = 1;
|
||||
repeated UserPermission user_permission = 1;
|
||||
}
|
||||
|
||||
message CheckPermissionsRequest {
|
||||
repeated Permission permission = 1;
|
||||
repeated Permission permission = 1;
|
||||
}
|
||||
|
||||
message CheckPermissionsResponse {
|
||||
|
|
|
@ -20,23 +20,38 @@
|
|||
package org.apache.hadoop.hbase.migration;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.FSDataInputStream;
|
||||
import org.apache.hadoop.fs.FileStatus;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.fs.PathFilter;
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.HRegionInfo;
|
||||
import org.apache.hadoop.hbase.HTableDescriptor;
|
||||
import org.apache.hadoop.hbase.NamespaceDescriptor;
|
||||
import org.apache.hadoop.hbase.ServerName;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.catalog.MetaEditor;
|
||||
import org.apache.hadoop.hbase.exceptions.DeserializationException;
|
||||
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
|
||||
import org.apache.hadoop.hbase.regionserver.wal.HLog;
|
||||
import org.apache.hadoop.hbase.regionserver.wal.HLogFactory;
|
||||
import org.apache.hadoop.hbase.regionserver.wal.HLogUtil;
|
||||
import org.apache.hadoop.hbase.security.access.AccessControlLists;
|
||||
import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.FSTableDescriptors;
|
||||
import org.apache.hadoop.hbase.util.FSUtils;
|
||||
import org.apache.hadoop.util.Tool;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -58,6 +73,7 @@ public class NamespaceUpgrade implements Tool {
|
|||
private Path sysNsDir;
|
||||
private Path defNsDir;
|
||||
private Path baseDirs[];
|
||||
private Path backupDir;
|
||||
|
||||
public NamespaceUpgrade() throws IOException {
|
||||
}
|
||||
|
@ -70,6 +86,7 @@ public class NamespaceUpgrade implements Tool {
|
|||
baseDirs = new Path[]{rootDir,
|
||||
new Path(rootDir, HConstants.HFILE_ARCHIVE_DIRECTORY),
|
||||
new Path(rootDir, HConstants.HBASE_TEMP_DIRECTORY)};
|
||||
backupDir = new Path(rootDir, HConstants.MIGRATION_NAME);
|
||||
}
|
||||
|
||||
|
||||
|
@ -84,11 +101,14 @@ public class NamespaceUpgrade implements Tool {
|
|||
|
||||
makeNamespaceDirs();
|
||||
|
||||
migrateMeta();
|
||||
|
||||
migrateACL();
|
||||
|
||||
migrateTables();
|
||||
|
||||
migrateSnapshots();
|
||||
|
||||
migrateMeta();
|
||||
|
||||
FSUtils.setVersion(fs, rootDir);
|
||||
}
|
||||
|
@ -185,6 +205,156 @@ public class NamespaceUpgrade implements Tool {
|
|||
+ oldMetaRegionDir + " to " + newMetaRegionDir);
|
||||
}
|
||||
}
|
||||
|
||||
Path oldRootDir = new Path(rootDir, "-ROOT-");
|
||||
if(!fs.rename(oldRootDir, backupDir)) {
|
||||
throw new IllegalStateException("Failed to old data: "+oldRootDir+" to "+backupDir);
|
||||
}
|
||||
}
|
||||
|
||||
public void migrateACL() throws IOException {
|
||||
|
||||
TableName oldTableName = TableName.valueOf("_acl_");
|
||||
Path oldTablePath = new Path(rootDir, oldTableName.getNameAsString());
|
||||
|
||||
if(!fs.exists(oldTablePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.info("Migrating ACL table");
|
||||
|
||||
TableName newTableName = AccessControlLists.ACL_TABLE_NAME;
|
||||
Path newTablePath = FSUtils.getTableDir(rootDir, newTableName);
|
||||
HTableDescriptor oldDesc =
|
||||
readTableDescriptor(fs, getCurrentTableInfoStatus(fs, oldTablePath));
|
||||
|
||||
if(FSTableDescriptors.getTableInfoPath(fs, newTablePath) == null) {
|
||||
LOG.info("Creating new tableDesc for ACL");
|
||||
HTableDescriptor newDesc = new HTableDescriptor(oldDesc);
|
||||
newDesc.setName(newTableName);
|
||||
new FSTableDescriptors(this.conf).createTableDescriptorForTableDirectory(
|
||||
newTablePath, newDesc, true);
|
||||
}
|
||||
|
||||
|
||||
ServerName fakeServer = new ServerName("nsupgrade",96,123);
|
||||
String metaLogName = HLogUtil.getHLogDirectoryName(fakeServer.toString());
|
||||
HLog metaHLog = HLogFactory.createMetaHLog(fs, rootDir,
|
||||
metaLogName, conf, null,
|
||||
fakeServer.toString());
|
||||
HRegion meta = HRegion.openHRegion(rootDir, HRegionInfo.FIRST_META_REGIONINFO,
|
||||
HTableDescriptor.META_TABLEDESC, metaHLog, conf);
|
||||
HRegion region = null;
|
||||
try {
|
||||
for(Path regionDir : FSUtils.getRegionDirs(fs, oldTablePath)) {
|
||||
LOG.info("Migrating ACL region "+regionDir.getName());
|
||||
HRegionInfo oldRegionInfo = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
|
||||
HRegionInfo newRegionInfo =
|
||||
new HRegionInfo(newTableName,
|
||||
oldRegionInfo.getStartKey(),
|
||||
oldRegionInfo.getEndKey(),
|
||||
oldRegionInfo.isSplit(),
|
||||
oldRegionInfo.getRegionId());
|
||||
newRegionInfo.setOffline(oldRegionInfo.isOffline());
|
||||
region =
|
||||
new HRegion(
|
||||
HRegionFileSystem.openRegionFromFileSystem(conf, fs, oldTablePath,
|
||||
oldRegionInfo, false),
|
||||
metaHLog,
|
||||
conf,
|
||||
oldDesc,
|
||||
null);
|
||||
region.initialize();
|
||||
//Run major compaction to archive old stores
|
||||
//to keep any snapshots to _acl_ unbroken
|
||||
region.compactStores(true);
|
||||
region.waitForFlushesAndCompactions();
|
||||
region.close();
|
||||
|
||||
//Create new region dir
|
||||
Path newRegionDir = new Path(newTablePath, newRegionInfo.getEncodedName());
|
||||
if(!fs.exists(newRegionDir)) {
|
||||
if(!fs.mkdirs(newRegionDir)) {
|
||||
throw new IllegalStateException("Failed to create new region dir: " + newRegionDir);
|
||||
}
|
||||
}
|
||||
|
||||
//create new region info file, delete in case one exists
|
||||
HRegionFileSystem.openRegionFromFileSystem(conf, fs, newTablePath, newRegionInfo, false);
|
||||
|
||||
//migrate region contents
|
||||
for(FileStatus file : fs.listStatus(regionDir, new FSUtils.UserTableDirFilter(fs))) {
|
||||
if(file.getPath().getName().equals(HRegionFileSystem.REGION_INFO_FILE))
|
||||
continue;
|
||||
if(!fs.rename(file.getPath(), newRegionDir)) {
|
||||
throw new IllegalStateException("Failed to move file "+file.getPath()+" to " +
|
||||
newRegionDir);
|
||||
}
|
||||
}
|
||||
meta.put(MetaEditor.makePutFromRegionInfo(newRegionInfo));
|
||||
meta.delete(MetaEditor.makeDeleteFromRegionInfo(oldRegionInfo));
|
||||
}
|
||||
} finally {
|
||||
meta.flushcache();
|
||||
meta.waitForFlushesAndCompactions();
|
||||
meta.close();
|
||||
metaHLog.closeAndDelete();
|
||||
if(region != null) {
|
||||
region.close();
|
||||
}
|
||||
}
|
||||
if(!fs.rename(oldTablePath, backupDir)) {
|
||||
throw new IllegalStateException("Failed to old data: "+oldTablePath+" to "+backupDir);
|
||||
}
|
||||
}
|
||||
|
||||
//Culled from FSTableDescriptors
|
||||
private static HTableDescriptor readTableDescriptor(FileSystem fs,
|
||||
FileStatus status) throws IOException {
|
||||
int len = Ints.checkedCast(status.getLen());
|
||||
byte [] content = new byte[len];
|
||||
FSDataInputStream fsDataInputStream = fs.open(status.getPath());
|
||||
try {
|
||||
fsDataInputStream.readFully(content);
|
||||
} finally {
|
||||
fsDataInputStream.close();
|
||||
}
|
||||
HTableDescriptor htd = null;
|
||||
try {
|
||||
htd = HTableDescriptor.parseFrom(content);
|
||||
} catch (DeserializationException e) {
|
||||
throw new IOException("content=" + Bytes.toShort(content), e);
|
||||
}
|
||||
return htd;
|
||||
}
|
||||
|
||||
private static final PathFilter TABLEINFO_PATHFILTER = new PathFilter() {
|
||||
@Override
|
||||
public boolean accept(Path p) {
|
||||
// Accept any file that starts with TABLEINFO_NAME
|
||||
return p.getName().startsWith(".tableinfo");
|
||||
}
|
||||
};
|
||||
|
||||
static final Comparator<FileStatus> TABLEINFO_FILESTATUS_COMPARATOR =
|
||||
new Comparator<FileStatus>() {
|
||||
@Override
|
||||
public int compare(FileStatus left, FileStatus right) {
|
||||
return -left.compareTo(right);
|
||||
}};
|
||||
|
||||
// logic culled from FSTableDescriptors
|
||||
static FileStatus getCurrentTableInfoStatus(FileSystem fs, Path dir)
|
||||
throws IOException {
|
||||
FileStatus [] status = FSUtils.listStatus(fs, dir, TABLEINFO_PATHFILTER);
|
||||
if (status == null || status.length < 1) return null;
|
||||
FileStatus mostCurrent = null;
|
||||
for (FileStatus file : status) {
|
||||
if (mostCurrent == null || TABLEINFO_FILESTATUS_COMPARATOR.compare(file, mostCurrent) < 0) {
|
||||
mostCurrent = file;
|
||||
}
|
||||
}
|
||||
return mostCurrent;
|
||||
}
|
||||
|
||||
public static boolean verifyNSUpgrade(FileSystem fs, Path rootDir)
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.io.DataInput;
|
|||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -38,6 +39,7 @@ import org.apache.hadoop.hbase.HColumnDescriptor;
|
|||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.HTableDescriptor;
|
||||
import org.apache.hadoop.hbase.KeyValue;
|
||||
import org.apache.hadoop.hbase.NamespaceDescriptor;
|
||||
import org.apache.hadoop.hbase.catalog.MetaReader;
|
||||
import org.apache.hadoop.hbase.client.Delete;
|
||||
import org.apache.hadoop.hbase.client.Get;
|
||||
|
@ -89,17 +91,17 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
*/
|
||||
public class AccessControlLists {
|
||||
/** Internal storage table for access control lists */
|
||||
public static final String ACL_TABLE_NAME_STR = "_acl_";
|
||||
public static final byte[] ACL_TABLE_NAME = Bytes.toBytes(ACL_TABLE_NAME_STR);
|
||||
public static final TableName ACL_TABLE =
|
||||
TableName.valueOf(ACL_TABLE_NAME);
|
||||
public static final byte[] ACL_GLOBAL_NAME = ACL_TABLE_NAME;
|
||||
public static final TableName ACL_TABLE_NAME =
|
||||
TableName.valueOf(NamespaceDescriptor.SYSTEM_NAMESPACE_NAME_STR, "acl");
|
||||
public static final byte[] ACL_GLOBAL_NAME = ACL_TABLE_NAME.getName();
|
||||
/** Column family used to store ACL grants */
|
||||
public static final String ACL_LIST_FAMILY_STR = "l";
|
||||
public static final byte[] ACL_LIST_FAMILY = Bytes.toBytes(ACL_LIST_FAMILY_STR);
|
||||
|
||||
public static final char NAMESPACE_PREFIX = '@';
|
||||
|
||||
/** Table descriptor for ACL internal table */
|
||||
public static final HTableDescriptor ACL_TABLEDESC = new HTableDescriptor(ACL_TABLE);
|
||||
public static final HTableDescriptor ACL_TABLEDESC = new HTableDescriptor(ACL_TABLE_NAME);
|
||||
static {
|
||||
ACL_TABLEDESC.addFamily(
|
||||
new HColumnDescriptor(ACL_LIST_FAMILY,
|
||||
|
@ -125,7 +127,7 @@ public class AccessControlLists {
|
|||
* @param master reference to HMaster
|
||||
*/
|
||||
static void init(MasterServices master) throws IOException {
|
||||
if (!MetaReader.tableExists(master.getCatalogTracker(), ACL_TABLE)) {
|
||||
if (!MetaReader.tableExists(master.getCatalogTracker(), ACL_TABLE_NAME)) {
|
||||
master.createTable(ACL_TABLEDESC, null);
|
||||
}
|
||||
}
|
||||
|
@ -139,8 +141,8 @@ public class AccessControlLists {
|
|||
static void addUserPermission(Configuration conf, UserPermission userPerm)
|
||||
throws IOException {
|
||||
Permission.Action[] actions = userPerm.getActions();
|
||||
|
||||
Put p = new Put(userPerm.isGlobal() ? ACL_GLOBAL_NAME : userPerm.getTable().getName());
|
||||
byte[] rowKey = userPermissionRowKey(userPerm);
|
||||
Put p = new Put(rowKey);
|
||||
byte[] key = userPermissionKey(userPerm);
|
||||
|
||||
if ((actions == null) || (actions.length == 0)) {
|
||||
|
@ -155,8 +157,8 @@ public class AccessControlLists {
|
|||
}
|
||||
p.add(ACL_LIST_FAMILY, key, value);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Writing permission for table "+
|
||||
userPerm.getTable()+" "+
|
||||
LOG.debug("Writing permission with rowKey "+
|
||||
Bytes.toString(rowKey)+" "+
|
||||
Bytes.toString(key)+": "+Bytes.toStringBinary(value)
|
||||
);
|
||||
}
|
||||
|
@ -184,8 +186,7 @@ public class AccessControlLists {
|
|||
*/
|
||||
static void removeUserPermission(Configuration conf, UserPermission userPerm)
|
||||
throws IOException {
|
||||
|
||||
Delete d = new Delete(userPerm.isGlobal() ? ACL_GLOBAL_NAME : userPerm.getTable().getName());
|
||||
Delete d = new Delete(userPermissionRowKey(userPerm));
|
||||
byte[] key = userPermissionKey(userPerm);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
|
@ -222,7 +223,27 @@ public class AccessControlLists {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove specified table column from the _acl_ table.
|
||||
* Remove specified namespace from the acl table.
|
||||
*/
|
||||
static void removeNamespacePermissions(Configuration conf, String namespace)
|
||||
throws IOException{
|
||||
Delete d = new Delete(Bytes.toBytes(toNamespaceEntry(namespace)));
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Removing permissions of removed namespace "+ namespace);
|
||||
}
|
||||
|
||||
HTable acls = null;
|
||||
try {
|
||||
acls = new HTable(conf, ACL_TABLE_NAME);
|
||||
acls.delete(d);
|
||||
} finally {
|
||||
if (acls != null) acls.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specified table column from the acl table.
|
||||
*/
|
||||
static void removeTablePermissions(Configuration conf, TableName tableName, byte[] column)
|
||||
throws IOException{
|
||||
|
@ -269,6 +290,18 @@ public class AccessControlLists {
|
|||
}
|
||||
}
|
||||
|
||||
static byte[] userPermissionRowKey(UserPermission userPerm) {
|
||||
byte[] row;
|
||||
if(userPerm.hasNamespace()) {
|
||||
row = Bytes.toBytes(toNamespaceEntry(userPerm.getNamespace()));
|
||||
} else if(userPerm.isGlobal()) {
|
||||
row = ACL_GLOBAL_NAME;
|
||||
} else {
|
||||
row = userPerm.getTable().getName();
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build qualifier key from user permission:
|
||||
* username
|
||||
|
@ -295,14 +328,14 @@ public class AccessControlLists {
|
|||
* metadata table.
|
||||
*/
|
||||
static boolean isAclRegion(HRegion region) {
|
||||
return ACL_TABLE.equals(region.getTableDesc().getTableName());
|
||||
return ACL_TABLE_NAME.equals(region.getTableDesc().getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given table is {@code _acl_} metadata table.
|
||||
*/
|
||||
static boolean isAclTable(HTableDescriptor desc) {
|
||||
return ACL_TABLE.equals(desc.getTableName());
|
||||
return ACL_TABLE_NAME.equals(desc.getTableName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -313,16 +346,16 @@ public class AccessControlLists {
|
|||
* @return a map of the permissions for this table.
|
||||
* @throws IOException
|
||||
*/
|
||||
static Map<TableName,ListMultimap<String,TablePermission>> loadAll(
|
||||
static Map<byte[], ListMultimap<String,TablePermission>> loadAll(
|
||||
HRegion aclRegion)
|
||||
throws IOException {
|
||||
|
||||
if (!isAclRegion(aclRegion)) {
|
||||
throw new IOException("Can only load permissions from "+ACL_TABLE_NAME_STR);
|
||||
throw new IOException("Can only load permissions from "+ACL_TABLE_NAME);
|
||||
}
|
||||
|
||||
Map<TableName,ListMultimap<String,TablePermission>> allPerms =
|
||||
new TreeMap<TableName,ListMultimap<String,TablePermission>>();
|
||||
Map<byte[], ListMultimap<String, TablePermission>> allPerms =
|
||||
new TreeMap<byte[], ListMultimap<String, TablePermission>>();
|
||||
|
||||
// do a full scan of _acl_ table
|
||||
|
||||
|
@ -338,21 +371,21 @@ public class AccessControlLists {
|
|||
|
||||
boolean hasNext = iScanner.next(row);
|
||||
ListMultimap<String,TablePermission> perms = ArrayListMultimap.create();
|
||||
TableName table = null;
|
||||
byte[] entry = null;
|
||||
for (KeyValue kv : row) {
|
||||
if (table == null) {
|
||||
table = TableName.valueOf(kv.getRow());
|
||||
if (entry == null) {
|
||||
entry = kv.getRow();
|
||||
}
|
||||
Pair<String,TablePermission> permissionsOfUserOnTable =
|
||||
parseTablePermissionRecord(table, kv);
|
||||
parsePermissionRecord(entry, kv);
|
||||
if (permissionsOfUserOnTable != null) {
|
||||
String username = permissionsOfUserOnTable.getFirst();
|
||||
TablePermission permissions = permissionsOfUserOnTable.getSecond();
|
||||
perms.put(username, permissions);
|
||||
}
|
||||
}
|
||||
if (table != null) {
|
||||
allPerms.put(table, perms);
|
||||
if (entry != null) {
|
||||
allPerms.put(entry, perms);
|
||||
}
|
||||
if (!hasNext) {
|
||||
break;
|
||||
|
@ -371,10 +404,10 @@ public class AccessControlLists {
|
|||
* Load all permissions from the region server holding {@code _acl_},
|
||||
* primarily intended for testing purposes.
|
||||
*/
|
||||
static Map<TableName,ListMultimap<String,TablePermission>> loadAll(
|
||||
static Map<byte[], ListMultimap<String,TablePermission>> loadAll(
|
||||
Configuration conf) throws IOException {
|
||||
Map<TableName,ListMultimap<String,TablePermission>> allPerms =
|
||||
new TreeMap<TableName,ListMultimap<String,TablePermission>>();
|
||||
Map<byte[], ListMultimap<String,TablePermission>> allPerms =
|
||||
new TreeMap<byte[], ListMultimap<String,TablePermission>>(Bytes.BYTES_RAWCOMPARATOR);
|
||||
|
||||
// do a full scan of _acl_, filtering on only first table region rows
|
||||
|
||||
|
@ -387,10 +420,9 @@ public class AccessControlLists {
|
|||
acls = new HTable(conf, ACL_TABLE_NAME);
|
||||
scanner = acls.getScanner(scan);
|
||||
for (Result row : scanner) {
|
||||
TableName tableName = TableName.valueOf(row.getRow());
|
||||
ListMultimap<String,TablePermission> resultPerms =
|
||||
parseTablePermissions(tableName, row);
|
||||
allPerms.put(tableName, resultPerms);
|
||||
parsePermissions(row.getRow(), row);
|
||||
allPerms.put(row.getRow(), resultPerms);
|
||||
}
|
||||
} finally {
|
||||
if (scanner != null) scanner.close();
|
||||
|
@ -400,6 +432,16 @@ public class AccessControlLists {
|
|||
return allPerms;
|
||||
}
|
||||
|
||||
static ListMultimap<String, TablePermission> getTablePermissions(Configuration conf,
|
||||
TableName tableName) throws IOException {
|
||||
return getPermissions(conf, tableName != null ? tableName.getName() : null);
|
||||
}
|
||||
|
||||
static ListMultimap<String, TablePermission> getNamespacePermissions(Configuration conf,
|
||||
String namespace) throws IOException {
|
||||
return getPermissions(conf, Bytes.toBytes(toNamespaceEntry(namespace)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads user permission assignments stored in the <code>l:</code> column
|
||||
* family of the first table row in <code>_acl_</code>.
|
||||
|
@ -409,23 +451,23 @@ public class AccessControlLists {
|
|||
* used for storage.
|
||||
* </p>
|
||||
*/
|
||||
static ListMultimap<String, TablePermission> getTablePermissions(Configuration conf,
|
||||
TableName tableName) throws IOException {
|
||||
if (tableName == null) tableName = ACL_TABLE;
|
||||
static ListMultimap<String, TablePermission> getPermissions(Configuration conf,
|
||||
byte[] entryName) throws IOException {
|
||||
if (entryName == null) entryName = ACL_TABLE_NAME.getName();
|
||||
|
||||
// for normal user tables, we just read the table row from _acl_
|
||||
ListMultimap<String, TablePermission> perms = ArrayListMultimap.create();
|
||||
HTable acls = null;
|
||||
try {
|
||||
acls = new HTable(conf, ACL_TABLE);
|
||||
Get get = new Get(tableName.getName());
|
||||
acls = new HTable(conf, ACL_TABLE_NAME);
|
||||
Get get = new Get(entryName);
|
||||
get.addFamily(ACL_LIST_FAMILY);
|
||||
Result row = acls.get(get);
|
||||
if (!row.isEmpty()) {
|
||||
perms = parseTablePermissions(tableName, row);
|
||||
perms = parsePermissions(entryName, row);
|
||||
} else {
|
||||
LOG.info("No permissions found in " + ACL_TABLE_NAME_STR + " for table "
|
||||
+ tableName);
|
||||
LOG.info("No permissions found in " + ACL_TABLE_NAME + " for acl entry "
|
||||
+ Bytes.toString(entryName));
|
||||
}
|
||||
} finally {
|
||||
if (acls != null) acls.close();
|
||||
|
@ -438,11 +480,21 @@ public class AccessControlLists {
|
|||
* Returns the currently granted permissions for a given table as a list of
|
||||
* user plus associated permissions.
|
||||
*/
|
||||
static List<UserPermission> getUserTablePermissions(
|
||||
Configuration conf, TableName tableName) throws IOException {
|
||||
return getUserPermissions(conf, tableName.getName());
|
||||
}
|
||||
|
||||
static List<UserPermission> getUserNamespacePermissions(
|
||||
Configuration conf, String namespace) throws IOException {
|
||||
return getUserPermissions(conf, Bytes.toBytes(toNamespaceEntry(namespace)));
|
||||
}
|
||||
|
||||
static List<UserPermission> getUserPermissions(
|
||||
Configuration conf, TableName tableName)
|
||||
Configuration conf, byte[] entryName)
|
||||
throws IOException {
|
||||
ListMultimap<String,TablePermission> allPerms = getTablePermissions(
|
||||
conf, tableName);
|
||||
ListMultimap<String,TablePermission> allPerms = getPermissions(
|
||||
conf, entryName);
|
||||
|
||||
List<UserPermission> perms = new ArrayList<UserPermission>();
|
||||
|
||||
|
@ -455,14 +507,14 @@ public class AccessControlLists {
|
|||
return perms;
|
||||
}
|
||||
|
||||
private static ListMultimap<String,TablePermission> parseTablePermissions(
|
||||
TableName table, Result result) {
|
||||
ListMultimap<String,TablePermission> perms = ArrayListMultimap.create();
|
||||
private static ListMultimap<String, TablePermission> parsePermissions(
|
||||
byte[] entryName, Result result) {
|
||||
ListMultimap<String, TablePermission> perms = ArrayListMultimap.create();
|
||||
if (result != null && result.size() > 0) {
|
||||
for (KeyValue kv : result.raw()) {
|
||||
|
||||
Pair<String,TablePermission> permissionsOfUserOnTable =
|
||||
parseTablePermissionRecord(table, kv);
|
||||
parsePermissionRecord(entryName, kv);
|
||||
|
||||
if (permissionsOfUserOnTable != null) {
|
||||
String username = permissionsOfUserOnTable.getFirst();
|
||||
|
@ -474,8 +526,8 @@ public class AccessControlLists {
|
|||
return perms;
|
||||
}
|
||||
|
||||
private static Pair<String,TablePermission> parseTablePermissionRecord(
|
||||
TableName table, KeyValue kv) {
|
||||
private static Pair<String, TablePermission> parsePermissionRecord(
|
||||
byte[] entryName, KeyValue kv) {
|
||||
// return X given a set of permissions encoded in the permissionRecord kv.
|
||||
byte[] family = kv.getFamily();
|
||||
|
||||
|
@ -494,6 +546,15 @@ public class AccessControlLists {
|
|||
// check for a column family appended to the key
|
||||
// TODO: avoid the string conversion to make this more efficient
|
||||
String username = Bytes.toString(key);
|
||||
|
||||
//Handle namespace entry
|
||||
if(isNamespaceEntry(entryName)) {
|
||||
return new Pair<String, TablePermission>(username,
|
||||
new TablePermission(Bytes.toString(fromNamespaceEntry(entryName)), value));
|
||||
}
|
||||
|
||||
//Handle table and global entry
|
||||
//TODO global entry should be handled differently
|
||||
int idx = username.indexOf(ACL_KEY_DELIMITER);
|
||||
byte[] permFamily = null;
|
||||
byte[] permQualifier = null;
|
||||
|
@ -509,8 +570,8 @@ public class AccessControlLists {
|
|||
}
|
||||
}
|
||||
|
||||
return new Pair<String,TablePermission>(
|
||||
username, new TablePermission(table, permFamily, permQualifier, value));
|
||||
return new Pair<String,TablePermission>(username,
|
||||
new TablePermission(TableName.valueOf(entryName), permFamily, permQualifier, value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -534,8 +595,8 @@ public class AccessControlLists {
|
|||
if (ProtobufUtil.isPBMagicPrefix(data)) {
|
||||
int pblen = ProtobufUtil.lengthOfPBMagic();
|
||||
try {
|
||||
AccessControlProtos.UserTablePermissions perms =
|
||||
AccessControlProtos.UserTablePermissions.newBuilder().mergeFrom(
|
||||
AccessControlProtos.UsersAndPermissions perms =
|
||||
AccessControlProtos.UsersAndPermissions.newBuilder().mergeFrom(
|
||||
data, pblen, data.length - pblen).build();
|
||||
return ProtobufUtil.toUserTablePermissions(perms);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
|
@ -579,4 +640,37 @@ public class AccessControlLists {
|
|||
|
||||
return aclKey.substring(GROUP_PREFIX.length());
|
||||
}
|
||||
|
||||
public static boolean isNamespaceEntry(String entryName) {
|
||||
return entryName.charAt(0) == NAMESPACE_PREFIX;
|
||||
}
|
||||
|
||||
public static boolean isNamespaceEntry(byte[] entryName) {
|
||||
return entryName[0] == NAMESPACE_PREFIX;
|
||||
}
|
||||
|
||||
public static String toNamespaceEntry(String namespace) {
|
||||
return NAMESPACE_PREFIX + namespace;
|
||||
}
|
||||
|
||||
public static String fromNamespaceEntry(String namespace) {
|
||||
if(namespace.charAt(0) != NAMESPACE_PREFIX)
|
||||
throw new IllegalArgumentException("Argument is not a valid namespace entry");
|
||||
return namespace.substring(1);
|
||||
}
|
||||
|
||||
public static byte[] toNamespaceEntry(byte[] namespace) {
|
||||
byte[] ret = new byte[namespace.length+1];
|
||||
ret[0] = NAMESPACE_PREFIX;
|
||||
System.arraycopy(namespace, 0, ret, 1, namespace.length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] fromNamespaceEntry(byte[] namespace) {
|
||||
if(namespace[0] != NAMESPACE_PREFIX) {
|
||||
throw new IllegalArgumentException("Argument is not a valid namespace entry: " +
|
||||
Bytes.toString(namespace));
|
||||
}
|
||||
return Arrays.copyOfRange(namespace, 1, namespace.length);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ import java.util.TreeSet;
|
|||
import com.google.protobuf.RpcCallback;
|
||||
import com.google.protobuf.RpcController;
|
||||
import com.google.protobuf.Service;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
|
@ -139,16 +141,16 @@ public class AccessController extends BaseRegionObserver
|
|||
void initialize(RegionCoprocessorEnvironment e) throws IOException {
|
||||
final HRegion region = e.getRegion();
|
||||
|
||||
Map<TableName,ListMultimap<String,TablePermission>> tables =
|
||||
Map<byte[], ListMultimap<String,TablePermission>> tables =
|
||||
AccessControlLists.loadAll(region);
|
||||
// For each table, write out the table's permissions to the respective
|
||||
// znode for that table.
|
||||
for (Map.Entry<TableName,ListMultimap<String,TablePermission>> t:
|
||||
for (Map.Entry<byte[], ListMultimap<String,TablePermission>> t:
|
||||
tables.entrySet()) {
|
||||
TableName table = t.getKey();
|
||||
byte[] entry = t.getKey();
|
||||
ListMultimap<String,TablePermission> perms = t.getValue();
|
||||
byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, e.getConfiguration());
|
||||
this.authManager.getZKPermissionWatcher().writeToZookeeper(table, serialized);
|
||||
this.authManager.getZKPermissionWatcher().writeToZookeeper(entry, serialized);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,8 +161,8 @@ public class AccessController extends BaseRegionObserver
|
|||
*/
|
||||
void updateACL(RegionCoprocessorEnvironment e,
|
||||
final Map<byte[], List<? extends Cell>> familyMap) {
|
||||
Set<TableName> tableSet =
|
||||
new TreeSet<TableName>();
|
||||
Set<byte[]> entries =
|
||||
new TreeSet<byte[]>(Bytes.BYTES_RAWCOMPARATOR);
|
||||
for (Map.Entry<byte[], List<? extends Cell>> f : familyMap.entrySet()) {
|
||||
List<? extends Cell> cells = f.getValue();
|
||||
for (Cell cell: cells) {
|
||||
|
@ -168,21 +170,21 @@ public class AccessController extends BaseRegionObserver
|
|||
if (Bytes.equals(kv.getBuffer(), kv.getFamilyOffset(),
|
||||
kv.getFamilyLength(), AccessControlLists.ACL_LIST_FAMILY, 0,
|
||||
AccessControlLists.ACL_LIST_FAMILY.length)) {
|
||||
tableSet.add(TableName.valueOf(kv.getRow()));
|
||||
entries.add(kv.getRow());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ZKPermissionWatcher zkw = this.authManager.getZKPermissionWatcher();
|
||||
Configuration conf = regionEnv.getConfiguration();
|
||||
for (TableName tableName: tableSet) {
|
||||
for (byte[] entry: entries) {
|
||||
try {
|
||||
ListMultimap<String,TablePermission> perms =
|
||||
AccessControlLists.getTablePermissions(conf, tableName);
|
||||
AccessControlLists.getPermissions(conf, entry);
|
||||
byte[] serialized = AccessControlLists.writePermissionsAsBytes(perms, conf);
|
||||
zkw.writeToZookeeper(tableName, serialized);
|
||||
zkw.writeToZookeeper(entry, serialized);
|
||||
} catch (IOException ex) {
|
||||
LOG.error("Failed updating permissions mirror for '" + tableName + "'", ex);
|
||||
LOG.error("Failed updating permissions mirror for '" + Bytes.toString(entry) + "'",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,6 +354,35 @@ public class AccessController extends BaseRegionObserver
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorizes that the current user has any of the given permissions for the
|
||||
* given table, column family and column qualifier.
|
||||
* @param namespace
|
||||
* @throws IOException if obtaining the current user fails
|
||||
* @throws AccessDeniedException if user has no authorization
|
||||
*/
|
||||
private void requirePermission(String request, String namespace,
|
||||
Action... permissions) throws IOException {
|
||||
User user = getActiveUser();
|
||||
AuthResult result = null;
|
||||
|
||||
for (Action permission : permissions) {
|
||||
if (authManager.authorize(user, namespace, permission)) {
|
||||
result = AuthResult.allow(request, "Table permission granted", user,
|
||||
permission, namespace);
|
||||
break;
|
||||
} else {
|
||||
// rest of the world
|
||||
result = AuthResult.deny(request, "Insufficient permissions", user,
|
||||
permission, namespace);
|
||||
}
|
||||
}
|
||||
logResult(result);
|
||||
if (!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
|
||||
|
@ -393,7 +424,7 @@ public class AccessController extends BaseRegionObserver
|
|||
* being authorized, based on the given parameters.
|
||||
* @param perm Action being requested
|
||||
* @param tableName Affected table name.
|
||||
* @param familiMap Affected column families.
|
||||
* @param familyMap Affected column families.
|
||||
*/
|
||||
private void requireGlobalPermission(String request, Permission.Action perm, TableName tableName,
|
||||
Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException {
|
||||
|
@ -408,6 +439,26 @@ public class AccessController extends BaseRegionObserver
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the user has the given global permission. The generated
|
||||
* audit log message will contain context information for the operation
|
||||
* being authorized, based on the given parameters.
|
||||
* @param perm Action being requested
|
||||
* @param namespace
|
||||
*/
|
||||
private void requireGlobalPermission(String request, Permission.Action perm,
|
||||
String namespace) throws IOException {
|
||||
User user = getActiveUser();
|
||||
if (authManager.authorize(user, perm)) {
|
||||
logResult(AuthResult.allow(request, "Global check allowed", user, perm, namespace));
|
||||
} else {
|
||||
logResult(AuthResult.deny(request, "Global check failed", user, perm, namespace));
|
||||
throw new AccessDeniedException("Insufficient permissions for user '" +
|
||||
(user != null ? user.getShortName() : "null") +"' (global, action=" +
|
||||
perm.toString() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the current user is allowed the given action
|
||||
* over at least one of the column qualifiers in the given column families.
|
||||
|
@ -632,7 +683,7 @@ public class AccessController extends BaseRegionObserver
|
|||
throws IOException {
|
||||
if (Bytes.equals(tableName.getName(), AccessControlLists.ACL_GLOBAL_NAME)) {
|
||||
throw new AccessDeniedException("Not allowed to disable "
|
||||
+ AccessControlLists.ACL_TABLE_NAME_STR + " table.");
|
||||
+ AccessControlLists.ACL_TABLE_NAME + " table.");
|
||||
}
|
||||
requirePermission("disableTable", tableName, null, null, Action.ADMIN, Action.CREATE);
|
||||
}
|
||||
|
@ -779,27 +830,33 @@ public class AccessController extends BaseRegionObserver
|
|||
|
||||
@Override
|
||||
public void preCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
requireGlobalPermission("createNamespace", Action.ADMIN, ns.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postCreateNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
|
||||
String namespace) throws IOException {
|
||||
public void preDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx, String namespace)
|
||||
throws IOException {
|
||||
requireGlobalPermission("deleteNamespace", Action.ADMIN, namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postDeleteNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
|
||||
String namespace) throws IOException {
|
||||
AccessControlLists.removeNamespacePermissions(ctx.getEnvironment().getConfiguration(),
|
||||
namespace);
|
||||
LOG.info(namespace + "entry deleted in "+AccessControlLists.ACL_TABLE_NAME+" table.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preModifyNamespace(ObserverContext<MasterCoprocessorEnvironment> ctx,
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
NamespaceDescriptor ns) throws IOException {
|
||||
requireGlobalPermission("modifyNamespace", Action.ADMIN, ns.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1139,7 +1196,7 @@ public class AccessController extends BaseRegionObserver
|
|||
action, e, Collections.EMPTY_MAP);
|
||||
if (!authResult.isAllowed()) {
|
||||
for(UserPermission userPerm:
|
||||
AccessControlLists.getUserPermissions(regionEnv.getConfiguration(), tableName)) {
|
||||
AccessControlLists.getUserTablePermissions(regionEnv.getConfiguration(), tableName)) {
|
||||
for(Permission.Action userAction: userPerm.getActions()) {
|
||||
if(userAction.equals(action)) {
|
||||
return AuthResult.allow(method, "Access allowed", requestUser,
|
||||
|
@ -1188,7 +1245,7 @@ public class AccessController extends BaseRegionObserver
|
|||
public void grant(RpcController controller,
|
||||
AccessControlProtos.GrantRequest request,
|
||||
RpcCallback<AccessControlProtos.GrantResponse> done) {
|
||||
UserPermission perm = ProtobufUtil.toUserPermission(request.getPermission());
|
||||
UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
|
||||
AccessControlProtos.GrantResponse response = null;
|
||||
try {
|
||||
// verify it's only running at .acl.
|
||||
|
@ -1197,7 +1254,15 @@ public class AccessController extends BaseRegionObserver
|
|||
LOG.debug("Received request to grant access permission " + perm.toString());
|
||||
}
|
||||
|
||||
requirePermission("grant", perm.getTable(), perm.getFamily(), perm.getQualifier(), Action.ADMIN);
|
||||
switch(request.getUserPermission().getPermission().getType()) {
|
||||
case Global :
|
||||
case Table :
|
||||
requirePermission("grant", perm.getTable(), perm.getFamily(),
|
||||
perm.getQualifier(), Action.ADMIN);
|
||||
break;
|
||||
case Namespace :
|
||||
requireGlobalPermission("grant", Action.ADMIN, perm.getNamespace());
|
||||
}
|
||||
|
||||
AccessControlLists.addUserPermission(regionEnv.getConfiguration(), perm);
|
||||
if (AUDITLOG.isTraceEnabled()) {
|
||||
|
@ -1206,7 +1271,7 @@ public class AccessController extends BaseRegionObserver
|
|||
}
|
||||
} else {
|
||||
throw new CoprocessorException(AccessController.class, "This method "
|
||||
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
|
||||
+ "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
|
||||
}
|
||||
response = AccessControlProtos.GrantResponse.getDefaultInstance();
|
||||
} catch (IOException ioe) {
|
||||
|
@ -1220,7 +1285,7 @@ public class AccessController extends BaseRegionObserver
|
|||
public void revoke(RpcController controller,
|
||||
AccessControlProtos.RevokeRequest request,
|
||||
RpcCallback<AccessControlProtos.RevokeResponse> done) {
|
||||
UserPermission perm = ProtobufUtil.toUserPermission(request.getPermission());
|
||||
UserPermission perm = ProtobufUtil.toUserPermission(request.getUserPermission());
|
||||
AccessControlProtos.RevokeResponse response = null;
|
||||
try {
|
||||
// only allowed to be called on _acl_ region
|
||||
|
@ -1229,8 +1294,15 @@ public class AccessController extends BaseRegionObserver
|
|||
LOG.debug("Received request to revoke access permission " + perm.toString());
|
||||
}
|
||||
|
||||
requirePermission("revoke", perm.getTable(), perm.getFamily(),
|
||||
perm.getQualifier(), Action.ADMIN);
|
||||
switch(request.getUserPermission().getPermission().getType()) {
|
||||
case Global :
|
||||
case Table :
|
||||
requirePermission("revoke", perm.getTable(), perm.getFamily(),
|
||||
perm.getQualifier(), Action.ADMIN);
|
||||
break;
|
||||
case Namespace :
|
||||
requireGlobalPermission("revoke", Action.ADMIN, perm.getNamespace());
|
||||
}
|
||||
|
||||
AccessControlLists.removeUserPermission(regionEnv.getConfiguration(), perm);
|
||||
if (AUDITLOG.isTraceEnabled()) {
|
||||
|
@ -1239,7 +1311,7 @@ public class AccessController extends BaseRegionObserver
|
|||
}
|
||||
} else {
|
||||
throw new CoprocessorException(AccessController.class, "This method "
|
||||
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
|
||||
+ "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
|
||||
}
|
||||
response = AccessControlProtos.RevokeResponse.getDefaultInstance();
|
||||
} catch (IOException ioe) {
|
||||
|
@ -1254,21 +1326,30 @@ public class AccessController extends BaseRegionObserver
|
|||
AccessControlProtos.UserPermissionsRequest request,
|
||||
RpcCallback<AccessControlProtos.UserPermissionsResponse> done) {
|
||||
AccessControlProtos.UserPermissionsResponse response = null;
|
||||
TableName table = null;
|
||||
if (request.hasTableName()) {
|
||||
table = ProtobufUtil.toTableName(request.getTableName());
|
||||
}
|
||||
try {
|
||||
// only allowed to be called on _acl_ region
|
||||
if (aclRegion) {
|
||||
requirePermission("userPermissions", table, null, null, Action.ADMIN);
|
||||
List<UserPermission> perms = null;
|
||||
if(request.getType() == AccessControlProtos.Permission.Type.Table) {
|
||||
TableName table = null;
|
||||
if (request.hasTableName()) {
|
||||
table = ProtobufUtil.toTableName(request.getTableName());
|
||||
}
|
||||
requirePermission("userPermissions", table, null, null, Action.ADMIN);
|
||||
|
||||
List<UserPermission> perms = AccessControlLists.getUserPermissions(
|
||||
regionEnv.getConfiguration(), table);
|
||||
perms = AccessControlLists.getUserTablePermissions(
|
||||
regionEnv.getConfiguration(), table);
|
||||
} else if (request.getType() == AccessControlProtos.Permission.Type.Namespace) {
|
||||
perms = AccessControlLists.getUserNamespacePermissions(
|
||||
regionEnv.getConfiguration(), request.getNamespaceName().toStringUtf8());
|
||||
} else {
|
||||
perms = AccessControlLists.getUserPermissions(
|
||||
regionEnv.getConfiguration(), null);
|
||||
}
|
||||
response = ResponseConverter.buildUserPermissionsResponse(perms);
|
||||
} else {
|
||||
throw new CoprocessorException(AccessController.class, "This method "
|
||||
+ "can only execute at " + Bytes.toString(AccessControlLists.ACL_TABLE_NAME) + " table.");
|
||||
+ "can only execute at " + AccessControlLists.ACL_TABLE_NAME + " table.");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// pass exception back up
|
||||
|
@ -1371,7 +1452,8 @@ public class AccessController extends BaseRegionObserver
|
|||
|
||||
private boolean isSpecialTable(HRegionInfo regionInfo) {
|
||||
TableName tableName = regionInfo.getTableName();
|
||||
return tableName.equals(AccessControlLists.ACL_TABLE)
|
||||
return tableName.equals(AccessControlLists.ACL_TABLE_NAME)
|
||||
|| tableName.equals(TableName.NAMESPACE_TABLE_NAME)
|
||||
|| tableName.equals(TableName.META_TABLE_NAME);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.hadoop.hbase.util.Bytes;
|
|||
@InterfaceStability.Evolving
|
||||
public class AuthResult {
|
||||
private final boolean allowed;
|
||||
private final String namespace;
|
||||
private final TableName table;
|
||||
private final Permission.Action action;
|
||||
private final String request;
|
||||
|
@ -58,6 +59,7 @@ public class AuthResult {
|
|||
this.qualifier = qualifier;
|
||||
this.action = action;
|
||||
this.families = null;
|
||||
this.namespace = null;
|
||||
}
|
||||
|
||||
public AuthResult(boolean allowed, String request, String reason, User user,
|
||||
|
@ -72,6 +74,21 @@ public class AuthResult {
|
|||
this.qualifier = null;
|
||||
this.action = action;
|
||||
this.families = families;
|
||||
this.namespace = null;
|
||||
}
|
||||
|
||||
public AuthResult(boolean allowed, String request, String reason, User user,
|
||||
Permission.Action action, String namespace) {
|
||||
this.allowed = allowed;
|
||||
this.request = request;
|
||||
this.reason = reason;
|
||||
this.user = user;
|
||||
this.namespace = namespace;
|
||||
this.action = action;
|
||||
this.table = null;
|
||||
this.family = null;
|
||||
this.qualifier = null;
|
||||
this.families = null;
|
||||
}
|
||||
|
||||
public boolean isAllowed() {
|
||||
|
@ -153,11 +170,13 @@ public class AuthResult {
|
|||
.append(user != null ? user.getName() : "UNKNOWN")
|
||||
.append(", ");
|
||||
sb.append("scope=")
|
||||
.append(table == null ? "GLOBAL" : table)
|
||||
.append(namespace != null ? namespace : table == null ? "GLOBAL" : table);
|
||||
if(namespace == null) {
|
||||
sb.append(", ")
|
||||
.append("family=")
|
||||
.append(toFamilyString())
|
||||
.append(", ");
|
||||
sb.append("family=")
|
||||
.append(toFamilyString())
|
||||
.append(", ");
|
||||
}
|
||||
sb.append("action=")
|
||||
.append(action != null ? action.toString() : "")
|
||||
.append(")");
|
||||
|
@ -168,6 +187,11 @@ public class AuthResult {
|
|||
return "AuthResult" + toContextString();
|
||||
}
|
||||
|
||||
public static AuthResult allow(String request, String reason, User user,
|
||||
Permission.Action action, String namespace) {
|
||||
return new AuthResult(true, request, reason, user, action, namespace);
|
||||
}
|
||||
|
||||
public static AuthResult allow(String request, String reason, User user,
|
||||
Permission.Action action, TableName table, byte[] family, byte[] qualifier) {
|
||||
return new AuthResult(true, request, reason, user, action, table, family, qualifier);
|
||||
|
@ -179,6 +203,11 @@ public class AuthResult {
|
|||
return new AuthResult(true, request, reason, user, action, table, families);
|
||||
}
|
||||
|
||||
public static AuthResult deny(String request, String reason, User user,
|
||||
Permission.Action action, String namespace) {
|
||||
return new AuthResult(false, request, reason, user, action, namespace);
|
||||
}
|
||||
|
||||
public static AuthResult deny(String request, String reason, User user,
|
||||
Permission.Action action, TableName table, byte[] family, byte[] qualifier) {
|
||||
return new AuthResult(false, request, reason, user, action, table, family, qualifier);
|
||||
|
|
|
@ -96,6 +96,9 @@ public class TableAuthManager {
|
|||
private ConcurrentSkipListMap<TableName, PermissionCache<TablePermission>> tableCache =
|
||||
new ConcurrentSkipListMap<TableName, PermissionCache<TablePermission>>();
|
||||
|
||||
private ConcurrentSkipListMap<String, PermissionCache<TablePermission>> nsCache =
|
||||
new ConcurrentSkipListMap<String, PermissionCache<TablePermission>>();
|
||||
|
||||
private Configuration conf;
|
||||
private ZKPermissionWatcher zkperms;
|
||||
|
||||
|
@ -147,7 +150,7 @@ public class TableAuthManager {
|
|||
return this.zkperms;
|
||||
}
|
||||
|
||||
public void refreshCacheFromWritable(TableName table,
|
||||
public void refreshTableCacheFromWritable(TableName table,
|
||||
byte[] data) throws IOException {
|
||||
if (data != null && data.length > 0) {
|
||||
ListMultimap<String,TablePermission> perms;
|
||||
|
@ -169,6 +172,22 @@ public class TableAuthManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void refreshNamespaceCacheFromWritable(String namespace, byte[] data) throws IOException {
|
||||
if (data != null && data.length > 0) {
|
||||
ListMultimap<String,TablePermission> perms;
|
||||
try {
|
||||
perms = AccessControlLists.readPermissions(data, conf);
|
||||
} catch (DeserializationException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
if (perms != null) {
|
||||
updateNsCache(namespace, perms);
|
||||
}
|
||||
} else {
|
||||
LOG.debug("Skipping permission cache refresh because writable data is empty");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal global permissions cache
|
||||
*
|
||||
|
@ -216,6 +235,29 @@ public class TableAuthManager {
|
|||
tableCache.put(table, newTablePerms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal permissions cache for a single table, splitting
|
||||
* the permissions listed into separate caches for users and groups to optimize
|
||||
* group lookups.
|
||||
*
|
||||
* @param namespace
|
||||
* @param tablePerms
|
||||
*/
|
||||
private void updateNsCache(String namespace,
|
||||
ListMultimap<String, TablePermission> tablePerms) {
|
||||
PermissionCache<TablePermission> newTablePerms = new PermissionCache<TablePermission>();
|
||||
|
||||
for (Map.Entry<String, TablePermission> entry : tablePerms.entries()) {
|
||||
if (AccessControlLists.isGroupPrincipal(entry.getKey())) {
|
||||
newTablePerms.putGroup(AccessControlLists.getGroupName(entry.getKey()), entry.getValue());
|
||||
} else {
|
||||
newTablePerms.putUser(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
nsCache.put(namespace, newTablePerms);
|
||||
}
|
||||
|
||||
private PermissionCache<TablePermission> getTablePermissions(TableName table) {
|
||||
if (!tableCache.containsKey(table)) {
|
||||
tableCache.putIfAbsent(table, new PermissionCache<TablePermission>());
|
||||
|
@ -223,6 +265,13 @@ public class TableAuthManager {
|
|||
return tableCache.get(table);
|
||||
}
|
||||
|
||||
private PermissionCache<TablePermission> getNamespacePermissions(String namespace) {
|
||||
if (!nsCache.containsKey(namespace)) {
|
||||
nsCache.putIfAbsent(namespace, new PermissionCache<TablePermission>());
|
||||
}
|
||||
return nsCache.get(namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorizes a global permission
|
||||
* @param perms
|
||||
|
@ -329,6 +378,45 @@ public class TableAuthManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean authorize(User user, String namespace, Permission.Action action) {
|
||||
if (authorizeUser(user.getShortName(), action)) {
|
||||
return true;
|
||||
}
|
||||
PermissionCache<TablePermission> tablePerms = nsCache.get(namespace);
|
||||
if (tablePerms != null) {
|
||||
List<TablePermission> userPerms = tablePerms.getUser(user.getShortName());
|
||||
if (authorize(userPerms, namespace, action)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String[] groupNames = user.getGroupNames();
|
||||
if (groupNames != null) {
|
||||
for (String group : groupNames) {
|
||||
List<TablePermission> groupPerms = tablePerms.getGroup(group);
|
||||
if (authorize(groupPerms, namespace, action)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean authorize(List<TablePermission> perms, String namespace,
|
||||
Permission.Action action) {
|
||||
if (perms != null) {
|
||||
for (TablePermission p : perms) {
|
||||
if (p.implies(namespace, action)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("No permissions for authorize() check, table=" + namespace);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks global authorization for a specific action for a user, based on the
|
||||
* stored user permissions.
|
||||
|
@ -358,7 +446,7 @@ public class TableAuthManager {
|
|||
if (authorizeUser(username, action)) {
|
||||
return true;
|
||||
}
|
||||
if (table == null) table = AccessControlLists.ACL_TABLE;
|
||||
if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
|
||||
return authorize(getTablePermissions(table).getUser(username), table, family,
|
||||
qualifier, action);
|
||||
}
|
||||
|
@ -387,7 +475,7 @@ public class TableAuthManager {
|
|||
if (authorizeGroup(groupName, action)) {
|
||||
return true;
|
||||
}
|
||||
if (table == null) table = AccessControlLists.ACL_TABLE;
|
||||
if (table == null) table = AccessControlLists.ACL_TABLE_NAME;
|
||||
return authorize(getTablePermissions(table).getGroup(groupName), table, family, action);
|
||||
}
|
||||
|
||||
|
@ -481,11 +569,11 @@ public class TableAuthManager {
|
|||
return false;
|
||||
}
|
||||
|
||||
public void remove(byte[] table) {
|
||||
remove(TableName.valueOf(table));
|
||||
public void removeNamespace(byte[] ns) {
|
||||
nsCache.remove(ns);
|
||||
}
|
||||
|
||||
public void remove(TableName table) {
|
||||
public void removeTable(TableName table) {
|
||||
tableCache.remove(table);
|
||||
}
|
||||
|
||||
|
@ -496,11 +584,11 @@ public class TableAuthManager {
|
|||
* @param table
|
||||
* @param perms
|
||||
*/
|
||||
public void setUserPermissions(String username, TableName table,
|
||||
public void setTableUserPermissions(String username, TableName table,
|
||||
List<TablePermission> perms) {
|
||||
PermissionCache<TablePermission> tablePerms = getTablePermissions(table);
|
||||
tablePerms.replaceUser(username, perms);
|
||||
writeToZooKeeper(table, tablePerms);
|
||||
writeTableToZooKeeper(table, tablePerms);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -510,20 +598,58 @@ public class TableAuthManager {
|
|||
* @param table
|
||||
* @param perms
|
||||
*/
|
||||
public void setGroupPermissions(String group, TableName table,
|
||||
public void setTableGroupPermissions(String group, TableName table,
|
||||
List<TablePermission> perms) {
|
||||
PermissionCache<TablePermission> tablePerms = getTablePermissions(table);
|
||||
tablePerms.replaceGroup(group, perms);
|
||||
writeToZooKeeper(table, tablePerms);
|
||||
writeTableToZooKeeper(table, tablePerms);
|
||||
}
|
||||
|
||||
public void writeToZooKeeper(TableName table,
|
||||
/**
|
||||
* Overwrites the existing permission set for a given user for a table, and
|
||||
* triggers an update for zookeeper synchronization.
|
||||
* @param username
|
||||
* @param namespace
|
||||
* @param perms
|
||||
*/
|
||||
public void setNamespaceUserPermissions(String username, String namespace,
|
||||
List<TablePermission> perms) {
|
||||
PermissionCache<TablePermission> tablePerms = getNamespacePermissions(namespace);
|
||||
tablePerms.replaceUser(username, perms);
|
||||
writeNamespaceToZooKeeper(namespace, tablePerms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the existing permission set for a group and triggers an update
|
||||
* for zookeeper synchronization.
|
||||
* @param group
|
||||
* @param namespace
|
||||
* @param perms
|
||||
*/
|
||||
public void setNamespaceGroupPermissions(String group, String namespace,
|
||||
List<TablePermission> perms) {
|
||||
PermissionCache<TablePermission> tablePerms = getNamespacePermissions(namespace);
|
||||
tablePerms.replaceGroup(group, perms);
|
||||
writeNamespaceToZooKeeper(namespace, tablePerms);
|
||||
}
|
||||
|
||||
public void writeTableToZooKeeper(TableName table,
|
||||
PermissionCache<TablePermission> tablePerms) {
|
||||
byte[] serialized = new byte[0];
|
||||
if (tablePerms != null) {
|
||||
serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf);
|
||||
}
|
||||
zkperms.writeToZookeeper(table, serialized);
|
||||
zkperms.writeToZookeeper(table.getName(), serialized);
|
||||
}
|
||||
|
||||
public void writeNamespaceToZooKeeper(String namespace,
|
||||
PermissionCache<TablePermission> tablePerms) {
|
||||
byte[] serialized = new byte[0];
|
||||
if (tablePerms != null) {
|
||||
serialized = AccessControlLists.writePermissionsAsBytes(tablePerms.getAllPermissions(), conf);
|
||||
}
|
||||
zkperms.writeToZookeeper(Bytes.toBytes(AccessControlLists.toNamespaceEntry(namespace)),
|
||||
serialized);
|
||||
}
|
||||
|
||||
static Map<ZooKeeperWatcher,TableAuthManager> managerMap =
|
||||
|
|
|
@ -85,7 +85,11 @@ public class ZKPermissionWatcher extends ZooKeeperListener {
|
|||
public void nodeDeleted(String path) {
|
||||
if (aclZNode.equals(ZKUtil.getParent(path))) {
|
||||
String table = ZKUtil.getNodeName(path);
|
||||
authManager.remove(Bytes.toBytes(table));
|
||||
if(AccessControlLists.isNamespaceEntry(table)) {
|
||||
authManager.removeNamespace(Bytes.toBytes(table));
|
||||
} else {
|
||||
authManager.removeTable(TableName.valueOf(table));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,14 +97,14 @@ public class ZKPermissionWatcher extends ZooKeeperListener {
|
|||
public void nodeDataChanged(String path) {
|
||||
if (aclZNode.equals(ZKUtil.getParent(path))) {
|
||||
// update cache on an existing table node
|
||||
TableName table = TableName.valueOf(ZKUtil.getNodeName(path));
|
||||
String entry = ZKUtil.getNodeName(path);
|
||||
try {
|
||||
byte[] data = ZKUtil.getDataAndWatch(watcher, path);
|
||||
authManager.refreshCacheFromWritable(table, data);
|
||||
refreshAuthManager(entry, data);
|
||||
} catch (KeeperException ke) {
|
||||
LOG.error("Error reading data from zookeeper for node "+table, ke);
|
||||
LOG.error("Error reading data from zookeeper for node " + entry, ke);
|
||||
// only option is to abort
|
||||
watcher.abort("Zookeeper error getting data for node " + table, ke);
|
||||
watcher.abort("Zookeeper error getting data for node " + entry, ke);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Error reading permissions writables", ioe);
|
||||
}
|
||||
|
@ -126,36 +130,45 @@ public class ZKPermissionWatcher extends ZooKeeperListener {
|
|||
for (ZKUtil.NodeAndData n : nodes) {
|
||||
if (n.isEmpty()) continue;
|
||||
String path = n.getNode();
|
||||
TableName table = TableName.valueOf(ZKUtil.getNodeName(path));
|
||||
String entry = (ZKUtil.getNodeName(path));
|
||||
try {
|
||||
byte[] nodeData = n.getData();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Updating permissions cache from node "+table+" with data: "+
|
||||
Bytes.toStringBinary(nodeData));
|
||||
}
|
||||
authManager.refreshCacheFromWritable(table, nodeData);
|
||||
refreshAuthManager(entry, n.getData());
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Failed parsing permissions for table '" + table +
|
||||
LOG.error("Failed parsing permissions for table '" + entry +
|
||||
"' from zk", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshAuthManager(String entry, byte[] nodeData) throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Updating permissions cache from node "+entry+" with data: "+
|
||||
Bytes.toStringBinary(nodeData));
|
||||
}
|
||||
if(AccessControlLists.isNamespaceEntry(entry)) {
|
||||
authManager.refreshNamespaceCacheFromWritable(
|
||||
AccessControlLists.fromNamespaceEntry(entry), nodeData);
|
||||
} else {
|
||||
authManager.refreshTableCacheFromWritable(TableName.valueOf(entry), nodeData);
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* Write a table's access controls to the permissions mirror in zookeeper
|
||||
* @param tableName
|
||||
* @param entry
|
||||
* @param permsData
|
||||
*/
|
||||
public void writeToZookeeper(TableName tableName, byte[] permsData) {
|
||||
public void writeToZookeeper(byte[] entry, byte[] permsData) {
|
||||
String entryName = Bytes.toString(entry);
|
||||
String zkNode = ZKUtil.joinZNode(watcher.baseZNode, ACL_NODE);
|
||||
zkNode = ZKUtil.joinZNode(zkNode, tableName.getNameAsString());
|
||||
zkNode = ZKUtil.joinZNode(zkNode, entryName);
|
||||
|
||||
try {
|
||||
ZKUtil.createWithParents(watcher, zkNode);
|
||||
ZKUtil.updateExistingNodeData(watcher, zkNode, permsData, -1);
|
||||
} catch (KeeperException e) {
|
||||
LOG.error("Failed updating permissions for table '" +
|
||||
tableName + "'", e);
|
||||
LOG.error("Failed updating permissions for entry '" +
|
||||
entryName + "'", e);
|
||||
watcher.abort("Failed writing node "+zkNode+" to zookeeper", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3822,7 +3822,8 @@ public class Hbase {
|
|||
}
|
||||
}
|
||||
|
||||
public static class createTable<I extends Iface> extends org.apache.thrift.ProcessFunction<I, createTable_args> {
|
||||
public static class
|
||||
createTable<I extends Iface> extends org.apache.thrift.ProcessFunction<I, createTable_args> {
|
||||
public createTable() {
|
||||
super("createTable");
|
||||
}
|
||||
|
|
|
@ -36,26 +36,6 @@ module Hbase
|
|||
|
||||
# TODO: need to validate user name
|
||||
|
||||
# Verify that the specified permission is valid
|
||||
if (permissions == nil || permissions.length == 0)
|
||||
raise(ArgumentError, "Invalid permission: no actions associated with user")
|
||||
end
|
||||
|
||||
if (table_name != nil)
|
||||
# Table should exist
|
||||
raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name)
|
||||
|
||||
tablebytes=table_name.to_java_bytes
|
||||
htd = @admin.getTableDescriptor(tablebytes)
|
||||
|
||||
if (family != nil)
|
||||
raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes)
|
||||
end
|
||||
|
||||
fambytes = family.to_java_bytes if (family != nil)
|
||||
qualbytes = qualifier.to_java_bytes if (qualifier != nil)
|
||||
end
|
||||
|
||||
begin
|
||||
meta_table = org.apache.hadoop.hbase.client.HTable.new(@config,
|
||||
org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
|
||||
|
@ -67,10 +47,48 @@ module Hbase
|
|||
perm = org.apache.hadoop.hbase.security.access.Permission.new(
|
||||
permissions.to_java_bytes)
|
||||
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant(
|
||||
protocol, user, tablebytes, fambytes,
|
||||
qualbytes, perm.getActions())
|
||||
# Verify that the specified permission is valid
|
||||
if (permissions == nil || permissions.length == 0)
|
||||
raise(ArgumentError, "Invalid permission: no actions associated with user")
|
||||
end
|
||||
|
||||
if (table_name != nil)
|
||||
#check if the tablename passed is actually a namespace
|
||||
if (isNamespace?(table_name))
|
||||
# Namespace should exist first.
|
||||
namespace_name = table_name[1...table_name.length]
|
||||
raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name)
|
||||
|
||||
#We pass the namespace name along with "@" so that we can differentiate a namespace from a table.
|
||||
tablebytes=table_name.to_java_bytes
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant(
|
||||
protocol, user, tablebytes, perm.getActions())
|
||||
else
|
||||
# Table should exist
|
||||
raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name)
|
||||
|
||||
tableName = org.apache.hadoop.hbase.TableName.valueOf(table_name.to_java_bytes)
|
||||
htd = @admin.getTableDescriptor(tablebytes)
|
||||
|
||||
if (family != nil)
|
||||
raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes)
|
||||
end
|
||||
|
||||
fambytes = family.to_java_bytes if (family != nil)
|
||||
qualbytes = qualifier.to_java_bytes if (qualifier != nil)
|
||||
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant(
|
||||
protocol, user, tableName, fambytes,
|
||||
qualbytes, perm.getActions())
|
||||
end
|
||||
else
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.grant(
|
||||
protocol, user, perm.getActions())
|
||||
end
|
||||
|
||||
ensure
|
||||
meta_table.close()
|
||||
end
|
||||
|
@ -82,21 +100,6 @@ module Hbase
|
|||
|
||||
# TODO: need to validate user name
|
||||
|
||||
if (table_name != nil)
|
||||
# Table should exist
|
||||
raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name)
|
||||
|
||||
tablebytes=table_name.to_java_bytes
|
||||
htd = @admin.getTableDescriptor(tablebytes)
|
||||
|
||||
if (family != nil)
|
||||
raise(ArgumentError, "Can't find family: #{family}") unless htd.hasFamily(family.to_java_bytes)
|
||||
end
|
||||
|
||||
fambytes = family.to_java_bytes if (family != nil)
|
||||
qualbytes = qualifier.to_java_bytes if (qualifier != nil)
|
||||
end
|
||||
|
||||
begin
|
||||
meta_table = org.apache.hadoop.hbase.client.HTable.new(@config,
|
||||
org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
|
||||
|
@ -106,9 +109,41 @@ module Hbase
|
|||
protocol = org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos::
|
||||
AccessControlService.newBlockingStub(service)
|
||||
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke(
|
||||
protocol, user, tablebytes, fambytes, qualbytes)
|
||||
if (table_name != nil)
|
||||
#check if the tablename passed is actually a namespace
|
||||
if (isNamespace?(table_name))
|
||||
# Namespace should exist first.
|
||||
namespace_name = table_name[1...table_name.length]
|
||||
raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name)
|
||||
|
||||
#We pass the namespace name along with "@" so that we can differentiate a namespace from a table.
|
||||
tablebytes=table_name.to_java_bytes
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke(
|
||||
protocol, user, tablebytes)
|
||||
else
|
||||
# Table should exist
|
||||
raise(ArgumentError, "Can't find a table: #{table_name}") unless exists?(table_name)
|
||||
|
||||
tableName = org.apache.hadoop.hbase.TableName.valueOf(table_name.to_java_bytes)
|
||||
htd = @admin.getTableDescriptor(tableName)
|
||||
|
||||
if (family != nil)
|
||||
raise(ArgumentError, "Can't find a family: #{family}") unless htd.hasFamily(family.to_java_bytes)
|
||||
end
|
||||
|
||||
fambytes = family.to_java_bytes if (family != nil)
|
||||
qualbytes = qualifier.to_java_bytes if (qualifier != nil)
|
||||
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke(
|
||||
protocol, user, tableName, fambytes, qualbytes)
|
||||
end
|
||||
else
|
||||
# invoke cp endpoint to perform access controlse
|
||||
org.apache.hadoop.hbase.protobuf.ProtobufUtil.revoke(
|
||||
protocol, user)
|
||||
end
|
||||
ensure
|
||||
meta_table.close()
|
||||
end
|
||||
|
@ -118,12 +153,6 @@ module Hbase
|
|||
def user_permission(table_name=nil)
|
||||
security_available?
|
||||
|
||||
if (table_name != nil)
|
||||
raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name)
|
||||
|
||||
tablebytes=table_name.to_java_bytes
|
||||
end
|
||||
|
||||
begin
|
||||
meta_table = org.apache.hadoop.hbase.client.HTable.new(@config,
|
||||
org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
|
||||
|
@ -133,9 +162,23 @@ module Hbase
|
|||
protocol = org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos::
|
||||
AccessControlService.newBlockingStub(service)
|
||||
|
||||
# invoke cp endpoint to perform access controlse
|
||||
perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions(
|
||||
protocol, tablebytes)
|
||||
if (table_name != nil)
|
||||
#check if namespace is passed.
|
||||
if (isNamespace?(table_name))
|
||||
# Namespace should exist first.
|
||||
namespace_name = table_name[1...table_name.length]
|
||||
raise(ArgumentError, "Can't find a namespace: #{namespace_name}") unless namespace_exists?(namespace_name)
|
||||
# invoke cp endpoint to perform access controls
|
||||
perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions(
|
||||
protocol, table_name.to_java_bytes)
|
||||
else
|
||||
raise(ArgumentError, "Can't find table: #{table_name}") unless exists?(table_name)
|
||||
perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions(
|
||||
protocol, org.apache.hadoop.hbase.TableName.valueOf(table_name))
|
||||
end
|
||||
else
|
||||
perms = org.apache.hadoop.hbase.protobuf.ProtobufUtil.getUserPermissions(protocol)
|
||||
end
|
||||
ensure
|
||||
meta_table.close()
|
||||
end
|
||||
|
@ -167,11 +210,24 @@ module Hbase
|
|||
@admin.tableExists(table_name)
|
||||
end
|
||||
|
||||
def isNamespace?(table_name)
|
||||
table_name.start_with?('@')
|
||||
end
|
||||
|
||||
# Does Namespace exist
|
||||
def namespace_exists?(namespace_name)
|
||||
namespaceDesc = @admin.getNamespaceDescriptor(namespace_name)
|
||||
if(namespaceDesc == nil)
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# Make sure that security tables are available
|
||||
def security_available?()
|
||||
raise(ArgumentError, "DISABLED: Security features are not available") \
|
||||
unless exists?(org.apache.hadoop.hbase.security.access.AccessControlLists::ACL_TABLE_NAME)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
Binary file not shown.
|
@ -49,7 +49,7 @@ public class TestSecureLoadIncrementalHFiles extends TestLoadIncrementalHFiles{
|
|||
util.startMiniCluster();
|
||||
|
||||
// Wait for the ACL table to become available
|
||||
util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
|
||||
util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public class TestSecureLoadIncrementalHFilesSplitRecovery extends TestLoadIncrem
|
|||
util.startMiniCluster();
|
||||
|
||||
// Wait for the ACL table to become available
|
||||
util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
|
||||
util.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
|
||||
}
|
||||
|
||||
//Disabling this test as it does not work in secure mode
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
|
@ -41,8 +42,11 @@ import org.apache.hadoop.hbase.TableName;
|
|||
import org.apache.hadoop.hbase.Waiter;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
import org.apache.hadoop.hbase.client.Result;
|
||||
import org.apache.hadoop.hbase.client.ResultScanner;
|
||||
import org.apache.hadoop.hbase.client.Scan;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.security.access.AccessControlLists;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.FSUtils;
|
||||
import org.apache.hadoop.util.ToolRunner;
|
||||
|
@ -64,6 +68,9 @@ import org.junit.experimental.categories.Category;
|
|||
* Contains snapshots with snapshot{num}Keys as the contents:
|
||||
* snapshot1Keys, snapshot2Keys
|
||||
*
|
||||
* Image also contains _acl_ table with one region and two storefiles.
|
||||
* This is needed to test the acl table migration.
|
||||
*
|
||||
*/
|
||||
@Category(MediumTests.class)
|
||||
public class TestNamespaceUpgrade {
|
||||
|
@ -103,6 +110,7 @@ public class TestNamespaceUpgrade {
|
|||
Configuration toolConf = TEST_UTIL.getConfiguration();
|
||||
conf.set(HConstants.HBASE_DIR, TEST_UTIL.getDefaultRootDirPath().toString());
|
||||
ToolRunner.run(toolConf, new NamespaceUpgrade(), new String[]{"--upgrade"});
|
||||
doFsCommand(shell, new String [] {"-lsr", "/"});
|
||||
|
||||
assertTrue(FSUtils.getVersion(fs, hbaseRootDir).equals(HConstants.FILE_SYSTEM_VERSION));
|
||||
TEST_UTIL.startMiniHBaseCluster(1, 1);
|
||||
|
@ -115,6 +123,22 @@ public class TestNamespaceUpgrade {
|
|||
Assert.assertEquals(currentKeys.length, count);
|
||||
}
|
||||
assertEquals(2, TEST_UTIL.getHBaseAdmin().listNamespaceDescriptors().length);
|
||||
|
||||
//verify ACL table is migrated
|
||||
HTable secureTable = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
ResultScanner scanner = secureTable.getScanner(new Scan());
|
||||
int count = 0;
|
||||
for(Result r : scanner) {
|
||||
count++;
|
||||
}
|
||||
assertEquals(3, count);
|
||||
assertFalse(TEST_UTIL.getHBaseAdmin().tableExists("_acl_"));
|
||||
|
||||
//verify ACL table was compacted
|
||||
List<HRegion> regions = TEST_UTIL.getMiniHBaseCluster().getRegions(secureTable.getName());
|
||||
for(HRegion region : regions) {
|
||||
assertEquals(1, region.getStores().size());
|
||||
}
|
||||
}
|
||||
|
||||
private static File untar(final File testdir) throws IOException {
|
||||
|
|
|
@ -18,11 +18,25 @@
|
|||
|
||||
package org.apache.hadoop.hbase.security.access;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException;
|
||||
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.CheckPermissionsRequest;
|
||||
import org.apache.hadoop.hbase.security.AccessDeniedException;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
|
||||
import com.google.protobuf.ServiceException;
|
||||
|
||||
/**
|
||||
* Utility methods for testing security
|
||||
*/
|
||||
|
@ -37,4 +51,105 @@ public class SecureTestUtil {
|
|||
String currentUser = User.getCurrent().getName();
|
||||
conf.set("hbase.superuser", "admin,"+currentUser);
|
||||
}
|
||||
|
||||
public void verifyAllowed(User user, PrivilegedExceptionAction... actions) throws Exception {
|
||||
for (PrivilegedExceptionAction action : actions) {
|
||||
try {
|
||||
user.runAs(action);
|
||||
} catch (AccessDeniedException ade) {
|
||||
fail("Expected action to pass for user '" + user.getShortName() + "' but was denied");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyAllowed(PrivilegedExceptionAction action, User... users) throws Exception {
|
||||
for (User user : users) {
|
||||
verifyAllowed(user, action);
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyDenied(User user, PrivilegedExceptionAction... actions) throws Exception {
|
||||
for (PrivilegedExceptionAction action : actions) {
|
||||
try {
|
||||
user.runAs(action);
|
||||
fail("Expected AccessDeniedException for user '" + user.getShortName() + "'");
|
||||
} catch (IOException e) {
|
||||
boolean isAccessDeniedException = false;
|
||||
if(e instanceof RetriesExhaustedWithDetailsException) {
|
||||
// in case of batch operations, and put, the client assembles a
|
||||
// RetriesExhaustedWithDetailsException instead of throwing an
|
||||
// AccessDeniedException
|
||||
for(Throwable ex : ((RetriesExhaustedWithDetailsException) e).getCauses()) {
|
||||
if (ex instanceof AccessDeniedException) {
|
||||
isAccessDeniedException = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For doBulkLoad calls AccessDeniedException
|
||||
// is buried in the stack trace
|
||||
Throwable ex = e;
|
||||
do {
|
||||
if (ex instanceof AccessDeniedException) {
|
||||
isAccessDeniedException = true;
|
||||
break;
|
||||
}
|
||||
} while((ex = ex.getCause()) != null);
|
||||
}
|
||||
if (!isAccessDeniedException) {
|
||||
fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
|
||||
}
|
||||
} catch (UndeclaredThrowableException ute) {
|
||||
// TODO why we get a PrivilegedActionException, which is unexpected?
|
||||
Throwable ex = ute.getUndeclaredThrowable();
|
||||
if (ex instanceof PrivilegedActionException) {
|
||||
ex = ((PrivilegedActionException) ex).getException();
|
||||
}
|
||||
if (ex instanceof ServiceException) {
|
||||
ServiceException se = (ServiceException)ex;
|
||||
if (se.getCause() != null && se.getCause() instanceof AccessDeniedException) {
|
||||
// expected result
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("Not receiving AccessDeniedException for user '" + user.getShortName() + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyDenied(PrivilegedExceptionAction action, User... users) throws Exception {
|
||||
for (User user : users) {
|
||||
verifyDenied(user, action);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkTablePerms(Configuration conf, byte[] table, byte[] family, byte[] column,
|
||||
Permission.Action... actions) throws IOException {
|
||||
Permission[] perms = new Permission[actions.length];
|
||||
for (int i = 0; i < actions.length; i++) {
|
||||
perms[i] = new TablePermission(TableName.valueOf(table), family, column, actions[i]);
|
||||
}
|
||||
|
||||
checkTablePerms(conf, table, perms);
|
||||
}
|
||||
|
||||
public void checkTablePerms(Configuration conf, byte[] table, Permission... perms) throws IOException {
|
||||
CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
|
||||
for (Permission p : perms) {
|
||||
request.addPermission(ProtobufUtil.toPermission(p));
|
||||
}
|
||||
HTable acl = new HTable(conf, table);
|
||||
try {
|
||||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(acl.coprocessorService(new byte[0]));
|
||||
try {
|
||||
protocol.checkPermissions(null, request.build());
|
||||
} catch (ServiceException se) {
|
||||
ProtobufUtil.toIOException(se);
|
||||
}
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ public class TestAccessControlFilter {
|
|||
conf.set("hbase.superuser", conf.get("hbase.superuser", "") +
|
||||
String.format(",%s.hfs.0,%s.hfs.1,%s.hfs.2", baseuser, baseuser, baseuser));
|
||||
TEST_UTIL.startMiniCluster();
|
||||
TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
|
||||
TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
|
||||
|
||||
ADMIN = User.createUserForTesting(conf, "admin", new String[]{"supergroup"});
|
||||
READER = User.createUserForTesting(conf, "reader", new String[0]);
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
package org.apache.hadoop.hbase.security.access;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
|
@ -103,7 +103,7 @@ import com.google.protobuf.ServiceException;
|
|||
*/
|
||||
@Category(LargeTests.class)
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class TestAccessController {
|
||||
public class TestAccessController extends SecureTestUtil {
|
||||
private static final Log LOG = LogFactory.getLog(TestAccessController.class);
|
||||
@Rule public TestTableName TEST_TABLE = new TestTableName();
|
||||
private static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
|
||||
|
@ -156,7 +156,7 @@ public class TestAccessController {
|
|||
Coprocessor.PRIORITY_HIGHEST, 1, conf);
|
||||
|
||||
// Wait for the ACL table to become available
|
||||
TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
|
||||
TEST_UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
|
||||
|
||||
// create a set of test users
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
|
@ -196,7 +196,6 @@ public class TestAccessController {
|
|||
AccessControlService.newBlockingStub(service);
|
||||
|
||||
protocol.grant(null, RequestConverter.buildGrantRequest(USER_ADMIN.getShortName(),
|
||||
null, null, null,
|
||||
AccessControlProtos.Permission.Action.ADMIN,
|
||||
AccessControlProtos.Permission.Action.CREATE,
|
||||
AccessControlProtos.Permission.Action.READ,
|
||||
|
@ -235,17 +234,6 @@ public class TestAccessController {
|
|||
assertEquals(0, AccessControlLists.getTablePermissions(conf, TEST_TABLE.getTableName()).size());
|
||||
}
|
||||
|
||||
public void verifyAllowed(User user, PrivilegedExceptionAction... actions) throws Exception {
|
||||
for (PrivilegedExceptionAction action : actions) {
|
||||
try {
|
||||
user.runAs(action);
|
||||
} catch (AccessDeniedException ade) {
|
||||
fail("Expected action to pass for user '" + user.getShortName() + "' but was denied: " +
|
||||
ade.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyAllowed(PrivilegedExceptionAction action, User... users) throws Exception {
|
||||
for (User user : users) {
|
||||
verifyAllowed(user, action);
|
||||
|
@ -415,7 +403,7 @@ public class TestAccessController {
|
|||
PrivilegedExceptionAction disableAclTable = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
ACCESS_CONTROLLER.preDisableTable(ObserverContext.createAndPrepare(CP_ENV, null),
|
||||
AccessControlLists.ACL_TABLE);
|
||||
AccessControlLists.ACL_TABLE_NAME);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
@ -1164,7 +1152,7 @@ public class TestAccessController {
|
|||
ProtobufUtil.grant(protocol, tblUser.getShortName(),
|
||||
tableName, null, null, Permission.Action.READ);
|
||||
ProtobufUtil.grant(protocol, gblUser.getShortName(),
|
||||
null, null, null, Permission.Action.READ);
|
||||
Permission.Action.READ);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1188,7 +1176,7 @@ public class TestAccessController {
|
|||
ProtobufUtil.grant(protocol, tblUser.getShortName(),
|
||||
tableName, null, null, Permission.Action.WRITE);
|
||||
ProtobufUtil.grant(protocol, gblUser.getShortName(),
|
||||
null, null, null, Permission.Action.WRITE);
|
||||
Permission.Action.WRITE);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1212,7 +1200,7 @@ public class TestAccessController {
|
|||
ProtobufUtil.grant(protocol, tblUser.getShortName(), tableName, null, null,
|
||||
Permission.Action.READ, Permission.Action.WRITE);
|
||||
ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, null, null);
|
||||
ProtobufUtil.revoke(protocol, gblUser.getShortName(), null, null, null);
|
||||
ProtobufUtil.revoke(protocol, gblUser.getShortName());
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1236,7 +1224,7 @@ public class TestAccessController {
|
|||
ProtobufUtil.grant(protocol, tblUser.getShortName(),
|
||||
tableName, family1, null, Permission.Action.READ);
|
||||
ProtobufUtil.grant(protocol, gblUser.getShortName(),
|
||||
null, null, null, Permission.Action.READ);
|
||||
Permission.Action.READ);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1262,7 +1250,7 @@ public class TestAccessController {
|
|||
ProtobufUtil.grant(protocol, tblUser.getShortName(),
|
||||
tableName, family2, null, Permission.Action.WRITE);
|
||||
ProtobufUtil.grant(protocol, gblUser.getShortName(),
|
||||
null, null, null, Permission.Action.WRITE);
|
||||
Permission.Action.WRITE);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1287,7 +1275,7 @@ public class TestAccessController {
|
|||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(service);
|
||||
ProtobufUtil.revoke(protocol, tblUser.getShortName(), tableName, family2, null);
|
||||
ProtobufUtil.revoke(protocol, gblUser.getShortName(), null, null, null);
|
||||
ProtobufUtil.revoke(protocol, gblUser.getShortName());
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
|
@ -1607,12 +1595,12 @@ public class TestAccessController {
|
|||
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
|
||||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(service);
|
||||
perms = ProtobufUtil.getUserPermissions(protocol, null);
|
||||
perms = ProtobufUtil.getUserPermissions(protocol);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
UserPermission adminPerm = new UserPermission(Bytes.toBytes(USER_ADMIN.getShortName()),
|
||||
AccessControlLists.ACL_TABLE, null, null, Bytes.toBytes("ACRW"));
|
||||
AccessControlLists.ACL_TABLE_NAME, null, null, Bytes.toBytes("ACRW"));
|
||||
assertTrue("Only user admin has permission on table _acl_ per setup",
|
||||
perms.size() == 1 && hasFoundUserPermission(adminPerm, perms));
|
||||
}
|
||||
|
@ -1632,7 +1620,10 @@ public class TestAccessController {
|
|||
CheckPermissionsRequest.Builder request = CheckPermissionsRequest.newBuilder();
|
||||
for (Action a : actions) {
|
||||
request.addPermission(AccessControlProtos.Permission.newBuilder()
|
||||
.addAction(ProtobufUtil.toPermissionAction(a)).build());
|
||||
.setType(AccessControlProtos.Permission.Type.Global)
|
||||
.setGlobalPermission(
|
||||
AccessControlProtos.GlobalPermission.newBuilder()
|
||||
.addAction(ProtobufUtil.toPermissionAction(a)).build()));
|
||||
}
|
||||
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
try {
|
||||
|
@ -1813,8 +1804,11 @@ public class TestAccessController {
|
|||
// check for wrong table region
|
||||
CheckPermissionsRequest checkRequest = CheckPermissionsRequest.newBuilder()
|
||||
.addPermission(AccessControlProtos.Permission.newBuilder()
|
||||
.setTableName(ProtobufUtil.toProtoTableName(TEST_TABLE.getTableName()))
|
||||
.addAction(AccessControlProtos.Permission.Action.CREATE)
|
||||
.setType(AccessControlProtos.Permission.Type.Table)
|
||||
.setTablePermission(
|
||||
AccessControlProtos.TablePermission.newBuilder()
|
||||
.setTableName(ProtobufUtil.toProtoTableName(TEST_TABLE.getTableName()))
|
||||
.addAction(AccessControlProtos.Permission.Action.CREATE))
|
||||
).build();
|
||||
acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
try {
|
||||
|
@ -1935,7 +1929,7 @@ public class TestAccessController {
|
|||
// User name for the new RegionServer we plan to add.
|
||||
String activeUserForNewRs = currentUser + ".hfs."
|
||||
+ hbaseCluster.getLiveRegionServerThreads().size();
|
||||
ProtobufUtil.grant(protocol, activeUserForNewRs, null, null, null,
|
||||
ProtobufUtil.grant(protocol, activeUserForNewRs,
|
||||
Permission.Action.ADMIN, Permission.Action.CREATE,
|
||||
Permission.Action.READ, Permission.Action.WRITE);
|
||||
} finally {
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hbase.security.access;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.Coprocessor;
|
||||
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||
import org.apache.hadoop.hbase.HColumnDescriptor;
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.HTableDescriptor;
|
||||
import org.apache.hadoop.hbase.MediumTests;
|
||||
import org.apache.hadoop.hbase.NamespaceDescriptor;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.client.Get;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
import org.apache.hadoop.hbase.client.Result;
|
||||
import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
|
||||
import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
|
||||
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
|
||||
import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
|
||||
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos.AccessControlService;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
import org.apache.hadoop.hbase.security.access.Permission.Action;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.protobuf.BlockingRpcChannel;
|
||||
|
||||
@Category(MediumTests.class)
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class TestNamespaceCommands extends SecureTestUtil {
|
||||
private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
|
||||
private static String TestNamespace = "ns1";
|
||||
private static Configuration conf;
|
||||
private static MasterCoprocessorEnvironment CP_ENV;
|
||||
private static AccessController ACCESS_CONTROLLER;
|
||||
|
||||
//user with all permissions
|
||||
private static User SUPERUSER;
|
||||
// user with rw permissions
|
||||
private static User USER_RW;
|
||||
// user with create table permissions alone
|
||||
private static User USER_CREATE;
|
||||
// user with permission on namespace for testing all operations.
|
||||
private static User USER_NSP_WRITE;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
conf = UTIL.getConfiguration();
|
||||
SecureTestUtil.enableSecurity(conf);
|
||||
conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY, AccessController.class.getName());
|
||||
UTIL.startMiniCluster();
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
USER_RW = User.createUserForTesting(conf, "rw_user", new String[0]);
|
||||
USER_CREATE = User.createUserForTesting(conf, "create_user", new String[0]);
|
||||
USER_NSP_WRITE = User.createUserForTesting(conf, "namespace_write", new String[0]);
|
||||
UTIL.getHBaseAdmin().createNamespace(NamespaceDescriptor.create(TestNamespace).build());
|
||||
|
||||
// Wait for the ACL table to become available
|
||||
UTIL.waitTableAvailable(AccessControlLists.ACL_TABLE_NAME.getName(), 8000);
|
||||
|
||||
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
MasterCoprocessorHost cpHost = UTIL.getMiniHBaseCluster().getMaster().getCoprocessorHost();
|
||||
cpHost.load(AccessController.class, Coprocessor.PRIORITY_HIGHEST, conf);
|
||||
ACCESS_CONTROLLER = (AccessController) cpHost.findCoprocessor(AccessController.class.getName());
|
||||
try {
|
||||
BlockingRpcChannel service =
|
||||
acl.coprocessorService(HConstants.EMPTY_START_ROW);
|
||||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(service);
|
||||
ProtobufUtil.grant(protocol, USER_NSP_WRITE.getShortName(),
|
||||
TestNamespace, Action.WRITE);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
UTIL.getHBaseAdmin().deleteNamespace(TestNamespace);
|
||||
UTIL.shutdownMiniCluster();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAclTableEntries() throws Exception {
|
||||
String userTestNamespace = "userTestNsp";
|
||||
AccessControlService.BlockingInterface protocol = null;
|
||||
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
try {
|
||||
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
|
||||
protocol = AccessControlService.newBlockingStub(service);
|
||||
ProtobufUtil.grant(protocol, userTestNamespace, TestNamespace, Permission.Action.WRITE);
|
||||
Result result = acl.get(new Get(Bytes.toBytes(userTestNamespace)));
|
||||
assertTrue(result != null);
|
||||
ListMultimap<String, TablePermission> perms =
|
||||
AccessControlLists.getNamespacePermissions(conf, TestNamespace);
|
||||
assertEquals(2, perms.size());
|
||||
List<TablePermission> namespacePerms = perms.get(userTestNamespace);
|
||||
assertTrue(perms.containsKey(userTestNamespace));
|
||||
assertEquals(1, namespacePerms.size());
|
||||
assertEquals(TestNamespace,
|
||||
namespacePerms.get(0).getNamespace());
|
||||
assertEquals(null, namespacePerms.get(0).getFamily());
|
||||
assertEquals(null, namespacePerms.get(0).getQualifier());
|
||||
assertEquals(1, namespacePerms.get(0).getActions().length);
|
||||
assertEquals(Permission.Action.WRITE, namespacePerms.get(0).getActions()[0]);
|
||||
// Now revoke and check.
|
||||
ProtobufUtil.revoke(protocol, userTestNamespace, TestNamespace,
|
||||
Permission.Action.WRITE);
|
||||
perms = AccessControlLists.getNamespacePermissions(conf, TestNamespace);
|
||||
assertEquals(1, perms.size());
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyNamespace() throws Exception {
|
||||
PrivilegedExceptionAction modifyNamespace = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
ACCESS_CONTROLLER.preModifyNamespace(ObserverContext.createAndPrepare(CP_ENV, null),
|
||||
NamespaceDescriptor.create(TestNamespace).addConfiguration("abc", "156").build());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
// verify that superuser or hbase admin can modify namespaces.
|
||||
verifyAllowed(modifyNamespace, SUPERUSER);
|
||||
// all others should be denied
|
||||
verifyDenied(modifyNamespace, USER_NSP_WRITE, USER_CREATE, USER_RW);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrantRevoke() throws Exception{
|
||||
//Only HBase super user should be able to grant and revoke permissions to
|
||||
// namespaces.
|
||||
final String testUser = "testUser";
|
||||
PrivilegedExceptionAction grantAction = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
try {
|
||||
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
|
||||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(service);
|
||||
ProtobufUtil.grant(protocol, testUser, TestNamespace, Action.WRITE);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
PrivilegedExceptionAction revokeAction = new PrivilegedExceptionAction() {
|
||||
public Object run() throws Exception {
|
||||
HTable acl = new HTable(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
try {
|
||||
BlockingRpcChannel service = acl.coprocessorService(HConstants.EMPTY_START_ROW);
|
||||
AccessControlService.BlockingInterface protocol =
|
||||
AccessControlService.newBlockingStub(service);
|
||||
ProtobufUtil.revoke(protocol, testUser, TestNamespace, Action.WRITE);
|
||||
} finally {
|
||||
acl.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
verifyAllowed(grantAction, SUPERUSER);
|
||||
verifyDenied(grantAction, USER_CREATE, USER_RW);
|
||||
|
||||
verifyAllowed(revokeAction, SUPERUSER);
|
||||
verifyDenied(revokeAction, USER_CREATE, USER_RW);
|
||||
|
||||
}
|
||||
}
|
|
@ -97,7 +97,7 @@ public class TestTablePermissions {
|
|||
UTIL.startMiniCluster();
|
||||
|
||||
// Wait for the ACL table to become available
|
||||
UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME);
|
||||
UTIL.waitTableEnabled(AccessControlLists.ACL_TABLE_NAME.getName());
|
||||
|
||||
ZKW = new ZooKeeperWatcher(UTIL.getConfiguration(),
|
||||
"TestTablePermissions", ABORTABLE);
|
||||
|
@ -116,7 +116,7 @@ public class TestTablePermissions {
|
|||
Configuration conf = UTIL.getConfiguration();
|
||||
AccessControlLists.removeTablePermissions(conf, TEST_TABLE);
|
||||
AccessControlLists.removeTablePermissions(conf, TEST_TABLE2);
|
||||
AccessControlLists.removeTablePermissions(conf, AccessControlLists.ACL_TABLE);
|
||||
AccessControlLists.removeTablePermissions(conf, AccessControlLists.ACL_TABLE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,12 +240,12 @@ public class TestTablePermissions {
|
|||
TablePermission.Action.READ, TablePermission.Action.WRITE));
|
||||
|
||||
// check full load
|
||||
Map<TableName,ListMultimap<String,TablePermission>> allPerms =
|
||||
Map<byte[], ListMultimap<String,TablePermission>> allPerms =
|
||||
AccessControlLists.loadAll(conf);
|
||||
assertEquals("Full permission map should have entries for both test tables",
|
||||
2, allPerms.size());
|
||||
|
||||
userPerms = allPerms.get(TEST_TABLE).get("hubert");
|
||||
userPerms = allPerms.get(TEST_TABLE.getName()).get("hubert");
|
||||
assertNotNull(userPerms);
|
||||
assertEquals(1, userPerms.size());
|
||||
permission = userPerms.get(0);
|
||||
|
@ -253,7 +253,7 @@ public class TestTablePermissions {
|
|||
assertEquals(1, permission.getActions().length);
|
||||
assertEquals(TablePermission.Action.READ, permission.getActions()[0]);
|
||||
|
||||
userPerms = allPerms.get(TEST_TABLE2).get("hubert");
|
||||
userPerms = allPerms.get(TEST_TABLE2.getName()).get("hubert");
|
||||
assertNotNull(userPerms);
|
||||
assertEquals(1, userPerms.size());
|
||||
permission = userPerms.get(0);
|
||||
|
@ -310,7 +310,7 @@ public class TestTablePermissions {
|
|||
ListMultimap<String,TablePermission> permissions = createPermissions();
|
||||
byte[] permsData = AccessControlLists.writePermissionsAsBytes(permissions, conf);
|
||||
|
||||
ListMultimap<String,TablePermission> copy =
|
||||
ListMultimap<String, TablePermission> copy =
|
||||
AccessControlLists.readPermissions(permsData, conf);
|
||||
|
||||
checkMultimapEqual(permissions, copy);
|
||||
|
|
|
@ -108,7 +108,7 @@ public class TestZKPermissionsWatcher {
|
|||
List<TablePermission> acl = new ArrayList<TablePermission>();
|
||||
acl.add(new TablePermission(TEST_TABLE, null, TablePermission.Action.READ,
|
||||
TablePermission.Action.WRITE));
|
||||
AUTH_A.setUserPermissions("george", TEST_TABLE, acl);
|
||||
AUTH_A.setTableUserPermissions("george", TEST_TABLE, acl);
|
||||
Thread.sleep(100);
|
||||
|
||||
// check it
|
||||
|
@ -132,7 +132,7 @@ public class TestZKPermissionsWatcher {
|
|||
// update ACL: hubert R
|
||||
acl = new ArrayList<TablePermission>();
|
||||
acl.add(new TablePermission(TEST_TABLE, null, TablePermission.Action.READ));
|
||||
AUTH_B.setUserPermissions("hubert", TEST_TABLE, acl);
|
||||
AUTH_B.setTableUserPermissions("hubert", TEST_TABLE, acl);
|
||||
Thread.sleep(100);
|
||||
|
||||
// check it
|
||||
|
|
Loading…
Reference in New Issue