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