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