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