You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by st...@apache.org on 2022/03/23 06:26:07 UTC

[impala] 01/02: Revert "IMPALA-9242: Filter privileges before returning them to Sentry"

This is an automated email from the ASF dual-hosted git repository.

stigahuang pushed a commit to annotated tag 3.4.1-rc2
in repository https://gitbox.apache.org/repos/asf/impala.git

commit 134b6492edece7d723bd48890dc934e331988a16
Author: stiga-huang <hu...@gmail.com>
AuthorDate: Tue Mar 22 21:11:09 2022 +0800

    Revert "IMPALA-9242: Filter privileges before returning them to Sentry"
    
    This reverts commit e7d10df2ecaf14f244eb32224e2c8099f2f0d8cf.
---
 .../sentry/SentryAuthorizationPolicy.java          |  85 +------
 .../java/org/apache/impala/catalog/Principal.java  |  51 +----
 .../apache/impala/catalog/PrincipalPrivilege.java  |   4 +-
 .../impala/catalog/PrincipalPrivilegeTree.java     | 248 ---------------------
 .../impala/catalog/PrincipalPrivilegeTreeTest.java | 134 -----------
 5 files changed, 9 insertions(+), 513 deletions(-)

diff --git a/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationPolicy.java b/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationPolicy.java
index 333dd0e..1adfec8 100644
--- a/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationPolicy.java
+++ b/fe/src/main/java/org/apache/impala/authorization/sentry/SentryAuthorizationPolicy.java
@@ -20,24 +20,15 @@ package org.apache.impala.authorization.sentry;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
 import org.apache.impala.authorization.AuthorizationPolicy;
-import org.apache.impala.catalog.PrincipalPrivilege;
-import org.apache.impala.catalog.PrincipalPrivilegeTree;
 import org.apache.impala.catalog.Role;
 import org.apache.impala.catalog.User;
 import org.apache.sentry.core.common.ActiveRoleSet;
-import org.apache.sentry.core.common.Authorizable;
-import org.apache.sentry.core.common.utils.SentryConstants;
-import org.apache.sentry.core.model.db.DBModelAuthorizable;
-import org.apache.sentry.policy.common.Privilege;
-import org.apache.sentry.policy.common.PrivilegeFactory;
-import org.apache.sentry.policy.engine.common.CommonPrivilegeFactory;
-import org.apache.sentry.provider.cache.FilteredPrivilegeCache;
+import org.apache.sentry.provider.cache.PrivilegeCache;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * The source data this cache is backing is read from the Sentry Policy Service.
@@ -46,17 +37,15 @@ import java.util.stream.Collectors;
  * TODO: Instead of calling into Sentry to perform final authorization checks, we
  * should parse/validate the privileges in Impala.
  */
-public class SentryAuthorizationPolicy implements FilteredPrivilegeCache {
+public class SentryAuthorizationPolicy implements PrivilegeCache {
   private static final Logger LOG = LoggerFactory.getLogger(
       SentryAuthorizationPolicy.class);
 
   private final AuthorizationPolicy authzPolicy_;
-  private final PrivilegeFactory privilegeFactory;
 
   public SentryAuthorizationPolicy(AuthorizationPolicy authzPolicy) {
     Preconditions.checkNotNull(authzPolicy);
     authzPolicy_ = authzPolicy;
-    this.privilegeFactory = new CommonPrivilegeFactory();
   }
 
   /**
@@ -65,12 +54,6 @@ public class SentryAuthorizationPolicy implements FilteredPrivilegeCache {
   @Override
   public Set<String> listPrivileges(Set<String> groups,
       ActiveRoleSet roleSet) {
-    return listPrivilegesForGroups(groups, roleSet, null);
-  }
-
-
-  private Set<String> listPrivilegesForGroups(Set<String> groups,
-      ActiveRoleSet roleSet, PrincipalPrivilegeTree.Filter filter) {
     Set<String> privileges = Sets.newHashSet();
     if (roleSet != ActiveRoleSet.ALL) {
       throw new UnsupportedOperationException("Impala does not support role subsets.");
@@ -80,7 +63,7 @@ public class SentryAuthorizationPolicy implements FilteredPrivilegeCache {
     for (String groupName: groups) {
       List<Role> grantedRoles = authzPolicy_.getGrantedRoles(groupName);
       for (Role role: grantedRoles) {
-        privileges.addAll(role.getFilteredPrivilegeNames(filter));
+        privileges.addAll(role.getPrivilegeNames());
       }
     }
     return privileges;
@@ -92,75 +75,17 @@ public class SentryAuthorizationPolicy implements FilteredPrivilegeCache {
   @Override
   public Set<String> listPrivileges(Set<String> groups, Set<String> users,
       ActiveRoleSet roleSet) {
-    return listPrivilegesForGroupsAndUsers(groups, users, roleSet, null);
-  }
-
-  private Set<String> listPrivilegesForGroupsAndUsers(Set<String> groups,
-      Set<String> users, ActiveRoleSet roleSet, PrincipalPrivilegeTree.Filter filter) {
-    Set<String> privileges = listPrivilegesForGroups(groups, roleSet, filter);
+    Set<String> privileges = listPrivileges(groups, roleSet);
     for (String userName: users) {
       User user = authzPolicy_.getUser(userName);
       if (user != null) {
-        privileges.addAll(user.getFilteredPrivilegeNames(filter));
+        privileges.addAll(user.getPrivilegeNames());
       }
     }
     return privileges;
   }
 
   @Override
-  public Set<String> listPrivileges(Set<String> groups, Set<String> users,
-      ActiveRoleSet roleSet, Authorizable... authorizationHierarchy) {
-    PrincipalPrivilegeTree.Filter filter = createPrivilegeFilter(authorizationHierarchy);
-    return listPrivilegesForGroupsAndUsers(groups, users, roleSet, filter);
-  }
-
-  @Override
-  public Set<Privilege> listPrivilegeObjects(Set<String> groups, Set<String> users,
-      ActiveRoleSet roleSet, Authorizable... authorizationHierarchy) {
-    Set<String> privilegeStrings =
-        listPrivileges(groups, users, roleSet, authorizationHierarchy);
-
-    return privilegeStrings.stream()
-      .filter(priString -> priString != null)
-      .map(priString -> getPrivilegeObject(priString))
-      .collect(Collectors.toSet());
-  }
-
-  private Privilege getPrivilegeObject(String priString) {
-    return privilegeFactory.createPrivilege(priString);
-  }
-
-  private PrincipalPrivilegeTree.Filter createPrivilegeFilter(
-      Authorizable... authorizationHierarchy) {
-    PrincipalPrivilegeTree.Filter filter = new PrincipalPrivilegeTree.Filter();
-    for (Authorizable auth : authorizationHierarchy) {
-      String name = auth.getName().toLowerCase();
-      if (name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE) ||
-        name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE_SOME)||
-        name.equals(SentryConstants.RESOURCE_WILDCARD_VALUE_ALL)) {
-        name = null; // null will match with everything
-      }
-      if (!(auth instanceof DBModelAuthorizable)) continue;
-      DBModelAuthorizable dbAuth = (DBModelAuthorizable) auth;
-      switch (dbAuth.getAuthzType()) {
-        case Server:
-          filter.setServer(name);
-          break;
-        case Db:
-          filter.setDb(name);
-          break;
-        case Table:
-          filter.setTable(name);
-          break;
-        case URI:
-          filter.setIsUri(true);
-        // Do not do anything for Column and View
-      }
-    }
-    return filter;
-  }
-
-  @Override
   public void close() {
     // Nothing to do, but required by PrivilegeCache.
   }
diff --git a/fe/src/main/java/org/apache/impala/catalog/Principal.java b/fe/src/main/java/org/apache/impala/catalog/Principal.java
index 6d03e9d..39aeef1 100644
--- a/fe/src/main/java/org/apache/impala/catalog/Principal.java
+++ b/fe/src/main/java/org/apache/impala/catalog/Principal.java
@@ -22,7 +22,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 import org.apache.impala.thrift.TCatalogObject;
 import org.apache.impala.thrift.TCatalogObjectType;
@@ -44,17 +43,6 @@ public abstract class Principal extends CatalogObjectImpl {
   private final CatalogObjectCache<PrincipalPrivilege> principalPrivileges_ =
       new CatalogObjectCache<>(false);
 
-  // An index that allows efficient filtering of Privileges that are relevant to an
-  // access check. Should contain exactly the same privileges as principalPrivileges_.
-  // Does not support catalog version logic / removal of privileges by privilegeName,
-  // so principalPrivileges_ is still needed.
-  private final PrincipalPrivilegeTree privilegeTree_ = new PrincipalPrivilegeTree();
-
-  // Protects privilegeTree_ and its coherence with principalPrivileges_.
-  // Needs to be taken when accessing privilegeTree_ or when writing principalPrivileges_,
-  // but not when reading principalPrivileges_.
-  private final ReentrantReadWriteLock rwLock_ = new ReentrantReadWriteLock(true);
-
   protected Principal(String principalName, TPrincipalType type,
       Set<String> grantGroups) {
     principal_ = new TPrincipal();
@@ -74,14 +62,7 @@ public abstract class Principal extends CatalogObjectImpl {
    * to the principal.
    */
   public boolean addPrivilege(PrincipalPrivilege privilege) {
-    try {
-      rwLock_.writeLock().lock();
-      if (!principalPrivileges_.add(privilege)) return false;
-      privilegeTree_.add(privilege);
-    } finally {
-      rwLock_.writeLock().unlock();
-    }
-    return true;
+    return principalPrivileges_.add(privilege);
   }
 
   /**
@@ -93,7 +74,7 @@ public abstract class Principal extends CatalogObjectImpl {
   }
 
   /**
-   * Returns all privilege names for this principal, or an empty set if no privileges are
+   * Returns all privilege names for this principal, or an empty set of no privileges are
    * granted to the principal.
    */
   public Set<String> getPrivilegeNames() {
@@ -101,25 +82,6 @@ public abstract class Principal extends CatalogObjectImpl {
   }
 
   /**
-   * Returns all privilege names for this principal that match 'filter'.
-   */
-  public Set<String> getFilteredPrivilegeNames(PrincipalPrivilegeTree.Filter filter) {
-    if (filter == null) return getPrivilegeNames();
-
-    List<PrincipalPrivilege> privileges;
-    try {
-      rwLock_.readLock().lock();
-      privileges = privilegeTree_.getFilteredList(filter);
-    } finally {
-      rwLock_.readLock().unlock();
-    }
-
-    Set<String> results = new HashSet<>();
-    for (PrincipalPrivilege priv: privileges) results.add(priv.getName());
-    return results;
-  }
-
-  /**
    * Gets a privilege with the given name from this principal. If no privilege exists
    * with this name null is returned.
    */
@@ -132,14 +94,7 @@ public abstract class Principal extends CatalogObjectImpl {
    * privilege or null if no privilege exists with this name.
    */
   public PrincipalPrivilege removePrivilege(String privilegeName) {
-    try {
-      rwLock_.writeLock().lock();
-      PrincipalPrivilege privilege = principalPrivileges_.remove(privilegeName);
-      if (privilege != null) privilegeTree_.remove(privilege);
-      return privilege;
-    } finally {
-      rwLock_.writeLock().unlock();
-    }
+    return principalPrivileges_.remove(privilegeName);
   }
 
   /**
diff --git a/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilege.java b/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilege.java
index 7d1b90f..1b2da26 100644
--- a/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilege.java
+++ b/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilege.java
@@ -37,11 +37,9 @@ public class PrincipalPrivilege extends CatalogObjectImpl {
   private static final String AUTHORIZABLE_SEPARATOR = "->";
   private static final String KV_SEPARATOR = "=";
   private final TPrivilege privilege_;
-  private final String name_;
 
   private PrincipalPrivilege(TPrivilege privilege) {
     privilege_ = Preconditions.checkNotNull(privilege);
-    name_ =  buildPrivilegeName(privilege_);
   }
 
   public TPrivilege toThrift() { return privilege_; }
@@ -155,7 +153,7 @@ public class PrincipalPrivilege extends CatalogObjectImpl {
     return TCatalogObjectType.PRIVILEGE;
   }
   @Override
-  public String getName() { return name_; }
+  public String getName() { return buildPrivilegeName(privilege_); }
   public int getPrincipalId() { return privilege_.getPrincipal_id(); }
   public TPrincipalType getPrincipalType() { return privilege_.getPrincipal_type(); }
 
diff --git a/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilegeTree.java b/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilegeTree.java
deleted file mode 100644
index 61d87da..0000000
--- a/fe/src/main/java/org/apache/impala/catalog/PrincipalPrivilegeTree.java
+++ /dev/null
@@ -1,248 +0,0 @@
-// 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.impala.catalog;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.apache.impala.thrift.TPrivilege;
-import org.apache.impala.thrift.TPrivilegeScope;
-
-import com.google.common.base.Preconditions;
-
-/**
- * Tree that allows efficient lookup for all privileges that can be relevant in the
- * authorization of an object, e.g. getting privileges for server1/db1 should only iterate
- * through privileges in db1, but not in other databases.
- *
- * Performance characteristics:
- * add/remove: O(1), as the depth of the tree is limited
- * getFilteredList: O(number_of_privileges_that_match_the_filter)
- *
- * The primary motivation is to speed up SHOW DATABASES/TABLES calls which do a separate
- * listing to check access rights for each database/table. For this reason column and URI
- * privileges do not get their own "level" in the tree - column privileges are merged with
- * table privileges and URI privileges are stored per-server. This avoids unnecessarily
- * storing many small hash maps in memory.
- *
- * This class is expected to be used in pair with a CatalogObjectCache<PrincipalPrivilege>
- * which also handles catalog version logic and has efficient "remove" by full name.
- */
-public class PrincipalPrivilegeTree {
-   // Contains database/table/column privileges grouped by server name + database name
-   // + table name. Column privileges are stored with table privileges to avoid the
-   // memory cost of creating many small hashmaps for columns with privileges.
-   Node<PrincipalPrivilege> tableRoot_ = new Node<>();
-
-   // Contains URI privileges grouped by server name. Storing these separately from table
-   // privileges allows Node to be simpler, as the Server level doesn't need to
-   // differentiate between two kind of privileges.
-   Node<PrincipalPrivilege> uriRoot_ = new Node<>();
-
-   // Contains server privileges grouped by server name. Stored separately from other
-   // privileges, as these should be returned both when listing URI and non-URI
-   // privileges.
-   Node<PrincipalPrivilege> serverRoot_ = new Node<>();
-
-   public void add(PrincipalPrivilege privilege) {
-     TPrivilege priv = privilege.toThrift();
-     List<String> path = toPath(priv);
-     Node<PrincipalPrivilege> root = getRootForScope(priv.getScope());
-     root.add(privilege.getName(), privilege, path);
-   }
-
-   public void remove(PrincipalPrivilege privilege) {
-     TPrivilege priv = privilege.toThrift();
-     List<String> path = toPath(priv);
-     Node<PrincipalPrivilege> root = getRootForScope(priv.getScope());
-     root.remove(privilege.getName(), privilege, path);
-   }
-
-   /**
-   * Collect all privileges that match the filter.
-   * E.g. for server1.db1, it returns privileges on:
-   *  server1, server1.db1, server1.db1.table1,
-   * but not privileges on:
-   *  server2, server2.db1, server1.db2
-   */
-   public List<PrincipalPrivilege> getFilteredList(Filter filter) {
-     List<String> path = filter.toPath();
-     List<PrincipalPrivilege> results = new ArrayList<>();
-     List<Node<PrincipalPrivilege>> roots = new ArrayList<>();
-     // Server level privileges apply to both URIs and other objects.
-     roots.add(serverRoot_);
-     if (filter.isUri_ || path.size() == 1) roots.add(uriRoot_);
-     if (!filter.isUri_) roots.add(tableRoot_);
-     for (Node<PrincipalPrivilege> root : roots) {
-       root.getAllMatchingValues(results, path);
-     }
-     return results;
-   }
-
-   private Node<PrincipalPrivilege> getRootForScope(TPrivilegeScope scope) {
-     switch(scope) {
-     case URI: return uriRoot_;
-     case SERVER: return serverRoot_;
-     default: return tableRoot_;
-     }
-   }
-
-   /**
-    * Creates a path to the given privilege in the tree like ["server1", "db1", "table1"].
-    */
-   private static List<String> toPath(TPrivilege priv) {
-      List<String> path = new ArrayList<>();
-      String server = priv.getServer_name();
-      String db = priv.getDb_name();
-      String table = priv.getTable_name();
-      if (server == null) return path;
-      path.add(server.toLowerCase());
-      if (db == null) return path;
-      path.add(db.toLowerCase());
-      if (table != null) path.add(table.toLowerCase());
-      return path;
-   }
-
-  /**
-   * Lossy representation of an Authorizable (doesn't contain column/URI name).
-   * Can be used to rule out the bulk of the privileges that can have no effect on an
-   * access check.
-   */
-  public static class Filter {
-    String server_, db_, table_; // must be lower case, null matches everything
-    boolean isUri_ = false;
-
-    public void setServer(String server) { server_ = server; }
-    public void setDb(String db) { db_ = db; }
-    public void setTable(String  table) { table_ = table; }
-    public void setIsUri(boolean isUri) { isUri_ = isUri; }
-
-   /**
-    * Creates a path till the first null element of the filter, e.g.
-    * ["server1", "db1"] if table_ is null.
-    */
-    private List<String> toPath() {
-      List<String> path = new ArrayList<>();
-      if (server_ == null) return path;
-      path.add(server_);
-      if (db_ == null) return path;
-      Preconditions.checkState(!isUri_);
-      path.add(db_);
-      if (table_ != null) path.add(table_);
-      return path;
-    }
-  }
-
-  /**
-   * Tree node that holds the privileges for a given object (server, database, table),
-   * and its children objects (e.g. databases for a server). Descendants can be addressed
-   * with paths like ["server1", "db1", "table1"].
-   *
-   * Only used to store privileges, but creating a generic class seemed clearer.
-   */
-  private static class Node<T> {
-    Map<String, T> values_ = null;
-    Map<String, Node<T>> children_= null;
-
-    boolean isEmpty() { return values_ == null && children_ == null; }
-
-    /**
-     * Finds the Node at 'path' (or potentially builds it if it doesn't exist),
-     * and adds 'key' + 'value' to it.
-     */
-    public void add(String key, T value, List<String> path) {
-      add(key, value, path, 0);
-    }
-
-    /**
-     * Finds the Node at 'path' (it is treated as error if it doesn't exist),
-     * and removes 'key' + 'value' from it. If a Node becomes empty, it is removed from
-     * it's parent.
-     */
-    public void remove(String key, T value, List<String> path) {
-      remove(key, value, path, 0);
-    }
-
-    /**
-     * Collect all values in this node and those descendants that match 'path'.
-     */
-    public void getAllMatchingValues(List<T> results, List<String> path) {
-      getAllMatchingValues(results, path, 0);
-    }
-
-    /**
-     * Collect all values in this node and its descendants.
-     */
-    public void getAllValues(List<T> results) {
-      if (values_ != null) results.addAll(values_.values());
-      if (children_ != null) {
-        for (Map.Entry<String, Node<T>> entry : children_.entrySet()) {
-          entry.getValue().getAllValues(results);
-        }
-      }
-    }
-
-    private void add(String key, T value, List<String> path, int depth) {
-      if (path.size() <= depth) {
-        if (values_ == null) {
-          // It is very common to have only a single privilege on an object
-          // (e.g. ownership), so the initial capacity is 1 to avoid wasting memory.
-          values_ = new HashMap<>(1);
-        }
-        values_.put(key, value); // Can update an existing value.
-      } else {
-        if (children_ == null) children_ = new HashMap<>();
-        String child_name = path.get(depth);
-        Node<T> next = children_.computeIfAbsent(child_name, (k) -> new Node<T>());
-        next.add(key, value, path, depth + 1);
-      }
-    }
-
-    private void remove(String key, T value, List<String> path, int depth) {
-      if (path.size() <= depth) {
-        Preconditions.checkNotNull(values_);
-        T found = values_.remove(key);
-        Preconditions.checkNotNull(found);
-        if (values_.isEmpty()) values_ = null;
-      } else {
-        Preconditions.checkNotNull(children_);
-        String child_name = path.get(depth);
-        Node<T> next = children_.get(child_name);
-        Preconditions.checkNotNull(next);
-        next.remove(key, value, path, depth + 1);
-        // Remove the child if it became empty.
-        if (next.isEmpty()) children_.remove(child_name);
-        if (children_.isEmpty()) children_ = null;
-      }
-    }
-
-    private void getAllMatchingValues(List<T> results, List<String> path, int depth) {
-      if (path.size() <= depth) {
-        getAllValues(results);
-        return;
-      }
-      if (values_ != null) results.addAll(values_.values());
-      if (children_ == null) return;
-      String child_name = path.get(depth);
-      Preconditions.checkNotNull(child_name);
-      Node<T> next = children_.get(child_name);
-      if (next != null) next.getAllMatchingValues(results, path, depth + 1);
-    }
-  }
-}
diff --git a/fe/src/test/java/org/apache/impala/catalog/PrincipalPrivilegeTreeTest.java b/fe/src/test/java/org/apache/impala/catalog/PrincipalPrivilegeTreeTest.java
deleted file mode 100644
index dfc38f1..0000000
--- a/fe/src/test/java/org/apache/impala/catalog/PrincipalPrivilegeTreeTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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.impala.catalog;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.impala.thrift.TPrivilege;
-import org.apache.impala.thrift.TPrivilegeLevel;
-import org.apache.impala.thrift.TPrivilegeScope;
-
-public class PrincipalPrivilegeTreeTest {
-  @Test
-  public void testPrincipalPrivilegeTree() {
-    // This test is mainly based on TestTreePrivilegeCache.testListPrivilegesWildCard in
-    // Sentry.
-    List<PrincipalPrivilege> privs = new ArrayList<>();
-
-    privs.add(createTablePriv("server1", "db1", "t1"));
-    privs.add(createTablePriv("server1", "db2", "t1"));
-    privs.add(createTablePriv("server1", "db1", "t2"));
-    privs.add(createDbPriv("server1", "db1"));
-    privs.add(createServerPriv("server1"));
-    privs.add(createServerPriv("server2"));
-    privs.add(createColumnPriv("server1", "db1", "t1", "c1"));
-    privs.add(createUriPriv("server1", "uri1"));
-
-    PrincipalPrivilegeTree tree = new PrincipalPrivilegeTree();
-    // Run the same tests twice to check if removing privileges works correctly.
-    for (int i = 0; i < 2; i++) {
-      for (PrincipalPrivilege priv : privs) tree.add(priv);
-
-      // Update a privilege and check that the newer object is returned by
-      // getFilteredList.
-      PrincipalPrivilege newServer2Priv = createServerPriv("server2");
-      tree.add(newServer2Priv);
-      List<PrincipalPrivilege> results =
-          tree.getFilteredList(createFilter("server2", null, null));
-      assertEquals(1, results.size());
-      assertSame(results.get(0), newServer2Priv);
-
-      assertEquals(7, tree.getFilteredList(createFilter("server1", null, null)).size());
-      assertEquals(5, tree.getFilteredList(createFilter("server1", "db1", null)).size());
-      assertEquals(2, tree.getFilteredList(createFilter("server1", "db2", null)).size());
-      assertEquals(4, tree.getFilteredList(createFilter("server1", "db1", "t1")).size());
-      assertEquals(2, tree.getFilteredList(createFilter("server1", "db2", "t1")).size());
-      assertEquals(2, tree.getFilteredList(createUriFilter("server1")).size());
-
-      // Check that all privileges are removed successfully.
-      for (PrincipalPrivilege priv : privs) tree.remove(priv);
-      assertEquals(0, tree.getFilteredList(createFilter("server1", null, null)).size());
-    }
-  }
-
-  PrincipalPrivilege createColumnPriv(String server, String db, String table,
-      String column) {
-    TPrivilege priv =
-        new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.COLUMN, false);
-    priv.setServer_name(server);
-    priv.setDb_name(db);
-    priv.setTable_name(table);
-    priv.setColumn_name(column);
-    return PrincipalPrivilege.fromThrift(priv);
-  }
-
-  PrincipalPrivilege createTablePriv(String server, String db, String table) {
-    TPrivilege priv =
-        new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.TABLE, false);
-    priv.setServer_name(server);
-    priv.setDb_name(db);
-    priv.setTable_name(table);
-    return PrincipalPrivilege.fromThrift(priv);
-  }
-
-  PrincipalPrivilege createDbPriv(String server, String db) {
-    TPrivilege priv =
-        new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.DATABASE, false);
-    priv.setServer_name(server);
-    priv.setDb_name(db);
-    return PrincipalPrivilege.fromThrift(priv);
-  }
-
-  PrincipalPrivilege createUriPriv(String server, String uri) {
-    TPrivilege priv =
-        new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.URI, false);
-    priv.setServer_name(server);
-    priv.setUri(uri);
-    return PrincipalPrivilege.fromThrift(priv);
-  }
-
-  PrincipalPrivilege createServerPriv(String server) {
-    TPrivilege priv =
-        new TPrivilege(TPrivilegeLevel.SELECT, TPrivilegeScope.SERVER, false);
-    priv.setServer_name(server);
-    return PrincipalPrivilege.fromThrift(priv);
-  }
-
-  PrincipalPrivilegeTree.Filter createFilter(String server, String db,
-      String table) {
-    PrincipalPrivilegeTree.Filter filter = new PrincipalPrivilegeTree.Filter();
-    filter.setServer(server);
-    filter.setDb(db);
-    filter.setTable(table);
-    filter.setIsUri(false);
-    return filter;
-  }
-
-  PrincipalPrivilegeTree.Filter createUriFilter(String server) {
-    PrincipalPrivilegeTree.Filter filter = new PrincipalPrivilegeTree.Filter();
-    filter.setServer(server);
-    filter.setIsUri(true);
-    return filter;
-  }
-}