/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.security.access; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.util.Bytes; /** * Represents an authorization for access for the given actions, optionally * restricted to the given column family or column qualifier, over the * given table. If the family property is <code>null</code>, it implies * full table access. */ @InterfaceAudience.Private public class TablePermission extends Permission { private TableName table; private byte[] family; private byte[] qualifier; //TODO refactor this class //we need to refacting this into three classes (Global, Table, Namespace) private String namespace; /** Nullary constructor for Writable, do not use */ public TablePermission() { super(); } /** * Create a new permission for the given table and (optionally) column family, * allowing the given actions. * @param table the table * @param family the family, can be null if a global permission on the table * @param assigned the list of allowed actions */ public TablePermission(TableName table, byte[] family, Action... assigned) { this(table, family, null, assigned); } /** * Creates a new permission for the given table, restricted to the given * column family and qualifier, allowing the assigned actions to be performed. * @param table the table * @param family the family, can be null if a global permission on the table * @param assigned the list of allowed actions */ public TablePermission(TableName table, byte[] family, byte[] qualifier, Action... assigned) { super(assigned); this.table = table; this.family = family; this.qualifier = qualifier; } /** * Creates a new permission for the given table, family and column qualifier, * allowing the actions matching the provided byte codes to be performed. * @param table the table * @param family the family, can be null if a global permission on the table * @param actionCodes the list of allowed action codes */ public TablePermission(TableName table, byte[] family, byte[] qualifier, byte[] actionCodes) { super(actionCodes); this.table = table; this.family = family; this.qualifier = qualifier; } /** * Creates a new permission for the given namespace or table, restricted to the given * column family and qualifier, allowing the assigned actions to be performed. * @param namespace * @param table the table * @param family the family, can be null if a global permission on the table * @param assigned the list of allowed actions */ public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier, Action... assigned) { super(assigned); this.namespace = namespace; this.table = table; this.family = family; this.qualifier = qualifier; } /** * Creates a new permission for the given namespace or table, family and column qualifier, * allowing the actions matching the provided byte codes to be performed. * @param namespace * @param table the table * @param family the family, can be null if a global permission on the table * @param actionCodes the list of allowed action codes */ public TablePermission(String namespace, TableName table, byte[] family, byte[] qualifier, byte[] actionCodes) { super(actionCodes); this.namespace = namespace; this.table = table; this.family = family; this.qualifier = qualifier; } /** * Creates a new permission for the given namespace, * allowing the actions matching the provided byte codes to be performed. * @param namespace * @param actionCodes the list of allowed action codes */ public TablePermission(String namespace, byte[] actionCodes) { super(actionCodes); this.namespace = namespace; } /** * Create a new permission for the given namespace, * allowing the given actions. * @param namespace * @param assigned the list of allowed actions */ public TablePermission(String namespace, Action... assigned) { super(assigned); this.namespace = namespace; } public boolean hasTable() { return table != null; } public TableName getTableName() { return table; } public void setTableName(TableName table) { this.table = table; } public boolean hasFamily() { return family != null; } public byte[] getFamily() { return family; } public boolean hasQualifier() { return qualifier != null; } public byte[] getQualifier() { return qualifier; } public boolean hasNamespace() { return namespace != null; } public String getNamespace() { return namespace; } /** * Checks that a given table operation is authorized by this permission * instance. * * @param namespace the namespace where the operation is being performed * @param action the action being requested * @return <code>true</code> if the action within the given scope is allowed * by this permission, <code>false</code> */ public boolean implies(String namespace, Action action) { if (this.namespace == null || !this.namespace.equals(namespace)) { return false; } // check actions return super.implies(action); } /** * Checks that a given table operation is authorized by this permission * instance. * * @param table the table where the operation is being performed * @param family the column family to which the operation is restricted, * if <code>null</code> implies "all" * @param qualifier the column qualifier to which the action is restricted, * if <code>null</code> implies "all" * @param action the action being requested * @return <code>true</code> if the action within the given scope is allowed * by this permission, <code>false</code> */ public boolean implies(TableName table, byte[] family, byte[] qualifier, Action action) { if (this.table == null || !this.table.equals(table)) { return false; } if (this.family != null && (family == null || !Bytes.equals(this.family, family))) { return false; } if (this.qualifier != null && (qualifier == null || !Bytes.equals(this.qualifier, qualifier))) { return false; } // check actions return super.implies(action); } /** * Checks if this permission grants access to perform the given action on * the given table and key value. * @param table the table on which the operation is being performed * @param kv the KeyValue on which the operation is being requested * @param action the action requested * @return <code>true</code> if the action is allowed over the given scope * by this permission, otherwise <code>false</code> */ public boolean implies(TableName table, KeyValue kv, Action action) { if (this.table == null || !this.table.equals(table)) { return false; } if (family != null && !(CellUtil.matchingFamily(kv, family))) { return false; } if (qualifier != null && !(CellUtil.matchingQualifier(kv, qualifier))) { return false; } // check actions return super.implies(action); } /** * Returns <code>true</code> if this permission matches the given column * family at least. This only indicates a partial match against the table * and column family, however, and does not guarantee that implies() for the * column same family would return <code>true</code>. In the case of a * column-qualifier specific permission, for example, implies() would still * return false. */ public boolean matchesFamily(TableName table, byte[] family, Action action) { if (this.table == null || !this.table.equals(table)) { return false; } if (this.family != null && (family == null || !Bytes.equals(this.family, family))) { return false; } // ignore qualifier // check actions return super.implies(action); } /** * Returns if the given permission matches the given qualifier. * @param table the table name to match * @param family the column family to match * @param qualifier the qualifier name to match * @param action the action requested * @return <code>true</code> if the table, family and qualifier match, * otherwise <code>false</code> */ public boolean matchesFamilyQualifier(TableName table, byte[] family, byte[] qualifier, Action action) { if (!matchesFamily(table, family, action)) { return false; } else { if (this.qualifier != null && (qualifier == null || !Bytes.equals(this.qualifier, qualifier))) { return false; } } return super.implies(action); } public boolean tableFieldsEqual(TablePermission other){ if (!(((table == null && other.getTableName() == null) || (table != null && table.equals(other.getTableName()))) && ((family == null && other.getFamily() == null) || Bytes.equals(family, other.getFamily())) && ((qualifier == null && other.getQualifier() == null) || Bytes.equals(qualifier, other.getQualifier())) && ((namespace == null && other.getNamespace() == null) || (namespace != null && namespace.equals(other.getNamespace()))) )) { return false; } return true; } @Override @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH", justification="Passed on construction except on constructor not to be used") public boolean equals(Object obj) { if (!(obj instanceof TablePermission)) { return false; } TablePermission other = (TablePermission)obj; if(!this.tableFieldsEqual(other)){ return false; } // check actions return super.equals(other); } @Override public int hashCode() { final int prime = 37; int result = super.hashCode(); if (table != null) { result = prime * result + table.hashCode(); } if (family != null) { result = prime * result + Bytes.hashCode(family); } if (qualifier != null) { result = prime * result + Bytes.hashCode(qualifier); } if (namespace != null) { result = prime * result + namespace.hashCode(); } return result; } @Override public String toString() { StringBuilder str = new StringBuilder("[TablePermission: "); if(namespace != null) { str.append("namespace=").append(namespace) .append(", "); } if(table != null) { str.append("table=").append(table) .append(", family=") .append(family == null ? null : Bytes.toString(family)) .append(", qualifier=") .append(qualifier == null ? null : Bytes.toString(qualifier)) .append(", "); } if (actions != null) { str.append("actions="); for (int i=0; i<actions.length; i++) { if (i > 0) str.append(","); if (actions[i] != null) str.append(actions[i].toString()); else str.append("NULL"); } } str.append("]"); return str.toString(); } @Override public void readFields(DataInput in) throws IOException { super.readFields(in); byte[] tableBytes = Bytes.readByteArray(in); if(tableBytes.length > 0) { table = TableName.valueOf(tableBytes); } if (in.readBoolean()) { family = Bytes.readByteArray(in); } if (in.readBoolean()) { qualifier = Bytes.readByteArray(in); } if(in.readBoolean()) { namespace = Bytes.toString(Bytes.readByteArray(in)); } } @Override public void write(DataOutput out) throws IOException { super.write(out); // Explicitly writing null to maintain se/deserialize backward compatibility. Bytes.writeByteArray(out, (table == null) ? null : table.getName()); out.writeBoolean(family != null); if (family != null) { Bytes.writeByteArray(out, family); } out.writeBoolean(qualifier != null); if (qualifier != null) { Bytes.writeByteArray(out, qualifier); } out.writeBoolean(namespace != null); if(namespace != null) { Bytes.writeByteArray(out, Bytes.toBytes(namespace)); } } }