View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.security.visibility;
19  
20  import java.io.IOException;
21  import java.util.BitSet;
22  import java.util.Iterator;
23  import java.util.Map;
24  
25  import org.apache.hadoop.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.Cell;
27  import org.apache.hadoop.hbase.CellUtil;
28  import org.apache.hadoop.hbase.Tag;
29  import org.apache.hadoop.hbase.filter.FilterBase;
30  import org.apache.hadoop.hbase.io.util.StreamUtils;
31  import org.apache.hadoop.hbase.util.ByteRange;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import org.apache.hadoop.hbase.util.Pair;
34  import org.apache.hadoop.hbase.util.SimpleByteRange;
35  
36  /**
37   * This Filter checks the visibility expression with each KV against visibility labels associated
38   * with the scan. Based on the check the KV is included in the scan result or gets filtered out.
39   */
40  @InterfaceAudience.Private
41  class VisibilityLabelFilter extends FilterBase {
42  
43    private final BitSet authLabels;
44    private final Map<ByteRange, Integer> cfVsMaxVersions;
45    private final ByteRange curFamily;
46    private final ByteRange curQualifier;
47    private int curFamilyMaxVersions;
48    private int curQualMetVersions;
49  
50    public VisibilityLabelFilter(BitSet authLabels, Map<ByteRange, Integer> cfVsMaxVersions) {
51      this.authLabels = authLabels;
52      this.cfVsMaxVersions = cfVsMaxVersions;
53      this.curFamily = new SimpleByteRange();
54      this.curQualifier = new SimpleByteRange();
55    }
56  
57    @Override
58    public ReturnCode filterKeyValue(Cell cell) throws IOException {
59      if (curFamily.getBytes() == null
60          || (Bytes.compareTo(curFamily.getBytes(), curFamily.getOffset(), curFamily.getLength(),
61              cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()) != 0)) {
62        curFamily.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
63        // For this family, all the columns can have max of curFamilyMaxVersions versions. No need to
64        // consider the older versions for visibility label check.
65        // Ideally this should have been done at a lower layer by HBase (?)
66        curFamilyMaxVersions = cfVsMaxVersions.get(curFamily);
67        // Family is changed. Just unset curQualifier.
68        curQualifier.unset();
69      }
70      if (curQualifier.getBytes() == null
71          || (Bytes.compareTo(curQualifier.getBytes(), curQualifier.getOffset(),
72              curQualifier.getLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
73              cell.getQualifierLength()) != 0)) {
74        curQualifier.set(cell.getQualifierArray(), cell.getQualifierOffset(),
75            cell.getQualifierLength());
76        curQualMetVersions = 0;
77      }
78      curQualMetVersions++;
79      if (curQualMetVersions > curFamilyMaxVersions) {
80        return ReturnCode.SKIP;
81      }
82  
83      Iterator<Tag> tagsItr = CellUtil.tagsIterator(cell.getTagsArray(), cell.getTagsOffset(),
84          cell.getTagsLengthUnsigned());
85      boolean visibilityTagPresent = false;
86      while (tagsItr.hasNext()) {
87        boolean includeKV = true;
88        Tag tag = tagsItr.next();
89        if (tag.getType() == VisibilityUtils.VISIBILITY_TAG_TYPE) {
90          visibilityTagPresent = true;
91          int offset = tag.getTagOffset();
92          int endOffset = offset + tag.getTagLength();
93          while (offset < endOffset) {
94            Pair<Integer, Integer> result = StreamUtils.readRawVarint32(tag.getBuffer(), offset);
95            int currLabelOrdinal = result.getFirst();
96            if (currLabelOrdinal < 0) {
97              // check for the absence of this label in the Scan Auth labels
98              // ie. to check BitSet corresponding bit is 0
99              int temp = -currLabelOrdinal;
100             if (this.authLabels.get(temp)) {
101               includeKV = false;
102               break;
103             }
104           } else {
105             if (!this.authLabels.get(currLabelOrdinal)) {
106               includeKV = false;
107               break;
108             }
109           }
110           offset += result.getSecond();
111         }
112         if (includeKV) {
113           // We got one visibility expression getting evaluated to true. Good to include this KV in
114           // the result then.
115           return ReturnCode.INCLUDE;
116         }
117       }
118     }
119     return visibilityTagPresent ? ReturnCode.SKIP : ReturnCode.INCLUDE;
120   }
121 
122   @Override
123   public void reset() throws IOException {
124     this.curFamily.unset();
125     this.curQualifier.unset();
126     this.curFamilyMaxVersions = 0;
127     this.curQualMetVersions = 0;
128   }
129 }