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