HBASE-11553 Abstract visibility label related services into an interface.
This commit is contained in:
parent
dd3c9da27a
commit
062f6a6aac
|
@ -44,10 +44,10 @@ public final class VisibilityConstants {
|
|||
* Visibility serialization version format. It indicates the visibility labels
|
||||
* are sorted based on ordinal
|
||||
**/
|
||||
public static final byte VISIBILITY_SERIALIZATION_VERSION = 1;
|
||||
public static final byte SORTED_ORDINAL_SERIALIZATION_FORMAT = 1;
|
||||
/** Byte representation of the visibility_serialization_version **/
|
||||
public static final byte[] SORTED_ORDINAL_SERIALIZATION_FORMAT =
|
||||
new byte[] { VISIBILITY_SERIALIZATION_VERSION };
|
||||
public static final byte[] SORTED_ORDINAL_SERIALIZATION_FORMAT_TAG_VAL =
|
||||
new byte[] { SORTED_ORDINAL_SERIALIZATION_FORMAT };
|
||||
|
||||
public static final String CHECK_AUTHS_FOR_MUTATION =
|
||||
"hbase.security.visibility.mutations.checkauths";
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
package org.apache.hadoop.hbase.zookeeper;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for internal listeners of ZooKeeper events.
|
||||
|
@ -78,4 +76,11 @@ public abstract class ZooKeeperListener {
|
|||
public void nodeChildrenChanged(String path) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The watcher associated with this listener
|
||||
*/
|
||||
public ZooKeeperWatcher getWatcher() {
|
||||
return this.watcher;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,5 +27,5 @@ public final class TagType {
|
|||
public static final byte ACL_TAG_TYPE = (byte) 1;
|
||||
public static final byte VISIBILITY_TAG_TYPE = (byte) 2;
|
||||
public static final byte LOG_REPLAY_TAG_TYPE = (byte) 3;
|
||||
public static final byte VISIBILITY_EXP_SERIALIZATION_TAG_TYPE = (byte)4;
|
||||
public static final byte VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE = (byte)4;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.Map;
|
|||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.KeyValue;
|
||||
import org.apache.hadoop.hbase.TagType;
|
||||
import org.apache.hadoop.hbase.KeyValue.Type;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
|
@ -85,11 +86,11 @@ public class LabelExpander {
|
|||
// We will be adding this tag before the visibility tags and the presence of
|
||||
// this
|
||||
// tag indicates we are supporting deletes with cell visibility
|
||||
tags.add(VisibilityUtils.VIS_SERIALIZATION_TAG);
|
||||
tags.add(VisibilityUtils.SORTED_ORDINAL_SERIALIZATION_FORMAT_TAG);
|
||||
if (node.isSingleNode()) {
|
||||
getLabelOrdinals(node, labelOrdinals);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
tags.add(new Tag(TagType.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
} else {
|
||||
NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
|
||||
|
@ -97,14 +98,14 @@ public class LabelExpander {
|
|||
for (ExpressionNode child : nlNode.getChildExps()) {
|
||||
getLabelOrdinals(child, labelOrdinals);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
tags.add(new Tag(TagType.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
labelOrdinals.clear();
|
||||
}
|
||||
} else {
|
||||
getLabelOrdinals(nlNode, labelOrdinals);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VisibilityUtils.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
tags.add(new Tag(TagType.VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,11 @@ public class OperationStatus {
|
|||
this.exceptionMsg = exceptionMsg;
|
||||
}
|
||||
|
||||
|
||||
public OperationStatus(OperationStatusCode code, Exception e) {
|
||||
this.code = code;
|
||||
this.exceptionMsg = (e == null) ? "" : e.getClass().getName() + ": " + e.getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OperationStatusCode
|
||||
*/
|
||||
|
@ -70,5 +74,4 @@ public class OperationStatus {
|
|||
public String getExceptionMsg() {
|
||||
return exceptionMsg;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,10 +38,10 @@ public class DefaultScanLabelGenerator implements ScanLabelGenerator {
|
|||
|
||||
private Configuration conf;
|
||||
|
||||
private VisibilityLabelsManager labelsManager;
|
||||
private VisibilityLabelsCache labelsCache;
|
||||
|
||||
public DefaultScanLabelGenerator() {
|
||||
this.labelsManager = VisibilityLabelsManager.get();
|
||||
this.labelsCache = VisibilityLabelsCache.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +59,7 @@ public class DefaultScanLabelGenerator implements ScanLabelGenerator {
|
|||
if (authorizations != null) {
|
||||
List<String> labels = authorizations.getLabels();
|
||||
String userName = user.getShortName();
|
||||
List<String> auths = this.labelsManager.getAuths(userName);
|
||||
List<String> auths = this.labelsCache.getAuths(userName);
|
||||
return dropLabelsNotInUserAuths(labels, auths, userName);
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -0,0 +1,698 @@
|
|||
/**
|
||||
* 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.TagType.VISIBILITY_TAG_TYPE;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABEL_QUALIFIER;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.client.Delete;
|
||||
import org.apache.hadoop.hbase.client.Mutation;
|
||||
import org.apache.hadoop.hbase.client.Put;
|
||||
import org.apache.hadoop.hbase.client.Scan;
|
||||
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||
import org.apache.hadoop.hbase.filter.Filter;
|
||||
import org.apache.hadoop.hbase.io.util.StreamUtils;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.regionserver.OperationStatus;
|
||||
import org.apache.hadoop.hbase.regionserver.RegionScanner;
|
||||
import org.apache.hadoop.hbase.security.AccessDeniedException;
|
||||
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.LeafExpressionNode;
|
||||
import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
|
||||
import org.apache.hadoop.hbase.security.visibility.expression.Operator;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.Pair;
|
||||
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
@InterfaceAudience.Private
|
||||
public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(DefaultVisibilityLabelServiceImpl.class);
|
||||
|
||||
// "system" label is having an ordinal value 1.
|
||||
private static final int SYSTEM_LABEL_ORDINAL = 1;
|
||||
private static final Tag[] LABELS_TABLE_TAGS = new Tag[1];
|
||||
private static final byte[] DUMMY_VALUE = new byte[0];
|
||||
|
||||
private volatile int ordinalCounter = -1;
|
||||
private final ExpressionParser expressionParser = new ExpressionParser();
|
||||
private final ExpressionExpander expressionExpander = new ExpressionExpander();
|
||||
private Configuration conf;
|
||||
private HRegion labelsRegion;
|
||||
private VisibilityLabelsCache labelsCache;
|
||||
private List<ScanLabelGenerator> scanLabelGenerators;
|
||||
|
||||
static {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
try {
|
||||
StreamUtils.writeRawVInt32(dos, SYSTEM_LABEL_ORDINAL);
|
||||
} catch (IOException e) {
|
||||
// We write to a byte array. No Exception can happen.
|
||||
}
|
||||
LABELS_TABLE_TAGS[0] = new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
|
||||
}
|
||||
|
||||
public DefaultVisibilityLabelServiceImpl() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConf(Configuration conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConf() {
|
||||
return this.conf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RegionCoprocessorEnvironment e) throws IOException {
|
||||
ZooKeeperWatcher zk = e.getRegionServerServices().getZooKeeper();
|
||||
try {
|
||||
labelsCache = VisibilityLabelsCache.createAndGet(zk, this.conf);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Error creating VisibilityLabelsCache", ioe);
|
||||
throw ioe;
|
||||
}
|
||||
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
||||
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
||||
this.labelsRegion = e.getRegion();
|
||||
Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
|
||||
extractLabelsAndAuths(getExistingLabelsWithAuths());
|
||||
Map<String, Integer> labels = labelsAndUserAuths.getFirst();
|
||||
Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
|
||||
// Add the "system" label if it is not added into the system yet
|
||||
addSystemLabel(this.labelsRegion, labels, userAuths);
|
||||
int ordinal = SYSTEM_LABEL_ORDINAL; // Ordinal 1 is reserved for "system" label.
|
||||
for (Integer i : labels.values()) {
|
||||
if (i > ordinal) {
|
||||
ordinal = i;
|
||||
}
|
||||
}
|
||||
this.ordinalCounter = ordinal + 1;
|
||||
if (labels.size() > 0) {
|
||||
// If there is no data need not write to zk
|
||||
byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(labels);
|
||||
this.labelsCache.writeToZookeeper(serialized, true);
|
||||
this.labelsCache.refreshLabelsCache(serialized);
|
||||
}
|
||||
if (userAuths.size() > 0) {
|
||||
byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
|
||||
this.labelsCache.writeToZookeeper(serialized, false);
|
||||
this.labelsCache.refreshUserAuthsCache(serialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<List<Cell>> getExistingLabelsWithAuths() throws IOException {
|
||||
Scan scan = new Scan();
|
||||
RegionScanner scanner = labelsRegion.getScanner(scan);
|
||||
List<List<Cell>> existingLabels = new ArrayList<List<Cell>>();
|
||||
try {
|
||||
while (true) {
|
||||
List<Cell> cells = new ArrayList<Cell>();
|
||||
scanner.next(cells);
|
||||
if (cells.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
existingLabels.add(cells);
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
return existingLabels;
|
||||
}
|
||||
|
||||
protected Pair<Map<String, Integer>, Map<String, List<Integer>>> extractLabelsAndAuths(
|
||||
List<List<Cell>> labelDetails) {
|
||||
Map<String, Integer> labels = new HashMap<String, Integer>();
|
||||
Map<String, List<Integer>> userAuths = new HashMap<String, List<Integer>>();
|
||||
for (List<Cell> cells : labelDetails) {
|
||||
for (Cell cell : cells) {
|
||||
if (Bytes.equals(cell.getQualifierArray(), cell.getQualifierOffset(),
|
||||
cell.getQualifierLength(), LABEL_QUALIFIER, 0, LABEL_QUALIFIER.length)) {
|
||||
labels.put(
|
||||
Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()),
|
||||
Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
|
||||
} else {
|
||||
// These are user cells who has authorization for this label
|
||||
String user = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(),
|
||||
cell.getQualifierLength());
|
||||
List<Integer> auths = userAuths.get(user);
|
||||
if (auths == null) {
|
||||
auths = new ArrayList<Integer>();
|
||||
userAuths.put(user, auths);
|
||||
}
|
||||
auths.add(Bytes.toInt(cell.getRowArray(), cell.getRowOffset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Pair<Map<String, Integer>, Map<String, List<Integer>>>(labels, userAuths);
|
||||
}
|
||||
|
||||
protected void addSystemLabel(HRegion region, Map<String, Integer> labels,
|
||||
Map<String, List<Integer>> userAuths) throws IOException {
|
||||
if (!labels.containsKey(SYSTEM_LABEL)) {
|
||||
Put p = new Put(Bytes.toBytes(SYSTEM_LABEL_ORDINAL));
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, Bytes.toBytes(SYSTEM_LABEL));
|
||||
// Set auth for "system" label for all super users.
|
||||
List<String> superUsers = getSystemAndSuperUsers();
|
||||
for (String superUser : superUsers) {
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, Bytes.toBytes(superUser), DUMMY_VALUE,
|
||||
LABELS_TABLE_TAGS);
|
||||
}
|
||||
region.put(p);
|
||||
labels.put(SYSTEM_LABEL, SYSTEM_LABEL_ORDINAL);
|
||||
for (String superUser : superUsers) {
|
||||
List<Integer> auths = userAuths.get(superUser);
|
||||
if (auths == null) {
|
||||
auths = new ArrayList<Integer>(1);
|
||||
userAuths.put(superUser, auths);
|
||||
}
|
||||
auths.add(SYSTEM_LABEL_ORDINAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
||||
assert labelsRegion != null;
|
||||
OperationStatus[] finalOpStatus = new OperationStatus[labels.size()];
|
||||
List<Mutation> puts = new ArrayList<Mutation>(labels.size());
|
||||
int i = 0;
|
||||
for (byte[] label : labels) {
|
||||
String labelStr = Bytes.toString(label);
|
||||
if (this.labelsCache.getLabelOrdinal(labelStr) > 0) {
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
|
||||
new LabelAlreadyExistsException("Label '" + labelStr + "' already exists"));
|
||||
} else {
|
||||
Put p = new Put(Bytes.toBytes(ordinalCounter));
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, LABEL_QUALIFIER, label, LABELS_TABLE_TAGS);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Adding the label " + labelStr);
|
||||
}
|
||||
puts.add(p);
|
||||
ordinalCounter++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (mutateLabelsRegion(puts, finalOpStatus)) {
|
||||
updateZk(true);
|
||||
}
|
||||
return finalOpStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||
assert labelsRegion != null;
|
||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||
List<Mutation> puts = new ArrayList<Mutation>(authLabels.size());
|
||||
int i = 0;
|
||||
for (byte[] auth : authLabels) {
|
||||
String authStr = Bytes.toString(auth);
|
||||
int labelOrdinal = this.labelsCache.getLabelOrdinal(authStr);
|
||||
if (labelOrdinal == 0) {
|
||||
// This label is not yet added. 1st this should be added to the system
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
|
||||
new InvalidLabelException("Label '" + authStr + "' doesn't exists"));
|
||||
} else {
|
||||
Put p = new Put(Bytes.toBytes(labelOrdinal));
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, user, DUMMY_VALUE, LABELS_TABLE_TAGS);
|
||||
puts.add(p);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (mutateLabelsRegion(puts, finalOpStatus)) {
|
||||
updateZk(false);
|
||||
}
|
||||
return finalOpStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||
assert labelsRegion != null;
|
||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||
List<String> currentAuths = this.getAuths(user, true);
|
||||
List<Mutation> deletes = new ArrayList<Mutation>(authLabels.size());
|
||||
int i = 0;
|
||||
for (byte[] authLabel : authLabels) {
|
||||
String authLabelStr = Bytes.toString(authLabel);
|
||||
if (currentAuths.contains(authLabelStr)) {
|
||||
int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabelStr);
|
||||
assert labelOrdinal > 0;
|
||||
Delete d = new Delete(Bytes.toBytes(labelOrdinal));
|
||||
d.deleteColumns(LABELS_TABLE_FAMILY, user);
|
||||
deletes.add(d);
|
||||
} else {
|
||||
// This label is not set for the user.
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
|
||||
new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
|
||||
+ Bytes.toString(user)));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (mutateLabelsRegion(deletes, finalOpStatus)) {
|
||||
updateZk(false);
|
||||
}
|
||||
return finalOpStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the mutations to labels region and set the results to the finalOpStatus. finalOpStatus
|
||||
* might have some entries in it where the OpStatus is FAILURE. We will leave those and set in
|
||||
* others in the order.
|
||||
* @param mutations
|
||||
* @param finalOpStatus
|
||||
* @return whether we need a ZK update or not.
|
||||
*/
|
||||
private boolean mutateLabelsRegion(List<Mutation> mutations, OperationStatus[] finalOpStatus)
|
||||
throws IOException {
|
||||
OperationStatus[] opStatus = this.labelsRegion.batchMutate(mutations
|
||||
.toArray(new Mutation[mutations.size()]));
|
||||
int i = 0;
|
||||
boolean updateZk = false;
|
||||
for (OperationStatus status : opStatus) {
|
||||
// Update the zk when atleast one of the mutation was added successfully.
|
||||
updateZk = updateZk || (status.getOperationStatusCode() == OperationStatusCode.SUCCESS);
|
||||
for (; i < finalOpStatus.length; i++) {
|
||||
if (finalOpStatus[i] == null) {
|
||||
finalOpStatus[i] = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return updateZk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
|
||||
assert (labelsRegion != null || systemCall);
|
||||
if (systemCall || labelsRegion == null) {
|
||||
return this.labelsCache.getAuths(Bytes.toString(user));
|
||||
}
|
||||
Scan s = new Scan();
|
||||
s.addColumn(LABELS_TABLE_FAMILY, user);
|
||||
Filter filter = VisibilityUtils.createVisibilityLabelFilter(this.labelsRegion,
|
||||
new Authorizations(SYSTEM_LABEL));
|
||||
s.setFilter(filter);
|
||||
List<String> auths = new ArrayList<String>();
|
||||
RegionScanner scanner = this.labelsRegion.getScanner(s);
|
||||
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();
|
||||
}
|
||||
return auths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
|
||||
boolean checkAuths) throws IOException {
|
||||
ExpressionNode node = null;
|
||||
try {
|
||||
node = this.expressionParser.parse(visExpression);
|
||||
} catch (ParseException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
node = this.expressionExpander.expand(node);
|
||||
List<Tag> tags = new ArrayList<Tag>();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
List<Integer> labelOrdinals = new ArrayList<Integer>();
|
||||
// We will be adding this tag before the visibility tags and the presence of this
|
||||
// tag indicates we are supporting deletes with cell visibility
|
||||
if (withSerializationFormat) {
|
||||
tags.add(VisibilityUtils.SORTED_ORDINAL_SERIALIZATION_FORMAT_TAG);
|
||||
}
|
||||
Set<Integer> auths = null;
|
||||
if (checkAuths) {
|
||||
auths = this.labelsCache.getAuthsAsOrdinals(VisibilityUtils.getActiveUser().getShortName());
|
||||
}
|
||||
if (node.isSingleNode()) {
|
||||
getLabelOrdinals(node, labelOrdinals, auths, checkAuths);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
} else {
|
||||
NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
|
||||
if (nlNode.getOperator() == Operator.OR) {
|
||||
for (ExpressionNode child : nlNode.getChildExps()) {
|
||||
getLabelOrdinals(child, labelOrdinals, auths, checkAuths);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
labelOrdinals.clear();
|
||||
}
|
||||
} else {
|
||||
getLabelOrdinals(nlNode, labelOrdinals, auths, checkAuths);
|
||||
writeLabelOrdinalsToStream(labelOrdinals, dos);
|
||||
tags.add(new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray()));
|
||||
baos.reset();
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
protected void getLabelOrdinals(ExpressionNode node, List<Integer> labelOrdinals,
|
||||
Set<Integer> auths, boolean checkAuths) throws IOException, InvalidLabelException {
|
||||
if (node.isSingleNode()) {
|
||||
String identifier = null;
|
||||
int labelOrdinal = 0;
|
||||
if (node instanceof LeafExpressionNode) {
|
||||
identifier = ((LeafExpressionNode) node).getIdentifier();
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The identifier is " + identifier);
|
||||
}
|
||||
labelOrdinal = this.labelsCache.getLabelOrdinal(identifier);
|
||||
checkAuths(auths, labelOrdinal, identifier, checkAuths);
|
||||
} else {
|
||||
// This is a NOT node.
|
||||
LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
|
||||
.getChildExps().get(0);
|
||||
identifier = lNode.getIdentifier();
|
||||
labelOrdinal = this.labelsCache.getLabelOrdinal(identifier);
|
||||
checkAuths(auths, labelOrdinal, identifier, checkAuths);
|
||||
labelOrdinal = -1 * labelOrdinal; // Store NOT node as -ve ordinal.
|
||||
}
|
||||
if (labelOrdinal == 0) {
|
||||
throw new InvalidLabelException("Invalid visibility label " + identifier);
|
||||
}
|
||||
labelOrdinals.add(labelOrdinal);
|
||||
} else {
|
||||
List<ExpressionNode> childExps = ((NonLeafExpressionNode) node).getChildExps();
|
||||
for (ExpressionNode child : childExps) {
|
||||
getLabelOrdinals(child, labelOrdinals, auths, checkAuths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAuths(Set<Integer> auths, int labelOrdinal, String identifier,
|
||||
boolean checkAuths) throws IOException {
|
||||
if (checkAuths) {
|
||||
if (auths == null || (!auths.contains(labelOrdinal))) {
|
||||
throw new AccessDeniedException("Visibility label " + identifier
|
||||
+ " not authorized for the user " + VisibilityUtils.getActiveUser().getShortName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This will sort the passed labels in ascending oder and then will write one after the other
|
||||
* to the passed stream.
|
||||
* @param labelOrdinals Unsorted label ordinals
|
||||
* @param dos Stream where to write the labels.
|
||||
* @throws IOException When IOE during writes to Stream.
|
||||
*/
|
||||
protected void writeLabelOrdinalsToStream(List<Integer> labelOrdinals, DataOutputStream dos)
|
||||
throws IOException {
|
||||
Collections.sort(labelOrdinals);
|
||||
for (Integer labelOrdinal : labelOrdinals) {
|
||||
StreamUtils.writeRawVInt32(dos, labelOrdinal);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateZk(boolean labelAddition) throws IOException {
|
||||
// We will add to zookeeper here.
|
||||
// TODO we should add the delta only to zk. Else this will be a very heavy op and when there are
|
||||
// so many labels and auth in the system, we will end up adding lots of data to zk. Most
|
||||
// possibly we will exceed zk node data limit!
|
||||
Pair<Map<String, Integer>, Map<String, List<Integer>>> labelsAndUserAuths =
|
||||
extractLabelsAndAuths(getExistingLabelsWithAuths());
|
||||
Map<String, Integer> existingLabels = labelsAndUserAuths.getFirst();
|
||||
Map<String, List<Integer>> userAuths = labelsAndUserAuths.getSecond();
|
||||
if (labelAddition) {
|
||||
byte[] serialized = VisibilityUtils.getDataToWriteToZooKeeper(existingLabels);
|
||||
this.labelsCache.writeToZookeeper(serialized, true);
|
||||
} else {
|
||||
byte[] serialized = VisibilityUtils.getUserAuthsDataToWriteToZooKeeper(userAuths);
|
||||
this.labelsCache.writeToZookeeper(serialized, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
|
||||
throws IOException {
|
||||
// If a super user issues a get/scan, he should be able to scan the cells
|
||||
// irrespective of the Visibility labels
|
||||
if (isReadFromSuperUser()) {
|
||||
return new VisibilityExpEvaluator() {
|
||||
@Override
|
||||
public boolean evaluate(Cell cell) throws IOException {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
List<String> authLabels = null;
|
||||
for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
|
||||
try {
|
||||
// null authorizations to be handled inside SLG impl.
|
||||
authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
|
||||
authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
|
||||
authorizations = new Authorizations(authLabels);
|
||||
} catch (Throwable t) {
|
||||
LOG.error(t);
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
int labelsCount = this.labelsCache.getLabelsCount();
|
||||
final BitSet bs = new BitSet(labelsCount + 1); // ordinal is index 1 based
|
||||
if (authLabels != null) {
|
||||
for (String authLabel : authLabels) {
|
||||
int labelOrdinal = this.labelsCache.getLabelOrdinal(authLabel);
|
||||
if (labelOrdinal != 0) {
|
||||
bs.set(labelOrdinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new VisibilityExpEvaluator() {
|
||||
@Override
|
||||
public boolean evaluate(Cell cell) throws IOException {
|
||||
boolean visibilityTagPresent = false;
|
||||
// Save an object allocation where we can
|
||||
if (cell.getTagsLength() > 0) {
|
||||
Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsItr.hasNext()) {
|
||||
boolean includeKV = true;
|
||||
Tag tag = tagsItr.next();
|
||||
if (tag.getType() == VISIBILITY_TAG_TYPE) {
|
||||
visibilityTagPresent = true;
|
||||
int offset = tag.getTagOffset();
|
||||
int endOffset = offset + tag.getTagLength();
|
||||
while (offset < endOffset) {
|
||||
Pair<Integer, Integer> result = StreamUtils
|
||||
.readRawVarint32(tag.getBuffer(), offset);
|
||||
int currLabelOrdinal = result.getFirst();
|
||||
if (currLabelOrdinal < 0) {
|
||||
// check for the absence of this label in the Scan Auth labels
|
||||
// ie. to check BitSet corresponding bit is 0
|
||||
int temp = -currLabelOrdinal;
|
||||
if (bs.get(temp)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!bs.get(currLabelOrdinal)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset += result.getSecond();
|
||||
}
|
||||
if (includeKV) {
|
||||
// We got one visibility expression getting evaluated to true. Good to include this
|
||||
// KV in the result then.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !(visibilityTagPresent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected boolean isReadFromSuperUser() throws IOException {
|
||||
byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
|
||||
return havingSystemAuth(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havingSystemAuth(byte[] user) throws IOException {
|
||||
List<String> auths = this.getAuths(user, true);
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("The auths for user " + Bytes.toString(user) + " are " + auths);
|
||||
}
|
||||
return auths.contains(SYSTEM_LABEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchVisibility(List<Tag> putVisTags, Byte putTagsFormat, List<Tag> deleteVisTags,
|
||||
Byte deleteTagsFormat) throws IOException {
|
||||
if ((deleteTagsFormat != null && deleteTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)
|
||||
&& (putTagsFormat == null || putTagsFormat == SORTED_ORDINAL_SERIALIZATION_FORMAT)) {
|
||||
if (putVisTags.size() == 0) {
|
||||
// Early out if there are no tags in the cell
|
||||
return false;
|
||||
}
|
||||
if (putTagsFormat == null) {
|
||||
return matchUnSortedVisibilityTags(putVisTags, deleteVisTags);
|
||||
} else {
|
||||
return matchOrdinalSortedVisibilityTags(putVisTags, deleteVisTags);
|
||||
}
|
||||
}
|
||||
throw new IOException("Unexpected tag format passed for comparison, deleteTagsFormat : "
|
||||
+ deleteTagsFormat + ", putTagsFormat : " + putTagsFormat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param putVisTags Visibility tags in Put Mutation
|
||||
* @param deleteVisTags Visibility tags in Delete Mutation
|
||||
* @return true when all the visibility tags in Put matches with visibility tags in Delete.
|
||||
* This is used when, at least one set of tags are not sorted based on the label ordinal.
|
||||
*/
|
||||
private static boolean matchUnSortedVisibilityTags(List<Tag> putVisTags,
|
||||
List<Tag> deleteVisTags) throws IOException {
|
||||
return compareTagsOrdinals(sortTagsBasedOnOrdinal(putVisTags),
|
||||
sortTagsBasedOnOrdinal(deleteVisTags));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param putVisTags Visibility tags in Put Mutation
|
||||
* @param deleteVisTags Visibility tags in Delete Mutation
|
||||
* @return true when all the visibility tags in Put matches with visibility tags in Delete.
|
||||
* This is used when both the set of tags are sorted based on the label ordinal.
|
||||
*/
|
||||
private static boolean matchOrdinalSortedVisibilityTags(List<Tag> putVisTags,
|
||||
List<Tag> deleteVisTags) {
|
||||
boolean matchFound = false;
|
||||
// If the size does not match. Definitely we are not comparing the equal tags.
|
||||
if ((deleteVisTags.size()) == putVisTags.size()) {
|
||||
for (Tag tag : deleteVisTags) {
|
||||
matchFound = false;
|
||||
for (Tag givenTag : putVisTags) {
|
||||
if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
|
||||
givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) break;
|
||||
}
|
||||
}
|
||||
return matchFound;
|
||||
}
|
||||
|
||||
private static List<List<Integer>> sortTagsBasedOnOrdinal(List<Tag> tags) throws IOException {
|
||||
List<List<Integer>> fullTagsList = new ArrayList<List<Integer>>();
|
||||
for (Tag tag : tags) {
|
||||
if (tag.getType() == VISIBILITY_TAG_TYPE) {
|
||||
getSortedTagOrdinals(fullTagsList, tag);
|
||||
}
|
||||
}
|
||||
return fullTagsList;
|
||||
}
|
||||
|
||||
private static void getSortedTagOrdinals(List<List<Integer>> fullTagsList, Tag tag)
|
||||
throws IOException {
|
||||
List<Integer> tagsOrdinalInSortedOrder = new ArrayList<Integer>();
|
||||
int offset = tag.getTagOffset();
|
||||
int endOffset = offset + tag.getTagLength();
|
||||
while (offset < endOffset) {
|
||||
Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
|
||||
tagsOrdinalInSortedOrder.add(result.getFirst());
|
||||
offset += result.getSecond();
|
||||
}
|
||||
Collections.sort(tagsOrdinalInSortedOrder);
|
||||
fullTagsList.add(tagsOrdinalInSortedOrder);
|
||||
}
|
||||
|
||||
/*
|
||||
* @return true when all the visibility tags in Put matches with visibility tags in Delete.
|
||||
*/
|
||||
private static boolean compareTagsOrdinals(List<List<Integer>> putVisTags,
|
||||
List<List<Integer>> deleteVisTags) {
|
||||
boolean matchFound = false;
|
||||
if (deleteVisTags.size() == putVisTags.size()) {
|
||||
for (List<Integer> deleteTagOrdinals : deleteVisTags) {
|
||||
matchFound = false;
|
||||
for (List<Integer> tagOrdinals : putVisTags) {
|
||||
if (deleteTagOrdinals.equals(tagOrdinals)) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) break;
|
||||
}
|
||||
}
|
||||
return matchFound;
|
||||
}
|
||||
}
|
|
@ -37,10 +37,10 @@ public class EnforcingScanLabelGenerator implements ScanLabelGenerator {
|
|||
private static final Log LOG = LogFactory.getLog(EnforcingScanLabelGenerator.class);
|
||||
|
||||
private Configuration conf;
|
||||
private VisibilityLabelsManager labelsManager;
|
||||
private VisibilityLabelsCache labelsCache;
|
||||
|
||||
public EnforcingScanLabelGenerator() {
|
||||
this.labelsManager = VisibilityLabelsManager.get();
|
||||
this.labelsCache = VisibilityLabelsCache.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +59,7 @@ public class EnforcingScanLabelGenerator implements ScanLabelGenerator {
|
|||
if (authorizations != null) {
|
||||
LOG.warn("Dropping authorizations requested by user " + userName + ": " + authorizations);
|
||||
}
|
||||
return this.labelsManager.getAuths(userName);
|
||||
return this.labelsCache.getAuths(userName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
*
|
||||
* 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 java.io.IOException;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
|
||||
/**
|
||||
* During the read (ie. get/Scan) the VisibilityController calls this interface for each of the
|
||||
* Cell. Depending on the evaluate() result, a cell can be either included or filtered out from the
|
||||
* read results.
|
||||
*/
|
||||
@InterfaceAudience.Public
|
||||
@InterfaceStability.Evolving
|
||||
public interface VisibilityExpEvaluator {
|
||||
|
||||
/**
|
||||
* Evaluates whether the passed cell passes Scan/Get Authorization.
|
||||
* @param cell Cell under evaluation
|
||||
* @return true if this cell can be included in the Result. Else false.
|
||||
*/
|
||||
boolean evaluate(Cell cell) throws IOException;
|
||||
}
|
|
@ -18,19 +18,13 @@
|
|||
package org.apache.hadoop.hbase.security.visibility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.filter.FilterBase;
|
||||
import org.apache.hadoop.hbase.io.util.StreamUtils;
|
||||
import org.apache.hadoop.hbase.util.ByteRange;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.Pair;
|
||||
import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
|
||||
|
||||
/**
|
||||
|
@ -40,15 +34,16 @@ import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
|
|||
@InterfaceAudience.Private
|
||||
class VisibilityLabelFilter extends FilterBase {
|
||||
|
||||
private final BitSet authLabels;
|
||||
private final VisibilityExpEvaluator expEvaluator;
|
||||
private final Map<ByteRange, Integer> cfVsMaxVersions;
|
||||
private final ByteRange curFamily;
|
||||
private final ByteRange curQualifier;
|
||||
private int curFamilyMaxVersions;
|
||||
private int curQualMetVersions;
|
||||
|
||||
public VisibilityLabelFilter(BitSet authLabels, Map<ByteRange, Integer> cfVsMaxVersions) {
|
||||
this.authLabels = authLabels;
|
||||
public VisibilityLabelFilter(VisibilityExpEvaluator expEvaluator,
|
||||
Map<ByteRange, Integer> cfVsMaxVersions) {
|
||||
this.expEvaluator = expEvaluator;
|
||||
this.cfVsMaxVersions = cfVsMaxVersions;
|
||||
this.curFamily = new SimpleMutableByteRange();
|
||||
this.curQualifier = new SimpleMutableByteRange();
|
||||
|
@ -80,46 +75,7 @@ class VisibilityLabelFilter extends FilterBase {
|
|||
return ReturnCode.SKIP;
|
||||
}
|
||||
|
||||
boolean visibilityTagPresent = false;
|
||||
// Save an object allocation where we can
|
||||
if (cell.getTagsLength() > 0) {
|
||||
Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsItr.hasNext()) {
|
||||
boolean includeKV = true;
|
||||
Tag tag = tagsItr.next();
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
|
||||
visibilityTagPresent = true;
|
||||
int offset = tag.getTagOffset();
|
||||
int endOffset = offset + tag.getTagLength();
|
||||
while (offset < endOffset) {
|
||||
Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
|
||||
int currLabelOrdinal = result.getFirst();
|
||||
if (currLabelOrdinal < 0) {
|
||||
// check for the absence of this label in the Scan Auth labels
|
||||
// ie. to check BitSet corresponding bit is 0
|
||||
int temp = -currLabelOrdinal;
|
||||
if (this.authLabels.get(temp)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!this.authLabels.get(currLabelOrdinal)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset += result.getSecond();
|
||||
}
|
||||
if (includeKV) {
|
||||
// We got one visibility expression getting evaluated to true. Good to include this KV in
|
||||
// the result then.
|
||||
return ReturnCode.INCLUDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return visibilityTagPresent ? ReturnCode.SKIP : ReturnCode.INCLUDE;
|
||||
return this.expEvaluator.evaluate(cell) ? ReturnCode.INCLUDE : ReturnCode.SKIP;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
* 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 java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
import org.apache.hadoop.conf.Configurable;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||
import org.apache.hadoop.hbase.regionserver.OperationStatus;
|
||||
|
||||
/**
|
||||
* The interface which deals with visibility labels and user auths admin service as well as the cell
|
||||
* visibility expression storage part and read time evaluation.
|
||||
*/
|
||||
@InterfaceAudience.Public
|
||||
@InterfaceStability.Evolving
|
||||
public interface VisibilityLabelService extends Configurable {
|
||||
|
||||
/**
|
||||
* System calls this after opening of regions. Gives a chance for the VisibilityLabelService to so
|
||||
* any initialization logic.
|
||||
* @param e
|
||||
* the region coprocessor env
|
||||
*/
|
||||
void init(RegionCoprocessorEnvironment e) throws IOException;
|
||||
|
||||
/**
|
||||
* Adds the set of labels into the system.
|
||||
* @param labels
|
||||
* Labels to add to the system.
|
||||
* @return OperationStatus for each of the label addition
|
||||
*/
|
||||
OperationStatus[] addLabels(List<byte[]> labels) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets given labels globally authorized for the user.
|
||||
* @param user
|
||||
* The authorizing user
|
||||
* @param authLabels
|
||||
* Labels which are getting authorized for the user
|
||||
* @return OperationStatus for each of the label auth addition
|
||||
*/
|
||||
OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException;
|
||||
|
||||
/**
|
||||
* Removes given labels from user's globally authorized list of labels.
|
||||
* @param user
|
||||
* The user whose authorization to be removed
|
||||
* @param authLabels
|
||||
* Labels which are getting removed from authorization set
|
||||
* @return OperationStatus for each of the label auth removal
|
||||
*/
|
||||
OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException;
|
||||
|
||||
/**
|
||||
* @param user
|
||||
* Name of the user whose authorization to be retrieved
|
||||
* @param systemCall
|
||||
* Whether a system or user originated call.
|
||||
* @return Visibility labels authorized for the given user.
|
||||
*/
|
||||
List<String> getAuths(byte[] user, boolean systemCall) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates tags corresponding to given visibility expression.
|
||||
* <br>
|
||||
* Note: This will be concurrently called from multiple threads and implementation should
|
||||
* take care of thread safety.
|
||||
* @param visExpression The Expression for which corresponding Tags to be created.
|
||||
* @param withSerializationFormat specifies whether a tag, denoting the serialization version
|
||||
* of the tags, to be added in the list. When this is true make sure to add the
|
||||
* serialization format Tag also. The format tag value should be byte type.
|
||||
* @param checkAuths denotes whether to check individual labels in visExpression against user's
|
||||
* global auth label.
|
||||
* @return The list of tags corresponds to the visibility expression. These tags will be stored
|
||||
* along with the Cells.
|
||||
*/
|
||||
List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
|
||||
boolean checkAuths) throws IOException;
|
||||
|
||||
/**
|
||||
* Creates VisibilityExpEvaluator corresponding to given Authorizations. <br>
|
||||
* Note: This will be concurrently called from multiple threads and implementation should take
|
||||
* care of thread safety.
|
||||
* @param authorizations
|
||||
* Authorizations for the read request
|
||||
* @return The VisibilityExpEvaluator corresponding to the given set of authorization labels.
|
||||
*/
|
||||
VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* System checks for user auth during admin operations. (ie. Label add, set/clear auth). The
|
||||
* operation is allowed only for users having system auth. Also during read, if the requesting
|
||||
* user has system auth, he can view all the data irrespective of its labels.
|
||||
* @param user
|
||||
* User for whom system auth check to be done.
|
||||
* @return true if the given user is having system/super auth
|
||||
*/
|
||||
boolean havingSystemAuth(byte[] user) throws IOException;
|
||||
|
||||
/**
|
||||
* System uses this for deciding whether a Cell can be deleted by matching visibility expression
|
||||
* in Delete mutation and the cell in consideration. Also system passes the serialization format
|
||||
* of visibility tags in Put and Delete.<br>
|
||||
* Note: This will be concurrently called from multiple threads and implementation should take
|
||||
* care of thread safety.
|
||||
* @param putVisTags
|
||||
* The visibility tags present in the Put mutation
|
||||
* @param putVisTagFormat
|
||||
* The serialization format for the Put visibility tags. A <code>null</code> value for
|
||||
* this format means the tags are written with unsorted label ordinals
|
||||
* @param deleteVisTags
|
||||
* - The visibility tags in the delete mutation (the specified Cell Visibility)
|
||||
* @param deleteVisTagFormat
|
||||
* The serialization format for the Delete visibility tags. A <code>null</code> value for
|
||||
* this format means the tags are written with unsorted label ordinals
|
||||
* @return true if matching tags are found
|
||||
* @see VisibilityConstants#SORTED_ORDINAL_SERIALIZATION_FORMAT
|
||||
*/
|
||||
boolean matchVisibility(List<Tag> putVisTags, Byte putVisTagFormat, List<Tag> deleteVisTags,
|
||||
Byte deleteVisTagFormat) throws IOException;
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* 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 java.io.IOException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Manages singleton instance of {@link VisibilityLabelService}
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class VisibilityLabelServiceManager {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(VisibilityLabelServiceManager.class);
|
||||
|
||||
public static final String VISIBILITY_LABEL_SERVICE_CLASS =
|
||||
"hbase.regionserver.visibility.label.service.class";
|
||||
private static final VisibilityLabelServiceManager INSTANCE = new VisibilityLabelServiceManager();
|
||||
|
||||
private volatile VisibilityLabelService visibilityLabelService = null;
|
||||
private String vlsClazzName = null;
|
||||
|
||||
private VisibilityLabelServiceManager() {
|
||||
|
||||
}
|
||||
|
||||
public static VisibilityLabelServiceManager getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param conf
|
||||
* @return singleton instance of {@link VisibilityLabelService}. The FQCN of the implementation
|
||||
* class can be specified using "hbase.regionserver.visibility.label.service.class".
|
||||
* @throws IOException When VLS implementation, as specified in conf, can not be loaded.
|
||||
*/
|
||||
public VisibilityLabelService getVisibilityLabelService(Configuration conf) throws IOException {
|
||||
String vlsClassName = conf.get(VISIBILITY_LABEL_SERVICE_CLASS,
|
||||
DefaultVisibilityLabelServiceImpl.class.getCanonicalName()).trim();
|
||||
if (this.visibilityLabelService != null) {
|
||||
checkForClusterLevelSingleConf(vlsClassName);
|
||||
return this.visibilityLabelService;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (this.visibilityLabelService != null) {
|
||||
checkForClusterLevelSingleConf(vlsClassName);
|
||||
return this.visibilityLabelService;
|
||||
}
|
||||
this.vlsClazzName = vlsClassName;
|
||||
try {
|
||||
this.visibilityLabelService = (VisibilityLabelService) ReflectionUtils.newInstance(
|
||||
Class.forName(vlsClassName), conf);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
return this.visibilityLabelService;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForClusterLevelSingleConf(String vlsClassName) {
|
||||
assert this.vlsClazzName != null;
|
||||
if (!this.vlsClazzName.equals(vlsClassName)) {
|
||||
LOG.warn("Trying to use table specific value for config "
|
||||
+ "'hbase.regionserver.visibility.label.service.class' which is not supported."
|
||||
+ " Will use the cluster level VisibilityLabelService class " + this.vlsClazzName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return singleton instance of {@link VisibilityLabelService}.
|
||||
* @throws IllegalStateException if this called before initialization of singleton instance.
|
||||
*/
|
||||
public VisibilityLabelService getVisibilityLabelService() {
|
||||
// By the time this method is called, the singleton instance of visibilityLabelService should
|
||||
// have been created. And it will be created as getVisibilityLabelService(Configuration conf)
|
||||
// is called from VC#start() and that will be the 1st thing core code do with any CP.
|
||||
if (this.visibilityLabelService == null) {
|
||||
throw new IllegalStateException("VisibilityLabelService not yet instantiated");
|
||||
}
|
||||
return this.visibilityLabelService;
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.security.visibility;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -44,19 +45,24 @@ import org.apache.zookeeper.KeeperException;
|
|||
* znode for labels table
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class VisibilityLabelsManager {
|
||||
public class VisibilityLabelsCache {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(VisibilityLabelsManager.class);
|
||||
private static final List<String> EMPTY_LIST = new ArrayList<String>(0);
|
||||
private static VisibilityLabelsManager instance;
|
||||
private static final Log LOG = LogFactory.getLog(VisibilityLabelsCache.class);
|
||||
private static final int NON_EXIST_LABEL_ORDINAL = 0;
|
||||
private static final List<String> EMPTY_LIST = Collections.emptyList();
|
||||
private static final Set<Integer> EMPTY_SET = Collections.emptySet();
|
||||
private static VisibilityLabelsCache instance;
|
||||
|
||||
private ZKVisibilityLabelWatcher zkVisibilityWatcher;
|
||||
private Map<String, Integer> labels = new HashMap<String, Integer>();
|
||||
private Map<Integer, String> ordinalVsLabels = new HashMap<Integer, String>();
|
||||
private Map<String, Set<Integer>> userAuths = new HashMap<String, Set<Integer>>();
|
||||
/**
|
||||
* This covers the members labels, ordinalVsLabels and userAuths
|
||||
*/
|
||||
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
|
||||
private VisibilityLabelsManager(ZooKeeperWatcher watcher, Configuration conf) throws IOException {
|
||||
private VisibilityLabelsCache(ZooKeeperWatcher watcher, Configuration conf) throws IOException {
|
||||
zkVisibilityWatcher = new ZKVisibilityLabelWatcher(watcher, this, conf);
|
||||
try {
|
||||
zkVisibilityWatcher.start();
|
||||
|
@ -66,15 +72,39 @@ public class VisibilityLabelsManager {
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized static VisibilityLabelsManager get(ZooKeeperWatcher watcher,
|
||||
/**
|
||||
* Creates the singleton instance, if not yet present, and returns the same.
|
||||
* @param watcher
|
||||
* @param conf
|
||||
* @return Singleton instance of VisibilityLabelsCache
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized static VisibilityLabelsCache createAndGet(ZooKeeperWatcher watcher,
|
||||
Configuration conf) throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new VisibilityLabelsManager(watcher, conf);
|
||||
// VisibilityLabelService#init() for different regions (in same RS) passes same instance of
|
||||
// watcher as all get the instance from RS.
|
||||
// watcher != instance.zkVisibilityWatcher.getWatcher() - This check is needed only in UTs with
|
||||
// RS restart. It will be same JVM in which RS restarts and instance will be not null. But the
|
||||
// watcher associated with existing instance will be stale as the restarted RS will have new
|
||||
// watcher with it.
|
||||
if (instance == null || watcher != instance.zkVisibilityWatcher.getWatcher()) {
|
||||
instance = new VisibilityLabelsCache(watcher, conf);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static VisibilityLabelsManager get() {
|
||||
/**
|
||||
* @return Singleton instance of VisibilityLabelsCache
|
||||
* @throws IllegalStateException
|
||||
* when this is called before calling
|
||||
* {@link #createAndGet(ZooKeeperWatcher, Configuration)}
|
||||
*/
|
||||
public static VisibilityLabelsCache get() {
|
||||
// By the time this method is called, the singleton instance of VisibilityLabelsCache should
|
||||
// have been created.
|
||||
if (instance == null) {
|
||||
throw new IllegalStateException("VisibilityLabelsCache not yet instantiated");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -87,6 +117,8 @@ public class VisibilityLabelsManager {
|
|||
}
|
||||
this.lock.writeLock().lock();
|
||||
try {
|
||||
labels.clear();
|
||||
ordinalVsLabels.clear();
|
||||
for (VisibilityLabel visLabel : visibilityLabels) {
|
||||
String label = Bytes.toString(visLabel.getLabel().toByteArray());
|
||||
labels.put(label, visLabel.getOrdinal());
|
||||
|
@ -106,6 +138,7 @@ public class VisibilityLabelsManager {
|
|||
}
|
||||
this.lock.writeLock().lock();
|
||||
try {
|
||||
this.userAuths.clear();
|
||||
for (UserAuthorizations userAuths : multiUserAuths.getUserAuthsList()) {
|
||||
String user = Bytes.toString(userAuths.getUser().toByteArray());
|
||||
this.userAuths.put(user, new HashSet<Integer>(userAuths.getAuthList()));
|
||||
|
@ -116,8 +149,8 @@ public class VisibilityLabelsManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param label
|
||||
* @return The ordinal for the label. The ordinal starts from 1. Returns 0 when the passed a non
|
||||
* @param label Not null label string
|
||||
* @return The ordinal for the label. The ordinal starts from 1. Returns 0 when passed a non
|
||||
* existing label.
|
||||
*/
|
||||
public int getLabelOrdinal(String label) {
|
||||
|
@ -132,9 +165,14 @@ public class VisibilityLabelsManager {
|
|||
return ordinal.intValue();
|
||||
}
|
||||
// 0 denotes not available
|
||||
return 0;
|
||||
return NON_EXIST_LABEL_ORDINAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ordinal The ordinal of label which we are looking for.
|
||||
* @return The label having the given ordinal. Returns <code>null</code> when no label exist in
|
||||
* the system with given ordinal
|
||||
*/
|
||||
public String getLabel(int ordinal) {
|
||||
this.lock.readLock().lock();
|
||||
try {
|
||||
|
@ -147,14 +185,15 @@ public class VisibilityLabelsManager {
|
|||
/**
|
||||
* @return The total number of visibility labels.
|
||||
*/
|
||||
public int getLabelsCount(){
|
||||
return this.labels.size();
|
||||
public int getLabelsCount() {
|
||||
this.lock.readLock().lock();
|
||||
try {
|
||||
return this.labels.size();
|
||||
} finally {
|
||||
this.lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param user
|
||||
* @return The labels that the given user is authorized for.
|
||||
*/
|
||||
public List<String> getAuths(String user) {
|
||||
List<String> auths = EMPTY_LIST;
|
||||
this.lock.readLock().lock();
|
||||
|
@ -175,23 +214,19 @@ public class VisibilityLabelsManager {
|
|||
/**
|
||||
* Returns the list of ordinals of authentications associated with the user
|
||||
*
|
||||
* @param user
|
||||
* @param user Not null value.
|
||||
* @return the list of ordinals
|
||||
*/
|
||||
public Set<Integer> getAuthsAsOrdinals(String user) {
|
||||
this.lock.readLock().lock();
|
||||
try {
|
||||
return userAuths.get(user);
|
||||
Set<Integer> auths = userAuths.get(user);
|
||||
return (auths == null) ? EMPTY_SET : auths;
|
||||
} finally {
|
||||
this.lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the labels data to zookeeper node.
|
||||
* @param data
|
||||
* @param labelsOrUserAuths true for writing labels and false for user auths.
|
||||
*/
|
||||
public void writeToZookeeper(byte[] data, boolean labelsOrUserAuths) {
|
||||
this.zkVisibilityWatcher.writeToZookeeper(data, labelsOrUserAuths);
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.apache.hadoop.hbase.security.visibility;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
@ -26,6 +27,8 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.KeyValue;
|
||||
|
@ -33,6 +36,7 @@ import org.apache.hadoop.hbase.KeyValue.Type;
|
|||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.regionserver.ScanDeleteTracker;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.Pair;
|
||||
|
||||
/**
|
||||
* Similar to ScanDeletTracker but tracks the visibility expression also before
|
||||
|
@ -41,6 +45,8 @@ import org.apache.hadoop.hbase.util.Bytes;
|
|||
@InterfaceAudience.Private
|
||||
public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(VisibilityScanDeleteTracker.class);
|
||||
|
||||
// Its better to track the visibility tags in delete based on each type. Create individual
|
||||
// data structures for tracking each of them. This would ensure that there is no tracking based
|
||||
// on time and also would handle all cases where deletefamily or deletecolumns is specified with
|
||||
|
@ -49,14 +55,17 @@ public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
|||
// type would solve this problem and also ensure that the combination of different type
|
||||
// of deletes with diff ts would also work fine
|
||||
// Track per TS
|
||||
private Map<Long, List<Tag>> visibilityTagsDeleteFamily = new HashMap<Long, List<Tag>>();
|
||||
private Map<Long, Pair<List<Tag>, Byte>> visibilityTagsDeleteFamily =
|
||||
new HashMap<Long, Pair<List<Tag>, Byte>>();
|
||||
// Delete family version with different ts and different visibility expression could come.
|
||||
// Need to track it per ts.
|
||||
private Map<Long,List<Tag>> visibilityTagsDeleteFamilyVersion = new HashMap<Long, List<Tag>>();
|
||||
private List<List<Tag>> visibilityTagsDeleteColumns;
|
||||
private Map<Long,Pair<List<Tag>, Byte>> visibilityTagsDeleteFamilyVersion =
|
||||
new HashMap<Long, Pair<List<Tag>, Byte>>();
|
||||
private List<Pair<List<Tag>, Byte>> visibilityTagsDeleteColumns;
|
||||
// Tracking as List<List> is to handle same ts cell but different visibility tag.
|
||||
// TODO : Need to handle puts with same ts but different vis tags.
|
||||
private List<List<Tag>> visiblityTagsDeleteColumnVersion = new ArrayList<List<Tag>>();
|
||||
private List<Pair<List<Tag>, Byte>> visiblityTagsDeleteColumnVersion =
|
||||
new ArrayList<Pair<List<Tag>, Byte>>();
|
||||
|
||||
public VisibilityScanDeleteTracker() {
|
||||
super();
|
||||
|
@ -72,11 +81,11 @@ public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
|||
if (type == KeyValue.Type.DeleteFamily.getCode()) {
|
||||
hasFamilyStamp = true;
|
||||
//familyStamps.add(delCell.getTimestamp());
|
||||
extractDeleteTags(delCell, KeyValue.Type.DeleteFamily);
|
||||
extractDeleteCellVisTags(delCell, KeyValue.Type.DeleteFamily);
|
||||
return;
|
||||
} else if (type == KeyValue.Type.DeleteFamilyVersion.getCode()) {
|
||||
familyVersionStamps.add(timestamp);
|
||||
extractDeleteTags(delCell, KeyValue.Type.DeleteFamilyVersion);
|
||||
extractDeleteCellVisTags(delCell, KeyValue.Type.DeleteFamilyVersion);
|
||||
return;
|
||||
}
|
||||
// new column, or more general delete type
|
||||
|
@ -103,68 +112,72 @@ public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
|||
deleteLength = qualifierLength;
|
||||
deleteType = type;
|
||||
deleteTimestamp = timestamp;
|
||||
extractDeleteTags(delCell, KeyValue.Type.codeToType(type));
|
||||
extractDeleteCellVisTags(delCell, KeyValue.Type.codeToType(type));
|
||||
}
|
||||
|
||||
private void extractDeleteTags(Cell delCell, Type type) {
|
||||
private void extractDeleteCellVisTags(Cell delCell, Type type) {
|
||||
// If tag is present in the delete
|
||||
if (delCell.getTagsLength() > 0) {
|
||||
switch (type) {
|
||||
case DeleteFamily:
|
||||
List<Tag> delTags = new ArrayList<Tag>();
|
||||
if (visibilityTagsDeleteFamily != null) {
|
||||
VisibilityUtils.getVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visibilityTagsDeleteFamily.put(delCell.getTimestamp(), delTags);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DeleteFamilyVersion:
|
||||
delTags = new ArrayList<Tag>();
|
||||
VisibilityUtils.getVisibilityTags(delCell, delTags);
|
||||
case DeleteFamily:
|
||||
List<Tag> delTags = new ArrayList<Tag>();
|
||||
if (visibilityTagsDeleteFamily != null) {
|
||||
Byte deleteCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visibilityTagsDeleteFamilyVersion.put(delCell.getTimestamp(), delTags);
|
||||
visibilityTagsDeleteFamily.put(delCell.getTimestamp(), new Pair<List<Tag>, Byte>(
|
||||
delTags, deleteCellVisTagsFormat));
|
||||
}
|
||||
break;
|
||||
case DeleteColumn:
|
||||
if (visibilityTagsDeleteColumns == null) {
|
||||
visibilityTagsDeleteColumns = new ArrayList<List<Tag>>();
|
||||
}
|
||||
delTags = new ArrayList<Tag>();
|
||||
VisibilityUtils.getVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visibilityTagsDeleteColumns.add(delTags);
|
||||
}
|
||||
break;
|
||||
case Delete:
|
||||
if (visiblityTagsDeleteColumnVersion == null) {
|
||||
visiblityTagsDeleteColumnVersion = new ArrayList<List<Tag>>();
|
||||
}
|
||||
delTags = new ArrayList<Tag>();
|
||||
VisibilityUtils.getVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visiblityTagsDeleteColumnVersion.add(delTags);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid delete type");
|
||||
}
|
||||
break;
|
||||
case DeleteFamilyVersion:
|
||||
delTags = new ArrayList<Tag>();
|
||||
Byte deleteCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visibilityTagsDeleteFamilyVersion.put(delCell.getTimestamp(), new Pair<List<Tag>, Byte>(
|
||||
delTags, deleteCellVisTagsFormat));
|
||||
}
|
||||
break;
|
||||
case DeleteColumn:
|
||||
if (visibilityTagsDeleteColumns == null) {
|
||||
visibilityTagsDeleteColumns = new ArrayList<Pair<List<Tag>, Byte>>();
|
||||
}
|
||||
delTags = new ArrayList<Tag>();
|
||||
deleteCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visibilityTagsDeleteColumns.add(new Pair<List<Tag>, Byte>(delTags,
|
||||
deleteCellVisTagsFormat));
|
||||
}
|
||||
break;
|
||||
case Delete:
|
||||
if (visiblityTagsDeleteColumnVersion == null) {
|
||||
visiblityTagsDeleteColumnVersion = new ArrayList<Pair<List<Tag>, Byte>>();
|
||||
}
|
||||
delTags = new ArrayList<Tag>();
|
||||
deleteCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(delCell, delTags);
|
||||
if (!delTags.isEmpty()) {
|
||||
visiblityTagsDeleteColumnVersion.add(new Pair<List<Tag>, Byte>(delTags,
|
||||
deleteCellVisTagsFormat));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid delete type");
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case DeleteFamily:
|
||||
visibilityTagsDeleteFamily = null;
|
||||
break;
|
||||
case DeleteFamilyVersion:
|
||||
visibilityTagsDeleteFamilyVersion = null;
|
||||
break;
|
||||
case DeleteColumn:
|
||||
visibilityTagsDeleteColumns = null;
|
||||
break;
|
||||
case Delete:
|
||||
visiblityTagsDeleteColumnVersion = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid delete type");
|
||||
case DeleteFamily:
|
||||
visibilityTagsDeleteFamily = null;
|
||||
break;
|
||||
case DeleteFamilyVersion:
|
||||
visibilityTagsDeleteFamilyVersion = null;
|
||||
break;
|
||||
case DeleteColumn:
|
||||
visibilityTagsDeleteColumns = null;
|
||||
break;
|
||||
case Delete:
|
||||
visiblityTagsDeleteColumnVersion = null;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid delete type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,93 +187,120 @@ public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
|||
long timestamp = cell.getTimestamp();
|
||||
int qualifierOffset = cell.getQualifierOffset();
|
||||
int qualifierLength = cell.getQualifierLength();
|
||||
if (hasFamilyStamp) {
|
||||
if (visibilityTagsDeleteFamily != null) {
|
||||
Set<Entry<Long, List<Tag>>> deleteFamilies = visibilityTagsDeleteFamily.entrySet();
|
||||
Iterator<Entry<Long, List<Tag>>> iterator = deleteFamilies.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Long, List<Tag>> entry = iterator.next();
|
||||
if (timestamp <= entry.getKey()) {
|
||||
boolean matchFound = VisibilityUtils.checkForMatchingVisibilityTags(cell,
|
||||
entry.getValue());
|
||||
try {
|
||||
if (hasFamilyStamp) {
|
||||
if (visibilityTagsDeleteFamily != null) {
|
||||
Set<Entry<Long, Pair<List<Tag>, Byte>>> deleteFamilies = visibilityTagsDeleteFamily
|
||||
.entrySet();
|
||||
Iterator<Entry<Long, Pair<List<Tag>, Byte>>> iterator = deleteFamilies.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entry<Long, Pair<List<Tag>, Byte>> entry = iterator.next();
|
||||
if (timestamp <= entry.getKey()) {
|
||||
List<Tag> putVisTags = new ArrayList<Tag>();
|
||||
Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
|
||||
boolean matchFound = VisibilityLabelServiceManager
|
||||
.getInstance()
|
||||
.getVisibilityLabelService()
|
||||
.matchVisibility(putVisTags, putCellVisTagsFormat, entry.getValue().getFirst(),
|
||||
entry.getValue().getSecond());
|
||||
if (matchFound) {
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (familyVersionStamps.contains(Long.valueOf(timestamp))) {
|
||||
if (visibilityTagsDeleteFamilyVersion != null) {
|
||||
Pair<List<Tag>, Byte> tags = visibilityTagsDeleteFamilyVersion.get(Long
|
||||
.valueOf(timestamp));
|
||||
if (tags != null) {
|
||||
List<Tag> putVisTags = new ArrayList<Tag>();
|
||||
Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
|
||||
boolean matchFound = VisibilityLabelServiceManager
|
||||
.getInstance()
|
||||
.getVisibilityLabelService()
|
||||
.matchVisibility(putVisTags, putCellVisTagsFormat, tags.getFirst(),
|
||||
tags.getSecond());
|
||||
if (matchFound) {
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (familyVersionStamps.contains(Long.valueOf(timestamp))) {
|
||||
if (visibilityTagsDeleteFamilyVersion != null) {
|
||||
List<Tag> tags = visibilityTagsDeleteFamilyVersion.get(Long.valueOf(timestamp));
|
||||
if (tags != null) {
|
||||
boolean matchFound = VisibilityUtils.checkForMatchingVisibilityTags(cell, tags);
|
||||
if (matchFound) {
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.FAMILY_VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deleteBuffer != null) {
|
||||
int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength, cell.getQualifierArray(),
|
||||
qualifierOffset, qualifierLength);
|
||||
if (deleteBuffer != null) {
|
||||
int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
|
||||
cell.getQualifierArray(), qualifierOffset, qualifierLength);
|
||||
|
||||
if (ret == 0) {
|
||||
if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
|
||||
if (visibilityTagsDeleteColumns != null) {
|
||||
for (List<Tag> tags : visibilityTagsDeleteColumns) {
|
||||
boolean matchFound = VisibilityUtils.checkForMatchingVisibilityTags(cell,
|
||||
tags);
|
||||
if (matchFound) {
|
||||
if (ret == 0) {
|
||||
if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
|
||||
if (visibilityTagsDeleteColumns != null) {
|
||||
for (Pair<List<Tag>, Byte> tags : visibilityTagsDeleteColumns) {
|
||||
List<Tag> putVisTags = new ArrayList<Tag>();
|
||||
Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
|
||||
boolean matchFound = VisibilityLabelServiceManager
|
||||
.getInstance()
|
||||
.getVisibilityLabelService()
|
||||
.matchVisibility(putVisTags, putCellVisTagsFormat, tags.getFirst(),
|
||||
tags.getSecond());
|
||||
if (matchFound) {
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete (aka DeleteVersion)
|
||||
// If the timestamp is the same, keep this one
|
||||
if (timestamp == deleteTimestamp) {
|
||||
if (visiblityTagsDeleteColumnVersion != null) {
|
||||
for (List<Tag> tags : visiblityTagsDeleteColumnVersion) {
|
||||
boolean matchFound = VisibilityUtils.checkForMatchingVisibilityTags(cell,
|
||||
tags);
|
||||
if (matchFound) {
|
||||
// Delete (aka DeleteVersion)
|
||||
// If the timestamp is the same, keep this one
|
||||
if (timestamp == deleteTimestamp) {
|
||||
if (visiblityTagsDeleteColumnVersion != null) {
|
||||
for (Pair<List<Tag>, Byte> tags : visiblityTagsDeleteColumnVersion) {
|
||||
List<Tag> putVisTags = new ArrayList<Tag>();
|
||||
Byte putCellVisTagsFormat = VisibilityUtils.extractVisibilityTags(cell, putVisTags);
|
||||
boolean matchFound = VisibilityLabelServiceManager
|
||||
.getInstance()
|
||||
.getVisibilityLabelService()
|
||||
.matchVisibility(putVisTags, putCellVisTagsFormat, tags.getFirst(),
|
||||
tags.getSecond());
|
||||
if (matchFound) {
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!VisibilityUtils.isVisibilityTagsPresent(cell)) {
|
||||
// No tags
|
||||
return DeleteResult.VERSION_DELETED;
|
||||
}
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
// Next column case.
|
||||
deleteBuffer = null;
|
||||
visibilityTagsDeleteColumns = null;
|
||||
visiblityTagsDeleteColumnVersion = null;
|
||||
} else {
|
||||
throw new IllegalStateException("isDeleted failed: deleteBuffer="
|
||||
+ Bytes.toStringBinary(deleteBuffer, deleteOffset, deleteLength) + ", qualifier="
|
||||
+ Bytes.toStringBinary(cell.getQualifierArray(), qualifierOffset, qualifierLength)
|
||||
+ ", timestamp=" + timestamp + ", comparison result: " + ret);
|
||||
}
|
||||
} else if (ret < 0) {
|
||||
// Next column case.
|
||||
deleteBuffer = null;
|
||||
visibilityTagsDeleteColumns = null;
|
||||
visiblityTagsDeleteColumnVersion = null;
|
||||
} else {
|
||||
throw new IllegalStateException("isDeleted failed: deleteBuffer="
|
||||
+ Bytes.toStringBinary(deleteBuffer, deleteOffset, deleteLength) + ", qualifier="
|
||||
+ Bytes.toStringBinary(cell.getQualifierArray(), qualifierOffset, qualifierLength)
|
||||
+ ", timestamp=" + timestamp + ", comparison result: " + ret);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error in isDeleted() check! Will treat cell as not deleted", e);
|
||||
}
|
||||
return DeleteResult.NOT_DELETED;
|
||||
}
|
||||
|
@ -269,8 +309,8 @@ public class VisibilityScanDeleteTracker extends ScanDeleteTracker {
|
|||
public void reset() {
|
||||
super.reset();
|
||||
visibilityTagsDeleteColumns = null;
|
||||
visibilityTagsDeleteFamily = new HashMap<Long, List<Tag>>();
|
||||
visibilityTagsDeleteFamilyVersion = new HashMap<Long, List<Tag>>();
|
||||
visibilityTagsDeleteFamily = new HashMap<Long, Pair<List<Tag>, Byte>>();
|
||||
visibilityTagsDeleteFamilyVersion = new HashMap<Long, Pair<List<Tag>, Byte>>();
|
||||
visiblityTagsDeleteColumnVersion = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,31 +17,40 @@
|
|||
*/
|
||||
package org.apache.hadoop.hbase.security.visibility;
|
||||
|
||||
import static org.apache.hadoop.hbase.TagType.VISIBILITY_TAG_TYPE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.HColumnDescriptor;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.TagType;
|
||||
import org.apache.hadoop.hbase.exceptions.DeserializationException;
|
||||
import org.apache.hadoop.hbase.io.util.StreamUtils;
|
||||
import org.apache.hadoop.hbase.filter.Filter;
|
||||
import org.apache.hadoop.hbase.ipc.RequestContext;
|
||||
import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
|
||||
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.VisibilityLabel;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsRequest;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
import org.apache.hadoop.hbase.util.ByteRange;
|
||||
import org.apache.hadoop.hbase.util.ByteStringer;
|
||||
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.util.ReflectionUtils;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
@ -52,14 +61,14 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
@InterfaceAudience.Private
|
||||
public class VisibilityUtils {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(VisibilityUtils.class);
|
||||
|
||||
public static final String VISIBILITY_LABEL_GENERATOR_CLASS =
|
||||
"hbase.regionserver.scan.visibility.label.generator.class";
|
||||
public static final byte VISIBILITY_TAG_TYPE = TagType.VISIBILITY_TAG_TYPE;
|
||||
public static final byte VISIBILITY_EXP_SERIALIZATION_TAG_TYPE =
|
||||
TagType.VISIBILITY_EXP_SERIALIZATION_TAG_TYPE;
|
||||
public static final String SYSTEM_LABEL = "system";
|
||||
public static final Tag VIS_SERIALIZATION_TAG = new Tag(VISIBILITY_EXP_SERIALIZATION_TAG_TYPE,
|
||||
VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT);
|
||||
public static final Tag SORTED_ORDINAL_SERIALIZATION_FORMAT_TAG = new Tag(
|
||||
TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
|
||||
VisibilityConstants.SORTED_ORDINAL_SERIALIZATION_FORMAT_TAG_VAL);
|
||||
private static final String COMMA = ",";
|
||||
|
||||
/**
|
||||
|
@ -140,8 +149,15 @@ public class VisibilityUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static List<ScanLabelGenerator> getScanLabelGenerators(Configuration conf)
|
||||
throws IOException {
|
||||
/**
|
||||
* @param conf The configuration to use
|
||||
* @return Stack of ScanLabelGenerator instances. ScanLabelGenerator classes can be specified in
|
||||
* Configuration as comma separated list using key
|
||||
* "hbase.regionserver.scan.visibility.label.generator.class"
|
||||
* @throws IllegalArgumentException
|
||||
* when any of the specified ScanLabelGenerator class can not be loaded.
|
||||
*/
|
||||
public static List<ScanLabelGenerator> getScanLabelGenerators(Configuration conf) {
|
||||
// There can be n SLG specified as comma separated in conf
|
||||
String slgClassesCommaSeparated = conf.get(VISIBILITY_LABEL_GENERATOR_CLASS);
|
||||
// We have only System level SLGs now. The order of execution will be same as the order in the
|
||||
|
@ -155,7 +171,7 @@ public class VisibilityUtils {
|
|||
slgKlass = (Class<? extends ScanLabelGenerator>) conf.getClassByName(slgClass.trim());
|
||||
slgs.add(ReflectionUtils.newInstance(slgKlass, conf));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IOException(e);
|
||||
throw new IllegalArgumentException("Unable to find " + slgClass, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,40 +184,28 @@ public class VisibilityUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the list of visibility tags in the given cell
|
||||
* Extract the visibility tags of the given Cell into the given List
|
||||
* @param cell - the cell
|
||||
* @param tags - the tags array that will be populated if
|
||||
* visibility tags are present
|
||||
* @return true if the tags are in sorted order.
|
||||
* @param tags - the array that will be populated if visibility tags are present
|
||||
* @return The visibility tags serialization format
|
||||
*/
|
||||
public static boolean getVisibilityTags(Cell cell, List<Tag> tags) {
|
||||
boolean sortedOrder = false;
|
||||
if (cell.getTagsLength() == 0) {
|
||||
return sortedOrder;
|
||||
}
|
||||
Iterator<Tag> tagsIterator = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsIterator.hasNext()) {
|
||||
Tag tag = tagsIterator.next();
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_EXP_SERIALIZATION_TAG_TYPE) {
|
||||
int serializationVersion = tag.getBuffer()[tag.getTagOffset()];
|
||||
if (serializationVersion == VisibilityConstants.VISIBILITY_SERIALIZATION_VERSION) {
|
||||
sortedOrder = true;
|
||||
continue;
|
||||
public static Byte extractVisibilityTags(Cell cell, List<Tag> tags) {
|
||||
Byte serializationFormat = null;
|
||||
if (cell.getTagsLength() > 0) {
|
||||
Iterator<Tag> tagsIterator = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsIterator.hasNext()) {
|
||||
Tag tag = tagsIterator.next();
|
||||
if (tag.getType() == TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) {
|
||||
serializationFormat = tag.getBuffer()[tag.getTagOffset()];
|
||||
} else if (tag.getType() == VISIBILITY_TAG_TYPE) {
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
return sortedOrder;
|
||||
return serializationFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cell has a visibility tag
|
||||
* @param cell
|
||||
* @return true if found, false if not found
|
||||
*/
|
||||
public static boolean isVisibilityTagsPresent(Cell cell) {
|
||||
if (cell.getTagsLength() == 0) {
|
||||
return false;
|
||||
|
@ -210,127 +214,38 @@ public class VisibilityUtils {
|
|||
cell.getTagsLength());
|
||||
while (tagsIterator.hasNext()) {
|
||||
Tag tag = tagsIterator.next();
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
|
||||
if (tag.getType() == VISIBILITY_TAG_TYPE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the matching visibility labels in the delete mutation and
|
||||
* the cell in consideration
|
||||
* @param cell - the cell
|
||||
* @param visibilityTagsInDeleteCell - that list of tags in the delete mutation
|
||||
* (the specified Cell Visibility)
|
||||
* @return true if matching tags are found
|
||||
*/
|
||||
public static boolean checkForMatchingVisibilityTags(Cell cell,
|
||||
List<Tag> visibilityTagsInDeleteCell) {
|
||||
List<Tag> tags = new ArrayList<Tag>();
|
||||
boolean sortedTags = getVisibilityTags(cell, tags);
|
||||
if (tags.size() == 0) {
|
||||
// Early out if there are no tags in the cell
|
||||
return false;
|
||||
}
|
||||
if (sortedTags) {
|
||||
return checkForMatchingVisibilityTagsWithSortedOrder(visibilityTagsInDeleteCell, tags);
|
||||
} else {
|
||||
try {
|
||||
return checkForMatchingVisibilityTagsWithOutSortedOrder(cell, visibilityTagsInDeleteCell);
|
||||
} catch (IOException e) {
|
||||
// Should not happen
|
||||
throw new RuntimeException("Exception while sorting the tags from the cell", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkForMatchingVisibilityTagsWithOutSortedOrder(Cell cell,
|
||||
List<Tag> visibilityTagsInDeleteCell) throws IOException {
|
||||
List<List<Integer>> sortedDeleteTags = sortTagsBasedOnOrdinal(
|
||||
visibilityTagsInDeleteCell);
|
||||
List<List<Integer>> sortedTags = sortTagsBasedOnOrdinal(cell);
|
||||
return compareTagsOrdinals(sortedDeleteTags, sortedTags);
|
||||
}
|
||||
|
||||
private static boolean checkForMatchingVisibilityTagsWithSortedOrder(
|
||||
List<Tag> visibilityTagsInDeleteCell, List<Tag> tags) {
|
||||
boolean matchFound = false;
|
||||
if ((visibilityTagsInDeleteCell.size()) != tags.size()) {
|
||||
// If the size does not match. Definitely we are not comparing the
|
||||
// equal tags.
|
||||
// Return false in that case.
|
||||
return matchFound;
|
||||
}
|
||||
for (Tag tag : visibilityTagsInDeleteCell) {
|
||||
matchFound = false;
|
||||
for (Tag givenTag : tags) {
|
||||
if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
|
||||
givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchFound;
|
||||
}
|
||||
|
||||
private static List<List<Integer>> sortTagsBasedOnOrdinal(Cell cell) throws IOException {
|
||||
List<List<Integer>> fullTagsList = new ArrayList<List<Integer>>();
|
||||
if (cell.getTagsLength() == 0) {
|
||||
return fullTagsList;
|
||||
}
|
||||
Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsItr.hasNext()) {
|
||||
Tag tag = tagsItr.next();
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
|
||||
getSortedTagOrdinals(fullTagsList, tag);
|
||||
}
|
||||
}
|
||||
return fullTagsList;
|
||||
}
|
||||
|
||||
private static List<List<Integer>> sortTagsBasedOnOrdinal(List<Tag> tags) throws IOException {
|
||||
List<List<Integer>> fullTagsList = new ArrayList<List<Integer>>();
|
||||
for (Tag tag : tags) {
|
||||
if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
|
||||
getSortedTagOrdinals(fullTagsList, tag);
|
||||
}
|
||||
}
|
||||
return fullTagsList;
|
||||
}
|
||||
|
||||
private static void getSortedTagOrdinals(List<List<Integer>> fullTagsList, Tag tag)
|
||||
public static Filter createVisibilityLabelFilter(HRegion region, Authorizations authorizations)
|
||||
throws IOException {
|
||||
List<Integer> tagsOrdinalInSortedOrder = new ArrayList<Integer>();
|
||||
int offset = tag.getTagOffset();
|
||||
int endOffset = offset + tag.getTagLength();
|
||||
while (offset < endOffset) {
|
||||
Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
|
||||
tagsOrdinalInSortedOrder.add(result.getFirst());
|
||||
offset += result.getSecond();
|
||||
Map<ByteRange, Integer> cfVsMaxVersions = new HashMap<ByteRange, Integer>();
|
||||
for (HColumnDescriptor hcd : region.getTableDesc().getFamilies()) {
|
||||
cfVsMaxVersions.put(new SimpleMutableByteRange(hcd.getName()), hcd.getMaxVersions());
|
||||
}
|
||||
Collections.sort(tagsOrdinalInSortedOrder);
|
||||
fullTagsList.add(tagsOrdinalInSortedOrder);
|
||||
VisibilityLabelService vls = VisibilityLabelServiceManager.getInstance()
|
||||
.getVisibilityLabelService();
|
||||
Filter visibilityLabelFilter = new VisibilityLabelFilter(
|
||||
vls.getVisibilityExpEvaluator(authorizations), cfVsMaxVersions);
|
||||
return visibilityLabelFilter;
|
||||
}
|
||||
|
||||
private static boolean compareTagsOrdinals(List<List<Integer>> tagsInDeletes,
|
||||
List<List<Integer>> tags) {
|
||||
boolean matchFound = false;
|
||||
if (tagsInDeletes.size() != tags.size()) {
|
||||
return matchFound;
|
||||
} else {
|
||||
for (List<Integer> deleteTagOrdinals : tagsInDeletes) {
|
||||
matchFound = false;
|
||||
for (List<Integer> tagOrdinals : tags) {
|
||||
if (deleteTagOrdinals.equals(tagOrdinals)) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchFound;
|
||||
/**
|
||||
* @return User who called RPC method. For non-RPC handling, falls back to system user
|
||||
* @throws IOException When there is IOE in getting the system user (During non-RPC handling).
|
||||
*/
|
||||
public static User getActiveUser() throws IOException {
|
||||
User user = RequestContext.getRequestUser();
|
||||
if (!RequestContext.isInRequestContext()) {
|
||||
user = User.getCurrent();
|
||||
}
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Current active user name is " + user.getShortName());
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,14 +42,14 @@ public class ZKVisibilityLabelWatcher extends ZooKeeperListener {
|
|||
"zookeeper.znode.visibility.user.auths.parent";
|
||||
private static final String DEFAULT_VISIBILITY_USER_AUTHS_NODE = "visibility/user_auths";
|
||||
|
||||
private VisibilityLabelsManager labelsManager;
|
||||
private VisibilityLabelsCache labelsCache;
|
||||
private String labelZnode;
|
||||
private String userAuthsZnode;
|
||||
|
||||
public ZKVisibilityLabelWatcher(ZooKeeperWatcher watcher, VisibilityLabelsManager labelsManager,
|
||||
public ZKVisibilityLabelWatcher(ZooKeeperWatcher watcher, VisibilityLabelsCache labelsCache,
|
||||
Configuration conf) {
|
||||
super(watcher);
|
||||
this.labelsManager = labelsManager;
|
||||
this.labelsCache = labelsCache;
|
||||
String labelZnodeParent = conf.get(VISIBILITY_LABEL_ZK_PATH, DEFAULT_VISIBILITY_LABEL_NODE);
|
||||
String userAuthsZnodeParent = conf.get(VISIBILITY_USER_AUTHS_ZK_PATH,
|
||||
DEFAULT_VISIBILITY_USER_AUTHS_NODE);
|
||||
|
@ -75,7 +75,7 @@ public class ZKVisibilityLabelWatcher extends ZooKeeperListener {
|
|||
|
||||
private void refreshVisibilityLabelsCache(byte[] data) {
|
||||
try {
|
||||
this.labelsManager.refreshLabelsCache(data);
|
||||
this.labelsCache.refreshLabelsCache(data);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Failed parsing data from labels table " + " from zk", ioe);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public class ZKVisibilityLabelWatcher extends ZooKeeperListener {
|
|||
|
||||
private void refreshUserAuthsCache(byte[] data) {
|
||||
try {
|
||||
this.labelsManager.refreshUserAuthsCache(data);
|
||||
this.labelsCache.refreshUserAuthsCache(data);
|
||||
} catch (IOException ioe) {
|
||||
LOG.error("Failed parsing data from labels table " + " from zk", ioe);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
/**
|
||||
* 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.TagType.VISIBILITY_TAG_TYPE;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_FAMILY;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityUtils.SYSTEM_LABEL;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.TagType;
|
||||
import org.apache.hadoop.hbase.HConstants.OperationStatusCode;
|
||||
import org.apache.hadoop.hbase.Tag;
|
||||
import org.apache.hadoop.hbase.client.Delete;
|
||||
import org.apache.hadoop.hbase.client.Get;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
import org.apache.hadoop.hbase.client.Put;
|
||||
import org.apache.hadoop.hbase.client.Result;
|
||||
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
|
||||
import org.apache.hadoop.hbase.regionserver.HRegion;
|
||||
import org.apache.hadoop.hbase.regionserver.OperationStatus;
|
||||
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.LeafExpressionNode;
|
||||
import org.apache.hadoop.hbase.security.visibility.expression.NonLeafExpressionNode;
|
||||
import org.apache.hadoop.hbase.security.visibility.expression.Operator;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* This is a VisibilityLabelService where labels in Mutation's visibility expression will be
|
||||
* persisted as Strings itself rather than ordinals in 'labels' table. Also there is no need to add
|
||||
* labels to the system, prior to using them in Mutations/Authorizations.
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelService {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(ExpAsStringVisibilityLabelServiceImpl.class);
|
||||
|
||||
private static final byte[] DUMMY_VALUE = new byte[0];
|
||||
private static final byte STRING_SERIALIZATION_FORMAT = 2;
|
||||
private static final Tag STRING_SERIALIZATION_FORMAT_TAG = new Tag(
|
||||
TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE,
|
||||
new byte[]{STRING_SERIALIZATION_FORMAT});
|
||||
private final ExpressionParser expressionParser = new ExpressionParser();
|
||||
private final ExpressionExpander expressionExpander = new ExpressionExpander();
|
||||
private Configuration conf;
|
||||
private HRegion labelsRegion;
|
||||
private List<ScanLabelGenerator> scanLabelGenerators;
|
||||
|
||||
@Override
|
||||
public OperationStatus[] addLabels(List<byte[]> labels) throws IOException {
|
||||
// Not doing specific label add. We will just add labels in Mutation visibility expression as it
|
||||
// is along with every cell.
|
||||
OperationStatus[] status = new OperationStatus[labels.size()];
|
||||
for (int i = 0; i < labels.size(); i++) {
|
||||
status[i] = new OperationStatus(OperationStatusCode.SUCCESS);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationStatus[] setAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||
assert labelsRegion != null;
|
||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||
Put p = new Put(user);
|
||||
for (byte[] auth : authLabels) {
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, auth, DUMMY_VALUE);
|
||||
}
|
||||
this.labelsRegion.put(p);
|
||||
// This is a testing impl and so not doing any caching
|
||||
for (int i = 0; i < authLabels.size(); i++) {
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
|
||||
}
|
||||
return finalOpStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationStatus[] clearAuths(byte[] user, List<byte[]> authLabels) throws IOException {
|
||||
assert labelsRegion != null;
|
||||
OperationStatus[] finalOpStatus = new OperationStatus[authLabels.size()];
|
||||
List<String> currentAuths = this.getAuths(user, true);
|
||||
Delete d = new Delete(user);
|
||||
int i = 0;
|
||||
for (byte[] authLabel : authLabels) {
|
||||
String authLabelStr = Bytes.toString(authLabel);
|
||||
if (currentAuths.contains(authLabelStr)) {
|
||||
d.deleteColumns(LABELS_TABLE_FAMILY, authLabel);
|
||||
} else {
|
||||
// This label is not set for the user.
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.FAILURE,
|
||||
new InvalidLabelException("Label '" + authLabelStr + "' is not set for the user "
|
||||
+ Bytes.toString(user)));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
this.labelsRegion.delete(d);
|
||||
// This is a testing impl and so not doing any caching
|
||||
for (i = 0; i < authLabels.size(); i++) {
|
||||
if (finalOpStatus[i] == null) {
|
||||
finalOpStatus[i] = new OperationStatus(OperationStatusCode.SUCCESS);
|
||||
}
|
||||
}
|
||||
return finalOpStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAuths(byte[] user, boolean systemCall) throws IOException {
|
||||
assert (labelsRegion != null || systemCall);
|
||||
List<String> auths = new ArrayList<String>();
|
||||
Get get = new Get(user);
|
||||
List<Cell> cells = null;
|
||||
if (labelsRegion == null) {
|
||||
HTable 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
|
||||
public List<Tag> createVisibilityExpTags(String visExpression, boolean withSerializationFormat,
|
||||
boolean checkAuths) throws IOException {
|
||||
ExpressionNode node = null;
|
||||
try {
|
||||
node = this.expressionParser.parse(visExpression);
|
||||
} catch (ParseException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
node = this.expressionExpander.expand(node);
|
||||
List<Tag> tags = new ArrayList<Tag>();
|
||||
if (withSerializationFormat) {
|
||||
tags.add(STRING_SERIALIZATION_FORMAT_TAG);
|
||||
}
|
||||
if (node instanceof NonLeafExpressionNode
|
||||
&& ((NonLeafExpressionNode) node).getOperator() == Operator.OR) {
|
||||
for (ExpressionNode child : ((NonLeafExpressionNode) node).getChildExps()) {
|
||||
tags.add(createTag(child));
|
||||
}
|
||||
} else {
|
||||
tags.add(createTag(node));
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VisibilityExpEvaluator getVisibilityExpEvaluator(Authorizations authorizations)
|
||||
throws IOException {
|
||||
// If a super user issues a get/scan, he should be able to scan the cells
|
||||
// irrespective of the Visibility labels
|
||||
if (isReadFromSuperUser()) {
|
||||
return new VisibilityExpEvaluator() {
|
||||
@Override
|
||||
public boolean evaluate(Cell cell) throws IOException {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
List<String> authLabels = null;
|
||||
for (ScanLabelGenerator scanLabelGenerator : scanLabelGenerators) {
|
||||
try {
|
||||
// null authorizations to be handled inside SLG impl.
|
||||
authLabels = scanLabelGenerator.getLabels(VisibilityUtils.getActiveUser(), authorizations);
|
||||
authLabels = (authLabels == null) ? new ArrayList<String>() : authLabels;
|
||||
authorizations = new Authorizations(authLabels);
|
||||
} catch (Throwable t) {
|
||||
LOG.error(t);
|
||||
throw new IOException(t);
|
||||
}
|
||||
}
|
||||
final List<String> authLabelsFinal = authLabels;
|
||||
return new VisibilityExpEvaluator() {
|
||||
@Override
|
||||
public boolean evaluate(Cell cell) throws IOException {
|
||||
boolean visibilityTagPresent = false;
|
||||
// Save an object allocation where we can
|
||||
if (cell.getTagsLength() > 0) {
|
||||
Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
|
||||
cell.getTagsLength());
|
||||
while (tagsItr.hasNext()) {
|
||||
boolean includeKV = true;
|
||||
Tag tag = tagsItr.next();
|
||||
if (tag.getType() == VISIBILITY_TAG_TYPE) {
|
||||
visibilityTagPresent = true;
|
||||
int offset = tag.getTagOffset();
|
||||
int endOffset = offset + tag.getTagLength();
|
||||
while (offset < endOffset) {
|
||||
short len = Bytes.toShort(tag.getBuffer(), offset);
|
||||
offset += 2;
|
||||
if (len < 0) {
|
||||
// This is a NOT label.
|
||||
len = (short) (-1 * len);
|
||||
String label = Bytes.toString(tag.getBuffer(), offset, len);
|
||||
if (authLabelsFinal.contains(label)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
String label = Bytes.toString(tag.getBuffer(), offset, len);
|
||||
if (!authLabelsFinal.contains(label)) {
|
||||
includeKV = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
if (includeKV) {
|
||||
// We got one visibility expression getting evaluated to true. Good to include this
|
||||
// KV in the result then.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !(visibilityTagPresent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected boolean isReadFromSuperUser() throws IOException {
|
||||
byte[] user = Bytes.toBytes(VisibilityUtils.getActiveUser().getShortName());
|
||||
return havingSystemAuth(user);
|
||||
}
|
||||
|
||||
private Tag createTag(ExpressionNode node) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(baos);
|
||||
List<String> labels = new ArrayList<String>();
|
||||
List<String> notLabels = new ArrayList<String>();
|
||||
extractLabels(node, labels, notLabels);
|
||||
Collections.sort(labels);
|
||||
Collections.sort(notLabels);
|
||||
// We will write the NOT labels 1st followed by normal labels
|
||||
// Each of the label we will write with label length (as short 1st) followed by the label bytes.
|
||||
// For a NOT node we will write the label length as -ve.
|
||||
for (String label : notLabels) {
|
||||
byte[] bLabel = Bytes.toBytes(label);
|
||||
short length = (short) bLabel.length;
|
||||
length = (short) (-1 * length);
|
||||
dos.writeShort(length);
|
||||
dos.write(bLabel);
|
||||
}
|
||||
for (String label : labels) {
|
||||
byte[] bLabel = Bytes.toBytes(label);
|
||||
dos.writeShort(bLabel.length);
|
||||
dos.write(bLabel);
|
||||
}
|
||||
return new Tag(VISIBILITY_TAG_TYPE, baos.toByteArray());
|
||||
}
|
||||
|
||||
private void extractLabels(ExpressionNode node, List<String> labels, List<String> notLabels) {
|
||||
if (node.isSingleNode()) {
|
||||
if (node instanceof NonLeafExpressionNode) {
|
||||
// This is a NOT node.
|
||||
LeafExpressionNode lNode = (LeafExpressionNode) ((NonLeafExpressionNode) node)
|
||||
.getChildExps().get(0);
|
||||
notLabels.add(lNode.getIdentifier());
|
||||
} else {
|
||||
labels.add(((LeafExpressionNode) node).getIdentifier());
|
||||
}
|
||||
} else {
|
||||
// A non leaf expression of labels with & operator.
|
||||
NonLeafExpressionNode nlNode = (NonLeafExpressionNode) node;
|
||||
assert nlNode.getOperator() == Operator.AND;
|
||||
List<ExpressionNode> childExps = nlNode.getChildExps();
|
||||
for (ExpressionNode child : childExps) {
|
||||
extractLabels(child, labels, notLabels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getConf() {
|
||||
return this.conf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConf(Configuration conf) {
|
||||
this.conf = conf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RegionCoprocessorEnvironment e) throws IOException {
|
||||
this.scanLabelGenerators = VisibilityUtils.getScanLabelGenerators(this.conf);
|
||||
if (e.getRegion().getRegionInfo().getTable().equals(LABELS_TABLE_NAME)) {
|
||||
this.labelsRegion = e.getRegion();
|
||||
// Set auth for "system" label for all super users.
|
||||
List<String> superUsers = getSystemAndSuperUsers();
|
||||
for (String superUser : superUsers) {
|
||||
byte[] user = Bytes.toBytes(superUser);
|
||||
List<String> auths = this.getAuths(user, true);
|
||||
if (auths == null || auths.isEmpty()) {
|
||||
Put p = new Put(user);
|
||||
p.addImmutable(LABELS_TABLE_FAMILY, Bytes.toBytes(SYSTEM_LABEL), DUMMY_VALUE);
|
||||
labelsRegion.put(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean havingSystemAuth(byte[] user) throws IOException {
|
||||
List<String> auths = this.getAuths(user, true);
|
||||
return auths.contains(SYSTEM_LABEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matchVisibility(List<Tag> putTags, Byte putTagsFormat, List<Tag> deleteTags,
|
||||
Byte deleteTagsFormat) throws IOException {
|
||||
assert putTagsFormat == STRING_SERIALIZATION_FORMAT;
|
||||
assert deleteTagsFormat == STRING_SERIALIZATION_FORMAT;
|
||||
return checkForMatchingVisibilityTagsWithSortedOrder(putTags, deleteTags);
|
||||
}
|
||||
|
||||
private static boolean checkForMatchingVisibilityTagsWithSortedOrder(List<Tag> putVisTags,
|
||||
List<Tag> deleteVisTags) {
|
||||
boolean matchFound = false;
|
||||
// If the size does not match. Definitely we are not comparing the equal tags.
|
||||
if ((deleteVisTags.size()) == putVisTags.size()) {
|
||||
for (Tag tag : deleteVisTags) {
|
||||
matchFound = false;
|
||||
for (Tag givenTag : putVisTags) {
|
||||
if (Bytes.equals(tag.getBuffer(), tag.getTagOffset(), tag.getTagLength(),
|
||||
givenTag.getBuffer(), givenTag.getTagOffset(), givenTag.getTagLength())) {
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matchFound) break;
|
||||
}
|
||||
}
|
||||
return matchFound;
|
||||
}
|
||||
}
|
|
@ -39,12 +39,10 @@ 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.TableName;
|
||||
import org.apache.hadoop.hbase.client.Admin;
|
||||
import org.apache.hadoop.hbase.client.Append;
|
||||
import org.apache.hadoop.hbase.client.Get;
|
||||
import org.apache.hadoop.hbase.client.HBaseAdmin;
|
||||
import org.apache.hadoop.hbase.client.HTable;
|
||||
import org.apache.hadoop.hbase.client.Increment;
|
||||
import org.apache.hadoop.hbase.client.Put;
|
||||
|
@ -63,64 +61,42 @@ import org.apache.hadoop.hbase.util.Bytes;
|
|||
import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Test class that tests the visibility labels
|
||||
* Base test class for visibility labels basic features
|
||||
*/
|
||||
@Category(MediumTests.class)
|
||||
public class TestVisibilityLabels {
|
||||
public abstract class TestVisibilityLabels {
|
||||
|
||||
private static final String TOPSECRET = "topsecret";
|
||||
private static final String PUBLIC = "public";
|
||||
private static final String PRIVATE = "private";
|
||||
private static final String CONFIDENTIAL = "confidential";
|
||||
private static final String SECRET = "secret";
|
||||
private static final String COPYRIGHT = "\u00A9ABC";
|
||||
private static final String ACCENT = "\u0941";
|
||||
private static final String UNICODE_VIS_TAG = COPYRIGHT + "\"" + ACCENT + "\\" + SECRET + "\""
|
||||
public static final String TOPSECRET = "topsecret";
|
||||
public static final String PUBLIC = "public";
|
||||
public static final String PRIVATE = "private";
|
||||
public static final String CONFIDENTIAL = "confidential";
|
||||
public static final String SECRET = "secret";
|
||||
public static final String COPYRIGHT = "\u00A9ABC";
|
||||
public static final String ACCENT = "\u0941";
|
||||
public static final String UNICODE_VIS_TAG = COPYRIGHT + "\"" + ACCENT + "\\" + SECRET + "\""
|
||||
+ "\u0027&\\";
|
||||
private static final String UC1 = "\u0027\"\u002b";
|
||||
private static final String UC2 = "\u002d\u003f";
|
||||
public static final String UC1 = "\u0027\"\u002b";
|
||||
public static final String UC2 = "\u002d\u003f";
|
||||
public static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
|
||||
private static final byte[] row1 = Bytes.toBytes("row1");
|
||||
private static final byte[] row2 = Bytes.toBytes("row2");
|
||||
private static final byte[] row3 = Bytes.toBytes("row3");
|
||||
private static final byte[] row4 = Bytes.toBytes("row4");
|
||||
private final static byte[] fam = Bytes.toBytes("info");
|
||||
private final static byte[] qual = Bytes.toBytes("qual");
|
||||
private final static byte[] value = Bytes.toBytes("value");
|
||||
public static final byte[] row1 = Bytes.toBytes("row1");
|
||||
public static final byte[] row2 = Bytes.toBytes("row2");
|
||||
public static final byte[] row3 = Bytes.toBytes("row3");
|
||||
public static final byte[] row4 = Bytes.toBytes("row4");
|
||||
public final static byte[] fam = Bytes.toBytes("info");
|
||||
public final static byte[] qual = Bytes.toBytes("qual");
|
||||
public final static byte[] value = Bytes.toBytes("value");
|
||||
public static Configuration conf;
|
||||
|
||||
private volatile boolean killedRS = false;
|
||||
@Rule
|
||||
public final TestName TEST_NAME = new TestName();
|
||||
public static User SUPERUSER;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupBeforeClass() throws Exception {
|
||||
// setup configuration
|
||||
conf = TEST_UTIL.getConfiguration();
|
||||
conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false);
|
||||
conf.setBoolean("hbase.online.schema.update.enable", true);
|
||||
VisibilityTestUtil.enableVisiblityLabels(conf);
|
||||
conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
|
||||
ScanLabelGenerator.class);
|
||||
conf.set("hbase.superuser", "admin");
|
||||
TEST_UTIL.startMiniCluster(2);
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
|
||||
// Wait for the labels table to become available
|
||||
TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
|
||||
addLabels();
|
||||
}
|
||||
public static User SUPERUSER, USER1;
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAfterClass() throws Exception {
|
||||
|
@ -440,53 +416,7 @@ public class TestVisibilityLabels {
|
|||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 60 * 1000)
|
||||
public void testAddVisibilityLabelsOnRSRestart() throws Exception {
|
||||
List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
|
||||
.getRegionServerThreads();
|
||||
for (RegionServerThread rsThread : regionServerThreads) {
|
||||
rsThread.getRegionServer().abort("Aborting ");
|
||||
}
|
||||
// Start one new RS
|
||||
RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer();
|
||||
waitForLabelsRegionAvailability(rs.getRegionServer());
|
||||
PrivilegedExceptionAction<VisibilityLabelsResponse> action =
|
||||
new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
|
||||
public VisibilityLabelsResponse run() throws Exception {
|
||||
String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, "ABC", "XYZ" };
|
||||
try {
|
||||
VisibilityClient.addLabels(conf, labels);
|
||||
} catch (Throwable t) {
|
||||
throw new IOException(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
SUPERUSER.runAs(action);
|
||||
// Scan the visibility label
|
||||
Scan s = new Scan();
|
||||
s.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
|
||||
HTable ht = new HTable(conf, LABELS_TABLE_NAME.getName());
|
||||
int i = 0;
|
||||
try {
|
||||
ResultScanner scanner = ht.getScanner(s);
|
||||
while (true) {
|
||||
Result next = scanner.next();
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} finally {
|
||||
if (ht != null) {
|
||||
ht.close();
|
||||
}
|
||||
}
|
||||
// One label is the "system" label.
|
||||
Assert.assertEquals("The count should be 13", 13, i);
|
||||
}
|
||||
|
||||
private void waitForLabelsRegionAvailability(HRegionServer regionServer) {
|
||||
protected void waitForLabelsRegionAvailability(HRegionServer regionServer) {
|
||||
while (!regionServer.isOnline()) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
|
@ -525,32 +455,6 @@ public class TestVisibilityLabels {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLabels() throws Throwable {
|
||||
PrivilegedExceptionAction<VisibilityLabelsResponse> action =
|
||||
new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
|
||||
public VisibilityLabelsResponse run() throws Exception {
|
||||
String[] labels = { "L1", SECRET, "L2", "invalid~", "L3" };
|
||||
VisibilityLabelsResponse response = null;
|
||||
try {
|
||||
response = VisibilityClient.addLabels(conf, labels);
|
||||
} catch (Throwable e) {
|
||||
fail("Should not have thrown exception");
|
||||
}
|
||||
List<RegionActionResult> resultList = response.getResultList();
|
||||
assertEquals(5, resultList.size());
|
||||
assertTrue(resultList.get(0).getException().getValue().isEmpty());
|
||||
assertEquals("org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException",
|
||||
resultList.get(1).getException().getName());
|
||||
assertTrue(resultList.get(2).getException().getValue().isEmpty());
|
||||
assertTrue(resultList.get(3).getException().getValue().isEmpty());
|
||||
assertTrue(resultList.get(4).getException().getValue().isEmpty());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
SUPERUSER.runAs(action);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAndGetUserAuths() throws Throwable {
|
||||
final String user = "user1";
|
||||
|
@ -572,18 +476,14 @@ public class TestVisibilityLabels {
|
|||
scan.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
|
||||
ResultScanner scanner = ht.getScanner(scan);
|
||||
Result result = null;
|
||||
List<Result> results = new ArrayList<Result>();
|
||||
while ((result = scanner.next()) != null) {
|
||||
Cell label = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
|
||||
Cell userAuth = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
|
||||
if (Bytes.equals(SECRET.getBytes(), 0, SECRET.getBytes().length, label.getValueArray(),
|
||||
label.getValueOffset(), label.getValueLength())
|
||||
|| Bytes.equals(CONFIDENTIAL.getBytes(), 0, CONFIDENTIAL.getBytes().length,
|
||||
label.getValueArray(), label.getValueOffset(), label.getValueLength())) {
|
||||
assertNotNull(userAuth);
|
||||
} else {
|
||||
assertNull(userAuth);
|
||||
}
|
||||
results.add(result);
|
||||
}
|
||||
List<String> auths = extractAuths(user, results);
|
||||
assertTrue(auths.contains(SECRET));
|
||||
assertTrue(auths.contains(CONFIDENTIAL));
|
||||
assertEquals(2, auths.size());
|
||||
} finally {
|
||||
if (ht != null) {
|
||||
ht.close();
|
||||
|
@ -637,6 +537,19 @@ public class TestVisibilityLabels {
|
|||
SUPERUSER.runAs(action);
|
||||
}
|
||||
|
||||
protected List<String> extractAuths(String user, List<Result> results) {
|
||||
List<String> auths = new ArrayList<String>();
|
||||
for (Result result : results) {
|
||||
Cell labelCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
|
||||
Cell userAuthCell = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
|
||||
if (userAuthCell != null) {
|
||||
auths.add(Bytes.toString(labelCell.getValueArray(), labelCell.getValueOffset(),
|
||||
labelCell.getValueLength()));
|
||||
}
|
||||
}
|
||||
return auths;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUserAuths() throws Throwable {
|
||||
PrivilegedExceptionAction<Void> action = new PrivilegedExceptionAction<Void>() {
|
||||
|
@ -660,24 +573,25 @@ public class TestVisibilityLabels {
|
|||
List<RegionActionResult> resultList = response.getResultList();
|
||||
assertEquals(3, resultList.size());
|
||||
assertTrue(resultList.get(0).getException().getValue().isEmpty());
|
||||
assertEquals("org.apache.hadoop.hbase.security.visibility.InvalidLabelException",
|
||||
assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException",
|
||||
resultList.get(1).getException().getName());
|
||||
assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray())
|
||||
.contains(
|
||||
"org.apache.hadoop.hbase.security.visibility.InvalidLabelException: "
|
||||
+ "Label 'public' is not set for the user testUser"));
|
||||
assertTrue(resultList.get(2).getException().getValue().isEmpty());
|
||||
HTable ht = null;
|
||||
try {
|
||||
ht = new HTable(conf, LABELS_TABLE_NAME);
|
||||
ResultScanner scanner = ht.getScanner(new Scan());
|
||||
Result result = null;
|
||||
List<Result> results = new ArrayList<Result>();
|
||||
while ((result = scanner.next()) != null) {
|
||||
Cell label = result.getColumnLatestCell(LABELS_TABLE_FAMILY, LABEL_QUALIFIER);
|
||||
Cell userAuth = result.getColumnLatestCell(LABELS_TABLE_FAMILY, user.getBytes());
|
||||
if (Bytes.equals(PRIVATE.getBytes(), 0, PRIVATE.getBytes().length,
|
||||
label.getValueArray(), label.getValueOffset(), label.getValueLength())) {
|
||||
assertNotNull(userAuth);
|
||||
} else {
|
||||
assertNull(userAuth);
|
||||
}
|
||||
results.add(result);
|
||||
}
|
||||
List<String> curAuths = extractAuths(user, results);
|
||||
assertTrue(curAuths.contains(PRIVATE));
|
||||
assertEquals(1, curAuths.size());
|
||||
} finally {
|
||||
if (ht != null) {
|
||||
ht.close();
|
||||
|
@ -978,7 +892,7 @@ public class TestVisibilityLabels {
|
|||
}
|
||||
}
|
||||
|
||||
private static HTable createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
|
||||
static HTable createTableAndWriteDataWithLabels(TableName tableName, String... labelExps)
|
||||
throws Exception {
|
||||
HTable table = null;
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* 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_FAMILY;
|
||||
import static org.apache.hadoop.hbase.security.visibility.VisibilityConstants.LABELS_TABLE_NAME;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NavigableMap;
|
||||
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.MediumTests;
|
||||
import org.apache.hadoop.hbase.TableName;
|
||||
import org.apache.hadoop.hbase.client.Result;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
@Category(MediumTests.class)
|
||||
public class TestVisibilityLabelsWithCustomVisLabService extends TestVisibilityLabels {
|
||||
|
||||
@BeforeClass
|
||||
public static void setupBeforeClass() throws Exception {
|
||||
// setup configuration
|
||||
conf = TEST_UTIL.getConfiguration();
|
||||
conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false);
|
||||
VisibilityTestUtil.enableVisiblityLabels(conf);
|
||||
conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
|
||||
ScanLabelGenerator.class);
|
||||
conf.setClass(VisibilityLabelServiceManager.VISIBILITY_LABEL_SERVICE_CLASS,
|
||||
ExpAsStringVisibilityLabelServiceImpl.class, VisibilityLabelService.class);
|
||||
conf.set("hbase.superuser", "admin");
|
||||
TEST_UTIL.startMiniCluster(2);
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
|
||||
// Wait for the labels table to become available
|
||||
TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
|
||||
addLabels();
|
||||
}
|
||||
|
||||
// Extending this test from super as we don't verify predefined labels in ExpAsStringVisibilityLabelServiceImpl
|
||||
@Test
|
||||
public void testVisibilityLabelsInPutsThatDoesNotMatchAnyDefinedLabels() throws Exception {
|
||||
TableName tableName = TableName.valueOf(TEST_NAME.getMethodName());
|
||||
// This put with label "SAMPLE_LABEL" should not get failed.
|
||||
createTableAndWriteDataWithLabels(tableName, "SAMPLE_LABEL", "TEST");
|
||||
}
|
||||
|
||||
protected List<String> extractAuths(String user, List<Result> results) {
|
||||
List<String> auths = new ArrayList<String>();
|
||||
for (Result result : results) {
|
||||
if (Bytes.equals(result.getRow(), Bytes.toBytes(user))) {
|
||||
NavigableMap<byte[], byte[]> familyMap = result.getFamilyMap(LABELS_TABLE_FAMILY);
|
||||
for (byte[] q : familyMap.keySet()) {
|
||||
auths.add(Bytes.toString(q, 0, q.length));
|
||||
}
|
||||
}
|
||||
}
|
||||
return auths;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
/**
|
||||
* 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.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.MediumTests;
|
||||
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.ClientProtos.RegionActionResult;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse;
|
||||
import org.apache.hadoop.hbase.security.User;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.categories.Category;
|
||||
|
||||
@Category(MediumTests.class)
|
||||
public class TestVisibilityLabelsWithDefaultVisLabelService extends TestVisibilityLabels {
|
||||
|
||||
@BeforeClass
|
||||
public static void setupBeforeClass() throws Exception {
|
||||
// setup configuration
|
||||
conf = TEST_UTIL.getConfiguration();
|
||||
conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false);
|
||||
conf.setBoolean("hbase.online.schema.update.enable", true);
|
||||
VisibilityTestUtil.enableVisiblityLabels(conf);
|
||||
conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
|
||||
ScanLabelGenerator.class);
|
||||
conf.set("hbase.superuser", "admin");
|
||||
TEST_UTIL.startMiniCluster(2);
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
USER1 = User.createUserForTesting(conf, "user1", new String[] {});
|
||||
|
||||
// Wait for the labels table to become available
|
||||
TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
|
||||
addLabels();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddLabels() throws Throwable {
|
||||
PrivilegedExceptionAction<VisibilityLabelsResponse> action =
|
||||
new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
|
||||
public VisibilityLabelsResponse run() throws Exception {
|
||||
String[] labels = { "L1", SECRET, "L2", "invalid~", "L3" };
|
||||
VisibilityLabelsResponse response = null;
|
||||
try {
|
||||
response = VisibilityClient.addLabels(conf, labels);
|
||||
} catch (Throwable e) {
|
||||
fail("Should not have thrown exception");
|
||||
}
|
||||
List<RegionActionResult> resultList = response.getResultList();
|
||||
assertEquals(5, resultList.size());
|
||||
assertTrue(resultList.get(0).getException().getValue().isEmpty());
|
||||
assertEquals("org.apache.hadoop.hbase.DoNotRetryIOException", resultList.get(1)
|
||||
.getException().getName());
|
||||
assertTrue(Bytes.toString(resultList.get(1).getException().getValue().toByteArray())
|
||||
.contains(
|
||||
"org.apache.hadoop.hbase.security.visibility.LabelAlreadyExistsException: "
|
||||
+ "Label 'secret' already exists"));
|
||||
assertTrue(resultList.get(2).getException().getValue().isEmpty());
|
||||
assertTrue(resultList.get(3).getException().getValue().isEmpty());
|
||||
assertTrue(resultList.get(4).getException().getValue().isEmpty());
|
||||
return null;
|
||||
}
|
||||
};
|
||||
SUPERUSER.runAs(action);
|
||||
}
|
||||
|
||||
@Test(timeout = 60 * 1000)
|
||||
public void testAddVisibilityLabelsOnRSRestart() throws Exception {
|
||||
List<RegionServerThread> regionServerThreads = TEST_UTIL.getHBaseCluster()
|
||||
.getRegionServerThreads();
|
||||
for (RegionServerThread rsThread : regionServerThreads) {
|
||||
rsThread.getRegionServer().abort("Aborting ");
|
||||
}
|
||||
// Start one new RS
|
||||
RegionServerThread rs = TEST_UTIL.getHBaseCluster().startRegionServer();
|
||||
waitForLabelsRegionAvailability(rs.getRegionServer());
|
||||
PrivilegedExceptionAction<VisibilityLabelsResponse> action =
|
||||
new PrivilegedExceptionAction<VisibilityLabelsResponse>() {
|
||||
public VisibilityLabelsResponse run() throws Exception {
|
||||
String[] labels = { SECRET, CONFIDENTIAL, PRIVATE, "ABC", "XYZ" };
|
||||
try {
|
||||
VisibilityClient.addLabels(conf, labels);
|
||||
} catch (Throwable t) {
|
||||
throw new IOException(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
SUPERUSER.runAs(action);
|
||||
// Scan the visibility label
|
||||
Scan s = new Scan();
|
||||
s.setAuthorizations(new Authorizations(VisibilityUtils.SYSTEM_LABEL));
|
||||
HTable ht = new HTable(conf, LABELS_TABLE_NAME.getName());
|
||||
int i = 0;
|
||||
try {
|
||||
ResultScanner scanner = ht.getScanner(s);
|
||||
while (true) {
|
||||
Result next = scanner.next();
|
||||
if (next == null) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} finally {
|
||||
if (ht != null) {
|
||||
ht.close();
|
||||
}
|
||||
}
|
||||
// One label is the "system" label.
|
||||
Assert.assertEquals("The count should be 13", 13, i);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,8 @@ import org.junit.experimental.categories.Category;
|
|||
* Test class that tests the visibility labels with distributed log replay feature ON.
|
||||
*/
|
||||
@Category(MediumTests.class)
|
||||
public class TestVisibilityLabelsWithDistributedLogReplay extends TestVisibilityLabels {
|
||||
public class TestVisibilityLabelsWithDistributedLogReplay extends
|
||||
TestVisibilityLabelsWithDefaultVisLabelService {
|
||||
|
||||
@BeforeClass
|
||||
public static void setupBeforeClass() throws Exception {
|
||||
|
@ -43,6 +44,7 @@ public class TestVisibilityLabelsWithDistributedLogReplay extends TestVisibility
|
|||
conf.set("hbase.superuser", "admin");
|
||||
TEST_UTIL.startMiniCluster(2);
|
||||
SUPERUSER = User.createUserForTesting(conf, "admin", new String[] { "supergroup" });
|
||||
USER1 = User.createUserForTesting(conf, "user1", new String[] {});
|
||||
|
||||
// Wait for the labels table to become available
|
||||
TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000);
|
||||
|
|
|
@ -68,9 +68,7 @@ public class TestVisibilityWithCheckAuths {
|
|||
// setup configuration
|
||||
conf = TEST_UTIL.getConfiguration();
|
||||
conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false);
|
||||
conf.setInt("hfile.format.version", 3);
|
||||
conf.set("hbase.coprocessor.master.classes", VisibilityController.class.getName());
|
||||
conf.set("hbase.coprocessor.region.classes", VisibilityController.class.getName());
|
||||
VisibilityTestUtil.enableVisiblityLabels(conf);
|
||||
conf.setBoolean(VisibilityConstants.CHECK_AUTHS_FOR_MUTATION, true);
|
||||
conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class,
|
||||
ScanLabelGenerator.class);
|
||||
|
|
Loading…
Reference in New Issue