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 */
018package org.apache.hadoop.hbase.security.access;
019
020import java.io.DataInput;
021import java.io.DataOutput;
022import java.io.IOException;
023import org.apache.hadoop.hbase.CellUtil;
024import org.apache.hadoop.hbase.KeyValue;
025import org.apache.hadoop.hbase.TableName;
026import org.apache.hadoop.hbase.util.Bytes;
027import org.apache.yetus.audience.InterfaceAudience;
028
029/**
030 * Represents an authorization for access for the given actions, optionally restricted to the given
031 * column family or column qualifier, over the given table. If the family property is
032 * <code>null</code>, it implies full table access.
033 */
034@InterfaceAudience.Public
035public class TablePermission extends Permission {
036
037  private TableName table;
038  private byte[] family;
039  private byte[] qualifier;
040
041  /**
042   * Construct a table:family:qualifier permission.
043   * @param table     table name
044   * @param family    family name
045   * @param qualifier qualifier name
046   * @param assigned  assigned actions
047   */
048  TablePermission(TableName table, byte[] family, byte[] qualifier, Action... assigned) {
049    super(assigned);
050    this.table = table;
051    this.family = family;
052    this.qualifier = qualifier;
053    this.scope = Scope.TABLE;
054  }
055
056  public TableName getTableName() {
057    return table;
058  }
059
060  public boolean hasFamily() {
061    return family != null;
062  }
063
064  public byte[] getFamily() {
065    return family;
066  }
067
068  public boolean hasQualifier() {
069    return qualifier != null;
070  }
071
072  public byte[] getQualifier() {
073    return qualifier;
074  }
075
076  public String getNamespace() {
077    return table.getNamespaceAsString();
078  }
079
080  /**
081   * Check if given action can performs on given table:family:qualifier.
082   * @param table     table name
083   * @param family    family name
084   * @param qualifier qualifier name
085   * @param action    one of [Read, Write, Create, Exec, Admin]
086   * @return true if can, false otherwise
087   */
088  public boolean implies(TableName table, byte[] family, byte[] qualifier, Action action) {
089    if (failCheckTable(table)) {
090      return false;
091    }
092    if (failCheckFamily(family)) {
093      return false;
094    }
095    if (failCheckQualifier(qualifier)) {
096      return false;
097    }
098    return implies(action);
099  }
100
101  /**
102   * Check if given action can performs on given table:family.
103   * @param table  table name
104   * @param family family name
105   * @param action one of [Read, Write, Create, Exec, Admin]
106   * @return true if can, false otherwise
107   */
108  public boolean implies(TableName table, byte[] family, Action action) {
109    if (failCheckTable(table)) {
110      return false;
111    }
112    if (failCheckFamily(family)) {
113      return false;
114    }
115    return implies(action);
116  }
117
118  private boolean failCheckTable(TableName table) {
119    return this.table == null || !this.table.equals(table);
120  }
121
122  private boolean failCheckFamily(byte[] family) {
123    return this.family != null && (family == null || !Bytes.equals(this.family, family));
124  }
125
126  private boolean failCheckQualifier(byte[] qual) {
127    return this.qualifier != null && (qual == null || !Bytes.equals(this.qualifier, qual));
128  }
129
130  /**
131   * Checks if this permission grants access to perform the given action on the given table and key
132   * value.
133   * @param table  the table on which the operation is being performed
134   * @param kv     the KeyValue on which the operation is being requested
135   * @param action the action requested
136   * @return <code>true</code> if the action is allowed over the given scope by this permission,
137   *         otherwise <code>false</code>
138   */
139  public boolean implies(TableName table, KeyValue kv, Action action) {
140    if (failCheckTable(table)) {
141      return false;
142    }
143
144    if (family != null && !CellUtil.matchingFamily(kv, family)) {
145      return false;
146    }
147
148    if (qualifier != null && !CellUtil.matchingQualifier(kv, qualifier)) {
149      return false;
150    }
151
152    // check actions
153    return super.implies(action);
154  }
155
156  /**
157   * Check if fields of table in table permission equals.
158   * @param tp to be checked table permission
159   * @return true if equals, false otherwise
160   */
161  public boolean tableFieldsEqual(TablePermission tp) {
162    if (tp == null) {
163      return false;
164    }
165
166    boolean tEq = (table == null && tp.table == null) || (table != null && table.equals(tp.table));
167    boolean fEq = (family == null && tp.family == null) || Bytes.equals(family, tp.family);
168    boolean qEq =
169      (qualifier == null && tp.qualifier == null) || Bytes.equals(qualifier, tp.qualifier);
170    return tEq && fEq && qEq;
171  }
172
173  @Override
174  public boolean equalsExceptActions(Object obj) {
175    if (!(obj instanceof TablePermission)) {
176      return false;
177    }
178    TablePermission other = (TablePermission) obj;
179    return tableFieldsEqual(other);
180  }
181
182  @Override
183  public boolean equals(Object obj) {
184    return equalsExceptActions(obj) && super.equals(obj);
185  }
186
187  @Override
188  public int hashCode() {
189    final int prime = 37;
190    int result = super.hashCode();
191    if (table != null) {
192      result = prime * result + table.hashCode();
193    }
194    if (family != null) {
195      result = prime * result + Bytes.hashCode(family);
196    }
197    if (qualifier != null) {
198      result = prime * result + Bytes.hashCode(qualifier);
199    }
200    return result;
201  }
202
203  @Override
204  public String toString() {
205    return "[TablePermission: " + rawExpression() + "]";
206  }
207
208  @Override
209  protected String rawExpression() {
210    StringBuilder raw = new StringBuilder();
211    if (table != null) {
212      raw.append("table=").append(table).append(", family=")
213        .append(family == null ? null : Bytes.toString(family)).append(", qualifier=")
214        .append(qualifier == null ? null : Bytes.toString(qualifier)).append(", ");
215    }
216    return raw.toString() + super.rawExpression();
217  }
218
219  @Override
220  public void readFields(DataInput in) throws IOException {
221    super.readFields(in);
222    byte[] tableBytes = Bytes.readByteArray(in);
223    if (tableBytes.length > 0) {
224      table = TableName.valueOf(tableBytes);
225    }
226    if (in.readBoolean()) {
227      family = Bytes.readByteArray(in);
228    }
229    if (in.readBoolean()) {
230      qualifier = Bytes.readByteArray(in);
231    }
232  }
233
234  @Override
235  public void write(DataOutput out) throws IOException {
236    super.write(out);
237    // Explicitly writing null to maintain se/deserialize backward compatibility.
238    Bytes.writeByteArray(out, table == null ? null : table.getName());
239    out.writeBoolean(family != null);
240    if (family != null) {
241      Bytes.writeByteArray(out, family);
242    }
243    out.writeBoolean(qualifier != null);
244    if (qualifier != null) {
245      Bytes.writeByteArray(out, qualifier);
246    }
247  }
248}