HBASE-6386 Audit log messages do not include column family / qualifier information consistently (Marcelo Vanzin)

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1430691 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
mbertozzi 2013-01-09 05:45:33 +00:00
parent 4ab4e075bb
commit 369b436bff
2 changed files with 154 additions and 67 deletions

View File

@ -64,6 +64,7 @@ import org.apache.hadoop.hbase.security.access.Permission.Action;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.MapMaker;
@ -198,12 +199,14 @@ public class AccessController extends BaseRegionObserver
// this is a very common operation, so deal with it quickly.
if (hri.isRootRegion() || hri.isMetaRegion()) {
if (permRequest == Permission.Action.READ) {
return AuthResult.allow(request, "All users allowed", user, permRequest, tableName);
return AuthResult.allow(request, "All users allowed", user,
permRequest, tableName, families);
}
}
if (user == null) {
return AuthResult.deny(request, "No user associated with request!", null, permRequest, tableName);
return AuthResult.deny(request, "No user associated with request!", null,
permRequest, tableName, families);
}
// Users with CREATE/ADMIN rights need to modify .META. and _acl_ table
@ -217,12 +220,14 @@ public class AccessController extends BaseRegionObserver
(authManager.authorize(user, Permission.Action.CREATE) ||
authManager.authorize(user, Permission.Action.ADMIN)))
{
return AuthResult.allow(request, "Table permission granted", user, permRequest, tableName);
return AuthResult.allow(request, "Table permission granted", user,
permRequest, tableName, families);
}
// 2. check for the table-level, if successful we can short-circuit
if (authManager.authorize(user, tableName, (byte[])null, permRequest)) {
return AuthResult.allow(request, "Table permission granted", user, permRequest, tableName);
return AuthResult.allow(request, "Table permission granted", user,
permRequest, tableName, families);
}
// 3. check permissions against the requested families
@ -244,7 +249,7 @@ public class AccessController extends BaseRegionObserver
if (!authManager.authorize(user, tableName, family.getKey(),
qualifier, permRequest)) {
return AuthResult.deny(request, "Failed qualifier check", user,
permRequest, tableName, family.getKey(), qualifier);
permRequest, tableName, makeFamilyMap(family.getKey(), qualifier));
}
}
} else if (family.getValue() instanceof List) { // List<KeyValue>
@ -253,25 +258,25 @@ public class AccessController extends BaseRegionObserver
if (!authManager.authorize(user, tableName, family.getKey(),
kv.getQualifier(), permRequest)) {
return AuthResult.deny(request, "Failed qualifier check", user,
permRequest, tableName, family.getKey(), kv.getQualifier());
permRequest, tableName, makeFamilyMap(family.getKey(), kv.getQualifier()));
}
}
}
} else {
// no qualifiers and family-level check already failed
return AuthResult.deny(request, "Failed family check", user, permRequest,
tableName, family.getKey(), null);
tableName, makeFamilyMap(family.getKey(), null));
}
}
// all family checks passed
return AuthResult.allow(request, "All family checks passed", user, permRequest,
tableName);
tableName, families);
}
// 4. no families to check and table level access failed
return AuthResult.deny(request, "No families to check and table permission failed",
user, permRequest, tableName);
user, permRequest, tableName, families);
}
private void logResult(AuthResult result) {
@ -344,34 +349,15 @@ public class AccessController extends BaseRegionObserver
private void requirePermission(String request, Permission.Action perm) throws IOException {
User user = getActiveUser();
if (authManager.authorize(user, perm)) {
logResult(AuthResult.allow(request, "Global check allowed", user, perm, null));
logResult(AuthResult.allow(request, "Global check allowed", user, perm, null, null));
} else {
logResult(AuthResult.deny(request, "Global check failed", user, perm, null));
logResult(AuthResult.deny(request, "Global check failed", user, perm, null, null));
throw new AccessDeniedException("Insufficient permissions for user '" +
(user != null ? user.getShortName() : "null") +"' (global, action=" +
perm.toString() + ")");
}
}
/**
* Authorizes that the current user has permission to perform the given
* action on the set of table column families.
* @param perm Action that is required
* @param env The current coprocessor environment
* @param families The set of column families present/required in the request
* @throws AccessDeniedException if the authorization check failed
*/
private void requirePermission(String request, Permission.Action perm,
RegionCoprocessorEnvironment env, Collection<byte[]> families)
throws IOException {
// create a map of family-qualifier
HashMap<byte[], Set<byte[]>> familyMap = new HashMap<byte[], Set<byte[]>>();
for (byte[] family : families) {
familyMap.put(family, null);
}
requirePermission(request, perm, env, familyMap);
}
/**
* Authorizes that the current user has permission to perform the given
* action on the set of table column families.
@ -389,23 +375,35 @@ public class AccessController extends BaseRegionObserver
logResult(result);
if (!result.isAllowed()) {
StringBuilder sb = new StringBuilder("");
if ((families != null && families.size() > 0)) {
for (byte[] familyName : families.keySet()) {
if (sb.length() != 0) {
sb.append(", ");
}
sb.append(Bytes.toString(familyName));
}
}
throw new AccessDeniedException("Insufficient permissions (table=" +
env.getRegion().getTableDesc().getNameAsString()+
((families != null && families.size() > 0) ? ", family: " +
sb.toString() : "") + ", action=" +
result.toFamilyString() : "") + ", action=" +
perm.toString() + ")");
}
}
/**
* Checks that the user has the given global permission. The generated
* audit log message will contain context information for the operation
* being authorized, based on the given parameters.
* @param perm Action being requested
* @param tableName Affected table name.
* @param familiMap Affected column families.
*/
private void requireGlobalPermission(String request, Permission.Action perm, byte[] tableName,
Map<byte[], ? extends Collection<byte[]>> familyMap) throws IOException {
User user = getActiveUser();
if (authManager.authorize(user, perm)) {
logResult(AuthResult.allow(request, "Global check allowed", user, perm, tableName, familyMap));
} else {
logResult(AuthResult.deny(request, "Global check failed", user, perm, tableName, familyMap));
throw new AccessDeniedException("Insufficient permissions for user '" +
(user != null ? user.getShortName() : "null") +"' (global, action=" +
perm.toString() + ")");
}
}
/**
* Returns <code>true</code> if the current user is allowed the given action
* over at least one of the column qualifiers in the given column families.
@ -785,7 +783,7 @@ public class AccessController extends BaseRegionObserver
assert family != null;
//noinspection PrimitiveArrayArgumentToVariableArgMethod
requirePermission("getClosestRowBefore", Permission.Action.READ, c.getEnvironment(),
Lists.newArrayList(family));
makeFamilyMap(family, null));
}
@Override
@ -815,7 +813,7 @@ public class AccessController extends BaseRegionObserver
get.setFilter(filter);
}
logResult(AuthResult.allow("get", "Access allowed with filter", requestUser,
Permission.Action.READ, authResult.getTable()));
Permission.Action.READ, authResult.getTable(), get.getFamilyMap()));
} else {
logResult(authResult);
throw new AccessDeniedException("Insufficient permissions (table=" +
@ -831,7 +829,7 @@ public class AccessController extends BaseRegionObserver
public boolean preExists(final ObserverContext<RegionCoprocessorEnvironment> c,
final Get get, final boolean exists) throws IOException {
requirePermission("exists", Permission.Action.READ, c.getEnvironment(),
get.familySet());
get.getFamilyMap());
return exists;
}
@ -874,7 +872,7 @@ public class AccessController extends BaseRegionObserver
final CompareFilter.CompareOp compareOp,
final ByteArrayComparable comparator, final Put put,
final boolean result) throws IOException {
Collection<byte[]> familyMap = Arrays.asList(new byte[][]{family});
Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
requirePermission("checkAndPut", Permission.Action.READ, c.getEnvironment(), familyMap);
requirePermission("checkAndPut", Permission.Action.WRITE, c.getEnvironment(), familyMap);
return result;
@ -886,7 +884,7 @@ public class AccessController extends BaseRegionObserver
final CompareFilter.CompareOp compareOp,
final ByteArrayComparable comparator, final Delete delete,
final boolean result) throws IOException {
Collection<byte[]> familyMap = Arrays.asList(new byte[][]{family});
Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
requirePermission("checkAndDelete", Permission.Action.READ, c.getEnvironment(), familyMap);
requirePermission("checkAndDelete", Permission.Action.WRITE, c.getEnvironment(), familyMap);
return result;
@ -897,8 +895,8 @@ public class AccessController extends BaseRegionObserver
final byte [] row, final byte [] family, final byte [] qualifier,
final long amount, final boolean writeToWAL)
throws IOException {
requirePermission("incrementColumnValue", Permission.Action.WRITE, c.getEnvironment(),
Arrays.asList(new byte[][]{family}));
Map<byte[], ? extends Collection<byte[]>> familyMap = makeFamilyMap(family, qualifier);
requirePermission("incrementColumnValue", Permission.Action.WRITE, c.getEnvironment(), familyMap);
return -1;
}
@ -913,8 +911,11 @@ public class AccessController extends BaseRegionObserver
public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
final Increment increment)
throws IOException {
requirePermission("increment", Permission.Action.WRITE, c.getEnvironment(),
increment.getFamilyMap().keySet());
Map<byte[], Set<byte[]>> familyMap = Maps.newHashMap();
for (Map.Entry<byte[], ? extends Map<byte[], Long>> entry : increment.getFamilyMap().entrySet()) {
familyMap.put(entry.getKey(), entry.getValue().keySet());
}
requirePermission("increment", Permission.Action.WRITE, c.getEnvironment(), familyMap);
return null;
}
@ -945,7 +946,7 @@ public class AccessController extends BaseRegionObserver
scan.setFilter(filter);
}
logResult(AuthResult.allow("scannerOpen", "Access allowed with filter", user,
Permission.Action.READ, authResult.getTable()));
Permission.Action.READ, authResult.getTable(), scan.getFamilyMap()));
} else {
// no table/family level perms and no qualifier level perms, reject
logResult(authResult);
@ -1204,4 +1205,15 @@ public class AccessController extends BaseRegionObserver
throws IOException {
requirePermission("preStopRegionServer", Permission.Action.ADMIN);
}
private Map<byte[], ? extends Collection<byte[]>> makeFamilyMap(byte[] family,
byte[] qualifier) {
if (family == null) {
return null;
}
Map<byte[], Collection<byte[]>> familyMap = Maps.newHashMapWithExpectedSize(1);
familyMap.put(family, qualifier != null ? ImmutableSet.of(qualifier) : null);
return familyMap;
}
}

View File

@ -18,8 +18,12 @@
package org.apache.hadoop.hbase.security.access;
import java.util.Collection;
import java.util.Map;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.Bytes;
@ -32,14 +36,17 @@ import org.apache.hadoop.hbase.util.Bytes;
public class AuthResult {
private final boolean allowed;
private final byte[] table;
private final byte[] family;
private final byte[] qualifier;
private final Permission.Action action;
private final String request;
private final String reason;
private final User user;
public AuthResult(boolean allowed, String request, String reason, User user,
// "family" and "qualifier" should only be used if "families" is null.
private final byte[] family;
private final byte[] qualifier;
private final Map<byte[], ? extends Collection<?>> families;
public AuthResult(boolean allowed, String request, String reason, User user,
Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
this.allowed = allowed;
this.request = request;
@ -49,6 +56,21 @@ public class AuthResult {
this.family = family;
this.qualifier = qualifier;
this.action = action;
this.families = null;
}
public AuthResult(boolean allowed, String request, String reason, User user,
Permission.Action action, byte[] table,
Map<byte[], ? extends Collection<?>> families) {
this.allowed = allowed;
this.request = request;
this.reason = reason;
this.user = user;
this.table = table;
this.family = null;
this.qualifier = null;
this.action = action;
this.families = families;
}
public boolean isAllowed() {
@ -83,34 +105,87 @@ public class AuthResult {
return request;
}
String toFamilyString() {
StringBuilder sb = new StringBuilder();
if (families != null) {
boolean first = true;
for (Map.Entry<byte[], ? extends Collection<?>> entry : families.entrySet()) {
String familyName = Bytes.toString(entry.getKey());
if (entry.getValue() != null && !entry.getValue().isEmpty()) {
for (Object o : entry.getValue()) {
String qualifier;
if (o instanceof byte[]) {
qualifier = Bytes.toString((byte[])o);
} else if (o instanceof KeyValue) {
byte[] rawQualifier = ((KeyValue)o).getQualifier();
qualifier = Bytes.toString(rawQualifier);
} else {
// Shouldn't really reach this?
qualifier = o.toString();
}
if (!first) {
sb.append("|");
}
first = false;
sb.append(familyName).append(":").append(qualifier);
}
} else {
if (!first) {
sb.append("|");
}
first = false;
sb.append(familyName);
}
}
} else if (family != null) {
sb.append(Bytes.toString(family));
if (qualifier != null) {
sb.append(":").append(Bytes.toString(qualifier));
}
}
return sb.toString();
}
public String toContextString() {
return "(user=" + (user != null ? user.getName() : "UNKNOWN") + ", " +
"scope=" + (table == null ? "GLOBAL" : Bytes.toString(table)) + ", " +
"family=" + (family != null ? Bytes.toString(family) : "") + ", " +
"qualifer=" + (qualifier != null ? Bytes.toString(qualifier) : "") + ", " +
"action=" + (action != null ? action.toString() : "") + ")";
StringBuilder sb = new StringBuilder();
sb.append("(user=")
.append(user != null ? user.getName() : "UNKNOWN")
.append(", ");
sb.append("scope=")
.append(table == null ? "GLOBAL" : Bytes.toString(table))
.append(", ");
sb.append("family=")
.append(toFamilyString())
.append(", ");
sb.append("action=")
.append(action != null ? action.toString() : "")
.append(")");
return sb.toString();
}
public String toString() {
return "AuthResult" + toContextString();
}
public static AuthResult allow(String request, String reason, User user, Permission.Action action,
byte[] table, byte[] family, byte[] qualifier) {
public static AuthResult allow(String request, String reason, User user,
Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
return new AuthResult(true, request, reason, user, action, table, family, qualifier);
}
public static AuthResult allow(String request, String reason, User user, Permission.Action action, byte[] table) {
return new AuthResult(true, request, reason, user, action, table, null, null);
}
public static AuthResult deny(String request, String reason, User user,
Permission.Action action, byte[] table) {
return new AuthResult(false, request, reason, user, action, table, null, null);
public static AuthResult allow(String request, String reason, User user,
Permission.Action action, byte[] table,
Map<byte[], ? extends Collection<?>> families) {
return new AuthResult(true, request, reason, user, action, table, families);
}
public static AuthResult deny(String request, String reason, User user,
Permission.Action action, byte[] table, byte[] family, byte[] qualifier) {
return new AuthResult(false, request, reason, user, action, table, family, qualifier);
}
public static AuthResult deny(String request, String reason, User user,
Permission.Action action, byte[] table,
Map<byte[], ? extends Collection<?>> families) {
return new AuthResult(false, request, reason, user, action, table, families);
}
}