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 org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.TableName;
25  import org.apache.hadoop.hbase.KeyValue;
26  import org.apache.hadoop.hbase.util.Bytes;
27  
28  import java.io.DataInput;
29  import java.io.DataOutput;
30  import java.io.IOException;
31  
32  /**
33   * Represents an authorization for access for the given actions, optionally
34   * restricted to the given column family or column qualifier, over the
35   * given table.  If the family property is <code>null</code>, it implies
36   * full table access.
37   */
38  @InterfaceAudience.Private
39  public class TablePermission extends Permission {
40    private static Log LOG = LogFactory.getLog(TablePermission.class);
41  
42    private TableName table;
43    private byte[] family;
44    private byte[] qualifier;
45  
46    //TODO refactor this class
47    //we need to refacting this into three classes (Global, Table, Namespace)
48    private String namespace;
49  
50    /** Nullary constructor for Writable, do not use */
51    public TablePermission() {
52      super();
53    }
54  
55    /**
56     * Create a new permission for the given table and (optionally) column family,
57     * allowing the given actions.
58     * @param table the table
59     * @param family the family, can be null if a global permission on the table
60     * @param assigned the list of allowed actions
61     */
62    public TablePermission(TableName table, byte[] family, Action... assigned) {
63      this(table, family, null, assigned);
64    }
65  
66    /**
67     * Creates a new permission for the given table, restricted to the given
68     * column family and qualifier, allowing the assigned actions to be performed.
69     * @param table the table
70     * @param family the family, can be null if a global permission on the table
71     * @param assigned the list of allowed actions
72     */
73    public TablePermission(TableName table, byte[] family, byte[] qualifier,
74        Action... assigned) {
75      super(assigned);
76      this.table = table;
77      this.family = family;
78      this.qualifier = qualifier;
79    }
80  
81    /**
82     * Creates a new permission for the given table, family and column qualifier,
83     * allowing the actions matching the provided byte codes to be performed.
84     * @param table the table
85     * @param family the family, can be null if a global permission on the table
86     * @param actionCodes the list of allowed action codes
87     */
88    public TablePermission(TableName table, byte[] family, byte[] qualifier,
89        byte[] actionCodes) {
90      super(actionCodes);
91      this.table = table;
92      this.family = family;
93      this.qualifier = qualifier;
94    }
95  
96    /**
97     * Creates a new permission for the given namespace or table, restricted to the given
98     * column family and qualifier, allowing the assigned actions to be performed.
99     * @param namespace
100    * @param table the table
101    * @param family the family, can be null if a global permission on the table
102    * @param assigned the list of allowed actions
103    */
104   public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier,
105       Action... assigned) {
106     super(assigned);
107     this.namespace = namespace;
108     this.table = table;
109     this.family = family;
110     this.qualifier = qualifier;
111   }
112 
113   /**
114    * Creates a new permission for the given namespace or table, family and column qualifier,
115    * allowing the actions matching the provided byte codes to be performed.
116    * @param namespace
117    * @param table the table
118    * @param family the family, can be null if a global permission on the table
119    * @param actionCodes the list of allowed action codes
120    */
121   public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier,
122       byte[] actionCodes) {
123     super(actionCodes);
124     this.namespace = namespace;
125     this.table = table;
126     this.family = family;
127     this.qualifier = qualifier;
128   }
129 
130   /**
131    * Creates a new permission for the given namespace,
132    * allowing the actions matching the provided byte codes to be performed.
133    * @param namespace
134    * @param actionCodes the list of allowed action codes
135    */
136   public TablePermission(String namespace, byte[] actionCodes) {
137     super(actionCodes);
138     this.namespace = namespace;
139   }
140 
141   /**
142    * Create a new permission for the given namespace,
143    * allowing the given actions.
144    * @param namespace
145    * @param assigned the list of allowed actions
146    */
147   public TablePermission(String namespace, Action... assigned) {
148     super(assigned);
149     this.namespace = namespace;
150   }
151 
152   public boolean hasTable() {
153     return table != null;
154   }
155 
156   public TableName getTableName() {
157     return table;
158   }
159 
160   public void setTableName(TableName table) {
161     this.table = table;
162   }
163 
164   public boolean hasFamily() {
165     return family != null;
166   }
167 
168   public byte[] getFamily() {
169     return family;
170   }
171 
172   public boolean hasQualifier() {
173     return qualifier != null;
174   }
175 
176   public byte[] getQualifier() {
177     return qualifier;
178   }
179 
180   public boolean hasNamespace() {
181     return namespace != null;
182   }
183 
184   public String getNamespace() {
185     return namespace;
186   }
187 
188   /**
189    * Checks that a given table operation is authorized by this permission
190    * instance.
191    *
192    * @param namespace the namespace where the operation is being performed
193    * @param action the action being requested
194    * @return <code>true</code> if the action within the given scope is allowed
195    *   by this permission, <code>false</code>
196    */
197   public boolean implies(String namespace, Action action) {
198     if (!this.namespace.equals(namespace)) {
199       return false;
200     }
201 
202     // check actions
203     return super.implies(action);
204   }
205 
206   /**
207    * Checks that a given table operation is authorized by this permission
208    * instance.
209    *
210    * @param table the table where the operation is being performed
211    * @param family the column family to which the operation is restricted,
212    *   if <code>null</code> implies "all"
213    * @param qualifier the column qualifier to which the action is restricted,
214    *   if <code>null</code> implies "all"
215    * @param action the action being requested
216    * @return <code>true</code> if the action within the given scope is allowed
217    *   by this permission, <code>false</code>
218    */
219   public boolean implies(TableName table, byte[] family, byte[] qualifier,
220       Action action) {
221     if (!this.table.equals(table)) {
222       return false;
223     }
224 
225     if (this.family != null &&
226         (family == null ||
227          !Bytes.equals(this.family, family))) {
228       return false;
229     }
230 
231     if (this.qualifier != null &&
232         (qualifier == null ||
233          !Bytes.equals(this.qualifier, qualifier))) {
234       return false;
235     }
236 
237     // check actions
238     return super.implies(action);
239   }
240 
241   /**
242    * Checks if this permission grants access to perform the given action on
243    * the given table and key value.
244    * @param table the table on which the operation is being performed
245    * @param kv the KeyValue on which the operation is being requested
246    * @param action the action requested
247    * @return <code>true</code> if the action is allowed over the given scope
248    *   by this permission, otherwise <code>false</code>
249    */
250   public boolean implies(TableName table, KeyValue kv, Action action) {
251     if (!this.table.equals(table)) {
252       return false;
253     }
254 
255     if (family != null &&
256         (Bytes.compareTo(family, 0, family.length,
257             kv.getFamilyArray(), kv.getFamilyOffset(), kv.getFamilyLength()) != 0)) {
258       return false;
259     }
260 
261     if (qualifier != null &&
262         (Bytes.compareTo(qualifier, 0, qualifier.length,
263             kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength()) != 0)) {
264       return false;
265     }
266 
267     // check actions
268     return super.implies(action);
269   }
270 
271   /**
272    * Returns <code>true</code> if this permission matches the given column
273    * family at least.  This only indicates a partial match against the table
274    * and column family, however, and does not guarantee that implies() for the
275    * column same family would return <code>true</code>.  In the case of a
276    * column-qualifier specific permission, for example, implies() would still
277    * return false.
278    */
279   public boolean matchesFamily(TableName table, byte[] family, Action action) {
280     if (!this.table.equals(table)) {
281       return false;
282     }
283 
284     if (this.family != null &&
285         (family == null ||
286          !Bytes.equals(this.family, family))) {
287       return false;
288     }
289 
290     // ignore qualifier
291     // check actions
292     return super.implies(action);
293   }
294 
295   /**
296    * Returns if the given permission matches the given qualifier.
297    * @param table the table name to match
298    * @param family the column family to match
299    * @param qualifier the qualifier name to match
300    * @param action the action requested
301    * @return <code>true</code> if the table, family and qualifier match,
302    *   otherwise <code>false</code>
303    */
304   public boolean matchesFamilyQualifier(TableName table, byte[] family, byte[] qualifier,
305                                 Action action) {
306     if (!matchesFamily(table, family, action)) {
307       return false;
308     } else {
309       if (this.qualifier != null &&
310           (qualifier == null ||
311            !Bytes.equals(this.qualifier, qualifier))) {
312         return false;
313       }
314     }
315     return super.implies(action);
316   }
317 
318   @Override
319   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH",
320     justification="Passed on construction except on constructor not to be used")
321   public boolean equals(Object obj) {
322     if (!(obj instanceof TablePermission)) {
323       return false;
324     }
325     TablePermission other = (TablePermission)obj;
326 
327     if (!(table.equals(other.getTableName()) &&
328         ((family == null && other.getFamily() == null) ||
329          Bytes.equals(family, other.getFamily())) &&
330         ((qualifier == null && other.getQualifier() == null) ||
331          Bytes.equals(qualifier, other.getQualifier())) &&
332         ((namespace == null && other.getNamespace() == null) ||
333          (namespace != null && namespace.equals(other.getNamespace())))
334        )) {
335       return false;
336     }
337 
338     // check actions
339     return super.equals(other);
340   }
341 
342   @Override
343   public int hashCode() {
344     final int prime = 37;
345     int result = super.hashCode();
346     if (table != null) {
347       result = prime * result + table.hashCode();
348     }
349     if (family != null) {
350       result = prime * result + Bytes.hashCode(family);
351     }
352     if (qualifier != null) {
353       result = prime * result + Bytes.hashCode(qualifier);
354     }
355     if (namespace != null) {
356       result = prime * result + namespace.hashCode();
357     }
358     return result;
359   }
360 
361   @Override
362   public String toString() {
363     StringBuilder str = new StringBuilder("[TablePermission: ");
364     if(namespace != null) {
365       str.append("namespace=").append(namespace)
366          .append(", ");
367     }
368     else if(table != null) {
369        str.append("table=").append(table)
370           .append(", family=")
371           .append(family == null ? null : Bytes.toString(family))
372           .append(", qualifier=")
373           .append(qualifier == null ? null : Bytes.toString(qualifier))
374           .append(", ");
375     } else {
376       str.append("actions=");
377     }
378     if (actions != null) {
379       for (int i=0; i<actions.length; i++) {
380         if (i > 0)
381           str.append(",");
382         if (actions[i] != null)
383           str.append(actions[i].toString());
384         else
385           str.append("NULL");
386       }
387     }
388     str.append("]");
389 
390     return str.toString();
391   }
392 
393   @Override
394   public void readFields(DataInput in) throws IOException {
395     super.readFields(in);
396     byte[] tableBytes = Bytes.readByteArray(in);
397     table = TableName.valueOf(tableBytes);
398     if (in.readBoolean()) {
399       family = Bytes.readByteArray(in);
400     }
401     if (in.readBoolean()) {
402       qualifier = Bytes.readByteArray(in);
403     }
404     if(in.readBoolean()) {
405       namespace = Bytes.toString(Bytes.readByteArray(in));
406     }
407   }
408 
409   @Override
410   public void write(DataOutput out) throws IOException {
411     super.write(out);
412     Bytes.writeByteArray(out, table.getName());
413     out.writeBoolean(family != null);
414     if (family != null) {
415       Bytes.writeByteArray(out, family);
416     }
417     out.writeBoolean(qualifier != null);
418     if (qualifier != null) {
419       Bytes.writeByteArray(out, qualifier);
420     }
421     out.writeBoolean(namespace != null);
422     if(namespace != null) {
423       Bytes.writeByteArray(out, Bytes.toBytes(namespace));
424     }
425   }
426 }