HBASE-12745 Visibility Labels: support visibility labels for user groups. (Jerry He)
This commit is contained in:
parent
8b7a20f4ee
commit
833feefbf9
|
@ -641,6 +641,13 @@ public class AccessControlLists {
|
||||||
return aclKey.substring(GROUP_PREFIX.length());
|
return aclKey.substring(GROUP_PREFIX.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the group entry with the group prefix for a group principal.
|
||||||
|
*/
|
||||||
|
public static String toGroupEntry(String name) {
|
||||||
|
return GROUP_PREFIX + name;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isNamespaceEntry(String entryName) {
|
public static boolean isNamespaceEntry(String entryName) {
|
||||||
return entryName.charAt(0) == NAMESPACE_PREFIX;
|
return entryName.charAt(0) == NAMESPACE_PREFIX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -63,8 +64,6 @@ import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.apache.hadoop.hbase.util.Pair;
|
import org.apache.hadoop.hbase.util.Pair;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
@InterfaceAudience.Private
|
@InterfaceAudience.Private
|
||||||
public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService {
|
public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService {
|
||||||
|
|
||||||
|
@ -81,6 +80,7 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
private VisibilityLabelsCache labelsCache;
|
private VisibilityLabelsCache labelsCache;
|
||||||
private List<ScanLabelGenerator> scanLabelGenerators;
|
private List<ScanLabelGenerator> scanLabelGenerators;
|
||||||
private List<String> superUsers;
|
private List<String> superUsers;
|
||||||
|
private List<String> superGroups;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
@ -117,7 +117,10 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
throw ioe;
|
throw ioe;
|
||||||
}
|
}
|
||||||
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
||||||
this.superUsers = getSystemAndSuperUsers();
|
Pair<List<String>, List<String>> superUsersAndGroups =
|
||||||
|
VisibilityUtils.getSystemAndSuperUsers(this.conf);
|
||||||
|
this.superUsers = superUsersAndGroups.getFirst();
|
||||||
|
this.superGroups = superUsersAndGroups.getSecond();
|
||||||
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
||||||
this.labelsRegion = e.getRegion();
|
this.labelsRegion = e.getRegion();
|
||||||
Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
|
Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
|
||||||
|
@ -203,21 +206,6 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<String> getSystemAndSuperUsers() throws IOException {
|
|
||||||
User user = User.getCurrent();
|
|
||||||
if (user == null) {
|
|
||||||
throw new IOException("Unable to obtain the current user, "
|
|
||||||
+ "authorization checks for internal operations will not work correctly!");
|
|
||||||
}
|
|
||||||
if (LOG.isTraceEnabled()) {
|
|
||||||
LOG.trace("Current user name is " + user.getShortName());
|
|
||||||
}
|
|
||||||
String currentUser = user.getShortName();
|
|
||||||
List<String> superUsers = Lists.asList(currentUser,
|
|
||||||
this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
|
|
||||||
return superUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
||||||
assert labelsRegion != null;
|
assert labelsRegion != null;
|
||||||
|
@ -276,7 +264,14 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||||
assert labelsRegion != null;
|
assert labelsRegion != null;
|
||||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||||
List<String> currentAuths = this.getAuths(user, true);
|
List<String> currentAuths;
|
||||||
|
if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
|
||||||
|
String group = AccessControlLists.getGroupName(Bytes.toString(user));
|
||||||
|
currentAuths = this.getGroupAuths(new String[]{group}, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentAuths = this.getUserAuths(user, true);
|
||||||
|
}
|
||||||
List<Mutation> deletes = new ArrayList<Mutation>(authLabels.size());
|
List<Mutation> deletes = new ArrayList<Mutation>(authLabels.size());
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (byte[] authLabel : authLabels) {
|
for (byte[] authLabel : authLabels) {
|
||||||
|
@ -329,17 +324,20 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
|
public List<String> getUserAuths(byte[] user, boolean systemCall)
|
||||||
|
throws IOException {
|
||||||
assert (labelsRegion != null || systemCall);
|
assert (labelsRegion != null || systemCall);
|
||||||
if (systemCall || labelsRegion == null) {
|
if (systemCall || labelsRegion == null) {
|
||||||
return this.labelsCache.getAuths(Bytes.toString(user));
|
return this.labelsCache.getUserAuths(Bytes.toString(user));
|
||||||
}
|
}
|
||||||
Scan s = new Scan();
|
Scan s = new Scan();
|
||||||
|
if (user != null && user.length > 0) {
|
||||||
s.addColumn(LABELS_TABLE_FAMILY, user);
|
s.addColumn(LABELS_TABLE_FAMILY, user);
|
||||||
|
}
|
||||||
Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
|
Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
|
||||||
new Authorizations(SYSTEM_LABEL));
|
new Authorizations(SYSTEM_LABEL));
|
||||||
s.setFilter(filter);
|
s.setFilter(filter);
|
||||||
List<String> auths = new ArrayList<String>();
|
ArrayList<String> auths = new ArrayList<String>();
|
||||||
RegionScanner scanner = this.labelsRegion.getScanner(s);
|
RegionScanner scanner = this.labelsRegion.getScanner(s);
|
||||||
try {
|
try {
|
||||||
List<Cell> results = new ArrayList<Cell>(1);
|
List<Cell> results = new ArrayList<Cell>(1);
|
||||||
|
@ -360,6 +358,43 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
return auths;
|
return auths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getGroupAuths(String[] groups, boolean systemCall)
|
||||||
|
throws IOException {
|
||||||
|
assert (labelsRegion != null || systemCall);
|
||||||
|
if (systemCall || labelsRegion == null) {
|
||||||
|
return this.labelsCache.getGroupAuths(groups);
|
||||||
|
}
|
||||||
|
Scan s = new Scan();
|
||||||
|
if (groups != null && groups.length > 0) {
|
||||||
|
for (String group : groups) {
|
||||||
|
s.addColumn(LABELS_TABLE_FAMILY, Bytes.toBytes(AccessControlLists.toGroupEntry(group)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
|
||||||
|
new Authorizations(SYSTEM_LABEL));
|
||||||
|
s.setFilter(filter);
|
||||||
|
Set<String> auths = new HashSet<String>();
|
||||||
|
RegionScanner scanner = this.labelsRegion.getScanner(s);
|
||||||
|
try {
|
||||||
|
List<Cell> results = new ArrayList<Cell>(1);
|
||||||
|
while (true) {
|
||||||
|
scanner.next(results);
|
||||||
|
if (results.isEmpty()) break;
|
||||||
|
Cell cell = results.get(0);
|
||||||
|
int ordinal = Bytes.toInt(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
|
||||||
|
String label = this.labelsCache.getLabel(ordinal);
|
||||||
|
if (label != null) {
|
||||||
|
auths.add(label);
|
||||||
|
}
|
||||||
|
results.clear();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
scanner.close();
|
||||||
|
}
|
||||||
|
return new ArrayList<String>(auths);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> listLabels(String regex) throws IOException {
|
public List<String> listLabels(String regex) throws IOException {
|
||||||
assert (labelsRegion != null);
|
assert (labelsRegion != null);
|
||||||
|
@ -383,9 +418,11 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
@Override
|
@Override
|
||||||
public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
|
public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
|
||||||
boolean checkAuths) throws IOException {
|
boolean checkAuths) throws IOException {
|
||||||
Set<Integer> auths = null;
|
Set<Integer> auths = new HashSet<Integer>();
|
||||||
if (checkAuths) {
|
if (checkAuths) {
|
||||||
auths = this.labelsCache.getAuthsAsOrdinals(VisibilityUtils.getActiveUser().getShortName());
|
User user = VisibilityUtils.getActiveUser();
|
||||||
|
auths.addAll(this.labelsCache.getUserAuthsAsOrdinals(user.getShortName()));
|
||||||
|
auths.addAll(this.labelsCache.getGroupAuthsAsOrdinals(user.getGroupNames()));
|
||||||
}
|
}
|
||||||
return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat,
|
return VisibilityUtils.createVisibilityExpTags(visExpression, withSerializationFormat,
|
||||||
checkAuths, auths, labelsCache);
|
checkAuths, auths, labelsCache);
|
||||||
|
@ -494,26 +531,44 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isReadFromSystemAuthUser() throws IOException {
|
protected boolean isReadFromSystemAuthUser() throws IOException {
|
||||||
byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
|
User user = VisibilityUtils.getActiveUser();
|
||||||
return havingSystemAuth(user);
|
return havingSystemAuth(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean havingSystemAuth(byte[] user) throws IOException {
|
public boolean havingSystemAuth(User user) throws IOException {
|
||||||
// A super user has 'system' auth.
|
// A super user has 'system' auth.
|
||||||
if (isSystemOrSuperUser(user)) {
|
if (isSystemOrSuperUser(user)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// A user can also be explicitly granted 'system' auth.
|
// A user can also be explicitly granted 'system' auth.
|
||||||
List<String> auths = this.getAuths(user, true);
|
List<String> auths = this.getUserAuths(Bytes.toBytes(user.getShortName()), true);
|
||||||
if (LOG.isTraceEnabled()) {
|
if (LOG.isTraceEnabled()) {
|
||||||
LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
|
LOG.trace("The auths for user " + user.getShortName() + " are " + auths);
|
||||||
|
}
|
||||||
|
if (auths.contains(SYSTEM_LABEL)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auths = this.getGroupAuths(user.getGroupNames(), true);
|
||||||
|
if (LOG.isTraceEnabled()) {
|
||||||
|
LOG.trace("The auths for groups of user " + user.getShortName() + " are " + auths);
|
||||||
}
|
}
|
||||||
return auths.contains(SYSTEM_LABEL);
|
return auths.contains(SYSTEM_LABEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isSystemOrSuperUser(byte[] user) throws IOException {
|
private boolean isSystemOrSuperUser(User user) throws IOException {
|
||||||
return this.superUsers.contains(Bytes.toString(user));
|
if (this.superUsers.contains(user.getShortName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String[] groups = user.getGroupNames();
|
||||||
|
if (groups != null && groups.length > 0) {
|
||||||
|
for (String group : groups) {
|
||||||
|
if (this.superGroups.contains(group)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
package org.apache.hadoop.hbase.security.visibility;
|
package org.apache.hadoop.hbase.security.visibility;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -60,8 +62,10 @@ public class DefinedSetFilterScanLabelGenerator implements ScanLabelGenerator {
|
||||||
if (authorizations != null) {
|
if (authorizations != null) {
|
||||||
List<String> labels = authorizations.getLabels();
|
List<String> labels = authorizations.getLabels();
|
||||||
String userName = user.getShortName();
|
String userName = user.getShortName();
|
||||||
List<String> auths = this.labelsCache.getAuths(userName);
|
Set<String> auths = new HashSet<String>();
|
||||||
return dropLabelsNotInUserAuths(labels, auths, userName);
|
auths.addAll(this.labelsCache.getUserAuths(userName));
|
||||||
|
auths.addAll(this.labelsCache.getGroupAuths(user.getGroupNames()));
|
||||||
|
return dropLabelsNotInUserAuths(labels, new ArrayList<String>(auths), userName);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.security.visibility;
|
package org.apache.hadoop.hbase.security.visibility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -59,7 +62,10 @@ public class EnforcingScanLabelGenerator implements ScanLabelGenerator {
|
||||||
if (authorizations != null) {
|
if (authorizations != null) {
|
||||||
LOG.warn("Dropping authorizations requested by user " + userName + ": " + authorizations);
|
LOG.warn("Dropping authorizations requested by user " + userName + ": " + authorizations);
|
||||||
}
|
}
|
||||||
return this.labelsCache.getAuths(userName);
|
Set<String> auths = new HashSet<String>();
|
||||||
|
auths.addAll(this.labelsCache.getUserAuths(userName));
|
||||||
|
auths.addAll(this.labelsCache.getGroupAuths(user.getGroupNames()));
|
||||||
|
return new ArrayList<String>(auths);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hbase.security.visibility;
|
package org.apache.hadoop.hbase.security.visibility;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -62,7 +65,10 @@ public class FeedUserAuthScanLabelGenerator implements ScanLabelGenerator {
|
||||||
if (authorizations == null || authorizations.getLabels() == null
|
if (authorizations == null || authorizations.getLabels() == null
|
||||||
|| authorizations.getLabels().isEmpty()) {
|
|| authorizations.getLabels().isEmpty()) {
|
||||||
String userName = user.getShortName();
|
String userName = user.getShortName();
|
||||||
return this.labelsCache.getAuths(userName);
|
Set<String> auths = new HashSet<String>();
|
||||||
|
auths.addAll(this.labelsCache.getUserAuths(userName));
|
||||||
|
auths.addAll(this.labelsCache.getGroupAuths(user.getGroupNames()));
|
||||||
|
return new ArrayList<String>(auths);
|
||||||
}
|
}
|
||||||
return authorizations.getLabels();
|
return authorizations.getLabels();
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,8 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements
|
||||||
private Map<InternalScanner,String> scannerOwners =
|
private Map<InternalScanner,String> scannerOwners =
|
||||||
new MapMaker().weakKeys().makeMap();
|
new MapMaker().weakKeys().makeMap();
|
||||||
|
|
||||||
List<String> superUsers;
|
private List<String> superUsers;
|
||||||
|
private List<String> superGroups;
|
||||||
private VisibilityLabelService visibilityLabelService;
|
private VisibilityLabelService visibilityLabelService;
|
||||||
|
|
||||||
// Add to this list if there are any reserved tag types
|
// Add to this list if there are any reserved tag types
|
||||||
|
@ -159,7 +160,10 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements
|
||||||
visibilityLabelService = VisibilityLabelServiceManager.getInstance()
|
visibilityLabelService = VisibilityLabelServiceManager.getInstance()
|
||||||
.getVisibilityLabelService(this.conf);
|
.getVisibilityLabelService(this.conf);
|
||||||
}
|
}
|
||||||
this.superUsers = getSystemAndSuperUsers();
|
Pair<List<String>, List<String>> superUsersAndGroups =
|
||||||
|
VisibilityUtils.getSystemAndSuperUsers(this.conf);
|
||||||
|
this.superUsers = superUsersAndGroups.getFirst();
|
||||||
|
this.superGroups = superUsersAndGroups.getSecond();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -632,24 +636,20 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getSystemAndSuperUsers() throws IOException {
|
|
||||||
User user = User.getCurrent();
|
|
||||||
if (user == null) {
|
|
||||||
throw new IOException("Unable to obtain the current user, "
|
|
||||||
+ "authorization checks for internal operations will not work correctly!");
|
|
||||||
}
|
|
||||||
if (LOG.isTraceEnabled()) {
|
|
||||||
LOG.trace("Current user name is "+user.getShortName());
|
|
||||||
}
|
|
||||||
String currentUser = user.getShortName();
|
|
||||||
List<String> superUsers = Lists.asList(currentUser,
|
|
||||||
this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
|
|
||||||
return superUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSystemOrSuperUser() throws IOException {
|
private boolean isSystemOrSuperUser() throws IOException {
|
||||||
User activeUser = VisibilityUtils.getActiveUser();
|
User activeUser = VisibilityUtils.getActiveUser();
|
||||||
return this.superUsers.contains(activeUser.getShortName());
|
if (this.superUsers.contains(activeUser.getShortName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String[] groups = activeUser.getGroupNames();
|
||||||
|
if (groups != null && groups.length > 0) {
|
||||||
|
for (String group : groups) {
|
||||||
|
if (this.superGroups.contains(group)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -825,7 +825,13 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements
|
||||||
+ (requestingUser != null ? requestingUser.getShortName() : "null")
|
+ (requestingUser != null ? requestingUser.getShortName() : "null")
|
||||||
+ "' is not authorized to perform this action.");
|
+ "' is not authorized to perform this action.");
|
||||||
}
|
}
|
||||||
labels = this.visibilityLabelService.getAuths(user, false);
|
if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
|
||||||
|
String group = AccessControlLists.getGroupName(Bytes.toString(user));
|
||||||
|
labels = this.visibilityLabelService.getGroupAuths(new String[]{group}, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
labels = this.visibilityLabelService.getUserAuths(user, false);
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
ResponseConverter.setControllerException(controller, e);
|
ResponseConverter.setControllerException(controller, e);
|
||||||
}
|
}
|
||||||
|
@ -919,7 +925,7 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new IOException("Unable to retrieve calling user");
|
throw new IOException("Unable to retrieve calling user");
|
||||||
}
|
}
|
||||||
if (!(this.visibilityLabelService.havingSystemAuth(Bytes.toBytes(user.getShortName())))) {
|
if (!(this.visibilityLabelService.havingSystemAuth(user))) {
|
||||||
throw new AccessDeniedException("User '" + user.getShortName()
|
throw new AccessDeniedException("User '" + user.getShortName()
|
||||||
+ "' is not authorized to perform this action.");
|
+ "' is not authorized to perform this action.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
|
||||||
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
import org.apache.hadoop.hbase.classification.InterfaceStability;
|
||||||
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||||
import org.apache.hadoop.hbase.regionserver.OperationStatus;
|
import org.apache.hadoop.hbase.regionserver.OperationStatus;
|
||||||
|
import org.apache.hadoop.hbase.security.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface which deals with visibility labels and user auths admin service as well as the cell
|
* The interface which deals with visibility labels and user auths admin service as well as the cell
|
||||||
|
@ -73,13 +74,24 @@ public interface VisibilityLabelService extends Configurable {
|
||||||
OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException;
|
OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Retrieve the visibility labels for the user.
|
||||||
* @param user
|
* @param user
|
||||||
* Name of the user whose authorization to be retrieved
|
* Name of the user whose authorization to be retrieved
|
||||||
* @param systemCall
|
* @param systemCall
|
||||||
* Whether a system or user originated call.
|
* Whether a system or user originated call.
|
||||||
* @return Visibility labels authorized for the given user.
|
* @return Visibility labels authorized for the given user.
|
||||||
*/
|
*/
|
||||||
List<String> getAuths(byte[] user, boolean systemCall) throws IOException;
|
List<String> getUserAuths(byte[] user, boolean systemCall) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the visibility labels for the groups.
|
||||||
|
* @param groups
|
||||||
|
* Name of the groups whose authorization to be retrieved
|
||||||
|
* @param systemCall
|
||||||
|
* Whether a system or user originated call.
|
||||||
|
* @return Visibility labels authorized for the given group.
|
||||||
|
*/
|
||||||
|
List<String> getGroupAuths(String[] groups, boolean systemCall) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the list of visibility labels defined in the system.
|
* Retrieve the list of visibility labels defined in the system.
|
||||||
|
@ -124,7 +136,7 @@ public interface VisibilityLabelService extends Configurable {
|
||||||
* User for whom system auth check to be done.
|
* User for whom system auth check to be done.
|
||||||
* @return true if the given user is having system/super auth
|
* @return true if the given user is having system/super auth
|
||||||
*/
|
*/
|
||||||
boolean havingSystemAuth(byte[] user) throws IOException;
|
boolean havingSystemAuth(User user) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* System uses this for deciding whether a Cell can be deleted by matching visibility expression
|
* System uses this for deciding whether a Cell can be deleted by matching visibility expression
|
||||||
|
@ -167,4 +179,5 @@ public interface VisibilityLabelService extends Configurable {
|
||||||
*/
|
*/
|
||||||
byte[] encodeVisibilityForReplication(final List<Tag> visTags,
|
byte[] encodeVisibilityForReplication(final List<Tag> visTags,
|
||||||
final Byte serializationFormat) throws IOException;
|
final Byte serializationFormat) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.exceptions.DeserializationException;
|
||||||
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.MultiUserAuthorizations;
|
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.MultiUserAuthorizations;
|
||||||
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.UserAuthorizations;
|
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.UserAuthorizations;
|
||||||
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel;
|
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabel;
|
||||||
|
import org.apache.hadoop.hbase.security.access.AccessControlLists;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||||
import org.apache.zookeeper.KeeperException;
|
import org.apache.zookeeper.KeeperException;
|
||||||
|
@ -57,6 +58,8 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider {
|
||||||
private Map<String, Integer> labels = new HashMap<String, Integer>();
|
private Map<String, Integer> labels = new HashMap<String, Integer>();
|
||||||
private Map<Integer, String> ordinalVsLabels = new HashMap<Integer, String>();
|
private Map<Integer, String> ordinalVsLabels = new HashMap<Integer, String>();
|
||||||
private Map<String, Set<Integer>> userAuths = new HashMap<String, Set<Integer>>();
|
private Map<String, Set<Integer>> userAuths = new HashMap<String, Set<Integer>>();
|
||||||
|
private Map<String, Set<Integer>> groupAuths = new HashMap<String, Set<Integer>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This covers the members labels, ordinalVsLabels and userAuths
|
* This covers the members labels, ordinalVsLabels and userAuths
|
||||||
*/
|
*/
|
||||||
|
@ -139,10 +142,16 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider {
|
||||||
this.lock.writeLock().lock();
|
this.lock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
this.userAuths.clear();
|
this.userAuths.clear();
|
||||||
|
this.groupAuths.clear();
|
||||||
for (UserAuthorizations userAuths : multiUserAuths.getUserAuthsList()) {
|
for (UserAuthorizations userAuths : multiUserAuths.getUserAuthsList()) {
|
||||||
String user = Bytes.toString(userAuths.getUser().toByteArray());
|
String user = Bytes.toString(userAuths.getUser().toByteArray());
|
||||||
|
if (AccessControlLists.isGroupPrincipal(user)) {
|
||||||
|
this.groupAuths.put(AccessControlLists.getGroupName(user),
|
||||||
|
new HashSet<Integer>(userAuths.getAuthList()));
|
||||||
|
} else {
|
||||||
this.userAuths.put(user, new HashSet<Integer>(userAuths.getAuthList()));
|
this.userAuths.put(user, new HashSet<Integer>(userAuths.getAuthList()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.lock.writeLock().unlock();
|
this.lock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
@ -196,30 +205,37 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAuths(String user) {
|
public List<String> getUserAuths(String user) {
|
||||||
List<String> auths = EMPTY_LIST;
|
List<String> auths = EMPTY_LIST;
|
||||||
this.lock.readLock().lock();
|
Set<Integer> authOrdinals = getUserAuthsAsOrdinals(user);
|
||||||
try {
|
if (!authOrdinals.equals(EMPTY_SET)) {
|
||||||
Set<Integer> authOrdinals = userAuths.get(user);
|
|
||||||
if (authOrdinals != null) {
|
|
||||||
auths = new ArrayList<String>(authOrdinals.size());
|
auths = new ArrayList<String>(authOrdinals.size());
|
||||||
for (Integer authOrdinal : authOrdinals) {
|
for (Integer authOrdinal : authOrdinals) {
|
||||||
auths.add(ordinalVsLabels.get(authOrdinal));
|
auths.add(ordinalVsLabels.get(authOrdinal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
return auths;
|
||||||
this.lock.readLock().unlock();
|
}
|
||||||
|
|
||||||
|
public List<String> getGroupAuths(String[] groups) {
|
||||||
|
List<String> auths = EMPTY_LIST;
|
||||||
|
Set<Integer> authOrdinals = getGroupAuthsAsOrdinals(groups);
|
||||||
|
if (!authOrdinals.equals(EMPTY_SET)) {
|
||||||
|
auths = new ArrayList<String>(authOrdinals.size());
|
||||||
|
for (Integer authOrdinal : authOrdinals) {
|
||||||
|
auths.add(ordinalVsLabels.get(authOrdinal));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return auths;
|
return auths;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the list of ordinals of authentications associated with the user
|
* Returns the list of ordinals of labels associated with the user
|
||||||
*
|
*
|
||||||
* @param user Not null value.
|
* @param user Not null value.
|
||||||
* @return the list of ordinals
|
* @return the list of ordinals
|
||||||
*/
|
*/
|
||||||
public Set<Integer> getAuthsAsOrdinals(String user) {
|
public Set<Integer> getUserAuthsAsOrdinals(String user) {
|
||||||
this.lock.readLock().lock();
|
this.lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
Set<Integer> auths = userAuths.get(user);
|
Set<Integer> auths = userAuths.get(user);
|
||||||
|
@ -229,6 +245,31 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of ordinals of labels associated with the groups
|
||||||
|
*
|
||||||
|
* @param groups
|
||||||
|
* @return the list of ordinals
|
||||||
|
*/
|
||||||
|
public Set<Integer> getGroupAuthsAsOrdinals(String[] groups) {
|
||||||
|
this.lock.readLock().lock();
|
||||||
|
try {
|
||||||
|
Set<Integer> authOrdinals = new HashSet<Integer>();
|
||||||
|
if (groups != null && groups.length > 0) {
|
||||||
|
Set<Integer> groupAuthOrdinals = null;
|
||||||
|
for (String group : groups) {
|
||||||
|
groupAuthOrdinals = groupAuths.get(group);
|
||||||
|
if (groupAuthOrdinals != null && !groupAuthOrdinals.isEmpty()) {
|
||||||
|
authOrdinals.addAll(groupAuthOrdinals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (authOrdinals.isEmpty()) ? EMPTY_SET : authOrdinals;
|
||||||
|
} finally {
|
||||||
|
this.lock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void writeToZookeeper(byte[] data, boolean labelsOrUserAuths) {
|
public void writeToZookeeper(byte[] data, boolean labelsOrUserAuths) {
|
||||||
this.zkVisibilityWatcher.writeToZookeeper(data, labelsOrUserAuths);
|
this.zkVisibilityWatcher.writeToZookeeper(data, labelsOrUserAuths);
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.Visibil
|
||||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||||
import org.apache.hadoop.hbase.security.AccessDeniedException;
|
import org.apache.hadoop.hbase.security.AccessDeniedException;
|
||||||
import org.apache.hadoop.hbase.security.User;
|
import org.apache.hadoop.hbase.security.User;
|
||||||
|
import org.apache.hadoop.hbase.security.access.AccessControlLists;
|
||||||
import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
|
import org.apache.hadoop.hbase.security.visibility.expression.ExpressionNode;
|
||||||
import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
|
import org.apache.hadoop.hbase.security.visibility.expression.LeafExpressionNode;
|
||||||
import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
|
import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
|
||||||
|
@ -60,6 +61,7 @@ import org.apache.hadoop.hbase.security.visibility.expression.Operator;
|
||||||
import org.apache.hadoop.hbase.util.ByteRange;
|
import org.apache.hadoop.hbase.util.ByteRange;
|
||||||
import org.apache.hadoop.hbase.util.ByteStringer;
|
import org.apache.hadoop.hbase.util.ByteStringer;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.apache.hadoop.hbase.util.Pair;
|
||||||
import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
|
import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
|
||||||
import org.apache.hadoop.util.ReflectionUtils;
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
|
|
||||||
|
@ -100,6 +102,38 @@ public class VisibilityUtils {
|
||||||
return ProtobufUtil.prependPBMagic(visReqBuilder.build().toByteArray());
|
return ProtobufUtil.prependPBMagic(visReqBuilder.build().toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the super users and groups defined in the configuration.
|
||||||
|
* The user running the hbase server is always included.
|
||||||
|
* @param conf
|
||||||
|
* @return Pair of super user list and super group list.
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static Pair<List<String>, List<String>> getSystemAndSuperUsers(Configuration conf)
|
||||||
|
throws IOException {
|
||||||
|
ArrayList<String> superUsers = new ArrayList<String>();
|
||||||
|
ArrayList<String> superGroups = new ArrayList<String>();
|
||||||
|
User user = User.getCurrent();
|
||||||
|
if (user == null) {
|
||||||
|
throw new IOException("Unable to obtain the current user, "
|
||||||
|
+ "authorization checks for internal operations will not work correctly!");
|
||||||
|
}
|
||||||
|
if (LOG.isTraceEnabled()) {
|
||||||
|
LOG.trace("Current user name is " + user.getShortName());
|
||||||
|
}
|
||||||
|
String currentUser = user.getShortName();
|
||||||
|
String[] superUserList = conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]);
|
||||||
|
for (String name : superUserList) {
|
||||||
|
if (AccessControlLists.isGroupPrincipal(name)) {
|
||||||
|
superGroups.add(AccessControlLists.getGroupName(name));
|
||||||
|
} else {
|
||||||
|
superUsers.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
superUsers.add(currentUser);
|
||||||
|
return new Pair<List<String>, List<String>>(superUsers, superGroups);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the user auth data to be written to zookeeper.
|
* Creates the user auth data to be written to zookeeper.
|
||||||
* @param userAuths
|
* @param userAuths
|
||||||
|
|
|
@ -27,8 +27,10 @@ import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -82,6 +84,7 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
private HRegion labelsRegion;
|
private HRegion labelsRegion;
|
||||||
private List<ScanLabelGenerator> scanLabelGenerators;
|
private List<ScanLabelGenerator> scanLabelGenerators;
|
||||||
private List<String> superUsers;
|
private List<String> superUsers;
|
||||||
|
private List<String> superGroups;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
||||||
|
@ -115,7 +118,14 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||||
assert labelsRegion != null;
|
assert labelsRegion != null;
|
||||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||||
List<String> currentAuths = this.getAuths(user, true);
|
List<String> currentAuths;
|
||||||
|
if (AccessControlLists.isGroupPrincipal(Bytes.toString(user))) {
|
||||||
|
String group = AccessControlLists.getGroupName(Bytes.toString(user));
|
||||||
|
currentAuths = this.getGroupAuths(new String[]{group}, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentAuths = this.getUserAuths(user, true);
|
||||||
|
}
|
||||||
Delete d = new Delete(user);
|
Delete d = new Delete(user);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (byte[] authLabel : authLabels) {
|
for (byte[] authLabel : authLabels) {
|
||||||
|
@ -141,7 +151,7 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
|
public List<String> getUserAuths(byte[] user, boolean systemCall) throws IOException {
|
||||||
assert (labelsRegion != null || systemCall);
|
assert (labelsRegion != null || systemCall);
|
||||||
List<String> auths = new ArrayList<String>();
|
List<String> auths = new ArrayList<String>();
|
||||||
Get get = new Get(user);
|
Get get = new Get(user);
|
||||||
|
@ -175,6 +185,40 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
return auths;
|
return auths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getGroupAuths(String[] groups, boolean systemCall) throws IOException {
|
||||||
|
assert (labelsRegion != null || systemCall);
|
||||||
|
List<String> auths = new ArrayList<String>();
|
||||||
|
if (groups != null && groups.length > 0) {
|
||||||
|
for (String group : groups) {
|
||||||
|
Get get = new Get(Bytes.toBytes(AccessControlLists.toGroupEntry(group)));
|
||||||
|
List<Cell> cells = null;
|
||||||
|
if (labelsRegion == null) {
|
||||||
|
Table table = null;
|
||||||
|
try {
|
||||||
|
table = new HTable(conf, VisibilityConstants.LABELS_TABLE_NAME);
|
||||||
|
Result result = table.get(get);
|
||||||
|
cells = result.listCells();
|
||||||
|
} finally {
|
||||||
|
if (table != null) {
|
||||||
|
table.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cells = this.labelsRegion.get(get, false);
|
||||||
|
}
|
||||||
|
if (cells != null) {
|
||||||
|
for (Cell cell : cells) {
|
||||||
|
String auth = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
|
||||||
|
cell.getQualifierLength());
|
||||||
|
auths.add(auth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return auths;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> listLabels(String regex) throws IOException {
|
public List<String> listLabels(String regex) throws IOException {
|
||||||
// return an empty list for this implementation.
|
// return an empty list for this implementation.
|
||||||
|
@ -282,7 +326,7 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isReadFromSystemAuthUser() throws IOException {
|
protected boolean isReadFromSystemAuthUser() throws IOException {
|
||||||
byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
|
User user = VisibilityUtils.getActiveUser();
|
||||||
return havingSystemAuth(user);
|
return havingSystemAuth(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,13 +391,15 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
@Override
|
@Override
|
||||||
public void init(RegionCoprocessorEnvironment e) throws IOException {
|
public void init(RegionCoprocessorEnvironment e) throws IOException {
|
||||||
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
||||||
this.superUsers = getSystemAndSuperUsers();
|
initSystemAndSuperUsers();
|
||||||
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
||||||
this.labelsRegion = e.getRegion();
|
this.labelsRegion = e.getRegion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> getSystemAndSuperUsers() throws IOException {
|
private void initSystemAndSuperUsers() throws IOException {
|
||||||
|
this.superUsers = new ArrayList<String>();
|
||||||
|
this.superGroups = new ArrayList<String>();
|
||||||
User user = User.getCurrent();
|
User user = User.getCurrent();
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new IOException("Unable to obtain the current user, "
|
throw new IOException("Unable to obtain the current user, "
|
||||||
|
@ -363,21 +409,42 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer
|
||||||
LOG.trace("Current user name is " + user.getShortName());
|
LOG.trace("Current user name is " + user.getShortName());
|
||||||
}
|
}
|
||||||
String currentUser = user.getShortName();
|
String currentUser = user.getShortName();
|
||||||
List<String> superUsers = Lists.asList(currentUser,
|
List<String> superUserList = Lists.asList(currentUser,
|
||||||
this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
|
this.conf.getStrings(AccessControlLists.SUPERUSER_CONF_KEY, new String[0]));
|
||||||
return superUsers;
|
if (superUserList != null) {
|
||||||
|
for (String name : superUserList) {
|
||||||
|
if (AccessControlLists.isGroupPrincipal(name)) {
|
||||||
|
this.superGroups.add(AccessControlLists.getGroupName(name));
|
||||||
|
} else {
|
||||||
|
this.superUsers.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isSystemOrSuperUser(byte[] user) throws IOException {
|
protected boolean isSystemOrSuperUser(User user) throws IOException {
|
||||||
return this.superUsers.contains(Bytes.toString(user));
|
if (this.superUsers.contains(user.getShortName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String[] groups = user.getGroupNames();
|
||||||
|
if (groups != null) {
|
||||||
|
for (String group : groups) {
|
||||||
|
if (this.superGroups.contains(group)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean havingSystemAuth(byte[] user) throws IOException {
|
public boolean havingSystemAuth(User user) throws IOException {
|
||||||
if (isSystemOrSuperUser(user)) {
|
if (isSystemOrSuperUser(user)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
List<String> auths = this.getAuths(user, true);
|
Set<String> auths = new HashSet<String>();
|
||||||
|
auths.addAll(this.getUserAuths(Bytes.toBytes(user.getShortName()), true));
|
||||||
|
auths.addAll(this.getGroupAuths(user.getGroupNames(), true));
|
||||||
return auths.contains(SYSTEM_LABEL);
|
return auths.contains(SYSTEM_LABEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,346 @@
|
||||||
|
/**
|
||||||
|
* 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.visibility;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.hbase.Cell;
|
||||||
|
import org.apache.hadoop.hbase.CellScanner;
|
||||||
|
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||||
|
import org.apache.hadoop.hbase.HConstants;
|
||||||
|
import org.apache.hadoop.hbase.TableName;
|
||||||
|
import org.apache.hadoop.hbase.client.Connection;
|
||||||
|
import org.apache.hadoop.hbase.client.ConnectionFactory;
|
||||||
|
import org.apache.hadoop.hbase.client.Put;
|
||||||
|
import org.apache.hadoop.hbase.client.Result;
|
||||||
|
import org.apache.hadoop.hbase.client.ResultScanner;
|
||||||
|
import org.apache.hadoop.hbase.client.Scan;
|
||||||
|
import org.apache.hadoop.hbase.client.Table;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.GetAuthsResponse;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
|
||||||
|
import org.apache.hadoop.hbase.security.User;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.MediumTests;
|
||||||
|
import org.apache.hadoop.hbase.testclassification.SecurityTests;
|
||||||
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.experimental.categories.Category;
|
||||||
|
import org.junit.rules.TestName;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
|
@Category({SecurityTests.class, MediumTests.class})
|
||||||
|
public class TestVisibilityLablesWithGroups {
|
||||||
|
|
||||||
|
public static final String CONFIDENTIAL = "confidential";
|
||||||
|
private static final String SECRET = "secret";
|
||||||
|
public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
|
||||||
|
private static final byte[] ROW_1 = Bytes.toBytes("row1");
|
||||||
|
private final static byte[] CF = Bytes.toBytes("f");
|
||||||
|
private final static byte[] Q1 = Bytes.toBytes("q1");
|
||||||
|
private final static byte[] Q2 = Bytes.toBytes("q2");
|
||||||
|
private final static byte[] Q3 = Bytes.toBytes("q3");
|
||||||
|
private final static byte[] value1 = Bytes.toBytes("value1");
|
||||||
|
private final static byte[] value2 = Bytes.toBytes("value2");
|
||||||
|
private final static byte[] value3 = Bytes.toBytes("value3");
|
||||||
|
public static Configuration conf;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final TestName TEST_NAME = new TestName();
|
||||||
|
public static User SUPERUSER;
|
||||||
|
public static User TESTUSER;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setupBeforeClass() throws Exception {
|
||||||
|
// setup configuration
|
||||||
|
conf = TEST_UTIL.getConfiguration();
|
||||||
|
VisibilityTestUtil.enableVisiblityLabels(conf);
|
||||||
|
// Not setting any SLG class. This means to use the default behavior.
|
||||||
|
// Use a group as the super user.
|
||||||
|
conf.set("hbase.superuser", "@supergroup");
|
||||||
|
TEST_UTIL.startMiniCluster(1);
|
||||||
|
// 'admin' has super user permission because it is part of the 'supergroup'
|
||||||
|
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||||
|
// 'test' user will inherit 'testgroup' visibility labels
|
||||||
|
TESTUSER = User.createUserForTesting(conf, "test", new String[] {"testgroup" });
|
||||||
|
|
||||||
|
// Wait for the labels table to become available
|
||||||
|
TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
|
||||||
|
|
||||||
|
// Set up for the test
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
try {
|
||||||
|
VisibilityClient.addLabels(conf, new String[] { SECRET, CONFIDENTIAL });
|
||||||
|
// set auth for @testgroup
|
||||||
|
VisibilityClient.setAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
throw new IOException(t);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGroupAuths() throws Exception {
|
||||||
|
final TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
|
||||||
|
|
||||||
|
// create the table and put data.
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
Table table = TEST_UTIL.createTable(tableName, CF);
|
||||||
|
try {
|
||||||
|
Put put = new Put(ROW_1);
|
||||||
|
put.add(CF, Q1, HConstants.LATEST_TIMESTAMP, value1);
|
||||||
|
put.setCellVisibility(new CellVisibility(SECRET));
|
||||||
|
table.put(put);
|
||||||
|
put = new Put(ROW_1);
|
||||||
|
put.add(CF, Q2, HConstants.LATEST_TIMESTAMP, value2);
|
||||||
|
put.setCellVisibility(new CellVisibility(CONFIDENTIAL));
|
||||||
|
table.put(put);
|
||||||
|
put = new Put(ROW_1);
|
||||||
|
put.add(CF, Q3, HConstants.LATEST_TIMESTAMP, value3);
|
||||||
|
table.put(put);
|
||||||
|
} finally {
|
||||||
|
table.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 'admin' user is part of 'supergroup', thus can see all the cells.
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
Connection connection = ConnectionFactory.createConnection(conf);
|
||||||
|
Table table = connection.getTable(tableName);
|
||||||
|
try {
|
||||||
|
Scan s = new Scan();
|
||||||
|
ResultScanner scanner = table.getScanner(s);
|
||||||
|
Result[] next = scanner.next(1);
|
||||||
|
|
||||||
|
// Test that super user can see all the cells.
|
||||||
|
assertTrue(next.length == 1);
|
||||||
|
CellScanner cellScanner = next[0].cellScanner();
|
||||||
|
cellScanner.advance();
|
||||||
|
Cell current = cellScanner.current();
|
||||||
|
assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
|
||||||
|
current.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current.getQualifier(), Q1));
|
||||||
|
assertTrue(Bytes.equals(current.getValue(), value1));
|
||||||
|
cellScanner.advance();
|
||||||
|
current = cellScanner.current();
|
||||||
|
assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
|
||||||
|
current.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current.getQualifier(), Q2));
|
||||||
|
assertTrue(Bytes.equals(current.getValue(), value2));
|
||||||
|
cellScanner.advance();
|
||||||
|
current = cellScanner.current();
|
||||||
|
assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
|
||||||
|
current.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current.getQualifier(), Q3));
|
||||||
|
assertTrue(Bytes.equals(current.getValue(), value3));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
table.close();
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get testgroup's labels.
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
GetAuthsResponse authsResponse = null;
|
||||||
|
try {
|
||||||
|
authsResponse = VisibilityClient.getAuths(conf, "@testgroup");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
fail("Should not have failed");
|
||||||
|
}
|
||||||
|
List<String> authsList = new ArrayList<String>();
|
||||||
|
for (ByteString authBS : authsResponse.getAuthList()) {
|
||||||
|
authsList.add(Bytes.toString(authBS.toByteArray()));
|
||||||
|
}
|
||||||
|
assertEquals(1, authsList.size());
|
||||||
|
assertTrue(authsList.contains(CONFIDENTIAL));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test that test user can see what 'testgroup' has been authorized to.
|
||||||
|
TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
Connection connection = ConnectionFactory.createConnection(conf);
|
||||||
|
Table table = connection.getTable(tableName);
|
||||||
|
try {
|
||||||
|
// Test scan with no auth attribute
|
||||||
|
Scan s = new Scan();
|
||||||
|
ResultScanner scanner = table.getScanner(s);
|
||||||
|
Result[] next = scanner.next(1);
|
||||||
|
|
||||||
|
assertTrue(next.length == 1);
|
||||||
|
CellScanner cellScanner = next[0].cellScanner();
|
||||||
|
cellScanner.advance();
|
||||||
|
Cell current = cellScanner.current();
|
||||||
|
// test user can see value2 (CONFIDENTIAL) and value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
|
||||||
|
current.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current.getQualifier(), Q2));
|
||||||
|
assertTrue(Bytes.equals(current.getValue(), value2));
|
||||||
|
cellScanner.advance();
|
||||||
|
current = cellScanner.current();
|
||||||
|
// test user can see value2 (CONFIDENTIAL) and value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(),
|
||||||
|
current.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current.getQualifier(), Q3));
|
||||||
|
assertTrue(Bytes.equals(current.getValue(), value3));
|
||||||
|
|
||||||
|
// Test scan with correct auth attribute for test user
|
||||||
|
Scan s1 = new Scan();
|
||||||
|
// test user is entitled to 'CONFIDENTIAL'.
|
||||||
|
// If we set both labels in the scan, 'SECRET' will be dropped by the SLGs.
|
||||||
|
s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
|
||||||
|
ResultScanner scanner1 = table.getScanner(s1);
|
||||||
|
Result[] next1 = scanner1.next(1);
|
||||||
|
|
||||||
|
assertTrue(next1.length == 1);
|
||||||
|
CellScanner cellScanner1 = next1[0].cellScanner();
|
||||||
|
cellScanner1.advance();
|
||||||
|
Cell current1 = cellScanner1.current();
|
||||||
|
// test user can see value2 (CONFIDENTIAL) and value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
|
||||||
|
current1.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current1.getQualifier(), Q2));
|
||||||
|
assertTrue(Bytes.equals(current1.getValue(), value2));
|
||||||
|
cellScanner1.advance();
|
||||||
|
current1 = cellScanner1.current();
|
||||||
|
// test user can see value2 (CONFIDENTIAL) and value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
|
||||||
|
current1.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current1.getQualifier(), Q3));
|
||||||
|
assertTrue(Bytes.equals(current1.getValue(), value3));
|
||||||
|
|
||||||
|
// Test scan with incorrect auth attribute for test user
|
||||||
|
Scan s2 = new Scan();
|
||||||
|
// test user is entitled to 'CONFIDENTIAL'.
|
||||||
|
// If we set 'SECRET', it will be dropped by the SLGs.
|
||||||
|
s2.setAuthorizations(new Authorizations(new String[] { SECRET }));
|
||||||
|
ResultScanner scanner2 = table.getScanner(s2);
|
||||||
|
Result next2 = scanner2.next();
|
||||||
|
CellScanner cellScanner2 = next2.cellScanner();
|
||||||
|
cellScanner2.advance();
|
||||||
|
Cell current2 = cellScanner2.current();
|
||||||
|
// This scan will only see value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current2.getRowArray(), current2.getRowOffset(),
|
||||||
|
current2.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current2.getQualifier(), Q3));
|
||||||
|
assertTrue(Bytes.equals(current2.getValue(), value3));
|
||||||
|
|
||||||
|
assertFalse(cellScanner2.advance());
|
||||||
|
} finally {
|
||||||
|
table.close();
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear 'testgroup' of CONFIDENTIAL label.
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
VisibilityLabelsResponse response = null;
|
||||||
|
try {
|
||||||
|
response = VisibilityClient.clearAuths(conf, new String[] { CONFIDENTIAL }, "@testgroup");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
fail("Should not have failed");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get testgroup's labels. No label is returned.
|
||||||
|
SUPERUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
GetAuthsResponse authsResponse = null;
|
||||||
|
try {
|
||||||
|
authsResponse = VisibilityClient.getAuths(conf, "@testgroup");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
fail("Should not have failed");
|
||||||
|
}
|
||||||
|
List<String> authsList = new ArrayList<String>();
|
||||||
|
for (ByteString authBS : authsResponse.getAuthList()) {
|
||||||
|
authsList.add(Bytes.toString(authBS.toByteArray()));
|
||||||
|
}
|
||||||
|
assertEquals(0, authsList.size());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test that test user cannot see the cells with the labels anymore.
|
||||||
|
TESTUSER.runAs(new PrivilegedExceptionAction<Void>() {
|
||||||
|
public Void run() throws Exception {
|
||||||
|
Connection connection = ConnectionFactory.createConnection(conf);
|
||||||
|
Table table = connection.getTable(tableName);
|
||||||
|
try {
|
||||||
|
Scan s1 = new Scan();
|
||||||
|
// test user is not entitled to 'CONFIDENTIAL' anymore since we dropped
|
||||||
|
// testgroup's label. test user has no auth labels now.
|
||||||
|
// scan's labels will be dropped on the server side.
|
||||||
|
s1.setAuthorizations(new Authorizations(new String[] { SECRET, CONFIDENTIAL }));
|
||||||
|
ResultScanner scanner1 = table.getScanner(s1);
|
||||||
|
Result[] next1 = scanner1.next(1);
|
||||||
|
|
||||||
|
assertTrue(next1.length == 1);
|
||||||
|
CellScanner cellScanner1 = next1[0].cellScanner();
|
||||||
|
cellScanner1.advance();
|
||||||
|
Cell current1 = cellScanner1.current();
|
||||||
|
// test user can only see value3 (no label)
|
||||||
|
assertTrue(Bytes.equals(current1.getRowArray(), current1.getRowOffset(),
|
||||||
|
current1.getRowLength(), ROW_1, 0, ROW_1.length));
|
||||||
|
assertTrue(Bytes.equals(current1.getQualifier(), Q3));
|
||||||
|
assertTrue(Bytes.equals(current1.getValue(), value3));
|
||||||
|
|
||||||
|
assertFalse(cellScanner1.advance());
|
||||||
|
} finally {
|
||||||
|
table.close();
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownAfterClass() throws Exception {
|
||||||
|
TEST_UTIL.shutdownMiniCluster();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue