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.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 ReturnCode filterKeyValue(Cell cell) {
91      if (isSystemTable) {
92        return ReturnCode.INCLUDE;
93      }
94      if (prevFam.getBytes() == null
95          || (Bytes.compareTo(prevFam.getBytes(), prevFam.getOffset(), prevFam.getLength(),
96              cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()) != 0)) {
97        prevFam.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
98        // Similar to VisibilityLabelFilter
99        familyMaxVersions = cfVsMaxVersions.get(prevFam);
100       // Family is changed. Just unset curQualifier.
101       prevQual.unset();
102     }
103     if (prevQual.getBytes() == null
104         || (Bytes.compareTo(prevQual.getBytes(), prevQual.getOffset(),
105             prevQual.getLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
106             cell.getQualifierLength()) != 0)) {
107       prevQual.set(cell.getQualifierArray(), cell.getQualifierOffset(),
108           cell.getQualifierLength());
109       currentVersions = 0;
110     }
111     currentVersions++;
112     if (currentVersions > familyMaxVersions) {
113       return ReturnCode.SKIP;
114     }
115     // XXX: Compare in place, don't clone
116     byte[] family = CellUtil.cloneFamily(cell);
117     byte[] qualifier = CellUtil.cloneQualifier(cell);
118     switch (strategy) {
119       // Filter only by checking the table or CF permissions
120       case CHECK_TABLE_AND_CF_ONLY: {
121         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ)) {
122           return ReturnCode.INCLUDE;
123         }
124       }
125       break;
126       // Cell permissions can override table or CF permissions
127       case CHECK_CELL_DEFAULT: {
128         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ) ||
129             authManager.authorize(user, table, cell, Permission.Action.READ)) {
130           return ReturnCode.INCLUDE;
131         }
132       }
133       break;
134       default:
135         throw new RuntimeException("Unhandled strategy " + strategy);
136     }
137 
138     return ReturnCode.SKIP;
139   }
140 
141   @Override
142   public void reset() throws IOException {
143     this.prevFam.unset();
144     this.prevQual.unset();
145     this.familyMaxVersions = 0;
146     this.currentVersions = 0;
147   }
148 
149   /**
150    * @return The filter serialized using pb
151    */
152   public byte [] toByteArray() {
153     // no implementation, server-side use only
154     throw new UnsupportedOperationException(
155       "Serialization not supported.  Intended for server-side use only.");
156   }
157 
158   /**
159    * @param pbBytes A pb serialized {@link AccessControlFilter} instance
160    * @return An instance of {@link AccessControlFilter} made from <code>bytes</code>
161    * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
162    * @see {@link #toByteArray()}
163    */
164   public static AccessControlFilter parseFrom(final byte [] pbBytes)
165   throws DeserializationException {
166     // no implementation, server-side use only
167     throw new UnsupportedOperationException(
168       "Serialization not supported.  Intended for server-side use only.");
169   }
170 }