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  
19  package org.apache.hadoop.hbase.security.access;
20  
21  import java.io.IOException;
22  import java.util.Map;
23  import java.util.Objects;
24  
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.Cell;
27  import org.apache.hadoop.hbase.CellUtil;
28  import org.apache.hadoop.hbase.TableName;
29  import org.apache.hadoop.hbase.exceptions.DeserializationException;
30  import org.apache.hadoop.hbase.filter.FilterBase;
31  import org.apache.hadoop.hbase.security.User;
32  import org.apache.hadoop.hbase.util.ByteRange;
33  import org.apache.hadoop.hbase.util.Bytes;
34  import org.apache.hadoop.hbase.util.SimpleMutableByteRange;
35  
36  /**
37   * <strong>NOTE: for internal use only by AccessController implementation</strong>
38   *
39   * <p>
40   * TODO: There is room for further performance optimization here.
41   * Calling TableAuthManager.authorize() per KeyValue imposes a fair amount of
42   * overhead.  A more optimized solution might look at the qualifiers where
43   * permissions are actually granted and explicitly limit the scan to those.
44   * </p>
45   * <p>
46   * We should aim to use this _only_ when access to the requested column families
47   * is not granted at the column family levels.  If table or column family
48   * access succeeds, then there is no need to impose the overhead of this filter.
49   * </p>
50   */
51  @InterfaceAudience.Private
52  class AccessControlFilter extends FilterBase {
53  
54    public static enum Strategy {
55      /** Filter only by checking the table or CF permissions */
56      CHECK_TABLE_AND_CF_ONLY,
57      /** Cell permissions can override table or CF permissions */
58      CHECK_CELL_DEFAULT,
59    };
60  
61    private TableAuthManager authManager;
62    private TableName table;
63    private User user;
64    private boolean isSystemTable;
65    private Strategy strategy;
66    private Map<ByteRange, Integer> cfVsMaxVersions;
67    private int familyMaxVersions;
68    private int currentVersions;
69    private ByteRange prevFam;
70    private ByteRange prevQual;
71  
72    /**
73     * For Writable
74     */
75    AccessControlFilter() {
76    }
77  
78    AccessControlFilter(TableAuthManager mgr, User ugi, TableName tableName,
79        Strategy strategy, Map<ByteRange, Integer> cfVsMaxVersions) {
80      authManager = mgr;
81      table = tableName;
82      user = ugi;
83      isSystemTable = tableName.isSystemTable();
84      this.strategy = strategy;
85      this.cfVsMaxVersions = cfVsMaxVersions;
86      this.prevFam = new SimpleMutableByteRange();
87      this.prevQual = new SimpleMutableByteRange();
88    }
89  
90    @Override
91    public ReturnCode filterKeyValue(Cell cell) {
92      if (isSystemTable) {
93        return ReturnCode.INCLUDE;
94      }
95      if (prevFam.getBytes() == null
96          || (Bytes.compareTo(prevFam.getBytes(), prevFam.getOffset(), prevFam.getLength(),
97              cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()) != 0)) {
98        prevFam.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
99        // Similar to VisibilityLabelFilter
100       familyMaxVersions = cfVsMaxVersions.get(prevFam);
101       // Family is changed. Just unset curQualifier.
102       prevQual.unset();
103     }
104     if (prevQual.getBytes() == null
105         || (Bytes.compareTo(prevQual.getBytes(), prevQual.getOffset(),
106             prevQual.getLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
107             cell.getQualifierLength()) != 0)) {
108       prevQual.set(cell.getQualifierArray(), cell.getQualifierOffset(),
109           cell.getQualifierLength());
110       currentVersions = 0;
111     }
112     currentVersions++;
113     if (currentVersions > familyMaxVersions) {
114       return ReturnCode.SKIP;
115     }
116     // XXX: Compare in place, don't clone
117     byte[] family = CellUtil.cloneFamily(cell);
118     byte[] qualifier = CellUtil.cloneQualifier(cell);
119     switch (strategy) {
120       // Filter only by checking the table or CF permissions
121       case CHECK_TABLE_AND_CF_ONLY: {
122         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ)) {
123           return ReturnCode.INCLUDE;
124         }
125       }
126       break;
127       // Cell permissions can override table or CF permissions
128       case CHECK_CELL_DEFAULT: {
129         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ) ||
130             authManager.authorize(user, table, cell, Permission.Action.READ)) {
131           return ReturnCode.INCLUDE;
132         }
133       }
134       break;
135       default:
136         throw new RuntimeException("Unhandled strategy " + strategy);
137     }
138 
139     return ReturnCode.SKIP;
140   }
141 
142   // Override here explicitly as the method in super class FilterBase might do a KeyValue recreate.
143   // See HBASE-12068
144   @Override
145   public Cell transformCell(Cell v) {
146     return v;
147   }
148 
149   @Override
150   public void reset() throws IOException {
151     this.prevFam.unset();
152     this.prevQual.unset();
153     this.familyMaxVersions = 0;
154     this.currentVersions = 0;
155   }
156 
157   /**
158    * @return The filter serialized using pb
159    */
160   public byte [] toByteArray() {
161     // no implementation, server-side use only
162     throw new UnsupportedOperationException(
163       "Serialization not supported.  Intended for server-side use only.");
164   }
165 
166   /**
167    * @param pbBytes A pb serialized {@link AccessControlFilter} instance
168    * @return An instance of {@link AccessControlFilter} made from <code>bytes</code>
169    * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
170    * @see {@link #toByteArray()}
171    */
172   public static AccessControlFilter parseFrom(final byte [] pbBytes)
173   throws DeserializationException {
174     // no implementation, server-side use only
175     throw new UnsupportedOperationException(
176       "Serialization not supported.  Intended for server-side use only.");
177   }
178 
179   @Override
180   public boolean equals(Object obj) {
181     if (!(obj instanceof AccessControlFilter)) {
182       return false;
183     }
184     if (this == obj){
185       return true;
186     }
187     AccessControlFilter f=(AccessControlFilter)obj;
188     return this.authManager.equals(f.authManager) &&
189       this.table.equals(f.table) &&
190       this.user.equals(f.user) &&
191       this.strategy.equals(f.strategy) &&
192       this.cfVsMaxVersions.equals(f.cfVsMaxVersions);
193   }
194 
195   @Override
196   public int hashCode() {
197     return Objects.hash(this.authManager, this.table, this.strategy, this.user,
198       this.cfVsMaxVersions);
199   }
200 }