001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.hbase.security.access;
020
021import java.io.DataInput;
022import java.io.DataOutput;
023import java.io.IOException;
024
025import org.apache.hadoop.hbase.CellUtil;
026import org.apache.hadoop.hbase.KeyValue;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.apache.hadoop.hbase.util.Bytes;
030
031/**
032 * Represents an authorization for access for the given actions, optionally
033 * restricted to the given column family or column qualifier, over the
034 * given table.  If the family property is <code>null</code>, it implies
035 * full table access.
036 */
037@InterfaceAudience.Private
038public class TablePermission extends Permission {
039
040  private TableName table;
041  private byte[] family;
042  private byte[] qualifier;
043
044  //TODO refactor this class
045  //we need to refacting this into three classes (Global, Table, Namespace)
046  private String namespace;
047
048  /** Nullary constructor for Writable, do not use */
049  public TablePermission() {
050    super();
051  }
052
053  /**
054   * Create a new permission for the given table and (optionally) column family,
055   * allowing the given actions.
056   * @param table the table
057   * @param family the family, can be null if a global permission on the table
058   * @param assigned the list of allowed actions
059   */
060  public TablePermission(TableName table, byte[] family, Action... assigned) {
061    this(table, family, null, assigned);
062  }
063
064  /**
065   * Creates a new permission for the given table, restricted to the given
066   * column family and qualifier, allowing the assigned actions to be performed.
067   * @param table the table
068   * @param family the family, can be null if a global permission on the table
069   * @param assigned the list of allowed actions
070   */
071  public TablePermission(TableName table, byte[] family, byte[] qualifier,
072      Action... assigned) {
073    super(assigned);
074    this.table = table;
075    this.family = family;
076    this.qualifier = qualifier;
077  }
078
079  /**
080   * Creates a new permission for the given table, family and column qualifier,
081   * allowing the actions matching the provided byte codes to be performed.
082   * @param table the table
083   * @param family the family, can be null if a global permission on the table
084   * @param actionCodes the list of allowed action codes
085   */
086  public TablePermission(TableName table, byte[] family, byte[] qualifier,
087      byte[] actionCodes) {
088    super(actionCodes);
089    this.table = table;
090    this.family = family;
091    this.qualifier = qualifier;
092  }
093
094  /**
095   * Creates a new permission for the given namespace or table, restricted to the given
096   * column family and qualifier, allowing the assigned actions to be performed.
097   * @param namespace
098   * @param table the table
099   * @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 void setTableName(TableName table) {
159    this.table = table;
160  }
161
162  public boolean hasFamily() {
163    return family != null;
164  }
165
166  public byte[] getFamily() {
167    return family;
168  }
169
170  public boolean hasQualifier() {
171    return qualifier != null;
172  }
173
174  public byte[] getQualifier() {
175    return qualifier;
176  }
177
178  public boolean hasNamespace() {
179    return namespace != null;
180  }
181
182  public String getNamespace() {
183    return namespace;
184  }
185
186  /**
187   * Checks that a given table operation is authorized by this permission
188   * instance.
189   *
190   * @param namespace the namespace where the operation is being performed
191   * @param action the action being requested
192   * @return <code>true</code> if the action within the given scope is allowed
193   *   by this permission, <code>false</code>
194   */
195  public boolean implies(String namespace, Action action) {
196    if (this.namespace == null || !this.namespace.equals(namespace)) {
197      return false;
198    }
199
200    // check actions
201    return super.implies(action);
202  }
203
204  /**
205   * Checks that a given table operation is authorized by this permission
206   * instance.
207   *
208   * @param table the table where the operation is being performed
209   * @param family the column family to which the operation is restricted,
210   *   if <code>null</code> implies "all"
211   * @param qualifier the column qualifier to which the action is restricted,
212   *   if <code>null</code> implies "all"
213   * @param action the action being requested
214   * @return <code>true</code> if the action within the given scope is allowed
215   *   by this permission, <code>false</code>
216   */
217  public boolean implies(TableName table, byte[] family, byte[] qualifier,
218      Action action) {
219    if (this.table == null || !this.table.equals(table)) {
220      return false;
221    }
222
223    if (this.family != null &&
224        (family == null ||
225         !Bytes.equals(this.family, family))) {
226      return false;
227    }
228
229    if (this.qualifier != null &&
230        (qualifier == null ||
231         !Bytes.equals(this.qualifier, qualifier))) {
232      return false;
233    }
234
235    // check actions
236    return super.implies(action);
237  }
238
239  /**
240   * Checks if this permission grants access to perform the given action on
241   * the given table and key value.
242   * @param table the table on which the operation is being performed
243   * @param kv the KeyValue on which the operation is being requested
244   * @param action the action requested
245   * @return <code>true</code> if the action is allowed over the given scope
246   *   by this permission, otherwise <code>false</code>
247   */
248  public boolean implies(TableName table, KeyValue kv, Action action) {
249    if (this.table == null || !this.table.equals(table)) {
250      return false;
251    }
252
253    if (family != null && !(CellUtil.matchingFamily(kv, family))) {
254      return false;
255    }
256
257    if (qualifier != null && !(CellUtil.matchingQualifier(kv, qualifier))) {
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 == null || !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  public boolean tableFieldsEqual(TablePermission other){
313    if (!(((table == null && other.getTableName() == null) ||
314           (table != null && table.equals(other.getTableName()))) &&
315         ((family == null && other.getFamily() == null) ||
316           Bytes.equals(family, other.getFamily())) &&
317         ((qualifier == null && other.getQualifier() == null) ||
318          Bytes.equals(qualifier, other.getQualifier())) &&
319         ((namespace == null && other.getNamespace() == null) ||
320          (namespace != null && namespace.equals(other.getNamespace())))
321    )) {
322      return false;
323    }
324    return true;
325  }
326
327  @Override
328  @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH",
329    justification="Passed on construction except on constructor not to be used")
330  public boolean equals(Object obj) {
331    if (!(obj instanceof TablePermission)) {
332      return false;
333    }
334    TablePermission other = (TablePermission)obj;
335
336    if(!this.tableFieldsEqual(other)){
337      return false;
338    }
339
340    // check actions
341    return super.equals(other);
342  }
343
344  @Override
345  public int hashCode() {
346    final int prime = 37;
347    int result = super.hashCode();
348    if (table != null) {
349      result = prime * result + table.hashCode();
350    }
351    if (family != null) {
352      result = prime * result + Bytes.hashCode(family);
353    }
354    if (qualifier != null) {
355      result = prime * result + Bytes.hashCode(qualifier);
356    }
357    if (namespace != null) {
358      result = prime * result + namespace.hashCode();
359    }
360    return result;
361  }
362
363  @Override
364  public String toString() {
365    StringBuilder str = new StringBuilder("[TablePermission: ");
366    if(namespace != null) {
367      str.append("namespace=").append(namespace)
368         .append(", ");
369    }
370    if(table != null) {
371       str.append("table=").append(table)
372          .append(", family=")
373          .append(family == null ? null : Bytes.toString(family))
374          .append(", qualifier=")
375          .append(qualifier == null ? null : Bytes.toString(qualifier))
376          .append(", ");
377    }
378    if (actions != null) {
379      str.append("actions=");
380      for (int i=0; i<actions.length; i++) {
381        if (i > 0)
382          str.append(",");
383        if (actions[i] != null)
384          str.append(actions[i].toString());
385        else
386          str.append("NULL");
387      }
388    }
389    str.append("]");
390
391    return str.toString();
392  }
393
394  @Override
395  public void readFields(DataInput in) throws IOException {
396    super.readFields(in);
397    byte[] tableBytes = Bytes.readByteArray(in);
398    if(tableBytes.length > 0) {
399      table = TableName.valueOf(tableBytes);
400    }
401    if (in.readBoolean()) {
402      family = Bytes.readByteArray(in);
403    }
404    if (in.readBoolean()) {
405      qualifier = Bytes.readByteArray(in);
406    }
407    if(in.readBoolean()) {
408      namespace = Bytes.toString(Bytes.readByteArray(in));
409    }
410  }
411
412  @Override
413  public void write(DataOutput out) throws IOException {
414    super.write(out);
415    // Explicitly writing null to maintain se/deserialize backward compatibility.
416    Bytes.writeByteArray(out, (table == null) ? null : table.getName());
417    out.writeBoolean(family != null);
418    if (family != null) {
419      Bytes.writeByteArray(out, family);
420    }
421    out.writeBoolean(qualifier != null);
422    if (qualifier != null) {
423      Bytes.writeByteArray(out, qualifier);
424    }
425    out.writeBoolean(namespace != null);
426    if(namespace != null) {
427      Bytes.writeByteArray(out, Bytes.toBytes(namespace));
428    }
429  }
430}