.../security/visibility/VisibilityConstants.java | 7 +- .../main/java/org/apache/hadoop/hbase/TagType.java | 4 + .../DefaultVisibilityExpressionResolver.java | 8 +- .../replication/VisibilityReplicationEndpoint.java | 146 +++++++++ .../DefaultVisibilityLabelServiceImpl.java | 92 +++++- .../security/visibility/VisibilityController.java | 119 +++++-- .../security/visibility/VisibilityLabelFilter.java | 7 +- .../visibility/VisibilityLabelOrdinalProvider.java | 2 + .../visibility/VisibilityLabelService.java | 44 ++- .../security/visibility/VisibilityLabelsCache.java | 3 +- .../hbase/security/visibility/VisibilityUtils.java | 33 +- .../ExpAsStringVisibilityLabelServiceImpl.java | 60 +++- ...tVisibilityLabelReplicationWithExpAsString.java | 189 +++++++++++ .../TestVisibilityLabelsReplication.java | 364 +++++++++++++++++++++ 14 files changed, 1046 insertions(+), 32 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java index ce5bca0..f8c2a87 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityConstants.java @@ -17,9 +17,9 @@ */ package org.apache.hadoop.hbase.security.visibility; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.NamespaceDescriptor; import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.util.Bytes; @InterfaceAudience.Private @@ -52,4 +52,9 @@ public final class VisibilityConstants { public static final String CHECK_AUTHS_FOR_MUTATION = "hbase.security.visibility.mutations.checkauths"; + public static final String NOT_OPERATOR = "!"; + public static final String AND_OPERATOR = "&"; + public static final String OR_OPERATOR = "|"; + public static final String OPEN_PARAN = "("; + public static final String CLOSED_PARAN = ")"; } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/TagType.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/TagType.java index 45c8476..cc5299b 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/TagType.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/TagType.java @@ -28,4 +28,8 @@ public final class TagType { 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_FORMAT_TAG_TYPE = (byte)4; + // The tag type that indicates that the tags with the type VISIBILITY_TAG_TYPE + // have been rewritten in String based form // TODO - better name + public static final byte MODIFIED_VIS_TAG_TYPE = (byte) 5; + public static final byte STRING_VIS_TAG_TYPE = (byte) 6; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/DefaultVisibilityExpressionResolver.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/DefaultVisibilityExpressionResolver.java index 150bb25..c590228 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/DefaultVisibilityExpressionResolver.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/mapreduce/DefaultVisibilityExpressionResolver.java @@ -28,10 +28,10 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.Tag; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; @@ -114,6 +114,12 @@ public class DefaultVisibilityExpressionResolver implements VisibilityExpression public int getLabelOrdinal(String label) { return labels.get(label); } + + @Override + public String getLabel(int ordinal) { + // Unused + return null; + } }; return VisibilityUtils.createVisibilityExpTags(visExpression, true, false, null, provider); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/VisibilityReplicationEndpoint.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/VisibilityReplicationEndpoint.java new file mode 100644 index 0000000..e143777 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/VisibilityReplicationEndpoint.java @@ -0,0 +1,146 @@ +/** + * 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.replication; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.KeyValue.Type; +import org.apache.hadoop.hbase.Tag; +import org.apache.hadoop.hbase.TagType; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.regionserver.wal.HLog.Entry; +import org.apache.hadoop.hbase.regionserver.wal.WALEdit; +import org.apache.hadoop.hbase.security.visibility.VisibilityLabelService; +import org.apache.hadoop.hbase.security.visibility.VisibilityUtils; + +import com.google.common.util.concurrent.ListenableFuture; + +@InterfaceAudience.Private +public class VisibilityReplicationEndpoint implements ReplicationEndpoint { + + private static final Log LOG = LogFactory.getLog(VisibilityReplicationEndpoint.class); + // Leaking to this package + private ReplicationEndpoint delegator; + private VisibilityLabelService visibilityLabelsService; + + public VisibilityReplicationEndpoint(ReplicationEndpoint endpoint, + VisibilityLabelService visibilityLabelsService) { + this.delegator = endpoint; + this.visibilityLabelsService = visibilityLabelsService; + } + + @Override + public void init(Context context) throws IOException { + delegator.init(context); + } + + @Override + public boolean replicate(ReplicateContext replicateContext) { + try { + List entries = replicateContext.getEntries(); + List newEntries = new ArrayList(entries.size()); + WALEdit newEdit = new WALEdit(); + for (Entry entry : entries) { + ArrayList cells = entry.getEdit().getCells(); + for (Cell cell : cells) { + if (cell.getTagsLength() > 0) { + List visTags = new ArrayList(); + List nonVisTags = VisibilityUtils.extractVisibilityAndNonVisibilityTags(cell, + visTags); + List existingTags = visibilityLabelsService + .modifyVisibilityLabels(visTags); + existingTags.add(new Tag(TagType.MODIFIED_VIS_TAG_TYPE, HConstants.EMPTY_BYTE_ARRAY)); + nonVisTags.addAll(existingTags); + // Recreate the cell with the new tags and the existing tags + Cell newCell = new KeyValue(cell.getRowArray(), cell.getRowOffset(), + cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), + cell.getFamilyLength(), cell.getQualifierArray(), cell.getQualifierOffset(), + cell.getQualifierLength(), cell.getTimestamp(), + Type.codeToType(cell.getTypeByte()), cell.getValueArray(), cell.getValueOffset(), + cell.getValueLength(), existingTags); + newEdit.add(newCell); + } else { + newEdit.add(cell); + } + } + newEntries.add(new Entry(entry.getKey(), newEdit)); + } + replicateContext.setEntries(newEntries); + return delegator.replicate(replicateContext); + } catch (IOException ioe) { + LOG.error("Exception while reading the visibility labels from the cell", ioe); + // TODO: + // throw run time exception? or just return the old entries? + return false; + } + } + + @Override + public synchronized UUID getPeerUUID() { + return delegator.getPeerUUID(); + } + + @Override + public boolean canReplicateToSameCluster() { + return delegator.canReplicateToSameCluster(); + } + + @Override + public WALEntryFilter getWALEntryfilter() { + return delegator.getWALEntryfilter(); + } + + @Override + public boolean isRunning() { + return delegator.isRunning(); + } + + @Override + public ListenableFuture start() { + return delegator.start(); + } + + @Override + public State startAndWait() { + return delegator.startAndWait(); + } + + @Override + public State state() { + return delegator.state(); + } + + @Override + public ListenableFuture stop() { + return delegator.stop(); + } + + @Override + public State stopAndWait() { + return delegator.stopAndWait(); + } + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java index e94760e..376b3f8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/DefaultVisibilityLabelServiceImpl.java @@ -39,12 +39,13 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.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.TagType; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; @@ -598,4 +599,93 @@ public class DefaultVisibilityLabelServiceImpl implements VisibilityLabelService } return matchFound; } + + @Override + public List modifyVisibilityLabels(List tags) throws IOException { + List existingTags = new ArrayList(); + createModifiedVisTags(existingTags, tags); + return existingTags; + } + + private List createModifiedVisTags(List existingTags, List tags) + throws IOException { + String visibilityString = ""; + boolean firstExpression = true; + for (Tag tag : tags) { + if (tag.getType() == TagType.VISIBILITY_TAG_TYPE) { + if (!firstExpression) { + visibilityString += VisibilityConstants.CLOSED_PARAN + VisibilityConstants.OR_OPERATOR; + } + int offset = tag.getTagOffset(); + int endOffset = offset + tag.getTagLength(); + boolean expressionStart = true; + while (offset < endOffset) { + Pair result = StreamUtils.readRawVarint32(tag.getBuffer(), offset); + int currLabelOrdinal = result.getFirst(); + if (currLabelOrdinal < 0) { + int temp = -currLabelOrdinal; + String label = this.labelsCache.getLabel(temp); + if (expressionStart) { + visibilityString += VisibilityConstants.OPEN_PARAN + VisibilityConstants.NOT_OPERATOR + + label; + } else { + visibilityString += VisibilityConstants.AND_OPERATOR + + VisibilityConstants.NOT_OPERATOR + label; + } + } else { + String label = this.labelsCache.getLabel(currLabelOrdinal); + if (expressionStart) { + visibilityString += VisibilityConstants.OPEN_PARAN + label; + } else { + visibilityString += VisibilityConstants.AND_OPERATOR + label; + } + } + firstExpression = false; + expressionStart = false; + offset += result.getSecond(); + } + } else if (tag.getType() != TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) { + // Avoiding all tags related to visibility expression as ordinal + existingTags.add(tag); + } + } + if (visibilityString.length() != 0) { + visibilityString += VisibilityConstants.CLOSED_PARAN; + // Storing only string based serialization + Tag stringTag = new Tag(TagType.STRING_VIS_TAG_TYPE, Bytes.toBytes(visibilityString)); + existingTags.add(stringTag); + } + return existingTags; + } + + @Override + public Tag getModifiedTag(List tags) throws IOException { + Tag modifiedTag = null; + boolean modifiedTagFound = false; + for (Tag tag : tags) { + // assume this is the special tag + if (tag.getType() == TagType.MODIFIED_VIS_TAG_TYPE) { + if (!modifiedTagFound) { + modifiedTagFound = true; + } + } + if (tag.getType() == TagType.STRING_VIS_TAG_TYPE) { + modifiedTag = tag; + } + } + return modifiedTag; + } + + @Override + public List removeModifiedTags(List tags, Tag modifiedTag) throws IOException { + List rewriteTags = new ArrayList(); + for (Tag tag : tags) { + if (tag.getType() != TagType.MODIFIED_VIS_TAG_TYPE && + tag.getType() != modifiedTag.getType()) { + rewriteTags.add(tag); + } + } + LOG.debug("Rewritten tags "+rewriteTags.size()); + return rewriteTags; + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java index 4986e0f..4988371 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityController.java @@ -32,7 +32,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellScanner; @@ -66,6 +65,7 @@ import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.RegionServerObserver; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException; import org.apache.hadoop.hbase.filter.Filter; @@ -91,13 +91,19 @@ import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; import org.apache.hadoop.hbase.regionserver.OperationStatus; +import org.apache.hadoop.hbase.MetaMutationAnnotation; import org.apache.hadoop.hbase.regionserver.RegionScanner; +import org.apache.hadoop.hbase.replication.ReplicationEndpoint; +import org.apache.hadoop.hbase.replication.VisibilityReplicationEndpoint; import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.TagType; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.security.access.AccessControlLists; import org.apache.hadoop.hbase.security.access.AccessController; import org.apache.hadoop.hbase.util.ByteStringer; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.Pair; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; @@ -112,7 +118,7 @@ import com.google.protobuf.Service; */ @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG) public class VisibilityController extends BaseMasterAndRegionObserver implements - VisibilityLabelsService.Interface, CoprocessorService { + RegionServerObserver, VisibilityLabelsService.Interface, CoprocessorService { private static final Log LOG = LogFactory.getLog(VisibilityController.class); // flags if we are running on a region of the 'labels' table @@ -144,17 +150,9 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements + " is required to persist visibility labels. Consider setting " + HFile.FORMAT_VERSION_KEY + " accordingly."); } - if (env instanceof RegionServerCoprocessorEnvironment) { - throw new RuntimeException( - "Visibility controller should not be configured as " + - "'hbase.coprocessor.regionserver.classes'."); - } - if (env instanceof RegionCoprocessorEnvironment) { - // VisibilityLabelService to be instantiated only with Region Observer. - visibilityLabelService = VisibilityLabelServiceManager.getInstance() - .getVisibilityLabelService(this.conf); - } + visibilityLabelService = VisibilityLabelServiceManager.getInstance().getVisibilityLabelService( + this.conf); this.superUsers = getSystemAndSuperUsers(); } @@ -282,12 +280,24 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements continue; } boolean sanityFailure = false; + Tag modifiedTag = null; for (CellScanner cellScanner = m.cellScanner(); cellScanner.advance();) { - if (!checkForReservedVisibilityTagPresence(cellScanner.current())) { + Pair pair = checkForReservedVisibilityTagPresence(cellScanner.current()); + if (!pair.getFirst()) { miniBatchOp.setOperationStatus(i, new OperationStatus(SANITY_CHECK_FAILURE, "Mutation contains cell with reserved type tag")); sanityFailure = true; break; + } else { + // Indicates that the cell has a the tag which was modified in the src replication cluster + if(pair.getSecond() != null) { + modifiedTag = pair.getSecond(); + String tag = Bytes.toString(modifiedTag.getValue()); + if(cellVisibility == null && tag != null) { + // May need to store only the first one + cellVisibility = new CellVisibility(tag); + } + } } } if (!sanityFailure) { @@ -314,6 +324,13 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements Cell cell = cellScanner.current(); List tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); + if (modifiedTag != null) { + // Rewrite the tags by removing the modified tags. Again depends on the + // VLS impl as how to remove the replication related tags + List rewriteTags = this.visibilityLabelService.removeModifiedTags(tags, + modifiedTag); + tags = rewriteTags; + } tags.addAll(visibilityTags); Cell updatedCell = new KeyValue(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(), cell.getFamilyArray(), cell.getFamilyOffset(), @@ -389,23 +406,34 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements // Checks whether cell contains any tag with type as VISIBILITY_TAG_TYPE. // This tag type is reserved and should not be explicitly set by user. - private boolean checkForReservedVisibilityTagPresence(Cell cell) throws IOException { + private Pair checkForReservedVisibilityTagPresence(Cell cell) throws IOException { // Bypass this check when the operation is done by a system/super user. // This is done because, while Replication, the Cells coming to the peer cluster with reserved // typed tags and this is fine and should get added to the peer cluster table + Pair pair = new Pair(false, null); if (isSystemOrSuperUser()) { - return true; + // Does the cell contain special tag which indicates that the replicated + // cell visiblilty tags + // have been modified + List tags = + Tag.asList(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); + Tag modifiedTag = this.visibilityLabelService.getModifiedTag(tags); + pair.setFirst(true); + pair.setSecond(modifiedTag); + return pair; } if (cell.getTagsLength() > 0) { Iterator tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); while (tagsItr.hasNext()) { if (RESERVED_VIS_TAG_TYPES.contains(tagsItr.next().getType())) { - return false; + return pair; } } } - return true; + pair.setFirst(true); + pair.setSecond(null); + return pair; } @Override @@ -564,7 +592,7 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements public Result preAppend(ObserverContext e, Append append) throws IOException { for (CellScanner cellScanner = append.cellScanner(); cellScanner.advance();) { - if (!checkForReservedVisibilityTagPresence(cellScanner.current())) { + if (!checkForReservedVisibilityTagPresence(cellScanner.current()).getFirst()) { throw new FailedSanityCheckException("Append contains cell with reserved type tag"); } } @@ -575,7 +603,7 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements public Result preIncrement(ObserverContext e, Increment increment) throws IOException { for (CellScanner cellScanner = increment.cellScanner(); cellScanner.advance();) { - if (!checkForReservedVisibilityTagPresence(cellScanner.current())) { + if (!checkForReservedVisibilityTagPresence(cellScanner.current()).getFirst()) { throw new FailedSanityCheckException("Increment contains cell with reserved type tag"); } } @@ -833,4 +861,57 @@ public class VisibilityController extends BaseMasterAndRegionObserver implements return matchFound ? ReturnCode.INCLUDE : ReturnCode.SKIP; } } + + @Override + public void preStopRegionServer(ObserverContext env) + throws IOException { + } + + @Override + public void preMerge(ObserverContext ctx, HRegion regionA, + HRegion regionB) throws IOException { + } + + @Override + public void postMerge(ObserverContext c, HRegion regionA, + HRegion regionB, HRegion mergedRegion) throws IOException { + + } + + @Override + public void preMergeCommit(ObserverContext ctx, + HRegion regionA, HRegion regionB, @MetaMutationAnnotation List metaEntries) + throws IOException { + } + + @Override + public void postMergeCommit(ObserverContext ctx, + HRegion regionA, HRegion regionB, HRegion mergedRegion) throws IOException { + } + + @Override + public void preRollBackMerge(ObserverContext ctx, + HRegion regionA, HRegion regionB) throws IOException { + } + + @Override + public void postRollBackMerge(ObserverContext ctx, + HRegion regionA, HRegion regionB) throws IOException { + } + + @Override + public void preRollWALWriterRequest(ObserverContext ctx) + throws IOException { + } + + @Override + public void postRollWALWriterRequest(ObserverContext ctx) + throws IOException { + } + + @Override + public ReplicationEndpoint postCreateReplicationEndPoint( + ObserverContext ctx, ReplicationEndpoint endpoint) { + return new VisibilityReplicationEndpoint(endpoint, visibilityLabelService); + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelFilter.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelFilter.java index eb8abbe..68b3757 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelFilter.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelFilter.java @@ -20,8 +20,10 @@ package org.apache.hadoop.hbase.security.visibility; import java.io.IOException; import java.util.Map; -import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.filter.FilterBase; import org.apache.hadoop.hbase.util.ByteRange; import org.apache.hadoop.hbase.util.Bytes; @@ -33,7 +35,7 @@ import org.apache.hadoop.hbase.util.SimpleMutableByteRange; */ @InterfaceAudience.Private class VisibilityLabelFilter extends FilterBase { - + private static final Log LOG = LogFactory.getLog(VisibilityLabelFilter.class); private final VisibilityExpEvaluator expEvaluator; private final Map cfVsMaxVersions; private final ByteRange curFamily; @@ -74,7 +76,6 @@ class VisibilityLabelFilter extends FilterBase { if (curQualMetVersions > curFamilyMaxVersions) { return ReturnCode.SKIP; } - return this.expEvaluator.evaluate(cell) ? ReturnCode.INCLUDE : ReturnCode.SKIP; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelOrdinalProvider.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelOrdinalProvider.java index 81be70b..3c89fbb 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelOrdinalProvider.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelOrdinalProvider.java @@ -28,4 +28,6 @@ public interface VisibilityLabelOrdinalProvider { * existing label. */ public int getLabelOrdinal(String label); + + public String getLabel(int ordinal); } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java index 7f4c2ca..0756e6d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelService.java @@ -20,12 +20,15 @@ package org.apache.hadoop.hbase.security.visibility; import java.io.IOException; import java.util.List; -import org.apache.hadoop.hbase.classification.InterfaceAudience; -import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.hbase.Tag; +import org.apache.hadoop.hbase.TagType; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.regionserver.OperationStatus; +import org.apache.hadoop.hbase.replication.ReplicationEndpoint; +import org.apache.hadoop.hbase.replication.VisibilityReplicationEndpoint; /** * The interface which deals with visibility labels and user auths admin service as well as the cell @@ -139,4 +142,41 @@ public interface VisibilityLabelService extends Configurable { */ boolean matchVisibility(List putVisTags, Byte putVisTagFormat, List deleteVisTags, Byte deleteVisTagFormat) throws IOException; + + /** + * Provides a way to modify the visibility tags of type + * {@link TagType.VISIBILITY_TAG_TYPE}, that are part of the cell created from + * the WALEdits that are prepared for replication while calling + * {@link ReplicationEndpoint} .replicate(). + * {@link VisibilityReplicationEndpoint} calls this API to provide an + * opportunity to modify the visibility tags before replicating. + * + * @param visTags + * the tags associated with the cell + * @return the modified list of visibility tags along with other existing + * tags, return the original tags if nothing to be modified + * @throws IOException + */ + List modifyVisibilityLabels(List visTags) throws IOException; + + /** + * Gets the modified tag, if found, base do on the + * {@link VisibilityLabelService} implementation. + * + * @param tags - list of tags in the cell + * @return - the modified tag + * @throws IOException + */ + Tag getModifiedTag(List tags) throws IOException; + + /** + * Provides a way to remove the modified tags. This may be helpful when + * the impl feels that the modified tags need not be serialized along with the + * cells + * @param tags - list of tags in the cell + * @param modifiedTag - the tag that was modified + * @return - list of tags after removal, if any + * @throws IOException + */ + List removeModifiedTags(List tags, Tag modifiedTag) throws IOException; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java index 693134b..a5c2155 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityLabelsCache.java @@ -29,8 +29,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.MultiUserAuthorizations; import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.UserAuthorizations; @@ -174,6 +174,7 @@ public class VisibilityLabelsCache implements VisibilityLabelOrdinalProvider { * @return The label having the given ordinal. Returns null when no label exist in * the system with given ordinal */ + @Override public String getLabel(int ordinal) { this.lock.readLock().lock(); try { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityUtils.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityUtils.java index 96185dd..dff6443 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityUtils.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/security/visibility/VisibilityUtils.java @@ -28,19 +28,19 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.Map.Entry; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.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.classification.InterfaceAudience; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.filter.Filter; import org.apache.hadoop.hbase.io.util.StreamUtils; @@ -219,6 +219,35 @@ public class VisibilityUtils { return serializationFormat; } + /** + * Extracts the visibility tags and nonVisibility Tags + * + * @param cell + * @param visTags + * - all the visibilty tags of type TagType.VISIBILITY_TAG_TYPE would + * be added to this list + * @return - list of all non visibility tags that are associated with the cell + */ + public static List extractVisibilityAndNonVisibilityTags(Cell cell, List visTags) { + List nonVisTags = new ArrayList(); + if (cell.getTagsLength() > 0) { + Iterator tagsIterator = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(), + cell.getTagsLength()); + while (tagsIterator.hasNext()) { + Tag tag = tagsIterator.next(); + if (tag.getType() == VISIBILITY_TAG_TYPE + || tag.getType() == TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) { + visTags.add(tag); + } else { + // Just leave out the string format that will be added as part of + // modification + nonVisTags.add(tag); + } + } + } + return nonVisTags; + } + public static boolean isVisibilityTagsPresent(Cell cell) { if (cell.getTagsLength() == 0) { return false; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java index 997c17d..f694e7c 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/ExpAsStringVisibilityLabelServiceImpl.java @@ -32,13 +32,13 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.hbase.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.TagType; +import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HTable; @@ -393,4 +393,60 @@ public class ExpAsStringVisibilityLabelServiceImpl implements VisibilityLabelSer } return matchFound; } + + @Override + public List modifyVisibilityLabels(List tags) throws IOException { + boolean stringBasedTags = false; + for (Tag tag : tags) { + if (tag.getType() == TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE) { + byte b = tag.getBuffer()[tag.getTagOffset()]; + if (b == STRING_SERIALIZATION_FORMAT) { + // having string based tags + stringBasedTags = true; + break; + } + } + + } + if (stringBasedTags) { + // Indicates that the server already has string based tags and hence return the tags as is + return tags; + } else { + // Apply the modifications if needed. + return tags; + } + } + + @Override + public Tag getModifiedTag(List tags) throws IOException { + Tag modifiedTag = null; + boolean modifiedTagFound = false; + for (Tag tag : tags) { + // assume this is the special tag + if (tag.getType() == TagType.MODIFIED_VIS_TAG_TYPE) { + if (!modifiedTagFound) { + modifiedTagFound = true; + } + } + // An example to show + if (tag.getType() == TagType.VISIBILITY_TAG_TYPE + || tag.getType() == TagType.STRING_VIS_TAG_TYPE) { + modifiedTag = tag; + } + } + return modifiedTag; + } + + @Override + public List removeModifiedTags(List tags, Tag modifiedTag) throws IOException { + List rewriteTags = new ArrayList(); + if (modifiedTag != null) { + for (Tag tag : tags) { + if (tag.getType() != TagType.MODIFIED_VIS_TAG_TYPE) { + rewriteTags.add(tag); + } + } + } + return rewriteTags; + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelReplicationWithExpAsString.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelReplicationWithExpAsString.java new file mode 100644 index 0000000..cfba6b1 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelReplicationWithExpAsString.java @@ -0,0 +1,189 @@ +/** + * 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.security.PrivilegedExceptionAction; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellScanner; +import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.HBaseConfiguration; +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.TableName; +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.Result; +import org.apache.hadoop.hbase.client.replication.ReplicationAdmin; +import org.apache.hadoop.hbase.codec.KeyValueCodecWithTags; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import org.junit.BeforeClass; +import org.junit.experimental.categories.Category; + +@Category({ SecurityTests.class, MediumTests.class }) +public class TestVisibilityLabelReplicationWithExpAsString extends TestVisibilityLabelsReplication { + private static final Log LOG = LogFactory + .getLog(TestVisibilityLabelReplicationWithExpAsString.class); + + @BeforeClass + public static void setupBeforeClass() throws Exception { + expected[0] = 5; + expected[1] = 7; + expected[2] = 5; + expected[3] = 0; + // setup configuration + conf = HBaseConfiguration.create(); + conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false); + conf.setBoolean("hbase.online.schema.update.enable", true); + conf.setInt("hfile.format.version", 3); + conf.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/1"); + conf.setInt("replication.source.size.capacity", 10240); + conf.setLong("replication.source.sleepforretries", 100); + conf.setInt("hbase.regionserver.maxlogs", 10); + conf.setLong("hbase.master.logcleaner.ttl", 10); + conf.setInt("zookeeper.recovery.retry", 1); + conf.setInt("zookeeper.recovery.retry.intervalmill", 10); + conf.setBoolean("dfs.support.append", true); + conf.setLong(HConstants.THREAD_WAKE_FREQUENCY, 100); + conf.setInt("replication.stats.thread.period.seconds", 5); + conf.setBoolean("hbase.tests.use.shortcircuit.reads", false); + setVisibilityLabelServiceImpl(conf, ExpAsStringVisibilityLabelServiceImpl.class); + conf.setBoolean(HConstants.REPLICATION_ENABLE_KEY, HConstants.REPLICATION_ENABLE_DEFAULT); + conf.setStrings(HConstants.REPLICATION_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); + conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, + VisibilityController.class.getName()); + // Have to reset conf1 in case zk cluster location different + // than default + conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, + ScanLabelGenerator.class); + conf.set("hbase.superuser", "admin"); + conf.set("hbase.superuser", User.getCurrent().getShortName()); + SUPERUSER = User.createUserForTesting(conf, User.getCurrent().getShortName(), + new String[] { "supergroup" }); + User.createUserForTesting(conf, User.getCurrent().getShortName(), new String[] { "supergroup" }); + USER1 = User.createUserForTesting(conf, "user1", new String[] {}); + TEST_UTIL = new HBaseTestingUtility(conf); + TEST_UTIL.startMiniZKCluster(); + MiniZooKeeperCluster miniZK = TEST_UTIL.getZkCluster(); + zkw1 = new ZooKeeperWatcher(conf, "cluster1", null, true); + replicationAdmin = new ReplicationAdmin(conf); + + // Base conf2 on conf1 so it gets the right zk cluster. + conf1 = HBaseConfiguration.create(conf); + conf1.setInt("hfile.format.version", 3); + conf1.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/2"); + conf1.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6); + conf1.setBoolean("dfs.support.append", true); + conf1.setBoolean("hbase.tests.use.shortcircuit.reads", false); + conf.setBoolean(HConstants.REPLICATION_ENABLE_KEY, HConstants.REPLICATION_ENABLE_DEFAULT); + conf1.setStrings(HConstants.REPLICATION_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName()); + conf1.setStrings(CoprocessorHost.USER_REGION_COPROCESSOR_CONF_KEY, + TestCoprocessorForTagsAtSink.class.getName()); + setVisibilityLabelServiceImpl(conf1, ExpAsStringVisibilityLabelServiceImpl.class); + TEST_UTIL1 = new HBaseTestingUtility(conf1); + TEST_UTIL1.setZkCluster(miniZK); + zkw2 = new ZooKeeperWatcher(conf1, "cluster2", null, true); + replicationAdmin.addPeer("2", TEST_UTIL1.getClusterKey()); + + TEST_UTIL.startMiniCluster(1); + // Wait for the labels table to become available + TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); + TEST_UTIL1.startMiniCluster(1); + HBaseAdmin hBaseAdmin = TEST_UTIL.getHBaseAdmin(); + HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME)); + HColumnDescriptor desc = new HColumnDescriptor(fam); + desc.setScope(HConstants.REPLICATION_SCOPE_GLOBAL); + table.addFamily(desc); + try { + hBaseAdmin.createTable(table); + } finally { + if (hBaseAdmin != null) { + hBaseAdmin.close(); + } + } + HBaseAdmin hBaseAdmin1 = TEST_UTIL1.getHBaseAdmin(); + try { + hBaseAdmin1.createTable(table); + } finally { + if (hBaseAdmin1 != null) { + hBaseAdmin1.close(); + } + } + addLabels(); + setAuths(conf); + setAuths(conf1); + } + + protected static void setVisibilityLabelServiceImpl(Configuration conf, Class clazz) { + conf.setClass(VisibilityLabelServiceManager.VISIBILITY_LABEL_SERVICE_CLASS, + clazz, VisibilityLabelService.class); + } + + @Override + protected void verifyGet(final byte[] row, final String visStringTag, final int expected, + final boolean nullExpected, final String... auths) + throws IOException, InterruptedException { + PrivilegedExceptionAction scanAction = new PrivilegedExceptionAction() { + HTable table2 = null; + + public Void run() throws Exception { + try { + table2 = new HTable(conf1, TABLE_NAME_BYTES); + CellScanner cellScanner; + Cell current; + Get get = new Get(row); + get.setAuthorizations(new Authorizations(auths)); + Result result = table2.get(get); + cellScanner = result.cellScanner(); + boolean advance = cellScanner.advance(); + if (nullExpected) { + assertTrue(!advance); + return null; + } + current = cellScanner.current(); + assertArrayEquals(CellUtil.cloneRow(current), row); + assertEquals(expected, TestCoprocessorForTagsAtSink.tags.size()); + return null; + } finally { + if (table2 != null) { + table2.close(); + } + } + } + }; + USER1.runAs(scanAction); + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelsReplication.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelsReplication.java new file mode 100644 index 0000000..21de071 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/visibility/TestVisibilityLabelsReplication.java @@ -0,0 +1,364 @@ +/** + * 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellScanner; +import org.apache.hadoop.hbase.CellUtil; +import org.apache.hadoop.hbase.HBaseConfiguration; +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.TableName; +import org.apache.hadoop.hbase.Tag; +import org.apache.hadoop.hbase.TagType; +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.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.Table; +import org.apache.hadoop.hbase.client.replication.ReplicationAdmin; +import org.apache.hadoop.hbase.codec.KeyValueCodecWithTags; +import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver; +import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; +import org.apache.hadoop.hbase.protobuf.generated.VisibilityLabelsProtos.VisibilityLabelsResponse; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.testclassification.SecurityTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster; +import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; + +@Category({ SecurityTests.class, MediumTests.class }) +public class TestVisibilityLabelsReplication { + private static final Log LOG = LogFactory.getLog(TestVisibilityLabelsReplication.class); + protected static Configuration conf; + protected static Configuration conf1; + protected static String TABLE_NAME = "TABLE_NAME"; + protected static byte[] TABLE_NAME_BYTES = Bytes.toBytes(TABLE_NAME); + protected static ReplicationAdmin replicationAdmin; + 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 HBaseTestingUtility TEST_UTIL; + public static HBaseTestingUtility TEST_UTIL1; + 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"); + protected static ZooKeeperWatcher zkw1; + protected static ZooKeeperWatcher zkw2; + protected static int expected[] = { 3, 5, 3, 0 }; + + @Rule + public final TestName TEST_NAME = new TestName(); + public static User SUPERUSER, USER1; + + @BeforeClass + public static void setupBeforeClass() throws Exception { + // setup configuration + conf = HBaseConfiguration.create(); + conf.setBoolean(HConstants.DISTRIBUTED_LOG_REPLAY_KEY, false); + conf.setBoolean("hbase.online.schema.update.enable", true); + conf.setInt("hfile.format.version", 3); + conf.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/1"); + conf.setInt("replication.source.size.capacity", 10240); + conf.setLong("replication.source.sleepforretries", 100); + conf.setInt("hbase.regionserver.maxlogs", 10); + conf.setLong("hbase.master.logcleaner.ttl", 10); + conf.setInt("zookeeper.recovery.retry", 1); + conf.setInt("zookeeper.recovery.retry.intervalmill", 10); + conf.setBoolean("dfs.support.append", true); + conf.setLong(HConstants.THREAD_WAKE_FREQUENCY, 100); + conf.setInt("replication.stats.thread.period.seconds", 5); + conf.setBoolean("hbase.tests.use.shortcircuit.reads", false); + setVisibilityLabelServiceImpl(conf); + conf.setBoolean(HConstants.REPLICATION_ENABLE_KEY, HConstants.REPLICATION_ENABLE_DEFAULT); + conf.setStrings(HConstants.REPLICATION_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName()); + VisibilityTestUtil.enableVisiblityLabels(conf); + conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, + VisibilityController.class.getName()); + // Have to reset conf1 in case zk cluster location different + // than default + conf.setClass(VisibilityUtils.VISIBILITY_LABEL_GENERATOR_CLASS, SimpleScanLabelGenerator.class, + ScanLabelGenerator.class); + conf.set("hbase.superuser", User.getCurrent().getShortName()); + SUPERUSER = User.createUserForTesting(conf, User.getCurrent().getShortName(), + new String[] { "supergroup" }); + // User.createUserForTesting(conf, User.getCurrent().getShortName(), new + // String[] { "supergroup" }); + USER1 = User.createUserForTesting(conf, "user1", new String[] {}); + TEST_UTIL = new HBaseTestingUtility(conf); + TEST_UTIL.startMiniZKCluster(); + MiniZooKeeperCluster miniZK = TEST_UTIL.getZkCluster(); + zkw1 = new ZooKeeperWatcher(conf, "cluster1", null, true); + replicationAdmin = new ReplicationAdmin(conf); + + // Base conf2 on conf1 so it gets the right zk cluster. + conf1 = HBaseConfiguration.create(conf); + conf1.setInt("hfile.format.version", 3); + conf1.set(HConstants.ZOOKEEPER_ZNODE_PARENT, "/2"); + conf1.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 6); + conf1.setBoolean("dfs.support.append", true); + conf1.setBoolean("hbase.tests.use.shortcircuit.reads", false); + conf.setBoolean(HConstants.REPLICATION_ENABLE_KEY, HConstants.REPLICATION_ENABLE_DEFAULT); + conf1.setStrings(HConstants.REPLICATION_CODEC_CONF_KEY, KeyValueCodecWithTags.class.getName()); + conf1.setStrings(CoprocessorHost.USER_REGION_COPROCESSOR_CONF_KEY, + TestCoprocessorForTagsAtSink.class.getName()); + // setVisibilityLabelServiceImpl(conf1); + USER1 = User.createUserForTesting(conf1, "user1", new String[] {}); + TEST_UTIL1 = new HBaseTestingUtility(conf1); + TEST_UTIL1.setZkCluster(miniZK); + zkw2 = new ZooKeeperWatcher(conf1, "cluster2", null, true); + replicationAdmin.addPeer("2", TEST_UTIL1.getClusterKey()); + + TEST_UTIL.startMiniCluster(1); + // Wait for the labels table to become available + TEST_UTIL.waitTableEnabled(LABELS_TABLE_NAME.getName(), 50000); + TEST_UTIL1.startMiniCluster(1); + HBaseAdmin hBaseAdmin = TEST_UTIL.getHBaseAdmin(); + HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME)); + HColumnDescriptor desc = new HColumnDescriptor(fam); + desc.setScope(HConstants.REPLICATION_SCOPE_GLOBAL); + table.addFamily(desc); + try { + hBaseAdmin.createTable(table); + } finally { + if (hBaseAdmin != null) { + hBaseAdmin.close(); + } + } + HBaseAdmin hBaseAdmin1 = TEST_UTIL1.getHBaseAdmin(); + try { + hBaseAdmin1.createTable(table); + } finally { + if (hBaseAdmin1 != null) { + hBaseAdmin1.close(); + } + } + addLabels(); + setAuths(conf); + setAuths(conf1); + } + + protected static void setVisibilityLabelServiceImpl(Configuration conf) { + conf.setClass(VisibilityLabelServiceManager.VISIBILITY_LABEL_SERVICE_CLASS, + DefaultVisibilityLabelServiceImpl.class, VisibilityLabelService.class); + } + + @Test + public void testVisibilityReplication() throws Exception { + TableName tableName = TableName.valueOf(TABLE_NAME); + Table table = writeData(tableName, "(" + SECRET + "&" + PUBLIC + ")" + "|(" + CONFIDENTIAL + + ")&(" + TOPSECRET + ")", "(" + PRIVATE + "|" + CONFIDENTIAL + ")&(" + PUBLIC + "|" + + TOPSECRET + ")", "(" + SECRET + "|" + CONFIDENTIAL + ")" + "&" + "!" + TOPSECRET); + int retry = 0; + try { + Scan s = new Scan(); + s.setAuthorizations(new Authorizations(SECRET, CONFIDENTIAL, PRIVATE, TOPSECRET)); + ResultScanner scanner = table.getScanner(s); + Result[] next = scanner.next(3); + + assertTrue(next.length == 3); + CellScanner cellScanner = next[0].cellScanner(); + cellScanner.advance(); + Cell current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row1, 0, row1.length)); + cellScanner = next[1].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row2, 0, row2.length)); + cellScanner = next[2].cellScanner(); + cellScanner.advance(); + current = cellScanner.current(); + assertTrue(Bytes.equals(current.getRowArray(), current.getRowOffset(), + current.getRowLength(), row3, 0, row3.length)); + HTable table2 = null; + try { + table2 = new HTable(TEST_UTIL1.getConfiguration(), TABLE_NAME_BYTES); + s = new Scan(); + // Ensure both rows are replicated + scanner = table2.getScanner(s); + next = scanner.next(3); + while (next.length == 0 && retry <= 10) { + scanner = table2.getScanner(s); + next = scanner.next(3); + Thread.sleep(2000); + retry++; + } + assertTrue(next.length == 3); + verifyGet(row1, "(secret&topsecret&public)|(topsecret&confidential)", expected[0], false, + TOPSECRET, CONFIDENTIAL); + TestCoprocessorForTagsAtSink.tags.clear(); + verifyGet(row2, + "(public&private)|(topsecret&private)|(confidential&public)|(topsecret&confidential)", + expected[1], false, CONFIDENTIAL, PUBLIC); + TestCoprocessorForTagsAtSink.tags.clear(); + verifyGet(row3, "(!topsecret&secret)|(!topsecret&confidential)", expected[2], false, + PRIVATE, SECRET); + verifyGet(row3, "(!topsecret&secret)|(!topsecret&confidential)", expected[3], true, + TOPSECRET, SECRET); + } finally { + if (table2 != null) { + table2.close(); + } + } + } finally { + if (table != null) { + table.close(); + } + } + } + + protected void verifyGet(final byte[] row, final String visStringTag, final int expected, + final boolean nullExpected, final String... auths) throws IOException, InterruptedException { + PrivilegedExceptionAction scanAction = new PrivilegedExceptionAction() { + HTable table2 = null; + + public Void run() throws Exception { + try { + table2 = new HTable(conf1, TABLE_NAME_BYTES); + CellScanner cellScanner; + Cell current; + Get get = new Get(row); + get.setAuthorizations(new Authorizations(auths)); + Result result = table2.get(get); + cellScanner = result.cellScanner(); + boolean advance = cellScanner.advance(); + if (nullExpected) { + assertTrue(!advance); + return null; + } + current = cellScanner.current(); + assertArrayEquals(CellUtil.cloneRow(current), row); + for (Tag tag : TestCoprocessorForTagsAtSink.tags) { + System.out.println("The tag type is "+ tag.getType()); + } + assertEquals(expected, TestCoprocessorForTagsAtSink.tags.size()); + Tag tag = TestCoprocessorForTagsAtSink.tags.get(0); + assertEquals(TagType.VISIBILITY_EXP_SERIALIZATION_FORMAT_TAG_TYPE, tag.getType()); + return null; + } finally { + if (table2 != null) { + table2.close(); + } + } + } + }; + USER1.runAs(scanAction); + } + + public static void addLabels() throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public VisibilityLabelsResponse run() throws Exception { + String[] labels = { SECRET, TOPSECRET, CONFIDENTIAL, PUBLIC, PRIVATE }; + try { + VisibilityClient.addLabels(conf, labels); + } catch (Throwable t) { + throw new IOException(t); + } + return null; + } + }; + SUPERUSER.runAs(action); + } + + public static void setAuths(final Configuration conf) throws Exception { + PrivilegedExceptionAction action = new PrivilegedExceptionAction() { + public VisibilityLabelsResponse run() throws Exception { + try { + return VisibilityClient.setAuths(conf, new String[] { SECRET, CONFIDENTIAL, PRIVATE, + TOPSECRET }, "user1"); + } catch (Throwable e) { + throw new Exception(e); + } + } + }; + VisibilityLabelsResponse response = SUPERUSER.runAs(action); + } + + static Table writeData(TableName tableName, String... labelExps) throws Exception { + HTable table = null; + try { + table = new HTable(conf, TABLE_NAME_BYTES); + int i = 1; + List puts = new ArrayList(); + for (String labelExp : labelExps) { + Put put = new Put(Bytes.toBytes("row" + i)); + put.add(fam, qual, HConstants.LATEST_TIMESTAMP, value); + put.setCellVisibility(new CellVisibility(labelExp)); + puts.add(put); + i++; + } + table.put(puts); + } finally { + if (table != null) { + table.flushCommits(); + } + } + return table; + } + + public static class TestCoprocessorForTagsAtSink extends BaseRegionObserver { + public static List tags = null; + + @Override + public void postGetOp(ObserverContext e, Get get, + List results) throws IOException { + if (results.size() > 0) { + // Check tag presence in the 1st cell in 1st Result + if (!results.isEmpty()) { + Cell cell = results.get(0); + tags = Tag.asList(cell.getTagsArray(), cell.getTagsOffset(), cell.getTagsLength()); + } + } + } + } + +}