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.Cell;
25  import org.apache.hadoop.hbase.CellUtil;
26  import org.apache.hadoop.hbase.TableName;
27  import org.apache.hadoop.hbase.exceptions.DeserializationException;
28  import org.apache.hadoop.hbase.filter.FilterBase;
29  import org.apache.hadoop.hbase.security.User;
30  import org.apache.hadoop.hbase.util.ByteRange;
31  import org.apache.hadoop.hbase.util.Bytes;
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  class AccessControlFilter extends FilterBase {
50  
51    public static enum Strategy {
52      /** Filter only by checking the table or CF permissions */
53      CHECK_TABLE_AND_CF_ONLY,
54      /** Cell permissions can override table or CF permissions */
55      CHECK_CELL_DEFAULT,
56    };
57  
58    private TableAuthManager authManager;
59    private TableName table;
60    private User user;
61    private boolean isSystemTable;
62    private Strategy strategy;
63    private Map<ByteRange, Integer> cfVsMaxVersions;
64    private int familyMaxVersions;
65    private int currentVersions;
66    private ByteRange prevFam;
67    private ByteRange prevQual;
68  
69    /**
70     * For Writable
71     */
72    AccessControlFilter() {
73    }
74  
75    AccessControlFilter(TableAuthManager mgr, User ugi, TableName tableName,
76        Strategy strategy, Map<ByteRange, Integer> cfVsMaxVersions) {
77      authManager = mgr;
78      table = tableName;
79      user = ugi;
80      isSystemTable = tableName.isSystemTable();
81      this.strategy = strategy;
82      this.cfVsMaxVersions = cfVsMaxVersions;
83      this.prevFam = new SimpleMutableByteRange();
84      this.prevQual = new SimpleMutableByteRange();
85    }
86  
87    @Override
88    public ReturnCode filterKeyValue(Cell cell) {
89      if (isSystemTable) {
90        return ReturnCode.INCLUDE;
91      }
92      if (prevFam.getBytes() == null
93          || (Bytes.compareTo(prevFam.getBytes(), prevFam.getOffset(), prevFam.getLength(),
94              cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength()) != 0)) {
95        prevFam.set(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
96        // Similar to VisibilityLabelFilter
97        familyMaxVersions = cfVsMaxVersions.get(prevFam);
98        // Family is changed. Just unset curQualifier.
99        prevQual.unset();
100     }
101     if (prevQual.getBytes() == null
102         || (Bytes.compareTo(prevQual.getBytes(), prevQual.getOffset(),
103             prevQual.getLength(), cell.getQualifierArray(), cell.getQualifierOffset(),
104             cell.getQualifierLength()) != 0)) {
105       prevQual.set(cell.getQualifierArray(), cell.getQualifierOffset(),
106           cell.getQualifierLength());
107       currentVersions = 0;
108     }
109     currentVersions++;
110     if (currentVersions > familyMaxVersions) {
111       return ReturnCode.SKIP;
112     }
113     // XXX: Compare in place, don't clone
114     byte[] family = CellUtil.cloneFamily(cell);
115     byte[] qualifier = CellUtil.cloneQualifier(cell);
116     switch (strategy) {
117       // Filter only by checking the table or CF permissions
118       case CHECK_TABLE_AND_CF_ONLY: {
119         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ)) {
120           return ReturnCode.INCLUDE;
121         }
122       }
123       break;
124       // Cell permissions can override table or CF permissions
125       case CHECK_CELL_DEFAULT: {
126         if (authManager.authorize(user, table, family, qualifier, Permission.Action.READ) ||
127             authManager.authorize(user, table, cell, Permission.Action.READ)) {
128           return ReturnCode.INCLUDE;
129         }
130       }
131       break;
132       default:
133         throw new RuntimeException("Unhandled strategy " + strategy);
134     }
135 
136     return ReturnCode.SKIP;
137   }
138 
139   @Override
140   public void reset() throws IOException {
141     this.prevFam.unset();
142     this.prevQual.unset();
143     this.familyMaxVersions = 0;
144     this.currentVersions = 0;
145   }
146 
147   /**
148    * @return The filter serialized using pb
149    */
150   public byte [] toByteArray() {
151     // no implementation, server-side use only
152     throw new UnsupportedOperationException(
153       "Serialization not supported.  Intended for server-side use only.");
154   }
155 
156   /**
157    * @param pbBytes A pb serialized {@link AccessControlFilter} instance
158    * @return An instance of {@link AccessControlFilter} made from <code>bytes</code>
159    * @throws org.apache.hadoop.hbase.exceptions.DeserializationException
160    * @see {@link #toByteArray()}
161    */
162   public static AccessControlFilter parseFrom(final byte [] pbBytes)
163   throws DeserializationException {
164     // no implementation, server-side use only
165     throw new UnsupportedOperationException(
166       "Serialization not supported.  Intended for server-side use only.");
167   }
168 }