You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by mb...@apache.org on 2013/01/09 06:45:33 UTC

svn commit: r1430691 - in /hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access: AccessController.java AuthResult.java

Author: mbertozzi
Date: Wed Jan  9 05:45:33 2013
New Revision: 1430691

URL: http://svn.apache.org/viewvc?rev=1430691&view=rev
Log:
HBASE-6386 Audit log messages do not include column family / qualifier information consistently (Marcelo Vanzin)

Modified:
    hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
    hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java?rev=1430691&r1=1430690&r2=1430691&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AccessController.java Wed Jan  9 05:45:33 2013
@@ -64,6 +64,7 @@ import org.apache.hadoop.hbase.security.
 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 Ba
     // 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 Ba
        (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 Ba
               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 Ba
               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,9 +349,9 @@ public class AccessController extends Ba
   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() + ")");
@@ -358,25 +363,6 @@ public class AccessController extends Ba
    * 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.
-   * @param perm Action that is required
-   * @param env The current coprocessor environment
    * @param families The map of column families-qualifiers.
    * @throws AccessDeniedException if the authorization check failed
    */
@@ -389,24 +375,36 @@ public class AccessController extends Ba
     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 Ba
     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 Ba
           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 Ba
   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 Ba
       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 Ba
       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 Ba
       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 Ba
   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 Ba
           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 Ba
       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;
+  }
 }

Modified: hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java
URL: http://svn.apache.org/viewvc/hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java?rev=1430691&r1=1430690&r2=1430691&view=diff
==============================================================================
--- hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java (original)
+++ hbase/trunk/hbase-server/src/main/java/org/apache/hadoop/hbase/security/access/AuthResult.java Wed Jan  9 05:45:33 2013
@@ -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.Byte
 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 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) {
-    return new AuthResult(false, request, reason, user, action, table, null, null);
+      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, byte[] family, byte[] qualifier) {
-    return new AuthResult(false, request, reason, user, action, table, family, qualifier);
+        Permission.Action action, byte[] table,
+        Map<byte[], ? extends Collection<?>> families) {
+    return new AuthResult(false, request, reason, user, action, table, families);
   }
 }
\ No newline at end of file